cursor.ts 34.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';
8
import * as strings from 'vs/base/common/strings';
J
Johannes Rieken 已提交
9
import { onUnexpectedError } from 'vs/base/common/errors';
A
Alex Dima 已提交
10 11
import { EventEmitter, BulkListenerCallback } from 'vs/base/common/eventEmitter';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
12
import { CursorCollection } from 'vs/editor/common/controller/cursorCollection';
J
Johannes Rieken 已提交
13 14
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
15
import { Selection, SelectionDirection, ISelection } from 'vs/editor/common/core/selection';
A
Alex Dima 已提交
16
import * as editorCommon from 'vs/editor/common/editorCommon';
A
Alex Dima 已提交
17
import { CursorColumns, CursorConfiguration, EditOperationResult, SingleCursorState, IViewModelHelper, CursorContext, CursorState, RevealTarget, IColumnSelectData, ICursors } from 'vs/editor/common/controller/cursorCommon';
J
Johannes Rieken 已提交
18
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
A
Alex Dima 已提交
19 20
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations';
21
import { TextModelEventType, ModelRawContentChangedEvent, RawContentChangedType } from 'vs/editor/common/model/textModelEvents';
22 23
import { CursorEventType, CursorChangeReason, ICursorPositionChangedEvent, VerticalRevealType, ICursorSelectionChangedEvent, ICursorRevealRangeEvent, CursorScrollRequest } from 'vs/editor/common/controller/cursorEvents';
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
A
Alex Dima 已提交
24
import { CoreEditorCommand } from 'vs/editor/common/controller/coreCommands';
E
Erich Gamma 已提交
25

26 27 28 29 30 31 32 33 34 35
class CursorOperationArgs<T> {
	public readonly eventSource: string;
	public readonly eventData: T;

	constructor(eventSource: string, eventData: T) {
		this.eventSource = eventSource;
		this.eventData = eventData;
	}
}

E
Erich Gamma 已提交
36
interface ICommandData {
A
Alex Dima 已提交
37
	operations: editorCommon.IIdentifiedSingleEditOperation[];
A
Alex Dima 已提交
38
	hadTrackedEditOperation: boolean;
E
Erich Gamma 已提交
39 40 41
}

interface ICommandsData {
A
Alex Dima 已提交
42
	operations: editorCommon.IIdentifiedSingleEditOperation[];
A
Alex Dima 已提交
43
	hadTrackedEditOperation: boolean;
E
Erich Gamma 已提交
44 45
}

A
Alex Dima 已提交
46
export class Cursor extends Disposable implements ICursors {
A
Alex Dima 已提交
47 48 49 50 51 52 53 54 55 56

	public onDidChangePosition(listener: (e: ICursorPositionChangedEvent) => void): IDisposable {
		return this._eventEmitter.addListener(CursorEventType.CursorPositionChanged, listener);
	}
	public onDidChangeSelection(listener: (e: ICursorSelectionChangedEvent) => void): IDisposable {
		return this._eventEmitter.addListener(CursorEventType.CursorSelectionChanged, listener);
	}
	public addBulkListener(listener: BulkListenerCallback): IDisposable {
		return this._eventEmitter.addBulkListener(listener);
	}
E
Erich Gamma 已提交
57

A
Alex Dima 已提交
58 59 60 61
	private readonly _eventEmitter: EventEmitter;
	private readonly _configuration: editorCommon.IConfiguration;
	private readonly _model: editorCommon.IModel;
	private readonly _viewModelHelper: IViewModelHelper;
E
Erich Gamma 已提交
62

A
Alex Dima 已提交
63 64 65
	public context: CursorContext;

	private _cursors: CursorCollection;
J
Johannes Rieken 已提交
66
	private _isHandling: boolean;
67
	private _isDoingComposition: boolean;
A
Alex Dima 已提交
68
	private _columnSelectData: IColumnSelectData;
E
Erich Gamma 已提交
69

J
Johannes Rieken 已提交
70
	private _handlers: {
71
		[key: string]: (args: CursorOperationArgs<any>) => EditOperationResult;
A
Alex Dima 已提交
72 73
	};

74
	constructor(configuration: editorCommon.IConfiguration, model: editorCommon.IModel, viewModelHelper: IViewModelHelper) {
A
Alex Dima 已提交
75 76
		super();
		this._eventEmitter = this._register(new EventEmitter());
A
Alex Dima 已提交
77 78 79
		this._configuration = configuration;
		this._model = model;
		this._viewModelHelper = viewModelHelper;
A
Alex Dima 已提交
80 81 82

		const createCursorContext = () => {
			const config = new CursorConfiguration(
A
Alex Dima 已提交
83 84 85 86
				this._model.getLanguageIdentifier(),
				this._model.getOneIndent(),
				this._model.getOptions(),
				this._configuration
A
Alex Dima 已提交
87 88
			);
			this.context = new CursorContext(
A
Alex Dima 已提交
89 90
				this._model,
				this._viewModelHelper,
A
Alex Dima 已提交
91 92
				config
			);
A
Alex Dima 已提交
93 94
			if (this._cursors) {
				this._cursors.updateContext(this.context);
A
Alex Dima 已提交
95 96 97 98
			}
		};
		createCursorContext();

A
Alex Dima 已提交
99
		this._cursors = new CursorCollection(this.context);
E
Erich Gamma 已提交
100 101

		this._isHandling = false;
102
		this._isDoingComposition = false;
A
Alex Dima 已提交
103
		this._columnSelectData = null;
E
Erich Gamma 已提交
104

A
Alex Dima 已提交
105
		this._register(this._model.addBulkListener((events) => {
106 107 108 109 110 111 112 113
			if (this._isHandling) {
				return;
			}

			let hadContentChange = false;
			let hadFlushEvent = false;
			for (let i = 0, len = events.length; i < len; i++) {
				const event = events[i];
A
Alex Dima 已提交
114
				const eventType = event.type;
115

A
Alex Dima 已提交
116
				if (eventType === TextModelEventType.ModelRawContentChanged2) {
117
					hadContentChange = true;
118
					const changeEvent = <ModelRawContentChangedEvent>event.data;
119

120 121
					for (let j = 0, lenJ = changeEvent.changes.length; j < lenJ; j++) {
						const change = changeEvent.changes[j];
122
						if (change.changeType === RawContentChangedType.Flush) {
123 124
							hadFlushEvent = true;
						}
125 126 127 128 129 130 131 132 133
					}
				}
			}

			if (!hadContentChange) {
				return;
			}

			this._onModelContentChanged(hadFlushEvent);
E
Erich Gamma 已提交
134
		}));
A
Alex Dima 已提交
135

A
Alex Dima 已提交
136
		this._register(this._model.onDidChangeLanguage((e) => {
A
Alex Dima 已提交
137
			createCursorContext();
E
Erich Gamma 已提交
138
		}));
A
Alex Dima 已提交
139
		this._register(LanguageConfigurationRegistry.onDidChange(() => {
140
			// TODO@Alex: react only if certain supports changed? (and if my model's mode changed)
A
Alex Dima 已提交
141 142
			createCursorContext();
		}));
A
Alex Dima 已提交
143
		this._register(model.onDidChangeOptions(() => {
A
Alex Dima 已提交
144 145
			createCursorContext();
		}));
A
Alex Dima 已提交
146
		this._register(this._configuration.onDidChange((e) => {
A
Alex Dima 已提交
147 148 149
			if (CursorConfiguration.shouldRecreate(e)) {
				createCursorContext();
			}
E
Erich Gamma 已提交
150 151
		}));

A
Alex Dima 已提交
152
		this._handlers = {};
E
Erich Gamma 已提交
153 154 155 156
		this._registerHandlers();
	}

