extHostTextEditor.ts 19.2 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.
 *--------------------------------------------------------------------------------------------*/

6
import { ok } from 'vs/base/common/assert';
A
Alex Dima 已提交
7
import { illegalArgument, readonly } from 'vs/base/common/errors';
J
Johannes Rieken 已提交
8
import { IdGenerator } from 'vs/base/common/idGenerator';
9 10
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { IRange } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
11
import { ISingleEditOperation } from 'vs/editor/common/model';
12
import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol';
J
Johannes Rieken 已提交
13 14 15
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes';
A
Alex Dima 已提交
16
import * as vscode from 'vscode';
E
Erich Gamma 已提交
17

J
Johannes Rieken 已提交
18
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
E
Erich Gamma 已提交
19

20
	private static readonly _Keys = new IdGenerator('TextEditorDecorationType');
E
Erich Gamma 已提交
21

22
	private _proxy: MainThreadTextEditorsShape;
E
Erich Gamma 已提交
23 24
	public key: string;

25
	constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) {
J
Johannes Rieken 已提交
26
		this.key = TextEditorDecorationType._Keys.nextId();
E
Erich Gamma 已提交
27
		this._proxy = proxy;
A
Alex Dima 已提交
28
		this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options));
E
Erich Gamma 已提交
29 30 31
	}

	public dispose(): void {
32
		this._proxy.$removeTextEditorDecorationType(this.key);
E
Erich Gamma 已提交
33 34 35 36
	}
}

export interface ITextEditOperation {
37
	range: vscode.Range;
M
Matt Bierner 已提交
38
	text: string | null;
E
Erich Gamma 已提交
39 40 41 42 43 44
	forceMoveMarkers: boolean;
}

export interface IEditData {
	documentVersionId: number;
	edits: ITextEditOperation[];
J
Johannes Rieken 已提交
45
	setEndOfLine: EndOfLine | undefined;
J
Johannes Rieken 已提交
46 47
	undoStopBefore: boolean;
	undoStopAfter: boolean;
E
Erich Gamma 已提交
48 49 50 51
}

export class TextEditorEdit {

52 53
	private readonly _document: vscode.TextDocument;
	private readonly _documentVersionId: number;
E
Erich Gamma 已提交
54
	private _collectedEdits: ITextEditOperation[];
J
Johannes Rieken 已提交
55
	private _setEndOfLine: EndOfLine | undefined;
56 57
	private readonly _undoStopBefore: boolean;
	private readonly _undoStopAfter: boolean;
E
Erich Gamma 已提交
58

J
Johannes Rieken 已提交
59
	constructor(document: vscode.TextDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean; }) {
60
		this._document = document;
E
Erich Gamma 已提交
61 62
		this._documentVersionId = document.version;
		this._collectedEdits = [];
J
Johannes Rieken 已提交
63
		this._setEndOfLine = undefined;
64 65
		this._undoStopBefore = options.undoStopBefore;
		this._undoStopAfter = options.undoStopAfter;
E
Erich Gamma 已提交
66 67 68 69 70
	}

	finalize(): IEditData {
		return {
			documentVersionId: this._documentVersionId,
71
			edits: this._collectedEdits,
72 73 74
			setEndOfLine: this._setEndOfLine,
			undoStopBefore: this._undoStopBefore,
			undoStopAfter: this._undoStopAfter
E
Erich Gamma 已提交
75 76 77 78
		};
	}

	replace(location: Position | Range | Selection, value: string): void {
79
		let range: Range | null = null;
E
Erich Gamma 已提交
80 81 82 83 84

		if (location instanceof Position) {
			range = new Range(location, location);
		} else if (location instanceof Range) {
			range = location;
A
Alex Dima 已提交
85
		} else {
E
Erich Gamma 已提交
86 87 88
			throw new Error('Unrecognized location');
		}

89
		this._pushEdit(range, value, false);
E
Erich Gamma 已提交
90 91 92
	}

	insert(location: Position, value: string): void {
93
		this._pushEdit(new Range(location, location), value, true);
E
Erich Gamma 已提交
94 95 96
	}

	delete(location: Range | Selection): void {
97
		let range: Range | null = null;
E
Erich Gamma 已提交
98 99 100

		if (location instanceof Range) {
			range = location;
A
Alex Dima 已提交
101
		} else {
E
Erich Gamma 已提交
102 103 104
			throw new Error('Unrecognized location');
		}

105 106 107
		this._pushEdit(range, null, true);
	}

