Skip to content

Commit 0505ece

Browse files
committed
Only provide semantic tokens if capability is registered
1 parent f541147 commit 0505ece

File tree

4 files changed

+60
-9
lines changed

4 files changed

+60
-9
lines changed

Sources/SourceKitLSP/CapabilityRegistry.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ public final class CapabilityRegistry {
142142
}
143143
}
144144

145+
/// Checks whether a registration for semantic tokens for the given languages exists.
146+
public func hasSemanticTokensRegistration(for languages: [Language]) -> Bool {
147+
registration(for: languages, in: semanticTokens) != nil
148+
}
149+
145150
private func documentSelector(for langauges: [Language]) -> DocumentSelector {
146151
return DocumentSelector(langauges.map { DocumentFilter(language: $0.rawValue) })
147152
}

Sources/SourceKitLSP/SourceKitServer.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -855,28 +855,49 @@ extension SourceKitServer {
855855
languageService.documentColor(req)
856856
}
857857

858+
private func supportsSemanticTokens(in uri: DocumentURI) -> Bool {
859+
guard let workspace = workspace,
860+
let snapshot = workspace.documentManager.latestSnapshot(uri) else {
861+
return false
862+
}
863+
let language = snapshot.document.language
864+
return workspace.capabilityRegistry.hasSemanticTokensRegistration(for: [language])
865+
}
866+
858867
func documentSemanticTokens(
859868
_ req: Request<DocumentSemanticTokensRequest>,
860869
workspace: Workspace,
861870
languageService: ToolchainLanguageServer
862871
) {
863-
languageService.documentSemanticTokens(req)
872+
if supportsSemanticTokens(in: req.params.textDocument.uri) {
873+
languageService.documentSemanticTokens(req)
874+
} else {
875+
req.reply(DocumentSemanticTokensResponse(data: []))
876+
}
864877
}
865878

866879
func documentSemanticTokensDelta(
867880
_ req: Request<DocumentSemanticTokensDeltaRequest>,
868881
workspace: Workspace,
869882
languageService: ToolchainLanguageServer
870883
) {
871-
languageService.documentSemanticTokensDelta(req)
884+
if supportsSemanticTokens(in: req.params.textDocument.uri) {
885+
languageService.documentSemanticTokensDelta(req)
886+
} else {
887+
req.reply(.tokens(.init(data: [])))
888+
}
872889
}
873890

874891
func documentSemanticTokensRange(
875892
_ req: Request<DocumentSemanticTokensRangeRequest>,
876893
workspace: Workspace,
877894
languageService: ToolchainLanguageServer
878895
) {
879-
languageService.documentSemanticTokensRange(req)
896+
if supportsSemanticTokens(in: req.params.textDocument.uri) {
897+
languageService.documentSemanticTokensRange(req)
898+
} else {
899+
req.reply(DocumentSemanticTokensResponse(data: []))
900+
}
880901
}
881902

882903
func colorPresentation(

Sources/SourceKitLSP/Swift/SyntaxHighlightingToken.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public struct SyntaxHighlightingToken: Hashable {
104104
case `operator`
105105

106106
/// The name of the token type used by LSP.
107-
var lspName: String {
107+
/// **Public for testing.**
108+
public var lspName: String {
108109
switch self {
109110
case .namespace: return "namespace"
110111
case .type: return "type"
@@ -168,6 +169,7 @@ public struct SyntaxHighlightingToken: Hashable {
168169
/// The name of the modifier used by LSP, if this
169170
/// is a single modifier. Note that every modifier
170171
/// in `allCases` must have an associated `lspName`.
172+
/// **Public for testing.**
171173
public var lspName: String? {
172174
switch self {
173175
case .declaration: return "declaration"

Tests/SourceKitLSPTests/SemanticTokensTests.swift

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,18 @@ final class SemanticTokensTests: XCTestCase {
4444
refreshSupport: true
4545
)
4646
),
47-
textDocument: nil
47+
textDocument: .init(
48+
semanticTokens: .init(
49+
dynamicRegistration: true,
50+
requests: .init(
51+
range: .bool(true),
52+
full: .bool(true)
53+
),
54+
tokenTypes: Token.Kind.allCases.map(\.lspName),
55+
tokenModifiers: Token.Modifiers.allCases.map { $0.lspName! },
56+
formats: [.relative]
57+
)
58+
)
4859
),
4960
trace: .off,
5061
workspaceFolders: nil
@@ -54,12 +65,24 @@ final class SemanticTokensTests: XCTestCase {
5465
private func performSemanticTokensRequest(text: String, range: Range<Position>? = nil) -> [Token] {
5566
let url = URL(fileURLWithPath: "/\(#function)/a.swift")
5667

57-
// We wait for the first refresh request to make sure that the semantic tokens are ready
68+
// We will wait for the server to dynamically register semantic tokens
69+
70+
let registerCapabilityExpectation = expectation(description: "performSemanticTokensRequest - register semantic tokens capability")
71+
sk.appendOneShotRequestHandler { (req: Request<RegisterCapabilityRequest>) in
72+
let registrations = req.params.registrations
73+
XCTAssert(registrations.contains { reg in
74+
reg.method == SemanticTokensRegistrationOptions.method
75+
})
76+
req.reply(VoidResponse())
77+
registerCapabilityExpectation.fulfill()
78+
}
79+
80+
// We will wait for the first refresh request to make sure that the semantic tokens are ready
5881

5982
let refreshExpectation = expectation(description: "performSemanticTokensRequest - refresh received")
60-
sk.handleNextRequest { (req: Request<WorkspaceSemanticTokensRefreshRequest>) in
61-
refreshExpectation.fulfill()
83+
sk.appendOneShotRequestHandler { (req: Request<WorkspaceSemanticTokensRefreshRequest>) in
6284
req.reply(VoidResponse())
85+
refreshExpectation.fulfill()
6386
}
6487

6588
sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem(
@@ -69,7 +92,7 @@ final class SemanticTokensTests: XCTestCase {
6992
text: text
7093
)))
7194

72-
wait(for: [refreshExpectation], timeout: 15)
95+
wait(for: [registerCapabilityExpectation, refreshExpectation], timeout: 15)
7396

7497
let textDocument = TextDocumentIdentifier(url)
7598
let response: DocumentSemanticTokensResponse!

0 commit comments

Comments
 (0)