viewImpl.ts 35.9 KB
Newer Older
E
Erich Gamma 已提交
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';

J
Johannes Rieken 已提交
7
import { onUnexpectedError } from 'vs/base/common/errors';
8
import { IEventEmitter } from 'vs/base/common/eventEmitter';
J
Johannes Rieken 已提交
9
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
10 11
import * as browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
J
Johannes Rieken 已提交
12 13 14
import { StyleMutator } from 'vs/base/browser/styleMutator';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { Range } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
15
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
16 17 18 19
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { KeyboardHandler, IKeyboardHandlerHelper } from 'vs/editor/browser/controller/keyboardHandler';
import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler';
A
Alex Dima 已提交
20
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
J
Johannes Rieken 已提交
21
import { ViewController, TriggerCursorHandler } from 'vs/editor/browser/view/viewController';
22
import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
J
Johannes Rieken 已提交
23 24 25 26
import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays';
import { LayoutProvider } from 'vs/editor/browser/viewLayout/layoutProvider';
import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets';
import { CurrentLineHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight';
27
import { CurrentLineMarginHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight';
J
Johannes Rieken 已提交
28 29 30 31 32
import { DecorationsOverlay } from 'vs/editor/browser/viewParts/decorations/decorations';
import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin';
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides';
import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines';
33
import { Margin } from 'vs/editor/browser/viewParts/margin/margin';
J
Johannes Rieken 已提交
34
import { LinesDecorationsOverlay } from 'vs/editor/browser/viewParts/linesDecorations/linesDecorations';
35
import { MarginViewLineDecorationsOverlay } from 'vs/editor/browser/viewParts/marginDecorations/marginDecorations';
J
Johannes Rieken 已提交
36 37 38 39 40 41 42 43
import { ViewOverlayWidgets } from 'vs/editor/browser/viewParts/overlayWidgets/overlayWidgets';
import { DecorationsOverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler';
import { OverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/overviewRuler';
import { Rulers } from 'vs/editor/browser/viewParts/rulers/rulers';
import { ScrollDecorationViewPart } from 'vs/editor/browser/viewParts/scrollDecoration/scrollDecoration';
import { SelectionsOverlay } from 'vs/editor/browser/viewParts/selections/selections';
import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors';
import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones';
A
Alex Dima 已提交
44
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
45
import { ViewContext } from 'vs/editor/common/view/viewContext';
J
Johannes Rieken 已提交
46
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
A
Alex Dima 已提交
47
import { RenderingContext } from 'vs/editor/common/view/renderingContext';
J
Johannes Rieken 已提交
48
import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler';
A
Alex Dima 已提交
49
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
50
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
A
Alex Dima 已提交
51
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
A
Alex Dima 已提交
52
import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap';
53
import * as viewEvents from 'vs/editor/common/view/viewEvents';
E
Erich Gamma 已提交
54

A
Alex Dima 已提交
55
export class View extends ViewEventHandler implements editorBrowser.IView, IDisposable {
E
Erich Gamma 已提交
56

J
Johannes Rieken 已提交
57
	private eventDispatcher: ViewEventDispatcher;
E
Erich Gamma 已提交
58

J
Johannes Rieken 已提交
59 60
	private listenersToRemove: IDisposable[];
	private listenersToDispose: IDisposable[];
E
Erich Gamma 已提交
61 62

	private layoutProvider: LayoutProvider;
63
	private _scrollbar: EditorScrollbar;
64
	public _context: ViewContext;
E
Erich Gamma 已提交
65 66 67 68 69 70 71 72

	// The view lines
	private viewLines: ViewLines;

	// These are parts, but we must do some API related calls on them, so we keep a reference
	private viewZones: ViewZones;
	private contentWidgets: ViewContentWidgets;
	private overlayWidgets: ViewOverlayWidgets;
73
	private viewCursors: ViewCursors;
A
Alex Dima 已提交
74
	private viewParts: ViewPart[];
E
Erich Gamma 已提交
75 76 77 78

	private keyboardHandler: KeyboardHandler;
	private pointerHandler: PointerHandler;

A
Alex Dima 已提交
79
	private outgoingEvents: ViewOutgoingEvents;
E
Erich Gamma 已提交
80 81 82 83 84 85 86 87 88 89

	// Dom nodes
	private linesContent: HTMLElement;
	public domNode: HTMLElement;
	public textArea: HTMLTextAreaElement;
	private textAreaCover: HTMLElement;
	private linesContentContainer: HTMLElement;
	private overflowGuardContainer: HTMLElement;

	// Actual mutable state
J
Johannes Rieken 已提交
90
	private hasFocus: boolean;
E
Erich Gamma 已提交
91 92
	private _isDisposed: boolean;

J
Johannes Rieken 已提交
93
	private handleAccumulatedModelEventsTimeout: number;
94
	private accumulatedModelEvents: viewEvents.ViewEvent[];
A
Alex Dima 已提交
95
	private _renderAnimationFrame: IDisposable;
E
Erich Gamma 已提交
96

A
Alex Dima 已提交
97
	constructor(
98
		commandService: ICommandService,
J
Johannes Rieken 已提交
99 100 101
		configuration: Configuration,
		model: IViewModel,
		private triggerCursorHandler: TriggerCursorHandler
A
Alex Dima 已提交
102
	) {
E
Erich Gamma 已提交
103 104 105
		super();
		this._isDisposed = false;
		this._renderAnimationFrame = null;
A
Alex Dima 已提交
106
		this.outgoingEvents = new ViewOutgoingEvents(model);
E
Erich Gamma 已提交
107

A
Alex Dima 已提交
108
		let viewController = new ViewController(model, triggerCursorHandler, this.outgoingEvents, commandService);
E
Erich Gamma 已提交
109 110 111 112 113

		this.listenersToRemove = [];
		this.listenersToDispose = [];

		// The event dispatcher will always go through _renderOnce before dispatching any events
J
Johannes Rieken 已提交
114
		this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback));
E
Erich Gamma 已提交
115 116 117

		// These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.)
		this.linesContent = document.createElement('div');
A
Alex Dima 已提交
118
		this.linesContent.className = editorBrowser.ClassNames.LINES_CONTENT + ' monaco-editor-background';
A
Alex Dima 已提交
119
		this.linesContent.style.position = 'absolute';
E
Erich Gamma 已提交
120
		this.domNode = document.createElement('div');
121
		this.domNode.className = configuration.editor.viewInfo.editorClassName;
E
Erich Gamma 已提交
122 123

		this.overflowGuardContainer = document.createElement('div');
A
Alex Dima 已提交
124
		PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard);
A
Alex Dima 已提交
125
		this.overflowGuardContainer.className = editorBrowser.ClassNames.OVERFLOW_GUARD;
E
Erich Gamma 已提交
126 127 128 129 130

		// The layout provider has such responsibilities as:
		// - scrolling (i.e. viewport / full size) & co.
		// - whitespaces (a.k.a. view zones) management & co.
		// - line heights updating & co.
A
Alex Dima 已提交
131
		this.layoutProvider = new LayoutProvider(configuration, model.getLineCount(), this.eventDispatcher);
E
Erich Gamma 已提交
132

A
Alex Dima 已提交
133
		this._scrollbar = new EditorScrollbar(this.layoutProvider.getScrollable(), configuration, this.linesContent, this.domNode, this.overflowGuardContainer);
134

E
Erich Gamma 已提交
135
		// The view context is passed on to most classes (basically to reduce param. counts in ctors)
136
		this._context = new ViewContext(configuration, model, this.eventDispatcher);
E
Erich Gamma 已提交
137

138
		this.createTextArea();
E
Erich Gamma 已提交
139
		this.createViewParts();
A
Alex Dima 已提交
140
		this._setLayout();
E
Erich Gamma 已提交
141 142

		// Keyboard handler
143
		this.keyboardHandler = new KeyboardHandler(this._context, viewController, this.createKeyboardHandlerHelper());
E
Erich Gamma 已提交
144 145

		// Pointer handler
146
		this.pointerHandler = new PointerHandler(this._context, viewController, this.createPointerHandlerHelper());
E
Erich Gamma 已提交
147 148 149 150 151 152 153 154 155 156

		this.hasFocus = false;
		this.codeEditorHelper = null;

		this.eventDispatcher.addEventHandler(this);

		// The view lines rendering calls model.getLineTokens() that might emit events that its tokens have changed.
		// This delayed processing of incoming model events acts as a guard against undesired/unexpected recursion.
		this.handleAccumulatedModelEventsTimeout = -1;
		this.accumulatedModelEvents = [];
157
		this.listenersToRemove.push(model.addEventListener((events: viewEvents.ViewEvent[]) => {
E
Erich Gamma 已提交
158 159 160 161 162 163 164 165 166 167 168
			this.accumulatedModelEvents = this.accumulatedModelEvents.concat(events);
			if (this.handleAccumulatedModelEventsTimeout === -1) {
				this.handleAccumulatedModelEventsTimeout = setTimeout(() => {
					this.handleAccumulatedModelEventsTimeout = -1;
					this._flushAnyAccumulatedEvents();
				});
			}
		}));
	}

	private _flushAnyAccumulatedEvents(): void {
A
Alex Dima 已提交
169
		let toEmit = this.accumulatedModelEvents;
E
Erich Gamma 已提交
170 171 172 173 174 175
		this.accumulatedModelEvents = [];
		if (toEmit.length > 0) {
			this.eventDispatcher.emitMany(toEmit);
		}
	}

176
	private createTextArea(): void {
E
Erich Gamma 已提交
177 178
		// Text Area (The focus will always be in the textarea when the cursor is blinking)
		this.textArea = <HTMLTextAreaElement>document.createElement('textarea');
A
Alex Dima 已提交
179
		PartFingerprints.write(this.textArea, PartFingerprint.TextArea);
A
Alex Dima 已提交
180
		this.textArea.className = editorBrowser.ClassNames.TEXTAREA;
E
Erich Gamma 已提交
181 182 183 184
		this.textArea.setAttribute('wrap', 'off');
		this.textArea.setAttribute('autocorrect', 'off');
		this.textArea.setAttribute('autocapitalize', 'off');
		this.textArea.setAttribute('spellcheck', 'false');
185
		this.textArea.setAttribute('aria-label', this._context.configuration.editor.viewInfo.ariaLabel);
E
Erich Gamma 已提交
186 187
		this.textArea.setAttribute('role', 'textbox');
		this.textArea.setAttribute('aria-multiline', 'true');
A
Alex Dima 已提交
188 189 190
		this.textArea.setAttribute('aria-haspopup', 'false');
		this.textArea.setAttribute('aria-autocomplete', 'both');

A
Alex Dima 已提交
191 192
		StyleMutator.setTop(this.textArea, 0);
		StyleMutator.setLeft(this.textArea, 0);
E
Erich Gamma 已提交
193

A
Alex Dima 已提交
194 195
		this.listenersToDispose.push(dom.addDisposableListener(this.textArea, 'focus', () => this._setHasFocus(true)));
		this.listenersToDispose.push(dom.addDisposableListener(this.textArea, 'blur', () => this._setHasFocus(false)));
E
Erich Gamma 已提交
196 197 198 199 200

		// On top of the text area, we position a dom node to cover it up
		// (there have been reports of tiny blinking cursors)
		// (in WebKit the textarea is 1px by 1px because it cannot handle input to a 0x0 textarea)
		this.textAreaCover = document.createElement('div');
201
		if (this._context.configuration.editor.viewInfo.glyphMargin) {
A
Alex Dima 已提交
202
			this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.GLYPH_MARGIN + ' ' + editorBrowser.ClassNames.TEXTAREA_COVER;
E
Erich Gamma 已提交
203
		} else {
204
			if (this._context.configuration.editor.viewInfo.renderLineNumbers) {
A
Alex Dima 已提交
205
				this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.LINE_NUMBERS + ' ' + editorBrowser.ClassNames.TEXTAREA_COVER;
E
Erich Gamma 已提交
206
			} else {
A
Alex Dima 已提交
207
				this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.TEXTAREA_COVER;
E
Erich Gamma 已提交
208 209 210
			}
		}
		this.textAreaCover.style.position = 'absolute';
A
Alex Dima 已提交
211 212 213 214
		StyleMutator.setWidth(this.textAreaCover, 1);
		StyleMutator.setHeight(this.textAreaCover, 1);
		StyleMutator.setTop(this.textAreaCover, 0);
		StyleMutator.setLeft(this.textAreaCover, 0);
E
Erich Gamma 已提交
215 216 217 218 219 220
	}

	private createViewParts(): void {
		this.viewParts = [];

		// View Lines
221
		this.viewLines = new ViewLines(this._context, this.layoutProvider);
E
Erich Gamma 已提交
222 223

		// View Zones
224
		this.viewZones = new ViewZones(this._context, this.layoutProvider);
E
Erich Gamma 已提交
225 226 227
		this.viewParts.push(this.viewZones);

		// Decorations overview ruler
A
Alex Dima 已提交
228
		let decorationsOverviewRuler = new DecorationsOverviewRuler(
J
Johannes Rieken 已提交
229 230
			this._context, this.layoutProvider.getScrollHeight(),
			(lineNumber: number) => this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber)
E
Erich Gamma 已提交
231 232 233 234
		);
		this.viewParts.push(decorationsOverviewRuler);


A
Alex Dima 已提交
235
		let scrollDecoration = new ScrollDecorationViewPart(this._context);
E
Erich Gamma 已提交
236 237
		this.viewParts.push(scrollDecoration);

A
Alex Dima 已提交
238
		let contentViewOverlays = new ContentViewOverlays(this._context);
E
Erich Gamma 已提交
239
		this.viewParts.push(contentViewOverlays);
A
Alex Dima 已提交
240
		contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context));
