codeEditorWidget.ts 23.9 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import 'vs/css!./media/editor';
8
import 'vs/editor/common/view/editorColorRegistry'; // initialze editor theming partcicpants
E
Erich Gamma 已提交
9
import 'vs/css!./media/tokens';
J
Johannes Rieken 已提交
10
import { onUnexpectedError } from 'vs/base/common/errors';
11
import { TPromise } from 'vs/base/common/winjs.base';
A
Alex Dima 已提交
12 13
import * as browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
J
Johannes Rieken 已提交
14 15 16 17 18
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { CommonCodeEditor } from 'vs/editor/common/commonCodeEditor';
import { CommonEditorConfiguration } from 'vs/editor/common/config/commonEditorConfig';
A
Alex Dima 已提交
19
import { Range, IRange } from 'vs/editor/common/core/range';
J
Johannes Rieken 已提交
20
import { Selection } from 'vs/editor/common/core/selection';
A
Alex Dima 已提交
21
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
22 23 24
import { EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { Configuration } from 'vs/editor/browser/config/configuration';
A
Alex Dima 已提交
25
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
J
Johannes Rieken 已提交
26
import { Colorizer } from 'vs/editor/browser/standalone/colorizer';
A
Alex Dima 已提交
27
import { View, IOverlayWidgetData, IContentWidgetData } from 'vs/editor/browser/view/viewImpl';
J
Johannes Rieken 已提交
28
import { Disposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
29
import Event, { Emitter } from 'vs/base/common/event';
J
Johannes Rieken 已提交
30 31
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { InternalEditorAction } from 'vs/editor/common/editorAction';
32
import { IEditorOptions } from "vs/editor/common/config/editorOptions";
A
Alex Dima 已提交
33
import { IPosition } from "vs/editor/common/core/position";
A
Alex Dima 已提交
34
import { IEditorWhitespace } from "vs/editor/common/viewLayout/whitespaceComputer";
E
Erich Gamma 已提交
35

36
export abstract class CodeEditorWidget extends CommonCodeEditor implements editorBrowser.ICodeEditor {
E
Erich Gamma 已提交
37

A
Alex Dima 已提交
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
	private readonly _onMouseUp: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onMouseUp: Event<editorBrowser.IEditorMouseEvent> = this._onMouseUp.event;

	private readonly _onMouseDown: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onMouseDown: Event<editorBrowser.IEditorMouseEvent> = this._onMouseDown.event;

	private readonly _onMouseDrag: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onMouseDrag: Event<editorBrowser.IEditorMouseEvent> = this._onMouseDrag.event;

	private readonly _onMouseDrop: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onMouseDrop: Event<editorBrowser.IEditorMouseEvent> = this._onMouseDrop.event;

	private readonly _onContextMenu: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onContextMenu: Event<editorBrowser.IEditorMouseEvent> = this._onContextMenu.event;

	private readonly _onMouseMove: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onMouseMove: Event<editorBrowser.IEditorMouseEvent> = this._onMouseMove.event;

	private readonly _onMouseLeave: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
	public readonly onMouseLeave: Event<editorBrowser.IEditorMouseEvent> = this._onMouseLeave.event;

	private readonly _onKeyUp: Emitter<IKeyboardEvent> = this._register(new Emitter<IKeyboardEvent>());
	public readonly onKeyUp: Event<IKeyboardEvent> = this._onKeyUp.event;

	private readonly _onKeyDown: Emitter<IKeyboardEvent> = this._register(new Emitter<IKeyboardEvent>());
	public readonly onKeyDown: Event<IKeyboardEvent> = this._onKeyDown.event;

	private readonly _onDidScrollChange: Emitter<editorCommon.IScrollEvent> = this._register(new Emitter<editorCommon.IScrollEvent>());
	public readonly onDidScrollChange: Event<editorCommon.IScrollEvent> = this._onDidScrollChange.event;

	private readonly _onDidChangeViewZones: Emitter<void> = this._register(new Emitter<void>());
	public readonly onDidChangeViewZones: Event<void> = this._onDidChangeViewZones.event;
A
Alex Dima 已提交
70

71
	private _codeEditorService: ICodeEditorService;
72
	private _commandService: ICommandService;
73

J
Johannes Rieken 已提交
74
	protected domElement: HTMLElement;
75
	private _focusTracker: CodeEditorWidgetFocusTracker;
E
Erich Gamma 已提交
76

J
Johannes Rieken 已提交
77
	_configuration: Configuration;
78

A
Alex Dima 已提交
79 80
	private contentWidgets: { [key: string]: IContentWidgetData; };
	private overlayWidgets: { [key: string]: IOverlayWidgetData; };
E
Erich Gamma 已提交
81

A
Alex Dima 已提交
82
	_view: View;
E
Erich Gamma 已提交
83 84

	constructor(
J
Johannes Rieken 已提交
85
		domElement: HTMLElement,
86
		options: IEditorOptions,
E
Erich Gamma 已提交
87 88
		@IInstantiationService instantiationService: IInstantiationService,
		@ICodeEditorService codeEditorService: ICodeEditorService,
89
		@ICommandService commandService: ICommandService,
90
		@IContextKeyService contextKeyService: IContextKeyService
E
Erich Gamma 已提交
91
	) {
92
		super(domElement, options, instantiationService, contextKeyService);
93
		this._codeEditorService = codeEditorService;
94
		this._commandService = commandService;
E
Erich Gamma 已提交
95

96 97 98
		this._focusTracker = new CodeEditorWidgetFocusTracker(domElement);
		this._focusTracker.onChage(() => {
			let hasFocus = this._focusTracker.hasFocus();
99

100
			if (hasFocus) {
A
Alex Dima 已提交
101
				this._onDidFocusEditor.fire();
102
			} else {
A
Alex Dima 已提交
103
				this._onDidBlurEditor.fire();
E
Erich Gamma 已提交
104 105 106 107 108 109
			}
		});

		this.contentWidgets = {};
		this.overlayWidgets = {};

110 111 112
		let contributions = this._getContributions();
		for (let i = 0, len = contributions.length; i < len; i++) {
			let ctor = contributions[i];
E
Erich Gamma 已提交
113
			try {
114
				let contribution = this._instantiationService.createInstance(ctor, this);
A
Alex Dima 已提交
115
				this._contributions[contribution.getId()] = contribution;
E
Erich Gamma 已提交
116
			} catch (err) {
117
				onUnexpectedError(err);
E
Erich Gamma 已提交
118 119
			}
		}
120

121
		this._getActions().forEach((action) => {
122 123 124 125 126 127 128 129 130 131 132 133
			const internalAction = new InternalEditorAction(
				action.id,
				action.label,
				action.alias,
				action.precondition,
				(): void | TPromise<void> => {
					return this._instantiationService.invokeFunction((accessor) => {
						return action.runEditorCommand(accessor, this, null);
					});
				},
				this._contextKeyService
			);
A
Alex Dima 已提交
134
			this._actions[internalAction.id] = internalAction;
135
		});
136 137

		this._codeEditorService.addCodeEditor(this);
E
Erich Gamma 已提交
138 139
	}

140
	protected abstract _getContributions(): editorBrowser.IEditorContributionCtor[];
141 142
	protected abstract _getActions(): EditorAction[];

A
Alex Dima 已提交
143
	protected _createConfiguration(options: IEditorOptions): CommonEditorConfiguration {
144
		return new Configuration(options, this.domElement);
E
Erich Gamma 已提交
145 146 147
	}

	public dispose(): void {
148 149
		this._codeEditorService.removeCodeEditor(this);

E
Erich Gamma 已提交
150 151 152
		this.contentWidgets = {};
		this.overlayWidgets = {};

153
		this._focusTracker.dispose();
E
Erich Gamma 已提交
154 155 156
		super.dispose();
	}

157
	public updateOptions(newOptions: IEditorOptions): void {
158
		let oldTheme = this._configuration.editor.viewInfo.theme;
159
		super.updateOptions(newOptions);
160
		let newTheme = this._configuration.editor.viewInfo.theme;
161 162 163 164 165 166

		if (oldTheme !== newTheme) {
			this.render();
		}
	}

J
Johannes Rieken 已提交
167
	public colorizeModelLine(lineNumber: number, model: editorCommon.IModel = this.model): string {
E
Erich Gamma 已提交
168 169 170
		if (!model) {
			return '';
		}
A
Alex Dima 已提交
171
		let content = model.getLineContent(lineNumber);
A
Alex Dima 已提交
172 173
		model.forceTokenization(lineNumber);
		let tokens = model.getLineTokens(lineNumber);
A
Alex Dima 已提交
174 175
		let inflatedTokens = tokens.inflate();
		let tabSize = model.getOptions().tabSize;
176
		return Colorizer.colorizeLine(content, model.mightContainRTL(), inflatedTokens, tabSize);
E
Erich Gamma 已提交
177
	}
A
Alex Dima 已提交
178 179 180

	public createOverviewRuler(cssClassName: string, minimumHeight: number, maximumHeight: number): editorBrowser.IOverviewRuler {
		return this._view.createOverviewRuler(cssClassName, minimumHeight, maximumHeight);
E
Erich Gamma 已提交
181 182 183 184 185 186
	}

	public getDomNode(): HTMLElement {
		if (!this.hasView) {
			return null;
		}
A
Alex Dima 已提交
187
		return this._view.domNode.domNode;
E
Erich Gamma 已提交
188 189
	}

190
	public getCenteredRangeInViewport(): Range {
E
Erich Gamma 已提交
191 192 193
		if (!this.hasView) {
			return null;
		}
A
Alex Dima 已提交
194
		return this.viewModel.getCenteredRangeInViewport();
E
Erich Gamma 已提交
195 196
	}

A
Alex Dima 已提交
197
	protected _getCompletelyVisibleViewRange(): Range {
198 199 200
		if (!this.hasView) {
			return null;
		}
A
Alex Dima 已提交
201
		return this._view.getCompletelyVisibleViewRange();
A
Alex Dima 已提交
202 203
	}

204 205 206 207 208 209 210 211 212 213 214 215 216 217
	protected _getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range {
		if (!this.hasView) {
			return null;
		}
		return this._view.getCompletelyVisibleViewRangeAtScrollTop(scrollTop);
	}

	protected _getVerticalOffsetForViewLineNumber(viewLineNumber: number): number {
		if (!this.hasView) {
			return 0;
		}
		return this._view.getVerticalOffsetForViewLineNumber(viewLineNumber);
	}

A
Alex Dima 已提交
218 219 220
	public getCompletelyVisibleLinesRangeInViewport(): Range {
		const viewRange = this._getCompletelyVisibleViewRange();
		return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
221 222
	}

223
	public getScrollWidth(): number {
E
Erich Gamma 已提交
224
		if (!this.hasView) {
225
			return -1;
E
Erich Gamma 已提交
226
		}
A
Alex Dima 已提交
227
		return this._view.getScrollWidth();
228 229 230 231
	}
	public getScrollLeft(): number {
		if (!this.hasView) {
			return -1;
E
Erich Gamma 已提交
232
		}
A
Alex Dima 已提交
233
		return this._view.getScrollLeft();
E
Erich Gamma 已提交
234 235
	}

236
	public getScrollHeight(): number {
E
Erich Gamma 已提交
237 238 239
		if (!this.hasView) {
			return -1;
		}
A
Alex Dima 已提交
240
		return this._view.getScrollHeight();
E
Erich Gamma 已提交
241
	}
242
	public getScrollTop(): number {
E
Erich Gamma 已提交
243
		if (!this.hasView) {
244
			return -1;
E
Erich Gamma 已提交
245
		}
A
Alex Dima 已提交
246
		return this._view.getScrollTop();
E
Erich Gamma 已提交
247 248
	}

J
Johannes Rieken 已提交
249
	public setScrollLeft(newScrollLeft: number): void {
E
Erich Gamma 已提交
250 251 252 253 254 255
		if (!this.hasView) {
			return;
		}
		if (typeof newScrollLeft !== 'number') {
			throw new Error('Invalid arguments');
		}
A
Alex Dima 已提交
256
		this._view.setScrollPosition({
257 258
			scrollLeft: newScrollLeft
		});
E
Erich Gamma 已提交
259
	}
J
Johannes Rieken 已提交
260
	public setScrollTop(newScrollTop: number): void {
E
Erich Gamma 已提交
261
		if (!this.hasView) {
262
			return;
E
Erich Gamma 已提交
263
		}
264 265 266
		if (typeof newScrollTop !== 'number') {
			throw new Error('Invalid arguments');
		}
A
Alex Dima 已提交
267
		this._view.setScrollPosition({
268 269
			scrollTop: newScrollTop
		});
E
Erich Gamma 已提交
270
	}
271
	public setScrollPosition(position: editorCommon.INewScrollPosition): void {
E
Erich Gamma 已提交
272
		if (!this.hasView) {
273
			return;
E
Erich Gamma 已提交
274
		}
A
Alex Dima 已提交
275
		this._view.setScrollPosition(position);
E
Erich Gamma 已提交
276 277
	}

J
Johannes Rieken 已提交
278
	public delegateVerticalScrollbarMouseDown(browserEvent: MouseEvent): void {
E
Erich Gamma 已提交
279
		if (!this.hasView) {
280
			return;
E
Erich Gamma 已提交
281
		}
A
Alex Dima 已提交
282
		this._view.delegateVerticalScrollbarMouseDown(browserEvent);
E
Erich Gamma 已提交
283 284
	}

A
Alex Dima 已提交
285
	public saveViewState(): editorCommon.ICodeEditorViewState {
E
Erich Gamma 已提交
286 287 288
		if (!this.cursor || !this.hasView) {
			return null;
		}
J
Johannes Rieken 已提交
289
		let contributionsState: { [key: string]: any } = {};
A
Alex Dima 已提交
290 291 292 293 294

		let keys = Object.keys(this._contributions);
		for (let i = 0, len = keys.length; i < len; i++) {
			let id = keys[i];
			let contribution = this._contributions[id];
295 296 297 298 299
			if (typeof contribution.saveViewState === 'function') {
				contributionsState[id] = contribution.saveViewState();
			}
		}

A
Alex Dima 已提交
300 301
		let cursorState = this.cursor.saveState();
		let viewState = this._view.saveState();
E
Erich Gamma 已提交
302 303
		return {
			cursorState: cursorState,
304 305
			viewState: viewState,
			contributionsState: contributionsState
E
Erich Gamma 已提交
306 307 308
		};
	}

A
Alex Dima 已提交
309
	public restoreViewState(s: editorCommon.ICodeEditorViewState): void {
E
Erich Gamma 已提交
310 311 312
		if (!this.cursor || !this.hasView) {
			return;
		}
B
Benjamin Pasero 已提交
313
		if (s && s.cursorState && s.viewState) {
A
Alex Dima 已提交
314 315
			let codeEditorState = <editorCommon.ICodeEditorViewState>s;
			let cursorState = <any>codeEditorState.cursorState;
B
Benjamin Pasero 已提交
316 317 318 319 320
			if (Array.isArray(cursorState)) {
				this.cursor.restoreState(<editorCommon.ICursorState[]>cursorState);
			} else {
				// Backwards compatibility
				this.cursor.restoreState([<editorCommon.ICursorState>cursorState]);
E
Erich Gamma 已提交
321
			}
B
Benjamin Pasero 已提交
322
			this._view.restoreState(codeEditorState.viewState);
323

B
Benjamin Pasero 已提交
324
			let contributionsState = s.contributionsState || {};
A
Alex Dima 已提交
325 326 327 328
			let keys = Object.keys(this._contributions);
			for (let i = 0, len = keys.length; i < len; i++) {
				let id = keys[i];
				let contribution = this._contributions[id];
329 330 331 332
				if (typeof contribution.restoreViewState === 'function') {
					contribution.restoreViewState(contributionsState[id]);
				}
			}
E
Erich Gamma 已提交
333 334 335
		}
	}

J
Johannes Rieken 已提交
336
	public layout(dimension?: editorCommon.IDimension): void {
E
Erich Gamma 已提交
337
		this._configuration.observeReferenceElement(dimension);
338
		this.render();
E
Erich Gamma 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351
	}

	public focus(): void {
		if (!this.hasView) {
			return;
		}
		this._view.focus();
	}

	public isFocused(): boolean {
		return this.hasView && this._view.isFocused();
	}

352
	public hasWidgetFocus(): boolean {
353
		return this._focusTracker && this._focusTracker.hasFocus();
354 355
	}

A
Alex Dima 已提交
356
	public addContentWidget(widget: editorBrowser.IContentWidget): void {
A
Alex Dima 已提交
357
		let widgetData: IContentWidgetData = {
E
Erich Gamma 已提交
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
			widget: widget,
			position: widget.getPosition()
		};

		if (this.contentWidgets.hasOwnProperty(widget.getId())) {
			console.warn('Overwriting a content widget with the same id.');
		}

		this.contentWidgets[widget.getId()] = widgetData;

		if (this.hasView) {
			this._view.addContentWidget(widgetData);
		}
	}

A
Alex Dima 已提交
373
	public layoutContentWidget(widget: editorBrowser.IContentWidget): void {
A
Alex Dima 已提交
374
		let widgetId = widget.getId();
E
Erich Gamma 已提交
375
		if (this.contentWidgets.hasOwnProperty(widgetId)) {
A
Alex Dima 已提交
376
			let widgetData = this.contentWidgets[widgetId];
E
Erich Gamma 已提交
377 378 379 380 381 382 383
			widgetData.position = widget.getPosition();
			if (this.hasView) {
				this._view.layoutContentWidget(widgetData);
			}
		}
	}

A
Alex Dima 已提交
384
	public removeContentWidget(widget: editorBrowser.IContentWidget): void {
A
Alex Dima 已提交
385
		let widgetId = widget.getId();
E
Erich Gamma 已提交
386
		if (this.contentWidgets.hasOwnProperty(widgetId)) {
A
Alex Dima 已提交
387
			let widgetData = this.contentWidgets[widgetId];
E
Erich Gamma 已提交
388 389 390 391 392 393 394
			delete this.contentWidgets[widgetId];
			if (this.hasView) {
				this._view.removeContentWidget(widgetData);
			}
		}
	}

A
Alex Dima 已提交
395
	public addOverlayWidget(widget: editorBrowser.IOverlayWidget): void {
A
Alex Dima 已提交
396
		let widgetData: IOverlayWidgetData = {
E
Erich Gamma 已提交
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
			widget: widget,
			position: widget.getPosition()
		};

		if (this.overlayWidgets.hasOwnProperty(widget.getId())) {
			console.warn('Overwriting an overlay widget with the same id.');
		}

		this.overlayWidgets[widget.getId()] = widgetData;

		if (this.hasView) {
			this._view.addOverlayWidget(widgetData);
		}
	}

A
Alex Dima 已提交
412
	public layoutOverlayWidget(widget: editorBrowser.IOverlayWidget): void {
A
Alex Dima 已提交
413
		let widgetId = widget.getId();
E
Erich Gamma 已提交
414
		if (this.overlayWidgets.hasOwnProperty(widgetId)) {
A
Alex Dima 已提交
415
			let widgetData = this.overlayWidgets[widgetId];
E
Erich Gamma 已提交
416 417 418 419 420 421 422
			widgetData.position = widget.getPosition();
			if (this.hasView) {
				this._view.layoutOverlayWidget(widgetData);
			}
		}
	}

A
Alex Dima 已提交
423
	public removeOverlayWidget(widget: editorBrowser.IOverlayWidget): void {
A
Alex Dima 已提交
424
		let widgetId = widget.getId();
E
Erich Gamma 已提交
425
		if (this.overlayWidgets.hasOwnProperty(widgetId)) {
A
Alex Dima 已提交
426
			let widgetData = this.overlayWidgets[widgetId];
E
Erich Gamma 已提交
427 428 429 430 431 432 433
			delete this.overlayWidgets[widgetId];
			if (this.hasView) {
				this._view.removeOverlayWidget(widgetData);
			}
		}
	}

J
Johannes Rieken 已提交
434
	public changeViewZones(callback: (accessor: editorBrowser.IViewZoneChangeAccessor) => void): void {
E
Erich Gamma 已提交
435 436 437
		if (!this.hasView) {
			return;
		}
A
Alex Dima 已提交
438
		let hasChanges = this._view.change(callback);
E
Erich Gamma 已提交
439
		if (hasChanges) {
A
Alex Dima 已提交
440
			this._onDidChangeViewZones.fire();
E
Erich Gamma 已提交
441 442 443
		}
	}

A
Alex Dima 已提交
444
	public getWhitespaces(): IEditorWhitespace[] {
E
Erich Gamma 已提交
445 446 447 448 449 450
		if (!this.hasView) {
			return [];
		}
		return this._view.getWhitespaces();
	}

451 452 453 454 455 456 457 458 459
	private _getVerticalOffsetForPosition(modelLineNumber: number, modelColumn: number): number {
		let modelPosition = this.model.validatePosition({
			lineNumber: modelLineNumber,
			column: modelColumn
		});
		let viewPosition = this.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
		return this._view.getVerticalOffsetForViewLineNumber(viewPosition.lineNumber);
	}

E
Erich Gamma 已提交
460 461 462 463
	public getTopForLineNumber(lineNumber: number): number {
		if (!this.hasView) {
			return -1;
		}
464
		return this._getVerticalOffsetForPosition(lineNumber, 1);
E
Erich Gamma 已提交
465 466 467 468 469 470
	}

	public getTopForPosition(lineNumber: number, column: number): number {
		if (!this.hasView) {
			return -1;
		}
471
		return this._getVerticalOffsetForPosition(lineNumber, column);
E
Erich Gamma 已提交
472 473
	}

474 475 476 477
	public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget {
		if (!this.hasView) {
			return null;
		}
A
Alex Dima 已提交
478
		return this._view.getTargetAtClientPoint(clientX, clientY);
479 480
	}

A
Alex Dima 已提交
481
	public getScrolledVisiblePosition(rawPosition: IPosition): { top: number; left: number; height: number; } {
E
Erich Gamma 已提交
482 483 484 485
		if (!this.hasView) {
			return null;
		}

A
Alex Dima 已提交
486 487
		let position = this.model.validatePosition(rawPosition);
		let layoutInfo = this._configuration.editor.layoutInfo;
E
Erich Gamma 已提交
488

489
		let top = this._getVerticalOffsetForPosition(position.lineNumber, position.column) - this._view.getScrollTop();
A
Alex Dima 已提交
490
		let left = this._view.getOffsetForColumn(position.lineNumber, position.column) + layoutInfo.glyphMarginWidth + layoutInfo.lineNumbersWidth + layoutInfo.decorationsWidth - this._view.getScrollLeft();
E
Erich Gamma 已提交
491 492 493 494 495 496 497 498

		return {
			top: top,
			left: left,
			height: this._configuration.editor.lineHeight
		};
	}

J
Johannes Rieken 已提交
499
	public getOffsetForColumn(lineNumber: number, column: number): number {
E
Erich Gamma 已提交
500 501 502
		if (!this.hasView) {
			return -1;
		}
A
Alex Dima 已提交
503
		return this._view.getOffsetForColumn(lineNumber, column);
E
Erich Gamma 已提交
504 505
	}

506 507 508 509
	public render(): void {
		if (!this.hasView) {
			return;
		}
510
		this._view.render(true, false);
511 512
	}

A
Alex Dima 已提交
513
	public setHiddenAreas(ranges: IRange[]): void {
M
Martin Aeschlimann 已提交
514
		if (this.viewModel) {
A
Alex Dima 已提交
515
			this.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r)));
M
Martin Aeschlimann 已提交
516 517 518
		}
	}

