modelServiceImpl.ts 41.3 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import * as nls from 'vs/nls';
A
Alex Dima 已提交
7
import { Emitter, Event } from 'vs/base/common/event';
8
import { Disposable, IDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
9
import * as platform from 'vs/base/common/platform';
A
wip  
Alexandru Dima 已提交
10
import * as errors from 'vs/base/common/errors';
11
import { URI } from 'vs/base/common/uri';
12
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
A
Alex Dima 已提交
13
import { EditOperation } from 'vs/editor/common/core/editOperation';
J
Johannes Rieken 已提交
14
import { Range } from 'vs/editor/common/core/range';
15
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions, IValidEditOperation } from 'vs/editor/common/model';
16
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
17
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
18
import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata, FontStyle, MetadataConsts } from 'vs/editor/common/modes';
A
Alex Dima 已提交
19
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
A
Alex Dima 已提交
20
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
A
Alex Dima 已提交
21
import { IModelService } from 'vs/editor/common/services/modelService';
S
rename  
Sandeep Somavarapu 已提交
22
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
A
Alex Dima 已提交
23
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
A
wip  
Alexandru Dima 已提交
24 25
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
A
Alexandru Dima 已提交
26
import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore';
27
import { IThemeService } from 'vs/platform/theme/common/themeService';
28
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
29 30 31
import { IUndoRedoService, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
import { StringSHA1 } from 'vs/base/common/hash';
import { SingleModelEditStackElement, MultiModelEditStackElement, EditStackElement } from 'vs/editor/common/model/editStack';
32 33 34
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
35 36

export const MAINTAIN_UNDO_REDO_STACK = true;
E
Erich Gamma 已提交
37

38 39 40 41
export interface IEditorSemanticHighlightingOptions {
	enabled?: boolean;
}

B
Benjamin Pasero 已提交
42
function MODEL_ID(resource: URI): string {
A
Alex Dima 已提交
43 44 45
	return resource.toString();
}

46 47 48 49 50 51 52 53 54 55 56 57
function computeModelSha1(model: ITextModel): string {
	// compute the sha1
	const shaComputer = new StringSHA1();
	const snapshot = model.createSnapshot();
	let text: string | null;
	while ((text = snapshot.read())) {
		shaComputer.update(text);
	}
	return shaComputer.digest();
}


A
Alex Dima 已提交
58
class ModelData implements IDisposable {
A
Alex Dima 已提交
59 60 61 62
	public readonly model: ITextModel;

	private _languageSelection: ILanguageSelection | null;
	private _languageSelectionListener: IDisposable | null;
E
Erich Gamma 已提交
63

64
	private readonly _modelEventListeners = new DisposableStore();
E
Erich Gamma 已提交
65

66
	constructor(
A
Alex Dima 已提交
67 68 69
		model: ITextModel,
		onWillDispose: (model: ITextModel) => void,
		onDidChangeLanguage: (model: ITextModel, e: IModelLanguageChangedEvent) => void
70
	) {
E
Erich Gamma 已提交
71
		this.model = model;
A
Alex Dima 已提交
72

A
Alex Dima 已提交
73 74 75
		this._languageSelection = null;
		this._languageSelectionListener = null;

76 77
		this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
		this._modelEventListeners.add(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
E
Erich Gamma 已提交
78 79
	}

A
Alex Dima 已提交
80 81 82 83 84 85 86 87 88 89 90
	private _disposeLanguageSelection(): void {
		if (this._languageSelectionListener) {
			this._languageSelectionListener.dispose();
			this._languageSelectionListener = null;
		}
		if (this._languageSelection) {
			this._languageSelection.dispose();
			this._languageSelection = null;
		}
	}

E
Erich Gamma 已提交
91
	public dispose(): void {
92
		this._modelEventListeners.dispose();
A
Alex Dima 已提交
93
		this._disposeLanguageSelection();
A
Alex Dima 已提交
94
	}
E
Erich Gamma 已提交
95

A
Alex Dima 已提交
96 97 98 99 100 101
	public setLanguage(languageSelection: ILanguageSelection): void {
		this._disposeLanguageSelection();
		this._languageSelection = languageSelection;
		this._languageSelectionListener = this._languageSelection.onDidChange(() => this.model.setMode(languageSelection.languageIdentifier));
		this.model.setMode(languageSelection.languageIdentifier);
	}
A
Alex Dima 已提交
102
}
E
Erich Gamma 已提交
103

S
Sandeep Somavarapu 已提交
104 105
interface IRawEditorConfig {
	tabSize?: any;
D
David Lechner 已提交
106
	indentSize?: any;
S
Sandeep Somavarapu 已提交
107 108 109 110 111 112 113
	insertSpaces?: any;
	detectIndentation?: any;
	trimAutoWhitespace?: any;
	creationOptions?: any;
	largeFileOptimizations?: any;
}

114
interface IRawConfig {
S
Sandeep Somavarapu 已提交
115 116
	eol?: any;
	editor?: IRawEditorConfig;
117 118
}

119
const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF;
120

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
interface EditStackPastFutureElements {
	past: EditStackElement[];
	future: EditStackElement[];
}

function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements {
	return (isEditStackElements(undoElements.past) && isEditStackElements(undoElements.future));
}

function isEditStackElements(elements: IUndoRedoElement[]): elements is EditStackElement[] {
	for (const element of elements) {
		if (element instanceof SingleModelEditStackElement) {
			continue;
		}
		if (element instanceof MultiModelEditStackElement) {
			continue;
		}
		return false;
	}
	return true;
}

class DisposedModelInfo {
	constructor(
		public readonly uri: URI,
		public readonly sha1: string,
147
		public readonly versionId: number,
148 149 150 151
		public readonly alternativeVersionId: number,
	) { }
}

A
Alex Dima 已提交
152
export class ModelServiceImpl extends Disposable implements IModelService {
E
Erich Gamma 已提交
153

154 155 156
	private static _PROMPT_UNDO_REDO_SIZE_LIMIT = 10 * 1024 * 1024; // 10MB

	public _serviceBrand: undefined;
E
Erich Gamma 已提交
157

A
Alex Dima 已提交
158 159 160 161 162 163
	private readonly _onModelAdded: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
	public readonly onModelAdded: Event<ITextModel> = this._onModelAdded.event;

	private readonly _onModelRemoved: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
	public readonly onModelRemoved: Event<ITextModel> = this._onModelRemoved.event;

164
	private readonly _onModelModeChanged: Emitter<{ model: ITextModel; oldModeId: string; }> = this._register(new Emitter<{ model: ITextModel; oldModeId: string; }>());
A
Alex Dima 已提交
165
	public readonly onModelModeChanged: Event<{ model: ITextModel; oldModeId: string; }> = this._onModelModeChanged.event;
166

167
	private _modelCreationOptionsByLanguageAndResource: { [languageAndResource: string]: ITextModelCreationOptions; };
A
Alex Dima 已提交
168 169 170 171

	/**
	 * All the models known in the system.
	 */
172
	private readonly _models: { [modelId: string]: ModelData; };
173
	private readonly _disposedModels: Map<string, DisposedModelInfo>;
E
Erich Gamma 已提交
174

175
	constructor(
176 177 178 179 180 181
		@IConfigurationService private readonly _configurationService: IConfigurationService,
		@ITextResourcePropertiesService private readonly _resourcePropertiesService: ITextResourcePropertiesService,
		@IThemeService private readonly _themeService: IThemeService,
		@ILogService private readonly _logService: ILogService,
		@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
		@IDialogService private readonly _dialogService: IDialogService,
182
	) {
A
Alex Dima 已提交
183
		super();
184
		this._modelCreationOptionsByLanguageAndResource = Object.create(null);
185 186
		this._models = {};
		this._disposedModels = new Map<string, DisposedModelInfo>();
B
Benjamin Pasero 已提交
187

188
		this._register(this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions()));
189
		this._updateModelOptions();
A
wip  
Alexandru Dima 已提交
190

191
		this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._logService));
192
	}
