textAreaState.ts 7.1 KB
Newer Older
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';

7
import * as EditorCommon from 'vs/editor/common/editorCommon';
8
import {Range} from 'vs/editor/common/core/range';
9
import Event from 'vs/base/common/event';
10

11 12 13 14 15 16 17
export interface IClipboardEvent {
	canUseTextData(): boolean;
	setTextData(text:string): void;
	getTextData(): string;
}

export interface IKeyboardEventWrapper {
18 19 20 21
	_actual: any;
	equals(keybinding:number): boolean;
	preventDefault(): void;
	isDefaultPrevented(): boolean;
22 23
}

24
export interface ITextAreaWrapper {
25 26 27
	onKeyDown: Event<IKeyboardEventWrapper>;
	onKeyUp: Event<IKeyboardEventWrapper>;
	onKeyPress: Event<IKeyboardEventWrapper>;
28 29 30
	onCompositionStart: Event<void>;
	onCompositionEnd: Event<void>;
	onInput: Event<void>;
31 32 33
	onCut: Event<IClipboardEvent>;
	onCopy: Event<IClipboardEvent>;
	onPaste: Event<IClipboardEvent>;
34 35 36 37 38 39

	value: string;
	selectionStart: number;
	selectionEnd: number;

	setSelectionRange(selectionStart:number, selectionEnd:number): void;
40
	isInOverwriteMode(): boolean;
41 42 43 44 45 46 47 48 49 50 51 52 53 54
}

export interface ISimpleModel {
	getLineMaxColumn(lineNumber:number): number;
	getValueInRange(range:EditorCommon.IRange, eol:EditorCommon.EndOfLinePreference): string;
	getModelLineContent(lineNumber:number): string;
	getLineCount(): number;
	convertViewPositionToModelPosition(viewLineNumber:number, viewColumn:number): EditorCommon.IEditorPosition;
}

export class TextAreaState {
	private value:string;
	private selectionStart:number;
	private selectionEnd:number;
55
	private isInOverwriteMode:boolean;
56 57
	private selectionToken:number;

58
	constructor(value:string, selectionStart:number, selectionEnd:number, isInOverwriteMode:boolean, selectionToken:number) {
59 60 61
		this.value = value;
		this.selectionStart = selectionStart;
		this.selectionEnd = selectionEnd;
62
		this.isInOverwriteMode = isInOverwriteMode;
63 64 65 66
		this.selectionToken = selectionToken;
	}

	public toString(): string {
A
Alex Dima 已提交
67 68 69 70 71 72 73 74 75 76 77
		return '[ <' + this.value + '>, selectionStart: ' + this.selectionStart + ', selectionEnd: ' + this.selectionEnd + ', isInOverwriteMode: ' + this.isInOverwriteMode + ', selectionToken: ' + this.selectionToken + ']';
	}

	public equals(other:TextAreaState): boolean {
		return (
			this.value === other.value
			&& this.selectionStart === other.selectionStart
			&& this.selectionEnd === other.selectionEnd
			&& this.isInOverwriteMode === other.isInOverwriteMode
			&& this.selectionToken === other.selectionToken
		);
78 79 80
	}

	public static fromTextArea(textArea:ITextAreaWrapper, selectionToken:number): TextAreaState {
81
		return new TextAreaState(textArea.value, textArea.selectionStart, textArea.selectionEnd, textArea.isInOverwriteMode(), selectionToken);
82 83 84
	}

