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

import {IEmitterEvent} from 'vs/base/common/eventEmitter';
import {IModeService} from 'vs/editor/common/services/modeService';
import {IMarker, IMarkerService} from 'vs/platform/markers/common/markers';
A
Alex Dima 已提交
10
import {IMirrorModelEvents, MirrorModel} from 'vs/editor/common/model/mirrorModel';
E
Erich Gamma 已提交
11 12 13 14 15 16 17 18 19 20 21 22
import {Range} from 'vs/editor/common/core/range';
import EditorCommon = require('vs/editor/common/editorCommon');
import Modes = require('vs/editor/common/modes');
import {IResourceService} from 'vs/editor/common/services/resourceService';
import {IModelService} from 'vs/editor/common/services/modelService';
import {Remotable, IThreadService, ThreadAffinity, IThreadSynchronizableObject} from 'vs/platform/thread/common/thread';
import {AllWorkersAttr} from 'vs/platform/thread/common/threadService';
import {IHTMLContentElement} from 'vs/base/common/htmlContent';
import {EventSource} from 'vs/base/common/eventSource';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import {EventProvider} from 'vs/base/common/eventProvider';
A
Alex Dima 已提交
23
import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
E
Erich Gamma 已提交
24 25 26 27 28 29
import {TPromise} from 'vs/base/common/winjs.base';
import Errors = require('vs/base/common/errors');
import {anonymize} from 'vs/platform/telemetry/common/telemetry';
import {Model} from 'vs/editor/common/model/model';

export interface IRawModelData {
J
Johannes Rieken 已提交
30
	url:URI;
E
Erich Gamma 已提交
31 32 33 34 35 36
	versionId:number;
	value:EditorCommon.IRawText;
	properties:any;
	modeId:string;
}

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

class ModelData implements IDisposable {
	model: EditorCommon.IModel;
	isSyncedToWorkers: boolean;
E
Erich Gamma 已提交
44

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

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

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

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

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

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

A
Alex Dima 已提交
72 73 74
class ModelMarkerHandler {

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

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

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

		modelData.acceptMarkerDecorations(newModelDecorations);
E
Erich Gamma 已提交
87 88
	}

A
Alex Dima 已提交
89 90 91
	private static _createDecorationRange(model:EditorCommon.IModel, rawMarker: IMarker): EditorCommon.IRange {
		let marker = model.validateRange(new Range(rawMarker.startLineNumber, rawMarker.startColumn, rawMarker.endLineNumber, rawMarker.endColumn));
		let ret: EditorCommon.IEditorRange = new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn);
E
Erich Gamma 已提交
92
		if (ret.isEmpty()) {
A
Alex Dima 已提交
93
			let word = model.getWordAtPosition(ret.getStartPosition());
E
Erich Gamma 已提交
94 95 96 97
			if (word) {
				ret.startColumn = word.startColumn;
				ret.endColumn = word.endColumn;
			} else {
A
Alex Dima 已提交
98 99
				let maxColumn = model.getLineLastNonWhitespaceColumn(marker.startLineNumber) ||
					model.getLineMaxColumn(marker.startLineNumber);
E
Erich Gamma 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113

				if (maxColumn === 1) {
					// empty line
//					console.warn('marker on empty line:', marker);
				} 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 已提交
114
			let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber);
E
Erich Gamma 已提交
115 116 117 118 119 120 121 122
			if (minColumn < ret.endColumn) {
				ret.startColumn = minColumn;
				rawMarker.startColumn = minColumn;
			}
		}
		return ret;
	}

A
Alex Dima 已提交
123
	private static _createDecorationOption(marker:IMarker): EditorCommon.IModelDecorationOptions {
E
Erich Gamma 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

		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:
				className = EditorCommon.ClassName.EditorWarningDecoration;
				color = 'rgba(18,136,18,0.7)';
				darkColor = 'rgba(18,136,18,0.7)';
				break;
			case Severity.Error:
			default:
				className = EditorCommon.ClassName.EditorErrorDecoration;
				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];
		}

		return {
			stickiness: EditorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
			className,
			htmlMessage: htmlMessage,
			overviewRuler: {
				color,
				darkColor,
				position: EditorCommon.OverviewRulerLane.Right
			}
		};
	}
}

export class ModelServiceImpl implements IModelService {
	public serviceId = IModelService;

	private _markerService: IMarkerService;
	private _markerServiceSubscription: IDisposable;
	private _threadService: IThreadService;
	private _workerHelper: ModelServiceWorkerHelper;

	private _onModelAdded: EventSource<(model: EditorCommon.IModel) => void>;
	private _onModelRemoved: EventSource<(model: EditorCommon.IModel) => void>;
	private _onModelModeChanged: EventSource<(model: EditorCommon.IModel, oldModeId:string) => void>;
A
Alex Dima 已提交
180 181 182 183 184

