Skip to content

Commit 7cb589f

Browse files
committed
Use batch soucekit request
1 parent 28ea629 commit 7cb589f

2 files changed

Lines changed: 91 additions & 36 deletions

File tree

Sources/SourceKitD/sourcekitd_uids.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,8 @@ package struct sourcekitd_api_requests {
886886
package let collectExpressionType: sourcekitd_api_uid_t
887887
/// `source.request.variable.type`
888888
package let collectVariableType: sourcekitd_api_uid_t
889+
/// `source.request.declaration.usr`
890+
package let collectDeclarationUSR: sourcekitd_api_uid_t
889891
/// `source.request.configuration.global`
890892
package let globalConfiguration: sourcekitd_api_uid_t
891893
/// `source.request.dependency_updated`
@@ -955,6 +957,7 @@ package struct sourcekitd_api_requests {
955957
testNotification = api.uid_get_from_cstr("source.request.test_notification")!
956958
collectExpressionType = api.uid_get_from_cstr("source.request.expression.type")!
957959
collectVariableType = api.uid_get_from_cstr("source.request.variable.type")!
960+
collectDeclarationUSR = api.uid_get_from_cstr("source.request.declaration.usr")!
958961
globalConfiguration = api.uid_get_from_cstr("source.request.configuration.global")!
959962
dependencyUpdated = api.uid_get_from_cstr("source.request.dependency_updated")!
960963
diagnostics = api.uid_get_from_cstr("source.request.diagnostics")!

Sources/SwiftLanguageService/SwiftCodeLensScanner.swift

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal import IndexStoreDB
1616
@_spi(SourceKitLSP) import LanguageServerProtocol
1717
@_spi(SourceKitLSP) import SKLogging
1818
import SemanticIndex
19+
import SourceKitD
1920
import SourceKitLSP
2021
import SwiftSyntax
2122
import ToolchainRegistry
@@ -32,7 +33,7 @@ final class SwiftCodeLensScanner: SyntaxVisitor {
3233
/// The display name of the build target containing this document, if available.
3334
private let targetName: String?
3435

35-
/// The language service used to resolve cursor info for symbols.
36+
/// The language service used to resolve symbol metadata for code lenses.
3637
private let languageService: SwiftLanguageService
3738

3839
/// The map of supported commands and their client side command names.
@@ -86,10 +87,7 @@ final class SwiftCodeLensScanner: SyntaxVisitor {
8687
let syntaxTree = await syntaxTreeManager.syntaxTree(for: snapshot)
8788
visitor.walk(syntaxTree)
8889

89-
// Process collected symbols asynchronously for reference counts
90-
for (nameToken, displayRange) in visitor.symbolsToProcess {
91-
await visitor.captureReferenceLens(for: nameToken, at: displayRange)
92-
}
90+
await visitor.captureReferenceLenses()
9391

9492
var codeLenses = visitor.result
9593

@@ -197,45 +195,51 @@ final class SwiftCodeLensScanner: SyntaxVisitor {
197195
}
198196
}
199197

200-
/// Queries the index for the number of references to a symbol and appends a code lens with the count.
201-
private func captureReferenceLens(for nameToken: TokenSyntax, at displayRange: Range<AbsolutePosition>) async {
202-
guard let referencesCommand = supportedCommands[.references] else { return }
203-
204-
let lensRange = snapshot.absolutePositionRange(of: displayRange)
205-
let nameRange = snapshot.absolutePositionRange(of: nameToken.trimmedRange)
198+
/// Queries sourcekitd once for declaration USRs, then looks up reference counts in the index.
199+
private func captureReferenceLenses() async {
200+
guard let referencesCommand = supportedCommands[.references],
201+
let index = await workspace?.index(checkedFor: .deletedFiles)
202+
else {
203+
return
204+
}
206205

207206
do {
208-
let cursorInfoResults = try await languageService.cursorInfo(
207+
let declarationUsrs = try await languageService.declarationUSRs(
209208
snapshot,
210-
compileCommand: await languageService.compileCommand(for: snapshot.uri, fallbackAfterTimeout: false),
211-
nameRange
209+
compileCommand: await languageService.compileCommand(for: snapshot.uri, fallbackAfterTimeout: false)
210+
)
211+
let usrsByOffset = Dictionary(
212+
declarationUsrs.map { ($0.offset, $0.usr) },
213+
uniquingKeysWith: { first, _ in first }
212214
)
213-
.cursorInfo
214-
215-
guard let cursorInfo = cursorInfoResults.first,
216-
let usr = cursorInfo.symbolInfo.usr,
217-
let index = await workspace?.index(checkedFor: .deletedFiles)
218-
else { return }
219-
220-
var referenceCount = 0
221-
index.forEachSymbolOccurrence(byUSR: usr, roles: .reference) { _ in
222-
referenceCount += 1
223-
return true
224-
}
225215

226-
let title = "\(referenceCount) reference\(referenceCount == 1 ? "" : "s")"
227-
result.append(
228-
CodeLens(
229-
range: lensRange,
230-
command: Command(
231-
title: title,
232-
command: referencesCommand,
233-
arguments: [.string(snapshot.uri.stringValue), nameRange.lowerBound.encodeToLSPAny()]
216+
for (nameToken, displayRange) in symbolsToProcess {
217+
guard let usr = usrsByOffset[nameToken.trimmedRange.lowerBound.utf8Offset] else {
218+
continue
219+
}
220+
221+
var referenceCount = 0
222+
try index.forEachSymbolOccurrence(byUSR: usr, roles: .reference) { _ in
223+
referenceCount += 1
224+
return true
225+
}
226+
227+
let lensRange = snapshot.absolutePositionRange(of: displayRange)
228+
let nameRange = snapshot.absolutePositionRange(of: nameToken.trimmedRange)
229+
let title = "\(referenceCount) reference\(referenceCount == 1 ? "" : "s")"
230+
result.append(
231+
CodeLens(
232+
range: lensRange,
233+
command: Command(
234+
title: title,
235+
command: referencesCommand,
236+
arguments: [.string(snapshot.uri.stringValue), nameRange.lowerBound.encodeToLSPAny()]
237+
)
234238
)
235239
)
236-
)
240+
}
237241
} catch {
238-
logger.info("Failed to get cursor info for reference count: \(error.forLogging, privacy: .public)")
242+
logger.info("Failed to get declaration USRs for reference count: \(error.forLogging, privacy: .public)")
239243
}
240244
}
241245

@@ -284,3 +288,51 @@ final class SwiftCodeLensScanner: SyntaxVisitor {
284288
}
285289
}
286290
}
291+
292+
private struct DeclarationUSRInfo {
293+
let offset: Int
294+
let usr: String
295+
}
296+
297+
extension SwiftLanguageService {
298+
fileprivate func declarationUSRs(
299+
_ snapshot: DocumentSnapshot,
300+
compileCommand: SwiftCompileCommand?,
301+
_ range: Range<Position>? = nil
302+
) async throws -> [DeclarationUSRInfo] {
303+
let skreq = sourcekitd.dictionary([
304+
keys.cancelOnSubsequentRequest: 0,
305+
keys.filePath: snapshot.uri.sourcekitdSourceFile,
306+
keys.compilerArgs: compileCommand?.compilerArgs as [any SKDRequestValue]?,
307+
])
308+
309+
if let range {
310+
let start = snapshot.utf8Offset(of: range.lowerBound)
311+
let end = snapshot.utf8Offset(of: range.upperBound)
312+
skreq.set(keys.offset, to: start)
313+
skreq.set(keys.length, to: end - start)
314+
}
315+
316+
let dict = try await send(sourcekitdRequest: \.collectDeclarationUSR, skreq, snapshot: snapshot)
317+
guard let declarations: SKDResponseArray = dict[keys.declarations] else {
318+
return []
319+
}
320+
321+
var result: [DeclarationUSRInfo] = []
322+
result.reserveCapacity(declarations.count)
323+
324+
// swift-format-ignore: ReplaceForEachWithForLoop
325+
declarations.forEach { (_, declaration) -> Bool in
326+
guard let offset: Int = declaration[keys.offset],
327+
let usr: String = declaration[keys.usr]
328+
else {
329+
assertionFailure("DeclarationUSRInfo failed to deserialize")
330+
return true
331+
}
332+
result.append(DeclarationUSRInfo(offset: offset, usr: usr))
333+
return true
334+
}
335+
336+
return result
337+
}
338+
}

0 commit comments

Comments
 (0)