J
Joao Moreno 已提交
193

194
	private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
195
		let tabSize = EDITOR_MODEL_DEFAULTS.tabSize;
196
		if (config.editor && typeof config.editor.tabSize !== 'undefined') {
A
Alex Dima 已提交
197
			const parsedTabSize = parseInt(config.editor.tabSize, 10);
198 199
			if (!isNaN(parsedTabSize)) {
				tabSize = parsedTabSize;
200
			}
A
Alex Dima 已提交
201 202 203
			if (tabSize < 1) {
				tabSize = 1;
			}
204
		}
205

D
David Lechner 已提交
206
		let indentSize = tabSize;
A
Alex Dima 已提交
207
		if (config.editor && typeof config.editor.indentSize !== 'undefined' && config.editor.indentSize !== 'tabSize') {
A
Alex Dima 已提交
208
			const parsedIndentSize = parseInt(config.editor.indentSize, 10);
D
David Lechner 已提交
209 210 211 212 213 214 215 216
			if (!isNaN(parsedIndentSize)) {
				indentSize = parsedIndentSize;
			}
			if (indentSize < 1) {
				indentSize = 1;
			}
		}

217
		let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces;
218 219 220
		if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
			insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
		}
221

222
		let newDefaultEOL = DEFAULT_EOL;
S
Sandeep Somavarapu 已提交
223
		const eol = config.eol;
224
		if (eol === '\r\n') {
225
			newDefaultEOL = DefaultEndOfLine.CRLF;
226
		} else if (eol === '\n') {
227
			newDefaultEOL = DefaultEndOfLine.LF;
228
		}
229

230
		let trimAutoWhitespace = EDITOR_MODEL_DEFAULTS.trimAutoWhitespace;
231 232 233
		if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') {
			trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace));
		}
234

235
		let detectIndentation = EDITOR_MODEL_DEFAULTS.detectIndentation;
236 237 238
		if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
			detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
		}
239

240 241 242
		let largeFileOptimizations = EDITOR_MODEL_DEFAULTS.largeFileOptimizations;
		if (config.editor && typeof config.editor.largeFileOptimizations !== 'undefined') {
			largeFileOptimizations = (config.editor.largeFileOptimizations === 'false' ? false : Boolean(config.editor.largeFileOptimizations));
243 244
		}

245
		return {
246
			isForSimpleWidget: isForSimpleWidget,
247
			tabSize: tabSize,
D
David Lechner 已提交
248
			indentSize: indentSize,
249 250 251
			insertSpaces: insertSpaces,
			detectIndentation: detectIndentation,
			defaultEOL: newDefaultEOL,
252
			trimAutoWhitespace: trimAutoWhitespace,
253
			largeFileOptimizations: largeFileOptimizations
254
		};
E
Erich Gamma 已提交
255 256
	}

257 258 259 260 261 262 263 264 265 266 267
	private _getEOL(resource: URI | undefined, language: string): string {
		if (resource) {
			return this._resourcePropertiesService.getEOL(resource, language);
		}
		const eol = this._configurationService.getValue<string>('files.eol', { overrideIdentifier: language });
		if (eol && eol !== 'auto') {
			return eol;
		}
		return platform.OS === platform.OperatingSystem.Linux || platform.OS === platform.OperatingSystem.Macintosh ? '\n' : '\r\n';
	}

A
Alex Dima 已提交
268
	public getCreationOptions(language: string, resource: URI | undefined, isForSimpleWidget: boolean): ITextModelCreationOptions {
269
		let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
270
		if (!creationOptions) {
S
Sandeep Somavarapu 已提交
271
			const editor = this._configurationService.getValue<IRawEditorConfig>('editor', { overrideIdentifier: language, resource });
272
			const eol = this._getEOL(resource, language);
S
Sandeep Somavarapu 已提交
273
			creationOptions = ModelServiceImpl._readModelOptions({ editor, eol }, isForSimpleWidget);
274
			this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions;
275 276
		}
		return creationOptions;
277 278
	}

