Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.ts
5270 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 { CancelablePromise, createCancelablePromise, RunOnceScheduler } from '../../../../base/common/async.js';
7
import { Disposable, dispose, IDisposable } from '../../../../base/common/lifecycle.js';
8
import { ICodeEditor } from '../../../browser/editorBrowser.js';
9
import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js';
10
import { Range } from '../../../common/core/range.js';
11
import { IEditorContribution } from '../../../common/editorCommon.js';
12
import { ITextModel } from '../../../common/model.js';
13
import { getDocumentRangeSemanticTokens, hasDocumentRangeSemanticTokensProvider } from '../common/getSemanticTokens.js';
14
import { isSemanticColoringEnabled, SEMANTIC_HIGHLIGHTING_SETTING_ID } from '../common/semanticTokensConfig.js';
15
import { toMultilineTokens2 } from '../../../common/services/semanticTokensProviderStyling.js';
16
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
17
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
18
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';
19
import { StopWatch } from '../../../../base/common/stopwatch.js';
20
import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js';
21
import { DocumentRangeSemanticTokensProvider } from '../../../common/languages.js';
22
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
23
import { ISemanticTokensStylingService } from '../../../common/services/semanticTokensStyling.js';
24
25
export class ViewportSemanticTokensContribution extends Disposable implements IEditorContribution {
26
27
public static readonly ID = 'editor.contrib.viewportSemanticTokens';
28
29
public static get(editor: ICodeEditor): ViewportSemanticTokensContribution | null {
30
return editor.getContribution<ViewportSemanticTokensContribution>(ViewportSemanticTokensContribution.ID);
31
}
32
33
private readonly _editor: ICodeEditor;
34
private readonly _provider: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>;
35
private readonly _debounceInformation: IFeatureDebounceInformation;
36
private readonly _tokenizeViewport: RunOnceScheduler;
37
private _outstandingRequests: CancelablePromise<unknown>[];
38
private _rangeProvidersChangeListeners: IDisposable[];
39
40
constructor(
41
editor: ICodeEditor,
42
@ISemanticTokensStylingService private readonly _semanticTokensStylingService: ISemanticTokensStylingService,
43
@IThemeService private readonly _themeService: IThemeService,
44
@IConfigurationService private readonly _configurationService: IConfigurationService,
45
@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,
46
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
47
) {
48
super();
49
this._editor = editor;
50
this._provider = languageFeaturesService.documentRangeSemanticTokensProvider;
51
this._debounceInformation = languageFeatureDebounceService.for(this._provider, 'DocumentRangeSemanticTokens', { min: 100, max: 500 });
52
this._tokenizeViewport = this._register(new RunOnceScheduler(() => this._tokenizeViewportNow(), 100));
53
this._outstandingRequests = [];
54
this._rangeProvidersChangeListeners = [];
55
const scheduleTokenizeViewport = () => {
56
if (this._editor.hasModel()) {
57
this._tokenizeViewport.schedule(this._debounceInformation.get(this._editor.getModel()));
58
}
59
};
60
const bindRangeProvidersChangeListeners = () => {
61
this._cleanupProviderListeners();
62
if (this._editor.hasModel()) {
63
const model = this._editor.getModel();
64
for (const provider of this._provider.all(model)) {
65
const disposable = provider.onDidChange?.(() => {
66
this._cancelAll();
67
scheduleTokenizeViewport();
68
});
69
if (disposable) {
70
this._rangeProvidersChangeListeners.push(disposable);
71
}
72
}
73
}
74
};
75
76
this._register(this._editor.onDidScrollChange(() => {
77
scheduleTokenizeViewport();
78
}));
79
this._register(this._editor.onDidChangeModel(() => {
80
bindRangeProvidersChangeListeners();
81
this._cancelAll();
82
scheduleTokenizeViewport();
83
}));
84
this._register(this._editor.onDidChangeModelLanguage(() => {
85
// The cleanup of the model's semantic tokens happens in the DocumentSemanticTokensFeature
86
bindRangeProvidersChangeListeners();
87
this._cancelAll();
88
scheduleTokenizeViewport();
89
}));
90
this._register(this._editor.onDidChangeModelContent((e) => {
91
this._cancelAll();
92
scheduleTokenizeViewport();
93
}));
94
95
bindRangeProvidersChangeListeners();
96
this._register(this._provider.onDidChange(() => {
97
bindRangeProvidersChangeListeners();
98
this._cancelAll();
99
scheduleTokenizeViewport();
100
}));
101
this._register(this._configurationService.onDidChangeConfiguration(e => {
102
if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
103
this._cancelAll();
104
scheduleTokenizeViewport();
105
}
106
}));
107
this._register(this._themeService.onDidColorThemeChange(() => {
108
this._cancelAll();
109
scheduleTokenizeViewport();
110
}));
111
scheduleTokenizeViewport();
112
}
113
114
public override dispose(): void {
115
this._cleanupProviderListeners();
116
super.dispose();
117
}
118
119
private _cleanupProviderListeners(): void {
120
dispose(this._rangeProvidersChangeListeners);
121
this._rangeProvidersChangeListeners = [];
122
}
123
124
private _cancelAll(): void {
125
for (const request of this._outstandingRequests) {
126
request.cancel();
127
}
128
this._outstandingRequests = [];
129
}
130
131
private _removeOutstandingRequest(req: CancelablePromise<unknown>): void {
132
for (let i = 0, len = this._outstandingRequests.length; i < len; i++) {
133
if (this._outstandingRequests[i] === req) {
134
this._outstandingRequests.splice(i, 1);
135
return;
136
}
137
}
138
}
139
140
private _tokenizeViewportNow(): void {
141
if (!this._editor.hasModel()) {
142
return;
143
}
144
const model = this._editor.getModel();
145
if (model.tokenization.hasCompleteSemanticTokens()) {
146
return;
147
}
148
if (!isSemanticColoringEnabled(model, this._themeService, this._configurationService)) {
149
if (model.tokenization.hasSomeSemanticTokens()) {
150
model.tokenization.setSemanticTokens(null, false);
151
}
152
return;
153
}
154
if (!hasDocumentRangeSemanticTokensProvider(this._provider, model)) {
155
if (model.tokenization.hasSomeSemanticTokens()) {
156
model.tokenization.setSemanticTokens(null, false);
157
}
158
return;
159
}
160
const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow();
161
162
this._outstandingRequests = this._outstandingRequests.concat(visibleRanges.map(range => this._requestRange(model, range)));
163
}
164
165
private _requestRange(model: ITextModel, range: Range): CancelablePromise<unknown> {
166
const requestVersionId = model.getVersionId();
167
const request = createCancelablePromise(token => Promise.resolve(getDocumentRangeSemanticTokens(this._provider, model, range, token)));
168
const sw = new StopWatch(false);
169
request.then((r) => {
170
this._debounceInformation.update(model, sw.elapsed());
171
if (!r || !r.tokens || model.isDisposed() || model.getVersionId() !== requestVersionId) {
172
return;
173
}
174
const { provider, tokens: result } = r;
175
const styling = this._semanticTokensStylingService.getStyling(provider);
176
model.tokenization.setPartialSemanticTokens(range, toMultilineTokens2(result, styling, model.getLanguageId()));
177
}).then(() => this._removeOutstandingRequest(request), () => this._removeOutstandingRequest(request));
178
return request;
179
}
180
}
181
182
registerEditorContribution(ViewportSemanticTokensContribution.ID, ViewportSemanticTokensContribution, EditorContributionInstantiation.AfterFirstRender);
183
184