	public dispose(): void {
A
Alex Dima 已提交
157 158
		this._cursors.dispose();
		this._cursors = null;
E
Erich Gamma 已提交
159 160 161
		super.dispose();
	}

A
Alex Dima 已提交
162
	public getPrimaryCursor(): CursorState {
A
Alex Dima 已提交
163
		return this._cursors.getPrimaryCursor();
A
Alex Dima 已提交
164 165 166
	}

	public getLastAddedCursorIndex(): number {
A
Alex Dima 已提交
167
		return this._cursors.getLastAddedCursorIndex();
A
Alex Dima 已提交
168 169 170
	}

	public getAll(): CursorState[] {
A
Alex Dima 已提交
171
		return this._cursors.getAll();
A
Alex Dima 已提交
172 173 174
	}

	public setStates(source: string, reason: CursorChangeReason, states: CursorState[]): void {
A
Alex Dima 已提交
175 176
		const oldSelections = this._cursors.getSelections();
		const oldViewSelections = this._cursors.getViewSelections();
A
Alex Dima 已提交
177 178 179 180 181

		// TODO@Alex
		// ensure valid state on all cursors
		// this.cursors.ensureValidState();

A
Alex Dima 已提交
182 183
		this._cursors.setStates(states);
		this._cursors.normalize();
A
Alex Dima 已提交
184 185
		this._columnSelectData = null;

A
Alex Dima 已提交
186 187
		const newSelections = this._cursors.getSelections();
		const newViewSelections = this._cursors.getViewSelections();
A
Alex Dima 已提交
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

		let somethingChanged = false;
		if (oldSelections.length !== newSelections.length) {
			somethingChanged = true;
		} else {
			for (let i = 0, len = oldSelections.length; !somethingChanged && i < len; i++) {
				if (!oldSelections[i].equalsSelection(newSelections[i])) {
					somethingChanged = true;
				}
			}
			for (let i = 0, len = oldViewSelections.length; !somethingChanged && i < len; i++) {
				if (!oldViewSelections[i].equalsSelection(newViewSelections[i])) {
					somethingChanged = true;
				}
			}
		}

		if (somethingChanged) {
			this.emitCursorPositionChanged(source, reason);
			this.emitCursorSelectionChanged(source, reason);
		}
	}

A
Alex Dima 已提交
211 212 213 214
	public setColumnSelectData(columnSelectData: IColumnSelectData): void {
		this._columnSelectData = columnSelectData;
	}

A
Alex Dima 已提交
215
	public reveal(horizontal: boolean, target: RevealTarget): void {
216 217 218 219 220
		this._revealRange(target, VerticalRevealType.Simple, horizontal);
	}

