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

A
Alex Dima 已提交
7
import * as nls from 'vs/nls';
S
Sandeep Somavarapu 已提交
8
import network = require('vs/base/common/network');
J
Johannes Rieken 已提交
9 10
import Event, { Emitter } from 'vs/base/common/event';
import { EmitterEvent } from 'vs/base/common/eventEmitter';
J
Johannes Rieken 已提交
11
import { MarkdownString } from 'vs/base/common/htmlContent';
J
Johannes Rieken 已提交
12
import { IDisposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
13 14
import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
J
Johannes Rieken 已提交
15 16 17
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
import { Range } from 'vs/editor/common/core/range';
18
import { Selection } from 'vs/editor/common/core/selection';
A
Alex Dima 已提交
19
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
20
import { Model } from 'vs/editor/common/model/model';
A
Alex Dima 已提交
21
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
22
import { IModelService } from 'vs/editor/common/services/modelService';
23
import * as platform from 'vs/base/common/platform';
J
Johannes Rieken 已提交
24
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
25
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
A
Alex Dima 已提交
26
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
27
import { IRawTextSource, TextSource, RawTextSource, ITextSource } from 'vs/editor/common/model/textSource';
28
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
29
import { ClassName } from 'vs/editor/common/model/textModelWithDecorations';
30 31
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
import { EditOperation } from 'vs/editor/common/core/editOperation';
32
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
33
import { overviewRulerWarning, overviewRulerError, overviewRulerInfo } from 'vs/editor/common/view/editorColorRegistry';
E
Erich Gamma 已提交
34

B
Benjamin Pasero 已提交
35
function MODEL_ID(resource: URI): string {
A
Alex Dima 已提交
36 37 38 39
	return resource.toString();
}

class ModelData implements IDisposable {
A
Alex Dima 已提交
40
	model: editorCommon.IModel;
E
Erich Gamma 已提交
41

A
Alex Dima 已提交
42 43
	private _markerDecorations: string[];
	private _modelEventsListener: IDisposable;
E
Erich Gamma 已提交
44

A
Alex Dima 已提交
45
	constructor(model: editorCommon.IModel, eventsHandler: (modelData: ModelData, events: EmitterEvent[]) => void) {
E
Erich Gamma 已提交
46
		this.model = model;
A
Alex Dima 已提交
47 48

		this._markerDecorations = [];
A
Alex Dima 已提交
49
		this._modelEventsListener = model.addBulkListener((events) => eventsHandler(this, events));
E
Erich Gamma 已提交
50 51 52
	}

	public dispose(): void {
A
Alex Dima 已提交
53 54 55
		this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []);
		this._modelEventsListener.dispose();
		this._modelEventsListener = null;
E
Erich Gamma 已提交
56
		this.model = null;
A
Alex Dima 已提交
57
	}
E
Erich Gamma 已提交
58

A
Alex Dima 已提交
59
	public getModelId(): string {
60
		return MODEL_ID(this.model.uri);
A
Alex Dima 已提交
61 62
	}

B
Benjamin Pasero 已提交
63
	public acceptMarkerDecorations(newDecorations: editorCommon.IModelDeltaDecoration[]): void {
A
Alex Dima 已提交
64
		this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations);
E
Erich Gamma 已提交
65
	}
A
Alex Dima 已提交
66
}
E
Erich Gamma 已提交
67