	public static fromEditorSelectionAndPreviousState(model:ISimpleModel, selection:EditorCommon.IEditorRange, previousSelectionToken:number): TextAreaState {
85 86
		let LIMIT_CHARS = 100;
		let PADDING_LINES_COUNT = 0;
87

88
		let selectionStartLineNumber = selection.startLineNumber,
89 90 91 92 93 94 95 96 97 98 99 100
			selectionStartColumn = selection.startColumn,
			selectionEndLineNumber = selection.endLineNumber,
			selectionEndColumn = selection.endColumn,
			selectionEndLineNumberMaxColumn = model.getLineMaxColumn(selectionEndLineNumber);

		// If the selection is empty and we have switched line numbers, expand selection to full line (helps Narrator trigger a full line read)
		if (selection.isEmpty() && previousSelectionToken !== selectionStartLineNumber) {
			selectionStartColumn = 1;
			selectionEndColumn = selectionEndLineNumberMaxColumn;
		}

		// `pretext` contains the text before the selection
101 102
		let pretext = '';
		let startLineNumber = Math.max(1, selectionStartLineNumber - PADDING_LINES_COUNT);
103 104 105 106 107 108 109 110 111 112
		if (startLineNumber < selectionStartLineNumber) {
			pretext = model.getValueInRange(new Range(startLineNumber, 1, selectionStartLineNumber, 1), EditorCommon.EndOfLinePreference.LF);
		}
		pretext += model.getValueInRange(new Range(selectionStartLineNumber, 1, selectionStartLineNumber, selectionStartColumn), EditorCommon.EndOfLinePreference.LF);
		if (pretext.length > LIMIT_CHARS) {
			pretext = pretext.substring(pretext.length - LIMIT_CHARS, pretext.length);
		}


		// `posttext` contains the text after the selection
113 114
		let posttext = '';
		let endLineNumber = Math.min(selectionEndLineNumber + PADDING_LINES_COUNT, model.getLineCount());
115 116 117 118 119 120 121 122 123 124
		posttext += model.getValueInRange(new Range(selectionEndLineNumber, selectionEndColumn, selectionEndLineNumber, selectionEndLineNumberMaxColumn), EditorCommon.EndOfLinePreference.LF);
		if (endLineNumber > selectionEndLineNumber) {
			posttext = '\n' + model.getValueInRange(new Range(selectionEndLineNumber + 1, 1, endLineNumber, model.getLineMaxColumn(endLineNumber)), EditorCommon.EndOfLinePreference.LF);
		}
		if (posttext.length > LIMIT_CHARS) {
			posttext = posttext.substring(0, LIMIT_CHARS);
		}


		// `text` contains the text of the selection
125
		let text = model.getValueInRange(new Range(selectionStartLineNumber, selectionStartColumn, selectionEndLineNumber, selectionEndColumn), EditorCommon.EndOfLinePreference.LF);
126 127 128 129
		if (text.length > 2 * LIMIT_CHARS) {
			text = text.substring(0, LIMIT_CHARS) + String.fromCharCode(8230) + text.substring(text.length - LIMIT_CHARS, text.length);
		}

130
		return new TextAreaState(pretext + text + posttext, pretext.length, pretext.length + text.length, false, selectionStartLineNumber);
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
	}

	public getSelectionStart(): number {
		return this.selectionStart;
	}

	public resetSelection(): void {
		this.selectionStart = this.value.length;
		this.selectionEnd = this.value.length;
	}

	public getValue(): string {
		return this.value;
	}

	public getSelectionToken(): number {
		return this.selectionToken;
	}

	public applyToTextArea(textArea:ITextAreaWrapper, select:boolean): void {
		// console.log('applyToTextArea: ' + this.toString());
		if (textArea.value !== this.value) {
			textArea.value = this.value;
		}
		if (select) {
			textArea.setSelectionRange(this.selectionStart, this.selectionEnd);
		}
	}

	public extractNewText(previousState:TextAreaState): string {
A
Alex Dima 已提交
161 162 163
		// console.log('-----------')
		// console.log('prev:' + String(previousState));
		// console.log('curr:' + String(this));
164 165 166 167 168 169 170
		if (this.selectionStart !== this.selectionEnd) {
			// There is a selection in the textarea => ignore input
			return '';
		}
		if (!previousState) {
			return this.value;
		}
171 172
		let previousPrefix = previousState.value.substring(0, previousState.selectionStart);
		let previousSuffix = previousState.value.substring(previousState.selectionEnd, previousState.value.length);
173

174
		if (this.isInOverwriteMode) {
175 176 177
			previousSuffix = previousSuffix.substr(1);
		}

178
		let value = this.value;
179 180 181 182 183 184 185 186 187
		if (value.substring(0, previousPrefix.length) === previousPrefix) {
			value = value.substring(previousPrefix.length);
		}
		if (value.substring(value.length - previousSuffix.length, value.length) === previousSuffix) {
			value = value.substring(0, value.length - previousSuffix.length);
		}
		return value;
	}
}