J
Johannes Rieken 已提交
519
	public setAriaActiveDescendant(id: string): void {
A
Alex Dima 已提交
520 521 522 523 524 525
		if (!this.hasView) {
			return;
		}
		this._view.setAriaActiveDescendant(id);
	}

J
Johannes Rieken 已提交
526
	public applyFontInfo(target: HTMLElement): void {
527 528 529
		Configuration.applyFontInfoSlow(target, this._configuration.editor.fontInfo);
	}

J
Johannes Rieken 已提交
530
	_attachModel(model: editorCommon.IModel): void {
E
Erich Gamma 已提交
531 532
		this._view = null;

533
		super._attachModel(model);
E
Erich Gamma 已提交
534

535
		if (this._view) {
A
Alex Dima 已提交
536
			this.domElement.appendChild(this._view.domNode.domNode);
E
Erich Gamma 已提交
537

A
Alex Dima 已提交
538 539 540 541 542
			let keys = Object.keys(this.contentWidgets);
			for (let i = 0, len = keys.length; i < len; i++) {
				let widgetId = keys[i];
				this._view.addContentWidget(this.contentWidgets[widgetId]);
			}
E
Erich Gamma 已提交
543

A
Alex Dima 已提交
544 545 546 547 548
			keys = Object.keys(this.overlayWidgets);
			for (let i = 0, len = keys.length; i < len; i++) {
				let widgetId = keys[i];
				this._view.addOverlayWidget(this.overlayWidgets[widgetId]);
			}
E
Erich Gamma 已提交
549

A
Alex Dima 已提交
550 551
			this._view.render(false, true);
			this.hasView = true;
E
Erich Gamma 已提交
552 553 554
		}
	}

