Path: blob/main/src/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from '../../../../base/common/async.js';6import { Disposable } from '../../../../base/common/lifecycle.js';7import { ICodeEditor } from '../../../browser/editorBrowser.js';8import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js';9import { Range } from '../../../common/core/range.js';10import { IEditorContribution } from '../../../common/editorCommon.js';11import { ITextModel } from '../../../common/model.js';12import { getDocumentRangeSemanticTokens, hasDocumentRangeSemanticTokensProvider } from '../common/getSemanticTokens.js';13import { isSemanticColoringEnabled, SEMANTIC_HIGHLIGHTING_SETTING_ID } from '../common/semanticTokensConfig.js';14import { toMultilineTokens2 } from '../../../common/services/semanticTokensProviderStyling.js';15import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';16import { IThemeService } from '../../../../platform/theme/common/themeService.js';17import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';18import { StopWatch } from '../../../../base/common/stopwatch.js';19import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js';20import { DocumentRangeSemanticTokensProvider } from '../../../common/languages.js';21import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';22import { ISemanticTokensStylingService } from '../../../common/services/semanticTokensStyling.js';2324export class ViewportSemanticTokensContribution extends Disposable implements IEditorContribution {2526public static readonly ID = 'editor.contrib.viewportSemanticTokens';2728public static get(editor: ICodeEditor): ViewportSemanticTokensContribution | null {29return editor.getContribution<ViewportSemanticTokensContribution>(ViewportSemanticTokensContribution.ID);30}3132private readonly _editor: ICodeEditor;33private readonly _provider: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>;34private readonly _debounceInformation: IFeatureDebounceInformation;35private readonly _tokenizeViewport: RunOnceScheduler;36private _outstandingRequests: CancelablePromise<any>[];3738constructor(39editor: ICodeEditor,40@ISemanticTokensStylingService private readonly _semanticTokensStylingService: ISemanticTokensStylingService,41@IThemeService private readonly _themeService: IThemeService,42@IConfigurationService private readonly _configurationService: IConfigurationService,43@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,44@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,45) {46super();47this._editor = editor;48this._provider = languageFeaturesService.documentRangeSemanticTokensProvider;49this._debounceInformation = languageFeatureDebounceService.for(this._provider, 'DocumentRangeSemanticTokens', { min: 100, max: 500 });50this._tokenizeViewport = this._register(new RunOnceScheduler(() => this._tokenizeViewportNow(), 100));51this._outstandingRequests = [];52const scheduleTokenizeViewport = () => {53if (this._editor.hasModel()) {54this._tokenizeViewport.schedule(this._debounceInformation.get(this._editor.getModel()));55}56};57this._register(this._editor.onDidScrollChange(() => {58scheduleTokenizeViewport();59}));60this._register(this._editor.onDidChangeModel(() => {61this._cancelAll();62scheduleTokenizeViewport();63}));64this._register(this._editor.onDidChangeModelContent((e) => {65this._cancelAll();66scheduleTokenizeViewport();67}));68this._register(this._provider.onDidChange(() => {69this._cancelAll();70scheduleTokenizeViewport();71}));72this._register(this._configurationService.onDidChangeConfiguration(e => {73if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {74this._cancelAll();75scheduleTokenizeViewport();76}77}));78this._register(this._themeService.onDidColorThemeChange(() => {79this._cancelAll();80scheduleTokenizeViewport();81}));82scheduleTokenizeViewport();83}8485private _cancelAll(): void {86for (const request of this._outstandingRequests) {87request.cancel();88}89this._outstandingRequests = [];90}9192private _removeOutstandingRequest(req: CancelablePromise<any>): void {93for (let i = 0, len = this._outstandingRequests.length; i < len; i++) {94if (this._outstandingRequests[i] === req) {95this._outstandingRequests.splice(i, 1);96return;97}98}99}100101private _tokenizeViewportNow(): void {102if (!this._editor.hasModel()) {103return;104}105const model = this._editor.getModel();106if (model.tokenization.hasCompleteSemanticTokens()) {107return;108}109if (!isSemanticColoringEnabled(model, this._themeService, this._configurationService)) {110if (model.tokenization.hasSomeSemanticTokens()) {111model.tokenization.setSemanticTokens(null, false);112}113return;114}115if (!hasDocumentRangeSemanticTokensProvider(this._provider, model)) {116if (model.tokenization.hasSomeSemanticTokens()) {117model.tokenization.setSemanticTokens(null, false);118}119return;120}121const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow();122123this._outstandingRequests = this._outstandingRequests.concat(visibleRanges.map(range => this._requestRange(model, range)));124}125126private _requestRange(model: ITextModel, range: Range): CancelablePromise<any> {127const requestVersionId = model.getVersionId();128const request = createCancelablePromise(token => Promise.resolve(getDocumentRangeSemanticTokens(this._provider, model, range, token)));129const sw = new StopWatch(false);130request.then((r) => {131this._debounceInformation.update(model, sw.elapsed());132if (!r || !r.tokens || model.isDisposed() || model.getVersionId() !== requestVersionId) {133return;134}135const { provider, tokens: result } = r;136const styling = this._semanticTokensStylingService.getStyling(provider);137model.tokenization.setPartialSemanticTokens(range, toMultilineTokens2(result, styling, model.getLanguageId()));138}).then(() => this._removeOutstandingRequest(request), () => this._removeOutstandingRequest(request));139return request;140}141}142143registerEditorContribution(ViewportSemanticTokensContribution.ID, ViewportSemanticTokensContribution, EditorContributionInstantiation.AfterFirstRender);144145146