@@ -65,9 +65,9 @@ package final actor DocumentationManager {
6565 // Search for the nearest documentable symbol at this location
6666 let syntaxTree = await swiftLanguageService. syntaxTreeManager. syntaxTree ( for: snapshot)
6767 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)
7171 )
7272 else {
7373 throw ResponseError . requestFailed ( convertError: . noDocumentation)
@@ -140,149 +140,53 @@ fileprivate extension ResponseError {
140140 }
141141}
142142
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 [ ]
271160 }
272161 }
273- return . skipChildren
274162 }
163+ }
275164
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
284189 }
285- return . skipChildren
286190 }
287191}
288192#endif
0 commit comments