555
	protected _enableEmptySelectionClipboard(): boolean {
A
Alex Dima 已提交
556
		return browser.enableEmptySelectionClipboard;
E
Erich Gamma 已提交
557 558
	}

559 560
	protected _createView(): void {
		this._view = new View(
561
			this._commandService,
562 563
			this._configuration,
			this.viewModel,
J
Johannes Rieken 已提交
564
			(source: string, handlerId: string, payload: any) => {
A
Alex Dima 已提交
565 566 567 568 569
				if (!this.cursor) {
					return;
				}
				this.cursor.trigger(source, handlerId, payload);
			}
570
		);
E
Erich Gamma 已提交
571

A
Alex Dima 已提交
572
		const viewEventBus = this._view.getInternalEventBus();
A
Alex Dima 已提交
573

A
Alex Dima 已提交
574 575 576 577 578
		this.listenersToRemove.push(viewEventBus.onDidGainFocus(() => {
			this._onDidFocusEditorText.fire();
			// In IE, the focus is not synchronous, so we give it a little help
			this._onDidFocusEditor.fire();
		}));
A
Alex Dima 已提交
579

A
Alex Dima 已提交
580 581 582
		this.listenersToRemove.push(viewEventBus.onDidScroll((e) => {
			this._onDidScrollChange.fire(e);
		}));
A
Alex Dima 已提交
583

A
Alex Dima 已提交
584 585 586
		this.listenersToRemove.push(viewEventBus.onDidLoseFocus(() => {
			this._onDidBlurEditorText.fire();
		}));
A
Alex Dima 已提交
587

A
Alex Dima 已提交
588 589 590
		this.listenersToRemove.push(viewEventBus.onContextMenu((e) => {
			this._onContextMenu.fire(e);
		}));
A
Alex Dima 已提交
591

A
Alex Dima 已提交
592 593 594
		this.listenersToRemove.push(viewEventBus.onMouseDown((e) => {
			this._onMouseDown.fire(e);
		}));
A
Alex Dima 已提交
595

A
Alex Dima 已提交
596 597 598
		this.listenersToRemove.push(viewEventBus.onMouseUp((e) => {
			this._onMouseUp.fire(e);
		}));
A
Alex Dima 已提交
599

A
Alex Dima 已提交
600 601 602
		this.listenersToRemove.push(viewEventBus.onMouseDrag((e) => {
			this._onMouseDrag.fire(e);
		}));
A
Alex Dima 已提交
603

A
Alex Dima 已提交
604 605 606
		this.listenersToRemove.push(viewEventBus.onMouseDrop((e) => {
			this._onMouseDrop.fire(e);
		}));
A
Alex Dima 已提交
607

A
Alex Dima 已提交
608 609 610
		this.listenersToRemove.push(viewEventBus.onKeyUp((e) => {
			this._onKeyUp.fire(e);
		}));
A
Alex Dima 已提交
611

A
Alex Dima 已提交
612 613 614
		this.listenersToRemove.push(viewEventBus.onMouseMove((e) => {
			this._onMouseMove.fire(e);
		}));
A
Alex Dima 已提交
615

A
Alex Dima 已提交
616 617 618
		this.listenersToRemove.push(viewEventBus.onMouseLeave((e) => {
			this._onMouseLeave.fire(e);
		}));
A
Alex Dima 已提交
619

A
Alex Dima 已提交
620 621
		this.listenersToRemove.push(viewEventBus.onKeyDown((e) => {
			this._onKeyDown.fire(e);
A
Alex Dima 已提交
622
		}));
623
	}