	public revealRange(revealHorizontal: boolean, modelRange: Range, viewRange: Range, verticalType: VerticalRevealType) {
		this.emitCursorRevealRange(modelRange, viewRange, verticalType, revealHorizontal);
A
Alex Dima 已提交
221 222
	}

A
Alex Dima 已提交
223 224 225 226 227 228
	public scrollTo(desiredScrollTop: number): void {
		this._eventEmitter.emit(CursorEventType.CursorScrollRequest, new CursorScrollRequest(
			desiredScrollTop
		));
	}

A
Alex Dima 已提交
229
	public saveState(): editorCommon.ICursorState[] {
E
Erich Gamma 已提交
230

A
Alex Dima 已提交
231
		let result: editorCommon.ICursorState[] = [];
E
Erich Gamma 已提交
232

A
Alex Dima 已提交
233 234 235
		const selections = this._cursors.getSelections();
		for (let i = 0, len = selections.length; i < len; i++) {
			const selection = selections[i];
E
Erich Gamma 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

			result.push({
				inSelectionMode: !selection.isEmpty(),
				selectionStart: {
					lineNumber: selection.selectionStartLineNumber,
					column: selection.selectionStartColumn,
				},
				position: {
					lineNumber: selection.positionLineNumber,
					column: selection.positionColumn,
				}
			});
		}

		return result;
	}

J
Johannes Rieken 已提交
253
	public restoreState(states: editorCommon.ICursorState[]): void {
E
Erich Gamma 已提交
254

A
Alex Dima 已提交
255
		let desiredSelections: ISelection[] = [];
E
Erich Gamma 已提交
256

A
Alex Dima 已提交
257 258
		for (let i = 0, len = states.length; i < len; i++) {
			const state = states[i];
E
Erich Gamma 已提交
259

A
Alex Dima 已提交
260 261
			let positionLineNumber = 1;
			let positionColumn = 1;
E
Erich Gamma 已提交
262 263 264 265 266 267 268 269 270

			// Avoid missing properties on the literal
			if (state.position && state.position.lineNumber) {
				positionLineNumber = state.position.lineNumber;
			}
			if (state.position && state.position.column) {
				positionColumn = state.position.column;
			}

A
Alex Dima 已提交
271 272
			let selectionStartLineNumber = positionLineNumber;
			let selectionStartColumn = positionColumn;
E
Erich Gamma 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

			// Avoid missing properties on the literal
			if (state.selectionStart && state.selectionStart.lineNumber) {
				selectionStartLineNumber = state.selectionStart.lineNumber;
			}
			if (state.selectionStart && state.selectionStart.column) {
				selectionStartColumn = state.selectionStart.column;
			}

			desiredSelections.push({
				selectionStartLineNumber: selectionStartLineNumber,
				selectionStartColumn: selectionStartColumn,
				positionLineNumber: positionLineNumber,
				positionColumn: positionColumn
			});
		}

A
Alex Dima 已提交
290 291
		this.setStates('restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections));
		this.reveal(true, RevealTarget.Primary);
E
Erich Gamma 已提交
292 293
	}

294 295
	private _onModelContentChanged(hadFlushEvent: boolean): void {
		if (hadFlushEvent) {
E
Erich Gamma 已提交
296
			// a model.setValue() was called
A
Alex Dima 已提交
297
			this._cursors.dispose();
E
Erich Gamma 已提交
298

A
Alex Dima 已提交
299
			this._cursors = new CursorCollection(this.context);
E
Erich Gamma 已提交
300

301 302
			this.emitCursorPositionChanged('model', CursorChangeReason.ContentFlush);
			this.emitCursorSelectionChanged('model', CursorChangeReason.ContentFlush);
E
Erich Gamma 已提交
303 304
		} else {
			if (!this._isHandling) {
A
Alex Dima 已提交
305
				const selectionsFromMarkers = this._cursors.readSelectionFromMarkers();
A
Alex Dima 已提交
306
				this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers));
E
Erich Gamma 已提交
307 308 309 310 311 312
			}
		}
	}

	// ------ some getters/setters

313
	public getSelection(): Selection {
A
Alex Dima 已提交
314
		return this._cursors.getPrimaryCursor().modelState.selection;
E
Erich Gamma 已提交
315 316
	}

317
	public getSelections(): Selection[] {
A
Alex Dima 已提交
318
		return this._cursors.getSelections();
E
Erich Gamma 已提交
319 320
	}

A
Alex Dima 已提交
321
	public getPosition(): Position {
A
Alex Dima 已提交
322
		return this._cursors.getPrimaryCursor().modelState.position;
E
Erich Gamma 已提交
323 324
	}

