modelServiceImpl.ts 21.5 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';
A
Alex Dima 已提交
8 9
import {onUnexpectedError} from 'vs/base/common/errors';
import Event, {Emitter} from 'vs/base/common/event';
E
Erich Gamma 已提交
10 11
import {IEmitterEvent} from 'vs/base/common/eventEmitter';
import {IHTMLContentElement} from 'vs/base/common/htmlContent';
A
tslint  
Alex Dima 已提交
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';
E
Erich Gamma 已提交
15
import {TPromise} from 'vs/base/common/winjs.base';
A
Alex Dima 已提交
16
import {IMarker, IMarkerService} from 'vs/platform/markers/common/markers';
E
Erich Gamma 已提交
17
import {anonymize} from 'vs/platform/telemetry/common/telemetry';
A
Alex Dima 已提交
18 19 20 21
import {IThreadService, Remotable, ThreadAffinity} from 'vs/platform/thread/common/thread';
import {Range} from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {IMirrorModelEvents, MirrorModel} from 'vs/editor/common/model/mirrorModel';
E
Erich Gamma 已提交
22
import {Model} from 'vs/editor/common/model/model';
A
Alex Dima 已提交
23 24 25 26
import {IMode} from 'vs/editor/common/modes';
import {IModeService} from 'vs/editor/common/services/modeService';
import {IModelService} from 'vs/editor/common/services/modelService';
import {IResourceService} from 'vs/editor/common/services/resourceService';
27
import * as platform from 'vs/base/common/platform';
28
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
29
import {DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE} from 'vs/editor/common/config/defaultConfig';
A
Alex Dima 已提交
30
import {IMessageService} from 'vs/platform/message/common/message';
E
Erich Gamma 已提交
31 32

export interface IRawModelData {
B
Benjamin Pasero 已提交
33 34 35 36
	url: URI;
	versionId: number;
	value: editorCommon.IRawText;
	modeId: string;
E
Erich Gamma 已提交
37 38
}

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

class ModelData implements IDisposable {
A
Alex Dima 已提交
44
	model: editorCommon.IModel;
A
Alex Dima 已提交
45
	isSyncedToWorkers: boolean;
E
Erich Gamma 已提交
46

A
Alex Dima 已提交
47 48
	private _markerDecorations: string[];
	private _modelEventsListener: IDisposable;
E
Erich Gamma 已提交
49

B
Benjamin Pasero 已提交
50
	constructor(model: editorCommon.IModel, eventsHandler: (modelData: ModelData, events: IEmitterEvent[]) => void) {
E
Erich Gamma 已提交
51
		this.model = model;
A
Alex Dima 已提交
52 53 54 55
		this.isSyncedToWorkers = false;

		this._markerDecorations = [];
		this._modelEventsListener = model.addBulkListener2((events) => eventsHandler(this, events));
E
Erich Gamma 已提交
56 57 58
	}

	public dispose(): void {
A
Alex Dima 已提交
59 60 61
		this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []);
		this._modelEventsListener.dispose();
		this._modelEventsListener = null;
E
Erich Gamma 已提交
62
		this.model = null;
A
Alex Dima 已提交
63
	}
E
Erich Gamma 已提交
64

A
Alex Dima 已提交
65 66 67 68
	public getModelId(): string {
		return MODEL_ID(this.model.getAssociatedResource());
	}

B
Benjamin Pasero 已提交
69
	public acceptMarkerDecorations(newDecorations: editorCommon.IModelDeltaDecoration[]): void {
A
Alex Dima 已提交
70
		this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations);
E
Erich Gamma 已提交
71
	}
A
Alex Dima 已提交
72
}
E
Erich Gamma 已提交
73