A
Alex Dima 已提交
68 69
class ModelMarkerHandler {

70
	public static setMarkers(modelData: ModelData, markerService: IMarkerService): void {
E
Erich Gamma 已提交
71 72

		// Limit to the first 500 errors/warnings
73
		const markers = markerService.read({ resource: modelData.model.uri, take: 500 });
E
Erich Gamma 已提交
74

B
Benjamin Pasero 已提交
75
		let newModelDecorations: editorCommon.IModelDeltaDecoration[] = markers.map((marker) => {
A
Alex Dima 已提交
76 77
			return {
				range: this._createDecorationRange(modelData.model, marker),
E
Erich Gamma 已提交
78 79 80
				options: this._createDecorationOption(marker)
			};
		});
A
Alex Dima 已提交
81 82

		modelData.acceptMarkerDecorations(newModelDecorations);
E
Erich Gamma 已提交
83 84
	}

85
	private static _createDecorationRange(model: editorCommon.IModel, rawMarker: IMarker): Range {
A
Alex Dima 已提交
86
		let marker = model.validateRange(new Range(rawMarker.startLineNumber, rawMarker.startColumn, rawMarker.endLineNumber, rawMarker.endColumn));
87
		let ret: Range = new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn);
E
Erich Gamma 已提交
88
		if (ret.isEmpty()) {
A
Alex Dima 已提交
89
			let word = model.getWordAtPosition(ret.getStartPosition());
E
Erich Gamma 已提交
90
			if (word) {
91
				ret = new Range(ret.startLineNumber, word.startColumn, ret.endLineNumber, word.endColumn);
E
Erich Gamma 已提交
92
			} else {
A
Alex Dima 已提交
93 94
				let maxColumn = model.getLineLastNonWhitespaceColumn(marker.startLineNumber) ||
					model.getLineMaxColumn(marker.startLineNumber);
E
Erich Gamma 已提交
95 96 97

				if (maxColumn === 1) {
					// empty line
98
					// console.warn('marker on empty line:', marker);
E
Erich Gamma 已提交
99 100
				} else if (ret.endColumn >= maxColumn) {
					// behind eol
101
					ret = new Range(ret.startLineNumber, maxColumn - 1, ret.endLineNumber, maxColumn);
E
Erich Gamma 已提交
102 103
				} else {
					// extend marker to width = 1
104
					ret = new Range(ret.startLineNumber, ret.startColumn, ret.endLineNumber, ret.endColumn + 1);
E
Erich Gamma 已提交
105 106 107
				}
			}
		} else if (rawMarker.endColumn === Number.MAX_VALUE && rawMarker.startColumn === 1 && ret.startLineNumber === ret.endLineNumber) {
A
Alex Dima 已提交
108
			let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber);
E
Erich Gamma 已提交
109
			if (minColumn < ret.endColumn) {
110
				ret = new Range(ret.startLineNumber, minColumn, ret.endLineNumber, ret.endColumn);
E
Erich Gamma 已提交
111 112 113 114 115 116
				rawMarker.startColumn = minColumn;
			}
		}
		return ret;
	}

B
Benjamin Pasero 已提交
117
	private static _createDecorationOption(marker: IMarker): editorCommon.IModelDecorationOptions {
E
Erich Gamma 已提交
118 119

		let className: string;
120 121
		let color: ThemeColor;
		let darkColor: ThemeColor;
E
Erich Gamma 已提交
122 123 124 125 126 127

		switch (marker.severity) {
			case Severity.Ignore:
				// do something
				break;
			case Severity.Warning:
A
Alex Dima 已提交
128
				className = ClassName.EditorWarningDecoration;
129 130
				color = themeColorFromId(overviewRulerWarning);
				darkColor = themeColorFromId(overviewRulerWarning);
E
Erich Gamma 已提交
131
				break;
132 133 134 135 136
			case Severity.Info:
				className = ClassName.EditorInfoDecoration;
				color = themeColorFromId(overviewRulerInfo);
				darkColor = themeColorFromId(overviewRulerInfo);
				break;
E
Erich Gamma 已提交
137 138
			case Severity.Error:
			default:
A
Alex Dima 已提交
139
				className = ClassName.EditorErrorDecoration;
140 141
				color = themeColorFromId(overviewRulerError);
				darkColor = themeColorFromId(overviewRulerError);
E
Erich Gamma 已提交
142 143 144
				break;
		}

J
Johannes Rieken 已提交
145
		let hoverMessage: MarkdownString = null;
A
Alex Dima 已提交
146
		let { message, source } = marker;
E
Erich Gamma 已提交
147

148
		if (typeof message === 'string') {
J
Joao Moreno 已提交
149 150
			message = message.trim();

151
			if (source) {
J
Joao Moreno 已提交
152 153 154 155 156
				if (/\n/g.test(message)) {
					message = nls.localize('diagAndSourceMultiline', "[{0}]\n{1}", source, message);
				} else {
					message = nls.localize('diagAndSource', "[{0}] {1}", source, message);
				}
157
			}
J
Joao Moreno 已提交
158

J
Johannes Rieken 已提交
159
			hoverMessage = new MarkdownString().appendCodeblock('_', message);
160 161
		}

E
Erich Gamma 已提交
162
		return {
A
Alex Dima 已提交
163
			stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
E
Erich Gamma 已提交
164
			className,
165
			hoverMessage,
166
			showIfCollapsed: true,
E
Erich Gamma 已提交
167 168 169
			overviewRuler: {
				color,
				darkColor,
A
Alex Dima 已提交
170
				position: editorCommon.OverviewRulerLane.Right
E
Erich Gamma 已提交
171 172 173 174 175
			}
		};
	}
}