241 242
		contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context));
		contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
243
		contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context));
E
Erich Gamma 已提交
244

A
Alex Dima 已提交
245
		let marginViewOverlays = new MarginViewOverlays(this._context);
E
Erich Gamma 已提交
246
		this.viewParts.push(marginViewOverlays);
A
Alex Dima 已提交
247
		marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context));
248
		marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context));
249
		marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context));
250 251
		marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context));
		marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context));
E
Erich Gamma 已提交
252

A
Alex Dima 已提交
253
		let margin = new Margin(this._context);
254 255 256
		margin.domNode.appendChild(this.viewZones.marginDomNode);
		margin.domNode.appendChild(marginViewOverlays.getDomNode());
		this.viewParts.push(margin);
E
Erich Gamma 已提交
257 258

		// Content widgets
259
		this.contentWidgets = new ViewContentWidgets(this._context, this.domNode);
E
Erich Gamma 已提交
260 261
		this.viewParts.push(this.contentWidgets);

262 263
		this.viewCursors = new ViewCursors(this._context);
		this.viewParts.push(this.viewCursors);
E
Erich Gamma 已提交
264 265

		// Overlay widgets
266
		this.overlayWidgets = new ViewOverlayWidgets(this._context);
E
Erich Gamma 已提交
267 268
		this.viewParts.push(this.overlayWidgets);

