@@ -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.
@@ -265,9 +284,15 @@ extension SourceKitLSPServer {
265
284
266
285
let syntacticTests = try await languageService. syntacticDocumentTests ( for: req. textDocument. uri, in: workspace)
267
286
268
- if let index = workspace. index ( checkedFor: . imMemoryModifiedFiles( documentManager) ) {
287
+ // We `syntacticDocumentTests` returns `nil`, it indicates that it doesn't support syntactic test discovery.
288
+ // In that case, the semantic index is the only source of tests we have and we thus want to show tests from the
289
+ // semantic index, even if they are out-of-date. The alternative would be showing now tests after an edit to a file.
290
+ let indexCheckLevel : IndexCheckLevel =
291
+ syntacticTests == nil ? . deletedFiles : . imMemoryModifiedFiles( documentManager)
292
+
293
+ if let index = workspace. index ( checkedFor: indexCheckLevel) {
269
294
var syntacticSwiftTestingTests : [ TestItem ] {
270
- syntacticTests. filter { $0. style == TestStyle . swiftTesting }
295
+ syntacticTests? . filter { $0. style == TestStyle . swiftTesting } ?? [ ]
271
296
}
272
297
273
298
let testSymbols =
@@ -285,7 +310,7 @@ extension SourceKitLSPServer {
285
310
for: testSymbols,
286
311
resolveLocation: { uri, position in
287
312
if uri == snapshot. uri, let documentSymbols,
288
- let range = findInnermostSymbolRange ( containing: position, documentSymbols : documentSymbols)
313
+ let range = findInnermostSymbolRange ( containing: position, documentSymbolsResponse : documentSymbols)
289
314
{
290
315
return Location ( uri: uri, range: range)
291
316
}
@@ -300,7 +325,7 @@ extension SourceKitLSPServer {
300
325
}
301
326
}
302
327
// We don't have any up-to-date semantic index entries for this file. Syntactically look for tests.
303
- return syntacticTests
328
+ return syntacticTests ?? [ ]
304
329
}
305
330
}
306
331
@@ -439,7 +464,7 @@ extension TestItem {
439
464
}
440
465
441
466
extension SwiftLanguageService {
442
- public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async throws -> [ TestItem ] {
467
+ public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async throws -> [ TestItem ] ? {
443
468
let snapshot = try documentManager. latestSnapshot ( uri)
444
469
let semanticSymbols = workspace. index ( checkedFor: . deletedFiles) ? . symbols ( inFilePath: snapshot. uri. pseudoPath)
445
470
let xctestSymbols = await SyntacticSwiftXCTestScanner . findTestSymbols (
@@ -455,7 +480,7 @@ extension SwiftLanguageService {
455
480
}
456
481
457
482
extension ClangLanguageService {
458
- public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async -> [ TestItem ] {
459
- return [ ]
483
+ public func syntacticDocumentTests( for uri: DocumentURI , in workspace: Workspace ) async -> [ TestItem ] ? {
484
+ return nil
460
485
}
461
486
}
0 commit comments