M
Matt Bierner 已提交
108
	private _pushEdit(range: Range, text: string | null, forceMoveMarkers: boolean): void {
109
		const validRange = this._document.validateRange(range);
E
Erich Gamma 已提交
110
		this._collectedEdits.push({
111 112 113
			range: validRange,
			text: text,
			forceMoveMarkers: forceMoveMarkers
E
Erich Gamma 已提交
114 115
		});
	}
116

J
Johannes Rieken 已提交
117
	setEndOfLine(endOfLine: EndOfLine): void {
118
		if (endOfLine !== EndOfLine.LF && endOfLine !== EndOfLine.CRLF) {
J
Johannes Rieken 已提交
119
			throw illegalArgument('endOfLine');
120 121 122 123
		}

		this._setEndOfLine = endOfLine;
	}
E
Erich Gamma 已提交
124 125 126
}


J
Johannes Rieken 已提交
127
function deprecated(name: string, message: string = 'Refer to the documentation for further details.') {
E
Erich Gamma 已提交
128 129
	return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
		const originalMethod = descriptor.value;
J
Johannes Rieken 已提交
130
		descriptor.value = function (...args: any[]) {
E
Erich Gamma 已提交
131 132
			console.warn(`[Deprecation Warning] method '${name}' is deprecated and should no longer be used. ${message}`);
			return originalMethod.apply(this, args);
B
Benjamin Pasero 已提交
133
		};
E
Erich Gamma 已提交
134 135

		return descriptor;
B
Benjamin Pasero 已提交
136
	};
E
Erich Gamma 已提交
137 138
}

139 140
export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {

141
	private _proxy: MainThreadTextEditorsShape;
142 143 144
	private _id: string;

	private _tabSize: number;
D
David Lechner 已提交
145
	private _indentSize: number;
146 147 148 149
	private _insertSpaces: boolean;
	private _cursorStyle: TextEditorCursorStyle;
	private _lineNumbers: TextEditorLineNumbersStyle;

150
	constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration) {
151 152 153 154 155 156 157
		this._proxy = proxy;
		this._id = id;
		this._accept(source);
	}

	public _accept(source: IResolvedTextEditorConfiguration): void {
		this._tabSize = source.tabSize;
D
David Lechner 已提交
158
		this._indentSize = source.indentSize;
159 160
		this._insertSpaces = source.insertSpaces;
		this._cursorStyle = source.cursorStyle;
161
		this._lineNumbers = TypeConverters.TextEditorLineNumbersStyle.to(source.lineNumbers);
162 163 164 165 166 167 168 169 170 171 172
	}

	public get tabSize(): number | string {
		return this._tabSize;
	}

	private _validateTabSize(value: number | string): number | 'auto' | null {
		if (value === 'auto') {
			return 'auto';
		}
		if (typeof value === 'number') {
173
			const r = Math.floor(value);
174 175 176
			return (r > 0 ? r : null);
		}
		if (typeof value === 'string') {
177
			const r = parseInt(value, 10);
178 179 180 181 182 183 184 185 186
			if (isNaN(r)) {
				return null;
			}
			return (r > 0 ? r : null);
		}
		return null;
	}

	public set tabSize(value: number | string) {
187
		const tabSize = this._validateTabSize(value);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
		if (tabSize === null) {
			// ignore invalid call
			return;
		}
		if (typeof tabSize === 'number') {
			if (this._tabSize === tabSize) {
				// nothing to do
				return;
			}
			// reflect the new tabSize value immediately
			this._tabSize = tabSize;
		}
		warnOnError(this._proxy.$trySetOptions(this._id, {
			tabSize: tabSize
		}));
	}

D
David Lechner 已提交
205 206 207 208
	public get indentSize(): number | string {
		return this._indentSize;
	}