A
Alex Dima 已提交
325
	public setSelections(source: string, selections: ISelection[]): void {
A
Alex Dima 已提交
326
		this.setStates(source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
E
Erich Gamma 已提交
327 328 329 330
	}

	// ------ auxiliary handling logic

331
	private _createAndInterpretHandlerCtx(callback: () => EditOperationResult): void {
E
Erich Gamma 已提交
332

333
		const opResult = callback();
E
Erich Gamma 已提交
334

335
		if (opResult && opResult.shouldPushStackElementBefore) {
A
Alex Dima 已提交
336 337 338 339 340
			this._model.pushStackElement();
		}

		this._columnSelectData = null;

341 342 343 344 345
		if (opResult && !this._configuration.editor.readOnly) {
			const result = CommandExecutor.executeCommands(this._model, this._cursors.getSelections(), opResult.commands);
			if (result) {
				// The commands were applied correctly
				this._interpretCommandResult(result);
346
			}
A
Alex Dima 已提交
347 348
		}

349
		if (opResult && opResult.shouldPushStackElementAfter) {
A
Alex Dima 已提交
350 351 352
			this._model.pushStackElement();
		}

A
Alex Dima 已提交
353
		this._cursors.normalize();
E
Erich Gamma 已提交
354 355
	}

356
	private _onHandler(command: string, handler: (args: CursorOperationArgs<any>) => EditOperationResult, args: CursorOperationArgs<any>): void {
E
Erich Gamma 已提交
357 358 359 360

		this._isHandling = true;

		try {
A
Alex Dima 已提交
361 362
			const oldSelections = this._cursors.getSelections();
			const oldViewSelections = this._cursors.getViewSelections();
363

A
Alex Dima 已提交
364
			// ensure valid state on all cursors
A
Alex Dima 已提交
365
			this._cursors.ensureValidState();
A
Alex Dima 已提交
366

A
Alex Dima 已提交
367
			let cursorPositionChangeReason: CursorChangeReason;
E
Erich Gamma 已提交
368

369 370 371 372
			this._createAndInterpretHandlerCtx(() => {
				let r = handler(args);
				cursorPositionChangeReason = r ? r.reason : CursorChangeReason.NotSet;
				return r;
E
Erich Gamma 已提交
373 374
			});

A
Alex Dima 已提交
375 376
			const newSelections = this._cursors.getSelections();
			const newViewSelections = this._cursors.getViewSelections();
E
Erich Gamma 已提交
377

A
Alex Dima 已提交
378
			let somethingChanged = false;
E
Erich Gamma 已提交
379 380 381
			if (oldSelections.length !== newSelections.length) {
				somethingChanged = true;
			} else {
A
Alex Dima 已提交
382
				for (let i = 0, len = oldSelections.length; !somethingChanged && i < len; i++) {
E
Erich Gamma 已提交
383 384 385 386
					if (!oldSelections[i].equalsSelection(newSelections[i])) {
						somethingChanged = true;
					}
				}
A
Alex Dima 已提交
387
				for (let i = 0, len = oldViewSelections.length; !somethingChanged && i < len; i++) {
E
Erich Gamma 已提交
388 389 390 391 392 393 394
					if (!oldViewSelections[i].equalsSelection(newViewSelections[i])) {
						somethingChanged = true;
					}
				}
			}

			if (somethingChanged) {
395 396 397
				this.emitCursorPositionChanged(args.eventSource, cursorPositionChangeReason);
				this._revealRange(RevealTarget.Primary, VerticalRevealType.Simple, true);
				this.emitCursorSelectionChanged(args.eventSource, cursorPositionChangeReason);
E
Erich Gamma 已提交
398
			}
399

E
Erich Gamma 已提交
400
		} catch (err) {
A
Alex Dima 已提交
401
			onUnexpectedError(err);
E
Erich Gamma 已提交
402 403 404 405 406
		}

		this._isHandling = false;
	}

A
Alex Dima 已提交
407
	private _interpretCommandResult(cursorState: Selection[]): void {
408
		if (!cursorState || cursorState.length === 0) {
A
Alex Dima 已提交
409
			return;
E
Erich Gamma 已提交
410 411
		}

A
Alex Dima 已提交
412
		this._cursors.setSelections(cursorState);
E
Erich Gamma 已提交
413 414
	}

415 416
	// -----------------------------------------------------------------------------------------------------------
	// ----- emitting events
E
Erich Gamma 已提交
417

418 419 420 421
	private emitCursorPositionChanged(source: string, reason: CursorChangeReason): void {
		const positions = this._cursors.getPositions();
		const primaryPosition = positions[0];
		const secondaryPositions = positions.slice(1);
A
Alex Dima 已提交
422

423 424 425
		const viewPositions = this._cursors.getViewPositions();
		const primaryViewPosition = viewPositions[0];
		const secondaryViewPositions = viewPositions.slice(1);
E
Erich Gamma 已提交
426

427 428 429 430 431
		let isInEditableRange: boolean = true;
		if (this._model.hasEditableRange()) {
			const editableRange = this._model.getEditableRange();
			if (!editableRange.containsPosition(primaryPosition)) {
				isInEditableRange = false;
E
Erich Gamma 已提交
432 433
			}
		}
434 435 436 437 438 439 440 441
		const e: ICursorPositionChangedEvent = {
			position: primaryPosition,
			viewPosition: primaryViewPosition,
			secondaryPositions: secondaryPositions,
			secondaryViewPositions: secondaryViewPositions,
			reason: reason,
			source: source,
			isInEditableRange: isInEditableRange
E
Erich Gamma 已提交
442
		};
443
		this._eventEmitter.emit(CursorEventType.CursorPositionChanged, e);
E
Erich Gamma 已提交
444 445
	}

446 447 448 449
	private emitCursorSelectionChanged(source: string, reason: CursorChangeReason): void {
		const selections = this._cursors.getSelections();
		const primarySelection = selections[0];
		const secondarySelections = selections.slice(1);
E
Erich Gamma 已提交
450

451 452 453 454 455 456 457 458 459 460 461
		const viewSelections = this._cursors.getViewSelections();
		const primaryViewSelection = viewSelections[0];
		const secondaryViewSelections = viewSelections.slice(1);

		const e: ICursorSelectionChangedEvent = {
			selection: primarySelection,
			viewSelection: primaryViewSelection,
			secondarySelections: secondarySelections,
			secondaryViewSelections: secondaryViewSelections,
			source: source || 'keyboard',
			reason: reason
E
Erich Gamma 已提交
462
		};
463
		this._eventEmitter.emit(CursorEventType.CursorSelectionChanged, e);
E
Erich Gamma 已提交
464 465
	}

466 467 468
	private _revealRange(revealTarget: RevealTarget, verticalType: VerticalRevealType, revealHorizontal: boolean): void {
		const positions = this._cursors.getPositions();
		const viewPositions = this._cursors.getViewPositions();
E
Erich Gamma 已提交
469

470 471
		let position = positions[0];
		let viewPosition = viewPositions[0];
E
Erich Gamma 已提交
472

473 474 475 476 477
		if (revealTarget === RevealTarget.TopMost) {
			for (let i = 1; i < positions.length; i++) {
				if (positions[i].isBefore(position)) {
					position = positions[i];
					viewPosition = viewPositions[i];
E
Erich Gamma 已提交
478
				}
479 480 481 482 483 484
			}
		} else if (revealTarget === RevealTarget.BottomMost) {
			for (let i = 1; i < positions.length; i++) {
				if (position.isBeforeOrEqual(positions[i])) {
					position = positions[i];
					viewPosition = viewPositions[i];
E
Erich Gamma 已提交
485 486
				}
			}
487 488 489 490 491
		} else {
			if (positions.length > 1) {
				// no revealing!
				return;
			}
E
Erich Gamma 已提交
492 493
		}

494 495 496
		const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
		const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
		this.emitCursorRevealRange(range, viewRange, verticalType, revealHorizontal);
E
Erich Gamma 已提交
497 498
	}

499 500 501 502 503 504 505 506
	public emitCursorRevealRange(range: Range, viewRange: Range, verticalType: VerticalRevealType, revealHorizontal: boolean) {
		const e: ICursorRevealRangeEvent = {
			range: range,
			viewRange: viewRange,
			verticalType: verticalType,
			revealHorizontal: revealHorizontal
		};
		this._eventEmitter.emit(CursorEventType.CursorRevealRange, e);
E
Erich Gamma 已提交
507 508
	}

509 510
	// -----------------------------------------------------------------------------------------------------------
	// ----- handlers beyond this point
E
Erich Gamma 已提交
511

512 513 514 515 516 517
	public trigger(source: string, handlerId: string, payload: any): void {
		if (!this._handlers.hasOwnProperty(handlerId)) {
			const command = CommonEditorRegistry.getEditorCommand(handlerId);
			if (!command || !(command instanceof CoreEditorCommand)) {
				return;
			}
E
Erich Gamma 已提交
518

519 520 521
			payload = payload || {};
			payload.source = source;
			command.runCoreEditorCommand(this, payload);
A
Alex Dima 已提交
522
			return;
E
Erich Gamma 已提交
523
		}
524 525 526 527
		const handler = this._handlers[handlerId];
		const args = new CursorOperationArgs(source, payload);
		this._onHandler(handlerId, handler, args);
	}
E
Erich Gamma 已提交
528

529 530
	private _registerHandlers(): void {
		let H = editorCommon.Handler;
E
Erich Gamma 已提交
531

532 533 534
		this._handlers[H.LineInsertBefore] = (args) => this._lineInsertBefore(args);
		this._handlers[H.LineInsertAfter] = (args) => this._lineInsertAfter(args);
		this._handlers[H.LineBreakInsert] = (args) => this._lineBreakInsert(args);
E
Erich Gamma 已提交
535

536 537 538 539 540 541 542 543
		this._handlers[H.Type] = (args) => this._type(args);
		this._handlers[H.ReplacePreviousChar] = (args) => this._replacePreviousChar(args);
		this._handlers[H.CompositionStart] = (args) => this._compositionStart(args);
		this._handlers[H.CompositionEnd] = (args) => this._compositionEnd(args);
		this._handlers[H.Tab] = (args) => this._tab(args);
		this._handlers[H.Indent] = (args) => this._indent(args);
		this._handlers[H.Outdent] = (args) => this._outdent(args);
		this._handlers[H.Paste] = (args) => this._paste(args);
E
Erich Gamma 已提交
544

545 546
		this._handlers[H.DeleteLeft] = (args) => this._deleteLeft(args);
		this._handlers[H.DeleteRight] = (args) => this._deleteRight(args);
E
Erich Gamma 已提交
547

548
		this._handlers[H.Cut] = (args) => this._cut(args);
E
Erich Gamma 已提交
549

550 551
		this._handlers[H.Undo] = (args) => this._undo(args);
		this._handlers[H.Redo] = (args) => this._redo(args);
E
Erich Gamma 已提交
552

553 554
		this._handlers[H.ExecuteCommand] = (args) => this._externalExecuteCommand(args);
		this._handlers[H.ExecuteCommands] = (args) => this._externalExecuteCommands(args);
E
Erich Gamma 已提交
555 556
	}

A
Alex Dima 已提交
557
	public getColumnSelectData(): IColumnSelectData {
A
Alex Dima 已提交
558 559
		if (this._columnSelectData) {
			return this._columnSelectData;
A
Alex Dima 已提交
560
		}
A
Alex Dima 已提交
561
		const primaryCursor = this._cursors.getPrimaryCursor();
A
Alex Dima 已提交
562 563 564 565 566
		const primaryPos = primaryCursor.viewState.position;
		return {
			toViewLineNumber: primaryPos.lineNumber,
			toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, primaryPos)
		};
A
Alex Dima 已提交
567
	}