A
Alex Dima 已提交
269
		let rulers = new Rulers(this._context);
270 271
		this.viewParts.push(rulers);

272
		let minimap = new Minimap(this._context, this.layoutProvider, this._scrollbar);
A
Alex Dima 已提交
273 274
		this.viewParts.push(minimap);

E
Erich Gamma 已提交
275 276
		// -------------- Wire dom nodes up

277
		this.linesContentContainer = this._scrollbar.getScrollbarContainerDomNode();
E
Erich Gamma 已提交
278 279 280
		this.linesContentContainer.style.position = 'absolute';

		if (decorationsOverviewRuler) {
281
			let overviewRulerData = this._scrollbar.getOverviewRulerLayoutInfo();
E
Erich Gamma 已提交
282 283 284 285
			overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore);
		}

		this.linesContent.appendChild(contentViewOverlays.getDomNode());
286
		this.linesContent.appendChild(rulers.domNode);
E
Erich Gamma 已提交
287
		this.linesContent.appendChild(this.viewZones.domNode);
A
Alex Dima 已提交
288
		this.linesContent.appendChild(this.viewLines.getDomNode());
E
Erich Gamma 已提交
289
		this.linesContent.appendChild(this.contentWidgets.domNode);
290
		this.linesContent.appendChild(this.viewCursors.getDomNode());
291
		this.overflowGuardContainer.appendChild(margin.domNode);
E
Erich Gamma 已提交
292 293 294 295 296
		this.overflowGuardContainer.appendChild(this.linesContentContainer);
		this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode());
		this.overflowGuardContainer.appendChild(this.overlayWidgets.domNode);
		this.overflowGuardContainer.appendChild(this.textArea);
		this.overflowGuardContainer.appendChild(this.textAreaCover);
297
		this.overflowGuardContainer.appendChild(minimap.getDomNode());
E
Erich Gamma 已提交
298
		this.domNode.appendChild(this.overflowGuardContainer);
299
		this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode);
E
Erich Gamma 已提交
300 301 302 303 304 305 306
	}

	private _flushAccumulatedAndRenderNow(): void {
		this._flushAnyAccumulatedEvents();
		this._renderNow();
	}