A
Alex Dima 已提交
209 210 211
	private _validateIndentSize(value: number | string): number | 'tabSize' | null {
		if (value === 'tabSize') {
			return 'tabSize';
D
David Lechner 已提交
212 213
		}
		if (typeof value === 'number') {
214
			const r = Math.floor(value);
D
David Lechner 已提交
215 216 217
			return (r > 0 ? r : null);
		}
		if (typeof value === 'string') {
218
			const r = parseInt(value, 10);
D
David Lechner 已提交
219 220 221 222 223 224 225 226 227
			if (isNaN(r)) {
				return null;
			}
			return (r > 0 ? r : null);
		}
		return null;
	}

	public set indentSize(value: number | string) {
228
		const indentSize = this._validateIndentSize(value);
D
David Lechner 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
		if (indentSize === null) {
			// ignore invalid call
			return;
		}
		if (typeof indentSize === 'number') {
			if (this._indentSize === indentSize) {
				// nothing to do
				return;
			}
			// reflect the new indentSize value immediately
			this._indentSize = indentSize;
		}
		warnOnError(this._proxy.$trySetOptions(this._id, {
			indentSize: indentSize
		}));
	}

246 247 248 249 250 251 252 253 254 255 256 257
	public get insertSpaces(): boolean | string {
		return this._insertSpaces;
	}

	private _validateInsertSpaces(value: boolean | string): boolean | 'auto' {
		if (value === 'auto') {
			return 'auto';
		}
		return (value === 'false' ? false : Boolean(value));
	}

	public set insertSpaces(value: boolean | string) {
258
		const insertSpaces = this._validateInsertSpaces(value);
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
		if (typeof insertSpaces === 'boolean') {
			if (this._insertSpaces === insertSpaces) {
				// nothing to do
				return;
			}
			// reflect the new insertSpaces value immediately
			this._insertSpaces = insertSpaces;
		}
		warnOnError(this._proxy.$trySetOptions(this._id, {
			insertSpaces: insertSpaces
		}));
	}

	public get cursorStyle(): TextEditorCursorStyle {
		return this._cursorStyle;
	}

	public set cursorStyle(value: TextEditorCursorStyle) {
		if (this._cursorStyle === value) {
			// nothing to do
			return;
		}
		this._cursorStyle = value;
		warnOnError(this._proxy.$trySetOptions(this._id, {
			cursorStyle: value
		}));
	}

	public get lineNumbers(): TextEditorLineNumbersStyle {
		return this._lineNumbers;
	}

	public set lineNumbers(value: TextEditorLineNumbersStyle) {
		if (this._lineNumbers === value) {
			// nothing to do
			return;
		}
		this._lineNumbers = value;
		warnOnError(this._proxy.$trySetOptions(this._id, {
298
			lineNumbers: TypeConverters.TextEditorLineNumbersStyle.from(value)
299 300 301 302
		}));
	}

	public assign(newOptions: vscode.TextEditorOptions) {
303
		const bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {};
304 305 306
		let hasUpdate = false;

		if (typeof newOptions.tabSize !== 'undefined') {
307
			const tabSize = this._validateTabSize(newOptions.tabSize);
308 309 310 311 312 313 314 315 316 317 318
			if (tabSize === 'auto') {
				hasUpdate = true;
				bulkConfigurationUpdate.tabSize = tabSize;
			} else if (typeof tabSize === 'number' && this._tabSize !== tabSize) {
				// reflect the new tabSize value immediately
				this._tabSize = tabSize;
				hasUpdate = true;
				bulkConfigurationUpdate.tabSize = tabSize;
			}
		}

319
		// if (typeof newOptions.indentSize !== 'undefined') {
320
		// 	const indentSize = this._validateIndentSize(newOptions.indentSize);
321 322 323 324 325 326 327 328 329 330
		// 	if (indentSize === 'tabSize') {
		// 		hasUpdate = true;
		// 		bulkConfigurationUpdate.indentSize = indentSize;
		// 	} else if (typeof indentSize === 'number' && this._indentSize !== indentSize) {
		// 		// reflect the new indentSize value immediately
		// 		this._indentSize = indentSize;
		// 		hasUpdate = true;
		// 		bulkConfigurationUpdate.indentSize = indentSize;
		// 	}
		// }
D
David Lechner 已提交
331

332
		if (typeof newOptions.insertSpaces !== 'undefined') {
333
			const insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces);
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
			if (insertSpaces === 'auto') {
				hasUpdate = true;
				bulkConfigurationUpdate.insertSpaces = insertSpaces;
			} else if (this._insertSpaces !== insertSpaces) {
				// reflect the new insertSpaces value immediately
				this._insertSpaces = insertSpaces;
				hasUpdate = true;
				bulkConfigurationUpdate.insertSpaces = insertSpaces;
			}
		}

		if (typeof newOptions.cursorStyle !== 'undefined') {
			if (this._cursorStyle !== newOptions.cursorStyle) {
				this._cursorStyle = newOptions.cursorStyle;
				hasUpdate = true;
				bulkConfigurationUpdate.cursorStyle = newOptions.cursorStyle;
			}
		}

		if (typeof newOptions.lineNumbers !== 'undefined') {
			if (this._lineNumbers !== newOptions.lineNumbers) {
				this._lineNumbers = newOptions.lineNumbers;
				hasUpdate = true;
357
				bulkConfigurationUpdate.lineNumbers = TypeConverters.TextEditorLineNumbersStyle.from(newOptions.lineNumbers);
358 359 360 361 362 363 364 365 366
			}
		}

		if (hasUpdate) {
			warnOnError(this._proxy.$trySetOptions(this._id, bulkConfigurationUpdate));
		}
	}
}