E
Erich Gamma 已提交
624

A
Alex Dima 已提交
625
	protected _detachModel(): editorCommon.IModel {
A
Alex Dima 已提交
626
		let removeDomNode: HTMLElement = null;
E
Erich Gamma 已提交
627 628 629

		if (this._view) {
			this._view.dispose();
A
Alex Dima 已提交
630
			removeDomNode = this._view.domNode.domNode;
E
Erich Gamma 已提交
631 632 633
			this._view = null;
		}

634
		let result = super._detachModel();
E
Erich Gamma 已提交
635 636 637 638 639 640 641

		if (removeDomNode) {
			this.domElement.removeChild(removeDomNode);
		}

		return result;
	}
642 643 644

	// BEGIN decorations

J
Johannes Rieken 已提交
645
	protected _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void {
646 647 648
		this._codeEditorService.registerDecorationType(key, options, parentTypeKey);
	}

J
Johannes Rieken 已提交
649
	protected _removeDecorationType(key: string): void {
650 651 652
		this._codeEditorService.removeDecorationType(key);
	}

J
Johannes Rieken 已提交
653
	protected _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions {
654 655 656 657
		return this._codeEditorService.resolveDecorationOptions(typeKey, writable);
	}

	// END decorations
E
Erich Gamma 已提交
658 659
}