279
	private _updateModelOptions(): void {
A
Alex Dima 已提交
280
		const oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource;
281
		this._modelCreationOptionsByLanguageAndResource = Object.create(null);
282

283
		// Update options on all models
A
Alex Dima 已提交
284
		const keys = Object.keys(this._models);
A
Alex Dima 已提交
285
		for (let i = 0, len = keys.length; i < len; i++) {
A
Alex Dima 已提交
286 287
			const modelId = keys[i];
			const modelData = this._models[modelId];
288
			const language = modelData.model.getLanguageIdentifier().language;
289 290
			const uri = modelData.model.uri;
			const oldOptions = oldOptionsByLanguageAndResource[language + uri];
291
			const newOptions = this.getCreationOptions(language, uri, modelData.model.isForSimpleWidget);
292 293 294
			ModelServiceImpl._setModelOptionsForModel(modelData.model, newOptions, oldOptions);
		}
	}
295

A
Alex Dima 已提交
296
	private static _setModelOptionsForModel(model: ITextModel, newOptions: ITextModelCreationOptions, currentOptions: ITextModelCreationOptions): void {
297 298 299 300
		if (currentOptions && currentOptions.defaultEOL !== newOptions.defaultEOL && model.getLineCount() === 1) {
			model.setEOL(newOptions.defaultEOL === DefaultEndOfLine.LF ? EndOfLineSequence.LF : EndOfLineSequence.CRLF);
		}

301 302 303 304
		if (currentOptions
			&& (currentOptions.detectIndentation === newOptions.detectIndentation)
			&& (currentOptions.insertSpaces === newOptions.insertSpaces)
			&& (currentOptions.tabSize === newOptions.tabSize)
A
Alex Dima 已提交
305
			&& (currentOptions.indentSize === newOptions.indentSize)
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
			&& (currentOptions.trimAutoWhitespace === newOptions.trimAutoWhitespace)
		) {
			// Same indent opts, no need to touch the model
			return;
		}

		if (newOptions.detectIndentation) {
			model.detectIndentation(newOptions.insertSpaces, newOptions.tabSize);
			model.updateOptions({
				trimAutoWhitespace: newOptions.trimAutoWhitespace
			});
		} else {
			model.updateOptions({
				insertSpaces: newOptions.insertSpaces,
				tabSize: newOptions.tabSize,
A
Alex Dima 已提交
321
				indentSize: newOptions.indentSize,
322 323
				trimAutoWhitespace: newOptions.trimAutoWhitespace
			});
324
		}
325 326
	}

E
Erich Gamma 已提交
327 328
	// --- begin IModelService

A
Alex Dima 已提交
329
	private _createModelData(value: string | ITextBufferFactory, languageIdentifier: LanguageIdentifier, resource: URI | undefined, isForSimpleWidget: boolean): ModelData {
A
Alex Dima 已提交
330
		// create & save the model
331
		const options = this.getCreationOptions(languageIdentifier.language, resource, isForSimpleWidget);
332
		const model: TextModel = new TextModel(value, options, languageIdentifier, resource, this._undoRedoService);
333 334 335 336 337 338 339 340 341 342 343 344
		if (resource && this._disposedModels.has(MODEL_ID(resource))) {
			const disposedModelData = this._disposedModels.get(MODEL_ID(resource))!;
			this._disposedModels.delete(MODEL_ID(resource));
			const elements = this._undoRedoService.getElements(resource);
			if (computeModelSha1(model) === disposedModelData.sha1 && isEditStackPastFutureElements(elements)) {
				for (const element of elements.past) {
					element.setModel(model);
				}
				for (const element of elements.future) {
					element.setModel(model);
				}
				this._undoRedoService.setElementsIsValid(resource, true);
345
				model._overwriteVersionId(disposedModelData.versionId);
346 347 348 349 350
				model._overwriteAlternativeVersionId(disposedModelData.alternativeVersionId);
			} else {
				this._undoRedoService.removeElements(resource);
			}
		}
351
		const modelId = MODEL_ID(model.uri);
E
Erich Gamma 已提交
352 353 354

		if (this._models[modelId]) {
			// There already exists a model with this id => this is a programmer error
355
			throw new Error('ModelService: Cannot add model because it already exists!');
E
Erich Gamma 已提交
356 357
		}

358
		const modelData = new ModelData(
359 360 361 362
			model,
			(model) => this._onWillDispose(model),
			(model, e) => this._onDidChangeLanguage(model, e)
		);
A
Alex Dima 已提交
363
		this._models[modelId] = modelData;
E
Erich Gamma 已提交
364

A
Alex Dima 已提交
365
		return modelData;
E
Erich Gamma 已提交
366 367
	}

368
	public updateModel(model: ITextModel, value: string | ITextBufferFactory): void {
369
		const options = this.getCreationOptions(model.getLanguageIdentifier().language, model.uri, model.isForSimpleWidget);
370
		const textBuffer = createTextBuffer(value, options.defaultEOL);
371 372

		// Return early if the text is already set in that form
373
		if (model.equalsTextBuffer(textBuffer)) {
374 375
			return;
		}
376 377

		// Otherwise find a diff between the values and update model
378
		model.pushStackElement();
A
Alex Dima 已提交
379
		model.pushEOL(textBuffer.getEOL() === '\r\n' ? EndOfLineSequence.CRLF : EndOfLineSequence.LF);
380
		model.pushEditOperations(
381
			[],
382
			ModelServiceImpl._computeEdits(model, textBuffer),
383
			(inverseEditOperations: IValidEditOperation[]) => []
384
		);
385
		model.pushStackElement();
386 387
	}

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
	private static _commonPrefix(a: ILineSequence, aLen: number, aDelta: number, b: ILineSequence, bLen: number, bDelta: number): number {
		const maxResult = Math.min(aLen, bLen);

		let result = 0;
		for (let i = 0; i < maxResult && a.getLineContent(aDelta + i) === b.getLineContent(bDelta + i); i++) {
			result++;
		}
		return result;
	}

	private static _commonSuffix(a: ILineSequence, aLen: number, aDelta: number, b: ILineSequence, bLen: number, bDelta: number): number {
		const maxResult = Math.min(aLen, bLen);

		let result = 0;
		for (let i = 0; i < maxResult && a.getLineContent(aDelta + aLen - i) === b.getLineContent(bDelta + bLen - i); i++) {
			result++;
		}
		return result;
	}