A
Alex Dima 已提交
74 75
class ModelMarkerHandler {

B
Benjamin Pasero 已提交
76
	public static setMarkers(modelData: ModelData, markers: IMarker[]): void {
E
Erich Gamma 已提交
77 78 79 80

		// Limit to the first 500 errors/warnings
		markers = markers.slice(0, 500);

B
Benjamin Pasero 已提交
81
		let newModelDecorations: editorCommon.IModelDeltaDecoration[] = markers.map((marker) => {
A
Alex Dima 已提交
82 83
			return {
				range: this._createDecorationRange(modelData.model, marker),
E
Erich Gamma 已提交
84 85 86
				options: this._createDecorationOption(marker)
			};
		});
A
Alex Dima 已提交
87 88

		modelData.acceptMarkerDecorations(newModelDecorations);
E
Erich Gamma 已提交
89 90
	}

B
Benjamin Pasero 已提交
91
	private static _createDecorationRange(model: editorCommon.IModel, rawMarker: IMarker): editorCommon.IRange {
A
Alex Dima 已提交
92
		let marker = model.validateRange(new Range(rawMarker.startLineNumber, rawMarker.startColumn, rawMarker.endLineNumber, rawMarker.endColumn));
A
Alex Dima 已提交
93
		let ret: editorCommon.IEditorRange = new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn);
E
Erich Gamma 已提交
94
		if (ret.isEmpty()) {
A
Alex Dima 已提交
95
			let word = model.getWordAtPosition(ret.getStartPosition());
E
Erich Gamma 已提交
96 97 98 99
			if (word) {
				ret.startColumn = word.startColumn;
				ret.endColumn = word.endColumn;
			} else {
A
Alex Dima 已提交
100 101
				let maxColumn = model.getLineLastNonWhitespaceColumn(marker.startLineNumber) ||
					model.getLineMaxColumn(marker.startLineNumber);
E
Erich Gamma 已提交
102 103 104

				if (maxColumn === 1) {
					// empty line
B
Benjamin Pasero 已提交
105
					//					console.warn('marker on empty line:', marker);
E
Erich Gamma 已提交
106 107 108 109 110 111 112 113 114 115
				} else if (ret.endColumn >= maxColumn) {
					// behind eol
					ret.endColumn = maxColumn;
					ret.startColumn = maxColumn - 1;
				} else {
					// extend marker to width = 1
					ret.endColumn += 1;
				}
			}
		} else if (rawMarker.endColumn === Number.MAX_VALUE && rawMarker.startColumn === 1 && ret.startLineNumber === ret.endLineNumber) {
A
Alex Dima 已提交
116
			let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber);
E
Erich Gamma 已提交
117 118 119 120 121 122 123 124
			if (minColumn < ret.endColumn) {
				ret.startColumn = minColumn;
				rawMarker.startColumn = minColumn;
			}
		}
		return ret;
	}