307
	private createPointerHandlerHelper(): IPointerHandlerHelper {
E
Erich Gamma 已提交
308 309 310 311 312 313 314 315 316 317 318
		return {
			viewDomNode: this.domNode,
			linesContentDomNode: this.linesContent,

			focusTextArea: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.focusTextArea: View is disposed');
				}
				this.focus();
			},

319 320 321 322
			isDirty: (): boolean => {
				return (this.accumulatedModelEvents.length > 0);
			},

E
Erich Gamma 已提交
323 324 325 326 327 328
			getScrollLeft: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getScrollLeft: View is disposed');
				}
				return this.layoutProvider.getScrollLeft();
			},
329 330 331 332 333 334 335
			getScrollTop: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getScrollTop: View is disposed');
				}
				return this.layoutProvider.getScrollTop();
			},

J
Johannes Rieken 已提交
336
			setScrollPosition: (position: editorCommon.INewScrollPosition) => {
E
Erich Gamma 已提交
337
				if (this._isDisposed) {
338
					throw new Error('ViewImpl.pointerHandler.setScrollPosition: View is disposed');
E
Erich Gamma 已提交
339
				}
340
				this.layoutProvider.setScrollPosition(position);
E
Erich Gamma 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
			},

			isAfterLines: (verticalOffset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.isAfterLines: View is disposed');
				}
				return this.layoutProvider.isAfterLines(verticalOffset);
			},
			getLineNumberAtVerticalOffset: (verticalOffset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getLineNumberAtVerticalOffset: View is disposed');
				}
				return this.layoutProvider.getLineNumberAtVerticalOffset(verticalOffset);
			},
			getVerticalOffsetForLineNumber: (lineNumber: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getVerticalOffsetForLineNumber: View is disposed');
				}
				return this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber);
			},
			getWhitespaceAtVerticalOffset: (verticalOffset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getWhitespaceAtVerticalOffset: View is disposed');
				}
				return this.layoutProvider.getWhitespaceAtVerticalOffset(verticalOffset);
			},
367 368 369 370 371 372
			getLastViewCursorsRenderData: () => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getLastViewCursorsRenderData: View is disposed');
				}
				return this.viewCursors.getLastRenderData() || [];
			},
E
Erich Gamma 已提交
373 374 375 376 377 378
			shouldSuppressMouseDownOnViewZone: (viewZoneId: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.shouldSuppressMouseDownOnViewZone: View is disposed');
				}
				return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
			},
379 380 381 382 383 384
			shouldSuppressMouseDownOnWidget: (widgetId: string) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.shouldSuppressMouseDownOnWidget: View is disposed');
				}
				return this.contentWidgets.shouldSuppressMouseDownOnWidget(widgetId);
			},
E
Erich Gamma 已提交
385 386 387 388 389 390 391 392 393 394 395 396 397
			getPositionFromDOMInfo: (spanNode: HTMLElement, offset: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getPositionFromDOMInfo: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
				return this.viewLines.getPositionFromDOMInfo(spanNode, offset);
			},

			visibleRangeForPosition2: (lineNumber: number, column: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.visibleRangeForPosition2: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
A
Alex Dima 已提交
398
				let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), 0);
E
Erich Gamma 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
				if (!visibleRanges) {
					return null;
				}
				return visibleRanges[0];
			},

			getLineWidth: (lineNumber: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.pointerHandler.getLineWidth: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
				return this.viewLines.getLineWidth(lineNumber);
			}
		};
	}

415
	private createKeyboardHandlerHelper(): IKeyboardHandlerHelper {
E
Erich Gamma 已提交
416 417 418 419 420 421 422 423
		return {
			viewDomNode: this.domNode,
			textArea: this.textArea,
			visibleRangeForPositionRelativeToEditor: (lineNumber: number, column: number) => {
				if (this._isDisposed) {
					throw new Error('ViewImpl.keyboardHandler.visibleRangeForPositionRelativeToEditor: View is disposed');
				}
				this._flushAccumulatedAndRenderNow();
A
Alex Dima 已提交
424 425
				let linesViewPortData = this.layoutProvider.getLinesViewportData();
				let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), linesViewPortData.visibleRangesDeltaTop);
E
Erich Gamma 已提交
426 427 428 429
				if (!visibleRanges) {
					return null;
				}
				return visibleRanges[0];
430 431 432
			},
			flushAnyAccumulatedEvents: () => {
				this._flushAnyAccumulatedEvents();
E
Erich Gamma 已提交
433 434 435 436
			}
		};
	}

J
Johannes Rieken 已提交
437
	public setAriaActiveDescendant(id: string): void {
A
Alex Dima 已提交
438 439 440 441 442 443 444 445 446 447 448 449 450
		if (id) {
			this.textArea.setAttribute('role', 'combobox');
			if (this.textArea.getAttribute('aria-activedescendant') !== id) {
				this.textArea.setAttribute('aria-haspopup', 'true');
				this.textArea.setAttribute('aria-activedescendant', id);
			}
		} else {
			this.textArea.setAttribute('role', 'textbox');
			this.textArea.removeAttribute('aria-activedescendant');
			this.textArea.removeAttribute('aria-haspopup');
		}
	}