408 409 410
	/**
	 * Compute edits to bring `model` to the state of `textSource`.
	 */
411
	public static _computeEdits(model: ITextModel, textBuffer: ITextBuffer): IIdentifiedSingleEditOperation[] {
412
		const modelLineCount = model.getLineCount();
413 414
		const textBufferLineCount = textBuffer.getLineCount();
		const commonPrefix = this._commonPrefix(model, modelLineCount, 1, textBuffer, textBufferLineCount, 1);
415

416 417 418 419
		if (modelLineCount === textBufferLineCount && commonPrefix === modelLineCount) {
			// equality case
			return [];
		}
420

421
		const commonSuffix = this._commonSuffix(model, modelLineCount - commonPrefix, commonPrefix, textBuffer, textBufferLineCount - commonPrefix, commonPrefix);
422

A
Alex Dima 已提交
423 424
		let oldRange: Range;
		let newRange: Range;
425 426 427 428 429 430 431 432 433
		if (commonSuffix > 0) {
			oldRange = new Range(commonPrefix + 1, 1, modelLineCount - commonSuffix + 1, 1);
			newRange = new Range(commonPrefix + 1, 1, textBufferLineCount - commonSuffix + 1, 1);
		} else if (commonPrefix > 0) {
			oldRange = new Range(commonPrefix, model.getLineMaxColumn(commonPrefix), modelLineCount, model.getLineMaxColumn(modelLineCount));
			newRange = new Range(commonPrefix, 1 + textBuffer.getLineLength(commonPrefix), textBufferLineCount, 1 + textBuffer.getLineLength(textBufferLineCount));
		} else {
			oldRange = new Range(1, 1, modelLineCount, model.getLineMaxColumn(modelLineCount));
			newRange = new Range(1, 1, textBufferLineCount, 1 + textBuffer.getLineLength(textBufferLineCount));
434 435
		}

436
		return [EditOperation.replaceMove(oldRange, textBuffer.getValueInRange(newRange, EndOfLinePreference.TextDefined))];
437 438
	}

A
Alex Dima 已提交
439
	public createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource?: URI, isForSimpleWidget: boolean = false): ITextModel {
440 441
		let modelData: ModelData;

A
Alex Dima 已提交
442 443 444
		if (languageSelection) {
			modelData = this._createModelData(value, languageSelection.languageIdentifier, resource, isForSimpleWidget);
			this.setMode(modelData.model, languageSelection);
445
		} else {
A
Alex Dima 已提交
446
			modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget);
447
		}
E
Erich Gamma 已提交
448

A
Alex Dima 已提交
449
		this._onModelAdded.fire(modelData.model);
E
Erich Gamma 已提交
450

A
Alex Dima 已提交
451
		return modelData.model;
E
Erich Gamma 已提交
452 453
	}

A
Alex Dima 已提交
454 455
	public setMode(model: ITextModel, languageSelection: ILanguageSelection): void {
		if (!languageSelection) {
456 457
			return;
		}
A
Alex Dima 已提交
458
		const modelData = this._models[MODEL_ID(model.uri)];
A
Alex Dima 已提交
459 460
		if (!modelData) {
			return;
461
		}
A
Alex Dima 已提交
462
		modelData.setLanguage(languageSelection);
463 464
	}