	/**
	 * All the models known in the system.
	 */
	private _models: {[modelId:string]:ModelData;};
E
Erich Gamma 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

	constructor(threadService: IThreadService, markerService: IMarkerService) {
		this._threadService = threadService;
		this._markerService = markerService;
		this._workerHelper = this._threadService.getRemotable(ModelServiceWorkerHelper);

		this._models = {};

		this._onModelAdded = new EventSource<(model: EditorCommon.IModel) => void>();
		this._onModelRemoved = new EventSource<(model: EditorCommon.IModel) => void>();
		this._onModelModeChanged = new EventSource<(model: EditorCommon.IModel, oldModeId:string) => void>();

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

	public dispose(): void {
		if(this._markerServiceSubscription) {
			this._markerServiceSubscription.dispose();
		}
	}

	private _handleMarkerChange(changedResources: URI[]): void {
A
Alex Dima 已提交
209 210 211 212
		changedResources.forEach((resource) => {
			let modelId = MODEL_ID(resource);
			let modelData = this._models[modelId];
			if (!modelData) {
E
Erich Gamma 已提交
213 214
				return;
			}
A
Alex Dima 已提交
215
			ModelMarkerHandler.setMarkers(modelData, this._markerService.read({ resource: resource, take: 500 }));
E
Erich Gamma 已提交
216 217 218 219 220
		});
	}

	// --- begin IModelService

J
Johannes Rieken 已提交
221
	private _createModelData(value:string, modeOrPromise:TPromise<Modes.IMode>|Modes.IMode, resource: URI): ModelData {
A
Alex Dima 已提交
222
		// create & save the model
J
Johannes Rieken 已提交
223
		let model = new Model(value, modeOrPromise, resource);
A
Alex Dima 已提交
224
		let modelId = MODEL_ID(model.getAssociatedResource());
E
Erich Gamma 已提交
225 226 227

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

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

A
Alex Dima 已提交
234
		return modelData;
E
Erich Gamma 已提交
235 236
	}

J
Johannes Rieken 已提交
237
	public createModel(value:string, modeOrPromise:TPromise<Modes.IMode>|Modes.IMode, resource: URI): EditorCommon.IModel {
A
Alex Dima 已提交
238 239
		let modelData = this._createModelData(value, modeOrPromise, resource);
		let modelId = modelData.getModelId();
E
Erich Gamma 已提交
240

A
Alex Dima 已提交
241 242 243
		// handle markers (marker service => model)
		if (this._markerService) {
			ModelMarkerHandler.setMarkers(modelData, this._markerService.read({ resource: modelData.model.getAssociatedResource() }));
E
Erich Gamma 已提交
244 245
		}

A
Alex Dima 已提交
246 247 248 249
		if (!modelData.model.isTooLargeForHavingARichMode()) {
			// send this model to the workers
			modelData.isSyncedToWorkers = true;
			this._workerHelper.$_acceptNewModel(ModelServiceImpl._getBoundModelData(modelData.model));
E
Erich Gamma 已提交
250 251
		}

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

A
Alex Dima 已提交
254
		return modelData.model;
E
Erich Gamma 已提交
255 256
	}

J
Johannes Rieken 已提交
257
	public destroyModel(resource: URI): void {
A
Alex Dima 已提交
258 259 260 261
		// 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 已提交
262
		}
A
Alex Dima 已提交
263
		modelData.model.dispose();
E
Erich Gamma 已提交
264 265 266
	}

	public getModels(): EditorCommon.IModel[] {
A
Alex Dima 已提交
267 268
		let ret: EditorCommon.IModel[] = [];
		for (let modelId in this._models) {
E
Erich Gamma 已提交
269 270 271 272 273 274 275
			if (this._models.hasOwnProperty(modelId)) {
				ret.push(this._models[modelId].model);
			}
		}
		return ret;
	}

J
Johannes Rieken 已提交
276
	public getModel(resource: URI): EditorCommon.IModel {
A
Alex Dima 已提交
277 278 279 280
		let modelId = MODEL_ID(resource);
		let modelData = this._models[modelId];
		if (!modelData) {
			return null;
E
Erich Gamma 已提交
281
		}
A
Alex Dima 已提交
282
		return modelData.model;
E
Erich Gamma 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
	}

	public get onModelAdded(): EventProvider<(model:EditorCommon.IModel)=>void> {
		return this._onModelAdded ? this._onModelAdded.value : null;
	}

	public get onModelRemoved(): EventProvider<(model:EditorCommon.IModel)=>void> {
		return this._onModelRemoved ? this._onModelRemoved.value : null;
	}

	public get onModelModeChanged(): EventProvider<(model:EditorCommon.IModel, oldModeId:string)=>void> {
		return this._onModelModeChanged ? this._onModelModeChanged.value : null;
	}