A
Alex Dima 已提交
451 452
	private _setLayout(): void {
		const layoutInfo = this._context.configuration.editor.layoutInfo;
A
Alex Dima 已提交
453
		if (browser.isChrome) {
A
tslint  
Alex Dima 已提交
454
			/* tslint:disable:no-unused-variable */
E
Erich Gamma 已提交
455 456
			// Access overflowGuardContainer.clientWidth to prevent relayouting bug in Chrome
			// See Bug 19676: Editor misses a layout event
A
Alex Dima 已提交
457
			let clientWidth = this.overflowGuardContainer.clientWidth + 'px';
A
tslint  
Alex Dima 已提交
458
			/* tslint:enable:no-unused-variable */
E
Erich Gamma 已提交
459
		}
A
Alex Dima 已提交
460 461
		StyleMutator.setWidth(this.domNode, layoutInfo.width);
		StyleMutator.setHeight(this.domNode, layoutInfo.height);
E
Erich Gamma 已提交
462

A
Alex Dima 已提交
463 464
		StyleMutator.setWidth(this.overflowGuardContainer, layoutInfo.width);
		StyleMutator.setHeight(this.overflowGuardContainer, layoutInfo.height);
E
Erich Gamma 已提交
465

A
Alex Dima 已提交
466 467
		StyleMutator.setWidth(this.linesContent, 1000000);
		StyleMutator.setHeight(this.linesContent, 1000000);
E
Erich Gamma 已提交
468

A
Alex Dima 已提交
469
		StyleMutator.setLeft(this.linesContentContainer, layoutInfo.contentLeft);
470
		StyleMutator.setWidth(this.linesContentContainer, layoutInfo.contentWidth + layoutInfo.minimapWidth);
A
Alex Dima 已提交
471
		StyleMutator.setHeight(this.linesContentContainer, layoutInfo.contentHeight);
E
Erich Gamma 已提交
472 473

	}
474 475 476

	// --- begin event handlers

A
Alex Dima 已提交
477
	public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
478 479
		if (e.viewInfo.editorClassName) {
			this.domNode.className = this._context.configuration.editor.viewInfo.editorClassName;
E
Erich Gamma 已提交
480
		}
481 482
		if (e.viewInfo.ariaLabel) {
			this.textArea.setAttribute('aria-label', this._context.configuration.editor.viewInfo.ariaLabel);
483
		}
A
Alex Dima 已提交
484 485 486
		if (e.layoutInfo) {
			this._setLayout();
		}
A
Alex Dima 已提交
487
		this.layoutProvider.onConfigurationChanged(e);
E
Erich Gamma 已提交
488 489
		return false;
	}
490 491
	public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
		this.layoutProvider.onFlushed(this._context.model.getLineCount());
492
		return false;
E
Erich Gamma 已提交
493
	}
A
Alex Dima 已提交
494
	public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {
A
Alex Dima 已提交
495 496
		dom.toggleClass(this.domNode, 'focused', e.isFocused);
		if (e.isFocused) {
A
Alex Dima 已提交
497
			this.outgoingEvents.emitViewFocusGained();
E
Erich Gamma 已提交
498
		} else {
A
Alex Dima 已提交
499
			this.outgoingEvents.emitViewFocusLost();
E
Erich Gamma 已提交
500 501 502
		}
		return false;
	}
503 504 505 506 507 508 509 510
	public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean {
		this.layoutProvider.onLinesDeleted(e);
		return false;
	}
	public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean {
		this.layoutProvider.onLinesInserted(e);
		return false;
	}
A
Alex Dima 已提交
511
	public onRevealRangeRequest(e: viewEvents.ViewRevealRangeRequestEvent): boolean {
512 513
		return e.revealCursor ? this.revealCursor() : false;
	}
514 515 516 517
	public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
		this.outgoingEvents.emitScrollChanged(e);
		return false;
	}
A
Alex Dima 已提交
518
	public onScrollRequest(e: editorCommon.ICursorScrollRequestEvent): boolean {
519 520 521 522 523 524
		return e.revealCursor ? this.revealCursor() : false;
	}
	private revealCursor(): boolean {
		this.triggerCursorHandler('revealCursor', editorCommon.Handler.CursorMove, { to: editorCommon.CursorMovePosition.ViewPortIfOutside });
		return false;
	}
525

E
Erich Gamma 已提交
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
	// --- end event handlers

	public dispose(): void {
		this._isDisposed = true;
		if (this.handleAccumulatedModelEventsTimeout !== -1) {
			clearTimeout(this.handleAccumulatedModelEventsTimeout);
			this.handleAccumulatedModelEventsTimeout = -1;
		}
		if (this._renderAnimationFrame !== null) {
			this._renderAnimationFrame.dispose();
			this._renderAnimationFrame = null;
		}
		this.accumulatedModelEvents = [];

		this.eventDispatcher.removeEventHandler(this);
A
Alex Dima 已提交
541
		this.outgoingEvents.dispose();
A
Alex Dima 已提交
542
		this.listenersToRemove = dispose(this.listenersToRemove);
J
Joao Moreno 已提交
543
		this.listenersToDispose = dispose(this.listenersToDispose);
E
Erich Gamma 已提交
544 545 546 547 548 549 550

		this.keyboardHandler.dispose();
		this.pointerHandler.dispose();

		this.viewLines.dispose();

		// Destroy IViewPart second
A
Alex Dima 已提交
551
		for (let i = 0, len = this.viewParts.length; i < len; i++) {
E
Erich Gamma 已提交
552 553 554 555
			this.viewParts[i].dispose();
		}
		this.viewParts = [];

556
		this._scrollbar.dispose();
E
Erich Gamma 已提交
557 558 559 560 561
		this.layoutProvider.dispose();
	}

	// --- begin Code Editor APIs

J
Johannes Rieken 已提交
562
	private codeEditorHelper: editorBrowser.ICodeEditorHelper;
A
Alex Dima 已提交
563
	public getCodeEditorHelper(): editorBrowser.ICodeEditorHelper {
E
Erich Gamma 已提交
564 565
		if (!this.codeEditorHelper) {
			this.codeEditorHelper = {
566
				getScrollWidth: () => {
E
Erich Gamma 已提交
567
					if (this._isDisposed) {
568
						throw new Error('ViewImpl.codeEditorHelper.getScrollWidth: View is disposed');
E
Erich Gamma 已提交
569
					}
570
					return this.layoutProvider.getScrollWidth();
E
Erich Gamma 已提交
571 572 573 574 575 576 577
				},
				getScrollLeft: () => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getScrollLeft: View is disposed');
					}
					return this.layoutProvider.getScrollLeft();
				},
578

E
Erich Gamma 已提交
579 580 581 582 583 584
				getScrollHeight: () => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getScrollHeight: View is disposed');
					}
					return this.layoutProvider.getScrollHeight();
				},