J
Johannes Rieken 已提交
465
	public destroyModel(resource: URI): void {
A
Alex Dima 已提交
466
		// We need to support that not all models get disposed through this service (i.e. model.dispose() should work!)
A
Alex Dima 已提交
467
		const modelData = this._models[MODEL_ID(resource)];
A
Alex Dima 已提交
468 469
		if (!modelData) {
			return;
E
Erich Gamma 已提交
470
		}
471 472
		const model = modelData.model;
		let maintainUndoRedoStack = false;
473 474
		let heapSize = 0;
		if (MAINTAIN_UNDO_REDO_STACK && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote)) {
475
			const elements = this._undoRedoService.getElements(resource);
476 477 478 479
			if ((elements.past.length > 0 || elements.future.length > 0) && isEditStackPastFutureElements(elements)) {
				maintainUndoRedoStack = true;
				for (const element of elements.past) {
					heapSize += element.heapSize(resource);
480
					element.setModel(resource); // remove reference from text buffer instance
481 482 483
				}
				for (const element of elements.future) {
					heapSize += element.heapSize(resource);
484
					element.setModel(resource); // remove reference from text buffer instance
485 486 487 488
				}
			} else {
				maintainUndoRedoStack = false;
			}
489 490 491 492 493
		}

		if (maintainUndoRedoStack) {
			// We only invalidate the elements, but they remain in the undo-redo service.
			this._undoRedoService.setElementsIsValid(resource, false);
494
			this._disposedModels.set(MODEL_ID(resource), new DisposedModelInfo(resource, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
495 496 497 498
		} else {
			this._undoRedoService.removeElements(resource);
		}

A
Alex Dima 已提交
499
		modelData.model.dispose();
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521

		// After disposing the model, prompt and ask if we should keep the undo-redo stack
		if (maintainUndoRedoStack && heapSize > ModelServiceImpl._PROMPT_UNDO_REDO_SIZE_LIMIT) {
			const mbSize = (heapSize / 1024 / 1024).toFixed(1);
			this._dialogService.show(
				Severity.Info,
				nls.localize('undoRedoConfirm', "Keep the undo-redo stack for {0} in memory ({1} MB)?", (resource.scheme === Schemas.file ? resource.fsPath : resource.path), mbSize),
				[
					nls.localize('nok', "Discard"),
					nls.localize('ok', "Keep"),
				],
				{
					cancelId: 2
				}
			).then((result) => {
				const discard = (result.choice === 2 || result.choice === 0);
				if (discard) {
					this._disposedModels.delete(MODEL_ID(resource));
					this._undoRedoService.removeElements(resource);
				}
			});
		}
E
Erich Gamma 已提交
522 523
	}

A
Alex Dima 已提交
524
	public getModels(): ITextModel[] {
A
Alex Dima 已提交
525
		const ret: ITextModel[] = [];
A
Alex Dima 已提交
526

A
Alex Dima 已提交
527
		const keys = Object.keys(this._models);
A
Alex Dima 已提交
528
		for (let i = 0, len = keys.length; i < len; i++) {
A
Alex Dima 已提交
529
			const modelId = keys[i];
A
Alex Dima 已提交
530
			ret.push(this._models[modelId].model);
E
Erich Gamma 已提交
531
		}
A
Alex Dima 已提交
532

E
Erich Gamma 已提交
533 534 535
		return ret;
	}

A
Alex Dima 已提交
536
	public getModel(resource: URI): ITextModel | null {
A
Alex Dima 已提交
537 538
		const modelId = MODEL_ID(resource);
		const modelData = this._models[modelId];
A
Alex Dima 已提交
539 540
		if (!modelData) {
			return null;
E
Erich Gamma 已提交
541
		}
A
Alex Dima 已提交
542
		return modelData.model;
E
Erich Gamma 已提交
543 544 545 546
	}

	// --- end IModelService

A
Alex Dima 已提交
547
	private _onWillDispose(model: ITextModel): void {
A
Alex Dima 已提交
548 549
		const modelId = MODEL_ID(model.uri);
		const modelData = this._models[modelId];
A
Alex Dima 已提交
550 551 552 553

		delete this._models[modelId];
		modelData.dispose();

554 555 556
		// clean up cache
		delete this._modelCreationOptionsByLanguageAndResource[model.getLanguageIdentifier().language + model.uri];

A
Alex Dima 已提交
557 558 559
		this._onModelRemoved.fire(model);
	}

A
Alex Dima 已提交
560
	private _onDidChangeLanguage(model: ITextModel, e: IModelLanguageChangedEvent): void {
561 562
		const oldModeId = e.oldLanguage;
		const newModeId = model.getLanguageIdentifier().language;
563 564
		const oldOptions = this.getCreationOptions(oldModeId, model.uri, model.isForSimpleWidget);
		const newOptions = this.getCreationOptions(newModeId, model.uri, model.isForSimpleWidget);
565 566
		ModelServiceImpl._setModelOptionsForModel(model, newOptions, oldOptions);
		this._onModelModeChanged.fire({ model, oldModeId });
E
Erich Gamma 已提交
567 568
	}
}
569 570 571 572

export interface ILineSequence {
	getLineContent(lineNumber: number): string;
}
A
wip  
Alexandru Dima 已提交
573

A
wip  
Alexandru Dima 已提交
574
class SemanticColoringFeature extends Disposable {
575 576 577

	private static readonly SETTING_ID = 'editor.semanticHighlighting';

A
wip  
Alexandru Dima 已提交
578
	private _watchers: Record<string, ModelSemanticColoring>;
579
	private _semanticStyling: SemanticStyling;
A
wip  
Alexandru Dima 已提交
580

581
	constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
A
wip  
Alexandru Dima 已提交
582 583
		super();
		this._watchers = Object.create(null);
584
		this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
585 586

		const isSemanticColoringEnabled = (model: ITextModel) => {
587 588 589
			if (!themeService.getColorTheme().semanticHighlighting) {
				return false;
			}
M
Martin Aeschlimann 已提交
590 591
			const options = configurationService.getValue<IEditorSemanticHighlightingOptions>(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
			return options && options.enabled;
592 593
		};
		const register = (model: ITextModel) => {
594
			this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling);
595 596 597 598 599
		};
		const deregister = (model: ITextModel, modelSemanticColoring: ModelSemanticColoring) => {
			modelSemanticColoring.dispose();
			delete this._watchers[model.uri.toString()];
		};
600 601 602 603 604 605 606 607 608 609 610 611 612 613
		const handleSettingOrThemeChange = () => {
			for (let model of modelService.getModels()) {
				const curr = this._watchers[model.uri.toString()];
				if (isSemanticColoringEnabled(model)) {
					if (!curr) {
						register(model);
					}
				} else {
					if (curr) {
						deregister(model, curr);
					}
				}
			}
		};
614 615 616 617
		this._register(modelService.onModelAdded((model) => {
			if (isSemanticColoringEnabled(model)) {
				register(model);
			}
A
wip  
Alexandru Dima 已提交
618 619
		}));
		this._register(modelService.onModelRemoved((model) => {
620 621 622 623
			const curr = this._watchers[model.uri.toString()];
			if (curr) {
				deregister(model, curr);
			}
A
wip  
Alexandru Dima 已提交
624
		}));
625
		this._register(configurationService.onDidChangeConfiguration(e => {
626
			if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
627
				handleSettingOrThemeChange();
628
			}
629 630
		}));
		this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
A
wip  
Alexandru Dima 已提交
631 632 633
	}
}

634
class SemanticStyling extends Disposable {
635

636
	private _caches: WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>;
637 638

	constructor(
639 640
		private readonly _themeService: IThemeService,
		private readonly _logService: ILogService
641
	) {
642
		super();
643
		this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
644 645 646
		this._register(this._themeService.onDidColorThemeChange(() => {
			this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
		}));
647 648
	}

649
	public get(provider: DocumentSemanticTokensProvider): SemanticColoringProviderStyling {
650
		if (!this._caches.has(provider)) {
651
			this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService, this._logService));
652 653 654 655 656 657 658 659 660
		}
		return this._caches.get(provider)!;
	}
}

