Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/semanticTokens/common/getSemanticTokens.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { CancellationToken } from '../../../../base/common/cancellation.js';
7
import { onUnexpectedExternalError } from '../../../../base/common/errors.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { ITextModel } from '../../../common/model.js';
10
import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend, DocumentRangeSemanticTokensProvider } from '../../../common/languages.js';
11
import { IModelService } from '../../../common/services/model.js';
12
import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
13
import { assertType } from '../../../../base/common/types.js';
14
import { VSBuffer } from '../../../../base/common/buffer.js';
15
import { encodeSemanticTokensDto } from '../../../common/services/semanticTokensDto.js';
16
import { Range } from '../../../common/core/range.js';
17
import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js';
18
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
19
20
export function isSemanticTokens(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokens {
21
return v && !!((<SemanticTokens>v).data);
22
}
23
24
export function isSemanticTokensEdits(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokensEdits {
25
return v && Array.isArray((<SemanticTokensEdits>v).edits);
26
}
27
28
export class DocumentSemanticTokensResult {
29
constructor(
30
public readonly provider: DocumentSemanticTokensProvider,
31
public readonly tokens: SemanticTokens | SemanticTokensEdits | null,
32
public readonly error: any
33
) { }
34
}
35
36
export function hasDocumentSemanticTokensProvider(registry: LanguageFeatureRegistry<DocumentSemanticTokensProvider>, model: ITextModel): boolean {
37
return registry.has(model);
38
}
39
40
function getDocumentSemanticTokensProviders(registry: LanguageFeatureRegistry<DocumentSemanticTokensProvider>, model: ITextModel): DocumentSemanticTokensProvider[] {
41
const groups = registry.orderedGroups(model);
42
return (groups.length > 0 ? groups[0] : []);
43
}
44
45
export async function getDocumentSemanticTokens(registry: LanguageFeatureRegistry<DocumentSemanticTokensProvider>, model: ITextModel, lastProvider: DocumentSemanticTokensProvider | null, lastResultId: string | null, token: CancellationToken): Promise<DocumentSemanticTokensResult | null> {
46
const providers = getDocumentSemanticTokensProviders(registry, model);
47
48
// Get tokens from all providers at the same time.
49
const results = await Promise.all(providers.map(async (provider) => {
50
let result: SemanticTokens | SemanticTokensEdits | null | undefined;
51
let error: unknown = null;
52
try {
53
result = await provider.provideDocumentSemanticTokens(model, (provider === lastProvider ? lastResultId : null), token);
54
} catch (err) {
55
error = err;
56
result = null;
57
}
58
59
if (!result || (!isSemanticTokens(result) && !isSemanticTokensEdits(result))) {
60
result = null;
61
}
62
63
return new DocumentSemanticTokensResult(provider, result, error);
64
}));
65
66
// Try to return the first result with actual tokens or
67
// the first result which threw an error (!!)
68
for (const result of results) {
69
if (result.error) {
70
throw result.error;
71
}
72
if (result.tokens) {
73
return result;
74
}
75
}
76
77
// Return the first result, even if it doesn't have tokens
78
if (results.length > 0) {
79
return results[0];
80
}
81
82
return null;
83
}
84
85
function _getDocumentSemanticTokensProviderHighestGroup(registry: LanguageFeatureRegistry<DocumentSemanticTokensProvider>, model: ITextModel): DocumentSemanticTokensProvider[] | null {
86
const result = registry.orderedGroups(model);
87
return (result.length > 0 ? result[0] : null);
88
}
89
90
class DocumentRangeSemanticTokensResult {
91
constructor(
92
public readonly provider: DocumentRangeSemanticTokensProvider,
93
public readonly tokens: SemanticTokens | null,
94
) { }
95
}
96
97
export function hasDocumentRangeSemanticTokensProvider(providers: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>, model: ITextModel): boolean {
98
return providers.has(model);
99
}
100
101
function getDocumentRangeSemanticTokensProviders(providers: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>, model: ITextModel): DocumentRangeSemanticTokensProvider[] {
102
const groups = providers.orderedGroups(model);
103
return (groups.length > 0 ? groups[0] : []);
104
}
105
106
export async function getDocumentRangeSemanticTokens(registry: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>, model: ITextModel, range: Range, token: CancellationToken): Promise<DocumentRangeSemanticTokensResult | null> {
107
const providers = getDocumentRangeSemanticTokensProviders(registry, model);
108
109
// Get tokens from all providers at the same time.
110
const results = await Promise.all(providers.map(async (provider) => {
111
let result: SemanticTokens | null | undefined;
112
try {
113
result = await provider.provideDocumentRangeSemanticTokens(model, range, token);
114
} catch (err) {
115
onUnexpectedExternalError(err);
116
result = null;
117
}
118
119
if (!result || !isSemanticTokens(result)) {
120
result = null;
121
}
122
123
return new DocumentRangeSemanticTokensResult(provider, result);
124
}));
125
126
// Try to return the first result with actual tokens
127
for (const result of results) {
128
if (result.tokens) {
129
return result;
130
}
131
}
132
133
// Return the first result, even if it doesn't have tokens
134
if (results.length > 0) {
135
return results[0];
136
}
137
138
return null;
139
}
140
141
CommandsRegistry.registerCommand('_provideDocumentSemanticTokensLegend', async (accessor, ...args): Promise<SemanticTokensLegend | undefined> => {
142
const [uri] = args;
143
assertType(uri instanceof URI);
144
145
const model = accessor.get(IModelService).getModel(uri);
146
if (!model) {
147
return undefined;
148
}
149
const { documentSemanticTokensProvider } = accessor.get(ILanguageFeaturesService);
150
151
const providers = _getDocumentSemanticTokensProviderHighestGroup(documentSemanticTokensProvider, model);
152
if (!providers) {
153
// there is no provider => fall back to a document range semantic tokens provider
154
return accessor.get(ICommandService).executeCommand('_provideDocumentRangeSemanticTokensLegend', uri);
155
}
156
157
return providers[0].getLegend();
158
});
159
160
CommandsRegistry.registerCommand('_provideDocumentSemanticTokens', async (accessor, ...args): Promise<VSBuffer | undefined> => {
161
const [uri] = args;
162
assertType(uri instanceof URI);
163
164
const model = accessor.get(IModelService).getModel(uri);
165
if (!model) {
166
return undefined;
167
}
168
const { documentSemanticTokensProvider } = accessor.get(ILanguageFeaturesService);
169
if (!hasDocumentSemanticTokensProvider(documentSemanticTokensProvider, model)) {
170
// there is no provider => fall back to a document range semantic tokens provider
171
return accessor.get(ICommandService).executeCommand('_provideDocumentRangeSemanticTokens', uri, model.getFullModelRange());
172
}
173
174
const r = await getDocumentSemanticTokens(documentSemanticTokensProvider, model, null, null, CancellationToken.None);
175
if (!r) {
176
return undefined;
177
}
178
179
const { provider, tokens } = r;
180
181
if (!tokens || !isSemanticTokens(tokens)) {
182
return undefined;
183
}
184
185
const buff = encodeSemanticTokensDto({
186
id: 0,
187
type: 'full',
188
data: tokens.data
189
});
190
if (tokens.resultId) {
191
provider.releaseDocumentSemanticTokens(tokens.resultId);
192
}
193
return buff;
194
});
195
196
CommandsRegistry.registerCommand('_provideDocumentRangeSemanticTokensLegend', async (accessor, ...args): Promise<SemanticTokensLegend | undefined> => {
197
const [uri, range] = args;
198
assertType(uri instanceof URI);
199
200
const model = accessor.get(IModelService).getModel(uri);
201
if (!model) {
202
return undefined;
203
}
204
const { documentRangeSemanticTokensProvider } = accessor.get(ILanguageFeaturesService);
205
const providers = getDocumentRangeSemanticTokensProviders(documentRangeSemanticTokensProvider, model);
206
if (providers.length === 0) {
207
// no providers
208
return undefined;
209
}
210
211
if (providers.length === 1) {
212
// straight forward case, just a single provider
213
return providers[0].getLegend();
214
}
215
216
if (!range || !Range.isIRange(range)) {
217
// if no range is provided, we cannot support multiple providers
218
// as we cannot fall back to the one which would give results
219
// => return the first legend for backwards compatibility and print a warning
220
console.warn(`provideDocumentRangeSemanticTokensLegend might be out-of-sync with provideDocumentRangeSemanticTokens unless a range argument is passed in`);
221
return providers[0].getLegend();
222
}
223
224
const result = await getDocumentRangeSemanticTokens(documentRangeSemanticTokensProvider, model, Range.lift(range), CancellationToken.None);
225
if (!result) {
226
return undefined;
227
}
228
229
return result.provider.getLegend();
230
});
231
232
CommandsRegistry.registerCommand('_provideDocumentRangeSemanticTokens', async (accessor, ...args): Promise<VSBuffer | undefined> => {
233
const [uri, range] = args;
234
assertType(uri instanceof URI);
235
assertType(Range.isIRange(range));
236
237
const model = accessor.get(IModelService).getModel(uri);
238
if (!model) {
239
return undefined;
240
}
241
const { documentRangeSemanticTokensProvider } = accessor.get(ILanguageFeaturesService);
242
243
const result = await getDocumentRangeSemanticTokens(documentRangeSemanticTokensProvider, model, Range.lift(range), CancellationToken.None);
244
if (!result || !result.tokens) {
245
// there is no provider or it didn't return tokens
246
return undefined;
247
}
248
249
return encodeSemanticTokensDto({
250
id: 0,
251
type: 'full',
252
data: result.tokens.data
253
});
254
});
255
256