585
				getScrollTop: () => {
E
Erich Gamma 已提交
586
					if (this._isDisposed) {
587
						throw new Error('ViewImpl.codeEditorHelper.getScrollTop: View is disposed');
E
Erich Gamma 已提交
588
					}
589
					return this.layoutProvider.getScrollTop();
E
Erich Gamma 已提交
590
				},
591

J
Johannes Rieken 已提交
592
				setScrollPosition: (position: editorCommon.INewScrollPosition) => {
593 594 595 596 597 598
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.setScrollPosition: View is disposed');
					}
					this.layoutProvider.setScrollPosition(position);
				},

J
Johannes Rieken 已提交
599
				getVerticalOffsetForPosition: (modelLineNumber: number, modelColumn: number) => {
E
Erich Gamma 已提交
600 601 602
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getVerticalOffsetForPosition: View is disposed');
					}
A
Alex Dima 已提交
603
					let modelPosition = this._context.model.validateModelPosition({
E
Erich Gamma 已提交
604 605 606
						lineNumber: modelLineNumber,
						column: modelColumn
					});
A
Alex Dima 已提交
607
					let viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
E
Erich Gamma 已提交
608 609 610 611 612 613
					return this.layoutProvider.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
				},
				delegateVerticalScrollbarMouseDown: (browserEvent: MouseEvent) => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.delegateVerticalScrollbarMouseDown: View is disposed');
					}
614
					this._scrollbar.delegateVerticalScrollbarMouseDown(browserEvent);
E
Erich Gamma 已提交
615 616 617 618 619
				},
				getOffsetForColumn: (modelLineNumber: number, modelColumn: number) => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getOffsetForColumn: View is disposed');
					}
A
Alex Dima 已提交
620
					let modelPosition = this._context.model.validateModelPosition({
E
Erich Gamma 已提交
621 622 623
						lineNumber: modelLineNumber,
						column: modelColumn
					});
A
Alex Dima 已提交
624
					let viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
E
Erich Gamma 已提交
625
					this._flushAccumulatedAndRenderNow();
A
Alex Dima 已提交
626
					let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column), 0);
E
Erich Gamma 已提交
627 628 629 630
					if (!visibleRanges) {
						return -1;
					}
					return visibleRanges[0].left;
631 632 633 634 635 636 637
				},

				getTargetAtClientPoint: (clientX: number, clientY: number): editorBrowser.IMouseTarget => {
					if (this._isDisposed) {
						throw new Error('ViewImpl.codeEditorHelper.getTargetAtClientPoint: View is disposed');
					}
					return this.pointerHandler.getTargetAtClientPoint(clientX, clientY);
E
Erich Gamma 已提交
638
				}
639

E
Erich Gamma 已提交
640 641 642 643 644
			};
		}
		return this.codeEditorHelper;
	}

645
	public getCompletelyVisibleLinesRangeInViewport(): Range {
646
		if (this._isDisposed) {
647
			throw new Error('ViewImpl.getCompletelyVisibleLinesRangeInViewport: View is disposed');
648
		}
649 650 651 652 653 654 655 656 657 658 659

		let partialData = this.layoutProvider.getLinesViewportData();
		let startLineNumber = partialData.startLineNumber === partialData.endLineNumber || partialData.relativeVerticalOffset[0] >= partialData.viewportTop ? partialData.startLineNumber : partialData.startLineNumber + 1;
		let endLineNumber = partialData.relativeVerticalOffset[partialData.relativeVerticalOffset.length - 1] + this._context.configuration.editor.lineHeight <= partialData.viewportTop + partialData.viewportHeight ? partialData.endLineNumber : partialData.endLineNumber - 1;
		let completelyVisibleLinesRange = new Range(
			startLineNumber,
			1,
			endLineNumber,
			this._context.model.getLineMaxColumn(endLineNumber)
		);

A
Alex Dima 已提交
660
		return this._context.model.coordinatesConverter.convertViewRangeToModelRange(completelyVisibleLinesRange);
661 662
	}

A
Alex Dima 已提交
663
	public getInternalEventBus(): IEventEmitter {
E
Erich Gamma 已提交
664 665 666
		if (this._isDisposed) {
			throw new Error('ViewImpl.getInternalEventBus: View is disposed');
		}
A
Alex Dima 已提交
667
		return this.outgoingEvents.getInternalEventBus();
E
Erich Gamma 已提交
668 669
	}

A
Alex Dima 已提交
670
	public saveState(): editorCommon.IViewState {
E
Erich Gamma 已提交
671 672 673 674 675 676
		if (this._isDisposed) {
			throw new Error('ViewImpl.saveState: View is disposed');
		}
		return this.layoutProvider.saveState();
	}

A
Alex Dima 已提交
677
	public restoreState(state: editorCommon.IViewState): void {
E
Erich Gamma 已提交
678 679 680 681 682 683 684 685 686 687 688
		if (this._isDisposed) {
			throw new Error('ViewImpl.restoreState: View is disposed');
		}
		this._flushAnyAccumulatedEvents();
		return this.layoutProvider.restoreState(state);
	}

	public focus(): void {
		if (this._isDisposed) {
			throw new Error('ViewImpl.focus: View is disposed');
		}
689
		this.keyboardHandler.focusTextArea();
E
Erich Gamma 已提交
690 691

		// IE does not trigger the focus event immediately, so we must help it a little bit
692 693 694
		if (document.activeElement === this.textArea) {
			this._setHasFocus(true);
		}
E
Erich Gamma 已提交
695 696 697 698 699 700 701 702 703 704 705 706 707 708
	}

	public isFocused(): boolean {
		if (this._isDisposed) {
			throw new Error('ViewImpl.isFocused: View is disposed');
		}
		return this.hasFocus;
	}

	public createOverviewRuler(cssClassName: string, minimumHeight: number, maximumHeight: number): OverviewRuler {
		if (this._isDisposed) {
			throw new Error('ViewImpl.createOverviewRuler: View is disposed');
		}
		return new OverviewRuler(
J
Johannes Rieken 已提交
709 710
			this._context, cssClassName, this.layoutProvider.getScrollHeight(), minimumHeight, maximumHeight,
			(lineNumber: number) => this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber)
E
Erich Gamma 已提交
711 712 713
		);
	}