const enum Constants {
	NO_STYLING = 0b01111111111111111111111111111111
}

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
class HashTableEntry {
	public readonly tokenTypeIndex: number;
	public readonly tokenModifierSet: number;
	public readonly metadata: number;
	public next: HashTableEntry | null;

	constructor(tokenTypeIndex: number, tokenModifierSet: number, metadata: number) {
		this.tokenTypeIndex = tokenTypeIndex;
		this.tokenModifierSet = tokenModifierSet;
		this.metadata = metadata;
		this.next = null;
	}
}

class HashTable {

	private static _SIZES = [3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143];

	private _elementsCount: number;
	private _currentLengthIndex: number;
	private _currentLength: number;
	private _growCount: number;
	private _elements: (HashTableEntry | null)[];

	constructor() {
		this._elementsCount = 0;
		this._currentLengthIndex = 0;
		this._currentLength = HashTable._SIZES[this._currentLengthIndex];
		this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0);
		this._elements = [];
		HashTable._nullOutEntries(this._elements, this._currentLength);
	}

	private static _nullOutEntries(entries: (HashTableEntry | null)[], length: number): void {
		for (let i = 0; i < length; i++) {
			entries[i] = null;
		}
	}

	private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number): number {
		return ((((tokenTypeIndex << 5) - tokenTypeIndex) + tokenModifierSet) | 0) % this._currentLength;  // tokenTypeIndex * 31 + tokenModifierSet, keep as int32
	}

	public get(tokenTypeIndex: number, tokenModifierSet: number): HashTableEntry | null {
		const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet);

		let p = this._elements[hash];
		while (p) {
			if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet) {
				return p;
			}
			p = p.next;
		}

		return null;
	}

	public add(tokenTypeIndex: number, tokenModifierSet: number, metadata: number): void {
		this._elementsCount++;
		if (this._growCount !== 0 && this._elementsCount >= this._growCount) {
			// expand!
			const oldElements = this._elements;

			this._currentLengthIndex++;
			this._currentLength = HashTable._SIZES[this._currentLengthIndex];
			this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0);
			this._elements = [];
			HashTable._nullOutEntries(this._elements, this._currentLength);

			for (const first of oldElements) {
				let p = first;
				while (p) {
					const oldNext = p.next;
					p.next = null;
					this._add(p);
					p = oldNext;
				}
			}
		}
		this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, metadata));
	}

	private _add(element: HashTableEntry): void {
		const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet);
		element.next = this._elements[hash];
		this._elements[hash] = element;
	}
}

750 751
class SemanticColoringProviderStyling {

752 753
	private readonly _hashTable: HashTable;

754
	constructor(
755
		private readonly _legend: SemanticTokensLegend,
756 757
		private readonly _themeService: IThemeService,
		private readonly _logService: ILogService
758
	) {
759
		this._hashTable = new HashTable();
760 761 762
	}

	public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number {
763
		const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet);
764
		let metadata: number;
765
		if (entry) {
M
Martin Aeschlimann 已提交
766 767 768 769
			metadata = entry.metadata;
		} else {
			const tokenType = this._legend.tokenTypes[tokenTypeIndex];
			const tokenModifiers: string[] = [];
770 771 772
			let modifierSet = tokenModifierSet;
			for (let modifierIndex = 0; modifierSet > 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) {
				if (modifierSet & 1) {
M
Martin Aeschlimann 已提交
773 774
					tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]);
				}
775
				modifierSet = modifierSet >> 1;
776 777
			}

M
Martin Aeschlimann 已提交
778
			const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers);
779
			if (typeof tokenStyle === 'undefined') {
M
Martin Aeschlimann 已提交
780
				metadata = Constants.NO_STYLING;
781
			} else {
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
				metadata = 0;
				if (typeof tokenStyle.italic !== 'undefined') {
					const italicBit = (tokenStyle.italic ? FontStyle.Italic : 0) << MetadataConsts.FONT_STYLE_OFFSET;
					metadata |= italicBit | MetadataConsts.SEMANTIC_USE_ITALIC;
				}
				if (typeof tokenStyle.bold !== 'undefined') {
					const boldBit = (tokenStyle.bold ? FontStyle.Bold : 0) << MetadataConsts.FONT_STYLE_OFFSET;
					metadata |= boldBit | MetadataConsts.SEMANTIC_USE_BOLD;
				}
				if (typeof tokenStyle.underline !== 'undefined') {
					const underlineBit = (tokenStyle.underline ? FontStyle.Underline : 0) << MetadataConsts.FONT_STYLE_OFFSET;
					metadata |= underlineBit | MetadataConsts.SEMANTIC_USE_UNDERLINE;
				}
				if (tokenStyle.foreground) {
					const foregroundBits = (tokenStyle.foreground) << MetadataConsts.FOREGROUND_OFFSET;
					metadata |= foregroundBits | MetadataConsts.SEMANTIC_USE_FOREGROUND;
				}
				if (metadata === 0) {
					// Nothing!
					metadata = Constants.NO_STYLING;
				}
M
Martin Aeschlimann 已提交
803 804
			}
			this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata);
805
		}
806
		if (this._logService.getLevel() === LogLevel.Trace) {
M
Martin Aeschlimann 已提交
807 808 809
			const type = this._legend.tokenTypes[tokenTypeIndex];
			const modifiers = tokenModifierSet ? ' ' + this._legend.tokenModifiers.filter((_, i) => tokenModifierSet & (1 << i)).join(' ') : '';
			this._logService.trace(`tokenStyleMetadata ${entry ? '[CACHED] ' : ''}${type}${modifiers}: foreground ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`);
810
		}
811 812
		return metadata;
	}
M
Martin Aeschlimann 已提交
813 814


815 816
}