176 177 178 179 180 181 182 183
interface IRawConfig {
	files?: {
		eol?: any;
	};
	editor?: {
		tabSize?: any;
		insertSpaces?: any;
		detectIndentation?: any;
184
		trimAutoWhitespace?: any;
185 186 187
	};
}

188 189
const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? editorCommon.DefaultEndOfLine.LF : editorCommon.DefaultEndOfLine.CRLF;

E
Erich Gamma 已提交
190
export class ModelServiceImpl implements IModelService {
191
	public _serviceBrand: any;
E
Erich Gamma 已提交
192 193 194

	private _markerService: IMarkerService;
	private _markerServiceSubscription: IDisposable;
195 196
	private _configurationService: IConfigurationService;
	private _configurationServiceSubscription: IDisposable;
E
Erich Gamma 已提交
197

A
Alex Dima 已提交
198 199 200
	private _onModelAdded: Emitter<editorCommon.IModel>;
	private _onModelRemoved: Emitter<editorCommon.IModel>;
	private _onModelModeChanged: Emitter<{ model: editorCommon.IModel; oldModeId: string; }>;
201

202 203
	private _modelCreationOptionsByLanguageAndResource: {
		[languageAndResource: string]: editorCommon.ITextModelCreationOptions;
204
	};
A
Alex Dima 已提交
205 206 207 208

	/**
	 * All the models known in the system.
	 */
B
Benjamin Pasero 已提交
209
	private _models: { [modelId: string]: ModelData; };
E
Erich Gamma 已提交
210

211
	constructor(
212 213
		@IMarkerService markerService: IMarkerService,
		@IConfigurationService configurationService: IConfigurationService,
214
	) {
E
Erich Gamma 已提交
215
		this._markerService = markerService;
216
		this._configurationService = configurationService;
B
Benjamin Pasero 已提交
217
		this._models = {};
218
		this._modelCreationOptionsByLanguageAndResource = Object.create(null);
B
Benjamin Pasero 已提交
219 220 221 222 223 224 225
		this._onModelAdded = new Emitter<editorCommon.IModel>();
		this._onModelRemoved = new Emitter<editorCommon.IModel>();
		this._onModelModeChanged = new Emitter<{ model: editorCommon.IModel; oldModeId: string; }>();

		if (this._markerService) {
			this._markerServiceSubscription = this._markerService.onMarkerChanged(this._handleMarkerChange, this);
		}
226

227 228 229
		this._configurationServiceSubscription = this._configurationService.onDidUpdateConfiguration(e => this._updateModelOptions());
		this._updateModelOptions();
	}
J
Joao Moreno 已提交
230

231
	private static _readModelOptions(config: IRawConfig): editorCommon.ITextModelCreationOptions {
232
		let tabSize = EDITOR_MODEL_DEFAULTS.tabSize;
233 234 235 236
		if (config.editor && typeof config.editor.tabSize !== 'undefined') {
			let parsedTabSize = parseInt(config.editor.tabSize, 10);
			if (!isNaN(parsedTabSize)) {
				tabSize = parsedTabSize;
237
			}
238
		}
239

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

245
		let newDefaultEOL = DEFAULT_EOL;
246
		const eol = config.files && config.files.eol;
247 248 249 250 251
		if (eol === '\r\n') {
			newDefaultEOL = editorCommon.DefaultEndOfLine.CRLF;
		} else if (eol === '\n') {
			newDefaultEOL = editorCommon.DefaultEndOfLine.LF;
		}
252

253
		let trimAutoWhitespace = EDITOR_MODEL_DEFAULTS.trimAutoWhitespace;
254 255 256
		if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') {
			trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace));
		}
257

258
		let detectIndentation = EDITOR_MODEL_DEFAULTS.detectIndentation;
