editorState.ts 5.0 KB
Newer Older
E
Erich Gamma 已提交
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.
 *--------------------------------------------------------------------------------------------*/

A
Alex Dima 已提交
6
import * as strings from 'vs/base/common/strings';
J
Johannes Rieken 已提交
7
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
J
Johannes Rieken 已提交
8 9
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
10
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
J
Johannes Rieken 已提交
11
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
12
import { ITextModel } from 'vs/editor/common/model';
E
Erich Gamma 已提交
13

A
Alex Dima 已提交
14 15 16 17 18 19 20 21
export const enum CodeEditorStateFlag {
	Value = 1,
	Selection = 2,
	Position = 4,
	Scroll = 8
}

export class EditorState {
E
Erich Gamma 已提交
22

A
Alex Dima 已提交
23
	private readonly flags: number;
E
Erich Gamma 已提交
24

A
Alex Dima 已提交
25 26
	private readonly position: Position | null;
	private readonly selection: Range | null;
A
Alex Dima 已提交
27
	private readonly modelVersionId: string | null;
A
Alex Dima 已提交
28 29
	private readonly scrollLeft: number;
	private readonly scrollTop: number;
E
Erich Gamma 已提交
30

31
	constructor(editor: ICodeEditor, flags: number) {
E
Erich Gamma 已提交
32 33
		this.flags = flags;

A
Alex Dima 已提交
34
		if ((this.flags & CodeEditorStateFlag.Value) !== 0) {
M
Matt Bierner 已提交
35
			const model = editor.getModel();
A
Alex Dima 已提交
36 37 38 39 40 41 42 43 44 45 46 47
			this.modelVersionId = model ? strings.format('{0}#{1}', model.uri.toString(), model.getVersionId()) : null;
		}
		if ((this.flags & CodeEditorStateFlag.Position) !== 0) {
			this.position = editor.getPosition();
		}
		if ((this.flags & CodeEditorStateFlag.Selection) !== 0) {
			this.selection = editor.getSelection();
		}
		if ((this.flags & CodeEditorStateFlag.Scroll) !== 0) {
			this.scrollLeft = editor.getScrollLeft();
			this.scrollTop = editor.getScrollTop();
		}
E
Erich Gamma 已提交
48 49
	}

J
Johannes Rieken 已提交
50
	private _equals(other: any): boolean {
E
Erich Gamma 已提交
51

J
Johannes Rieken 已提交
52
		if (!(other instanceof EditorState)) {
E
Erich Gamma 已提交
53 54
			return false;
		}
M
Matt Bierner 已提交
55
		const state = <EditorState>other;
E
Erich Gamma 已提交
56

J
Johannes Rieken 已提交
57
		if (this.modelVersionId !== state.modelVersionId) {
E
Erich Gamma 已提交
58 59
			return false;
		}
J
Johannes Rieken 已提交
60
		if (this.scrollLeft !== state.scrollLeft || this.scrollTop !== state.scrollTop) {
E
Erich Gamma 已提交
61 62
			return false;
		}
J
Johannes Rieken 已提交
63
		if (!this.position && state.position || this.position && !state.position || this.position && state.position && !this.position.equals(state.position)) {
E
Erich Gamma 已提交
64 65
			return false;
		}
J
Johannes Rieken 已提交
66
		if (!this.selection && state.selection || this.selection && !state.selection || this.selection && state.selection && !this.selection.equalsRange(state.selection)) {
E
Erich Gamma 已提交
67 68 69 70 71
			return false;
		}
		return true;
	}

72
	public validate(editor: ICodeEditor): boolean {
E
Erich Gamma 已提交
73 74 75
		return this._equals(new EditorState(editor, this.flags));
	}
}
A
Alex Dima 已提交
76

77 78 79 80
/**
 * A cancellation token source that cancels when the editor changes as expressed
 * by the provided flags
 */
J
Johannes Rieken 已提交
81 82 83 84
export class EditorStateCancellationTokenSource extends CancellationTokenSource {

	private readonly _listener: IDisposable[] = [];

85 86
	constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, parent?: CancellationToken) {
		super(parent);
J
Johannes Rieken 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

		if (flags & CodeEditorStateFlag.Position) {
			this._listener.push(editor.onDidChangeCursorPosition(_ => this.cancel()));
		}
		if (flags & CodeEditorStateFlag.Selection) {
			this._listener.push(editor.onDidChangeCursorSelection(_ => this.cancel()));
		}
		if (flags & CodeEditorStateFlag.Scroll) {
			this._listener.push(editor.onDidScrollChange(_ => this.cancel()));
		}
		if (flags & CodeEditorStateFlag.Value) {
			this._listener.push(editor.onDidChangeModel(_ => this.cancel()));
			this._listener.push(editor.onDidChangeModelContent(_ => this.cancel()));
		}
	}

	dispose() {
		dispose(this._listener);
		super.dispose();
	}
}

109 110 111
/**
 * A cancellation token source that cancels when the provided model changes
 */
112 113 114 115
export class TextModelCancellationTokenSource extends CancellationTokenSource {

	private _listener: IDisposable;

116 117
	constructor(model: ITextModel, parent?: CancellationToken) {
		super(parent);
118 119 120 121 122 123 124 125 126
		this._listener = model.onDidChangeContent(() => this.cancel());
	}

	dispose() {
		this._listener.dispose();
		super.dispose();
	}
}

A
Alex Dima 已提交
127 128 129
export class StableEditorScrollState {

	public static capture(editor: ICodeEditor): StableEditorScrollState {
130
		let visiblePosition: Position | null = null;
A
Alex Dima 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143
		let visiblePositionScrollDelta = 0;
		if (editor.getScrollTop() !== 0) {
			const visibleRanges = editor.getVisibleRanges();
			if (visibleRanges.length > 0) {
				visiblePosition = visibleRanges[0].getStartPosition();
				const visiblePositionScrollTop = editor.getTopForPosition(visiblePosition.lineNumber, visiblePosition.column);
				visiblePositionScrollDelta = editor.getScrollTop() - visiblePositionScrollTop;
			}
		}
		return new StableEditorScrollState(visiblePosition, visiblePositionScrollDelta);
	}

	constructor(
A
Alex Dima 已提交
144
		private readonly _visiblePosition: Position | null,
A
Alex Dima 已提交
145 146 147 148 149 150 151 152 153 154 155
		private readonly _visiblePositionScrollDelta: number
	) {
	}

	public restore(editor: ICodeEditor): void {
		if (this._visiblePosition) {
			const visiblePositionScrollTop = editor.getTopForPosition(this._visiblePosition.lineNumber, this._visiblePosition.column);
			editor.setScrollTop(visiblePositionScrollTop + this._visiblePositionScrollDelta);
		}
	}
}