J
Johannes Rieken 已提交
367
export class ExtHostTextEditor implements vscode.TextEditor {
E
Erich Gamma 已提交
368

369
	private readonly _proxy: MainThreadTextEditorsShape;
370 371
	private readonly _id: string;
	private readonly _documentData: ExtHostDocumentData;
E
Erich Gamma 已提交
372 373

	private _selections: Selection[];
374
	private _options: ExtHostTextEditorOptions;
375
	private _visibleRanges: Range[];
M
Matt Bierner 已提交
376
	private _viewColumn: vscode.ViewColumn | undefined;
377
	private _disposed: boolean = false;
A
Alex Dima 已提交
378
	private _hasDecorationsForKey: { [key: string]: boolean; };
E
Erich Gamma 已提交
379

380 381
	get id(): string { return this._id; }

382 383 384
	constructor(
		proxy: MainThreadTextEditorsShape, id: string, document: ExtHostDocumentData,
		selections: Selection[], options: IResolvedTextEditorConfiguration,
M
Matt Bierner 已提交
385
		visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined
386
	) {
E
Erich Gamma 已提交
387 388
		this._proxy = proxy;
		this._id = id;
389
		this._documentData = document;
E
Erich Gamma 已提交
390
		this._selections = selections;
391
		this._options = new ExtHostTextEditorOptions(this._proxy, this._id, options);
392
		this._visibleRanges = visibleRanges;
393
		this._viewColumn = viewColumn;
A
Alex Dima 已提交
394
		this._hasDecorationsForKey = Object.create(null);
E
Erich Gamma 已提交
395 396 397
	}

	dispose() {
398 399
		ok(!this._disposed);
		this._disposed = true;
E
Erich Gamma 已提交
400 401 402
	}

	@deprecated('TextEditor.show') show(column: vscode.ViewColumn) {
403
		this._proxy.$tryShowEditor(this._id, TypeConverters.ViewColumn.from(column));
E
Erich Gamma 已提交
404 405 406
	}

	@deprecated('TextEditor.hide') hide() {
407
		this._proxy.$tryHideEditor(this._id);
E
Erich Gamma 已提交
408 409 410 411 412
	}

	// ---- the document

	get document(): vscode.TextDocument {
413
		return this._documentData.document;
E
Erich Gamma 已提交
414 415 416 417 418 419 420 421
	}

	set document(value) {
		throw readonly('document');
	}

	// ---- options

422
	get options(): vscode.TextEditorOptions {
E
Erich Gamma 已提交
423 424 425
		return this._options;
	}

426
	set options(value: vscode.TextEditorOptions) {
427 428 429
		if (!this._disposed) {
			this._options.assign(value);
		}
E
Erich Gamma 已提交
430 431
	}

432
	_acceptOptions(options: IResolvedTextEditorConfiguration): void {
433
		ok(!this._disposed);
434
		this._options._accept(options);
E
Erich Gamma 已提交
435 436
	}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	// ---- visible ranges

	get visibleRanges(): Range[] {
		return this._visibleRanges;
	}

	set visibleRanges(value: Range[]) {
		throw readonly('visibleRanges');
	}

