cursorUndo.ts 3.9 KB
Newer Older
A
Alex Dima 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import * as nls from 'vs/nls';
A
Alex Dima 已提交
7
import { Selection } from 'vs/editor/common/core/selection';
8
import { ServicesAccessor, registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
A
Alex Dima 已提交
9 10
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
11
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
A
Alex Dima 已提交
12 13
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
14
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
A
Alex Dima 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

class CursorState {
	readonly selections: Selection[];

	constructor(selections: Selection[]) {
		this.selections = selections;
	}

	public equals(other: CursorState): boolean {
		const thisLen = this.selections.length;
		const otherLen = other.selections.length;
		if (thisLen !== otherLen) {
			return false;
		}
		for (let i = 0; i < thisLen; i++) {
			if (!this.selections[i].equalsSelection(other.selections[i])) {
				return false;
			}
		}
		return true;
	}
}

export class CursorUndoController extends Disposable implements IEditorContribution {

40
	private static readonly ID = 'editor.contrib.cursorUndoController';
A
Alex Dima 已提交
41

42
	public static get(editor: ICodeEditor): CursorUndoController {
A
Alex Dima 已提交
43 44 45 46 47 48 49
		return editor.getContribution<CursorUndoController>(CursorUndoController.ID);
	}

	private readonly _editor: ICodeEditor;
	private _isCursorUndo: boolean;

	private _undoStack: CursorState[];
A
Alex Dima 已提交
50
	private _prevState: CursorState | null;
A
Alex Dima 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

	constructor(editor: ICodeEditor) {
		super();
		this._editor = editor;
		this._isCursorUndo = false;

		this._undoStack = [];
		this._prevState = this._readState();

		this._register(editor.onDidChangeModel((e) => {
			this._undoStack = [];
			this._prevState = null;
		}));
		this._register(editor.onDidChangeModelContent((e) => {
			this._undoStack = [];
			this._prevState = null;
		}));
		this._register(editor.onDidChangeCursorSelection((e) => {

			if (!this._isCursorUndo && this._prevState) {
				this._undoStack.push(this._prevState);
				if (this._undoStack.length > 50) {
					// keep the cursor undo stack bounded
74
					this._undoStack.shift();
A
Alex Dima 已提交
75 76 77 78 79 80 81
				}
			}

			this._prevState = this._readState();
		}));
	}

A
Alex Dima 已提交
82 83
	private _readState(): CursorState | null {
		if (!this._editor.hasModel()) {
A
Alex Dima 已提交
84 85 86 87 88 89 90 91 92 93 94 95
			// no model => no state
			return null;
		}

		return new CursorState(this._editor.getSelections());
	}

	public getId(): string {
		return CursorUndoController.ID;
	}

	public cursorUndo(): void {
A
Alex Dima 已提交
96 97 98 99
		if (!this._editor.hasModel()) {
			return;
		}

A
Alex Dima 已提交
100 101 102
		const currState = new CursorState(this._editor.getSelections());

		while (this._undoStack.length > 0) {
A
Alex Dima 已提交
103
			const prevState = this._undoStack.pop()!;
A
Alex Dima 已提交
104 105 106 107

			if (!prevState.equals(currState)) {
				this._isCursorUndo = true;
				this._editor.setSelections(prevState.selections);
108
				this._editor.revealRangeInCenterIfOutsideViewport(prevState.selections[0], ScrollType.Smooth);
A
Alex Dima 已提交
109 110 111 112 113 114 115
				this._isCursorUndo = false;
				return;
			}
		}
	}
}

116
export class CursorUndo extends EditorAction {
A
Alex Dima 已提交
117 118 119
	constructor() {
		super({
			id: 'cursorUndo',
120 121
			label: nls.localize('cursor.undo', "Soft Undo"),
			alias: 'Soft Undo',
A
Alex Dima 已提交
122 123
			precondition: null,
			kbOpts: {
124
				kbExpr: EditorContextKeys.textInputFocus,
A
Alex Dima 已提交
125
				primary: KeyMod.CtrlCmd | KeyCode.KEY_U,
126
				weight: KeybindingWeight.EditorContrib
A
Alex Dima 已提交
127 128 129 130
			}
		});
	}

131
	public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
A
Alex Dima 已提交
132 133 134
		CursorUndoController.get(editor).cursorUndo();
	}
}
135

136
registerEditorContribution(CursorUndoController);
137
registerEditorAction(CursorUndo);