A
Alex Dima 已提交
714
	public change(callback: (changeAccessor: editorBrowser.IViewZoneChangeAccessor) => any): boolean {
E
Erich Gamma 已提交
715 716 717
		if (this._isDisposed) {
			throw new Error('ViewImpl.change: View is disposed');
		}
A
Alex Dima 已提交
718
		let zonesHaveChanged = false;
A
Alex Dima 已提交
719

E
Erich Gamma 已提交
720 721 722
		this._renderOnce(() => {
			// Handle events to avoid "adjusting" newly inserted view zones
			this._flushAnyAccumulatedEvents();
A
Alex Dima 已提交
723
			let changeAccessor: editorBrowser.IViewZoneChangeAccessor = {
J
Johannes Rieken 已提交
724
				addZone: (zone: editorBrowser.IViewZone): number => {
E
Erich Gamma 已提交
725 726 727
					zonesHaveChanged = true;
					return this.viewZones.addZone(zone);
				},
J
Johannes Rieken 已提交
728
				removeZone: (id: number): void => {
729 730 731
					if (!id) {
						return;
					}
E
Erich Gamma 已提交
732 733 734
					zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged;
				},
				layoutZone: (id: number): void => {
735 736 737
					if (!id) {
						return;
					}
E
Erich Gamma 已提交
738 739 740 741
					zonesHaveChanged = this.viewZones.layoutZone(id) || zonesHaveChanged;
				}
			};

A
Alex Dima 已提交
742
			safeInvoke1Arg(callback, changeAccessor);
E
Erich Gamma 已提交
743 744 745 746 747 748

			// Invalidate changeAccessor
			changeAccessor.addZone = null;
			changeAccessor.removeZone = null;

			if (zonesHaveChanged) {
A
Alex Dima 已提交
749
				this.layoutProvider.onHeightMaybeChanged();
750
				this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent());
E
Erich Gamma 已提交
751 752 753 754 755
			}
		});
		return zonesHaveChanged;
	}

J
Johannes Rieken 已提交
756
	public getWhitespaces(): editorCommon.IEditorWhitespace[] {
E
Erich Gamma 已提交
757 758 759 760 761 762
		if (this._isDisposed) {
			throw new Error('ViewImpl.getWhitespaces: View is disposed');
		}
		return this.layoutProvider.getWhitespaces();
	}

A
Alex Dima 已提交
763
	public addContentWidget(widgetData: editorBrowser.IContentWidgetData): void {
E
Erich Gamma 已提交
764 765 766
		if (this._isDisposed) {
			throw new Error('ViewImpl.addContentWidget: View is disposed');
		}
A
Alex Dima 已提交
767 768 769
		this.contentWidgets.addWidget(widgetData.widget);
		this.layoutContentWidget(widgetData);
		this._scheduleRender();
E
Erich Gamma 已提交
770 771
	}

A
Alex Dima 已提交
772
	public layoutContentWidget(widgetData: editorBrowser.IContentWidgetData): void {
E
Erich Gamma 已提交
773 774 775
		if (this._isDisposed) {
			throw new Error('ViewImpl.layoutContentWidget: View is disposed');
		}
776

A
Alex Dima 已提交
777 778 779 780
		let newPosition = widgetData.position ? widgetData.position.position : null;
		let newPreference = widgetData.position ? widgetData.position.preference : null;
		this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newPreference);
		this._scheduleRender();
E
Erich Gamma 已提交
781 782
	}

A
Alex Dima 已提交
783
	public removeContentWidget(widgetData: editorBrowser.IContentWidgetData): void {
E
Erich Gamma 已提交
784 785 786
		if (this._isDisposed) {
			throw new Error('ViewImpl.removeContentWidget: View is disposed');
		}
A
Alex Dima 已提交
787 788
		this.contentWidgets.removeWidget(widgetData.widget);
		this._scheduleRender();
E
Erich Gamma 已提交
789 790
	}

A
Alex Dima 已提交
791
	public addOverlayWidget(widgetData: editorBrowser.IOverlayWidgetData): void {
E
Erich Gamma 已提交
792 793 794
		if (this._isDisposed) {
			throw new Error('ViewImpl.addOverlayWidget: View is disposed');
		}
A
Alex Dima 已提交
795 796 797
		this.overlayWidgets.addWidget(widgetData.widget);
		this.layoutOverlayWidget(widgetData);
		this._scheduleRender();
E
Erich Gamma 已提交
798 799
	}

A
Alex Dima 已提交
800
	public layoutOverlayWidget(widgetData: editorBrowser.IOverlayWidgetData): void {
E
Erich Gamma 已提交
801 802 803
		if (this._isDisposed) {
			throw new Error('ViewImpl.layoutOverlayWidget: View is disposed');
		}
804 805 806 807 808 809

		let newPreference = widgetData.position ? widgetData.position.preference : null;
		let shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference);
		if (shouldRender) {
			this._scheduleRender();
		}
E
Erich Gamma 已提交
810 811
	}

A
Alex Dima 已提交
812
	public removeOverlayWidget(widgetData: editorBrowser.IOverlayWidgetData): void {
E
Erich Gamma 已提交
813 814 815
		if (this._isDisposed) {
			throw new Error('ViewImpl.removeOverlayWidget: View is disposed');
		}
A
Alex Dima 已提交
816 817
		this.overlayWidgets.removeWidget(widgetData.widget);
		this._scheduleRender();
E
Erich Gamma 已提交
818 819
	}