660 661
class CodeEditorWidgetFocusTracker extends Disposable {

662 663
	private _hasFocus: boolean;
	private _domFocusTracker: dom.IFocusTracker;
664 665 666 667

	private _onChange: Emitter<void> = this._register(new Emitter<void>());
	public onChage: Event<void> = this._onChange.event;

J
Johannes Rieken 已提交
668
	constructor(domElement: HTMLElement) {
669 670
		super();

671 672
		this._hasFocus = false;
		this._domFocusTracker = this._register(dom.trackFocus(domElement));
673

674 675 676
		this._domFocusTracker.addFocusListener(() => {
			this._hasFocus = true;
			this._onChange.fire(void 0);
677
		});
678 679 680
		this._domFocusTracker.addBlurListener(() => {
			this._hasFocus = false;
			this._onChange.fire(void 0);
681 682 683 684
		});
	}

	public hasFocus(): boolean {
685
		return this._hasFocus;
686 687 688
	}
}

A
Alex Dima 已提交
689
class OverlayWidget2 implements editorBrowser.IOverlayWidget {
E
Erich Gamma 已提交
690 691

	private _id: string;
A
Alex Dima 已提交
692
	private _position: editorBrowser.IOverlayWidgetPosition;
E
Erich Gamma 已提交
693 694
	private _domNode: HTMLElement;

J
Johannes Rieken 已提交
695
	constructor(id: string, position: editorBrowser.IOverlayWidgetPosition) {
E
Erich Gamma 已提交
696 697 698
		this._id = id;
		this._position = position;
		this._domNode = document.createElement('div');
J
Johannes Rieken 已提交
699
		this._domNode.className = this._id.replace(/\./g, '-').replace(/[^a-z0-9\-]/, '');
E
Erich Gamma 已提交
700 701 702 703 704 705 706 707 708 709
	}

