@21epub/epub-thirdparty
Version:
epub-thirdparty
745 lines (744 loc) • 36.3 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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;