Skip to content

Commit da867a1

Browse files
committed
Apply mostly stylistic suggestions regarding tokens
- Make token parser more robust Continue adding line and char deltas from LSP-encoded tokens even if the kind is not recognized. - Move DocumentTokens to its own file - Rename edit callbacks for clarity beforeCallback -> willEditDocument afterCallback -> updateDocumentTokens - Move token start setter into mutating function Since this setter has some subtle semantics, this might be expressed more clearly in a separate method. - Remove CaseIterable conformance from Token.Modifiers Since allCases didn't actually contain all combinations of modifiers, we remove this conformance and rename allCases to allModifiers. - Move SyntaxHighlightingTokenParser into separate file - Update doc comments on SyntaxHighlightingToken Reformat them to distinguish between brief and long documentation. - Add Token.move(lineDelta:utf16indexDelta:) - Remove redundant range emptiness check
1 parent 4bbff3d commit da867a1

File tree

8 files changed

+330
-301
lines changed

8 files changed

+330
-301
lines changed

Sources/SKTestSupport/Array+SyntaxHighlightingToken.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ extension Array where Element == SyntaxHighlightingToken {
2929
let rawKind = rawTokens[i + 3]
3030
let rawModifiers = rawTokens[i + 4]
3131

32-
guard let kind = SyntaxHighlightingToken.Kind(rawValue: rawKind) else { continue }
33-
let modifiers = SyntaxHighlightingToken.Modifiers(rawValue: rawModifiers)
34-
3532
current.line += lineDelta
3633

3734
if lineDelta == 0 {
@@ -40,6 +37,9 @@ extension Array where Element == SyntaxHighlightingToken {
4037
current.utf16index = charDelta
4138
}
4239

40+
guard let kind = SyntaxHighlightingToken.Kind(rawValue: rawKind) else { continue }
41+
let modifiers = SyntaxHighlightingToken.Modifiers(rawValue: rawModifiers)
42+
4343
append(SyntaxHighlightingToken(
4444
start: current,
4545
utf16length: length,

Sources/SourceKitLSP/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ endif()
88
add_library(SourceKitLSP
99
CapabilityRegistry.swift
1010
DocumentManager.swift
11+
DocumentTokens.swift
1112
IndexStoreDB+MainFilesProvider.swift
1213
SourceKitIndexDelegate.swift
1314
SourceKitLSPCommandMetadata.swift
@@ -31,6 +32,7 @@ target_sources(SourceKitLSP PRIVATE
3132
Swift/SwiftCommand.swift
3233
Swift/SwiftLanguageServer.swift
3334
Swift/SyntaxHighlightingToken.swift
35+
Swift/SyntaxHighlightingTokenParser.swift
3436
Swift/VariableTypeInfo.swift)
3537
set_target_properties(SourceKitLSP PROPERTIES
3638
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})

Sources/SourceKitLSP/DocumentManager.swift

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,6 @@ import LanguageServerProtocol
1515
import LSPLogging
1616
import SKSupport
1717

18-
public struct DocumentTokens {
19-
/// Lexical tokens, e.g. keywords, raw identifiers, ...
20-
public var lexical: [SyntaxHighlightingToken] = []
21-
/// Syntactic tokens, e.g. declarations, etc.
22-
public var syntactic: [SyntaxHighlightingToken] = []
23-
/// Semantic tokens, e.g. variable references, type references, ...
24-
public var semantic: [SyntaxHighlightingToken] = []
25-
26-
private var merged: [SyntaxHighlightingToken] {
27-
[lexical, syntactic, semantic].reduce([]) { $0.mergingTokens(with: $1) }
28-
}
29-
public var mergedAndSorted: [SyntaxHighlightingToken] {
30-
merged.sorted { $0.start < $1.start }
31-
}
32-
33-
/// Modifies the syntax highlighting tokens of each kind
34-
/// (lexical, syntactic, semantic) according to `action`.
35-
public mutating func withMutableTokensOfEachKind(_ action: (inout [SyntaxHighlightingToken]) -> Void) {
36-
action(&lexical)
37-
action(&syntactic)
38-
action(&semantic)
39-
}
40-
41-
// Replace all lexical tokens in `range`.
42-
public mutating func replaceLexical(in range: Range<Position>, with newTokens: [SyntaxHighlightingToken]) {
43-
lexical.removeAll { $0.range.overlaps(range) }
44-
lexical += newTokens
45-
}
46-
}
47-
4818
public struct DocumentSnapshot {
4919
public var document: Document
5020
public var version: Int
@@ -146,8 +116,8 @@ public final class DocumentManager {
146116

147117
/// Applies the given edits to the document.
148118
///
149-
/// - parameter beforeCallback: Optional closure to call before each edit.
150-
/// - parameter afterCallback: Optional closure to call after each edit.
119+
/// - parameter willEditDocument: Optional closure to call before each edit.
120+
/// - parameter updateDocumentTokens: Optional closure to call after each edit.
151121
/// - parameter before: The document contents *before* the edit is applied.
152122
/// - parameter after: The document contents *after* the edit is applied.
153123
/// - returns: The contents of the file after all the edits are applied.
@@ -157,16 +127,16 @@ public final class DocumentManager {
157127
_ uri: DocumentURI,
158128
newVersion: Int,
159129
edits: [TextDocumentContentChangeEvent],
160-
beforeCallback: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil,
161-
afterCallback: ((_ after: DocumentSnapshot) -> DocumentTokens?)? = nil
130+
willEditDocument: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil,
131+
updateDocumentTokens: ((_ after: DocumentSnapshot) -> DocumentTokens?)? = nil
162132
) throws -> DocumentSnapshot {
163133
return try queue.sync {
164134
guard let document = documents[uri] else {
165135
throw Error.missingDocument(uri)
166136
}
167137

168138
for edit in edits {
169-
if let f = beforeCallback {
139+
if let f = willEditDocument {
170140
f(document.latestSnapshot, edit)
171141
}
172142

@@ -199,10 +169,9 @@ public final class DocumentManager {
199169
var token = $0
200170
if token.start.line == range.upperBound.line
201171
&& token.start.utf16index >= range.upperBound.utf16index {
202-
token.start.utf16index += lastLineLengthDelta
203-
token.start.line += lineDelta
172+
token.move(lineDelta: lineDelta, utf16indexDelta: lastLineLengthDelta)
204173
} else if token.start.line > range.upperBound.line {
205-
token.start.line += lineDelta
174+
token.move(lineDelta: lineDelta)
206175
}
207176
return token
208177
})
@@ -213,7 +182,7 @@ public final class DocumentManager {
213182
document.latestTokens = DocumentTokens()
214183
}
215184

216-
if let f = afterCallback, let tokens = f(document.latestSnapshot) {
185+
if let f = updateDocumentTokens, let tokens = f(document.latestSnapshot) {
217186
document.latestTokens = tokens
218187
}
219188
}
@@ -270,21 +239,21 @@ extension DocumentManager {
270239
}
271240
}
272241

273-
/// Convenience wrapper for `edit(_:newVersion:edits:beforeCallback:afterCallback:)`
242+
/// Convenience wrapper for `edit(_:newVersion:edits:willEditDocument:updateDocumentTokens:)`
274243
/// that logs on failure.
275244
@discardableResult
276245
func edit(
277246
_ note: DidChangeTextDocumentNotification,
278-
beforeCallback: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil,
279-
afterCallback: ((_ after: DocumentSnapshot) -> DocumentTokens?)? = nil
247+
willEditDocument: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil,
248+
updateDocumentTokens: ((_ after: DocumentSnapshot) -> DocumentTokens?)? = nil
280249
) -> DocumentSnapshot? {
281250
return orLog("failed to edit document", level: .error) {
282251
try edit(
283252
note.textDocument.uri,
284253
newVersion: note.textDocument.version ?? -1,
285254
edits: note.contentChanges,
286-
beforeCallback: beforeCallback,
287-
afterCallback: afterCallback
255+
willEditDocument: willEditDocument,
256+
updateDocumentTokens: updateDocumentTokens
288257
)
289258
}
290259
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
import LanguageServerProtocol
14+
15+
/// Syntax highlighting tokens for a particular document.
16+
public struct DocumentTokens {
17+
/// Lexical tokens, e.g. keywords, raw identifiers, ...
18+
public var lexical: [SyntaxHighlightingToken] = []
19+
/// Syntactic tokens, e.g. declarations, etc.
20+
public var syntactic: [SyntaxHighlightingToken] = []
21+
/// Semantic tokens, e.g. variable references, type references, ...
22+
public var semantic: [SyntaxHighlightingToken] = []
23+
24+
private var merged: [SyntaxHighlightingToken] {
25+
[lexical, syntactic, semantic].reduce([]) { $0.mergingTokens(with: $1) }
26+
}
27+
public var mergedAndSorted: [SyntaxHighlightingToken] {
28+
merged.sorted { $0.start < $1.start }
29+
}
30+
31+
/// Modifies the syntax highlighting tokens of each kind
32+
/// (lexical, syntactic, semantic) according to `action`.
33+
public mutating func withMutableTokensOfEachKind(_ action: (inout [SyntaxHighlightingToken]) -> Void) {
34+
action(&lexical)
35+
action(&syntactic)
36+
action(&semantic)
37+
}
38+
39+
// Replace all lexical tokens in `range`.
40+
public mutating func replaceLexical(in range: Range<Position>, with newTokens: [SyntaxHighlightingToken]) {
41+
lexical.removeAll { $0.range.overlaps(range) }
42+
lexical += newTokens
43+
}
44+
}

Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,13 @@ public final class SwiftLanguageServer: ToolchainLanguageServer {
190190
let length: Int = response[keys.length],
191191
let start: Position = snapshot.positionOf(utf8Offset: offset),
192192
let end: Position = snapshot.positionOf(utf8Offset: offset + length) else {
193-
log("updateLexicalAndSyntacticTokens failed, no range found", level: .error)
193+
// This e.g. happens in the case of empty edits
194+
log("did not update lexical/syntactic tokens, no range found", level: .warning)
194195
return docTokens
195196
}
196197

197198
let range = start..<end
198199

199-
// If the range is empty we don't have to (and shouldn't) update anything.
200-
// This is important, since the substructure may be empty, causing us to
201-
// unnecessarily remove all syntactic tokens.
202-
guard !range.isEmpty else {
203-
return docTokens
204-
}
205-
206200
if let syntaxMap: SKDResponseArray = response[keys.syntaxmap] {
207201
let tokenParser = SyntaxHighlightingTokenParser(sourcekitd: sourcekitd)
208202
let tokens = tokenParser.parseTokens(syntaxMap, in: snapshot)
@@ -365,7 +359,7 @@ extension SwiftLanguageServer {
365359
semanticTokensProvider: SemanticTokensOptions(
366360
legend: SemanticTokensLegend(
367361
tokenTypes: SyntaxHighlightingToken.Kind.allCases.map(\.lspName),
368-
tokenModifiers: SyntaxHighlightingToken.Modifiers.allCases.map { $0.lspName! }),
362+
tokenModifiers: SyntaxHighlightingToken.Modifiers.allModifiers.map { $0.lspName! }),
369363
range: .bool(true),
370364
full: .bool(true))
371365
))
@@ -536,7 +530,7 @@ extension SwiftLanguageServer {
536530

537531
req[keys.sourcetext] = edit.text
538532
lastResponse = try? self.sourcekitd.sendSync(req)
539-
} afterCallback: { (after: DocumentSnapshot) in
533+
} updateDocumentTokens: { (after: DocumentSnapshot) in
540534
if let dict = lastResponse {
541535
return self.updatedLexicalAndSyntacticTokens(response: dict, for: after)
542536
} else {

0 commit comments

Comments
 (0)