	_acceptVisibleRanges(value: Range[]): void {
		ok(!this._disposed);
		this._visibleRanges = value;
	}

452 453
	// ---- view column

M
Matt Bierner 已提交
454
	get viewColumn(): vscode.ViewColumn | undefined {
455 456 457 458 459 460 461 462
		return this._viewColumn;
	}

	set viewColumn(value) {
		throw readonly('viewColumn');
	}

	_acceptViewColumn(value: vscode.ViewColumn) {
463
		ok(!this._disposed);
464 465 466
		this._viewColumn = value;
	}

E
Erich Gamma 已提交
467 468 469 470 471 472 473 474
	// ---- selections

	get selection(): Selection {
		return this._selections && this._selections[0];
	}

	set selection(value: Selection) {
		if (!(value instanceof Selection)) {
J
Johannes Rieken 已提交
475
			throw illegalArgument('selection');
E
Erich Gamma 已提交
476 477
		}
		this._selections = [value];
478
		this._trySetSelection();
E
Erich Gamma 已提交
479 480 481 482 483 484 485 486
	}

	get selections(): Selection[] {
		return this._selections;
	}

	set selections(value: Selection[]) {
		if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
J
Johannes Rieken 已提交
487
			throw illegalArgument('selections');
E
Erich Gamma 已提交
488 489
		}
		this._selections = value;
490
		this._trySetSelection();
E
Erich Gamma 已提交
491 492
	}

493
	setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
A
Alex Dima 已提交
494 495 496 497 498 499 500 501 502 503
		const willBeEmpty = (ranges.length === 0);
		if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
			// avoid no-op call to the renderer
			return;
		}
		if (willBeEmpty) {
			delete this._hasDecorationsForKey[decorationType.key];
		} else {
			this._hasDecorationsForKey[decorationType.key] = true;
		}
E
Erich Gamma 已提交
504
		this._runOnProxy(
505 506 507 508 509 510 511 512
			() => {
				if (TypeConverters.isDecorationOptionsArr(ranges)) {
					return this._proxy.$trySetDecorations(
						this._id,
						decorationType.key,
						TypeConverters.fromRangeOrRangeWithMessage(ranges)
					);
				} else {
513
					const _ranges: number[] = new Array<number>(4 * ranges.length);
514 515 516 517 518 519 520 521 522 523
					for (let i = 0, len = ranges.length; i < len; i++) {
						const range = ranges[i];
						_ranges[4 * i] = range.start.line + 1;
						_ranges[4 * i + 1] = range.start.character + 1;
						_ranges[4 * i + 2] = range.end.line + 1;
						_ranges[4 * i + 3] = range.end.character + 1;
					}
					return this._proxy.$trySetDecorationsFast(
						this._id,
						decorationType.key,
524
						_ranges
525 526 527
					);
				}
			}
E
Erich Gamma 已提交
528 529 530
		);
	}

J
Johannes Rieken 已提交
531
	revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
E
Erich Gamma 已提交
532
		this._runOnProxy(
533
			() => this._proxy.$tryRevealRange(
E
Erich Gamma 已提交
534
				this._id,
535
				TypeConverters.Range.from(range),
A
Alex Dima 已提交
536
				(revealType || TextEditorRevealType.Default)
537
			)
E
Erich Gamma 已提交
538 539 540
		);
	}

M
Matt Bierner 已提交
541
	private _trySetSelection(): Promise<vscode.TextEditor | null | undefined> {
542
		const selection = this._selections.map(TypeConverters.Selection.from);
543
		return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
E
Erich Gamma 已提交
544 545
	}

J
Johannes Rieken 已提交
546
	_acceptSelections(selections: Selection[]): void {
547
		ok(!this._disposed);
E
Erich Gamma 已提交
548 549 550 551 552
		this._selections = selections;
	}

	// ---- editing

J
Johannes Rieken 已提交
553
	edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
554
		if (this._disposed) {
J
Johannes Rieken 已提交
555
			return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
556
		}
557
		const edit = new TextEditorEdit(this._documentData.document, options);
558 559
		callback(edit);
		return this._applyEdit(edit);
E
Erich Gamma 已提交
560 561
	}

