src/vs/editor/common/services/modelService.ts

591 LOC · 461 covered · 130 uncovered · 110 ranges · 1602 concepts · 22 introducers · 811 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- modelService.ts ×30
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Emitter, Event } from '../../../base/common/event.js';
7 > import { StringSHA1 } from '../../../base/common/hash.js';
8 > import { Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
9 > import { Schemas } from '../../../base/common/network.js';
10 > import { equals } from '../../../base/common/objects.js';
11 > import * as platform from '../../../base/common/platform.js';
12 > import { URI } from '../../../base/common/uri.js';
13 > import { IConfigurationChangeEvent, IConfigurationService } from '../../../platform/configuration/common/configuration.js';
14 > import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
15 > import { IUndoRedoService, ResourceEditStackSnapshot } from '../../../platform/undoRedo/common/undoRedo.js';
16 > import { clampedInt } from '../config/editorOptions.js';
17 > import { EditOperation, ISingleEditOperation } from '../core/editOperation.js';
18 > import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js';
19 > import { Range } from '../core/range.js';
20 > import { ILanguageSelection } from '../languages/language.js';
21 > import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js';
22 > import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from '../model.js';
23 > import { isEditStackElement } from '../model/editStack.js';
24 > import { TextModel, createTextBuffer } from '../model/textModel.js';
25 > import { EditSources, TextModelEditSource } from '../textModelEditSource.js';
26 > import { IModelLanguageChangedEvent } from '../textModelEvents.js';
27 > import { IModelService } from './model.js';
28 > import { ITextResourcePropertiesService } from './textResourceConfiguration.js';
29 >
30 > function MODEL_ID(resource: URI): string { modelService.ts ×1
31 > return resource.toString();
32 > }
34 > class ModelData implements IDisposable {
35 >
36 > private readonly _modelEventListeners = new DisposableStore();
37 >
38 > constructor(
39 > public readonly model: TextModel, modelService.ts ×29
40 > onWillDispose: (model: ITextModel) => void,
41 > onDidChangeLanguage: (model: ITextModel, e: IModelLanguageChangedEvent) => void
42 > ) {
43 > this.model = model;
44 > this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
45 > this._modelEventListeners.add(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
46 > }
48 > public dispose(): void {
49 > this._modelEventListeners.dispose(); modelService.ts ×29
50 > }
52 >
53 > interface IRawEditorConfig {
54 > tabSize?: unknown;
55 > indentSize?: unknown;
56 > insertSpaces?: unknown;
57 > detectIndentation?: unknown;
58 > trimAutoWhitespace?: unknown;
59 > creationOptions?: unknown;
60 > largeFileOptimizations?: unknown;
61 > bracketPairColorization?: unknown;
62 > }
63 >
64 > interface IRawConfig {
65 > eol?: unknown;
66 > editor?: IRawEditorConfig;
67 > }
68 >
69 > const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF;
70 >
71 > class DisposedModelInfo {
72 > constructor(
73 > public readonly uri: URI, modelService.ts ×14
74 > public readonly initialUndoRedoSnapshot: ResourceEditStackSnapshot | null,
75 > public readonly time: number,
76 > public readonly sharesUndoRedoStack: boolean,
77 > public readonly heapSize: number,
78 > public readonly sha1: string,
79 > public readonly versionId: number,
80 > public readonly alternativeVersionId: number,
81 > ) { }
83 >
84 > export class ModelService extends Disposable implements IModelService {
85 >
86 > public static MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK = 20 * 1024 * 1024;
87 >
88 > public _serviceBrand: undefined;
89 >
90 > private readonly _onModelAdded: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
91 > public readonly onModelAdded: Event<ITextModel> = this._onModelAdded.event;
92 >
93 > private readonly _onModelRemoved: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
94 > public readonly onModelRemoved: Event<ITextModel> = this._onModelRemoved.event;
95 >
96 > private readonly _onModelModeChanged = this._register(new Emitter<{ model: ITextModel; oldLanguageId: string }>());
97 > public readonly onModelLanguageChanged = this._onModelModeChanged.event;
98 >
99 > private _modelCreationOptionsByLanguageAndResource: { [languageAndResource: string]: ITextModelCreationOptions };
100 >
101 > /**
102 > * All the models known in the system.
103 > */
104 > private readonly _models: { [modelId: string]: ModelData };
105 > private readonly _disposedModels: Map<string, DisposedModelInfo>;
106 > private _disposedModelsHeapSize: number;
107 >
108 > constructor(
109 > @IConfigurationService private readonly _configurationService: IConfigurationService, modelService.ts ×3
110 > @ITextResourcePropertiesService private readonly _resourcePropertiesService: ITextResourcePropertiesService,
111 > @IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
112 > @IInstantiationService private readonly _instantiationService: IInstantiationService
113 > ) {
114 > super();
115 > this._modelCreationOptionsByLanguageAndResource = Object.create(null);
116 > this._models = {};
117 > this._disposedModels = new Map<string, DisposedModelInfo>();
118 > this._disposedModelsHeapSize = 0;
119 >
120 > this._register(this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions(e)));
121 > this._updateModelOptions(undefined);
122 > }
124 > private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
125 > let tabSize = EDITOR_MODEL_DEFAULTS.tabSize; modelService.ts ×29
126 > if (config.editor && typeof config.editor.tabSize !== 'undefined') {
127 tabSize = clampedInt(config.editor.tabSize, EDITOR_MODEL_DEFAULTS.tabSize, 1, 100);
128 }
130 > let indentSize: number | 'tabSize' = 'tabSize';
131 > if (config.editor && typeof config.editor.indentSize !== 'undefined' && config.editor.indentSize !== 'tabSize') {
132 indentSize = clampedInt(config.editor.indentSize, 'tabSize', 1, 100);
133 }
135 > let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces;
136 > if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
137 insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
138 }
140 > let newDefaultEOL = DEFAULT_EOL;
141 > const eol = config.eol;
142 > if (eol === '\r\n') {
143 > newDefaultEOL = DefaultEndOfLine.CRLF; modelService.ts ×4
144 > } else if (eol === '\n') { modelService.ts ×29
145 > newDefaultEOL = DefaultEndOfLine.LF;
146 > }
147 >
148 > let trimAutoWhitespace = EDITOR_MODEL_DEFAULTS.trimAutoWhitespace;
149 > if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') {
150 trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace));
151 }
153 > let detectIndentation = EDITOR_MODEL_DEFAULTS.detectIndentation;
154 > if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
155 detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
156 }
158 > let largeFileOptimizations = EDITOR_MODEL_DEFAULTS.largeFileOptimizations;
159 > if (config.editor && typeof config.editor.largeFileOptimizations !== 'undefined') {
160 largeFileOptimizations = (config.editor.largeFileOptimizations === 'false' ? false : Boolean(config.editor.largeFileOptimizations));
161 }
162 > let bracketPairColorizationOptions = EDITOR_MODEL_DEFAULTS.bracketPairColorizationOptions; modelService.ts ×29
163 > if (config.editor?.bracketPairColorization && typeof config.editor.bracketPairColorization === 'object') {
164 const bpConfig = config.editor.bracketPairColorization as { enabled?: unknown; independentColorPoolPerBracketType?: unknown };
165 bracketPairColorizationOptions = {
166 enabled: !!bpConfig.enabled,
167 independentColorPoolPerBracketType: !!bpConfig.independentColorPoolPerBracketType
168 };
169 }
171 > return {
172 > isForSimpleWidget: isForSimpleWidget,
173 > tabSize: tabSize,
174 > indentSize: indentSize,
175 > insertSpaces: insertSpaces,
176 > detectIndentation: detectIndentation,
177 > defaultEOL: newDefaultEOL,
178 > trimAutoWhitespace: trimAutoWhitespace,
179 > largeFileOptimizations: largeFileOptimizations,
180 > bracketPairColorizationOptions
181 > };
182 > }
184 > private _getEOL(resource: URI | undefined, language: string): string {
185 > if (resource) { modelService.ts ×29
186 > return this._resourcePropertiesService.getEOL(resource, language);
187 > }
188 > const eol = this._configurationService.getValue('files.eol', { overrideIdentifier: language }); modelService.ts ×4
189 > if (eol && typeof eol === 'string' && eol !== 'auto') { modelService.ts ×29
190 > return eol; modelService.ts ×4
191 > }
192 > return platform.OS === platform.OperatingSystem.Linux || platform.OS === platform.OperatingSystem.Macintosh ? '\n' : '\r\n'; modelService.ts ×29
193 > }
195 > private _shouldRestoreUndoStack(): boolean {
196 > const result = this._configurationService.getValue('files.restoreUndoStack'); modelService.ts ×29
197 > if (typeof result === 'boolean') {
198 return result;
199 }
200 > return true; modelService.ts ×29
201 > }
203 > public getCreationOptions(languageIdOrSelection: string | ILanguageSelection, resource: URI | undefined, isForSimpleWidget: boolean): ITextModelCreationOptions {
204 > const language = (typeof languageIdOrSelection === 'string' ? languageIdOrSelection : languageIdOrSelection.languageId); modelService.ts ×29
205 > let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
206 > if (!creationOptions) {
207 > const editor = this._configurationService.getValue<IRawEditorConfig>('editor', { overrideIdentifier: language, resource });
208 > const eol = this._getEOL(resource, language);
209 > creationOptions = ModelService._readModelOptions({ editor, eol }, isForSimpleWidget);
210 > this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions;
211 > }
212 > return creationOptions;
213 > }
215 > private _updateModelOptions(e: IConfigurationChangeEvent | undefined): void {
216 > const oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource; modelService.ts ×3
217 > this._modelCreationOptionsByLanguageAndResource = Object.create(null);
218 >
219 > // Update options on all models
220 > const keys = Object.keys(this._models);
221 > for (let i = 0, len = keys.length; i < len; i++) {
222 const modelId = keys[i];
223 const modelData = this._models[modelId];
224 const language = modelData.model.getLanguageId();
225 const uri = modelData.model.uri;
226
227 if (e && !e.affectsConfiguration('editor', { overrideIdentifier: language, resource: uri }) && !e.affectsConfiguration('files.eol', { overrideIdentifier: language, resource: uri })) {
228 continue; // perf: skip if this model is not affected by configuration change
229 }
230
231 const oldOptions = oldOptionsByLanguageAndResource[language + uri];
232 const newOptions = this.getCreationOptions(language, uri, modelData.model.isForSimpleWidget);
233 ModelService._setModelOptionsForModel(modelData.model, newOptions, oldOptions);
234 }
237 > private static _setModelOptionsForModel(model: ITextModel, newOptions: ITextModelCreationOptions, currentOptions: ITextModelCreationOptions): void {
238 if (currentOptions && currentOptions.defaultEOL !== newOptions.defaultEOL && model.getLineCount() === 1) {
239 model.setEOL(newOptions.defaultEOL === DefaultEndOfLine.LF ? EndOfLineSequence.LF : EndOfLineSequence.CRLF);
240 }
241
242 if (currentOptions
243 && (currentOptions.detectIndentation === newOptions.detectIndentation)
244 && (currentOptions.insertSpaces === newOptions.insertSpaces)
245 && (currentOptions.tabSize === newOptions.tabSize)
246 && (currentOptions.indentSize === newOptions.indentSize)
247 && (currentOptions.trimAutoWhitespace === newOptions.trimAutoWhitespace)
248 && equals(currentOptions.bracketPairColorizationOptions, newOptions.bracketPairColorizationOptions)
249 ) {
250 // Same indent opts, no need to touch the model
251 return;
252 }
253
254 if (newOptions.detectIndentation) {
255 model.detectIndentation(newOptions.insertSpaces, newOptions.tabSize);
256 model.updateOptions({
257 trimAutoWhitespace: newOptions.trimAutoWhitespace,
258 bracketColorizationOptions: newOptions.bracketPairColorizationOptions
259 });
260 } else {
261 model.updateOptions({
262 insertSpaces: newOptions.insertSpaces,
263 tabSize: newOptions.tabSize,
264 indentSize: newOptions.indentSize,
265 trimAutoWhitespace: newOptions.trimAutoWhitespace,
266 bracketColorizationOptions: newOptions.bracketPairColorizationOptions
267 });
268 }
269 }
271 > // --- begin IModelService
272 >
273 > private _insertDisposedModel(disposedModelData: DisposedModelInfo): void {
274 > this._disposedModels.set(MODEL_ID(disposedModelData.uri), disposedModelData); modelService.ts ×14
275 > this._disposedModelsHeapSize += disposedModelData.heapSize;
276 > }
278 > private _removeDisposedModel(resource: URI): DisposedModelInfo | undefined {
279 > const disposedModelData = this._disposedModels.get(MODEL_ID(resource)); modelService.ts ×14
280 > if (disposedModelData) {
281 > this._disposedModelsHeapSize -= disposedModelData.heapSize;
282 > }
283 > this._disposedModels.delete(MODEL_ID(resource));
284 > return disposedModelData;
285 > }
287 > private _ensureDisposedModelsHeapSize(maxModelsHeapSize: number): void {
288 > if (this._disposedModelsHeapSize > maxModelsHeapSize) { modelService.ts ×14
289 // we must remove some old undo stack elements to free up some memory
290 const disposedModels: DisposedModelInfo[] = [];
291 this._disposedModels.forEach(entry => {
292 if (!entry.sharesUndoRedoStack) {
293 disposedModels.push(entry);
294 }
295 });
296 disposedModels.sort((a, b) => a.time - b.time);
297 while (disposedModels.length > 0 && this._disposedModelsHeapSize > maxModelsHeapSize) {
298 const disposedModel = disposedModels.shift()!;
299 this._removeDisposedModel(disposedModel.uri);
300 if (disposedModel.initialUndoRedoSnapshot !== null) {
301 this._undoRedoService.restoreSnapshot(disposedModel.initialUndoRedoSnapshot);
302 }
303 }
304 }
307 > private _createModelData(value: string | ITextBufferFactory, languageIdOrSelection: string | ILanguageSelection, resource: URI | undefined, isForSimpleWidget: boolean): ModelData {
308 > // create & save the model modelService.ts ×29
309 > const options = this.getCreationOptions(languageIdOrSelection, resource, isForSimpleWidget);
310 > const model: TextModel = this._instantiationService.createInstance(TextModel,
311 > value,
312 > languageIdOrSelection,
313 > options,
314 > resource
315 > );
316 > if (resource && this._disposedModels.has(MODEL_ID(resource))) {
317 > const disposedModelData = this._removeDisposedModel(resource)!; modelService.ts ×14
318 > const elements = this._undoRedoService.getElements(resource);
319 > const sha1Computer = this._getSHA1Computer();
320 > const sha1IsEqual = (
321 > sha1Computer.canComputeSHA1(model)
322 > ? sha1Computer.computeSHA1(model) === disposedModelData.sha1
323 : false
325 > if (sha1IsEqual || disposedModelData.sharesUndoRedoStack) {
326 > for (const element of elements.past) { textModel.ts ×2
327 > if (isEditStackElement(element) && element.matchesResource(resource)) {
328 > element.setModel(model);
329 > }
330 > }
331 > for (const element of elements.future) {
332 if (isEditStackElement(element) && element.matchesResource(resource)) {
333 element.setModel(model);
334 }
335 }
336 > this._undoRedoService.setElementsValidFlag(resource, true, (element) => (isEditStackElement(element) && element.matchesResource(resource))); textModel.ts ×2
337 > if (sha1IsEqual) {
338 > model._overwriteVersionId(disposedModelData.versionId);
339 > model._overwriteAlternativeVersionId(disposedModelData.alternativeVersionId);
340 > model._overwriteInitialUndoRedoSnapshot(disposedModelData.initialUndoRedoSnapshot);
341 > }
342 > } else { modelService.ts ×14
343 > if (disposedModelData.initialUndoRedoSnapshot !== null) { modelService.ts ×1
344 > this._undoRedoService.restoreSnapshot(disposedModelData.initialUndoRedoSnapshot);
345 > }
346 > }
348 > const modelId = MODEL_ID(model.uri); modelService.ts ×29
349 >
350 > if (this._models[modelId]) {
351 // There already exists a model with this id => this is a programmer error
352 throw new Error('ModelService: Cannot add model because it already exists!');
353 }
355 > const modelData = new ModelData(
356 > model,
357 > (model) => this._onWillDispose(model),
358 > (model, e) => this._onDidChangeLanguage(model, e)
359 > );
360 > this._models[modelId] = modelData;
361 >
362 > return modelData;
363 > }
365 > public updateModel(model: ITextModel, value: string | ITextBufferFactory, reason: TextModelEditSource = EditSources.unknown({ name: 'updateModel' })): void {
366 const options = this.getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget);
367 const { textBuffer, disposable } = createTextBuffer(value, options.defaultEOL);
368
369 // Return early if the text is already set in that form
370 if (model.equalsTextBuffer(textBuffer)) {
371 disposable.dispose();
372 return;
373 }
374
375 // Otherwise find a diff between the values and update model
376 model.pushStackElement();
377 model.pushEOL(textBuffer.getEOL() === '\r\n' ? EndOfLineSequence.CRLF : EndOfLineSequence.LF);
378 model.pushEditOperations(
379 [],
380 ModelService._computeEdits(model, textBuffer),
381 () => [],
382 undefined,
383 reason
384 );
385 model.pushStackElement();
386 disposable.dispose();
387 }
389 > private static _commonPrefix(a: ITextModel, aLen: number, aDelta: number, b: ITextBuffer, bLen: number, bDelta: number): number {
390 > const maxResult = Math.min(aLen, bLen); modelService.ts ×4
391 >
392 > let result = 0;
393 > for (let i = 0; i < maxResult && a.getLineContent(aDelta + i) === b.getLineContent(bDelta + i); i++) {
394 > result++; modelService.ts ×1
395 > }
396 > return result; modelService.ts ×4
397 > }
399 > private static _commonSuffix(a: ITextModel, aLen: number, aDelta: number, b: ITextBuffer, bLen: number, bDelta: number): number {
400 > const maxResult = Math.min(aLen, bLen); modelService.ts ×5
401 >
402 > let result = 0;
403 > for (let i = 0; i < maxResult && a.getLineContent(aDelta + aLen - i) === b.getLineContent(bDelta + bLen - i); i++) {
404 > result++; modelService.ts ×2
405 > }
406 > return result; modelService.ts ×5
407 > }
409 > /**
410 > * Compute edits to bring `model` to the state of `textSource`.
411 > */
412 > public static _computeEdits(model: ITextModel, textBuffer: ITextBuffer): ISingleEditOperation[] {
413 > const modelLineCount = model.getLineCount(); modelService.ts ×4
414 > const textBufferLineCount = textBuffer.getLineCount();
415 > const commonPrefix = this._commonPrefix(model, modelLineCount, 1, textBuffer, textBufferLineCount, 1);
416 >
417 > if (modelLineCount === textBufferLineCount && commonPrefix === modelLineCount) {
418 > // equality case modelService.ts ×1
419 > return [];
420 > }
422 > const commonSuffix = this._commonSuffix(model, modelLineCount - commonPrefix, commonPrefix, textBuffer, textBufferLineCount - commonPrefix, commonPrefix);
423 >
424 > let oldRange: Range;
425 > let newRange: Range;
426 > if (commonSuffix > 0) {
427 > oldRange = new Range(commonPrefix + 1, 1, modelLineCount - commonSuffix + 1, 1); modelService.ts ×2
428 > newRange = new Range(commonPrefix + 1, 1, textBufferLineCount - commonSuffix + 1, 1);
429 > } else if (commonPrefix > 0) { modelService.ts ×5
430 > oldRange = new Range(commonPrefix, model.getLineMaxColumn(commonPrefix), modelLineCount, model.getLineMaxColumn(modelLineCount)); modelService.ts ×1
431 > newRange = new Range(commonPrefix, 1 + textBuffer.getLineLength(commonPrefix), textBufferLineCount, 1 + textBuffer.getLineLength(textBufferLineCount));
432 > } else { modelService.ts ×1
433 > oldRange = new Range(1, 1, modelLineCount, model.getLineMaxColumn(modelLineCount)); modelService.ts ×1
434 > newRange = new Range(1, 1, textBufferLineCount, 1 + textBuffer.getLineLength(textBufferLineCount));
435 > }
437 > return [EditOperation.replaceMove(oldRange, textBuffer.getValueInRange(newRange, EndOfLinePreference.TextDefined))];
440 > public createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource?: URI, isForSimpleWidget: boolean = false): ITextModel {
441 > let modelData: ModelData; modelService.ts ×29
442 >
443 > if (languageSelection) {
444 modelData = this._createModelData(value, languageSelection, resource, isForSimpleWidget);
445 > } else { modelService.ts ×29
446 > modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_ID, resource, isForSimpleWidget);
447 > }
448 >
449 > this._onModelAdded.fire(modelData.model);
450 >
451 > return modelData.model;
452 > }
454 > public destroyModel(resource: URI): void {
455 > // We need to support that not all models get disposed through this service (i.e. model.dispose() should work!) modelService.ts ×2
456 > const modelData = this._models[MODEL_ID(resource)];
457 > if (!modelData) {
458 return;
459 }
460 > modelData.model.dispose(); modelService.ts ×2
461 > }
463 > public getModels(): ITextModel[] {
464 > const ret: ITextModel[] = []; promptsServiceImpl.ts ×11
465 >
466 > const keys = Object.keys(this._models);
467 > for (let i = 0, len = keys.length; i < len; i++) {
468 const modelId = keys[i];
469 ret.push(this._models[modelId].model);
470 }
472 > return ret;
473 > }
475 > public getModel(resource: URI): ITextModel | null {
476 > const modelId = MODEL_ID(resource); promptsServiceImpl.ts ×3
477 > const modelData = this._models[modelId];
478 > if (!modelData) {
479 > return null;
480 > }
481 return modelData.model;
484 > // --- end IModelService
485 >
486 > protected _schemaShouldMaintainUndoRedoElements(resource: URI) {
487 > return ( modelService.ts ×29
488 > resource.scheme === Schemas.file
489 > || resource.scheme === Schemas.vscodeRemote modelService.ts ×4
490 > || resource.scheme === Schemas.vscodeUserData
491 > || resource.scheme === Schemas.vscodeNotebookCell
492 > || resource.scheme === 'fake-fs' // for tests
494 > }
496 > private _onWillDispose(model: ITextModel): void {
497 > const modelId = MODEL_ID(model.uri); modelService.ts ×29
498 > const modelData = this._models[modelId];
499 >
500 > const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString());
501 > let maintainUndoRedoStack = false;
502 > let heapSize = 0;
503 > if (sharesUndoRedoStack || (this._shouldRestoreUndoStack() && this._schemaShouldMaintainUndoRedoElements(model.uri))) {
504 > const elements = this._undoRedoService.getElements(model.uri);
505 > if (elements.past.length > 0 || elements.future.length > 0) {
506 > for (const element of elements.past) { modelService.ts ×14
507 > if (isEditStackElement(element) && element.matchesResource(model.uri)) {
508 > maintainUndoRedoStack = true;
509 > heapSize += element.heapSize(model.uri);
510 > element.setModel(model.uri); // remove reference from text buffer instance
511 > }
512 > }
513 > for (const element of elements.future) {
514 > if (isEditStackElement(element) && element.matchesResource(model.uri)) { undoRedoService.ts ×2
515 > maintainUndoRedoStack = true;
516 > heapSize += element.heapSize(model.uri);
517 > element.setModel(model.uri); // remove reference from text buffer instance
518 > }
519 > }
522 >
523 > const maxMemory = ModelService.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK;
524 > const sha1Computer = this._getSHA1Computer();
525 > if (!maintainUndoRedoStack) {
526 > if (!sharesUndoRedoStack) { modelService.ts ×2
527 > const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
528 > if (initialUndoRedoSnapshot !== null) {
529 > this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot); undoRedoService.ts ×3
530 > }
532 > } else if (!sharesUndoRedoStack && (heapSize > maxMemory || !sha1Computer.canComputeSHA1(model))) { modelService.ts ×29
533 // the undo stack for this file would never fit in the configured memory or the file is very large, so don't bother with it.
534 const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
535 if (initialUndoRedoSnapshot !== null) {
536 this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
537 }
538 > } else { modelService.ts ×14
539 > this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
540 > // We only invalidate the elements, but they remain in the undo-redo service.
541 > this._undoRedoService.setElementsValidFlag(model.uri, false, (element) => (isEditStackElement(element) && element.matchesResource(model.uri)));
542 > this._insertDisposedModel(new DisposedModelInfo(model.uri, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, sha1Computer.computeSHA1(model), model.getVersionId(), model.getAlternativeVersionId()));
543 > }
545 > delete this._models[modelId];
546 > modelData.dispose();
547 >
548 > // clean up cache
549 > delete this._modelCreationOptionsByLanguageAndResource[model.getLanguageId() + model.uri];
550 >
551 > this._onModelRemoved.fire(model);
552 > }
554 > private _onDidChangeLanguage(model: ITextModel, e: IModelLanguageChangedEvent): void {
555 const oldLanguageId = e.oldLanguage;
556 const newLanguageId = model.getLanguageId();
557 const oldOptions = this.getCreationOptions(oldLanguageId, model.uri, model.isForSimpleWidget);
558 const newOptions = this.getCreationOptions(newLanguageId, model.uri, model.isForSimpleWidget);
559 ModelService._setModelOptionsForModel(model, newOptions, oldOptions);
560 this._onModelModeChanged.fire({ model, oldLanguageId: oldLanguageId });
561 }
563 > protected _getSHA1Computer(): ITextModelSHA1Computer {
564 > return new DefaultModelSHA1Computer(); modelService.ts ×29
565 > }
567 >
568 > export interface ITextModelSHA1Computer {
569 > canComputeSHA1(model: ITextModel): boolean;
570 > computeSHA1(model: ITextModel): string;
571 > }
572 >
573 > export class DefaultModelSHA1Computer implements ITextModelSHA1Computer {
574 >
575 > public static MAX_MODEL_SIZE = 10 * 1024 * 1024; // takes 200ms to compute a sha1 on a 10MB model on a new machine
576 >
577 > canComputeSHA1(model: ITextModel): boolean {
578 > return (model.getValueLength() <= DefaultModelSHA1Computer.MAX_MODEL_SIZE); modelService.ts ×14
579 > }
581 > computeSHA1(model: ITextModel): string {
582 > // compute the sha1 modelService.ts ×14
583 > const shaComputer = new StringSHA1();
584 > const snapshot = model.createSnapshot();
585 > let text: string | null;
586 > while ((text = snapshot.read())) {
587 > shaComputer.update(text);
588 > }
589 > return shaComputer.digest();
590 > }