	// --- end IModelService

A
Alex Dima 已提交
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	private _onModelDisposing(model:EditorCommon.IModel): void {
		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
			this._workerHelper.$_acceptDidDisposeModel(model.getAssociatedResource());
		}

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

		this._onModelRemoved.fire(model);
	}

E
Erich Gamma 已提交
323 324 325 326 327 328 329 330 331 332
	private static _getBoundModelData(model:EditorCommon.IModel): IRawModelData {
		return {
			url: model.getAssociatedResource(),
			versionId: model.getVersionId(),
			properties: model.getProperties(),
			value: model.toRawText(),
			modeId: model.getMode().getId()
		};
	}

A
Alex Dima 已提交
333 334
	private _onModelEvents(modelData:ModelData, events:IEmitterEvent[]): void {
		let eventsForWorkers: IMirrorModelEvents = { contentChanged: [], propertiesChanged: null };
E
Erich Gamma 已提交
335

A
Alex Dima 已提交
336 337 338
		for (let i = 0, len = events.length; i < len; i++) {
			let e = events[i];
			let data = e.getData();
E
Erich Gamma 已提交
339 340 341

			switch (e.getType()) {
				case EditorCommon.EventType.ModelDispose:
A
Alex Dima 已提交
342 343
					this._onModelDisposing(modelData.model);
					// no more event processing
E
Erich Gamma 已提交
344 345 346
					return;

				case EditorCommon.EventType.ModelContentChanged:
A
Alex Dima 已提交
347 348
					if (modelData.isSyncedToWorkers) {
						eventsForWorkers.contentChanged.push(<EditorCommon.IModelContentChangedEvent>data);
E
Erich Gamma 已提交
349 350 351 352
					}
					break;

				case EditorCommon.EventType.ModelPropertiesChanged:
A
Alex Dima 已提交
353 354 355
					if (modelData.isSyncedToWorkers) {
						eventsForWorkers.propertiesChanged = <EditorCommon.IModelPropertiesChangedEvent>data;
					}
E
Erich Gamma 已提交
356 357 358 359
					break;

				case EditorCommon.EventType.ModelModeChanged:
					let modeChangedEvent = <EditorCommon.IModelModeChangedEvent>data;
A
Alex Dima 已提交
360 361 362 363 364
					if (modelData.isSyncedToWorkers) {
						// Forward mode change to all the workers
						this._workerHelper.$_acceptDidChangeModelMode(modelData.getModelId(), modeChangedEvent.oldMode.getId(), modeChangedEvent.newMode.getId());
					}
					this._onModelModeChanged.fire(modelData.model, modeChangedEvent.oldMode.getId());
E
Erich Gamma 已提交
365 366 367 368
					break;
			}
		}

A
Alex Dima 已提交
369
		if (eventsForWorkers.contentChanged.length > 0 || eventsForWorkers.propertiesChanged) {
E
Erich Gamma 已提交
370
			// Forward events to all the workers
A
Alex Dima 已提交
371
			this._workerHelper.$_acceptModelEvents(modelData.getModelId(), eventsForWorkers);
E
Erich Gamma 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
		}
	}
}

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

	private _resourceService:IResourceService;
	private _modeService:IModeService;

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

A
Alex Dima 已提交
390
	public $_acceptNewModel(data:IRawModelData): TPromise<void> {
E
Erich Gamma 已提交
391
		// Create & insert the mirror model eagerly in the resource service
J
Johannes Rieken 已提交
392
		let mirrorModel = new MirrorModel(this._resourceService, data.versionId, data.value, null, data.url, data.properties);
E
Erich Gamma 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
		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);
		});
	}

A
Alex Dima 已提交
410 411
	public $_acceptDidChangeModelMode(modelId:string, oldModeId:string, newModeId:string): TPromise<void> {
		let mirrorModel = this._resourceService.get(URI.parse(modelId));
E
Erich Gamma 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427

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

J
Johannes Rieken 已提交
428
	public $_acceptDidDisposeModel(url:URI): void {
A
Alex Dima 已提交
429
		let model = <MirrorModel>this._resourceService.get(url);
E
Erich Gamma 已提交
430 431 432 433 434 435
		this._resourceService.remove(url);
		if (model) {
			model.dispose();
		}
	}

A
Alex Dima 已提交
436
	public $_acceptModelEvents(modelId: string, events:IMirrorModelEvents): void {
J
Johannes Rieken 已提交
437
		let model = <MirrorModel>this._resourceService.get(URI.parse(modelId));
A
Alex Dima 已提交
438 439 440 441 442 443 444
		if (!model) {
			throw new Error('Received model events for missing model ' + anonymize(modelId));
		}
		try {
			model.onEvents(events);
		} catch (err) {
			Errors.onUnexpectedError(err);
E
Erich Gamma 已提交
445 446 447
		}
	}
}