@@ -65,9 +65,9 @@ package final actor DocumentationManager {
65
65
// Search for the nearest documentable symbol at this location
66
66
let syntaxTree = await swiftLanguageService. syntaxTreeManager. syntaxTree ( for: snapshot)
67
67
guard
68
- let nearestDocumentableSymbol = DocumentableSymbolFinder . find (
69
- in : [ Syntax ( syntaxTree) ] ,
70
- at : snapshot. absolutePosition ( of: position)
68
+ let nearestDocumentableSymbol = DocumentableSymbol . findNearestSymbol (
69
+ syntaxTree : syntaxTree,
70
+ position : snapshot. absolutePosition ( of: position)
71
71
)
72
72
else {
73
73
throw ResponseError . requestFailed ( convertError: . noDocumentation)
@@ -140,149 +140,53 @@ fileprivate extension ResponseError {
140
140
}
141
141
}
142
142
143
- fileprivate final class DocumentableSymbolFinder : SyntaxAnyVisitor {
144
- struct Symbol {
145
- let position : AbsolutePosition
146
- let documentationComments : [ String ]
147
- let depth : Int
148
- }
149
-
150
- private let cursorPosition : AbsolutePosition
151
-
152
- /// Accumulating the result in here.
153
- private var result : Symbol ? = nil
154
-
155
- private var depth : Int = 0
156
-
157
- private init ( _ cursorPosition: AbsolutePosition ) {
158
- self . cursorPosition = cursorPosition
159
- super. init ( viewMode: . sourceAccurate)
160
- }
161
-
162
- /// Designated entry point for ``DocumentableSymbolFinder``.
163
- static func find(
164
- in nodes: some Sequence < Syntax > ,
165
- at cursorPosition: AbsolutePosition
166
- ) -> Symbol ? {
167
- let visitor = DocumentableSymbolFinder ( cursorPosition)
168
- for node in nodes {
169
- visitor. walk ( node)
170
- }
171
- return visitor. result
172
- }
173
-
174
- private func setResult( node: some SyntaxProtocol , position: AbsolutePosition ) {
175
- setResult (
176
- symbol: Symbol (
177
- position: position,
178
- documentationComments: node. leadingTrivia. flatMap { trivia -> [ String ] in
179
- switch trivia {
180
- case . docLineComment( let comment) :
181
- return [ String ( comment. dropFirst ( 3 ) . trimmingCharacters ( in: . whitespaces) ) ]
182
- case . docBlockComment( let comment) :
183
- return comment. dropFirst ( 3 )
184
- . dropLast ( 2 )
185
- . split ( separator: " \n " )
186
- . map { String ( $0) . trimmingCharacters ( in: . whitespaces) }
187
- default :
188
- return [ ]
189
- }
190
- } ,
191
- depth: depth
192
- )
193
- )
194
- }
195
-
196
- private func setResult( symbol: Symbol ) {
197
- guard symbol. depth > result? . depth ?? - 1 else {
198
- return
199
- }
200
- result = symbol
201
- }
202
-
203
- override func visitAny( _ node: Syntax ) -> SyntaxVisitorContinueKind {
204
- guard depth > result? . depth ?? - 1 else {
205
- return . skipChildren
206
- }
207
- return . visitChildren
208
- }
209
-
210
- private func visitNamedDecl( node: some NamedDeclSyntax ) -> SyntaxVisitorContinueKind {
211
- if cursorPosition < node. range. upperBound {
212
- setResult ( node: node, position: node. name. positionAfterSkippingLeadingTrivia)
213
- }
214
- return . visitChildren
215
- }
216
-
217
- override func visit( _ node: StructDeclSyntax ) -> SyntaxVisitorContinueKind {
218
- visitNamedDecl ( node: node)
219
- }
220
-
221
- override func visit( _ node: ClassDeclSyntax ) -> SyntaxVisitorContinueKind {
222
- visitNamedDecl ( node: node)
223
- }
224
-
225
- override func visit( _ node: ActorDeclSyntax ) -> SyntaxVisitorContinueKind {
226
- visitNamedDecl ( node: node)
227
- }
228
-
229
- override func visit( _ node: EnumDeclSyntax ) -> SyntaxVisitorContinueKind {
230
- visitNamedDecl ( node: node)
231
- }
232
-
233
- override func visit( _ node: ProtocolDeclSyntax ) -> SyntaxVisitorContinueKind {
234
- visitNamedDecl ( node: node)
235
- }
236
-
237
- override func visit( _ node: MemberBlockSyntax ) -> SyntaxVisitorContinueKind {
238
- depth += 1
239
- let range = node. leftBrace. endPositionBeforeTrailingTrivia..< node. rightBrace. positionAfterSkippingLeadingTrivia
240
- guard range. contains ( cursorPosition) else {
241
- return . skipChildren
242
- }
243
- return . visitChildren
244
- }
245
-
246
- override func visitPost( _ node: MemberBlockSyntax ) {
247
- depth -= 1 ;
248
- }
249
-
250
- override func visit( _ node: FunctionDeclSyntax ) -> SyntaxVisitorContinueKind {
251
- let symbolPosition = node. name. positionAfterSkippingLeadingTrivia
252
- if cursorPosition < node. range. upperBound {
253
- setResult ( node: node, position: symbolPosition)
254
- }
255
- return . skipChildren
256
- }
257
-
258
- override func visit( _ node: InitializerDeclSyntax ) -> SyntaxVisitorContinueKind {
259
- let symbolPosition = node. initKeyword. positionAfterSkippingLeadingTrivia
260
- if node. range. contains ( cursorPosition) || cursorPosition < symbolPosition {
261
- setResult ( node: node, position: symbolPosition)
262
- }
263
- return . skipChildren
264
- }
265
-
266
- override func visit( _ node: EnumCaseDeclSyntax ) -> SyntaxVisitorContinueKind {
267
- for element in node. elements {
268
- let symbolPosition = element. name. positionAfterSkippingLeadingTrivia
269
- if element. range. contains ( cursorPosition) || cursorPosition < symbolPosition {
270
- setResult ( node: node, position: symbolPosition)
143
+ fileprivate struct DocumentableSymbol {
144
+ let position : AbsolutePosition
145
+ let documentationComments : [ String ]
146
+
147
+ init ( node: any SyntaxProtocol , position: AbsolutePosition ) {
148
+ self . position = position
149
+ self . documentationComments = node. leadingTrivia. flatMap { trivia -> [ String ] in
150
+ switch trivia {
151
+ case . docLineComment( let comment) :
152
+ return [ String ( comment. dropFirst ( 3 ) . trimmingCharacters ( in: . whitespaces) ) ]
153
+ case . docBlockComment( let comment) :
154
+ return comment. dropFirst ( 3 )
155
+ . dropLast ( 2 )
156
+ . split ( separator: " \n " )
157
+ . map { String ( $0) . trimmingCharacters ( in: . whitespaces) }
158
+ default :
159
+ return [ ]
271
160
}
272
161
}
273
- return . skipChildren
274
162
}
163
+ }
275
164
276
- override func visit( _ node: VariableDeclSyntax ) -> SyntaxVisitorContinueKind {
277
- // A variable declaration is only documentable if there is only one pattern binding
278
- guard let identifier = node. bindings. only? . pattern. as ( IdentifierPatternSyntax . self) else {
279
- return . skipChildren
280
- }
281
- let symbolPosition = identifier. positionAfterSkippingLeadingTrivia
282
- if node. range. contains ( cursorPosition) || cursorPosition < symbolPosition {
283
- setResult ( node: node, position: symbolPosition)
165
+ fileprivate extension DocumentableSymbol {
166
+ static func findNearestSymbol( syntaxTree: SourceFileSyntax , position: AbsolutePosition ) -> DocumentableSymbol ? {
167
+ guard let token = syntaxTree. token ( at: position) else {
168
+ return nil
169
+ }
170
+ return token. ancestorOrSelf { node in
171
+ if let namedDecl = node. asProtocol ( NamedDeclSyntax . self) {
172
+ return DocumentableSymbol ( node: namedDecl, position: namedDecl. name. positionAfterSkippingLeadingTrivia)
173
+ } else if let initDecl = node. as ( InitializerDeclSyntax . self) {
174
+ return DocumentableSymbol ( node: initDecl, position: initDecl. initKeyword. positionAfterSkippingLeadingTrivia)
175
+ } else if let functionDecl = node. as ( FunctionDeclSyntax . self) {
176
+ return DocumentableSymbol ( node: functionDecl, position: functionDecl. name. positionAfterSkippingLeadingTrivia)
177
+ } else if let variableDecl = node. as ( VariableDeclSyntax . self) {
178
+ guard let identifier = variableDecl. bindings. only? . pattern. as ( IdentifierPatternSyntax . self) else {
179
+ return nil
180
+ }
181
+ return DocumentableSymbol ( node: variableDecl, position: identifier. positionAfterSkippingLeadingTrivia)
182
+ } else if let enumCaseDecl = node. as ( EnumCaseDeclSyntax . self) {
183
+ guard let name = enumCaseDecl. elements. only? . name else {
184
+ return nil
185
+ }
186
+ return DocumentableSymbol ( node: enumCaseDecl, position: name. positionAfterSkippingLeadingTrivia)
187
+ }
188
+ return nil
284
189
}
285
- return . skipChildren
286
190
}
287
191
}
288
192
#endif
0 commit comments