Send workspace/semanticTokens/refresh to client when build settings have changed

Similar to how we send a `workspace/diagnostic/refresh` request to the client when we get new build settings, we need to send a `workspace/semanticTokens/refresh` to the client to reload semantic tokens when builds settings change. This is particularly important so that we reload semantic tokens from real build setting after a SwiftPM package has finished loading (we previously used fallback settings).

Fixes #2141
rdar://150934682
This commit is contained in:
Alex Hoppen
2025-05-09 13:33:35 +02:00
parent 276ac80585
commit 1bc7d85dbd

View File

@@ -183,14 +183,14 @@ package actor SwiftLanguageService: LanguageService, Sendable {
/// `buildSettings(for:)` returns `nil` are not included.
private var buildSettingsForOpenFiles: [DocumentURI: SwiftCompileCommand] = [:]
/// Calling `scheduleCall` on `refreshDiagnosticsDebouncer` schedules a `DiagnosticsRefreshRequest` to be sent to
/// to the client.
/// Calling `scheduleCall` on `refreshDiagnosticsAndSemanticTokensDebouncer` schedules a `DiagnosticsRefreshRequest`
/// and `WorkspaceSemanticTokensRefreshRequest` to be sent to to the client.
///
/// We debounce these calls because the `DiagnosticsRefreshRequest` is a workspace-wide request. If we discover that
/// the client should update diagnostics for file A and then discover that it should also update diagnostics for file
/// B, we don't want to send two `DiagnosticsRefreshRequest`s. Instead, the two should be unified into a single
/// request.
private let refreshDiagnosticsDebouncer: Debouncer<Void>
private let refreshDiagnosticsAndSemanticTokensDebouncer: Debouncer<Void>
/// Creates a language server for the given client using the sourcekitd dylib specified in `toolchain`.
/// `reopenDocuments` is a closure that will be called if sourcekitd crashes and the `SwiftLanguageService` asks its
@@ -229,19 +229,29 @@ package actor SwiftLanguageService: LanguageService, Sendable {
self.options = options
// The debounce duration of 500ms was chosen arbitrarily without scientific research.
self.refreshDiagnosticsDebouncer = Debouncer(debounceDuration: .milliseconds(500)) { [weak sourceKitLSPServer] in
self.refreshDiagnosticsAndSemanticTokensDebouncer = Debouncer(debounceDuration: .milliseconds(500)) {
[weak sourceKitLSPServer] in
guard let sourceKitLSPServer else {
logger.fault("Not sending DiagnosticRefreshRequest to client because sourceKitLSPServer has been deallocated")
logger.fault(
"Not sending diagnostic and semantic token refresh request to client because sourceKitLSPServer has been deallocated"
)
return
}
guard
await sourceKitLSPServer.capabilityRegistry?.clientCapabilities.workspace?.diagnostics?.refreshSupport ?? false
else {
let clientCapabilities = await sourceKitLSPServer.capabilityRegistry?.clientCapabilities
if clientCapabilities?.workspace?.diagnostics?.refreshSupport ?? false {
_ = await orLog("Sending DiagnosticRefreshRequest to client after document dependencies updated") {
try await sourceKitLSPServer.sendRequestToClient(DiagnosticsRefreshRequest())
}
} else {
logger.debug("Not sending DiagnosticRefreshRequest because the client doesn't support it")
return
}
_ = await orLog("Sending DiagnosticRefreshRequest to client after document dependencies updated") {
try await sourceKitLSPServer.sendRequestToClient(DiagnosticsRefreshRequest())
if clientCapabilities?.workspace?.semanticTokens?.refreshSupport ?? false {
_ = await orLog("Sending WorkspaceSemanticTokensRefreshRequest to client after document dependencies updated") {
try await sourceKitLSPServer.sendRequestToClient(WorkspaceSemanticTokensRefreshRequest())
}
} else {
logger.debug("Not sending WorkspaceSemanticTokensRefreshRequest because the client doesn't support it")
}
}
@@ -336,7 +346,7 @@ package actor SwiftLanguageService: LanguageService, Sendable {
await sourceKitLSPServer.sourcekitdCrashedWorkDoneProgress.end()
// We can provide diagnostics again now. Send a diagnostic refresh request to prompt the editor to reload
// diagnostics.
await refreshDiagnosticsDebouncer.scheduleCall()
await refreshDiagnosticsAndSemanticTokensDebouncer.scheduleCall()
case (.connected, .connected),
(.connectionInterrupted, .connectionInterrupted),
(.connectionInterrupted, .semanticFunctionalityDisabled),
@@ -468,7 +478,7 @@ extension SwiftLanguageService {
}
if await capabilityRegistry.clientSupportsPullDiagnostics(for: .swift) {
await self.refreshDiagnosticsDebouncer.scheduleCall()
await self.refreshDiagnosticsAndSemanticTokensDebouncer.scheduleCall()
} else {
await publishDiagnosticsIfNeeded(for: snapshot.uri)
}