Skip to content

Commit 9f907b9

Browse files
improve logic for DocumentableSymbol.findNearestSymbol(node:)
1 parent 31d5dfc commit 9f907b9

File tree

2 files changed

+27
-29
lines changed

2 files changed

+27
-29
lines changed

Sources/SourceKitLSP/Swift/DoccDocumentation.swift

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ extension SwiftLanguageService {
4646
let nearestDocumentableSymbol = DocumentableSymbol.findNearestSymbol(
4747
syntaxTree: syntaxTree,
4848
position: snapshot.absolutePosition(of: position)
49-
) ?? DocumentableSymbol.findTopLevelSymbol(syntaxTree: syntaxTree)
49+
)
5050
else {
5151
throw ResponseError.requestFailed(doccDocumentationError: .noDocumentableSymbols)
5252
}
@@ -152,34 +152,31 @@ fileprivate struct DocumentableSymbol {
152152
}
153153
}
154154

155-
static func findTopLevelSymbol(syntaxTree: SourceFileSyntax) -> DocumentableSymbol? {
156-
class Visitor: SyntaxAnyVisitor {
157-
var topLevelSymbol: DocumentableSymbol? = nil
158-
159-
override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
160-
guard topLevelSymbol == nil else {
161-
return .skipChildren
162-
}
163-
164-
if let symbol = DocumentableSymbol(node: node) {
165-
topLevelSymbol = symbol
166-
return .skipChildren
167-
}
168-
return .visitChildren
155+
static func findNearestSymbol(syntaxTree: SourceFileSyntax, position: AbsolutePosition) -> DocumentableSymbol? {
156+
// token(at:) can return nil if the position is at the end of the document. Fall back to using the last token in this case.
157+
let token = syntaxTree.token(at: position) ?? syntaxTree.lastToken(viewMode: .all)
158+
// Check if the current token is within a valid documentable symbol
159+
if let token, let symbol = token.ancestorOrSelf(mapping: { DocumentableSymbol(node: $0) }) {
160+
return symbol
161+
}
162+
// Walk forward through the tokens until we find a documentable symbol
163+
var previousToken = token
164+
while let nextToken = previousToken?.nextToken(viewMode: .all) {
165+
if let symbol = nextToken.ancestorOrSelf(mapping: { DocumentableSymbol(node: $0) }) {
166+
return symbol
169167
}
168+
previousToken = nextToken
170169
}
171-
172-
let visitor = Visitor(viewMode: .all)
173-
visitor.walk(syntaxTree)
174-
return visitor.topLevelSymbol
175-
}
176-
177-
static func findNearestSymbol(syntaxTree: SourceFileSyntax, position: AbsolutePosition) -> DocumentableSymbol? {
178-
guard let token = syntaxTree.token(at: position) else {
179-
return nil
170+
// Walk backwards through the tokens until we find a documentable symbol
171+
previousToken = token
172+
while let nextToken = previousToken?.previousToken(viewMode: .all) {
173+
if let symbol = nextToken.ancestorOrSelf(mapping: { DocumentableSymbol(node: $0) }) {
174+
return symbol
175+
}
176+
previousToken = nextToken
180177
}
181-
// Walk up the tree until we find a documentable symbol
182-
return token.ancestorOrSelf { DocumentableSymbol(node: $0) }
178+
// We couldn't find anything
179+
return nil
183180
}
184181
}
185182
#endif

Tests/SourceKitLSPTests/DoccDocumentationTests.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ final class DoccDocumentationTests: XCTestCase {
350350
case first
351351
/// The se5️⃣cond kind
352352
case second
353-
}
354-
}6️⃣
353+
}6️⃣
354+
}7️⃣
355355
""",
356356
],
357357
enableBackgroundIndexing: true
@@ -365,7 +365,8 @@ final class DoccDocumentationTests: XCTestCase {
365365
"3️⃣": .renderNode(kind: .symbol, path: "MyLibrary/Structure/Kind"),
366366
"4️⃣": .renderNode(kind: .symbol, path: "MyLibrary/Structure/Kind/first"),
367367
"5️⃣": .renderNode(kind: .symbol, path: "MyLibrary/Structure/Kind/second"),
368-
"6️⃣": .renderNode(kind: .symbol, path: "MyLibrary/Structure/numberPlusOne"),
368+
"6️⃣": .renderNode(kind: .symbol, path: "MyLibrary/Structure/Kind"),
369+
"7️⃣": .renderNode(kind: .symbol, path: "MyLibrary/Structure/Kind"),
369370
]
370371
)
371372
}

0 commit comments

Comments
 (0)