817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
const enum SemanticColoringConstants {
	/**
	 * Let's aim at having 8KB buffers if possible...
	 * So that would be 8192 / (5 * 4) = 409.6 tokens per area
	 */
	DesiredTokensPerArea = 400,

	/**
	 * Try to keep the total number of areas under 1024 if possible,
	 * simply compensate by having more tokens per area...
	 */
	DesiredMaxAreas = 1024,
}

class SemanticTokensResponse {
	constructor(
833
		private readonly _provider: DocumentSemanticTokensProvider,
834 835 836 837 838
		public readonly resultId: string | undefined,
		public readonly data: Uint32Array
	) { }

	public dispose(): void {
839
		this._provider.releaseDocumentSemanticTokens(this.resultId);
840 841 842
	}
}

A
wip  
Alexandru Dima 已提交
843 844
class ModelSemanticColoring extends Disposable {

845
	private _isDisposed: boolean;
A
wip  
Alexandru Dima 已提交
846
	private readonly _model: ITextModel;
847
	private readonly _semanticStyling: SemanticStyling;
A
Alex Dima 已提交
848 849 850 851
	private readonly _fetchDocumentSemanticTokens: RunOnceScheduler;
	private _currentDocumentResponse: SemanticTokensResponse | null;
	private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null;
	private _documentProvidersChangeListeners: IDisposable[];
A
wip  
Alexandru Dima 已提交
852

853
	constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) {
A
wip  
Alexandru Dima 已提交
854
		super();
A
wip  
Alexandru Dima 已提交
855

856
		this._isDisposed = false;
A
wip  
Alexandru Dima 已提交
857
		this._model = model;
858
		this._semanticStyling = stylingProvider;
A
Alex Dima 已提交
859 860 861 862
		this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), 300));
		this._currentDocumentResponse = null;
		this._currentDocumentRequestCancellationTokenSource = null;
		this._documentProvidersChangeListeners = [];
A
wip  
Alexandru Dima 已提交
863

864
		this._register(this._model.onDidChangeContent(e => {
A
Alex Dima 已提交
865 866
			if (!this._fetchDocumentSemanticTokens.isScheduled()) {
				this._fetchDocumentSemanticTokens.schedule();
867 868
			}
		}));
A
Alex Dima 已提交
869 870 871
		const bindDocumentChangeListeners = () => {
			dispose(this._documentProvidersChangeListeners);
			this._documentProvidersChangeListeners = [];
872 873
			for (const provider of DocumentSemanticTokensProviderRegistry.all(model)) {
				if (typeof provider.onDidChange === 'function') {
A
Alex Dima 已提交
874
					this._documentProvidersChangeListeners.push(provider.onDidChange(() => this._fetchDocumentSemanticTokens.schedule(0)));
875 876 877
				}
			}
		};
A
Alex Dima 已提交
878
		bindDocumentChangeListeners();
879
		this._register(DocumentSemanticTokensProviderRegistry.onDidChange(e => {
A
Alex Dima 已提交
880 881
			bindDocumentChangeListeners();
			this._fetchDocumentSemanticTokens.schedule();
882 883
		}));

884 885
		this._register(themeService.onDidColorThemeChange(_ => {
			// clear out existing tokens
A
Alex Dima 已提交
886 887
			this._setDocumentSemanticTokens(null, null, null, []);
			this._fetchDocumentSemanticTokens.schedule();
888 889
		}));

A
Alex Dima 已提交
890
		this._fetchDocumentSemanticTokens.schedule(0);
A
wip  
Alexandru Dima 已提交
891 892 893
	}

	public dispose(): void {
A
Alex Dima 已提交
894 895 896
		if (this._currentDocumentResponse) {
			this._currentDocumentResponse.dispose();
			this._currentDocumentResponse = null;
A
wip  
Alexandru Dima 已提交
897
		}
A
Alex Dima 已提交
898 899 900
		if (this._currentDocumentRequestCancellationTokenSource) {
			this._currentDocumentRequestCancellationTokenSource.cancel();
			this._currentDocumentRequestCancellationTokenSource = null;
A
wip  
Alexandru Dima 已提交
901
		}
A
Alex Dima 已提交
902
		this._setDocumentSemanticTokens(null, null, null, []);
903 904
		this._isDisposed = true;

A
wip  
Alexandru Dima 已提交
905
		super.dispose();
A
wip  
Alexandru Dima 已提交
906 907
	}