B
Benjamin Pasero 已提交
125
	private static _createDecorationOption(marker: IMarker): editorCommon.IModelDecorationOptions {
E
Erich Gamma 已提交
126 127 128 129 130 131 132 133 134 135 136 137

		let className: string;
		let color: string;
		let darkColor: string;
		let htmlMessage: IHTMLContentElement[] = null;

		switch (marker.severity) {
			case Severity.Ignore:
				// do something
				break;
			case Severity.Warning:
			case Severity.Info:
A
Alex Dima 已提交
138
				className = editorCommon.ClassName.EditorWarningDecoration;
E
Erich Gamma 已提交
139 140 141 142 143
				color = 'rgba(18,136,18,0.7)';
				darkColor = 'rgba(18,136,18,0.7)';
				break;
			case Severity.Error:
			default:
A
Alex Dima 已提交
144
				className = editorCommon.ClassName.EditorErrorDecoration;
E
Erich Gamma 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157
				color = 'rgba(255,18,18,0.7)';
				darkColor = 'rgba(255,18,18,0.7)';
				break;
		}

		if (typeof marker.message === 'string') {
			htmlMessage = [{ isText: true, text: marker.message }];
		} else if (Array.isArray(marker.message)) {
			htmlMessage = <IHTMLContentElement[]><any>marker.message;
		} else if (marker.message) {
			htmlMessage = [marker.message];
		}

158
		if (htmlMessage && marker.source) {
159 160 161
			htmlMessage.unshift({ isText: true, text: `[${marker.source}] ` });
		}

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

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

E
Erich Gamma 已提交
187 188 189 190 191 192
export class ModelServiceImpl implements IModelService {
	public serviceId = IModelService;

	private _markerService: IMarkerService;
	private _markerServiceSubscription: IDisposable;
	private _threadService: IThreadService;
193
	private _modeService: IModeService;
A
Alex Dima 已提交
194
	private _messageService: IMessageService;
195 196
	private _configurationService: IConfigurationService;
	private _configurationServiceSubscription: IDisposable;
E
Erich Gamma 已提交
197 198
	private _workerHelper: ModelServiceWorkerHelper;

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

	private _modelCreationOptions: editorCommon.ITextModelCreationOptions;
A
Alex Dima 已提交
204

A
Alex Dima 已提交
205 206
	private _hasShownMigrationMessage: boolean;

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

212 213 214 215
	constructor(
		threadService: IThreadService,
		markerService: IMarkerService,
		modeService: IModeService,
A
Alex Dima 已提交
216 217
		configurationService: IConfigurationService,
		messageService: IMessageService
218
	) {
219
		this._modelCreationOptions = {
220 221 222
			tabSize: DEFAULT_INDENTATION.tabSize,
			insertSpaces: DEFAULT_INDENTATION.insertSpaces,
			detectIndentation: DEFAULT_INDENTATION.detectIndentation,
223
			defaultEOL: (platform.isLinux || platform.isMacintosh) ? editorCommon.DefaultEndOfLine.LF : editorCommon.DefaultEndOfLine.CRLF,
224
			trimAutoWhitespace: DEFAULT_TRIM_AUTO_WHITESPACE
225
		};
E
Erich Gamma 已提交
226 227
		this._threadService = threadService;
		this._markerService = markerService;
228
		this._modeService = modeService;
E
Erich Gamma 已提交
229
		this._workerHelper = this._threadService.getRemotable(ModelServiceWorkerHelper);
230
		this._configurationService = configurationService;
A
Alex Dima 已提交
231 232
		this._messageService = messageService;
		this._hasShownMigrationMessage = false;
B
Benjamin Pasero 已提交
233 234 235 236 237 238 239 240 241 242
		this._models = {};


		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);
		}
243

B
Benjamin Pasero 已提交
244
		let readConfig = (config: IRawConfig) => {
J
Joao Moreno 已提交
245

A
Alex Dima 已提交
246 247
			let shouldShowMigrationMessage = false;

248
			let tabSize = DEFAULT_INDENTATION.tabSize;
249 250 251
			if (config.editor && typeof config.editor.tabSize !== 'undefined') {
				let parsedTabSize = parseInt(config.editor.tabSize, 10);
				if (!isNaN(parsedTabSize)) {
252
					tabSize = parsedTabSize;
253
				}
A
Alex Dima 已提交
254
				shouldShowMigrationMessage = shouldShowMigrationMessage || (config.editor.tabSize === 'auto');
255 256
			}

257
			let insertSpaces = DEFAULT_INDENTATION.insertSpaces;
258
			if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
259
				insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
A
Alex Dima 已提交
260
				shouldShowMigrationMessage = shouldShowMigrationMessage || (config.editor.insertSpaces === 'auto');
261 262 263
			}

			let newDefaultEOL = this._modelCreationOptions.defaultEOL;
264
			const eol = config.files && config.files.eol;
J
Joao Moreno 已提交
265
			if (eol === '\r\n') {
266
				newDefaultEOL = editorCommon.DefaultEndOfLine.CRLF;
J
Joao Moreno 已提交
267
			} else if (eol === '\n') {
268
				newDefaultEOL = editorCommon.DefaultEndOfLine.LF;
269
			}
270

271 272 273 274 275
			let trimAutoWhitespace = this._modelCreationOptions.trimAutoWhitespace;
			if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') {
				trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace));
			}

276
			let detectIndentation = DEFAULT_INDENTATION.detectIndentation;
277 278 279
			if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
				detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
			}
280

