123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
- return c > 3 && r && Object.defineProperty(target, key, r), r;
- };
- var __param = (this && this.__param) || function (paramIndex, decorator) {
- return function (target, key) { decorator(target, key, paramIndex); }
- };
- import { Emitter } from '../../../base/common/event.js';
- import { Disposable, DisposableStore, dispose } from '../../../base/common/lifecycle.js';
- import * as platform from '../../../base/common/platform.js';
- import * as errors from '../../../base/common/errors.js';
- import { EDITOR_MODEL_DEFAULTS } from '../config/editorOptions.js';
- import { TextModel } from '../model/textModel.js';
- import { DocumentSemanticTokensProviderRegistry } from '../modes.js';
- import { PLAINTEXT_MODE_ID } from '../modes/modesRegistry.js';
- import { IModeService } from './modeService.js';
- import { ITextResourcePropertiesService } from './textResourceConfigurationService.js';
- import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
- import { RunOnceScheduler } from '../../../base/common/async.js';
- import { CancellationTokenSource } from '../../../base/common/cancellation.js';
- import { IThemeService } from '../../../platform/theme/common/themeService.js';
- import { ILogService } from '../../../platform/log/common/log.js';
- import { IUndoRedoService } from '../../../platform/undoRedo/common/undoRedo.js';
- import { StringSHA1 } from '../../../base/common/hash.js';
- import { isEditStackElement } from '../model/editStack.js';
- import { Schemas } from '../../../base/common/network.js';
- import { SemanticTokensProviderStyling, toMultilineTokens2 } from './semanticTokensProviderStyling.js';
- import { getDocumentSemanticTokens, hasDocumentSemanticTokensProvider, isSemanticTokens, isSemanticTokensEdits } from './getSemanticTokens.js';
- import { equals } from '../../../base/common/objects.js';
- import { ILanguageConfigurationService } from '../modes/languageConfigurationRegistry.js';
- function MODEL_ID(resource) {
- return resource.toString();
- }
- function computeModelSha1(model) {
- // compute the sha1
- const shaComputer = new StringSHA1();
- const snapshot = model.createSnapshot();
- let text;
- while ((text = snapshot.read())) {
- shaComputer.update(text);
- }
- return shaComputer.digest();
- }
- class ModelData {
- constructor(model, onWillDispose, onDidChangeLanguage) {
- this._modelEventListeners = new DisposableStore();
- this.model = model;
- this._languageSelection = null;
- this._languageSelectionListener = null;
- this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
- this._modelEventListeners.add(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
- }
- _disposeLanguageSelection() {
- if (this._languageSelectionListener) {
- this._languageSelectionListener.dispose();
- this._languageSelectionListener = null;
- }
- }
- dispose() {
- this._modelEventListeners.dispose();
- this._disposeLanguageSelection();
- }
- setLanguage(languageSelection) {
- this._disposeLanguageSelection();
- this._languageSelection = languageSelection;
- this._languageSelectionListener = this._languageSelection.onDidChange(() => this.model.setMode(languageSelection.languageId));
- this.model.setMode(languageSelection.languageId);
- }
- }
- const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? 1 /* LF */ : 2 /* CRLF */;
- class DisposedModelInfo {
- constructor(uri, initialUndoRedoSnapshot, time, sharesUndoRedoStack, heapSize, sha1, versionId, alternativeVersionId) {
- this.uri = uri;
- this.initialUndoRedoSnapshot = initialUndoRedoSnapshot;
- this.time = time;
- this.sharesUndoRedoStack = sharesUndoRedoStack;
- this.heapSize = heapSize;
- this.sha1 = sha1;
- this.versionId = versionId;
- this.alternativeVersionId = alternativeVersionId;
- }
- }
- let ModelServiceImpl = class ModelServiceImpl extends Disposable {
- constructor(_configurationService, _resourcePropertiesService, _themeService, _logService, _undoRedoService, _modeService, _languageConfigurationService) {
- super();
- this._configurationService = _configurationService;
- this._resourcePropertiesService = _resourcePropertiesService;
- this._themeService = _themeService;
- this._logService = _logService;
- this._undoRedoService = _undoRedoService;
- this._modeService = _modeService;
- this._languageConfigurationService = _languageConfigurationService;
- this._onModelAdded = this._register(new Emitter());
- this.onModelAdded = this._onModelAdded.event;
- this._onModelRemoved = this._register(new Emitter());
- this.onModelRemoved = this._onModelRemoved.event;
- this._onModelModeChanged = this._register(new Emitter());
- this.onModelModeChanged = this._onModelModeChanged.event;
- this._modelCreationOptionsByLanguageAndResource = Object.create(null);
- this._models = {};
- this._disposedModels = new Map();
- this._disposedModelsHeapSize = 0;
- this._semanticStyling = this._register(new SemanticStyling(this._themeService, this._modeService, this._logService));
- this._register(this._configurationService.onDidChangeConfiguration(() => this._updateModelOptions()));
- this._updateModelOptions();
- this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._semanticStyling));
- }
- static _readModelOptions(config, isForSimpleWidget) {
- var _a;
- let tabSize = EDITOR_MODEL_DEFAULTS.tabSize;
- if (config.editor && typeof config.editor.tabSize !== 'undefined') {
- const parsedTabSize = parseInt(config.editor.tabSize, 10);
- if (!isNaN(parsedTabSize)) {
- tabSize = parsedTabSize;
- }
- if (tabSize < 1) {
- tabSize = 1;
- }
- }
- let indentSize = tabSize;
- if (config.editor && typeof config.editor.indentSize !== 'undefined' && config.editor.indentSize !== 'tabSize') {
- const parsedIndentSize = parseInt(config.editor.indentSize, 10);
- if (!isNaN(parsedIndentSize)) {
- indentSize = parsedIndentSize;
- }
- if (indentSize < 1) {
- indentSize = 1;
- }
- }
- let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces;
- if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
- insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
- }
- let newDefaultEOL = DEFAULT_EOL;
- const eol = config.eol;
- if (eol === '\r\n') {
- newDefaultEOL = 2 /* CRLF */;
- }
- else if (eol === '\n') {
- newDefaultEOL = 1 /* LF */;
- }
- let trimAutoWhitespace = EDITOR_MODEL_DEFAULTS.trimAutoWhitespace;
- if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') {
- trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace));
- }
- let detectIndentation = EDITOR_MODEL_DEFAULTS.detectIndentation;
- if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
- detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
- }
- let largeFileOptimizations = EDITOR_MODEL_DEFAULTS.largeFileOptimizations;
- if (config.editor && typeof config.editor.largeFileOptimizations !== 'undefined') {
- largeFileOptimizations = (config.editor.largeFileOptimizations === 'false' ? false : Boolean(config.editor.largeFileOptimizations));
- }
- let bracketPairColorizationOptions = EDITOR_MODEL_DEFAULTS.bracketPairColorizationOptions;
- if (((_a = config.editor) === null || _a === void 0 ? void 0 : _a.bracketPairColorization) && typeof config.editor.bracketPairColorization === 'object') {
- bracketPairColorizationOptions = {
- enabled: !!config.editor.bracketPairColorization.enabled
- };
- }
- return {
- isForSimpleWidget: isForSimpleWidget,
- tabSize: tabSize,
- indentSize: indentSize,
- insertSpaces: insertSpaces,
- detectIndentation: detectIndentation,
- defaultEOL: newDefaultEOL,
- trimAutoWhitespace: trimAutoWhitespace,
- largeFileOptimizations: largeFileOptimizations,
- bracketPairColorizationOptions
- };
- }
- _getEOL(resource, language) {
- if (resource) {
- return this._resourcePropertiesService.getEOL(resource, language);
- }
- const eol = this._configurationService.getValue('files.eol', { overrideIdentifier: language });
- if (eol && typeof eol === 'string' && eol !== 'auto') {
- return eol;
- }
- return platform.OS === 3 /* Linux */ || platform.OS === 2 /* Macintosh */ ? '\n' : '\r\n';
- }
- _shouldRestoreUndoStack() {
- const result = this._configurationService.getValue('files.restoreUndoStack');
- if (typeof result === 'boolean') {
- return result;
- }
- return true;
- }
- getCreationOptions(language, resource, isForSimpleWidget) {
- let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
- if (!creationOptions) {
- const editor = this._configurationService.getValue('editor', { overrideIdentifier: language, resource });
- const eol = this._getEOL(resource, language);
- creationOptions = ModelServiceImpl._readModelOptions({ editor, eol }, isForSimpleWidget);
- this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions;
- }
- return creationOptions;
- }
- _updateModelOptions() {
- const oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource;
- this._modelCreationOptionsByLanguageAndResource = Object.create(null);
- // Update options on all models
- const keys = Object.keys(this._models);
- for (let i = 0, len = keys.length; i < len; i++) {
- const modelId = keys[i];
- const modelData = this._models[modelId];
- const language = modelData.model.getLanguageId();
- const uri = modelData.model.uri;
- const oldOptions = oldOptionsByLanguageAndResource[language + uri];
- const newOptions = this.getCreationOptions(language, uri, modelData.model.isForSimpleWidget);
- ModelServiceImpl._setModelOptionsForModel(modelData.model, newOptions, oldOptions);
- }
- }
- static _setModelOptionsForModel(model, newOptions, currentOptions) {
- if (currentOptions && currentOptions.defaultEOL !== newOptions.defaultEOL && model.getLineCount() === 1) {
- model.setEOL(newOptions.defaultEOL === 1 /* LF */ ? 0 /* LF */ : 1 /* CRLF */);
- }
- if (currentOptions
- && (currentOptions.detectIndentation === newOptions.detectIndentation)
- && (currentOptions.insertSpaces === newOptions.insertSpaces)
- && (currentOptions.tabSize === newOptions.tabSize)
- && (currentOptions.indentSize === newOptions.indentSize)
- && (currentOptions.trimAutoWhitespace === newOptions.trimAutoWhitespace)
- && equals(currentOptions.bracketPairColorizationOptions, newOptions.bracketPairColorizationOptions)) {
- // Same indent opts, no need to touch the model
- return;
- }
- if (newOptions.detectIndentation) {
- model.detectIndentation(newOptions.insertSpaces, newOptions.tabSize);
- model.updateOptions({
- trimAutoWhitespace: newOptions.trimAutoWhitespace,
- bracketColorizationOptions: newOptions.bracketPairColorizationOptions
- });
- }
- else {
- model.updateOptions({
- insertSpaces: newOptions.insertSpaces,
- tabSize: newOptions.tabSize,
- indentSize: newOptions.indentSize,
- trimAutoWhitespace: newOptions.trimAutoWhitespace,
- bracketColorizationOptions: newOptions.bracketPairColorizationOptions
- });
- }
- }
- // --- begin IModelService
- _insertDisposedModel(disposedModelData) {
- this._disposedModels.set(MODEL_ID(disposedModelData.uri), disposedModelData);
- this._disposedModelsHeapSize += disposedModelData.heapSize;
- }
- _removeDisposedModel(resource) {
- const disposedModelData = this._disposedModels.get(MODEL_ID(resource));
- if (disposedModelData) {
- this._disposedModelsHeapSize -= disposedModelData.heapSize;
- }
- this._disposedModels.delete(MODEL_ID(resource));
- return disposedModelData;
- }
- _ensureDisposedModelsHeapSize(maxModelsHeapSize) {
- if (this._disposedModelsHeapSize > maxModelsHeapSize) {
- // we must remove some old undo stack elements to free up some memory
- const disposedModels = [];
- this._disposedModels.forEach(entry => {
- if (!entry.sharesUndoRedoStack) {
- disposedModels.push(entry);
- }
- });
- disposedModels.sort((a, b) => a.time - b.time);
- while (disposedModels.length > 0 && this._disposedModelsHeapSize > maxModelsHeapSize) {
- const disposedModel = disposedModels.shift();
- this._removeDisposedModel(disposedModel.uri);
- if (disposedModel.initialUndoRedoSnapshot !== null) {
- this._undoRedoService.restoreSnapshot(disposedModel.initialUndoRedoSnapshot);
- }
- }
- }
- }
- _createModelData(value, languageId, resource, isForSimpleWidget) {
- // create & save the model
- const options = this.getCreationOptions(languageId, resource, isForSimpleWidget);
- const model = new TextModel(value, options, languageId, resource, this._undoRedoService, this._modeService, this._languageConfigurationService);
- if (resource && this._disposedModels.has(MODEL_ID(resource))) {
- const disposedModelData = this._removeDisposedModel(resource);
- const elements = this._undoRedoService.getElements(resource);
- const sha1IsEqual = (computeModelSha1(model) === disposedModelData.sha1);
- if (sha1IsEqual || disposedModelData.sharesUndoRedoStack) {
- for (const element of elements.past) {
- if (isEditStackElement(element) && element.matchesResource(resource)) {
- element.setModel(model);
- }
- }
- for (const element of elements.future) {
- if (isEditStackElement(element) && element.matchesResource(resource)) {
- element.setModel(model);
- }
- }
- this._undoRedoService.setElementsValidFlag(resource, true, (element) => (isEditStackElement(element) && element.matchesResource(resource)));
- if (sha1IsEqual) {
- model._overwriteVersionId(disposedModelData.versionId);
- model._overwriteAlternativeVersionId(disposedModelData.alternativeVersionId);
- model._overwriteInitialUndoRedoSnapshot(disposedModelData.initialUndoRedoSnapshot);
- }
- }
- else {
- if (disposedModelData.initialUndoRedoSnapshot !== null) {
- this._undoRedoService.restoreSnapshot(disposedModelData.initialUndoRedoSnapshot);
- }
- }
- }
- const modelId = MODEL_ID(model.uri);
- if (this._models[modelId]) {
- // There already exists a model with this id => this is a programmer error
- throw new Error('ModelService: Cannot add model because it already exists!');
- }
- const modelData = new ModelData(model, (model) => this._onWillDispose(model), (model, e) => this._onDidChangeLanguage(model, e));
- this._models[modelId] = modelData;
- return modelData;
- }
- createModel(value, languageSelection, resource, isForSimpleWidget = false) {
- let modelData;
- if (languageSelection) {
- modelData = this._createModelData(value, languageSelection.languageId, resource, isForSimpleWidget);
- this.setMode(modelData.model, languageSelection);
- }
- else {
- modelData = this._createModelData(value, PLAINTEXT_MODE_ID, resource, isForSimpleWidget);
- }
- this._onModelAdded.fire(modelData.model);
- return modelData.model;
- }
- setMode(model, languageSelection) {
- if (!languageSelection) {
- return;
- }
- const modelData = this._models[MODEL_ID(model.uri)];
- if (!modelData) {
- return;
- }
- modelData.setLanguage(languageSelection);
- }
- getModels() {
- const ret = [];
- const keys = Object.keys(this._models);
- for (let i = 0, len = keys.length; i < len; i++) {
- const modelId = keys[i];
- ret.push(this._models[modelId].model);
- }
- return ret;
- }
- getModel(resource) {
- const modelId = MODEL_ID(resource);
- const modelData = this._models[modelId];
- if (!modelData) {
- return null;
- }
- return modelData.model;
- }
- getSemanticTokensProviderStyling(provider) {
- return this._semanticStyling.get(provider);
- }
- // --- end IModelService
- _schemaShouldMaintainUndoRedoElements(resource) {
- return (resource.scheme === Schemas.file
- || resource.scheme === Schemas.vscodeRemote
- || resource.scheme === Schemas.userData
- || resource.scheme === Schemas.vscodeNotebookCell
- || resource.scheme === 'fake-fs' // for tests
- );
- }
- _onWillDispose(model) {
- const modelId = MODEL_ID(model.uri);
- const modelData = this._models[modelId];
- const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString());
- let maintainUndoRedoStack = false;
- let heapSize = 0;
- if (sharesUndoRedoStack || (this._shouldRestoreUndoStack() && this._schemaShouldMaintainUndoRedoElements(model.uri))) {
- const elements = this._undoRedoService.getElements(model.uri);
- if (elements.past.length > 0 || elements.future.length > 0) {
- for (const element of elements.past) {
- if (isEditStackElement(element) && element.matchesResource(model.uri)) {
- maintainUndoRedoStack = true;
- heapSize += element.heapSize(model.uri);
- element.setModel(model.uri); // remove reference from text buffer instance
- }
- }
- for (const element of elements.future) {
- if (isEditStackElement(element) && element.matchesResource(model.uri)) {
- maintainUndoRedoStack = true;
- heapSize += element.heapSize(model.uri);
- element.setModel(model.uri); // remove reference from text buffer instance
- }
- }
- }
- }
- const maxMemory = ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK;
- if (!maintainUndoRedoStack) {
- if (!sharesUndoRedoStack) {
- const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
- if (initialUndoRedoSnapshot !== null) {
- this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
- }
- }
- }
- else if (!sharesUndoRedoStack && heapSize > maxMemory) {
- // the undo stack for this file would never fit in the configured memory, so don't bother with it.
- const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
- if (initialUndoRedoSnapshot !== null) {
- this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
- }
- }
- else {
- this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
- // We only invalidate the elements, but they remain in the undo-redo service.
- this._undoRedoService.setElementsValidFlag(model.uri, false, (element) => (isEditStackElement(element) && element.matchesResource(model.uri)));
- this._insertDisposedModel(new DisposedModelInfo(model.uri, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
- }
- delete this._models[modelId];
- modelData.dispose();
- // clean up cache
- delete this._modelCreationOptionsByLanguageAndResource[model.getLanguageId() + model.uri];
- this._onModelRemoved.fire(model);
- }
- _onDidChangeLanguage(model, e) {
- const oldModeId = e.oldLanguage;
- const newModeId = model.getLanguageId();
- const oldOptions = this.getCreationOptions(oldModeId, model.uri, model.isForSimpleWidget);
- const newOptions = this.getCreationOptions(newModeId, model.uri, model.isForSimpleWidget);
- ModelServiceImpl._setModelOptionsForModel(model, newOptions, oldOptions);
- this._onModelModeChanged.fire({ model, oldModeId });
- }
- };
- ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK = 20 * 1024 * 1024;
- ModelServiceImpl = __decorate([
- __param(0, IConfigurationService),
- __param(1, ITextResourcePropertiesService),
- __param(2, IThemeService),
- __param(3, ILogService),
- __param(4, IUndoRedoService),
- __param(5, IModeService),
- __param(6, ILanguageConfigurationService)
- ], ModelServiceImpl);
- export { ModelServiceImpl };
- export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting';
- export function isSemanticColoringEnabled(model, themeService, configurationService) {
- var _a;
- const setting = (_a = configurationService.getValue(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageId(), resource: model.uri })) === null || _a === void 0 ? void 0 : _a.enabled;
- if (typeof setting === 'boolean') {
- return setting;
- }
- return themeService.getColorTheme().semanticHighlighting;
- }
- class SemanticColoringFeature extends Disposable {
- constructor(modelService, themeService, configurationService, semanticStyling) {
- super();
- this._watchers = Object.create(null);
- this._semanticStyling = semanticStyling;
- const register = (model) => {
- this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling);
- };
- const deregister = (model, modelSemanticColoring) => {
- modelSemanticColoring.dispose();
- delete this._watchers[model.uri.toString()];
- };
- const handleSettingOrThemeChange = () => {
- for (let model of modelService.getModels()) {
- const curr = this._watchers[model.uri.toString()];
- if (isSemanticColoringEnabled(model, themeService, configurationService)) {
- if (!curr) {
- register(model);
- }
- }
- else {
- if (curr) {
- deregister(model, curr);
- }
- }
- }
- };
- this._register(modelService.onModelAdded((model) => {
- if (isSemanticColoringEnabled(model, themeService, configurationService)) {
- register(model);
- }
- }));
- this._register(modelService.onModelRemoved((model) => {
- const curr = this._watchers[model.uri.toString()];
- if (curr) {
- deregister(model, curr);
- }
- }));
- this._register(configurationService.onDidChangeConfiguration(e => {
- if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
- handleSettingOrThemeChange();
- }
- }));
- this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
- }
- }
- class SemanticStyling extends Disposable {
- constructor(_themeService, _modeService, _logService) {
- super();
- this._themeService = _themeService;
- this._modeService = _modeService;
- this._logService = _logService;
- this._caches = new WeakMap();
- this._register(this._themeService.onDidColorThemeChange(() => {
- this._caches = new WeakMap();
- }));
- }
- get(provider) {
- if (!this._caches.has(provider)) {
- this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._modeService, this._logService));
- }
- return this._caches.get(provider);
- }
- }
- class SemanticTokensResponse {
- constructor(provider, resultId, data) {
- this.provider = provider;
- this.resultId = resultId;
- this.data = data;
- }
- dispose() {
- this.provider.releaseDocumentSemanticTokens(this.resultId);
- }
- }
- export class ModelSemanticColoring extends Disposable {
- constructor(model, themeService, stylingProvider) {
- super();
- this._isDisposed = false;
- this._model = model;
- this._semanticStyling = stylingProvider;
- this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY));
- this._currentDocumentResponse = null;
- this._currentDocumentRequestCancellationTokenSource = null;
- this._documentProvidersChangeListeners = [];
- this._register(this._model.onDidChangeContent(() => {
- if (!this._fetchDocumentSemanticTokens.isScheduled()) {
- this._fetchDocumentSemanticTokens.schedule();
- }
- }));
- this._register(this._model.onDidChangeLanguage(() => {
- // clear any outstanding state
- if (this._currentDocumentResponse) {
- this._currentDocumentResponse.dispose();
- this._currentDocumentResponse = null;
- }
- if (this._currentDocumentRequestCancellationTokenSource) {
- this._currentDocumentRequestCancellationTokenSource.cancel();
- this._currentDocumentRequestCancellationTokenSource = null;
- }
- this._setDocumentSemanticTokens(null, null, null, []);
- this._fetchDocumentSemanticTokens.schedule(0);
- }));
- const bindDocumentChangeListeners = () => {
- dispose(this._documentProvidersChangeListeners);
- this._documentProvidersChangeListeners = [];
- for (const provider of DocumentSemanticTokensProviderRegistry.all(model)) {
- if (typeof provider.onDidChange === 'function') {
- this._documentProvidersChangeListeners.push(provider.onDidChange(() => this._fetchDocumentSemanticTokens.schedule(0)));
- }
- }
- };
- bindDocumentChangeListeners();
- this._register(DocumentSemanticTokensProviderRegistry.onDidChange(() => {
- bindDocumentChangeListeners();
- this._fetchDocumentSemanticTokens.schedule();
- }));
- this._register(themeService.onDidColorThemeChange(_ => {
- // clear out existing tokens
- this._setDocumentSemanticTokens(null, null, null, []);
- this._fetchDocumentSemanticTokens.schedule();
- }));
- this._fetchDocumentSemanticTokens.schedule(0);
- }
- dispose() {
- if (this._currentDocumentResponse) {
- this._currentDocumentResponse.dispose();
- this._currentDocumentResponse = null;
- }
- if (this._currentDocumentRequestCancellationTokenSource) {
- this._currentDocumentRequestCancellationTokenSource.cancel();
- this._currentDocumentRequestCancellationTokenSource = null;
- }
- this._setDocumentSemanticTokens(null, null, null, []);
- this._isDisposed = true;
- super.dispose();
- }
- _fetchDocumentSemanticTokensNow() {
- if (this._currentDocumentRequestCancellationTokenSource) {
- // there is already a request running, let it finish...
- return;
- }
- if (!hasDocumentSemanticTokensProvider(this._model)) {
- // there is no provider
- if (this._currentDocumentResponse) {
- // there are semantic tokens set
- this._model.setSemanticTokens(null, false);
- }
- return;
- }
- const cancellationTokenSource = new CancellationTokenSource();
- const lastProvider = this._currentDocumentResponse ? this._currentDocumentResponse.provider : null;
- const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
- const request = getDocumentSemanticTokens(this._model, lastProvider, lastResultId, cancellationTokenSource.token);
- this._currentDocumentRequestCancellationTokenSource = cancellationTokenSource;
- const pendingChanges = [];
- const contentChangeListener = this._model.onDidChangeContent((e) => {
- pendingChanges.push(e);
- });
- request.then((res) => {
- this._currentDocumentRequestCancellationTokenSource = null;
- contentChangeListener.dispose();
- if (!res) {
- this._setDocumentSemanticTokens(null, null, null, pendingChanges);
- }
- else {
- const { provider, tokens } = res;
- const styling = this._semanticStyling.get(provider);
- this._setDocumentSemanticTokens(provider, tokens || null, styling, pendingChanges);
- }
- }, (err) => {
- const isExpectedError = err && (errors.isPromiseCanceledError(err) || (typeof err.message === 'string' && err.message.indexOf('busy') !== -1));
- if (!isExpectedError) {
- errors.onUnexpectedError(err);
- }
- // Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
- // The API does not have a special error kind to express this...
- this._currentDocumentRequestCancellationTokenSource = null;
- contentChangeListener.dispose();
- if (pendingChanges.length > 0) {
- // More changes occurred while the request was running
- if (!this._fetchDocumentSemanticTokens.isScheduled()) {
- this._fetchDocumentSemanticTokens.schedule();
- }
- }
- });
- }
- static _copy(src, srcOffset, dest, destOffset, length) {
- for (let i = 0; i < length; i++) {
- dest[destOffset + i] = src[srcOffset + i];
- }
- }
- _setDocumentSemanticTokens(provider, tokens, styling, pendingChanges) {
- const currentResponse = this._currentDocumentResponse;
- const rescheduleIfNeeded = () => {
- if (pendingChanges.length > 0 && !this._fetchDocumentSemanticTokens.isScheduled()) {
- this._fetchDocumentSemanticTokens.schedule();
- }
- };
- if (this._currentDocumentResponse) {
- this._currentDocumentResponse.dispose();
- this._currentDocumentResponse = null;
- }
- if (this._isDisposed) {
- // disposed!
- if (provider && tokens) {
- provider.releaseDocumentSemanticTokens(tokens.resultId);
- }
- return;
- }
- if (!provider || !styling) {
- this._model.setSemanticTokens(null, false);
- return;
- }
- if (!tokens) {
- this._model.setSemanticTokens(null, true);
- rescheduleIfNeeded();
- return;
- }
- if (isSemanticTokensEdits(tokens)) {
- if (!currentResponse) {
- // not possible!
- this._model.setSemanticTokens(null, true);
- return;
- }
- if (tokens.edits.length === 0) {
- // nothing to do!
- tokens = {
- resultId: tokens.resultId,
- data: currentResponse.data
- };
- }
- else {
- let deltaLength = 0;
- for (const edit of tokens.edits) {
- deltaLength += (edit.data ? edit.data.length : 0) - edit.deleteCount;
- }
- const srcData = currentResponse.data;
- const destData = new Uint32Array(srcData.length + deltaLength);
- let srcLastStart = srcData.length;
- let destLastStart = destData.length;
- for (let i = tokens.edits.length - 1; i >= 0; i--) {
- const edit = tokens.edits[i];
- const copyCount = srcLastStart - (edit.start + edit.deleteCount);
- if (copyCount > 0) {
- ModelSemanticColoring._copy(srcData, srcLastStart - copyCount, destData, destLastStart - copyCount, copyCount);
- destLastStart -= copyCount;
- }
- if (edit.data) {
- ModelSemanticColoring._copy(edit.data, 0, destData, destLastStart - edit.data.length, edit.data.length);
- destLastStart -= edit.data.length;
- }
- srcLastStart = edit.start;
- }
- if (srcLastStart > 0) {
- ModelSemanticColoring._copy(srcData, 0, destData, 0, srcLastStart);
- }
- tokens = {
- resultId: tokens.resultId,
- data: destData
- };
- }
- }
- if (isSemanticTokens(tokens)) {
- this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
- const result = toMultilineTokens2(tokens, styling, this._model.getLanguageId());
- // Adjust incoming semantic tokens
- if (pendingChanges.length > 0) {
- // More changes occurred while the request was running
- // We need to:
- // 1. Adjust incoming semantic tokens
- // 2. Request them again
- for (const change of pendingChanges) {
- for (const area of result) {
- for (const singleChange of change.changes) {
- area.applyEdit(singleChange.range, singleChange.text);
- }
- }
- }
- }
- this._model.setSemanticTokens(result, true);
- }
- else {
- this._model.setSemanticTokens(null, true);
- }
- rescheduleIfNeeded();
- }
- }
- ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = 300;
|