formatCommand.ts 5.3 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 strings from 'vs/base/common/strings';
J
Johannes Rieken 已提交
8
import { Range } from 'vs/editor/common/core/range';
9
import { TextEdit } from 'vs/editor/common/modes';
A
Alex Dima 已提交
10
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
11
import { Selection } from 'vs/editor/common/core/selection';
E
Erich Gamma 已提交
12

A
Alex Dima 已提交
13
export class EditOperationsCommand implements editorCommon.ICommand {
E
Erich Gamma 已提交
14

15 16 17 18 19
	static execute(editor: editorCommon.ICommonCodeEditor, edits: TextEdit[]) {
		const cmd = new EditOperationsCommand(edits, editor.getSelection());
		if (typeof cmd._newEol === 'number') {
			editor.getModel().setEOL(cmd._newEol);
		}
J
Johannes Rieken 已提交
20
		editor.executeCommand('formatEditsCommand', cmd);
21 22 23 24 25
	}

	private _edits: TextEdit[];
	private _newEol: editorCommon.EndOfLineSequence;

26
	private _initialSelection: Selection;
E
Erich Gamma 已提交
27 28
	private _selectionId: string;

29
	constructor(edits: TextEdit[], initialSelection: Selection) {
E
Erich Gamma 已提交
30
		this._initialSelection = initialSelection;
J
Johannes Rieken 已提交
31 32 33 34 35 36 37 38 39 40 41
		this._edits = [];
		this._newEol = undefined;

		for (let edit of edits) {
			if (typeof edit.eol === 'number') {
				this._newEol = edit.eol;
			}
			if (edit.range && edit.text) {
				this._edits.push(edit);
			}
		}
E
Erich Gamma 已提交
42 43
	}

A
Alex Dima 已提交
44
	public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void {
E
Erich Gamma 已提交
45

46 47 48 49 50 51 52 53
		for (let edit of this._edits) {
			// We know that this edit.range comes from the mirror model, so it should only contain \n and no \r's
			let trimEdit = EditOperationsCommand.trimEdit(edit, model);
			if (trimEdit !== null) { // produced above in case the edit.text is identical to the existing text
				builder.addEditOperation(Range.lift(edit.range), edit.text);
			}
		}

E
Erich Gamma 已提交
54 55 56
		var selectionIsSet = false;
		if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
			if (this._edits[0].range.startColumn === this._initialSelection.endColumn &&
J
Johannes Rieken 已提交
57
				this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
E
Erich Gamma 已提交
58 59 60
				selectionIsSet = true;
				this._selectionId = builder.trackSelection(this._initialSelection, true);
			} else if (this._edits[0].range.endColumn === this._initialSelection.startColumn &&
J
Johannes Rieken 已提交
61
				this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
E
Erich Gamma 已提交
62 63 64 65 66 67 68 69 70 71
				selectionIsSet = true;
				this._selectionId = builder.trackSelection(this._initialSelection, false);
			}
		}

		if (!selectionIsSet) {
			this._selectionId = builder.trackSelection(this._initialSelection);
		}
	}

72
	public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection {
E
Erich Gamma 已提交
73 74 75
		return helper.getTrackedSelection(this._selectionId);
	}

A
Alex Dima 已提交
76
	static fixLineTerminators(edit: editorCommon.ISingleEditOperation, model: editorCommon.ITokenizedModel): void {
E
Erich Gamma 已提交
77 78 79 80 81 82 83 84 85 86 87
		edit.text = edit.text.replace(/\r\n|\r|\n/g, model.getEOL());
	}

	/**
	 * This is used to minimize the edits by removing changes that appear on the edges of the range which are identical
	 * to the current text.
	 *
	 * The reason this was introduced is to allow better selection tracking of the current cursor and solve
	 * bug #15108. There the cursor was jumping since the tracked selection was in the middle of the range edit
	 * and was lost.
	 */
J
Johannes Rieken 已提交
88
	static trimEdit(edit: editorCommon.ISingleEditOperation, model: editorCommon.ITokenizedModel): editorCommon.ISingleEditOperation {
E
Erich Gamma 已提交
89

A
Alex Dima 已提交
90 91 92 93 94
		this.fixLineTerminators(edit, model);

		return this._trimEdit(model.validateRange(edit.range), edit.text, edit.forceMoveMarkers, model);
	}

J
Johannes Rieken 已提交
95
	static _trimEdit(editRange: Range, editText: string, editForceMoveMarkers: boolean, model: editorCommon.ITokenizedModel): editorCommon.ISingleEditOperation {
A
Alex Dima 已提交
96 97

		let currentText = model.getValueInRange(editRange);
E
Erich Gamma 已提交
98 99

		// Find the equal characters in the front
A
Alex Dima 已提交
100
		let commonPrefixLength = strings.commonPrefixLength(editText, currentText);
E
Erich Gamma 已提交
101

A
Alex Dima 已提交
102 103
		// If the two strings are identical, return no edit (no-op)
		if (commonPrefixLength === currentText.length && commonPrefixLength === editText.length) {
E
Erich Gamma 已提交
104 105 106
			return null;
		}

A
Alex Dima 已提交
107 108 109 110 111 112
		if (commonPrefixLength > 0) {
			// Apply front trimming
			let newStartPosition = model.modifyPosition(editRange.getStartPosition(), commonPrefixLength);
			editRange = new Range(newStartPosition.lineNumber, newStartPosition.column, editRange.endLineNumber, editRange.endColumn);
			editText = editText.substring(commonPrefixLength);
			currentText = currentText.substr(commonPrefixLength);
E
Erich Gamma 已提交
113 114
		}

A
Alex Dima 已提交
115
		// Find the equal characters in the rear
A
Alex Dima 已提交
116
		let commonSuffixLength = strings.commonSuffixLength(editText, currentText);
E
Erich Gamma 已提交
117

A
Alex Dima 已提交
118 119 120 121 122 123 124
		if (commonSuffixLength > 0) {
			// Apply rear trimming
			let newEndPosition = model.modifyPosition(editRange.getEndPosition(), -commonSuffixLength);
			editRange = new Range(editRange.startLineNumber, editRange.startColumn, newEndPosition.lineNumber, newEndPosition.column);
			editText = editText.substring(0, editText.length - commonSuffixLength);
			currentText = currentText.substring(0, currentText.length - commonSuffixLength);
		}
E
Erich Gamma 已提交
125 126

		return {
A
Alex Dima 已提交
127 128 129 130
			text: editText,
			range: editRange,
			forceMoveMarkers: editForceMoveMarkers
		};
E
Erich Gamma 已提交
131 132
	}
}