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";
35
import { CoreEditorCommand } from "vs/editor/common/controller/coreCommands";
E
Erich Gamma 已提交
36

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

A
Alex Dima 已提交
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 70
	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 已提交
71

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

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

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

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

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

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

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

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

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

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

122
		this._getActions().forEach((action) => {
123 124 125 126 127 128 129 130 131 132 133 134
			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 已提交
135
			this._actions[internalAction.id] = internalAction;
136
		});
137 138

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

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

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

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

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

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

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

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

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

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

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

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

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

205 206 207 208 209 210 211 212 213 214 215 216 217 218
	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 已提交
219 220 221
	public getCompletelyVisibleLinesRangeInViewport(): Range {
		const viewRange = this._getCompletelyVisibleViewRange();
		return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
222 223
	}

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

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

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

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

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

		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];
296 297 298 299 300
			if (typeof contribution.saveViewState === 'function') {
				contributionsState[id] = contribution.saveViewState();
			}
		}

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

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

B
Benjamin Pasero 已提交
325
			let contributionsState = s.contributionsState || {};
A
Alex Dima 已提交
326 327 328 329
			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];
330 331 332 333
				if (typeof contribution.restoreViewState === 'function') {
					contribution.restoreViewState(contributionsState[id]);
				}
			}
E
Erich Gamma 已提交
334 335 336
		}
	}

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

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

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

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

A
Alex Dima 已提交
357
	public addContentWidget(widget: editorBrowser.IContentWidget): void {
A
Alex Dima 已提交
358
		let widgetData: IContentWidgetData = {
E
Erich Gamma 已提交
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
			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 已提交
374
	public layoutContentWidget(widget: editorBrowser.IContentWidget): void {
A
Alex Dima 已提交
375
		let widgetId = widget.getId();
E
Erich Gamma 已提交
376
		if (this.contentWidgets.hasOwnProperty(widgetId)) {
A
Alex Dima 已提交
377
			let widgetData = this.contentWidgets[widgetId];
E
Erich Gamma 已提交
378 379 380 381 382 383 384
			widgetData.position = widget.getPosition();
			if (this.hasView) {
				this._view.layoutContentWidget(widgetData);
			}
		}
	}

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

A
Alex Dima 已提交
396
	public addOverlayWidget(widget: editorBrowser.IOverlayWidget): void {
A
Alex Dima 已提交
397
		let widgetData: IOverlayWidgetData = {
E
Erich Gamma 已提交
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
			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 已提交
413
	public layoutOverlayWidget(widget: editorBrowser.IOverlayWidget): void {
A
Alex Dima 已提交
414
		let widgetId = widget.getId();
E
Erich Gamma 已提交
415
		if (this.overlayWidgets.hasOwnProperty(widgetId)) {
A
Alex Dima 已提交
416
			let widgetData = this.overlayWidgets[widgetId];
E
Erich Gamma 已提交
417 418 419 420 421 422 423
			widgetData.position = widget.getPosition();
			if (this.hasView) {
				this._view.layoutOverlayWidget(widgetData);
			}
		}
	}

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

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

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

452 453 454 455 456 457 458 459 460
	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 已提交
461 462 463 464
	public getTopForLineNumber(lineNumber: number): number {
		if (!this.hasView) {
			return -1;
		}
465
		return this._getVerticalOffsetForPosition(lineNumber, 1);
E
Erich Gamma 已提交
466 467 468 469 470 471
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

A
Alex Dima 已提交
539 540 541 542 543
			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 已提交
544

A
Alex Dima 已提交
545 546 547 548 549
			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 已提交
550

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

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

560 561
	protected _createView(): void {
		this._view = new View(
562
			this._commandService,
563 564
			this._configuration,
			this.viewModel,
565
			(editorCommand: CoreEditorCommand, args: any) => {
A
Alex Dima 已提交
566 567 568
				if (!this.cursor) {
					return;
				}
569
				editorCommand.runCoreEditorCommand(this.cursor, args);
A
Alex Dima 已提交
570
			}
571
		);
E
Erich Gamma 已提交
572

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

A
Alex Dima 已提交
575 576 577 578 579
		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 已提交
580

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		return result;
	}
643 644 645

	// BEGIN decorations

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

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

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

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

661 662
class CodeEditorWidgetFocusTracker extends Disposable {

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

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

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

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

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

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

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

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

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

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

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

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

export enum EditCursorState {
	EndOfLastEditOperation = 0
}

A
Alex Dima 已提交
720 721
class SingleEditOperation {

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

J
Johannes Rieken 已提交
726
	constructor(source: editorCommon.ISingleEditOperation) {
A
Alex Dima 已提交
727 728 729 730 731 732 733
		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 已提交
734
export class CommandRunner implements editorCommon.ICommand {
E
Erich Gamma 已提交
735

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

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

A
Alex Dima 已提交
744
	public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void {
E
Erich Gamma 已提交
745 746 747 748 749 750 751 752 753 754
		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 已提交
755 756 757
		let resultOps: editorCommon.ISingleEditOperation[] = [];
		let previousOp = this._ops[0];
		for (let i = 1; i < this._ops.length; i++) {
E
Erich Gamma 已提交
758 759 760 761 762 763 764 765 766 767 768
			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 已提交
769
		for (let i = 0; i < resultOps.length; i++) {
E
Erich Gamma 已提交
770 771 772 773
			builder.addEditOperation(Range.lift(resultOps[i].range), resultOps[i].text);
		}
	}

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