281 282 283
			this._setModelOptions({
				tabSize: tabSize,
				insertSpaces: insertSpaces,
284
				detectIndentation: detectIndentation,
285 286
				defaultEOL: newDefaultEOL,
				trimAutoWhitespace: trimAutoWhitespace
287 288
			});

A
Alex Dima 已提交
289 290 291 292 293

			if (shouldShowMigrationMessage && !this._hasShownMigrationMessage) {
				this._hasShownMigrationMessage = true;
				this._messageService.show(Severity.Info, nls.localize('indentAutoMigrate', "Please update your settings: `editor.detectIndentation` replaces `editor.tabSize`: \"auto\" or `editor.insertSpaces`: \"auto\""));
			}
294
		};
295 296

		this._configurationServiceSubscription = this._configurationService.onDidUpdateConfiguration(e => {
297
			readConfig(e.config);
298
		});
299
		readConfig(this._configurationService.getConfiguration());
E
Erich Gamma 已提交
300 301
	}

302 303 304 305 306 307 308 309 310
	public getCreationOptions(): editorCommon.ITextModelCreationOptions {
		return this._modelCreationOptions;
	}

	private _setModelOptions(newOpts: editorCommon.ITextModelCreationOptions): void {
		if (
			(this._modelCreationOptions.detectIndentation === newOpts.detectIndentation)
			&& (this._modelCreationOptions.insertSpaces === newOpts.insertSpaces)
			&& (this._modelCreationOptions.tabSize === newOpts.tabSize)
311
			&& (this._modelCreationOptions.trimAutoWhitespace === newOpts.trimAutoWhitespace)
312 313 314 315 316 317 318
		) {
			// Same indent opts, no need to touch created models
			this._modelCreationOptions = newOpts;
			return;
		}
		this._modelCreationOptions = newOpts;

319
		// Update options on all models
A
Alex Dima 已提交
320 321 322 323
		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];
324

A
Alex Dima 已提交
325 326
			if (this._modelCreationOptions.detectIndentation) {
				modelData.model.detectIndentation(this._modelCreationOptions.insertSpaces, this._modelCreationOptions.tabSize);
327 328 329
				modelData.model.updateOptions({
					trimAutoWhitespace: this._modelCreationOptions.trimAutoWhitespace
				});
A
Alex Dima 已提交
330 331 332
			} else {
				modelData.model.updateOptions({
					insertSpaces: this._modelCreationOptions.insertSpaces,
333 334
					tabSize: this._modelCreationOptions.tabSize,
					trimAutoWhitespace: this._modelCreationOptions.trimAutoWhitespace
A
Alex Dima 已提交
335
				});
336 337
			}
		}
338 339
	}

E
Erich Gamma 已提交
340
	public dispose(): void {
B
Benjamin Pasero 已提交
341
		if (this._markerServiceSubscription) {
E
Erich Gamma 已提交
342 343
			this._markerServiceSubscription.dispose();
		}
344
		this._configurationServiceSubscription.dispose();
E
Erich Gamma 已提交
345 346 347
	}

	private _handleMarkerChange(changedResources: URI[]): void {
A
Alex Dima 已提交
348 349 350 351
		changedResources.forEach((resource) => {
			let modelId = MODEL_ID(resource);
			let modelData = this._models[modelId];
			if (!modelData) {
E
Erich Gamma 已提交
352 353
				return;
			}
A
Alex Dima 已提交
354
			ModelMarkerHandler.setMarkers(modelData, this._markerService.read({ resource: resource, take: 500 }));
E
Erich Gamma 已提交
355 356 357 358 359
		});
	}

	// --- begin IModelService

B
Benjamin Pasero 已提交
360
	private _shouldSyncModelToWorkers(model: editorCommon.IModel): boolean {
361 362 363 364 365 366 367
		if (model.isTooLargeForHavingARichMode()) {
			return false;
		}
		// Only sync models with compat modes to the workers
		return this._modeService.isCompatMode(model.getMode().getId());
	}