259 260 261
		if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
			detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
		}
262

263 264 265 266 267 268
		return {
			tabSize: tabSize,
			insertSpaces: insertSpaces,
			detectIndentation: detectIndentation,
			defaultEOL: newDefaultEOL,
			trimAutoWhitespace: trimAutoWhitespace
269
		};
E
Erich Gamma 已提交
270 271
	}

272 273
	public getCreationOptions(language: string, resource: URI): editorCommon.ITextModelCreationOptions {
		let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
274
		if (!creationOptions) {
S
Sandeep Somavarapu 已提交
275
			creationOptions = ModelServiceImpl._readModelOptions(this._configurationService.getConfiguration({ overrideIdentifier: language, resource }));
276
			this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions;
277 278
		}
		return creationOptions;
279 280
	}

281
	private _updateModelOptions(): void {
282 283
		let oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource;
		this._modelCreationOptionsByLanguageAndResource = Object.create(null);
284

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

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
	private static _setModelOptionsForModel(model: editorCommon.IModel, newOptions: editorCommon.ITextModelCreationOptions, currentOptions: editorCommon.ITextModelCreationOptions): void {
		if (currentOptions
			&& (currentOptions.detectIndentation === newOptions.detectIndentation)
			&& (currentOptions.insertSpaces === newOptions.insertSpaces)
			&& (currentOptions.tabSize === newOptions.tabSize)
			&& (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,
				trimAutoWhitespace: newOptions.trimAutoWhitespace
			});
320
		}
321 322
	}

E
Erich Gamma 已提交
323
	public dispose(): void {
B
Benjamin Pasero 已提交
324
		if (this._markerServiceSubscription) {
E
Erich Gamma 已提交
325 326
			this._markerServiceSubscription.dispose();
		}
327
		this._configurationServiceSubscription.dispose();
E
Erich Gamma 已提交
328 329 330
	}

	private _handleMarkerChange(changedResources: URI[]): void {
A
Alex Dima 已提交
331 332 333 334
		changedResources.forEach((resource) => {
			let modelId = MODEL_ID(resource);
			let modelData = this._models[modelId];
			if (!modelData) {
E
Erich Gamma 已提交
335 336
				return;
			}
337
			ModelMarkerHandler.setMarkers(modelData, this._markerService);
E
Erich Gamma 已提交
338 339 340
		});
	}

S
Sandeep Somavarapu 已提交
341 342 343
	private _cleanUp(model: editorCommon.IModel): void {
		// clean up markers for internal, transient models
		if (model.uri.scheme === network.Schemas.inMemory
B
Benjamin Pasero 已提交
344 345 346 347 348
			|| model.uri.scheme === network.Schemas.internal
			|| model.uri.scheme === network.Schemas.vscode) {
			if (this._markerService) {
				this._markerService.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri]));
			}
S
Sandeep Somavarapu 已提交
349
		}
350 351 352

		// clean up cache
		delete this._modelCreationOptionsByLanguageAndResource[model.getLanguageIdentifier().language + model.uri];
S
Sandeep Somavarapu 已提交
353 354
	}

E
Erich Gamma 已提交
355 356
	// --- begin IModelService

A
Alex Dima 已提交
357
	private _createModelData(value: string | IRawTextSource, languageIdentifier: LanguageIdentifier, resource: URI): ModelData {
A
Alex Dima 已提交
358
		// create & save the model
359
		const options = this.getCreationOptions(languageIdentifier.language, resource);
A
Alex Dima 已提交
360 361
		const rawTextSource = (typeof value === 'string' ? RawTextSource.fromString(value) : value);
		let model: Model = new Model(rawTextSource, options, languageIdentifier, resource);
362
		let modelId = MODEL_ID(model.uri);
E
Erich Gamma 已提交
363 364 365

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

A
Alex Dima 已提交
369 370
		let modelData = new ModelData(model, (modelData, events) => this._onModelEvents(modelData, events));
		this._models[modelId] = modelData;
E
Erich Gamma 已提交
371

A
Alex Dima 已提交
372
		return modelData;
E
Erich Gamma 已提交
373 374
	}