	public getId(): string {
		return this._id;
	}

	public getDomNode(): HTMLElement {
		return this._domNode;
	}

A
Alex Dima 已提交
710
	public getPosition(): editorBrowser.IOverlayWidgetPosition {
E
Erich Gamma 已提交
711 712 713 714 715 716 717 718
		return this._position;
	}
}

export enum EditCursorState {
	EndOfLastEditOperation = 0
}

A
Alex Dima 已提交
719 720
class SingleEditOperation {

721
	range: Range;
A
Alex Dima 已提交
722 723 724
	text: string;
	forceMoveMarkers: boolean;

J
Johannes Rieken 已提交
725
	constructor(source: editorCommon.ISingleEditOperation) {
A
Alex Dima 已提交
726 727 728 729 730 731 732
		this.range = new Range(source.range.startLineNumber, source.range.startColumn, source.range.endLineNumber, source.range.endColumn);
		this.text = source.text;
		this.forceMoveMarkers = source.forceMoveMarkers || false;
	}

}

A
Alex Dima 已提交
733
export class CommandRunner implements editorCommon.ICommand {
E
Erich Gamma 已提交
734

A
Alex Dima 已提交
735
	private _ops: SingleEditOperation[];
E
Erich Gamma 已提交
736 737
	private _editCursorState: EditCursorState;

A
Alex Dima 已提交
738
	constructor(ops: editorCommon.ISingleEditOperation[], editCursorState: EditCursorState) {
A
Alex Dima 已提交
739
		this._ops = ops.map(op => new SingleEditOperation(op));
E
Erich Gamma 已提交
740 741 742
		this._editCursorState = editCursorState;
	}

A
Alex Dima 已提交
743
	public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void {
E
Erich Gamma 已提交
744 745 746 747 748 749 750 751 752 753
		if (this._ops.length === 0) {
			return;
		}

		// Sort them in ascending order by range starts
		this._ops.sort((o1, o2) => {
			return Range.compareRangesUsingStarts(o1.range, o2.range);
		});

		// Merge operations that touch each other
A
Alex Dima 已提交
754 755 756
		let resultOps: editorCommon.ISingleEditOperation[] = [];
		let previousOp = this._ops[0];
		for (let i = 1; i < this._ops.length; i++) {
E
Erich Gamma 已提交
757 758 759 760 761 762 763 764 765 766 767
			if (previousOp.range.endLineNumber === this._ops[i].range.startLineNumber && previousOp.range.endColumn === this._ops[i].range.startColumn) {
				// These operations are one after another and can be merged
				previousOp.range = Range.plusRange(previousOp.range, this._ops[i].range);
				previousOp.text = previousOp.text + this._ops[i].text;
			} else {
				resultOps.push(previousOp);
				previousOp = this._ops[i];
			}
		}
		resultOps.push(previousOp);

A
Alex Dima 已提交
768
		for (let i = 0; i < resultOps.length; i++) {
E
Erich Gamma 已提交
769 770 771 772
			builder.addEditOperation(Range.lift(resultOps[i].range), resultOps[i].text);
		}
	}

773
	public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection {
A
Alex Dima 已提交
774 775
		let inverseEditOperations = helper.getInverseEditOperations();
		let srcRange = inverseEditOperations[inverseEditOperations.length - 1].range;
A
Alex Dima 已提交
776
		return new Selection(
E
Erich Gamma 已提交
777 778 779 780 781 782 783
			srcRange.endLineNumber,
			srcRange.endColumn,
			srcRange.endLineNumber,
			srcRange.endColumn
		);
	}
}