@@ -41,22 +41,26 @@ fileprivate extension SymbolOccurrence {
41
41
/// Find the innermost range of a document symbol that contains the given position.
42
42
private func findInnermostSymbolRange(
43
43
containing position: Position ,
44
- documentSymbols documentSymbolsResponse: DocumentSymbolResponse
44
+ documentSymbolsResponse: DocumentSymbolResponse
45
45
) -> Range < Position > ? {
46
- guard case . documentSymbols( let documentSymbols) = documentSymbolsResponse else {
47
- // Both `ClangLanguageService` and `SwiftLanguageService` return `documentSymbols` so we don't need to handle the
48
- // .symbolInformation case.
49
- logger. fault (
50
- """
51
- Expected documentSymbols response from language service to resolve test ranges but got \
52
- \( documentSymbolsResponse. forLogging)
53
- """
54
- )
55
- return nil
46
+ switch documentSymbolsResponse {
47
+ case . documentSymbols( let documentSymbols) :
48
+ return findInnermostSymbolRange ( containing: position, documentSymbols: documentSymbols)
49
+ case . symbolInformation( let symbolInformation) :
50
+ return findInnermostSymbolRange ( containing: position, symbolInformation: symbolInformation)
56
51
}
52
+ }
53
+
54
+ private func findInnermostSymbolRange(
55
+ containing position: Position ,
56
+ documentSymbols: [ DocumentSymbol ]
57
+ ) -> Range < Position > ? {
57
58
for documentSymbol in documentSymbols where documentSymbol. range. contains ( position) {
58
59
if let children = documentSymbol. children,
59
- let rangeOfChild = findInnermostSymbolRange ( containing: position, documentSymbols: . documentSymbols( children) )
60
+ let rangeOfChild = findInnermostSymbolRange (
61
+ containing: position,
62
+ documentSymbolsResponse: . documentSymbols( children)
63
+ )
60
64
{
61
65
// If a child contains the position, prefer that because it's more specific.
62
66
return rangeOfChild
@@ -66,6 +70,21 @@ private func findInnermostSymbolRange(
66
70
return nil
67
71
}
68
72
73
+ /// Return the smallest range in `symbolInformation` containing `position`.
74
+ private func findInnermostSymbolRange(
75
+ containing position: Position ,
76
+ symbolInformation symbolInformationArray: [ SymbolInformation ]
77
+ ) -> Range < Position > ? {
78
+ var bestRange : Range < Position > ? = nil
79
+ for symbolInformation in symbolInformationArray where symbolInformation. location. range. contains ( position) {
80
+ let range = symbolInformation. location. range
81
+ if bestRange == nil || ( bestRange!. lowerBound < range. lowerBound && range. upperBound < bestRange!. upperBound) {
82
+ bestRange = range
83
+ }
84
+ }
85
+ return bestRange
86
+ }
87
+
69
88
extension SourceKitLSPServer {
70
89
/// Converts a flat list of test symbol occurrences to a hierarchical `TestItem` array, inferring the hierarchical
71
90
/// structure from `childOf` relations between the symbol occurrences.
@@ -263,9 +282,15 @@ extension SourceKitLSPServer {
263
282
264
283
let syntacticTests = try await languageService. syntacticDocumentTests ( for: req. textDocument. uri, in: workspace)
265
284
266
- if let index = workspace. index ( checkedFor: . inMemoryModifiedFiles( documentManager) ) {
285
+ // We `syntacticDocumentTests` returns `nil`, it indicates that it doesn't support syntactic test discovery.
286
+ // In that case, the semantic index is the only source of tests we have and we thus want to show tests from the
287
+ // semantic index, even if they are out-of-date. The alternative would be showing now tests after an edit to a file.
288
+ let indexCheckLevel : IndexCheckLevel =
289
+ syntacticTests == nil ? . deletedFiles : . inMemoryModifiedFiles( documentManager)
290
+
291
+ if let index = workspace. index ( checkedFor: indexCheckLevel) {
267
292
var syntacticSwiftTestingTests : [ TestItem ] {
268
- syntacticTests. filter { $0. style == TestStyle . swiftTesting }
293
+ syntacticTests? . filter { $0. style == TestStyle . swiftTesting } ?? [ ]
269
294
}
270
295
271
296
let testSymbols =
@@ -283,7 +308,7 @@ extension SourceKitLSPServer {
283
308
for: testSymbols,
284
309
resolveLocation: { uri, position in
285
310
if uri == snapshot. uri, let documentSymbols,
286
- let range = findInnermostSymbolRange ( containing: position, documentSymbols : documentSymbols)
311
+ let range = findInnermostSymbolRange ( containing: position, documentSymbolsResponse : documentSymbols)
287
312
{
288
313
return Location ( uri: uri, range: range)
289
314
}
@@ -298,7 +323,7 @@ extension SourceKitLSPServer {
298
323
}
299
324
}
300
325
// We don't have any up-to-date semantic index entries for this file. Syntactically look for tests.
301
- return syntacticTests
326
+ return syntacticTests ?? [ ]
302
327
}
303
328
}
304
329
@@ -437,7 +462,7 @@ extension TestItem {
437
462
}
438
463
439
464
extension SwiftLanguageService {
440
- public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async throws -> [ TestItem ] {
465
+ public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async throws -> [ TestItem ] ? {
441
466
let snapshot = try documentManager. latestSnapshot ( uri)
442
467
let semanticSymbols = workspace. index ( checkedFor: . deletedFiles) ? . symbols ( inFilePath: snapshot. uri. pseudoPath)
443
468
let xctestSymbols = await SyntacticSwiftXCTestScanner . findTestSymbols (
@@ -453,7 +478,7 @@ extension SwiftLanguageService {
453
478
}
454
479
455
480
extension ClangLanguageService {
456
- public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async -> [ TestItem ] {
457
- return [ ]
481
+ public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async -> [ TestItem ] ? {
482
+ return nil
458
483
}
459
484
}
0 commit comments