A
Alex Dima 已提交
375
	public updateModel(model: editorCommon.IModel, value: string | IRawTextSource): void {
376
		let options = this.getCreationOptions(model.getLanguageIdentifier().language, model.uri);
A
Alex Dima 已提交
377
		const textSource = TextSource.create(value, options.defaultEOL);
378 379

		// Return early if the text is already set in that form
A
Alex Dima 已提交
380
		if (model.equals(textSource)) {
381 382
			return;
		}
383 384

		// Otherwise find a diff between the values and update model
385
		model.setEOL(textSource.EOL === '\r\n' ? editorCommon.EndOfLineSequence.CRLF : editorCommon.EndOfLineSequence.LF);
386 387
		model.pushEditOperations(
			[new Selection(1, 1, 1, 1)],
388
			ModelServiceImpl._computeEdits(model, textSource),
389 390
			(inverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[]) => [new Selection(1, 1, 1, 1)]
		);
391 392
	}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	/**
	 * Compute edits to bring `model` to the state of `textSource`.
	 */
	public static _computeEdits(model: editorCommon.IModel, textSource: ITextSource): editorCommon.IIdentifiedSingleEditOperation[] {
		const modelLineSequence = new class implements ISequence {
			public getLength(): number {
				return model.getLineCount();
			}
			public getElementHash(index: number): string {
				return model.getLineContent(index + 1);
			}
		};
		const textSourceLineSequence = new class implements ISequence {
			public getLength(): number {
				return textSource.lines.length;
			}
			public getElementHash(index: number): string {
				return textSource.lines[index];
			}
		};

		const diffResult = new LcsDiff(modelLineSequence, textSourceLineSequence).ComputeDiff(false);

		let edits: editorCommon.IIdentifiedSingleEditOperation[] = [], editsLen = 0;
		const modelLineCount = model.getLineCount();
		for (let i = 0, len = diffResult.length; i < len; i++) {
			const diff = diffResult[i];
			const originalStart = diff.originalStart;
			const originalLength = diff.originalLength;
			const modifiedStart = diff.modifiedStart;
			const modifiedLength = diff.modifiedLength;

			let lines: string[] = [];
			for (let j = 0; j < modifiedLength; j++) {
				lines[j] = textSource.lines[modifiedStart + j];
			}
			let text = lines.join('\n');

			let range: Range;
			if (originalLength === 0) {
				// insertion

				if (originalStart === modelLineCount) {
					// insert at the end
					const maxLineColumn = model.getLineMaxColumn(modelLineCount);
					range = new Range(
						modelLineCount, maxLineColumn,
						modelLineCount, maxLineColumn
					);
					text = '\n' + text;
				} else {
					// insert
					range = new Range(
						originalStart + 1, 1,
						originalStart + 1, 1
					);
					text = text + '\n';
				}

			} else if (modifiedLength === 0) {
				// deletion

				if (originalStart + originalLength >= modelLineCount) {
					// delete at the end
					range = new Range(
						originalStart, model.getLineMaxColumn(originalStart),
						originalStart + originalLength, model.getLineMaxColumn(originalStart + originalLength)
					);
				} else {
					// delete
					range = new Range(
						originalStart + 1, 1,
						originalStart + originalLength + 1, 1
					);
				}

			} else {
				// modification
				range = new Range(
					originalStart + 1, 1,
					originalStart + originalLength, model.getLineMaxColumn(originalStart + originalLength)
				);
			}

			edits[editsLen++] = EditOperation.replace(range, text);
		}

		return edits;
	}

A
Alex Dima 已提交
483
	public createModel(value: string | IRawTextSource, modeOrPromise: TPromise<IMode> | IMode, resource: URI): editorCommon.IModel {
484 485 486
		let modelData: ModelData;

		if (!modeOrPromise || TPromise.is(modeOrPromise)) {
A
Alex Dima 已提交
487
			modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource);
488 489
			this.setMode(modelData.model, modeOrPromise);
		} else {
A
Alex Dima 已提交
490
			modelData = this._createModelData(value, modeOrPromise.getLanguageIdentifier(), resource);
491
		}
E
Erich Gamma 已提交
492

A
Alex Dima 已提交
493 494
		// handle markers (marker service => model)
		if (this._markerService) {
495
			ModelMarkerHandler.setMarkers(modelData, this._markerService);
E
Erich Gamma 已提交
496 497
		}

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

A
Alex Dima 已提交
500
		return modelData.model;