B
Benjamin Pasero 已提交
368
	private _createModelData(value: string, modeOrPromise: TPromise<IMode> | IMode, resource: URI): ModelData {
A
Alex Dima 已提交
369
		// create & save the model
370
		let model = new Model(value, this._modelCreationOptions, modeOrPromise, resource);
A
Alex Dima 已提交
371
		let modelId = MODEL_ID(model.getAssociatedResource());
E
Erich Gamma 已提交
372 373 374

		if (this._models[modelId]) {
			// There already exists a model with this id => this is a programmer error
A
Alex Dima 已提交
375
			throw new Error('ModelService: Cannot add model ' + anonymize(modelId) + ' because it already exists!');
E
Erich Gamma 已提交
376 377
		}

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

A
Alex Dima 已提交
381
		return modelData;
E
Erich Gamma 已提交
382 383
	}

B
Benjamin Pasero 已提交
384
	public createModel(value: string, modeOrPromise: TPromise<IMode> | IMode, resource: URI): editorCommon.IModel {
A
Alex Dima 已提交
385
		let modelData = this._createModelData(value, modeOrPromise, resource);
E
Erich Gamma 已提交
386

A
Alex Dima 已提交
387 388 389
		// handle markers (marker service => model)
		if (this._markerService) {
			ModelMarkerHandler.setMarkers(modelData, this._markerService.read({ resource: modelData.model.getAssociatedResource() }));
E
Erich Gamma 已提交
390 391
		}

392
		if (this._shouldSyncModelToWorkers(modelData.model)) {
A
Alex Dima 已提交
393
			// send this model to the workers
394
			this._beginWorkerSync(modelData);
E
Erich Gamma 已提交
395 396
		}

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

A
Alex Dima 已提交
399
		return modelData.model;
E
Erich Gamma 已提交
400 401
	}

J
Johannes Rieken 已提交
402
	public destroyModel(resource: URI): void {
A
Alex Dima 已提交
403 404 405 406
		// 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 已提交
407
		}
A
Alex Dima 已提交
408
		modelData.model.dispose();
E
Erich Gamma 已提交
409 410
	}

A
Alex Dima 已提交
411 412
	public getModels(): editorCommon.IModel[] {
		let ret: editorCommon.IModel[] = [];
A
Alex Dima 已提交
413 414 415 416 417

		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 已提交
418
		}
A
Alex Dima 已提交
419

E
Erich Gamma 已提交
420 421 422
		return ret;
	}

A
Alex Dima 已提交
423
	public getModel(resource: URI): editorCommon.IModel {
A
Alex Dima 已提交
424 425 426 427
		let modelId = MODEL_ID(resource);
		let modelData = this._models[modelId];
		if (!modelData) {
			return null;
E
Erich Gamma 已提交
428
		}
A
Alex Dima 已提交
429
		return modelData.model;
E
Erich Gamma 已提交
430 431
	}

A
Alex Dima 已提交
432
	public get onModelAdded(): Event<editorCommon.IModel> {
433
		return this._onModelAdded ? this._onModelAdded.event : null;
E
Erich Gamma 已提交
434 435
	}

A
Alex Dima 已提交
436
	public get onModelRemoved(): Event<editorCommon.IModel> {
437
		return this._onModelRemoved ? this._onModelRemoved.event : null;
E
Erich Gamma 已提交
438 439
	}

A
Alex Dima 已提交
440
	public get onModelModeChanged(): Event<{ model: editorCommon.IModel; oldModeId: string; }> {
441
		return this._onModelModeChanged ? this._onModelModeChanged.event : null;
E
Erich Gamma 已提交
442 443 444 445
	}

	// --- end IModelService

B
Benjamin Pasero 已提交
446
	private _beginWorkerSync(modelData: ModelData): void {
447 448 449 450 451 452 453 454
		if (modelData.isSyncedToWorkers) {
			throw new Error('Model is already being synced to workers!');
		}

		modelData.isSyncedToWorkers = true;
		this._workerHelper.$_acceptNewModel(ModelServiceImpl._getBoundModelData(modelData.model));
	}