A
Alex Dima 已提交
568

A
Alex Dima 已提交
569 570
	// -------------------- START editing operations

571
	private _getAllCursorsModelState(sorted: boolean = false): SingleCursorState[] {
A
Alex Dima 已提交
572
		let cursors = this._cursors.getAll();
A
Alex Dima 已提交
573

574 575 576 577 578 579 580 581 582 583 584
		if (sorted) {
			cursors = cursors.sort((a, b) => {
				return Range.compareRangesUsingStarts(a.modelState.selection, b.modelState.selection);
			});
		}

		let r: SingleCursorState[] = [];
		for (let i = 0, len = cursors.length; i < len; i++) {
			r[i] = cursors[i].modelState;
		}
		return r;
A
Alex Dima 已提交
585 586
	}

587 588
	private _lineInsertBefore(args: CursorOperationArgs<void>): EditOperationResult {
		return TypeOperations.lineInsertBefore(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
589 590
	}

591 592
	private _lineInsertAfter(args: CursorOperationArgs<void>): EditOperationResult {
		return TypeOperations.lineInsertAfter(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
593 594
	}

595 596
	private _lineBreakInsert(args: CursorOperationArgs<void>): EditOperationResult {
		return TypeOperations.lineBreakInsert(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
597 598
	}

599
	private _type(args: CursorOperationArgs<{ text: string; }>): EditOperationResult {
600
		const text = args.eventData.text;
E
Erich Gamma 已提交
601

602
		if (!this._isDoingComposition && args.eventSource === 'keyboard') {
E
Erich Gamma 已提交
603 604
			// If this event is coming straight from the keyboard, look for electric characters and enter

605 606 607 608 609 610 611 612 613
			for (let i = 0, len = text.length; i < len; i++) {
				let charCode = text.charCodeAt(i);
				let chr: string;
				if (strings.isHighSurrogate(charCode) && i + 1 < len) {
					chr = text.charAt(i) + text.charAt(i + 1);
					i++;
				} else {
					chr = text.charAt(i);
				}
E
Erich Gamma 已提交
614 615

				// Here we must interpret each typed character individually, that's why we create a new context
616
				this._createAndInterpretHandlerCtx(() => {
E
Erich Gamma 已提交
617

618
					// Decide what all cursors will do up-front
619
					return TypeOperations.typeWithInterceptors(this.context.config, this.context.model, this._getAllCursorsModelState(), chr);
A
Alex Dima 已提交
620
				});
E
Erich Gamma 已提交
621 622

			}
623 624

			return null;
E
Erich Gamma 已提交
625
		} else {
626
			return TypeOperations.typeWithoutInterceptors(this.context.config, this.context.model, this._getAllCursorsModelState(), text);
E
Erich Gamma 已提交
627 628 629
		}
	}

630
	private _replacePreviousChar(args: CursorOperationArgs<{ text: string; replaceCharCnt: number; }>): EditOperationResult {
631 632
		let text = args.eventData.text;
		let replaceCharCnt = args.eventData.replaceCharCnt;
633
		return TypeOperations.replacePreviousChar(this.context.config, this.context.model, this._getAllCursorsModelState(), text, replaceCharCnt);
E
Erich Gamma 已提交
634 635
	}

636
	private _compositionStart(args: CursorOperationArgs<void>): EditOperationResult {
637
		this._isDoingComposition = true;
638
		return null;
639 640
	}

641
	private _compositionEnd(args: CursorOperationArgs<void>): EditOperationResult {
642
		this._isDoingComposition = false;
643
		return null;
644 645
	}

646 647
	private _tab(args: CursorOperationArgs<void>): EditOperationResult {
		return TypeOperations.tab(this.context.config, this.context.model, this._getAllCursorsModelState());
E
Erich Gamma 已提交
648 649
	}

650 651
	private _indent(args: CursorOperationArgs<void>): EditOperationResult {
		return TypeOperations.indent(this.context.config, this.context.model, this._getAllCursorsModelState());
E
Erich Gamma 已提交
652 653
	}

654 655
	private _outdent(args: CursorOperationArgs<void>): EditOperationResult {
		return TypeOperations.outdent(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
656 657
	}

658
	private _distributePasteToCursors(args: CursorOperationArgs<{ pasteOnNewLine: boolean; text: string; }>): string[] {
659
		if (args.eventData.pasteOnNewLine) {
A
Alex Dima 已提交
660 661 662
			return null;
		}

A
Alex Dima 已提交
663
		const selections = this._cursors.getSelections();
A
Alex Dima 已提交
664 665 666 667
		if (selections.length === 1) {
			return null;
		}

A
Alex Dima 已提交
668
		for (let i = 0; i < selections.length; i++) {
A
Alex Dima 已提交
669 670 671 672 673
			if (selections[i].startLineNumber !== selections[i].endLineNumber) {
				return null;
			}
		}

674
		let pastePieces = args.eventData.text.split(/\r\n|\r|\n/);
A
Alex Dima 已提交
675 676 677 678 679
		if (pastePieces.length !== selections.length) {
			return null;
		}

		return pastePieces;
E
Erich Gamma 已提交
680 681
	}

682 683
	private _paste(args: CursorOperationArgs<{ pasteOnNewLine: boolean; text: string; }>): EditOperationResult {
		const distributedPaste = this._distributePasteToCursors(args);
E
Erich Gamma 已提交
684 685

		if (distributedPaste) {
686
			return TypeOperations.distributedPaste(this.context.config, this.context.model, this._getAllCursorsModelState(true), distributedPaste);
E
Erich Gamma 已提交
687
		} else {
688
			return TypeOperations.paste(this.context.config, this.context.model, this._getAllCursorsModelState(), args.eventData.text, args.eventData.pasteOnNewLine);
E
Erich Gamma 已提交
689 690 691
		}
	}

692 693
	private _deleteLeft(args: CursorOperationArgs<void>): EditOperationResult {
		return DeleteOperations.deleteLeft(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
694 695
	}

696 697
	private _deleteRight(args: CursorOperationArgs<void>): EditOperationResult {
		return DeleteOperations.deleteRight(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
698 699
	}

700 701
	private _cut(args: CursorOperationArgs<void>): EditOperationResult {
		return DeleteOperations.cut(this.context.config, this.context.model, this._getAllCursorsModelState());
A
Alex Dima 已提交
702 703 704 705
	}

	// -------------------- END editing operations

706
	private _undo(args: CursorOperationArgs<void>): EditOperationResult {
A
Alex Dima 已提交
707
		this._interpretCommandResult(this._model.undo());
708 709 710 711 712 713

		return new EditOperationResult([], {
			shouldPushStackElementBefore: false,
			shouldPushStackElementAfter: false,
			reason: CursorChangeReason.Undo
		});
E
Erich Gamma 已提交
714 715
	}

716
	private _redo(args: CursorOperationArgs<void>): EditOperationResult {
A
Alex Dima 已提交
717
		this._interpretCommandResult(this._model.redo());
718 719 720 721 722 723

		return new EditOperationResult([], {
			shouldPushStackElementBefore: false,
			shouldPushStackElementAfter: false,
			reason: CursorChangeReason.Redo
		});
E
Erich Gamma 已提交
724 725
	}

726
	private _externalExecuteCommand(args: CursorOperationArgs<editorCommon.ICommand>): EditOperationResult {
727
		const command = args.eventData;
728

A
Alex Dima 已提交
729
		this._cursors.killSecondaryCursors();
730

A
Alex Dima 已提交
731 732 733 734
		return new EditOperationResult([command], {
			shouldPushStackElementBefore: true,
			shouldPushStackElementAfter: true
		});
E
Erich Gamma 已提交
735 736
	}

737
	private _externalExecuteCommands(args: CursorOperationArgs<editorCommon.ICommand[]>): EditOperationResult {
738
		const commands = args.eventData;
A
Alex Dima 已提交
739

A
Alex Dima 已提交
740
		return new EditOperationResult(commands, {
741 742 743
			shouldPushStackElementBefore: true,
			shouldPushStackElementAfter: true
		});
E
Erich Gamma 已提交
744 745
	}
}
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

interface IExecContext {
	readonly model: editorCommon.IModel;
	readonly selectionsBefore: Selection[];
	readonly selectionStartMarkers: string[];
	readonly positionMarkers: string[];
}

class CommandExecutor {

	public static executeCommands(model: editorCommon.IModel, selectionsBefore: Selection[], commands: editorCommon.ICommand[]): Selection[] {

		const ctx: IExecContext = {
			model: model,
			selectionsBefore: selectionsBefore,
			selectionStartMarkers: [],
			positionMarkers: []
		};

		const result = this._innerExecuteCommands(ctx, commands);

		for (let i = 0; i < ctx.selectionStartMarkers.length; i++) {
			ctx.model._removeMarker(ctx.selectionStartMarkers[i]);
			ctx.model._removeMarker(ctx.positionMarkers[i]);
		}

		return result;
	}

	private static _innerExecuteCommands(ctx: IExecContext, commands: editorCommon.ICommand[]): Selection[] {

		if (this._arrayIsEmpty(commands)) {
			return null;
		}

		const commandsData = this._getEditOperations(ctx, commands);
		if (commandsData.operations.length === 0) {
			return null;
		}

		const rawOperations = commandsData.operations;

		const editableRange = ctx.model.getEditableRange();
		const editableRangeStart = editableRange.getStartPosition();
		const editableRangeEnd = editableRange.getEndPosition();
		for (let i = 0, len = rawOperations.length; i < len; i++) {
			const operationRange = rawOperations[i].range;
			if (!editableRangeStart.isBeforeOrEqual(operationRange.getStartPosition()) || !operationRange.getEndPosition().isBeforeOrEqual(editableRangeEnd)) {
				// These commands are outside of the editable range
				return null;
			}
		}

		const loserCursorsMap = this._getLoserCursorMap(rawOperations);
		if (loserCursorsMap.hasOwnProperty('0')) {
			// These commands are very messed up
			console.warn('Ignoring commands');
			return null;
		}

		// Remove operations belonging to losing cursors
		let filteredOperations: editorCommon.IIdentifiedSingleEditOperation[] = [];
		for (let i = 0, len = rawOperations.length; i < len; i++) {
			if (!loserCursorsMap.hasOwnProperty(rawOperations[i].identifier.major.toString())) {
				filteredOperations.push(rawOperations[i]);
			}
		}

		// TODO@Alex: find a better way to do this.
		// give the hint that edit operations are tracked to the model
		if (commandsData.hadTrackedEditOperation && filteredOperations.length > 0) {
			filteredOperations[0]._isTracked = true;
		}
		const selectionsAfter = ctx.model.pushEditOperations(ctx.selectionsBefore, filteredOperations, (inverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[]): Selection[] => {
			let groupedInverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[][] = [];
			for (let i = 0; i < ctx.selectionsBefore.length; i++) {
				groupedInverseEditOperations[i] = [];
			}
			for (let i = 0; i < inverseEditOperations.length; i++) {
				const op = inverseEditOperations[i];
				if (!op.identifier) {
					// perhaps auto whitespace trim edits
					continue;
				}
				groupedInverseEditOperations[op.identifier.major].push(op);
			}
			const minorBasedSorter = (a: editorCommon.IIdentifiedSingleEditOperation, b: editorCommon.IIdentifiedSingleEditOperation) => {
				return a.identifier.minor - b.identifier.minor;
			};
			let cursorSelections: Selection[] = [];
			for (let i = 0; i < ctx.selectionsBefore.length; i++) {
				if (groupedInverseEditOperations[i].length > 0) {
					groupedInverseEditOperations[i].sort(minorBasedSorter);
					cursorSelections[i] = commands[i].computeCursorState(ctx.model, {
						getInverseEditOperations: () => {
							return groupedInverseEditOperations[i];
						},

						getTrackedSelection: (id: string) => {
							const idx = parseInt(id, 10);
							const selectionStartMarker = ctx.model._getMarker(ctx.selectionStartMarkers[idx]);
							const positionMarker = ctx.model._getMarker(ctx.positionMarkers[idx]);
							return new Selection(selectionStartMarker.lineNumber, selectionStartMarker.column, positionMarker.lineNumber, positionMarker.column);
						}
					});
				} else {
					cursorSelections[i] = ctx.selectionsBefore[i];
				}
			}
			return cursorSelections;
		});

		// Extract losing cursors
		let losingCursors: number[] = [];
		for (let losingCursorIndex in loserCursorsMap) {
			if (loserCursorsMap.hasOwnProperty(losingCursorIndex)) {
				losingCursors.push(parseInt(losingCursorIndex, 10));
			}
		}

		// Sort losing cursors descending
		losingCursors.sort((a: number, b: number): number => {
			return b - a;
		});

		// Remove losing cursors
		for (let i = 0; i < losingCursors.length; i++) {
			selectionsAfter.splice(losingCursors[i], 1);
		}

		return selectionsAfter;
	}

	private static _arrayIsEmpty(commands: editorCommon.ICommand[]): boolean {
		for (let i = 0, len = commands.length; i < len; i++) {
			if (commands[i]) {
				return false;
			}
		}
		return true;
	}

	private static _getEditOperations(ctx: IExecContext, commands: editorCommon.ICommand[]): ICommandsData {
		let operations: editorCommon.IIdentifiedSingleEditOperation[] = [];
		let hadTrackedEditOperation: boolean = false;

		for (let i = 0, len = commands.length; i < len; i++) {
			if (commands[i]) {
				const r = this._getEditOperationsFromCommand(ctx, i, commands[i]);
				operations = operations.concat(r.operations);
				hadTrackedEditOperation = hadTrackedEditOperation || r.hadTrackedEditOperation;
			}
		}
		return {
			operations: operations,
			hadTrackedEditOperation: hadTrackedEditOperation
		};
	}

	private static _getEditOperationsFromCommand(ctx: IExecContext, majorIdentifier: number, command: editorCommon.ICommand): ICommandData {
		// This method acts as a transaction, if the command fails
		// everything it has done is ignored
		let operations: editorCommon.IIdentifiedSingleEditOperation[] = [];
		let operationMinor = 0;

		const addEditOperation = (selection: Range, text: string) => {
			if (selection.isEmpty() && text === '') {
				// This command wants to add a no-op => no thank you
				return;
			}
			operations.push({
				identifier: {
					major: majorIdentifier,
					minor: operationMinor++
				},
				range: selection,
				text: text,
				forceMoveMarkers: false,
				isAutoWhitespaceEdit: command.insertsAutoWhitespace
			});
		};

		let hadTrackedEditOperation = false;
		const addTrackedEditOperation = (selection: Range, text: string) => {
			hadTrackedEditOperation = true;
			addEditOperation(selection, text);
		};

		const trackSelection = (selection: Selection, trackPreviousOnEmpty?: boolean) => {
			let selectionMarkerStickToPreviousCharacter: boolean;
			let positionMarkerStickToPreviousCharacter: boolean;

			if (selection.isEmpty()) {
				// Try to lock it with surrounding text
				if (typeof trackPreviousOnEmpty === 'boolean') {
					selectionMarkerStickToPreviousCharacter = trackPreviousOnEmpty;
					positionMarkerStickToPreviousCharacter = trackPreviousOnEmpty;
				} else {
					const maxLineColumn = ctx.model.getLineMaxColumn(selection.startLineNumber);
					if (selection.startColumn === maxLineColumn) {
						selectionMarkerStickToPreviousCharacter = true;
						positionMarkerStickToPreviousCharacter = true;
					} else {
						selectionMarkerStickToPreviousCharacter = false;
						positionMarkerStickToPreviousCharacter = false;
					}
				}
			} else {
				if (selection.getDirection() === SelectionDirection.LTR) {
					selectionMarkerStickToPreviousCharacter = false;
					positionMarkerStickToPreviousCharacter = true;
				} else {
					selectionMarkerStickToPreviousCharacter = true;
					positionMarkerStickToPreviousCharacter = false;
				}
			}

			const l = ctx.selectionStartMarkers.length;
			ctx.selectionStartMarkers[l] = ctx.model._addMarker(0, selection.selectionStartLineNumber, selection.selectionStartColumn, selectionMarkerStickToPreviousCharacter);
			ctx.positionMarkers[l] = ctx.model._addMarker(0, selection.positionLineNumber, selection.positionColumn, positionMarkerStickToPreviousCharacter);
			return l.toString();
		};

		const editOperationBuilder: editorCommon.IEditOperationBuilder = {
			addEditOperation: addEditOperation,
			addTrackedEditOperation: addTrackedEditOperation,
			trackSelection: trackSelection
		};

		try {
			command.getEditOperations(ctx.model, editOperationBuilder);
		} catch (e) {
			e.friendlyMessage = nls.localize('corrupt.commands', "Unexpected exception while executing command.");
			onUnexpectedError(e);
			return {
				operations: [],
				hadTrackedEditOperation: false
			};
		}

		return {
			operations: operations,
			hadTrackedEditOperation: hadTrackedEditOperation
		};
	}

	private static _getLoserCursorMap(operations: editorCommon.IIdentifiedSingleEditOperation[]): { [index: string]: boolean; } {
		// This is destructive on the array
		operations = operations.slice(0);

		// Sort operations with last one first
		operations.sort((a: editorCommon.IIdentifiedSingleEditOperation, b: editorCommon.IIdentifiedSingleEditOperation): number => {
			// Note the minus!
			return -(Range.compareRangesUsingEnds(a.range, b.range));
		});

		// Operations can not overlap!
		let loserCursorsMap: { [index: string]: boolean; } = {};

		for (let i = 1; i < operations.length; i++) {
			const previousOp = operations[i - 1];
			const currentOp = operations[i];

			if (previousOp.range.getStartPosition().isBefore(currentOp.range.getEndPosition())) {

				let loserMajor: number;

				if (previousOp.identifier.major > currentOp.identifier.major) {
					// previousOp loses the battle
					loserMajor = previousOp.identifier.major;
				} else {
					loserMajor = currentOp.identifier.major;
				}

				loserCursorsMap[loserMajor.toString()] = true;

				for (let j = 0; j < operations.length; j++) {
					if (operations[j].identifier.major === loserMajor) {
						operations.splice(j, 1);
						if (j < i) {
							i--;
						}
						j--;
					}
				}

				if (i > 0) {
					i--;
				}
			}
		}

		return loserCursorsMap;
	}
}