Skip to content

Commit 315fff8

Browse files
committed
feat: add SyntaxHighlightingTokens wrapper struct
1 parent 7ee04d1 commit 315fff8

File tree

8 files changed

+94
-54
lines changed

8 files changed

+94
-54
lines changed

Sources/SKTestSupport/Array+SyntaxHighlightingToken.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import LanguageServerProtocol
1414
import SourceKitLSP
1515

16+
// TODO: This part is deprecated, Should i remove it?
1617
extension Array where Element == SyntaxHighlightingToken {
1718
/// Decodes the LSP representation of syntax highlighting tokens
1819
public init(lspEncodedTokens rawTokens: [UInt32]) {

Sources/SKTestSupport/SkipUnless.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ public enum SkipUnless {
106106
let response = try unwrap(
107107
await testClient.send(DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri)))
108108
)
109+
110+
// TODO: This part is not directly related, should i change it to new struct?
109111
let tokens = [SyntaxHighlightingToken](lspEncodedTokens: response.data)
110112

111113
// If we don't have semantic token support in sourcekitd, the second token is an identifier based on the syntax

Sources/SourceKitLSP/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ target_sources(SourceKitLSP PRIVATE
3939
Swift/SwiftLanguageService.swift
4040
Swift/SymbolInfo.swift
4141
Swift/SyntaxHighlightingToken.swift
42+
Swift/SyntaxHighlightingTokens.swift
4243
Swift/SyntaxHighlightingTokenParser.swift
4344
Swift/SyntaxTreeManager.swift
4445
Swift/VariableTypeInfo.swift

Sources/SourceKitLSP/Swift/SemanticTokens.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import SwiftSyntax
1919

2020
extension SwiftLanguageService {
2121
/// Requests the semantic highlighting tokens for the given snapshot from sourcekitd.
22-
private func semanticHighlightingTokens(for snapshot: DocumentSnapshot) async throws -> [SyntaxHighlightingToken]? {
22+
private func semanticHighlightingTokens(for snapshot: DocumentSnapshot) async throws -> SyntaxHighlightingTokens? {
2323
guard let buildSettings = await self.buildSettings(for: snapshot.uri), !buildSettings.isFallback else {
2424
return nil
2525
}
@@ -35,7 +35,9 @@ extension SwiftLanguageService {
3535
guard let skTokens: SKDResponseArray = dict[keys.semanticTokens] else {
3636
return nil
3737
}
38-
return SyntaxHighlightingTokenParser(sourcekitd: sourcekitd).parseTokens(skTokens, in: snapshot)
38+
39+
let parsedTokens = SyntaxHighlightingTokenParser(sourcekitd: sourcekitd).parseTokens(skTokens, in: snapshot)
40+
return SyntaxHighlightingTokens(members: parsedTokens)
3941
}
4042

4143
/// Computes an array of syntax highlighting tokens from the syntax tree that
@@ -49,7 +51,7 @@ extension SwiftLanguageService {
4951
private func mergedAndSortedTokens(
5052
for snapshot: DocumentSnapshot,
5153
in range: Range<Position>? = nil
52-
) async throws -> [SyntaxHighlightingToken] {
54+
) async throws -> SyntaxHighlightingTokens {
5355
async let tree = syntaxTreeManager.syntaxTree(for: snapshot)
5456
let semanticTokens = await orLog("Loading semantic tokens") { try await semanticHighlightingTokens(for: snapshot) }
5557

@@ -59,10 +61,13 @@ extension SwiftLanguageService {
5961
} else {
6062
ByteSourceRange(offset: 0, length: await tree.totalLength.utf8Length)
6163
}
62-
return
64+
65+
let tokens =
6366
await tree
6467
.classifications(in: range)
6568
.flatMap({ $0.highlightingTokens(in: snapshot) })
69+
70+
return SyntaxHighlightingTokens(members: tokens)
6671
.mergingTokens(with: semanticTokens ?? [])
6772
.sorted { $0.start < $1.start }
6873
}
@@ -102,7 +107,7 @@ extension Range where Bound == Position {
102107
}
103108

104109
extension SyntaxClassifiedRange {
105-
fileprivate func highlightingTokens(in snapshot: DocumentSnapshot) -> [SyntaxHighlightingToken] {
110+
fileprivate func highlightingTokens(in snapshot: DocumentSnapshot) -> SyntaxHighlightingTokens {
106111
guard let (kind, modifiers) = self.kind.highlightingKindAndModifiers else {
107112
return []
108113
}
@@ -117,13 +122,15 @@ extension SyntaxClassifiedRange {
117122
let multiLineRange = start..<end
118123
let ranges = multiLineRange.splitToSingleLineRanges(in: snapshot)
119124

120-
return ranges.map {
125+
let tokens = ranges.map {
121126
SyntaxHighlightingToken(
122127
range: $0,
123128
kind: kind,
124129
modifiers: modifiers
125130
)
126131
}
132+
133+
return SyntaxHighlightingTokens(members: tokens)
127134
}
128135
}
129136

Sources/SourceKitLSP/Swift/SyntaxHighlightingToken.swift

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,48 +48,6 @@ public struct SyntaxHighlightingToken: Hashable {
4848
}
4949
}
5050

51-
extension Array where Element == SyntaxHighlightingToken {
52-
/// The LSP representation of syntax highlighting tokens. Note that this
53-
/// requires the tokens in this array to be sorted.
54-
public var lspEncoded: [UInt32] {
55-
var previous = Position(line: 0, utf16index: 0)
56-
var rawTokens: [UInt32] = []
57-
rawTokens.reserveCapacity(count * 5)
58-
59-
for token in self {
60-
let lineDelta = token.start.line - previous.line
61-
let charDelta =
62-
token.start.utf16index - (
63-
// The character delta is relative to the previous token's start
64-
// only if the token is on the previous token's line.
65-
previous.line == token.start.line ? previous.utf16index : 0)
66-
67-
// We assert that the tokens are actually sorted
68-
assert(lineDelta >= 0)
69-
assert(charDelta >= 0)
70-
71-
previous = token.start
72-
rawTokens += [
73-
UInt32(lineDelta),
74-
UInt32(charDelta),
75-
UInt32(token.utf16length),
76-
token.kind.tokenType,
77-
token.modifiers.rawValue,
78-
]
79-
}
80-
81-
return rawTokens
82-
}
83-
84-
/// Merges the tokens in this array into a new token array,
85-
/// preferring the given array's tokens if duplicate ranges are
86-
/// found.
87-
public func mergingTokens(with other: [SyntaxHighlightingToken]) -> [SyntaxHighlightingToken] {
88-
let otherRanges = Set(other.map(\.range))
89-
return filter { !otherRanges.contains($0.range) } + other
90-
}
91-
}
92-
9351
extension SemanticTokenTypes {
9452
/// **(LSP Extension)**
9553
public static let identifier = Self("identifier")

Sources/SourceKitLSP/Swift/SyntaxHighlightingTokenParser.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct SyntaxHighlightingTokenParser {
2525
private func parseTokens(
2626
_ response: SKDResponseDictionary,
2727
in snapshot: DocumentSnapshot,
28-
into tokens: inout [SyntaxHighlightingToken]
28+
into tokens: inout SyntaxHighlightingTokens
2929
) {
3030
let keys = sourcekitd.keys
3131

@@ -52,13 +52,12 @@ struct SyntaxHighlightingTokenParser {
5252
let multiLineRange = start..<end
5353
let ranges = multiLineRange.splitToSingleLineRanges(in: snapshot)
5454

55-
tokens += ranges.map {
55+
tokens.members += ranges.map {
5656
SyntaxHighlightingToken(
5757
range: $0,
5858
kind: kind,
5959
modifiers: modifiers
6060
)
61-
}
6261
}
6362
}
6463

@@ -70,16 +69,16 @@ struct SyntaxHighlightingTokenParser {
7069
private func parseTokens(
7170
_ response: SKDResponseArray,
7271
in snapshot: DocumentSnapshot,
73-
into tokens: inout [SyntaxHighlightingToken]
72+
into tokens: inout SyntaxHighlightingTokens
7473
) {
7574
response.forEach { (_, value) in
7675
parseTokens(value, in: snapshot, into: &tokens)
7776
return true
7877
}
7978
}
8079

81-
func parseTokens(_ response: SKDResponseArray, in snapshot: DocumentSnapshot) -> [SyntaxHighlightingToken] {
82-
var tokens: [SyntaxHighlightingToken] = []
80+
func parseTokens(_ response: SKDResponseArray, in snapshot: DocumentSnapshot) -> SyntaxHighlightingTokens {
81+
var tokens: SyntaxHighlightingTokens = SyntaxHighlightingTokens(members: [])
8382
parseTokens(response, in: snapshot, into: &tokens)
8483
return tokens
8584
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2924 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 LSPLogging
14+
import LanguageServerProtocol
15+
import SourceKitD
16+
17+
/// A ranged token in the document used for syntax highlighting.
18+
public struct SyntaxHighlightingTokens {
19+
public var members: [SyntaxHighlightingToken]
20+
21+
public init(members: [SyntaxHighlightingToken]) {
22+
self.members = members
23+
}
24+
25+
/// The LSP representation of syntax highlighting tokens. Note that this
26+
/// requires the tokens in this array to be sorted.
27+
public var lspEncoded: [UInt32] {
28+
var previous = Position(line: 0, utf16index: 0)
29+
var rawTokens: [UInt32] = []
30+
rawTokens.reserveCapacity(count * 5)
31+
32+
for token in self.members {
33+
let lineDelta = token.start.line - previous.line
34+
let charDelta =
35+
token.start.utf16index - (
36+
// The character delta is relative to the previous token's start
37+
// only if the token is on the previous token's line.
38+
previous.line == token.start.line ? previous.utf16index : 0)
39+
40+
// We assert that the tokens are actually sorted
41+
assert(lineDelta >= 0)
42+
assert(charDelta >= 0)
43+
44+
previous = token.start
45+
rawTokens += [
46+
UInt32(lineDelta),
47+
UInt32(charDelta),
48+
UInt32(token.utf16length),
49+
token.kind.tokenType,
50+
token.modifiers.rawValue,
51+
]
52+
}
53+
54+
return rawTokens
55+
}
56+
57+
/// Merges the tokens in this array into a new token array,
58+
/// preferring the given array's tokens if duplicate ranges are
59+
/// found.
60+
public func mergingTokens(with other: SyntaxHighlightingTokens) -> SyntaxHighlightingTokens {
61+
let otherRanges = Set(other.members.map(\.range))
62+
return SyntaxHighlightingTokens(members: members.filter { !otherRanges.contains($0.range) } + other.members)
63+
}
64+
65+
/// Sorts the tokens in this array by their start position.
66+
public func sorted(_ areInIncreasingOrder: (SyntaxHighlightingToken, SyntaxHighlightingToken) -> Bool) -> SyntaxHighlightingTokens {
67+
SyntaxHighlightingTokens(members: members.sorted(by: areInIncreasingOrder))
68+
}
69+
}

Tests/SourceKitLSPTests/SemanticTokensTests.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SourceKitLSP
1818
import XCTest
1919

2020
private typealias Token = SyntaxHighlightingToken
21+
private typealias Tokens = SyntaxHighlightingTokens
2122

2223
final class SemanticTokensTests: XCTestCase {
2324
/// The mock client used to communicate with the SourceKit-LSP server.
@@ -147,6 +148,8 @@ final class SemanticTokensTests: XCTestCase {
147148
)
148149
}
149150

151+
// TODO: This part is also not directly using the methods member, should i change it new struct type?
152+
150153
return [Token](lspEncodedTokens: response.data)
151154
}
152155

0 commit comments

Comments
 (0)