J
Johannes Rieken 已提交
562
	private _applyEdit(editBuilder: TextEditorEdit): Promise<boolean> {
563
		const editData = editBuilder.finalize();
E
Erich Gamma 已提交
564

J
Johannes Rieken 已提交
565 566
		// return when there is nothing to do
		if (editData.edits.length === 0 && !editData.setEndOfLine) {
J
Johannes Rieken 已提交
567
			return Promise.resolve(true);
J
Johannes Rieken 已提交
568 569
		}

570
		// check that the edits are not overlapping (i.e. illegal)
571
		const editRanges = editData.edits.map(edit => edit.range);
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593

		// sort ascending (by end and then by start)
		editRanges.sort((a, b) => {
			if (a.end.line === b.end.line) {
				if (a.end.character === b.end.character) {
					if (a.start.line === b.start.line) {
						return a.start.character - b.start.character;
					}
					return a.start.line - b.start.line;
				}
				return a.end.character - b.end.character;
			}
			return a.end.line - b.end.line;
		});

		// check that no edits are overlapping
		for (let i = 0, count = editRanges.length - 1; i < count; i++) {
			const rangeEnd = editRanges[i].end;
			const nextRangeStart = editRanges[i + 1].start;

			if (nextRangeStart.isBefore(rangeEnd)) {
				// overlapping ranges
J
Johannes Rieken 已提交
594
				return Promise.reject(
595 596 597 598 599
					new Error('Overlapping ranges are not allowed!')
				);
			}
		}

E
Erich Gamma 已提交
600
		// prepare data for serialization
M
Matt Bierner 已提交
601
		const edits = editData.edits.map((edit): ISingleEditOperation => {
E
Erich Gamma 已提交
602
			return {
603
				range: TypeConverters.Range.from(edit.range),
E
Erich Gamma 已提交
604 605 606 607 608
				text: edit.text,
				forceMoveMarkers: edit.forceMoveMarkers
			};
		});

609
		return this._proxy.$tryApplyEdits(this._id, editData.documentVersionId, edits, {
J
Johannes Rieken 已提交
610
			setEndOfLine: typeof editData.setEndOfLine === 'number' ? TypeConverters.EndOfLine.from(editData.setEndOfLine) : undefined,
611 612 613
			undoStopBefore: editData.undoStopBefore,
			undoStopAfter: editData.undoStopAfter
		});
E
Erich Gamma 已提交
614 615
	}

M
Matt Bierner 已提交
616
	insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
617
		if (this._disposed) {
J
Johannes Rieken 已提交
618
			return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
619
		}
620
		let ranges: IRange[];
621 622

		if (!where || (Array.isArray(where) && where.length === 0)) {
M
Matt Bierner 已提交
623
			ranges = this._selections.map(range => TypeConverters.Range.from(range));
624 625

		} else if (where instanceof Position) {
626
			const { lineNumber, column } = TypeConverters.Position.from(where);
627
			ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
628

629
		} else if (where instanceof Range) {
630
			ranges = [TypeConverters.Range.from(where)];
631 632 633 634
		} else {
			ranges = [];
			for (const posOrRange of where) {
				if (posOrRange instanceof Range) {
635
					ranges.push(TypeConverters.Range.from(posOrRange));
636
				} else {
637
					const { lineNumber, column } = TypeConverters.Position.from(posOrRange);
638 639 640 641
					ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
				}
			}
		}
642

643
		return this._proxy.$tryInsertSnippet(this._id, snippet.value, ranges, options);
644 645
	}

E
Erich Gamma 已提交
646 647
	// ---- util

M
Matt Bierner 已提交
648
	private _runOnProxy(callback: () => Promise<any>): Promise<ExtHostTextEditor | undefined | null> {
649
		if (this._disposed) {
650
			console.warn('TextEditor is closed/disposed');
J
Johannes Rieken 已提交
651
			return Promise.resolve(undefined);
652
		}
E
Erich Gamma 已提交
653
		return callback().then(() => this, err => {
R
Ron Buckton 已提交
654 655
			if (!(err instanceof Error && err.name === 'DISPOSED')) {
				console.warn(err);
656
			}
R
Ron Buckton 已提交
657
			return null;
E
Erich Gamma 已提交
658 659 660
		});
	}
}
661

J
Johannes Rieken 已提交
662
function warnOnError(promise: Promise<any>): void {
R
Rob Lourens 已提交
663
	promise.then(undefined, (err) => {
664 665 666
		console.warn(err);
	});
}