@@ -48,7 +48,7 @@ extension SwiftLanguageService {
48
48
position: snapshot. absolutePosition ( of: position)
49
49
)
50
50
else {
51
- throw ResponseError . requestFailed ( doccDocumentationError: . noDocumentation )
51
+ throw ResponseError . requestFailed ( doccDocumentationError: . noDocumentableSymbols )
52
52
}
53
53
// Retrieve the symbol graph as well as information about the symbol
54
54
let symbolPosition = await adjustPositionToStartOfIdentifier (
@@ -130,30 +130,67 @@ fileprivate struct DocumentableSymbol {
130
130
}
131
131
}
132
132
133
+ init ? ( node: any SyntaxProtocol ) {
134
+ if let namedDecl = node. asProtocol ( NamedDeclSyntax . self) {
135
+ self = DocumentableSymbol ( node: namedDecl, position: namedDecl. name. positionAfterSkippingLeadingTrivia)
136
+ } else if let initDecl = node. as ( InitializerDeclSyntax . self) {
137
+ self = DocumentableSymbol ( node: initDecl, position: initDecl. initKeyword. positionAfterSkippingLeadingTrivia)
138
+ } else if let deinitDecl = node. as ( DeinitializerDeclSyntax . self) {
139
+ self = DocumentableSymbol ( node: deinitDecl, position: deinitDecl. deinitKeyword. positionAfterSkippingLeadingTrivia)
140
+ } else if let functionDecl = node. as ( FunctionDeclSyntax . self) {
141
+ self = DocumentableSymbol ( node: functionDecl, position: functionDecl. name. positionAfterSkippingLeadingTrivia)
142
+ } else if let subscriptDecl = node. as ( SubscriptDeclSyntax . self) {
143
+ self = DocumentableSymbol ( node: subscriptDecl, position: subscriptDecl. positionAfterSkippingLeadingTrivia)
144
+ } else if let variableDecl = node. as ( VariableDeclSyntax . self) {
145
+ guard let identifier = variableDecl. bindings. only? . pattern. as ( IdentifierPatternSyntax . self) else {
146
+ return nil
147
+ }
148
+ self = DocumentableSymbol ( node: variableDecl, position: identifier. positionAfterSkippingLeadingTrivia)
149
+ } else if let enumCaseDecl = node. as ( EnumCaseDeclSyntax . self) {
150
+ guard let name = enumCaseDecl. elements. only? . name else {
151
+ return nil
152
+ }
153
+ self = DocumentableSymbol ( node: enumCaseDecl, position: name. positionAfterSkippingLeadingTrivia)
154
+ } else {
155
+ return nil
156
+ }
157
+ }
158
+
133
159
static func findNearestSymbol( syntaxTree: SourceFileSyntax , position: AbsolutePosition ) -> DocumentableSymbol ? {
134
- guard let token = syntaxTree. token ( at: position) else {
160
+ let token : TokenSyntax
161
+ if let tokenAtPosition = syntaxTree. token ( at: position) {
162
+ token = tokenAtPosition
163
+ } else if position >= syntaxTree. endPosition, let lastToken = syntaxTree. lastToken ( viewMode: . sourceAccurate) {
164
+ // token(at:) returns nil if position is at the end of the document.
165
+ token = lastToken
166
+ } else if position < syntaxTree. position, let firstToken = syntaxTree. firstToken ( viewMode: . sourceAccurate) {
167
+ // No case in practice where this happens but good to cover anyway
168
+ token = firstToken
169
+ } else {
135
170
return nil
136
171
}
137
- return token. ancestorOrSelf { node in
138
- if let namedDecl = node. asProtocol ( NamedDeclSyntax . self) {
139
- return DocumentableSymbol ( node: namedDecl, position: namedDecl. name. positionAfterSkippingLeadingTrivia)
140
- } else if let initDecl = node. as ( InitializerDeclSyntax . self) {
141
- return DocumentableSymbol ( node: initDecl, position: initDecl. initKeyword. positionAfterSkippingLeadingTrivia)
142
- } else if let functionDecl = node. as ( FunctionDeclSyntax . self) {
143
- return DocumentableSymbol ( node: functionDecl, position: functionDecl. name. positionAfterSkippingLeadingTrivia)
144
- } else if let variableDecl = node. as ( VariableDeclSyntax . self) {
145
- guard let identifier = variableDecl. bindings. only? . pattern. as ( IdentifierPatternSyntax . self) else {
146
- return nil
147
- }
148
- return DocumentableSymbol ( node: variableDecl, position: identifier. positionAfterSkippingLeadingTrivia)
149
- } else if let enumCaseDecl = node. as ( EnumCaseDeclSyntax . self) {
150
- guard let name = enumCaseDecl. elements. only? . name else {
151
- return nil
152
- }
153
- return DocumentableSymbol ( node: enumCaseDecl, position: name. positionAfterSkippingLeadingTrivia)
172
+ // Check if the current token is within a valid documentable symbol
173
+ if let symbol = token. ancestorOrSelf ( mapping: { DocumentableSymbol ( node: $0) } ) {
174
+ return symbol
175
+ }
176
+ // Walk forward through the tokens until we find a documentable symbol
177
+ var previousToken : TokenSyntax ? = token
178
+ while let nextToken = previousToken? . nextToken ( viewMode: . sourceAccurate) {
179
+ if let symbol = nextToken. ancestorOrSelf ( mapping: { DocumentableSymbol ( node: $0) } ) {
180
+ return symbol
154
181
}
155
- return nil
182
+ previousToken = nextToken
183
+ }
184
+ // Walk backwards through the tokens until we find a documentable symbol
185
+ previousToken = token
186
+ while let nextToken = previousToken? . previousToken ( viewMode: . sourceAccurate) {
187
+ if let symbol = nextToken. ancestorOrSelf ( mapping: { DocumentableSymbol ( node: $0) } ) {
188
+ return symbol
189
+ }
190
+ previousToken = nextToken
156
191
}
192
+ // We couldn't find anything
193
+ return nil
157
194
}
158
195
}
159
196
#endif
0 commit comments