E
Erich Gamma 已提交
501 502
	}

J
Johannes Rieken 已提交
503
	public setMode(model: editorCommon.IModel, modeOrPromise: TPromise<IMode> | IMode): void {
504 505 506 507 508 509
		if (!modeOrPromise) {
			return;
		}
		if (TPromise.is(modeOrPromise)) {
			modeOrPromise.then((mode) => {
				if (!model.isDisposed()) {
A
Alex Dima 已提交
510
					model.setMode(mode.getLanguageIdentifier());
511 512 513
				}
			});
		} else {
A
Alex Dima 已提交
514
			model.setMode(modeOrPromise.getLanguageIdentifier());
515 516 517
		}
	}

J
Johannes Rieken 已提交
518
	public destroyModel(resource: URI): void {
A
Alex Dima 已提交
519 520 521 522
		// We need to support that not all models get disposed through this service (i.e. model.dispose() should work!)
		let modelData = this._models[MODEL_ID(resource)];
		if (!modelData) {
			return;
E
Erich Gamma 已提交
523
		}
A
Alex Dima 已提交
524
		modelData.model.dispose();
E
Erich Gamma 已提交
525 526
	}

A
Alex Dima 已提交
527 528
	public getModels(): editorCommon.IModel[] {
		let ret: editorCommon.IModel[] = [];
A
Alex Dima 已提交
529 530 531 532 533

		let keys = Object.keys(this._models);
		for (let i = 0, len = keys.length; i < len; i++) {
			let modelId = keys[i];
			ret.push(this._models[modelId].model);
E
Erich Gamma 已提交
534
		}
A
Alex Dima 已提交
535

E
Erich Gamma 已提交
536 537 538
		return ret;
	}

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

A
Alex Dima 已提交
548
	public get onModelAdded(): Event<editorCommon.IModel> {
549
		return this._onModelAdded ? this._onModelAdded.event : null;
E
Erich Gamma 已提交
550 551
	}

A
Alex Dima 已提交
552
	public get onModelRemoved(): Event<editorCommon.IModel> {
553
		return this._onModelRemoved ? this._onModelRemoved.event : null;
E
Erich Gamma 已提交
554 555
	}

A
Alex Dima 已提交
556
	public get onModelModeChanged(): Event<{ model: editorCommon.IModel; oldModeId: string; }> {
557
		return this._onModelModeChanged ? this._onModelModeChanged.event : null;
E
Erich Gamma 已提交
558 559 560 561
	}

	// --- end IModelService

B
Benjamin Pasero 已提交
562
	private _onModelDisposing(model: editorCommon.IModel): void {
563
		let modelId = MODEL_ID(model.uri);
A
Alex Dima 已提交
564 565 566 567 568
		let modelData = this._models[modelId];

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

569
		this._cleanUp(model);
A
Alex Dima 已提交
570 571 572
		this._onModelRemoved.fire(model);
	}

A
Alex Dima 已提交
573
	private _onModelEvents(modelData: ModelData, events: EmitterEvent[]): void {
E
Erich Gamma 已提交
574

575
		// First look for dispose
A
Alex Dima 已提交
576 577
		for (let i = 0, len = events.length; i < len; i++) {
			let e = events[i];
578
			if (e.type === textModelEvents.TextModelEventType.ModelDispose) {
579 580 581 582 583
				this._onModelDisposing(modelData.model);
				// no more processing since model got disposed
				return;
			}
		}
E
Erich Gamma 已提交
584

585 586 587
		// Second, look for mode change
		for (let i = 0, len = events.length; i < len; i++) {
			let e = events[i];
588
			if (e.type === textModelEvents.TextModelEventType.ModelLanguageChanged) {
589
				const model = modelData.model;
590
				const oldModeId = (<textModelEvents.IModelLanguageChangedEvent>e.data).oldLanguage;
591
				const newModeId = model.getLanguageIdentifier().language;
592 593
				const oldOptions = this.getCreationOptions(oldModeId, model.uri);
				const newOptions = this.getCreationOptions(newModeId, model.uri);
594 595
				ModelServiceImpl._setModelOptionsForModel(model, newOptions, oldOptions);
				this._onModelModeChanged.fire({ model, oldModeId });
E
Erich Gamma 已提交
596 597 598 599
			}
		}
	}
}