Skip to content

Commit ad9b09e

Browse files
authored
Add support for clangd's semantic highlighting (#388)
- Add LSP types for semantic highlighting - Limited to clients which support dynamic registration for semantic highlighting - Requires clangd 11 or later
1 parent 602d5cc commit ad9b09e

17 files changed

+569
-16
lines changed

Sources/LanguageServerProtocol/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ add_library(LanguageServerProtocol
3131
Requests/DefinitionRequest.swift
3232
Requests/DocumentColorRequest.swift
3333
Requests/DocumentHighlightRequest.swift
34+
Requests/DocumentSemanticTokensDeltaRequest.swift
35+
Requests/DocumentSemanticTokensRangeRequest.swift
36+
Requests/DocumentSemanticTokensRequest.swift
3437
Requests/DocumentSymbolRequest.swift
3538
Requests/ExecuteCommandRequest.swift
3639
Requests/FoldingRangeRequest.swift
@@ -46,6 +49,7 @@ add_library(LanguageServerProtocol
4649
Requests/SymbolInfoRequest.swift
4750
Requests/UnregisterCapabilityRequest.swift
4851
Requests/WorkspaceFoldersRequest.swift
52+
Requests/WorkspaceSemanticTokensRefreshRequest.swift
4953
Requests/WorkspaceSymbolsRequest.swift
5054

5155
SupportTypes/CallHierarchyItem.swift
@@ -67,6 +71,7 @@ add_library(LanguageServerProtocol
6771
SupportTypes/MarkupContent.swift
6872
SupportTypes/Position.swift
6973
SupportTypes/RegistrationOptions.swift
74+
SupportTypes/SemanticTokens.swift
7075
SupportTypes/ServerCapabilities.swift
7176
SupportTypes/SKCompletionOptions.swift
7277
SupportTypes/SymbolKind.swift

Sources/LanguageServerProtocol/Messages.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public let builtinRequests: [_RequestType.Type] = [
2121
WorkspaceFoldersRequest.self,
2222
CompletionRequest.self,
2323
HoverRequest.self,
24+
WorkspaceSemanticTokensRefreshRequest.self,
2425
WorkspaceSymbolsRequest.self,
2526
CallHierarchyIncomingCallsRequest.self,
2627
CallHierarchyOutgoingCallsRequest.self,
@@ -31,6 +32,9 @@ public let builtinRequests: [_RequestType.Type] = [
3132
DocumentHighlightRequest.self,
3233
DocumentFormattingRequest.self,
3334
DocumentRangeFormattingRequest.self,
35+
DocumentSemanticTokensDeltaRequest.self,
36+
DocumentSemanticTokensRangeRequest.self,
37+
DocumentSemanticTokensRequest.self,
3438
DocumentOnTypeFormattingRequest.self,
3539
FoldingRangeRequest.self,
3640
DocumentSymbolRequest.self,
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public struct DocumentSemanticTokensDeltaRequest: TextDocumentRequest, Hashable {
14+
public static let method: String = "textDocument/semanticTokens/full/delta"
15+
public typealias Response = DocumentSemanticTokensDeltaResponse?
16+
17+
/// The document to fetch semantic tokens for.
18+
public var textDocument: TextDocumentIdentifier
19+
20+
/// The result identifier of a previous response, which acts as the diff base for the delta.
21+
/// This can either point to a full response or a delta response, depending on what was
22+
/// last received by the client.
23+
public var previousResultId: String
24+
25+
public init(textDocument: TextDocumentIdentifier, previousResultId: String) {
26+
self.textDocument = textDocument
27+
self.previousResultId = previousResultId
28+
}
29+
}
30+
31+
public enum DocumentSemanticTokensDeltaResponse: ResponseType, Codable, Equatable {
32+
case tokens(DocumentSemanticTokensResponse)
33+
case delta(SemanticTokensDelta)
34+
35+
public init(from decoder: Decoder) throws {
36+
let container = try decoder.singleValueContainer()
37+
if let tokens = try? container.decode(DocumentSemanticTokensResponse.self) {
38+
self = .tokens(tokens)
39+
} else if let delta = try? container.decode(SemanticTokensDelta.self) {
40+
self = .delta(delta)
41+
} else {
42+
let error = "DocumentSemanticTokensDeltaResponse has neither SemanticTokens or SemanticTokensDelta."
43+
throw DecodingError.dataCorruptedError(in: container, debugDescription: error)
44+
}
45+
}
46+
47+
public func encode(to encoder: Encoder) throws {
48+
var container = encoder.singleValueContainer()
49+
switch self {
50+
case .tokens(let tokens):
51+
try container.encode(tokens)
52+
case .delta(let delta):
53+
try container.encode(delta)
54+
}
55+
}
56+
}
57+
58+
public struct SemanticTokensDelta: Codable, Hashable {
59+
/// An optional result identifier which enables supporting clients to request semantic token deltas
60+
/// subsequent requests.
61+
public var resultId: String?
62+
63+
/// The edits to transform a previous result into a new result.
64+
public var edits: [SemanticTokensEdit]
65+
66+
public init(resultId: String? = nil, edits: [SemanticTokensEdit]) {
67+
self.resultId = resultId
68+
self.edits = edits
69+
}
70+
}
71+
72+
public struct SemanticTokensEdit: Codable, Hashable {
73+
/// Start offset of the edit.
74+
public var start: Int
75+
76+
/// The number of elements to remove.
77+
public var deleteCount: Int
78+
79+
/// The elements to insert.
80+
public var data: [UInt32]?
81+
82+
public init(start: Int, deleteCount: Int, data: [UInt32]? = nil) {
83+
self.start = start
84+
self.deleteCount = deleteCount
85+
self.data = data
86+
}
87+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public struct DocumentSemanticTokensRangeRequest: TextDocumentRequest, Hashable {
14+
public static let method: String = "textDocument/semanticTokens/range"
15+
public typealias Response = DocumentSemanticTokensResponse?
16+
17+
/// The document to fetch semantic tokens for.
18+
public var textDocument: TextDocumentIdentifier
19+
20+
/// The range to fetch semantic tokens for.
21+
@CustomCodable<PositionRange>
22+
public var range: Range<Position>
23+
24+
public init(textDocument: TextDocumentIdentifier, range: Range<Position>) {
25+
self.textDocument = textDocument
26+
self.range = range
27+
}
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public struct DocumentSemanticTokensRequest: TextDocumentRequest, Hashable {
14+
public static let method: String = "textDocument/semanticTokens/full"
15+
public typealias Response = DocumentSemanticTokensResponse?
16+
17+
/// The document to fetch semantic tokens for.
18+
public var textDocument: TextDocumentIdentifier
19+
20+
public init(textDocument: TextDocumentIdentifier) {
21+
self.textDocument = textDocument
22+
}
23+
}
24+
25+
public struct DocumentSemanticTokensResponse: ResponseType, Hashable {
26+
/// An optional result identifier which enables supporting clients to request semantic token deltas
27+
/// subsequent requests.
28+
public var resultId: String?
29+
30+
/// Raw tokens data.
31+
public var data: [UInt32]
32+
33+
public init(resultId: String? = nil, data: [UInt32]) {
34+
self.resultId = resultId
35+
self.data = data
36+
}
37+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Sent from the server to the client. Servers can use this to ask clients to
14+
/// refresh semantic tokens for editors for which this server provides semantic
15+
/// tokens, useful in cases of project wide configuration changes.
16+
public struct WorkspaceSemanticTokensRefreshRequest: RequestType, Hashable {
17+
public static let method: String = "workspace/semanticTokens/refresh"
18+
public typealias Response = VoidResponse
19+
}

Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,23 @@ public struct WorkspaceClientCapabilities: Hashable, Codable {
8080
}
8181
}
8282

83+
/// Capabilities specific to the `workspace/semanticTokens/refresh` request.
84+
public struct SemanticTokensWorkspace: Hashable, Codable {
85+
86+
/// Whether the client implementation supports a refresh request sent from
87+
/// the server to the client.
88+
///
89+
/// Note that this event is global and will force the client to refresh all
90+
/// semantic tokens currently shown. It should be used with absolute care
91+
/// and is useful for situation where a server, for example, detects a project
92+
/// wide change that requires such a calculation.
93+
public var refreshSupport: Bool?
94+
95+
public init(refreshSupport: Bool? = nil) {
96+
self.refreshSupport = refreshSupport
97+
}
98+
}
99+
83100
// MARK: Properties
84101

85102
/// Whether the client can apply text edits via the `workspace/applyEdit` request.
@@ -103,7 +120,19 @@ public struct WorkspaceClientCapabilities: Hashable, Codable {
103120
/// Whether the client supports the `workspace/configuration` request.
104121
public var configuration: Bool? = nil
105122

106-
public init(applyEdit: Bool? = nil, workspaceEdit: WorkspaceEdit? = nil, didChangeConfiguration: DynamicRegistrationCapability? = nil, didChangeWatchedFiles: DynamicRegistrationCapability? = nil, symbol: Symbol? = nil, executeCommand: DynamicRegistrationCapability? = nil, workspaceFolders: Bool? = nil, configuration: Bool? = nil) {
123+
public var semanticTokens: SemanticTokensWorkspace? = nil
124+
125+
public init(
126+
applyEdit: Bool? = nil,
127+
workspaceEdit: WorkspaceEdit? = nil,
128+
didChangeConfiguration: DynamicRegistrationCapability? = nil,
129+
didChangeWatchedFiles: DynamicRegistrationCapability? = nil,
130+
symbol: Symbol? = nil,
131+
executeCommand: DynamicRegistrationCapability? = nil,
132+
workspaceFolders: Bool? = nil,
133+
configuration: Bool? = nil,
134+
semanticTokens: SemanticTokensWorkspace? = nil
135+
) {
107136
self.applyEdit = applyEdit
108137
self.workspaceEdit = workspaceEdit
109138
self.didChangeConfiguration = didChangeConfiguration
@@ -112,6 +141,7 @@ public struct WorkspaceClientCapabilities: Hashable, Codable {
112141
self.executeCommand = executeCommand
113142
self.workspaceFolders = workspaceFolders
114143
self.configuration = configuration
144+
self.semanticTokens = semanticTokens
115145
}
116146
}
117147

@@ -394,6 +424,81 @@ public struct TextDocumentClientCapabilities: Hashable, Codable {
394424
}
395425
}
396426

427+
public struct SemanticTokensRangeClientCapabilities: Equatable, Hashable, Codable {
428+
// Empty in the LSP 3.16 spec.
429+
public init() {}
430+
}
431+
432+
public struct SemanticTokensFullClientCapabilities: Equatable, Hashable, Codable {
433+
/// The client will also send the `textDocument/semanticTokens/full/delta`
434+
/// request if the server provides a corresponding handler.
435+
public var delta: Bool?
436+
437+
public init(delta: Bool? = nil) {
438+
self.delta = delta
439+
}
440+
}
441+
442+
public struct SemanticTokensRequestsClientCapabilities: Equatable, Hashable, Codable {
443+
/// The client will send the `textDocument/semanticTokens/range` request
444+
/// if the server provides a corresponding handler.
445+
public var range: ValueOrBool<SemanticTokensRangeClientCapabilities>?
446+
447+
/// The client will send the `textDocument/semanticTokens/full` request
448+
/// if the server provides a corresponding handler.
449+
public var full: ValueOrBool<SemanticTokensFullClientCapabilities>?
450+
451+
public init(
452+
range: ValueOrBool<SemanticTokensRangeClientCapabilities>?,
453+
full: ValueOrBool<SemanticTokensFullClientCapabilities>?
454+
) {
455+
self.range = range
456+
self.full = full
457+
}
458+
}
459+
460+
/// Capabilities specific to `textDocument/semanticTokens`.
461+
public struct SemanticTokens: Equatable, Hashable, Codable {
462+
463+
/// Whether the client supports dynamic registration of this request.
464+
public var dynamicRegistration: Bool? = nil
465+
466+
public var requests: SemanticTokensRequestsClientCapabilities
467+
468+
/// The token types that the client supports.
469+
public var tokenTypes: [String]
470+
471+
/// The token modifiers that the client supports.
472+
public var tokenModifiers: [String]
473+
474+
/// The formats the clients supports.
475+
public var formats: [TokenFormat]
476+
477+
/// Whether the client supports tokens that can overlap each other.
478+
public var overlappingTokenSupport: Bool? = nil
479+
480+
/// Whether the client supports tokens that can span multiple lines.
481+
public var multilineTokenSupport: Bool? = nil
482+
483+
public init(
484+
dynamicRegistration: Bool? = nil,
485+
requests: SemanticTokensRequestsClientCapabilities,
486+
tokenTypes: [String],
487+
tokenModifiers: [String],
488+
formats: [TokenFormat],
489+
overlappingTokenSupport: Bool? = nil,
490+
multilineTokenSupport: Bool? = nil
491+
) {
492+
self.dynamicRegistration = dynamicRegistration
493+
self.requests = requests
494+
self.tokenTypes = tokenTypes
495+
self.tokenModifiers = tokenModifiers
496+
self.formats = formats
497+
self.overlappingTokenSupport = overlappingTokenSupport
498+
self.multilineTokenSupport = multilineTokenSupport
499+
}
500+
}
501+
397502
// MARK: Properties
398503

399504
public var synchronization: Synchronization? = nil
@@ -440,6 +545,8 @@ public struct TextDocumentClientCapabilities: Hashable, Codable {
440545

441546
public var callHierarchy: DynamicRegistrationCapability? = nil
442547

548+
public var semanticTokens: SemanticTokens? = nil
549+
443550
public init(synchronization: Synchronization? = nil,
444551
completion: Completion? = nil,
445552
hover: Hover? = nil,
@@ -461,7 +568,8 @@ public struct TextDocumentClientCapabilities: Hashable, Codable {
461568
rename: DynamicRegistrationCapability? = nil,
462569
publishDiagnostics: PublishDiagnostics? = nil,
463570
foldingRange: FoldingRange? = nil,
464-
callHierarchy: DynamicRegistrationCapability? = nil) {
571+
callHierarchy: DynamicRegistrationCapability? = nil,
572+
semanticTokens: SemanticTokens? = nil) {
465573
self.synchronization = synchronization
466574
self.completion = completion
467575
self.hover = hover
@@ -484,5 +592,6 @@ public struct TextDocumentClientCapabilities: Hashable, Codable {
484592
self.publishDiagnostics = publishDiagnostics
485593
self.foldingRange = foldingRange
486594
self.callHierarchy = callHierarchy
595+
self.semanticTokens = semanticTokens
487596
}
488597
}

0 commit comments

Comments
 (0)