Skip to content

Commit 3ca3b30

Browse files
Merge pull request #12 from cucumber/improve-semantic-tokens
Change semantic tokens
2 parents 9351647 + 962e2e9 commit 3ca3b30

3 files changed

Lines changed: 69 additions & 144 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
### Fixed
11+
- Generate semantic tokens that are supported by the Monaco / Visual Studio Code `vs` theme. ([#12](https://github.com/cucumber/language-service/pull/12)).
12+
1013
## [0.10.0] - 2021-11-08
1114
### Removed
1215
- Move `tree-sitter` functionality to `@cucumber/language-server`

src/service/getGherkinSemanticTokens.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import { SemanticTokens, SemanticTokenTypes } from 'vscode-languageserver-types'
55

66
import { parseGherkinDocument } from '../gherkin/parseGherkinDocument.js'
77

8+
// The default vs theme can only highlight certain tokens. See the list of those tokens in
9+
// https://microsoft.github.io/monaco-editor/monarch.html
810
export const semanticTokenTypes: SemanticTokenTypes[] = [
911
SemanticTokenTypes.keyword, // Feature, Scenario, Given etc
1012
SemanticTokenTypes.parameter, // step parameters
11-
SemanticTokenTypes.string, // DocString content and ``` delimiter
12-
SemanticTokenTypes.type, // DocString ```type
13-
SemanticTokenTypes.class, // @tags
13+
SemanticTokenTypes.string, // DocString content and ``` delimiter, table cells (except example table header rows)
14+
SemanticTokenTypes.type, // @tags and DocString ```type
1415
SemanticTokenTypes.variable, // step <placeholder>
1516
SemanticTokenTypes.property, // examples table header row
1617
]
@@ -66,7 +67,7 @@ export function getGherkinSemanticTokens(
6667

6768
const data = walkGherkinDocument<number[]>(gherkinDocument, [], {
6869
tag(tag, arr) {
69-
return makeLocationToken(tag.location, tag.name, SemanticTokenTypes.class, arr)
70+
return makeLocationToken(tag.location, tag.name, SemanticTokenTypes.type, arr)
7071
},
7172
feature(feature, arr) {
7273
return makeLocationToken(feature.location, feature.keyword, SemanticTokenTypes.keyword, arr)
@@ -155,7 +156,7 @@ export function getGherkinSemanticTokens(
155156
return arr
156157
},
157158
tableRow(tableRow, arr) {
158-
const type = inExamples ? SemanticTokenTypes.property : SemanticTokenTypes.parameter
159+
const type = inExamples ? SemanticTokenTypes.property : SemanticTokenTypes.string
159160
for (const cell of tableRow.cells) {
160161
arr = makeLocationToken(cell.location, cell.value, type, arr)
161162
}
Lines changed: 60 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { CucumberExpression, ParameterTypeRegistry } from '@cucumber/cucumber-expressions'
22
import assert from 'assert'
3-
import { SemanticTokens } from 'vscode-languageserver-types'
3+
import { SemanticTokenTypes, uinteger } from 'vscode-languageserver-types'
44

5-
import { getGherkinSemanticTokens } from '../../src/service/getGherkinSemanticTokens.js'
5+
import {
6+
getGherkinSemanticTokens,
7+
semanticTokenTypes,
8+
} from '../../src/service/getGherkinSemanticTokens.js'
9+
10+
type TokenWithType = [string, SemanticTokenTypes]
611

712
describe('getGherkinSemanticTokens', () => {
813
it('creates tokens for keywords', () => {
@@ -35,142 +40,58 @@ Feature: a
3540
)
3641

3742
const semanticTokens = getGherkinSemanticTokens(gherkinSource, [expression])
38-
const expectedSemanticTokens: SemanticTokens = {
39-
// See https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_semanticTokens
40-
// for details about how tokens are encoded
41-
data: [
42-
1,
43-
0,
44-
4,
45-
4,
46-
0, // @foo
47-
0,
48-
5,
49-
4,
50-
4,
51-
0, // @bar
52-
1,
53-
0,
54-
7,
55-
0,
56-
0, // Feature
57-
4,
58-
2,
59-
8,
60-
0,
61-
0, // Scenario
62-
1,
63-
4,
64-
6,
65-
0,
66-
0, // Given
67-
0,
68-
13,
69-
2,
70-
1,
71-
0, // 42
72-
0,
73-
15,
74-
5,
75-
1,
76-
0, // belly
77-
1,
78-
6,
79-
3,
80-
2,
81-
0, // """
82-
0,
83-
3,
84-
8,
85-
3,
86-
0, // sometype
87-
1,
88-
5,
89-
5,
90-
2,
91-
0, // hello
92-
1,
93-
8,
94-
5,
95-
2,
96-
0, // world
97-
1,
98-
7,
99-
3,
100-
2,
101-
0, // """
102-
1,
103-
4,
104-
4,
105-
0,
106-
0, // And
107-
1,
108-
8,
109-
1,
110-
1,
111-
0, // a
112-
0,
113-
5,
114-
3,
115-
1,
116-
0, // bbb
117-
1,
118-
8,
119-
2,
120-
1,
121-
0, // cc
122-
0,
123-
6,
124-
2,
125-
1,
126-
0, // dd
127-
2,
128-
2,
129-
16,
130-
0,
131-
0, // Scenario Outline
132-
1,
133-
4,
134-
6,
135-
0,
136-
0, // Given
137-
0,
138-
8,
139-
5,
140-
5,
141-
0, // <foo>
142-
0,
143-
10,
144-
5,
145-
5,
146-
0, // <bar>
147-
2,
148-
4,
149-
8,
150-
0,
151-
0, // Examples
152-
1,
153-
8,
154-
3,
155-
6,
156-
0, // foo
157-
0,
158-
6,
159-
3,
160-
6,
161-
0, // bar
162-
1,
163-
8,
164-
1,
165-
1,
166-
0, // a
167-
0,
168-
6,
169-
1,
170-
1,
171-
0, // b
172-
],
173-
}
174-
assert.deepStrictEqual(semanticTokens, expectedSemanticTokens)
43+
const actual = tokenize(gherkinSource, semanticTokens.data)
44+
const expected: TokenWithType[] = [
45+
['@foo', SemanticTokenTypes.type],
46+
['@bar', SemanticTokenTypes.type],
47+
['Feature', SemanticTokenTypes.keyword],
48+
['Scenario', SemanticTokenTypes.keyword],
49+
['Given ', SemanticTokenTypes.keyword],
50+
['42', SemanticTokenTypes.parameter],
51+
['belly', SemanticTokenTypes.parameter],
52+
['"""', SemanticTokenTypes.string],
53+
['sometype', SemanticTokenTypes.type],
54+
['hello', SemanticTokenTypes.string],
55+
['world', SemanticTokenTypes.string],
56+
['"""', SemanticTokenTypes.string],
57+
['And ', SemanticTokenTypes.keyword],
58+
['a', SemanticTokenTypes.string],
59+
['bbb', SemanticTokenTypes.string],
60+
['cc', SemanticTokenTypes.string],
61+
['dd', SemanticTokenTypes.string],
62+
['Scenario Outline', SemanticTokenTypes.keyword],
63+
['Given ', SemanticTokenTypes.keyword],
64+
['<foo>', SemanticTokenTypes.variable],
65+
['<bar>', SemanticTokenTypes.variable],
66+
['Examples', SemanticTokenTypes.keyword],
67+
['foo', SemanticTokenTypes.property],
68+
['bar', SemanticTokenTypes.property],
69+
['a', SemanticTokenTypes.string],
70+
['b', SemanticTokenTypes.string],
71+
]
72+
assert.deepStrictEqual(actual, expected)
17573
})
17674
})
75+
76+
// See https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_semanticTokens
77+
// for details about how tokens are encoded
78+
function tokenize(source: string, tokenData: readonly uinteger[]): readonly TokenWithType[] {
79+
const result: TokenWithType[] = []
80+
const lines = source.split('\n')
81+
let lineIndex = 0
82+
let start = 0
83+
for (let i = 0; i < tokenData.length; i += 5) {
84+
const deltaLine = tokenData[i]
85+
if (deltaLine > 0) {
86+
start = 0
87+
}
88+
lineIndex += deltaLine
89+
start += tokenData[i + 1]
90+
const length = tokenData[i + 2]
91+
const token = lines[lineIndex].substring(start, start + length)
92+
const tokenTypeIndex = tokenData[i + 3]
93+
const tokenType = semanticTokenTypes[tokenTypeIndex]
94+
result.push([token, tokenType])
95+
}
96+
return result
97+
}

0 commit comments

Comments
 (0)