B
Benjamin Pasero 已提交
455
	private _stopWorkerSync(modelData: ModelData): void {
456 457 458 459 460 461 462
		if (!modelData.isSyncedToWorkers) {
			throw new Error('Model is already not being synced to workers!');
		}
		modelData.isSyncedToWorkers = false;
		this._workerHelper.$_acceptDidDisposeModel(modelData.model.getAssociatedResource());
	}

B
Benjamin Pasero 已提交
463
	private _onModelDisposing(model: editorCommon.IModel): void {
A
Alex Dima 已提交
464 465 466 467 468 469 470 471 472 473 474 475 476 477
		let modelId = MODEL_ID(model.getAssociatedResource());
		let modelData = this._models[modelId];

		// TODO@Joh why are we removing markers here?
		if (this._markerService) {
			var markers = this._markerService.read({ resource: model.getAssociatedResource() }),
				owners: { [o: string]: any } = Object.create(null);

			markers.forEach(marker => owners[marker.owner] = this);
			Object.keys(owners).forEach(owner => this._markerService.changeOne(owner, model.getAssociatedResource(), []));
		}

		if (modelData.isSyncedToWorkers) {
			// Dispose model in workers
478
			this._stopWorkerSync(modelData);
A
Alex Dima 已提交
479 480 481 482 483 484 485 486
		}

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

		this._onModelRemoved.fire(model);
	}

B
Benjamin Pasero 已提交
487
	private static _getBoundModelData(model: editorCommon.IModel): IRawModelData {
E
Erich Gamma 已提交
488 489 490 491 492 493 494 495
		return {
			url: model.getAssociatedResource(),
			versionId: model.getVersionId(),
			value: model.toRawText(),
			modeId: model.getMode().getId()
		};
	}

B
Benjamin Pasero 已提交
496
	private _onModelEvents(modelData: ModelData, events: IEmitterEvent[]): void {
E
Erich Gamma 已提交
497

498
		// First look for dispose
A
Alex Dima 已提交
499 500
		for (let i = 0, len = events.length; i < len; i++) {
			let e = events[i];
A
Alex Dima 已提交
501
			if (e.getType() === editorCommon.EventType.ModelDispose) {
502 503 504 505 506
				this._onModelDisposing(modelData.model);
				// no more processing since model got disposed
				return;
			}
		}
E
Erich Gamma 已提交
507

508 509 510
		// Second, look for mode change
		for (let i = 0, len = events.length; i < len; i++) {
			let e = events[i];
A
Alex Dima 已提交
511
			if (e.getType() === editorCommon.EventType.ModelModeChanged) {
512 513
				let wasSyncedToWorkers = modelData.isSyncedToWorkers;
				let shouldSyncToWorkers = this._shouldSyncModelToWorkers(modelData.model);
E
Erich Gamma 已提交
514

515 516
				this._onModelModeChanged.fire({
					model: modelData.model,
A
Alex Dima 已提交
517
					oldModeId: (<editorCommon.IModelModeChangedEvent>e.getData()).oldMode.getId()
518 519
				});

520 521 522
				if (wasSyncedToWorkers) {
					if (shouldSyncToWorkers) {
						// true -> true
A
Alex Dima 已提交
523
						// Forward mode change to all the workers
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
						this._workerHelper.$_acceptDidChangeModelMode(modelData.getModelId(), modelData.model.getMode().getId());
					} else {
						// true -> false
						// Stop worker sync for this model
						this._stopWorkerSync(modelData);
						// no more processing since we have removed the model from the workers
						return;
					}
				} else {
					if (shouldSyncToWorkers) {
						// false -> true
						// Begin syncing this model to the workers
						this._beginWorkerSync(modelData);
						// no more processing since we are sending the latest state
						return;
					} else {
						// false -> false
						// no more processing since this model was not synced and will not be synced
						return;
A
Alex Dima 已提交
543
					}
544 545 546 547 548 549 550 551 552 553 554 555 556
				}
			}
		}

		if (!modelData.isSyncedToWorkers) {
			return;
		}

		// Finally, look for model content changes
		let eventsForWorkers: IMirrorModelEvents = { contentChanged: [] };
		for (let i = 0, len = events.length; i < len; i++) {
			let e = events[i];

A
Alex Dima 已提交
557 558
			if (e.getType() === editorCommon.EventType.ModelContentChanged) {
				eventsForWorkers.contentChanged.push(<editorCommon.IModelContentChangedEvent>e.getData());
E
Erich Gamma 已提交
559 560 561
			}
		}

A
Alex Dima 已提交
562
		if (eventsForWorkers.contentChanged.length > 0) {
E
Erich Gamma 已提交
563
			// Forward events to all the workers
A
Alex Dima 已提交
564
			this._workerHelper.$_acceptModelEvents(modelData.getModelId(), eventsForWorkers);
E
Erich Gamma 已提交
565 566 567 568 569 570 571
		}
	}
}