A
Alex Dima 已提交
908 909
	private _fetchDocumentSemanticTokensNow(): void {
		if (this._currentDocumentRequestCancellationTokenSource) {
A
wip  
Alexandru Dima 已提交
910 911 912 913 914 915 916
			// there is already a request running, let it finish...
			return;
		}
		const provider = this._getSemanticColoringProvider();
		if (!provider) {
			return;
		}
A
Alex Dima 已提交
917
		this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource();
918 919 920 921 922 923

		const pendingChanges: IModelContentChangedEvent[] = [];
		const contentChangeListener = this._model.onDidChangeContent((e) => {
			pendingChanges.push(e);
		});

924
		const styling = this._semanticStyling.get(provider);
925

A
Alex Dima 已提交
926 927
		const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
		const request = Promise.resolve(provider.provideDocumentSemanticTokens(this._model, lastResultId, this._currentDocumentRequestCancellationTokenSource.token));
A
wip  
Alexandru Dima 已提交
928 929

		request.then((res) => {
A
Alex Dima 已提交
930
			this._currentDocumentRequestCancellationTokenSource = null;
931
			contentChangeListener.dispose();
A
Alex Dima 已提交
932
			this._setDocumentSemanticTokens(provider, res || null, styling, pendingChanges);
A
wip  
Alexandru Dima 已提交
933
		}, (err) => {
934 935 936 937 938 939
			if (!err || typeof err.message !== 'string' || err.message.indexOf('busy') === -1) {
				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...
A
Alex Dima 已提交
940
			this._currentDocumentRequestCancellationTokenSource = null;
941
			contentChangeListener.dispose();
942 943 944

			if (pendingChanges.length > 0) {
				// More changes occurred while the request was running
A
Alex Dima 已提交
945 946
				if (!this._fetchDocumentSemanticTokens.isScheduled()) {
					this._fetchDocumentSemanticTokens.schedule();
947 948
				}
			}
A
wip  
Alexandru Dima 已提交
949 950 951
		});
	}

952 953 954 955 956 957 958 959 960 961 962 963 964 965
	private static _isSemanticTokens(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokens {
		return v && !!((<SemanticTokens>v).data);
	}

	private static _isSemanticTokensEdits(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokensEdits {
		return v && Array.isArray((<SemanticTokensEdits>v).edits);
	}

	private static _copy(src: Uint32Array, srcOffset: number, dest: Uint32Array, destOffset: number, length: number): void {
		for (let i = 0; i < length; i++) {
			dest[destOffset + i] = src[srcOffset + i];
		}
	}

A
Alex Dima 已提交
966 967 968 969 970
	private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticColoringProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
		const currentResponse = this._currentDocumentResponse;
		if (this._currentDocumentResponse) {
			this._currentDocumentResponse.dispose();
			this._currentDocumentResponse = null;
A
wip  
Alexandru Dima 已提交
971
		}
972 973
		if (this._isDisposed) {
			// disposed!
974
			if (provider && tokens) {
975
				provider.releaseDocumentSemanticTokens(tokens.resultId);
976 977 978
			}
			return;
		}
979
		if (!provider || !tokens || !styling) {
980 981 982
			this._model.setSemanticTokens(null);
			return;
		}
983

984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
		if (ModelSemanticColoring._isSemanticTokensEdits(tokens)) {
			if (!currentResponse) {
				// not possible!
				this._model.setSemanticTokens(null);
				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;
1022
				}
1023 1024 1025 1026 1027 1028 1029 1030 1031

				if (srcLastStart > 0) {
					ModelSemanticColoring._copy(srcData, 0, destData, 0, srcLastStart);
				}

				tokens = {
					resultId: tokens.resultId,
					data: destData
				};
A
wip  
Alexandru Dima 已提交
1032
			}
1033 1034 1035 1036
		}

		if (ModelSemanticColoring._isSemanticTokens(tokens)) {

A
Alex Dima 已提交
1037
			this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104

			const srcData = tokens.data;
			const tokenCount = (tokens.data.length / 5) | 0;
			const tokensPerArea = Math.max(Math.ceil(tokenCount / SemanticColoringConstants.DesiredMaxAreas), SemanticColoringConstants.DesiredTokensPerArea);

			const result: MultilineTokens2[] = [];

			let tokenIndex = 0;
			let lastLineNumber = 1;
			let lastStartCharacter = 0;
			while (tokenIndex < tokenCount) {
				const tokenStartIndex = tokenIndex;
				let tokenEndIndex = Math.min(tokenStartIndex + tokensPerArea, tokenCount);

				// Keep tokens on the same line in the same area...
				if (tokenEndIndex < tokenCount) {

					let smallTokenEndIndex = tokenEndIndex;
					while (smallTokenEndIndex - 1 > tokenStartIndex && srcData[5 * smallTokenEndIndex] === 0) {
						smallTokenEndIndex--;
					}

					if (smallTokenEndIndex - 1 === tokenStartIndex) {
						// there are so many tokens on this line that our area would be empty, we must now go right
						let bigTokenEndIndex = tokenEndIndex;
						while (bigTokenEndIndex + 1 < tokenCount && srcData[5 * bigTokenEndIndex] === 0) {
							bigTokenEndIndex++;
						}
						tokenEndIndex = bigTokenEndIndex;
					} else {
						tokenEndIndex = smallTokenEndIndex;
					}
				}

				let destData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 4);
				let destOffset = 0;
				let areaLine = 0;
				while (tokenIndex < tokenEndIndex) {
					const srcOffset = 5 * tokenIndex;
					const deltaLine = srcData[srcOffset];
					const deltaCharacter = srcData[srcOffset + 1];
					const lineNumber = lastLineNumber + deltaLine;
					const startCharacter = (deltaLine === 0 ? lastStartCharacter + deltaCharacter : deltaCharacter);
					const length = srcData[srcOffset + 2];
					const tokenTypeIndex = srcData[srcOffset + 3];
					const tokenModifierSet = srcData[srcOffset + 4];
					const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet);

					if (metadata !== Constants.NO_STYLING) {
						if (areaLine === 0) {
							areaLine = lineNumber;
						}
						destData[destOffset] = lineNumber - areaLine;
						destData[destOffset + 1] = startCharacter;
						destData[destOffset + 2] = startCharacter + length;
						destData[destOffset + 3] = metadata;
						destOffset += 4;
					}

					lastLineNumber = lineNumber;
					lastStartCharacter = startCharacter;
					tokenIndex++;
				}

				if (destOffset !== destData.length) {
					destData = destData.subarray(0, destOffset);
				}
A
Alexandru Dima 已提交
1105

1106 1107
				const tokens = new MultilineTokens2(areaLine, new SparseEncodedTokens(destData));
				result.push(tokens);
1108
			}
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120

			// 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);
						}
1121 1122
					}
				}
1123

A
Alex Dima 已提交
1124 1125
				if (!this._fetchDocumentSemanticTokens.isScheduled()) {
					this._fetchDocumentSemanticTokens.schedule();
1126
				}
1127
			}
1128

1129 1130
			this._model.setSemanticTokens(result);
			return;
1131
		}
1132

1133
		this._model.setSemanticTokens(null);
A
wip  
Alexandru Dima 已提交
1134 1135
	}

1136 1137
	private _getSemanticColoringProvider(): DocumentSemanticTokensProvider | null {
		const result = DocumentSemanticTokensProviderRegistry.ordered(this._model);
A
wip  
Alexandru Dima 已提交
1138 1139
		return (result.length > 0 ? result[0] : null);
	}
A
wip  
Alexandru Dima 已提交
1140
}