J
Johannes Rieken 已提交
820
	public render(now: boolean, everything: boolean): void {
E
Erich Gamma 已提交
821 822 823
		if (this._isDisposed) {
			throw new Error('ViewImpl.render: View is disposed');
		}
824
		if (everything) {
A
Alex Dima 已提交
825 826 827 828 829 830
			// Force everything to render...
			this.viewLines.forceShouldRender();
			for (let i = 0, len = this.viewParts.length; i < len; i++) {
				let viewPart = this.viewParts[i];
				viewPart.forceShouldRender();
			}
831
		}
832 833
		if (now) {
			this._flushAccumulatedAndRenderNow();
A
Alex Dima 已提交
834 835
		} else {
			this._scheduleRender();
836
		}
E
Erich Gamma 已提交
837 838 839 840 841 842 843 844
	}

	// --- end Code Editor APIs

	private _renderOnce(callback: () => any): any {
		if (this._isDisposed) {
			throw new Error('ViewImpl._renderOnce: View is disposed');
		}
A
Alex Dima 已提交
845
		return this.outgoingEvents.deferredEmit(() => {
A
Alex Dima 已提交
846 847
			let r = safeInvokeNoArg(callback);
			this._scheduleRender();
E
Erich Gamma 已提交
848 849 850 851 852 853 854 855 856
			return r;
		});
	}

	private _scheduleRender(): void {
		if (this._isDisposed) {
			throw new Error('ViewImpl._scheduleRender: View is disposed');
		}
		if (this._renderAnimationFrame === null) {
A
Alex Dima 已提交
857
			this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100);
E
Erich Gamma 已提交
858 859 860 861 862 863 864 865 866
		}
	}

	private _onRenderScheduled(): void {
		this._renderAnimationFrame = null;
		this._flushAccumulatedAndRenderNow();
	}

	private _renderNow(): void {
A
Alex Dima 已提交
867
		safeInvokeNoArg(() => this._actualRender());
E
Erich Gamma 已提交
868 869
	}

870
	private _getViewPartsToRender(): ViewPart[] {
J
Johannes Rieken 已提交
871
		let result: ViewPart[] = [];
872 873 874 875 876 877 878 879 880
		for (let i = 0, len = this.viewParts.length; i < len; i++) {
			let viewPart = this.viewParts[i];
			if (viewPart.shouldRender()) {
				result.push(viewPart);
			}
		}
		return result;
	}

A
Alex Dima 已提交
881
	private _actualRender(): void {
A
Alex Dima 已提交
882
		if (!dom.isInDOM(this.domNode)) {
E
Erich Gamma 已提交
883 884 885
			return;
		}

886
		let viewPartsToRender = this._getViewPartsToRender();
E
Erich Gamma 已提交
887

A
Alex Dima 已提交
888 889
		if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) {
			// Nothing to render
890
			this.keyboardHandler.writeToTextArea();
A
Alex Dima 已提交
891 892
			return;
		}
E
Erich Gamma 已提交
893

894
		let partialViewportData = this.layoutProvider.getLinesViewportData();
A
Alex Dima 已提交
895
		this._context.model.setViewport(partialViewportData.startLineNumber, partialViewportData.endLineNumber, partialViewportData.centeredLineNumber);
896 897

		let viewportData = new ViewportData(partialViewportData, this._context.model);
E
Erich Gamma 已提交
898

A
Alex Dima 已提交
899
		if (this.viewLines.shouldRender()) {
900
			this.viewLines.renderText(viewportData, () => {
901 902
				this.keyboardHandler.writeToTextArea();
			});
A
Alex Dima 已提交
903
			this.viewLines.onDidRender();
904 905 906

			// Rendering of viewLines might cause scroll events to occur, so collect view parts to render again
			viewPartsToRender = this._getViewPartsToRender();
907 908
		} else {
			this.keyboardHandler.writeToTextArea();
A
Alex Dima 已提交
909
		}
E
Erich Gamma 已提交
910

911
		let renderingContext = new RenderingContext(this.viewLines, this.layoutProvider, viewportData);
A
Alex Dima 已提交
912

A
Alex Dima 已提交
913 914 915 916 917
		// Render the rest of the parts
		for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
			let viewPart = viewPartsToRender[i];
			viewPart.prepareRender(renderingContext);
		}
A
Alex Dima 已提交
918

A
Alex Dima 已提交
919 920 921 922
		for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
			let viewPart = viewPartsToRender[i];
			viewPart.render(renderingContext);
			viewPart.onDidRender();
E
Erich Gamma 已提交
923 924
		}

925
		// Render the scrollbar
926
		this._scrollbar.renderScrollbar();
E
Erich Gamma 已提交
927 928
	}

J
Johannes Rieken 已提交
929
	private _setHasFocus(newHasFocus: boolean): void {
E
Erich Gamma 已提交
930 931
		if (this.hasFocus !== newHasFocus) {
			this.hasFocus = newHasFocus;
932
			this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(this.hasFocus));
E
Erich Gamma 已提交
933 934 935 936
		}
	}
}

J
Johannes Rieken 已提交
937
function safeInvokeNoArg(func: Function): any {
A
Alex Dima 已提交
938 939
	try {
		return func();
J
Johannes Rieken 已提交
940
	} catch (e) {
A
Alex Dima 已提交
941 942 943 944
		onUnexpectedError(e);
	}
}

J
Johannes Rieken 已提交
945
function safeInvoke1Arg(func: Function, arg1: any): any {
A
Alex Dima 已提交
946 947
	try {
		return func(arg1);
J
Johannes Rieken 已提交
948
	} catch (e) {
A
Alex Dima 已提交
949 950 951
		onUnexpectedError(e);
	}
}