@Remotable.WorkerContext('ModelServiceWorkerHelper', ThreadAffinity.All)
export class ModelServiceWorkerHelper {

B
Benjamin Pasero 已提交
572 573
	private _resourceService: IResourceService;
	private _modeService: IModeService;
E
Erich Gamma 已提交
574 575 576 577 578 579 580 581 582

	constructor(
		@IResourceService resourceService: IResourceService,
		@IModeService modeService: IModeService
	) {
		this._resourceService = resourceService;
		this._modeService = modeService;
	}

B
Benjamin Pasero 已提交
583
	public $_acceptNewModel(data: IRawModelData): TPromise<void> {
E
Erich Gamma 已提交
584
		// Create & insert the mirror model eagerly in the resource service
A
Alex Dima 已提交
585
		let mirrorModel = new MirrorModel(this._resourceService, data.versionId, data.value, null, data.url);
E
Erich Gamma 已提交
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
		this._resourceService.insert(mirrorModel.getAssociatedResource(), mirrorModel);

		// Block worker execution until the mode is instantiated
		return this._modeService.getOrCreateMode(data.modeId).then((mode) => {
			// Changing mode should trigger a remove & an add, therefore:

			// (1) Remove from resource service
			this._resourceService.remove(mirrorModel.getAssociatedResource());

			// (2) Change mode
			mirrorModel.setMode(mode);

			// (3) Insert again to resource service (it will have the new mode)
			this._resourceService.insert(mirrorModel.getAssociatedResource(), mirrorModel);
		});
	}

B
Benjamin Pasero 已提交
603
	public $_acceptDidChangeModelMode(modelId: string, newModeId: string): TPromise<void> {
A
Alex Dima 已提交
604
		let mirrorModel = this._resourceService.get(URI.parse(modelId));
E
Erich Gamma 已提交
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620

		// Block worker execution until the mode is instantiated
		return this._modeService.getOrCreateMode(newModeId).then((mode) => {
			// Changing mode should trigger a remove & an add, therefore:

			// (1) Remove from resource service
			this._resourceService.remove(mirrorModel.getAssociatedResource());

			// (2) Change mode
			mirrorModel.setMode(mode);

			// (3) Insert again to resource service (it will have the new mode)
			this._resourceService.insert(mirrorModel.getAssociatedResource(), mirrorModel);
		});
	}

B
Benjamin Pasero 已提交
621
	public $_acceptDidDisposeModel(url: URI): void {
A
Alex Dima 已提交
622
		let model = <MirrorModel>this._resourceService.get(url);
E
Erich Gamma 已提交
623 624 625 626 627 628
		this._resourceService.remove(url);
		if (model) {
			model.dispose();
		}
	}

B
Benjamin Pasero 已提交
629
	public $_acceptModelEvents(modelId: string, events: IMirrorModelEvents): void {
J
Johannes Rieken 已提交
630
		let model = <MirrorModel>this._resourceService.get(URI.parse(modelId));
A
Alex Dima 已提交
631 632 633 634 635 636
		if (!model) {
			throw new Error('Received model events for missing model ' + anonymize(modelId));
		}
		try {
			model.onEvents(events);
		} catch (err) {
A
Alex Dima 已提交
637
			onUnexpectedError(err);
E
Erich Gamma 已提交
638 639 640
		}
	}
}