diffEditorWidget.ts 79.7 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.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/diffEditor';
7
import * as nls from 'vs/nls';
A
Alex Dima 已提交
8
import * as dom from 'vs/base/browser/dom';
A
Alex Dima 已提交
9
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
J
Joao Moreno 已提交
10
import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash';
A
Alex Dima 已提交
11
import { RunOnceScheduler, IntervalTimer } from 'vs/base/common/async';
A
Alex Dima 已提交
12 13 14 15 16 17 18
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import * as objects from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
A
Alex Dima 已提交
19
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
A
Alex Dima 已提交
20
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
A
Alex Dima 已提交
21
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
A
Alex Dima 已提交
22
import { DiffReview } from 'vs/editor/browser/widget/diffReview';
23
import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, IComputedEditorOptions, EditorOption, EditorOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions';
A
Alex Dima 已提交
24 25 26 27 28 29 30 31
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
32
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
A
Alex Dima 已提交
33 34
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer';
35
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
A
Alex Dima 已提交
36 37 38 39
import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
40
import { INotificationService } from 'vs/platform/notification/common/notification';
A
Alex Dima 已提交
41 42
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
43 44 45
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
E
Erich Gamma 已提交
46 47

interface IEditorDiffDecorations {
48
	decorations: IModelDeltaDecoration[];
A
Alex Dima 已提交
49
	overviewZones: OverviewRulerZone[];
E
Erich Gamma 已提交
50 51 52
}

interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations {
53
	zones: IMyViewZone[];
E
Erich Gamma 已提交
54 55 56
}

interface IEditorsDiffDecorationsWithZones {
J
Johannes Rieken 已提交
57 58
	original: IEditorDiffDecorationsWithZones;
	modified: IEditorDiffDecorationsWithZones;
E
Erich Gamma 已提交
59 60 61
}

interface IEditorsZones {
62 63
	original: IMyViewZone[];
	modified: IMyViewZone[];
E
Erich Gamma 已提交
64 65 66
}

interface IDiffEditorWidgetStyle {
A
Alex Dima 已提交
67
	getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones;
J
Johannes Rieken 已提交
68
	setEnableSplitViewResizing(enableSplitViewResizing: boolean): void;
69
	applyColors(theme: ITheme): boolean;
E
Erich Gamma 已提交
70 71 72 73 74
	layout(): number;
	dispose(): void;
}

class VisualEditorState {
A
Alex Dima 已提交
75
	private _zones: string[];
76
	private inlineDiffMargins: InlineDiffMargin[];
J
Johannes Rieken 已提交
77 78
	private _zonesMap: { [zoneId: string]: boolean; };
	private _decorations: string[];
E
Erich Gamma 已提交
79

80 81
	constructor(
		private _contextMenuService: IContextMenuService,
82
		private _clipboardService: IClipboardService | null
83
	) {
E
Erich Gamma 已提交
84
		this._zones = [];
85
		this.inlineDiffMargins = [];
E
Erich Gamma 已提交
86 87 88 89
		this._zonesMap = {};
		this._decorations = [];
	}

A
Alex Dima 已提交
90
	public getForeignViewZones(allViewZones: IEditorWhitespace[]): IEditorWhitespace[] {
E
Erich Gamma 已提交
91 92 93
		return allViewZones.filter((z) => !this._zonesMap[String(z.id)]);
	}

A
Alex Dima 已提交
94
	public clean(editor: CodeEditorWidget): void {
E
Erich Gamma 已提交
95 96
		// (1) View zones
		if (this._zones.length > 0) {
J
Johannes Rieken 已提交
97
			editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => {
A
Alex Dima 已提交
98
				for (let i = 0, length = this._zones.length; i < length; i++) {
E
Erich Gamma 已提交
99 100 101 102 103 104 105 106
					viewChangeAccessor.removeZone(this._zones[i]);
				}
			});
		}
		this._zones = [];
		this._zonesMap = {};

		// (2) Model decorations
A
Alex Dima 已提交
107
		this._decorations = editor.deltaDecorations(this._decorations, []);
E
Erich Gamma 已提交
108 109
	}

A
Alex Dima 已提交
110
	public apply(editor: CodeEditorWidget, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones, restoreScrollState: boolean): void {
A
Alex Dima 已提交
111 112 113

		const scrollState = restoreScrollState ? StableEditorScrollState.capture(editor) : null;

E
Erich Gamma 已提交
114
		// view zones
J
Johannes Rieken 已提交
115
		editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => {
A
Alex Dima 已提交
116
			for (let i = 0, length = this._zones.length; i < length; i++) {
E
Erich Gamma 已提交
117 118
				viewChangeAccessor.removeZone(this._zones[i]);
			}
119 120 121
			for (let i = 0, length = this.inlineDiffMargins.length; i < length; i++) {
				this.inlineDiffMargins[i].dispose();
			}
E
Erich Gamma 已提交
122 123
			this._zones = [];
			this._zonesMap = {};
124
			this.inlineDiffMargins = [];
A
Alex Dima 已提交
125
			for (let i = 0, length = newDecorations.zones.length; i < length; i++) {
126 127 128
				const viewZone = <editorBrowser.IViewZone>newDecorations.zones[i];
				viewZone.suppressMouseDown = false;
				let zoneId = viewChangeAccessor.addZone(viewZone);
E
Erich Gamma 已提交
129 130
				this._zones.push(zoneId);
				this._zonesMap[String(zoneId)] = true;
131

132
				if (newDecorations.zones[i].diff && viewZone.marginDomNode && this._clipboardService) {
P
Peng Lyu 已提交
133
					this.inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService));
134
				}
E
Erich Gamma 已提交
135 136 137
			}
		});

A
Alex Dima 已提交
138 139 140 141
		if (scrollState) {
			scrollState.restore(editor);
		}

E
Erich Gamma 已提交
142 143 144 145
		// decorations
		this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations);

		// overview ruler
146 147 148
		if (overviewRuler) {
			overviewRuler.setZones(newDecorations.overviewZones);
		}
E
Erich Gamma 已提交
149 150 151
	}
}

A
Alex Dima 已提交
152
let DIFF_EDITOR_ID = 0;
E
Erich Gamma 已提交
153

A
Alex Dima 已提交
154
export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor {
A
Alex Dima 已提交
155

156
	private static readonly ONE_OVERVIEW_WIDTH = 15;
M
Matt Bierner 已提交
157
	public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = 30;
158
	private static readonly UPDATE_DIFF_DECORATIONS_DELAY = 200; // ms
E
Erich Gamma 已提交
159

A
Alex Dima 已提交
160 161
	private readonly _onDidDispose: Emitter<void> = this._register(new Emitter<void>());
	public readonly onDidDispose: Event<void> = this._onDidDispose.event;
E
Erich Gamma 已提交
162

A
Alex Dima 已提交
163 164 165 166
	private readonly _onDidUpdateDiff: Emitter<void> = this._register(new Emitter<void>());
	public readonly onDidUpdateDiff: Event<void> = this._onDidUpdateDiff.event;

	private readonly id: number;
E
Erich Gamma 已提交
167

168
	private readonly _domElement: HTMLElement;
A
Alex Dima 已提交
169 170 171
	protected readonly _containerDomElement: HTMLElement;
	private readonly _overviewDomElement: HTMLElement;
	private readonly _overviewViewportDomElement: FastDomNode<HTMLElement>;
E
Erich Gamma 已提交
172

J
Johannes Rieken 已提交
173 174
	private _width: number;
	private _height: number;
A
Alex Dima 已提交
175
	private _reviewHeight: number;
A
Alex Dima 已提交
176
	private readonly _measureDomElementToken: IntervalTimer | null;
E
Erich Gamma 已提交
177

A
Alex Dima 已提交
178
	private readonly originalEditor: CodeEditorWidget;
A
Alex Dima 已提交
179
	private readonly _originalDomNode: HTMLElement;
180
	private readonly _originalEditorState: VisualEditorState;
A
Alex Dima 已提交
181
	private _originalOverviewRuler: editorBrowser.IOverviewRuler | null;
E
Erich Gamma 已提交
182

A
Alex Dima 已提交
183
	private readonly modifiedEditor: CodeEditorWidget;
A
Alex Dima 已提交
184
	private readonly _modifiedDomNode: HTMLElement;
185
	private readonly _modifiedEditorState: VisualEditorState;
A
Alex Dima 已提交
186
	private _modifiedOverviewRuler: editorBrowser.IOverviewRuler | null;
E
Erich Gamma 已提交
187

J
Johannes Rieken 已提交
188 189 190
	private _currentlyChangingViewZones: boolean;
	private _beginUpdateDecorationsTimeout: number;
	private _diffComputationToken: number;
A
Alex Dima 已提交
191
	private _diffComputationResult: IDiffComputationResult | null;
E
Erich Gamma 已提交
192

J
Johannes Rieken 已提交
193 194
	private _isVisible: boolean;
	private _isHandlingScrollEvent: boolean;
E
Erich Gamma 已提交
195 196

	private _ignoreTrimWhitespace: boolean;
197
	private _originalIsEditable: boolean;
E
Erich Gamma 已提交
198

J
Johannes Rieken 已提交
199
	private _renderSideBySide: boolean;
200
	private _renderIndicators: boolean;
J
Johannes Rieken 已提交
201
	private _enableSplitViewResizing: boolean;
A
Alex Dima 已提交
202
	private _strategy!: IDiffEditorWidgetStyle;
E
Erich Gamma 已提交
203

204
	private readonly _updateDecorationsRunner: RunOnceScheduler;
E
Erich Gamma 已提交
205

206
	private readonly _editorWorkerService: IEditorWorkerService;
207
	protected _contextKeyService: IContextKeyService;
208 209 210
	private readonly _codeEditorService: ICodeEditorService;
	private readonly _themeService: IThemeService;
	private readonly _notificationService: INotificationService;
211

212
	private readonly _reviewPane: DiffReview;
A
Alex Dima 已提交
213

214
	constructor(
J
Johannes Rieken 已提交
215
		domElement: HTMLElement,
216
		options: IDiffEditorOptions,
217
		clipboardService: IClipboardService | null,
218
		@IEditorWorkerService editorWorkerService: IEditorWorkerService,
219
		@IContextKeyService contextKeyService: IContextKeyService,
220
		@IInstantiationService instantiationService: IInstantiationService,
221
		@ICodeEditorService codeEditorService: ICodeEditorService,
222
		@IThemeService themeService: IThemeService,
223 224
		@INotificationService notificationService: INotificationService,
		@IContextMenuService contextMenuService: IContextMenuService,
225
	) {
E
Erich Gamma 已提交
226
		super();
A
Alex Dima 已提交
227

228
		this._editorWorkerService = editorWorkerService;
229
		this._codeEditorService = codeEditorService;
230
		this._contextKeyService = this._register(contextKeyService.createScoped(domElement));
J
Joao Moreno 已提交
231
		this._contextKeyService.createKey('isInDiffEditor', true);
232
		this._themeService = themeService;
233
		this._notificationService = notificationService;
E
Erich Gamma 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

		this.id = (++DIFF_EDITOR_ID);

		this._domElement = domElement;
		options = options || {};

		// renderSideBySide
		this._renderSideBySide = true;
		if (typeof options.renderSideBySide !== 'undefined') {
			this._renderSideBySide = options.renderSideBySide;
		}

		// ignoreTrimWhitespace
		this._ignoreTrimWhitespace = true;
		if (typeof options.ignoreTrimWhitespace !== 'undefined') {
			this._ignoreTrimWhitespace = options.ignoreTrimWhitespace;
		}

252 253 254 255 256 257
		// renderIndicators
		this._renderIndicators = true;
		if (typeof options.renderIndicators !== 'undefined') {
			this._renderIndicators = options.renderIndicators;
		}

258 259 260 261 262
		this._originalIsEditable = false;
		if (typeof options.originalEditable !== 'undefined') {
			this._originalIsEditable = Boolean(options.originalEditable);
		}

A
Alex Dima 已提交
263
		this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0));
E
Erich Gamma 已提交
264 265

		this._containerDomElement = document.createElement('div');
266
		this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
E
Erich Gamma 已提交
267 268 269 270
		this._containerDomElement.style.position = 'relative';
		this._containerDomElement.style.height = '100%';
		this._domElement.appendChild(this._containerDomElement);

A
Alex Dima 已提交
271 272 273
		this._overviewViewportDomElement = createFastDomNode(document.createElement('div'));
		this._overviewViewportDomElement.setClassName('diffViewport');
		this._overviewViewportDomElement.setPosition('absolute');
E
Erich Gamma 已提交
274 275 276 277 278

		this._overviewDomElement = document.createElement('div');
		this._overviewDomElement.className = 'diffOverview';
		this._overviewDomElement.style.position = 'absolute';

A
Alex Dima 已提交
279
		this._overviewDomElement.appendChild(this._overviewViewportDomElement.domNode);
E
Erich Gamma 已提交
280

281
		this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => {
E
Erich Gamma 已提交
282 283 284 285
			this.modifiedEditor.delegateVerticalScrollbarMouseDown(e);
		}));
		this._containerDomElement.appendChild(this._overviewDomElement);

A
Alex Dima 已提交
286 287 288 289 290 291 292 293 294 295 296 297 298
		// Create left side
		this._originalDomNode = document.createElement('div');
		this._originalDomNode.className = 'editor original';
		this._originalDomNode.style.position = 'absolute';
		this._originalDomNode.style.height = '100%';
		this._containerDomElement.appendChild(this._originalDomNode);

		// Create right side
		this._modifiedDomNode = document.createElement('div');
		this._modifiedDomNode.className = 'editor modified';
		this._modifiedDomNode.style.position = 'absolute';
		this._modifiedDomNode.style.height = '100%';
		this._containerDomElement.appendChild(this._modifiedDomNode);
E
Erich Gamma 已提交
299 300 301 302 303

		this._beginUpdateDecorationsTimeout = -1;
		this._currentlyChangingViewZones = false;
		this._diffComputationToken = 0;

304 305
		this._originalEditorState = new VisualEditorState(contextMenuService, clipboardService);
		this._modifiedEditorState = new VisualEditorState(contextMenuService, clipboardService);
E
Erich Gamma 已提交
306 307 308 309 310 311

		this._isVisible = true;
		this._isHandlingScrollEvent = false;

		this._width = 0;
		this._height = 0;
A
Alex Dima 已提交
312
		this._reviewHeight = 0;
E
Erich Gamma 已提交
313

A
Alex Dima 已提交
314
		this._diffComputationResult = null;
E
Erich Gamma 已提交
315

316 317
		const leftContextKeyService = this._contextKeyService.createScoped();
		leftContextKeyService.createKey('isInDiffLeftEditor', true);
J
Joao Moreno 已提交
318

319 320 321
		const leftServices = new ServiceCollection();
		leftServices.set(IContextKeyService, leftContextKeyService);
		const leftScopedInstantiationService = instantiationService.createChild(leftServices);
J
Joao Moreno 已提交
322

323 324 325 326 327 328 329
		const rightContextKeyService = this._contextKeyService.createScoped();
		rightContextKeyService.createKey('isInDiffRightEditor', true);

		const rightServices = new ServiceCollection();
		rightServices.set(IContextKeyService, rightContextKeyService);
		const rightScopedInstantiationService = instantiationService.createChild(rightServices);

A
Alex Dima 已提交
330 331 332 333 334
		this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService);
		this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService);

		this._originalOverviewRuler = null;
		this._modifiedOverviewRuler = null;
E
Erich Gamma 已提交
335

336 337 338
		this._reviewPane = new DiffReview(this);
		this._containerDomElement.appendChild(this._reviewPane.domNode.domNode);
		this._containerDomElement.appendChild(this._reviewPane.shadow.domNode);
339
		this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode);
340

E
Erich Gamma 已提交
341
		if (options.automaticLayout) {
A
Alex Dima 已提交
342 343 344 345
			this._measureDomElementToken = new IntervalTimer();
			this._measureDomElementToken.cancelAndSet(() => this._measureDomElement(false), 100);
		} else {
			this._measureDomElementToken = null;
E
Erich Gamma 已提交
346 347 348 349 350 351 352 353 354
		}

		// enableSplitViewResizing
		this._enableSplitViewResizing = true;
		if (typeof options.enableSplitViewResizing !== 'undefined') {
			this._enableSplitViewResizing = options.enableSplitViewResizing;
		}

		if (this._renderSideBySide) {
H
Howard Hung 已提交
355
			this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
E
Erich Gamma 已提交
356
		} else {
H
Howard Hung 已提交
357
			this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
E
Erich Gamma 已提交
358
		}
359

360
		this._register(themeService.onThemeChange(t => {
361 362 363
			if (this._strategy && this._strategy.applyColors(t)) {
				this._updateDecorationsRunner.schedule();
			}
364 365
			this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
		}));
366 367

		this._codeEditorService.addDiffEditor(this);
E
Erich Gamma 已提交
368 369
	}

370 371 372 373 374 375 376 377
	public get ignoreTrimWhitespace(): boolean {
		return this._ignoreTrimWhitespace;
	}

	public get renderSideBySide(): boolean {
		return this._renderSideBySide;
	}

378 379 380 381
	public get renderIndicators(): boolean {
		return this._renderIndicators;
	}

A
Alex Dima 已提交
382 383 384 385 386 387 388 389 390 391 392 393
	public hasWidgetFocus(): boolean {
		return dom.isAncestor(document.activeElement, this._domElement);
	}

	public diffReviewNext(): void {
		this._reviewPane.next();
	}

	public diffReviewPrev(): void {
		this._reviewPane.prev();
	}

394
	private static _getClassName(theme: ITheme, renderSideBySide: boolean): string {
A
Alex Dima 已提交
395
		let result = 'monaco-diff-editor monaco-editor-background ';
E
Erich Gamma 已提交
396 397 398
		if (renderSideBySide) {
			result += 'side-by-side ';
		}
399
		result += getThemeTypeSelector(theme.type);
E
Erich Gamma 已提交
400 401 402 403 404 405 406 407
		return result;
	}

	private _recreateOverviewRulers(): void {
		if (this._originalOverviewRuler) {
			this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode());
			this._originalOverviewRuler.dispose();
		}
A
Alex Dima 已提交
408 409 410 411
		if (this.originalEditor.hasModel()) {
			this._originalOverviewRuler = this.originalEditor.createOverviewRuler('original diffOverviewRuler')!;
			this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode());
		}
E
Erich Gamma 已提交
412 413 414 415 416

		if (this._modifiedOverviewRuler) {
			this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode());
			this._modifiedOverviewRuler.dispose();
		}
A
Alex Dima 已提交
417 418 419 420
		if (this.modifiedEditor.hasModel()) {
			this._modifiedOverviewRuler = this.modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!;
			this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode());
		}
E
Erich Gamma 已提交
421 422 423 424

		this._layoutOverviewRulers();
	}

425
	private _createLeftHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget {
A
Alex Dima 已提交
426
		const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable));
A
Alex Dima 已提交
427

A
Alex Dima 已提交
428
		this._register(editor.onDidScrollChange((e) => {
A
Alex Dima 已提交
429 430 431
			if (this._isHandlingScrollEvent) {
				return;
			}
432
			if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) {
A
Alex Dima 已提交
433 434 435 436 437 438 439 440
				return;
			}
			this._isHandlingScrollEvent = true;
			this.modifiedEditor.setScrollPosition({
				scrollLeft: e.scrollLeft,
				scrollTop: e.scrollTop
			});
			this._isHandlingScrollEvent = false;
441 442

			this._layoutOverviewViewport();
A
Alex Dima 已提交
443 444
		}));

A
Alex Dima 已提交
445
		this._register(editor.onDidChangeViewZones(() => {
A
Alex Dima 已提交
446 447 448
			this._onViewZonesChanged();
		}));

A
Alex Dima 已提交
449
		this._register(editor.onDidChangeModelContent(() => {
A
Alex Dima 已提交
450 451 452 453
			if (this._isVisible) {
				this._beginUpdateDecorationsSoon();
			}
		}));
A
Alex Dima 已提交
454 455

		return editor;
E
Erich Gamma 已提交
456 457
	}

458
	private _createRightHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget {
A
Alex Dima 已提交
459
		const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options));
A
Alex Dima 已提交
460

A
Alex Dima 已提交
461
		this._register(editor.onDidScrollChange((e) => {
A
Alex Dima 已提交
462 463 464
			if (this._isHandlingScrollEvent) {
				return;
			}
465
			if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) {
A
Alex Dima 已提交
466 467 468 469 470 471 472 473 474 475 476 477
				return;
			}
			this._isHandlingScrollEvent = true;
			this.originalEditor.setScrollPosition({
				scrollLeft: e.scrollLeft,
				scrollTop: e.scrollTop
			});
			this._isHandlingScrollEvent = false;

			this._layoutOverviewViewport();
		}));

A
Alex Dima 已提交
478
		this._register(editor.onDidChangeViewZones(() => {
A
Alex Dima 已提交
479 480 481
			this._onViewZonesChanged();
		}));

A
Alex Dima 已提交
482
		this._register(editor.onDidChangeConfiguration((e) => {
483
			if (e.hasChanged(EditorOption.fontInfo) && editor.getModel()) {
A
Alex Dima 已提交
484 485 486 487
				this._onViewZonesChanged();
			}
		}));

A
Alex Dima 已提交
488
		this._register(editor.onDidChangeModelContent(() => {
A
Alex Dima 已提交
489 490 491 492
			if (this._isVisible) {
				this._beginUpdateDecorationsSoon();
			}
		}));
A
Alex Dima 已提交
493 494

		return editor;
E
Erich Gamma 已提交
495 496
	}

497
	protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: IEditorOptions): CodeEditorWidget {
498
		return instantiationService.createInstance(CodeEditorWidget, container, options, {});
A
Alex Dima 已提交
499 500
	}

E
Erich Gamma 已提交
501
	public dispose(): void {
502 503
		this._codeEditorService.removeDiffEditor(this);

504 505 506 507 508
		if (this._beginUpdateDecorationsTimeout !== -1) {
			window.clearTimeout(this._beginUpdateDecorationsTimeout);
			this._beginUpdateDecorationsTimeout = -1;
		}

A
Alex Dima 已提交
509 510 511
		if (this._measureDomElementToken) {
			this._measureDomElementToken.dispose();
		}
E
Erich Gamma 已提交
512 513 514

		this._cleanViewZonesAndDecorations();

515 516 517 518 519 520 521 522
		if (this._originalOverviewRuler) {
			this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode());
			this._originalOverviewRuler.dispose();
		}
		if (this._modifiedOverviewRuler) {
			this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode());
			this._modifiedOverviewRuler.dispose();
		}
523 524
		this._overviewDomElement.removeChild(this._overviewViewportDomElement.domNode);
		this._containerDomElement.removeChild(this._overviewDomElement);
E
Erich Gamma 已提交
525

526
		this._containerDomElement.removeChild(this._originalDomNode);
A
Alex Dima 已提交
527
		this.originalEditor.dispose();
528 529

		this._containerDomElement.removeChild(this._modifiedDomNode);
A
Alex Dima 已提交
530
		this.modifiedEditor.dispose();
E
Erich Gamma 已提交
531 532 533

		this._strategy.dispose();

534 535 536
		this._containerDomElement.removeChild(this._reviewPane.domNode.domNode);
		this._containerDomElement.removeChild(this._reviewPane.shadow.domNode);
		this._containerDomElement.removeChild(this._reviewPane.actionBarContainer.domNode);
537 538
		this._reviewPane.dispose();

539 540
		this._domElement.removeChild(this._containerDomElement);

A
Alex Dima 已提交
541
		this._onDidDispose.fire();
A
Alex Dima 已提交
542

E
Erich Gamma 已提交
543 544 545 546 547 548 549 550 551 552
		super.dispose();
	}

	//------------ begin IDiffEditor methods

	public getId(): string {
		return this.getEditorType() + ':' + this.id;
	}

	public getEditorType(): string {
A
Alex Dima 已提交
553
		return editorCommon.EditorType.IDiffEditor;
E
Erich Gamma 已提交
554 555
	}

A
Alex Dima 已提交
556
	public getLineChanges(): editorCommon.ILineChange[] | null {
A
Alex Dima 已提交
557 558 559 560
		if (!this._diffComputationResult) {
			return null;
		}
		return this._diffComputationResult.changes;
E
Erich Gamma 已提交
561 562
	}

A
Alex Dima 已提交
563
	public getOriginalEditor(): editorBrowser.ICodeEditor {
E
Erich Gamma 已提交
564 565 566
		return this.originalEditor;
	}

A
Alex Dima 已提交
567
	public getModifiedEditor(): editorBrowser.ICodeEditor {
E
Erich Gamma 已提交
568 569 570
		return this.modifiedEditor;
	}

571
	public updateOptions(newOptions: IDiffEditorOptions): void {
E
Erich Gamma 已提交
572 573

		// Handle side by side
A
Alex Dima 已提交
574
		let renderSideBySideChanged = false;
E
Erich Gamma 已提交
575 576 577 578 579 580 581
		if (typeof newOptions.renderSideBySide !== 'undefined') {
			if (this._renderSideBySide !== newOptions.renderSideBySide) {
				this._renderSideBySide = newOptions.renderSideBySide;
				renderSideBySideChanged = true;
			}
		}

582 583
		let beginUpdateDecorations = false;

E
Erich Gamma 已提交
584 585 586 587
		if (typeof newOptions.ignoreTrimWhitespace !== 'undefined') {
			if (this._ignoreTrimWhitespace !== newOptions.ignoreTrimWhitespace) {
				this._ignoreTrimWhitespace = newOptions.ignoreTrimWhitespace;
				// Begin comparing
588 589 590 591 592 593 594 595
				beginUpdateDecorations = true;
			}
		}

		if (typeof newOptions.renderIndicators !== 'undefined') {
			if (this._renderIndicators !== newOptions.renderIndicators) {
				this._renderIndicators = newOptions.renderIndicators;
				beginUpdateDecorations = true;
E
Erich Gamma 已提交
596 597 598
			}
		}

599 600 601 602
		if (beginUpdateDecorations) {
			this._beginUpdateDecorations();
		}

603 604 605 606
		if (typeof newOptions.originalEditable !== 'undefined') {
			this._originalIsEditable = Boolean(newOptions.originalEditable);
		}

E
Erich Gamma 已提交
607
		this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions));
608
		this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable));
E
Erich Gamma 已提交
609 610 611 612 613 614 615 616 617 618

		// enableSplitViewResizing
		if (typeof newOptions.enableSplitViewResizing !== 'undefined') {
			this._enableSplitViewResizing = newOptions.enableSplitViewResizing;
		}
		this._strategy.setEnableSplitViewResizing(this._enableSplitViewResizing);

		// renderSideBySide
		if (renderSideBySideChanged) {
			if (this._renderSideBySide) {
H
Howard Hung 已提交
619
				this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
E
Erich Gamma 已提交
620
			} else {
H
Howard Hung 已提交
621
				this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
E
Erich Gamma 已提交
622
			}
623 624
			// Update class name
			this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
E
Erich Gamma 已提交
625 626 627
		}
	}

A
Alex Dima 已提交
628
	public getModel(): editorCommon.IDiffEditorModel {
E
Erich Gamma 已提交
629
		return {
A
Alex Dima 已提交
630 631
			original: this.originalEditor.getModel()!,
			modified: this.modifiedEditor.getModel()!
E
Erich Gamma 已提交
632 633 634
		};
	}

J
Johannes Rieken 已提交
635
	public setModel(model: editorCommon.IDiffEditorModel): void {
E
Erich Gamma 已提交
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
		// Guard us against partial null model
		if (model && (!model.original || !model.modified)) {
			throw new Error(!model.original ? 'DiffEditorWidget.setModel: Original model is null' : 'DiffEditorWidget.setModel: Modified model is null');
		}

		// Remove all view zones & decorations
		this._cleanViewZonesAndDecorations();

		// Update code editor models
		this.originalEditor.setModel(model ? model.original : null);
		this.modifiedEditor.setModel(model ? model.modified : null);
		this._updateDecorationsRunner.cancel();

		if (model) {
			this.originalEditor.setScrollTop(0);
			this.modifiedEditor.setScrollTop(0);
		}

		// Disable any diff computations that will come in
A
Alex Dima 已提交
655
		this._diffComputationResult = null;
E
Erich Gamma 已提交
656 657 658 659 660 661 662 663
		this._diffComputationToken++;

		if (model) {
			this._recreateOverviewRulers();

			// Begin comparing
			this._beginUpdateDecorations();
		} else {
A
Alex Dima 已提交
664
			this._diffComputationResult = null;
E
Erich Gamma 已提交
665 666 667 668 669 670 671 672 673
		}

		this._layoutOverviewViewport();
	}

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

A
Alex Dima 已提交
674
	public getVisibleColumnFromPosition(position: IPosition): number {
E
Erich Gamma 已提交
675 676 677
		return this.modifiedEditor.getVisibleColumnFromPosition(position);
	}

A
Alex Dima 已提交
678
	public getPosition(): Position | null {
E
Erich Gamma 已提交
679 680 681
		return this.modifiedEditor.getPosition();
	}

682 683
	public setPosition(position: IPosition): void {
		this.modifiedEditor.setPosition(position);
E
Erich Gamma 已提交
684 685
	}

686 687
	public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLine(lineNumber, scrollType);
E
Erich Gamma 已提交
688 689
	}

690 691
	public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLineInCenter(lineNumber, scrollType);
E
Erich Gamma 已提交
692 693
	}

694 695
	public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType);
E
Erich Gamma 已提交
696 697
	}

698 699
	public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealPosition(position, scrollType);
E
Erich Gamma 已提交
700 701
	}

702 703
	public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealPositionInCenter(position, scrollType);
E
Erich Gamma 已提交
704 705
	}

706 707
	public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType);
E
Erich Gamma 已提交
708 709
	}

A
Alex Dima 已提交
710
	public getSelection(): Selection | null {
E
Erich Gamma 已提交
711 712 713
		return this.modifiedEditor.getSelection();
	}

A
Alex Dima 已提交
714
	public getSelections(): Selection[] | null {
E
Erich Gamma 已提交
715 716 717
		return this.modifiedEditor.getSelections();
	}

718 719 720 721 722 723
	public setSelection(range: IRange): void;
	public setSelection(editorRange: Range): void;
	public setSelection(selection: ISelection): void;
	public setSelection(editorSelection: Selection): void;
	public setSelection(something: any): void {
		this.modifiedEditor.setSelection(something);
E
Erich Gamma 已提交
724 725
	}

726
	public setSelections(ranges: readonly ISelection[]): void {
E
Erich Gamma 已提交
727 728 729
		this.modifiedEditor.setSelections(ranges);
	}

730 731
	public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType);
E
Erich Gamma 已提交
732 733
	}

734 735
	public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType);
E
Erich Gamma 已提交
736 737
	}

738 739
	public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType);
E
Erich Gamma 已提交
740 741
	}

742 743
	public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void {
		this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal);
E
Erich Gamma 已提交
744 745
	}

746 747
	public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealRangeInCenter(range, scrollType);
E
Erich Gamma 已提交
748 749
	}

750 751
	public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType);
E
Erich Gamma 已提交
752 753
	}

754 755
	public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealRangeAtTop(range, scrollType);
756 757
	}

A
Alex Dima 已提交
758
	public getSupportedActions(): editorCommon.IEditorAction[] {
759 760 761
		return this.modifiedEditor.getSupportedActions();
	}

A
Alex Dima 已提交
762
	public saveViewState(): editorCommon.IDiffEditorViewState {
A
Alex Dima 已提交
763 764
		let originalViewState = this.originalEditor.saveViewState();
		let modifiedViewState = this.modifiedEditor.saveViewState();
E
Erich Gamma 已提交
765 766 767 768 769 770
		return {
			original: originalViewState,
			modified: modifiedViewState
		};
	}

A
Alex Dima 已提交
771
	public restoreViewState(s: editorCommon.IDiffEditorViewState): void {
A
Alex Dima 已提交
772
		if (s.original && s.modified) {
A
Alex Dima 已提交
773
			let diffEditorState = <editorCommon.IDiffEditorViewState>s;
E
Erich Gamma 已提交
774 775 776 777 778
			this.originalEditor.restoreViewState(diffEditorState.original);
			this.modifiedEditor.restoreViewState(diffEditorState.modified);
		}
	}

J
Johannes Rieken 已提交
779
	public layout(dimension?: editorCommon.IDimension): void {
E
Erich Gamma 已提交
780 781 782 783 784 785 786
		this._measureDomElement(false, dimension);
	}

	public focus(): void {
		this.modifiedEditor.focus();
	}

A
Alex Dima 已提交
787 788
	public hasTextFocus(): boolean {
		return this.originalEditor.hasTextFocus() || this.modifiedEditor.hasTextFocus();
E
Erich Gamma 已提交
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
	}

	public onVisible(): void {
		this._isVisible = true;
		this.originalEditor.onVisible();
		this.modifiedEditor.onVisible();
		// Begin comparing
		this._beginUpdateDecorations();
	}

	public onHide(): void {
		this._isVisible = false;
		this.originalEditor.onHide();
		this.modifiedEditor.onHide();
		// Remove all view zones & decorations
		this._cleanViewZonesAndDecorations();
	}

J
Johannes Rieken 已提交
807
	public trigger(source: string, handlerId: string, payload: any): void {
E
Erich Gamma 已提交
808 809 810
		this.modifiedEditor.trigger(source, handlerId, payload);
	}

811
	public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any {
E
Erich Gamma 已提交
812 813 814 815 816 817 818 819 820
		return this.modifiedEditor.changeDecorations(callback);
	}

	//------------ end IDiffEditor methods



	//------------ begin layouting methods

J
Johannes Rieken 已提交
821
	private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void {
822 823 824 825
		dimensions = dimensions || {
			width: this._containerDomElement.clientWidth,
			height: this._containerDomElement.clientHeight
		};
E
Erich Gamma 已提交
826 827

		if (dimensions.width <= 0) {
828 829
			this._width = 0;
			this._height = 0;
A
Alex Dima 已提交
830
			this._reviewHeight = 0;
E
Erich Gamma 已提交
831 832 833 834 835 836 837 838 839 840
			return;
		}

		if (!forceDoLayoutCall && dimensions.width === this._width && dimensions.height === this._height) {
			// Nothing has changed
			return;
		}

		this._width = dimensions.width;
		this._height = dimensions.height;
841
		this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0;
E
Erich Gamma 已提交
842 843 844 845 846

		this._doLayout();
	}

	private _layoutOverviewRulers(): void {
A
Alex Dima 已提交
847 848 849
		if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) {
			return;
		}
A
Alex Dima 已提交
850 851
		let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH;
		let layoutInfo = this.modifiedEditor.getLayoutInfo();
E
Erich Gamma 已提交
852
		if (layoutInfo) {
A
Alex Dima 已提交
853
			this._originalOverviewRuler.setLayout({
E
Erich Gamma 已提交
854 855 856
				top: 0,
				width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
				right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH,
A
Alex Dima 已提交
857
				height: (this._height - this._reviewHeight)
A
Alex Dima 已提交
858 859
			});
			this._modifiedOverviewRuler.setLayout({
E
Erich Gamma 已提交
860 861 862
				top: 0,
				right: 0,
				width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
A
Alex Dima 已提交
863
				height: (this._height - this._reviewHeight)
A
Alex Dima 已提交
864
			});
E
Erich Gamma 已提交
865 866 867 868 869 870 871 872 873 874 875 876
		}
	}

	//------------ end layouting methods

	private _onViewZonesChanged(): void {
		if (this._currentlyChangingViewZones) {
			return;
		}
		this._updateDecorationsRunner.schedule();
	}

A
Alex Dima 已提交
877 878 879 880 881 882 883 884 885
	private _beginUpdateDecorationsSoon(): void {
		// Clear previous timeout if necessary
		if (this._beginUpdateDecorationsTimeout !== -1) {
			window.clearTimeout(this._beginUpdateDecorationsTimeout);
			this._beginUpdateDecorationsTimeout = -1;
		}
		this._beginUpdateDecorationsTimeout = window.setTimeout(() => this._beginUpdateDecorations(), DiffEditorWidget.UPDATE_DIFF_DECORATIONS_DELAY);
	}

886 887
	private _lastOriginalWarning: URI | null = null;
	private _lastModifiedWarning: URI | null = null;
888

A
Alex Dima 已提交
889
	private static _equals(a: URI | null, b: URI | null): boolean {
890 891 892 893 894 895 896 897 898
		if (!a && !b) {
			return true;
		}
		if (!a || !b) {
			return false;
		}
		return (a.toString() === b.toString());
	}

E
Erich Gamma 已提交
899 900
	private _beginUpdateDecorations(): void {
		this._beginUpdateDecorationsTimeout = -1;
901 902 903
		const currentOriginalModel = this.originalEditor.getModel();
		const currentModifiedModel = this.modifiedEditor.getModel();
		if (!currentOriginalModel || !currentModifiedModel) {
E
Erich Gamma 已提交
904 905 906 907 908 909 910
			return;
		}

		// Prevent old diff requests to come if a new request has been initiated
		// The best method would be to call cancel on the Promise, but this is not
		// yet supported, so using tokens for now.
		this._diffComputationToken++;
A
Alex Dima 已提交
911
		let currentToken = this._diffComputationToken;
E
Erich Gamma 已提交
912

913 914 915 916 917 918 919
		if (!this._editorWorkerService.canComputeDiff(currentOriginalModel.uri, currentModifiedModel.uri)) {
			if (
				!DiffEditorWidget._equals(currentOriginalModel.uri, this._lastOriginalWarning)
				|| !DiffEditorWidget._equals(currentModifiedModel.uri, this._lastModifiedWarning)
			) {
				this._lastOriginalWarning = currentOriginalModel.uri;
				this._lastModifiedWarning = currentModifiedModel.uri;
920
				this._notificationService.warn(nls.localize("diff.tooLarge", "Cannot compare files because one file is too large."));
921 922 923 924
			}
			return;
		}

925
		this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace).then((result) => {
926 927 928
			if (currentToken === this._diffComputationToken
				&& currentOriginalModel === this.originalEditor.getModel()
				&& currentModifiedModel === this.modifiedEditor.getModel()
J
Johannes Rieken 已提交
929
			) {
A
Alex Dima 已提交
930
				this._diffComputationResult = result;
931
				this._updateDecorationsRunner.schedule();
A
Alex Dima 已提交
932
				this._onDidUpdateDiff.fire();
933 934 935 936 937
			}
		}, (error) => {
			if (currentToken === this._diffComputationToken
				&& currentOriginalModel === this.originalEditor.getModel()
				&& currentModifiedModel === this.modifiedEditor.getModel()
J
Johannes Rieken 已提交
938
			) {
A
Alex Dima 已提交
939
				this._diffComputationResult = null;
E
Erich Gamma 已提交
940 941
				this._updateDecorationsRunner.schedule();
			}
942
		});
E
Erich Gamma 已提交
943 944 945 946 947 948 949 950
	}

	private _cleanViewZonesAndDecorations(): void {
		this._originalEditorState.clean(this.originalEditor);
		this._modifiedEditorState.clean(this.modifiedEditor);
	}

	private _updateDecorations(): void {
A
Alex Dima 已提交
951
		if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) {
952 953
			return;
		}
A
Alex Dima 已提交
954
		const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []);
E
Erich Gamma 已提交
955

A
Alex Dima 已提交
956 957
		let foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces());
		let foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces());
E
Erich Gamma 已提交
958

959
		let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor);
E
Erich Gamma 已提交
960 961 962

		try {
			this._currentlyChangingViewZones = true;
A
Alex Dima 已提交
963 964
			this._originalEditorState.apply(this.originalEditor, this._originalOverviewRuler, diffDecorations.original, false);
			this._modifiedEditorState.apply(this.modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true);
E
Erich Gamma 已提交
965 966 967 968 969
		} finally {
			this._currentlyChangingViewZones = false;
		}
	}

970 971
	private _adjustOptionsForSubEditor(options: IDiffEditorOptions): IDiffEditorOptions {
		let clonedOptions: IDiffEditorOptions = objects.deepClone(options || {});
A
Alex Dima 已提交
972
		clonedOptions.inDiffEditor = true;
973
		clonedOptions.wordWrap = 'off';
974
		clonedOptions.wordWrapMinified = false;
E
Erich Gamma 已提交
975 976 977
		clonedOptions.automaticLayout = false;
		clonedOptions.scrollbar = clonedOptions.scrollbar || {};
		clonedOptions.scrollbar.vertical = 'visible';
A
Alex Dima 已提交
978
		clonedOptions.folding = false;
979
		clonedOptions.codeLens = false;
J
Joao Moreno 已提交
980
		clonedOptions.fixedOverflowWidgets = true;
981
		// clonedOptions.lineDecorationsWidth = '2ch';
982 983 984
		if (!clonedOptions.minimap) {
			clonedOptions.minimap = {};
		}
985
		clonedOptions.minimap.enabled = false;
E
Erich Gamma 已提交
986 987 988
		return clonedOptions;
	}

989
	private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean): IEditorOptions {
990 991 992
		let result = this._adjustOptionsForSubEditor(options);
		result.readOnly = !isEditable;
		result.overviewRulerLanes = 1;
993
		result.extraEditorClassName = 'original-in-monaco-diff-editor';
994 995 996
		return result;
	}

997
	private _adjustOptionsForRightHandSide(options: IDiffEditorOptions): IEditorOptions {
998
		let result = this._adjustOptionsForSubEditor(options);
A
Alex Dima 已提交
999
		result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
A
Alex Dima 已提交
1000
		result.scrollbar!.verticalHasArrows = false;
1001
		result.extraEditorClassName = 'modified-in-monaco-diff-editor';
1002
		return result;
E
Erich Gamma 已提交
1003 1004
	}

1005 1006 1007 1008
	public doLayout(): void {
		this._measureDomElement(true);
	}

E
Erich Gamma 已提交
1009
	private _doLayout(): void {
A
Alex Dima 已提交
1010
		let splitPoint = this._strategy.layout();
E
Erich Gamma 已提交
1011 1012 1013 1014 1015 1016 1017 1018

		this._originalDomNode.style.width = splitPoint + 'px';
		this._originalDomNode.style.left = '0px';

		this._modifiedDomNode.style.width = (this._width - splitPoint) + 'px';
		this._modifiedDomNode.style.left = splitPoint + 'px';

		this._overviewDomElement.style.top = '0px';
A
Alex Dima 已提交
1019
		this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px';
E
Erich Gamma 已提交
1020 1021
		this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px';
		this._overviewDomElement.style.left = (this._width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px';
A
Alex Dima 已提交
1022 1023
		this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH);
		this._overviewViewportDomElement.setHeight(30);
E
Erich Gamma 已提交
1024

A
Alex Dima 已提交
1025 1026
		this.originalEditor.layout({ width: splitPoint, height: (this._height - this._reviewHeight) });
		this.modifiedEditor.layout({ width: this._width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (this._height - this._reviewHeight) });
E
Erich Gamma 已提交
1027 1028 1029 1030 1031

		if (this._originalOverviewRuler || this._modifiedOverviewRuler) {
			this._layoutOverviewRulers();
		}

A
Alex Dima 已提交
1032 1033
		this._reviewPane.layout(this._height - this._reviewHeight, this._width, this._reviewHeight);

E
Erich Gamma 已提交
1034 1035 1036 1037
		this._layoutOverviewViewport();
	}

	private _layoutOverviewViewport(): void {
A
Alex Dima 已提交
1038
		let layout = this._computeOverviewViewport();
E
Erich Gamma 已提交
1039
		if (!layout) {
A
Alex Dima 已提交
1040 1041
			this._overviewViewportDomElement.setTop(0);
			this._overviewViewportDomElement.setHeight(0);
E
Erich Gamma 已提交
1042
		} else {
A
Alex Dima 已提交
1043 1044
			this._overviewViewportDomElement.setTop(layout.top);
			this._overviewViewportDomElement.setHeight(layout.height);
E
Erich Gamma 已提交
1045 1046 1047
		}
	}

A
Alex Dima 已提交
1048
	private _computeOverviewViewport(): { height: number; top: number; } | null {
A
Alex Dima 已提交
1049
		let layoutInfo = this.modifiedEditor.getLayoutInfo();
E
Erich Gamma 已提交
1050 1051 1052 1053
		if (!layoutInfo) {
			return null;
		}

A
Alex Dima 已提交
1054 1055
		let scrollTop = this.modifiedEditor.getScrollTop();
		let scrollHeight = this.modifiedEditor.getScrollHeight();
E
Erich Gamma 已提交
1056

A
Alex Dima 已提交
1057 1058 1059
		let computedAvailableSize = Math.max(0, layoutInfo.contentHeight);
		let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0);
		let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0;
E
Erich Gamma 已提交
1060

1061
		let computedSliderSize = Math.max(0, Math.floor(layoutInfo.contentHeight * computedRatio));
A
Alex Dima 已提交
1062
		let computedSliderPosition = Math.floor(scrollTop * computedRatio);
E
Erich Gamma 已提交
1063 1064 1065 1066 1067 1068 1069

		return {
			height: computedSliderSize,
			top: computedSliderPosition
		};
	}

J
Johannes Rieken 已提交
1070
	private _createDataSource(): IDataSource {
E
Erich Gamma 已提交
1071 1072 1073 1074 1075 1076
		return {
			getWidth: () => {
				return this._width;
			},

			getHeight: () => {
A
Alex Dima 已提交
1077
				return (this._height - this._reviewHeight);
E
Erich Gamma 已提交
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
			},

			getContainerDomNode: () => {
				return this._containerDomElement;
			},

			relayoutEditors: () => {
				this._doLayout();
			},

			getOriginalEditor: () => {
				return this.originalEditor;
			},

			getModifiedEditor: () => {
				return this.modifiedEditor;
			}
		};
	}

J
Johannes Rieken 已提交
1098
	private _setStrategy(newStrategy: IDiffEditorWidgetStyle): void {
E
Erich Gamma 已提交
1099 1100 1101 1102 1103
		if (this._strategy) {
			this._strategy.dispose();
		}

		this._strategy = newStrategy;
1104
		newStrategy.applyColors(this._themeService.getTheme());
E
Erich Gamma 已提交
1105

A
Alex Dima 已提交
1106
		if (this._diffComputationResult) {
E
Erich Gamma 已提交
1107 1108 1109 1110 1111 1112 1113
			this._updateDecorations();
		}

		// Just do a layout, the strategy might need it
		this._measureDomElement(true);
	}

A
Alex Dima 已提交
1114
	private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange | null {
A
Alex Dima 已提交
1115 1116
		const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []);
		if (lineChanges.length === 0 || lineNumber < startLineNumberExtractor(lineChanges[0])) {
E
Erich Gamma 已提交
1117 1118 1119 1120
			// There are no changes or `lineNumber` is before the first change
			return null;
		}

A
Alex Dima 已提交
1121
		let min = 0, max = lineChanges.length - 1;
E
Erich Gamma 已提交
1122
		while (min < max) {
A
Alex Dima 已提交
1123
			let mid = Math.floor((min + max) / 2);
A
Alex Dima 已提交
1124 1125
			let midStart = startLineNumberExtractor(lineChanges[mid]);
			let midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Number.MAX_VALUE);
E
Erich Gamma 已提交
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136

			if (lineNumber < midStart) {
				max = mid - 1;
			} else if (lineNumber >= midEnd) {
				min = mid + 1;
			} else {
				// HIT!
				min = mid;
				max = mid;
			}
		}
A
Alex Dima 已提交
1137
		return lineChanges[min];
E
Erich Gamma 已提交
1138 1139 1140
	}

	private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number {
A
Alex Dima 已提交
1141
		let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber);
E
Erich Gamma 已提交
1142 1143 1144 1145 1146

		if (!lineChange) {
			return lineNumber;
		}

A
Alex Dima 已提交
1147 1148 1149 1150
		let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0);
		let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0);
		let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0);
		let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0);
E
Erich Gamma 已提交
1151 1152


A
Alex Dima 已提交
1153
		let delta = lineNumber - originalEquivalentLineNumber;
E
Erich Gamma 已提交
1154 1155 1156 1157 1158

		if (delta <= lineChangeOriginalLength) {
			return modifiedEquivalentLineNumber + Math.min(delta, lineChangeModifiedLength);
		}

J
Johannes Rieken 已提交
1159
		return modifiedEquivalentLineNumber + lineChangeModifiedLength - lineChangeOriginalLength + delta;
E
Erich Gamma 已提交
1160 1161 1162
	}

	private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number {
A
Alex Dima 已提交
1163
		let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber);
E
Erich Gamma 已提交
1164 1165 1166 1167 1168

		if (!lineChange) {
			return lineNumber;
		}

A
Alex Dima 已提交
1169 1170 1171 1172
		let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0);
		let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0);
		let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0);
		let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0);
E
Erich Gamma 已提交
1173 1174


A
Alex Dima 已提交
1175
		let delta = lineNumber - modifiedEquivalentLineNumber;
E
Erich Gamma 已提交
1176 1177 1178 1179 1180

		if (delta <= lineChangeModifiedLength) {
			return originalEquivalentLineNumber + Math.min(delta, lineChangeOriginalLength);
		}

J
Johannes Rieken 已提交
1181
		return originalEquivalentLineNumber + lineChangeOriginalLength - lineChangeModifiedLength + delta;
E
Erich Gamma 已提交
1182 1183
	}

A
Alex Dima 已提交
1184
	public getDiffLineInformationForOriginal(lineNumber: number): editorBrowser.IDiffLineInformation | null {
A
Alex Dima 已提交
1185
		if (!this._diffComputationResult) {
E
Erich Gamma 已提交
1186 1187 1188 1189 1190 1191 1192 1193
			// Cannot answer that which I don't know
			return null;
		}
		return {
			equivalentLineNumber: this._getEquivalentLineForOriginalLineNumber(lineNumber)
		};
	}

A
Alex Dima 已提交
1194
	public getDiffLineInformationForModified(lineNumber: number): editorBrowser.IDiffLineInformation | null {
A
Alex Dima 已提交
1195
		if (!this._diffComputationResult) {
E
Erich Gamma 已提交
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
			// Cannot answer that which I don't know
			return null;
		}
		return {
			equivalentLineNumber: this._getEquivalentLineForModifiedLineNumber(lineNumber)
		};
	}
}

interface IDataSource {
	getWidth(): number;
	getHeight(): number;
	getContainerDomNode(): HTMLElement;
	relayoutEditors(): void;

A
Alex Dima 已提交
1211 1212
	getOriginalEditor(): editorBrowser.ICodeEditor;
	getModifiedEditor(): editorBrowser.ICodeEditor;
E
Erich Gamma 已提交
1213 1214
}

A
Alex Dima 已提交
1215
abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWidgetStyle {
E
Erich Gamma 已提交
1216

J
Johannes Rieken 已提交
1217
	_dataSource: IDataSource;
A
Alex Dima 已提交
1218 1219
	_insertColor: Color | null;
	_removeColor: Color | null;
E
Erich Gamma 已提交
1220

J
Johannes Rieken 已提交
1221
	constructor(dataSource: IDataSource) {
A
Alex Dima 已提交
1222
		super();
E
Erich Gamma 已提交
1223
		this._dataSource = dataSource;
A
Alex Dima 已提交
1224 1225
		this._insertColor = null;
		this._removeColor = null;
E
Erich Gamma 已提交
1226 1227
	}

1228 1229 1230 1231 1232 1233 1234 1235 1236
	public applyColors(theme: ITheme): boolean {
		let newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2);
		let newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2);
		let hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor);
		this._insertColor = newInsertColor;
		this._removeColor = newRemoveColor;
		return hasChanges;
	}

A
Alex Dima 已提交
1237
	public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones {
E
Erich Gamma 已提交
1238 1239 1240 1241 1242 1243 1244
		// Get view zones
		modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => {
			return a.afterLineNumber - b.afterLineNumber;
		});
		originalWhitespaces = originalWhitespaces.sort((a, b) => {
			return a.afterLineNumber - b.afterLineNumber;
		});
1245
		let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators);
E
Erich Gamma 已提交
1246 1247

		// Get decorations & overview ruler zones
1248 1249
		let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor);
		let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor);
E
Erich Gamma 已提交
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264

		return {
			original: {
				decorations: originalDecorations.decorations,
				overviewZones: originalDecorations.overviewZones,
				zones: zones.original
			},
			modified: {
				decorations: modifiedDecorations.decorations,
				overviewZones: modifiedDecorations.overviewZones,
				zones: zones.modified
			}
		};
	}

1265 1266 1267
	protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones;
	protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations;
	protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations;
A
Alex Dima 已提交
1268 1269 1270

	public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void;
	public abstract layout(): number;
E
Erich Gamma 已提交
1271 1272
}

A
Alex Dima 已提交
1273
interface IMyViewZone {
E
Erich Gamma 已提交
1274
	shouldNotShrink?: boolean;
A
Alex Dima 已提交
1275 1276 1277 1278 1279
	afterLineNumber: number;
	heightInLines: number;
	minWidthInPx?: number;
	domNode: HTMLElement | null;
	marginDomNode?: HTMLElement | null;
1280
	diff?: IDiffLinesChange;
E
Erich Gamma 已提交
1281 1282 1283 1284 1285
}

class ForeignViewZonesIterator {

	private _index: number;
1286
	private readonly _source: IEditorWhitespace[];
A
Alex Dima 已提交
1287
	public current: IEditorWhitespace | null;
E
Erich Gamma 已提交
1288

A
Alex Dima 已提交
1289
	constructor(source: IEditorWhitespace[]) {
E
Erich Gamma 已提交
1290 1291
		this._source = source;
		this._index = -1;
A
Alex Dima 已提交
1292
		this.current = null;
E
Erich Gamma 已提交
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
		this.advance();
	}

	public advance(): void {
		this._index++;
		if (this._index < this._source.length) {
			this.current = this._source[this._index];
		} else {
			this.current = null;
		}
	}
}

1306
abstract class ViewZonesComputer {
E
Erich Gamma 已提交
1307

1308 1309 1310
	private readonly lineChanges: editorCommon.ILineChange[];
	private readonly originalForeignVZ: IEditorWhitespace[];
	private readonly modifiedForeignVZ: IEditorWhitespace[];
E
Erich Gamma 已提交
1311

A
Alex Dima 已提交
1312
	constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
E
Erich Gamma 已提交
1313 1314 1315 1316 1317 1318
		this.lineChanges = lineChanges;
		this.originalForeignVZ = originalForeignVZ;
		this.modifiedForeignVZ = modifiedForeignVZ;
	}

	public getViewZones(): IEditorsZones {
A
Alex Dima 已提交
1319
		let result: { original: IMyViewZone[]; modified: IMyViewZone[]; } = {
E
Erich Gamma 已提交
1320 1321 1322 1323
			original: [],
			modified: []
		};

A
Alex Dima 已提交
1324 1325 1326 1327 1328 1329 1330 1331
		let lineChangeModifiedLength: number = 0;
		let lineChangeOriginalLength: number = 0;
		let originalEquivalentLineNumber: number = 0;
		let modifiedEquivalentLineNumber: number = 0;
		let originalEndEquivalentLineNumber: number = 0;
		let modifiedEndEquivalentLineNumber: number = 0;

		let sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => {
E
Erich Gamma 已提交
1332 1333 1334
			return a.afterLineNumber - b.afterLineNumber;
		};

A
Alex Dima 已提交
1335
		let addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => {
E
Erich Gamma 已提交
1336
			if (item.domNode === null && destination.length > 0) {
A
Alex Dima 已提交
1337
				let lastItem = destination[destination.length - 1];
E
Erich Gamma 已提交
1338 1339 1340 1341 1342 1343 1344 1345
				if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) {
					lastItem.heightInLines += item.heightInLines;
					return;
				}
			}
			destination.push(item);
		};

A
Alex Dima 已提交
1346 1347
		let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ);
		let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ);
E
Erich Gamma 已提交
1348 1349

		// In order to include foreign view zones after the last line change, the for loop will iterate once more after the end of the `lineChanges` array
A
Alex Dima 已提交
1350 1351
		for (let i = 0, length = this.lineChanges.length; i <= length; i++) {
			let lineChange = (i < length ? this.lineChanges[i] : null);
E
Erich Gamma 已提交
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368

			if (lineChange !== null) {
				originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0);
				modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0);
				lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0);
				lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0);
				originalEndEquivalentLineNumber = Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber);
				modifiedEndEquivalentLineNumber = Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber);
			} else {
				// Increase to very large value to get the producing tests of foreign view zones running
				originalEquivalentLineNumber += 10000000 + lineChangeOriginalLength;
				modifiedEquivalentLineNumber += 10000000 + lineChangeModifiedLength;
				originalEndEquivalentLineNumber = originalEquivalentLineNumber;
				modifiedEndEquivalentLineNumber = modifiedEquivalentLineNumber;
			}

			// Each step produces view zones, and after producing them, we try to cancel them out, to avoid empty-empty view zone cases
A
Alex Dima 已提交
1369 1370
			let stepOriginal: IMyViewZone[] = [];
			let stepModified: IMyViewZone[] = [];
E
Erich Gamma 已提交
1371 1372 1373 1374 1375

			// ---------------------------- PRODUCE VIEW ZONES

			// [PRODUCE] View zone(s) in original-side due to foreign view zone(s) in modified-side
			while (modifiedForeignVZ.current && modifiedForeignVZ.current.afterLineNumber <= modifiedEndEquivalentLineNumber) {
A
Alex Dima 已提交
1376
				let viewZoneLineNumber: number;
E
Erich Gamma 已提交
1377 1378 1379 1380 1381
				if (modifiedForeignVZ.current.afterLineNumber <= modifiedEquivalentLineNumber) {
					viewZoneLineNumber = originalEquivalentLineNumber - modifiedEquivalentLineNumber + modifiedForeignVZ.current.afterLineNumber;
				} else {
					viewZoneLineNumber = originalEndEquivalentLineNumber;
				}
A
Alex Dima 已提交
1382

1383
				let marginDomNode: HTMLDivElement | null = null;
A
Alex Dima 已提交
1384 1385 1386 1387
				if (lineChange && lineChange.modifiedStartLineNumber <= modifiedForeignVZ.current.afterLineNumber && modifiedForeignVZ.current.afterLineNumber <= lineChange.modifiedEndLineNumber) {
					marginDomNode = this._createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion();
				}

E
Erich Gamma 已提交
1388 1389 1390
				stepOriginal.push({
					afterLineNumber: viewZoneLineNumber,
					heightInLines: modifiedForeignVZ.current.heightInLines,
A
Alex Dima 已提交
1391 1392
					domNode: null,
					marginDomNode: marginDomNode
E
Erich Gamma 已提交
1393 1394 1395 1396 1397 1398
				});
				modifiedForeignVZ.advance();
			}

			// [PRODUCE] View zone(s) in modified-side due to foreign view zone(s) in original-side
			while (originalForeignVZ.current && originalForeignVZ.current.afterLineNumber <= originalEndEquivalentLineNumber) {
A
Alex Dima 已提交
1399
				let viewZoneLineNumber: number;
E
Erich Gamma 已提交
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
				if (originalForeignVZ.current.afterLineNumber <= originalEquivalentLineNumber) {
					viewZoneLineNumber = modifiedEquivalentLineNumber - originalEquivalentLineNumber + originalForeignVZ.current.afterLineNumber;
				} else {
					viewZoneLineNumber = modifiedEndEquivalentLineNumber;
				}
				stepModified.push({
					afterLineNumber: viewZoneLineNumber,
					heightInLines: originalForeignVZ.current.heightInLines,
					domNode: null
				});
				originalForeignVZ.advance();
			}

			if (lineChange !== null && isChangeOrInsert(lineChange)) {
A
Alex Dima 已提交
1414
				let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength);
E
Erich Gamma 已提交
1415 1416 1417 1418 1419 1420
				if (r) {
					stepOriginal.push(r);
				}
			}

			if (lineChange !== null && isChangeOrDelete(lineChange)) {
A
Alex Dima 已提交
1421
				let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength);
E
Erich Gamma 已提交
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
				if (r) {
					stepModified.push(r);
				}
			}

			// ---------------------------- END PRODUCE VIEW ZONES


			// ---------------------------- EMIT MINIMAL VIEW ZONES

			// [CANCEL & EMIT] Try to cancel view zones out
A
Alex Dima 已提交
1433 1434
			let stepOriginalIndex = 0;
			let stepModifiedIndex = 0;
E
Erich Gamma 已提交
1435 1436 1437 1438 1439

			stepOriginal = stepOriginal.sort(sortMyViewZones);
			stepModified = stepModified.sort(sortMyViewZones);

			while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) {
A
Alex Dima 已提交
1440 1441
				let original = stepOriginal[stepOriginalIndex];
				let modified = stepModified[stepModifiedIndex];
E
Erich Gamma 已提交
1442

A
Alex Dima 已提交
1443 1444
				let originalDelta = original.afterLineNumber - originalEquivalentLineNumber;
				let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber;
E
Erich Gamma 已提交
1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485

				if (originalDelta < modifiedDelta) {
					addAndCombineIfPossible(result.original, original);
					stepOriginalIndex++;
				} else if (modifiedDelta < originalDelta) {
					addAndCombineIfPossible(result.modified, modified);
					stepModifiedIndex++;
				} else if (original.shouldNotShrink) {
					addAndCombineIfPossible(result.original, original);
					stepOriginalIndex++;
				} else if (modified.shouldNotShrink) {
					addAndCombineIfPossible(result.modified, modified);
					stepModifiedIndex++;
				} else {
					if (original.heightInLines >= modified.heightInLines) {
						// modified view zone gets removed
						original.heightInLines -= modified.heightInLines;
						stepModifiedIndex++;
					} else {
						// original view zone gets removed
						modified.heightInLines -= original.heightInLines;
						stepOriginalIndex++;
					}
				}
			}

			// [EMIT] Remaining original view zones
			while (stepOriginalIndex < stepOriginal.length) {
				addAndCombineIfPossible(result.original, stepOriginal[stepOriginalIndex]);
				stepOriginalIndex++;
			}

			// [EMIT] Remaining modified view zones
			while (stepModifiedIndex < stepModified.length) {
				addAndCombineIfPossible(result.modified, stepModified[stepModifiedIndex]);
				stepModifiedIndex++;
			}

			// ---------------------------- END EMIT MINIMAL VIEW ZONES
		}

A
Alex Dima 已提交
1486 1487 1488 1489 1490 1491
		return {
			original: ViewZonesComputer._ensureDomNodes(result.original),
			modified: ViewZonesComputer._ensureDomNodes(result.modified),
		};
	}

1492
	private static _ensureDomNodes(zones: IMyViewZone[]): IMyViewZone[] {
A
Alex Dima 已提交
1493
		return zones.map((z) => {
E
Erich Gamma 已提交
1494 1495 1496
			if (!z.domNode) {
				z.domNode = createFakeLinesDiv();
			}
1497
			return z;
A
Alex Dima 已提交
1498
		});
E
Erich Gamma 已提交
1499 1500
	}

A
Alex Dima 已提交
1501
	protected abstract _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null;
A
Alex Dima 已提交
1502

A
Alex Dima 已提交
1503
	protected abstract _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null;
E
Erich Gamma 已提交
1504

A
Alex Dima 已提交
1505
	protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null;
E
Erich Gamma 已提交
1506 1507
}

1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562
function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) {
	return {
		range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
		options: options
	};
}

const DECORATIONS = {

	charDelete: ModelDecorationOptions.register({
		className: 'char-delete'
	}),
	charDeleteWholeLine: ModelDecorationOptions.register({
		className: 'char-delete',
		isWholeLine: true
	}),

	charInsert: ModelDecorationOptions.register({
		className: 'char-insert'
	}),
	charInsertWholeLine: ModelDecorationOptions.register({
		className: 'char-insert',
		isWholeLine: true
	}),

	lineInsert: ModelDecorationOptions.register({
		className: 'line-insert',
		marginClassName: 'line-insert',
		isWholeLine: true
	}),
	lineInsertWithSign: ModelDecorationOptions.register({
		className: 'line-insert',
		linesDecorationsClassName: 'insert-sign',
		marginClassName: 'line-insert',
		isWholeLine: true
	}),

	lineDelete: ModelDecorationOptions.register({
		className: 'line-delete',
		marginClassName: 'line-delete',
		isWholeLine: true
	}),
	lineDeleteWithSign: ModelDecorationOptions.register({
		className: 'line-delete',
		linesDecorationsClassName: 'delete-sign',
		marginClassName: 'line-delete',
		isWholeLine: true

	}),
	lineDeleteMargin: ModelDecorationOptions.register({
		marginClassName: 'line-delete',
	})

};

H
Howard Hung 已提交
1563
class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
E
Erich Gamma 已提交
1564

1565
	static readonly MINIMUM_EDITOR_WIDTH = 100;
E
Erich Gamma 已提交
1566 1567

	private _disableSash: boolean;
1568
	private readonly _sash: Sash;
A
Alex Dima 已提交
1569 1570
	private _sashRatio: number | null;
	private _sashPosition: number | null;
A
Alex Dima 已提交
1571
	private _startSashPosition: number | null;
E
Erich Gamma 已提交
1572

J
Johannes Rieken 已提交
1573
	constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) {
E
Erich Gamma 已提交
1574 1575 1576 1577 1578
		super(dataSource);

		this._disableSash = (enableSplitViewResizing === false);
		this._sashRatio = null;
		this._sashPosition = null;
A
Alex Dima 已提交
1579
		this._startSashPosition = null;
A
Alex Dima 已提交
1580
		this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this));
E
Erich Gamma 已提交
1581 1582

		if (this._disableSash) {
J
Joao Moreno 已提交
1583
			this._sash.state = SashState.Disabled;
E
Erich Gamma 已提交
1584 1585
		}

I
isidor 已提交
1586 1587 1588 1589
		this._sash.onDidStart(() => this.onSashDragStart());
		this._sash.onDidChange((e: ISashEvent) => this.onSashDrag(e));
		this._sash.onDidEnd(() => this.onSashDragEnd());
		this._sash.onDidReset(() => this.onSashReset());
E
Erich Gamma 已提交
1590 1591
	}

J
Johannes Rieken 已提交
1592
	public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void {
A
Alex Dima 已提交
1593
		let newDisableSash = (enableSplitViewResizing === false);
E
Erich Gamma 已提交
1594 1595
		if (this._disableSash !== newDisableSash) {
			this._disableSash = newDisableSash;
J
Joao Moreno 已提交
1596
			this._sash.state = this._disableSash ? SashState.Disabled : SashState.Enabled;
E
Erich Gamma 已提交
1597 1598 1599
		}
	}

A
Alex Dima 已提交
1600
	public layout(sashRatio: number | null = this._sashRatio): number {
A
Alex Dima 已提交
1601 1602
		let w = this._dataSource.getWidth();
		let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
E
Erich Gamma 已提交
1603

A
Alex Dima 已提交
1604 1605
		let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth);
		let midPoint = Math.floor(0.5 * contentWidth);
E
Erich Gamma 已提交
1606

A
Alex Dima 已提交
1607
		sashPosition = this._disableSash ? midPoint : sashPosition || midPoint;
E
Erich Gamma 已提交
1608

H
Howard Hung 已提交
1609 1610 1611
		if (contentWidth > DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) {
			if (sashPosition < DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
				sashPosition = DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
E
Erich Gamma 已提交
1612 1613
			}

H
Howard Hung 已提交
1614 1615
			if (sashPosition > contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
				sashPosition = contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
E
Erich Gamma 已提交
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
			}
		} else {
			sashPosition = midPoint;
		}

		if (this._sashPosition !== sashPosition) {
			this._sashPosition = sashPosition;
			this._sash.layout();
		}

		return this._sashPosition;
	}

M
Maxime Quandalle 已提交
1629
	private onSashDragStart(): void {
A
Alex Dima 已提交
1630
		this._startSashPosition = this._sashPosition!;
E
Erich Gamma 已提交
1631 1632
	}

J
Johannes Rieken 已提交
1633
	private onSashDrag(e: ISashEvent): void {
A
Alex Dima 已提交
1634 1635
		let w = this._dataSource.getWidth();
		let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
A
Alex Dima 已提交
1636
		let sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth);
E
Erich Gamma 已提交
1637 1638 1639 1640 1641 1642

		this._sashRatio = sashPosition / contentWidth;

		this._dataSource.relayoutEditors();
	}

M
Maxime Quandalle 已提交
1643 1644 1645 1646 1647 1648 1649
	private onSashDragEnd(): void {
		this._sash.layout();
	}

	private onSashReset(): void {
		this._sashRatio = 0.5;
		this._dataSource.relayoutEditors();
E
Erich Gamma 已提交
1650 1651 1652
		this._sash.layout();
	}

A
Alex Dima 已提交
1653
	public getVerticalSashTop(sash: Sash): number {
E
Erich Gamma 已提交
1654 1655 1656
		return 0;
	}

A
Alex Dima 已提交
1657
	public getVerticalSashLeft(sash: Sash): number {
A
Alex Dima 已提交
1658
		return this._sashPosition!;
E
Erich Gamma 已提交
1659 1660
	}

A
Alex Dima 已提交
1661
	public getVerticalSashHeight(sash: Sash): number {
E
Erich Gamma 已提交
1662 1663 1664
		return this._dataSource.getHeight();
	}

1665
	protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones {
A
Alex Dima 已提交
1666
		let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ);
E
Erich Gamma 已提交
1667 1668 1669
		return c.getViewZones();
	}

1670
	protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
A
Alex Dima 已提交
1671
		const overviewZoneColor = String(this._removeColor);
E
Erich Gamma 已提交
1672

A
Alex Dima 已提交
1673
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1674 1675
			decorations: [],
			overviewZones: []
P
Peng Lyu 已提交
1676
		};
A
Alex Dima 已提交
1677

A
Alex Dima 已提交
1678
		let originalModel = originalEditor.getModel()!;
A
Alex Dima 已提交
1679 1680 1681

		for (let i = 0, length = lineChanges.length; i < length; i++) {
			let lineChange = lineChanges[i];
E
Erich Gamma 已提交
1682 1683

			if (isChangeOrDelete(lineChange)) {
1684 1685
				result.decorations.push({
					range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE),
1686
					options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete)
1687
				});
E
Erich Gamma 已提交
1688
				if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) {
1689
					result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE, DECORATIONS.charDeleteWholeLine));
E
Erich Gamma 已提交
1690 1691
				}

A
Alex Dima 已提交
1692
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1693 1694
					lineChange.originalStartLineNumber,
					lineChange.originalEndLineNumber,
A
Alex Dima 已提交
1695
					overviewZoneColor
A
Alex Dima 已提交
1696
				));
E
Erich Gamma 已提交
1697 1698

				if (lineChange.charChanges) {
A
Alex Dima 已提交
1699 1700
					for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
						let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1701 1702
						if (isChangeOrDelete(charChange)) {
							if (ignoreTrimWhitespace) {
A
Alex Dima 已提交
1703 1704 1705
								for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) {
									let startColumn: number;
									let endColumn: number;
E
Erich Gamma 已提交
1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
									if (lineNumber === charChange.originalStartLineNumber) {
										startColumn = charChange.originalStartColumn;
									} else {
										startColumn = originalModel.getLineFirstNonWhitespaceColumn(lineNumber);
									}
									if (lineNumber === charChange.originalEndLineNumber) {
										endColumn = charChange.originalEndColumn;
									} else {
										endColumn = originalModel.getLineLastNonWhitespaceColumn(lineNumber);
									}
1716
									result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charDelete));
E
Erich Gamma 已提交
1717 1718
								}
							} else {
1719
								result.decorations.push(createDecoration(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn, DECORATIONS.charDelete));
E
Erich Gamma 已提交
1720 1721 1722 1723 1724 1725 1726 1727 1728 1729
							}
						}
					}
				}
			}
		}

		return result;
	}

1730
	protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
A
Alex Dima 已提交
1731
		const overviewZoneColor = String(this._insertColor);
E
Erich Gamma 已提交
1732

A
Alex Dima 已提交
1733
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1734 1735
			decorations: [],
			overviewZones: []
A
Alex Dima 已提交
1736 1737
		};

A
Alex Dima 已提交
1738
		let modifiedModel = modifiedEditor.getModel()!;
A
Alex Dima 已提交
1739 1740 1741

		for (let i = 0, length = lineChanges.length; i < length; i++) {
			let lineChange = lineChanges[i];
E
Erich Gamma 已提交
1742 1743 1744

			if (isChangeOrInsert(lineChange)) {

1745 1746
				result.decorations.push({
					range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE),
1747
					options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert)
1748
				});
E
Erich Gamma 已提交
1749
				if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) {
1750
					result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine));
E
Erich Gamma 已提交
1751
				}
A
Alex Dima 已提交
1752
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1753 1754
					lineChange.modifiedStartLineNumber,
					lineChange.modifiedEndLineNumber,
A
Alex Dima 已提交
1755
					overviewZoneColor
A
Alex Dima 已提交
1756
				));
E
Erich Gamma 已提交
1757 1758

				if (lineChange.charChanges) {
A
Alex Dima 已提交
1759 1760
					for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
						let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1761 1762
						if (isChangeOrInsert(charChange)) {
							if (ignoreTrimWhitespace) {
A
Alex Dima 已提交
1763 1764 1765
								for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) {
									let startColumn: number;
									let endColumn: number;
E
Erich Gamma 已提交
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775
									if (lineNumber === charChange.modifiedStartLineNumber) {
										startColumn = charChange.modifiedStartColumn;
									} else {
										startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber);
									}
									if (lineNumber === charChange.modifiedEndLineNumber) {
										endColumn = charChange.modifiedEndColumn;
									} else {
										endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber);
									}
1776
									result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1777 1778
								}
							} else {
1779
								result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792
							}
						}
					}
				}

			}
		}
		return result;
	}
}

class SideBySideViewZonesComputer extends ViewZonesComputer {

A
Alex Dima 已提交
1793
	constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
E
Erich Gamma 已提交
1794 1795 1796
		super(lineChanges, originalForeignVZ, modifiedForeignVZ);
	}

A
Alex Dima 已提交
1797
	protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null {
A
Alex Dima 已提交
1798 1799 1800
		return null;
	}

A
Alex Dima 已提交
1801
	protected _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
E
Erich Gamma 已提交
1802 1803 1804 1805 1806 1807 1808 1809 1810 1811
		if (lineChangeModifiedLength > lineChangeOriginalLength) {
			return {
				afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber),
				heightInLines: (lineChangeModifiedLength - lineChangeOriginalLength),
				domNode: null
			};
		}
		return null;
	}

A
Alex Dima 已提交
1812
	protected _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
E
Erich Gamma 已提交
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823
		if (lineChangeOriginalLength > lineChangeModifiedLength) {
			return {
				afterLineNumber: Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber),
				heightInLines: (lineChangeOriginalLength - lineChangeModifiedLength),
				domNode: null
			};
		}
		return null;
	}
}

H
Howard Hung 已提交
1824
class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle {
E
Erich Gamma 已提交
1825 1826 1827

	private decorationsLeft: number;

J
Johannes Rieken 已提交
1828
	constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) {
E
Erich Gamma 已提交
1829 1830
		super(dataSource);

1831
		this.decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft;
E
Erich Gamma 已提交
1832

1833
		this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: EditorLayoutInfo) => {
E
Erich Gamma 已提交
1834 1835 1836 1837 1838 1839 1840
			if (this.decorationsLeft !== layoutInfo.decorationsLeft) {
				this.decorationsLeft = layoutInfo.decorationsLeft;
				dataSource.relayoutEditors();
			}
		}));
	}

J
Johannes Rieken 已提交
1841
	public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void {
E
Erich Gamma 已提交
1842 1843 1844
		// Nothing to do..
	}

1845
	protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones {
1846
		let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators);
E
Erich Gamma 已提交
1847 1848 1849
		return computer.getViewZones();
	}

1850
	protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
A
Alex Dima 已提交
1851
		const overviewZoneColor = String(this._removeColor);
A
Alex Dima 已提交
1852

A
Alex Dima 已提交
1853
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1854 1855
			decorations: [],
			overviewZones: []
A
Alex Dima 已提交
1856
		};
E
Erich Gamma 已提交
1857

A
Alex Dima 已提交
1858 1859
		for (let i = 0, length = lineChanges.length; i < length; i++) {
			let lineChange = lineChanges[i];
E
Erich Gamma 已提交
1860 1861 1862

			// Add overview zones in the overview ruler
			if (isChangeOrDelete(lineChange)) {
1863 1864
				result.decorations.push({
					range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE),
1865
					options: DECORATIONS.lineDeleteMargin
1866 1867
				});

A
Alex Dima 已提交
1868
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1869 1870
					lineChange.originalStartLineNumber,
					lineChange.originalEndLineNumber,
A
Alex Dima 已提交
1871
					overviewZoneColor
A
Alex Dima 已提交
1872
				));
E
Erich Gamma 已提交
1873 1874 1875 1876 1877 1878
			}
		}

		return result;
	}

1879
	protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
A
Alex Dima 已提交
1880
		const overviewZoneColor = String(this._insertColor);
E
Erich Gamma 已提交
1881

A
Alex Dima 已提交
1882
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1883 1884
			decorations: [],
			overviewZones: []
A
Alex Dima 已提交
1885 1886
		};

A
Alex Dima 已提交
1887
		let modifiedModel = modifiedEditor.getModel()!;
A
Alex Dima 已提交
1888 1889 1890

		for (let i = 0, length = lineChanges.length; i < length; i++) {
			let lineChange = lineChanges[i];
E
Erich Gamma 已提交
1891 1892 1893

			// Add decorations & overview zones
			if (isChangeOrInsert(lineChange)) {
1894 1895
				result.decorations.push({
					range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE),
1896
					options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert)
1897
				});
E
Erich Gamma 已提交
1898

A
Alex Dima 已提交
1899
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1900 1901
					lineChange.modifiedStartLineNumber,
					lineChange.modifiedEndLineNumber,
A
Alex Dima 已提交
1902
					overviewZoneColor
A
Alex Dima 已提交
1903
				));
E
Erich Gamma 已提交
1904 1905

				if (lineChange.charChanges) {
A
Alex Dima 已提交
1906 1907
					for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
						let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1908 1909
						if (isChangeOrInsert(charChange)) {
							if (ignoreTrimWhitespace) {
A
Alex Dima 已提交
1910 1911 1912
								for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) {
									let startColumn: number;
									let endColumn: number;
E
Erich Gamma 已提交
1913 1914 1915 1916 1917 1918 1919 1920 1921 1922
									if (lineNumber === charChange.modifiedStartLineNumber) {
										startColumn = charChange.modifiedStartColumn;
									} else {
										startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber);
									}
									if (lineNumber === charChange.modifiedEndLineNumber) {
										endColumn = charChange.modifiedEndColumn;
									} else {
										endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber);
									}
1923
									result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1924 1925
								}
							} else {
1926
								result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1927 1928 1929 1930
							}
						}
					}
				} else {
1931
					result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine));
E
Erich Gamma 已提交
1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947
				}
			}
		}

		return result;
	}

	public layout(): number {
		// An editor should not be smaller than 5px
		return Math.max(5, this.decorationsLeft);
	}

}

class InlineViewZonesComputer extends ViewZonesComputer {

1948
	private readonly originalModel: ITextModel;
1949
	private readonly modifiedEditorOptions: IComputedEditorOptions;
1950 1951
	private readonly modifiedEditorTabSize: number;
	private readonly renderIndicators: boolean;
E
Erich Gamma 已提交
1952

A
Alex Dima 已提交
1953
	constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) {
E
Erich Gamma 已提交
1954
		super(lineChanges, originalForeignVZ, modifiedForeignVZ);
A
Alex Dima 已提交
1955
		this.originalModel = originalEditor.getModel()!;
A
Alex Dima 已提交
1956
		this.modifiedEditorOptions = modifiedEditor.getOptions();
A
Alex Dima 已提交
1957
		this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize;
R
rebornix 已提交
1958
		this.renderIndicators = renderIndicators;
E
Erich Gamma 已提交
1959 1960
	}

A
Alex Dima 已提交
1961
	protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null {
A
Alex Dima 已提交
1962 1963 1964 1965 1966
		let result = document.createElement('div');
		result.className = 'inline-added-margin-view-zone';
		return result;
	}

A
Alex Dima 已提交
1967
	protected _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
1968 1969 1970
		let marginDomNode = document.createElement('div');
		marginDomNode.className = 'inline-added-margin-view-zone';

E
Erich Gamma 已提交
1971
		return {
J
Johannes Rieken 已提交
1972
			afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber),
E
Erich Gamma 已提交
1973
			heightInLines: lineChangeModifiedLength,
1974 1975
			domNode: document.createElement('div'),
			marginDomNode: marginDomNode
E
Erich Gamma 已提交
1976 1977 1978
		};
	}

A
Alex Dima 已提交
1979
	protected _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
A
Alex Dima 已提交
1980
		let decorations: InlineDecoration[] = [];
E
Erich Gamma 已提交
1981
		if (lineChange.charChanges) {
A
Alex Dima 已提交
1982 1983
			for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
				let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1984
				if (isChangeOrDelete(charChange)) {
1985 1986
					decorations.push(new InlineDecoration(
						new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn),
1987
						'char-delete',
1988
						InlineDecorationType.Regular
1989
					));
E
Erich Gamma 已提交
1990 1991 1992 1993
				}
			}
		}

1994
		let sb = createStringBuilder(10000);
1995
		let marginHTML: string[] = [];
A
renames  
Alex Dima 已提交
1996
		const layoutInfo = this.modifiedEditorOptions.get(EditorOption.layoutInfo);
1997
		const fontInfo = this.modifiedEditorOptions.get(EditorOption.fontInfo);
A
Alex Dima 已提交
1998 1999
		const lineDecorationsWidth = layoutInfo.decorationsWidth;

A
Alex Dima 已提交
2000
		let lineHeight = this.modifiedEditorOptions.get(EditorOption.lineHeight);
2001
		const typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth;
2002
		let maxCharsPerLine = 0;
2003
		const originalContent: string[] = [];
A
Alex Dima 已提交
2004
		for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) {
2005
			maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorOptions, this.modifiedEditorTabSize, lineNumber, decorations, sb));
2006
			originalContent.push(this.originalModel.getLineContent(lineNumber));
2007

R
rebornix 已提交
2008
			if (this.renderIndicators) {
2009 2010 2011 2012 2013
				let index = lineNumber - lineChange.originalStartLineNumber;
				marginHTML = marginHTML.concat([
					`<div class="delete-sign" style="position:absolute;top:${index * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;"></div>`
				]);
			}
E
Erich Gamma 已提交
2014
		}
A
Alex Dima 已提交
2015
		maxCharsPerLine += this.modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn);
E
Erich Gamma 已提交
2016

A
Alex Dima 已提交
2017
		let domNode = document.createElement('div');
E
Erich Gamma 已提交
2018
		domNode.className = 'view-lines line-delete';
2019
		domNode.innerHTML = sb.build();
2020
		Configuration.applyFontInfoSlow(domNode, fontInfo);
E
Erich Gamma 已提交
2021

2022 2023 2024
		let marginDomNode = document.createElement('div');
		marginDomNode.className = 'inline-deleted-margin-view-zone';
		marginDomNode.innerHTML = marginHTML.join('');
2025
		Configuration.applyFontInfoSlow(marginDomNode, fontInfo);
2026

E
Erich Gamma 已提交
2027 2028 2029 2030
		return {
			shouldNotShrink: true,
			afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1),
			heightInLines: lineChangeOriginalLength,
2031
			minWidthInPx: (maxCharsPerLine * typicalHalfwidthCharacterWidth),
2032
			domNode: domNode,
2033 2034 2035 2036 2037 2038 2039 2040
			marginDomNode: marginDomNode,
			diff: {
				originalStartLineNumber: lineChange.originalStartLineNumber,
				originalEndLineNumber: lineChange.originalEndLineNumber,
				modifiedStartLineNumber: lineChange.modifiedStartLineNumber,
				modifiedEndLineNumber: lineChange.modifiedEndLineNumber,
				originalContent: originalContent
			}
E
Erich Gamma 已提交
2041 2042 2043
		};
	}

2044
	private _renderOriginalLine(count: number, originalModel: ITextModel, options: IComputedEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): number {
2045 2046
		const lineTokens = originalModel.getLineTokens(lineNumber);
		const lineContent = lineTokens.getLineContent();
2047
		const fontInfo = options.get(EditorOption.fontInfo);
A
Alex Dima 已提交
2048

2049
		const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1);
A
Alex Dima 已提交
2050

2051 2052 2053 2054 2055 2056
		sb.appendASCIIString('<div class="view-line');
		if (decorations.length === 0) {
			// No char changes
			sb.appendASCIIString(' char-delete');
		}
		sb.appendASCIIString('" style="top:');
A
Alex Dima 已提交
2057
		sb.appendASCIIString(String(count * options.get(EditorOption.lineHeight)));
2058 2059
		sb.appendASCIIString('px;width:1000000px;">');

2060 2061
		const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, originalModel.mightContainNonBasicASCII());
		const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL());
2062
		const output = renderViewLine(new RenderLineInput(
2063
			(fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)),
2064
			fontInfo.canUseHalfwidthRightwardsArrow,
A
Alex Dima 已提交
2065
			lineContent,
A
Alex Dima 已提交
2066
			false,
2067 2068
			isBasicASCII,
			containsRTL,
2069
			0,
A
Alex Dima 已提交
2070
			lineTokens,
2071
			actualDecorations,
A
Alex Dima 已提交
2072
			tabSize,
2073
			fontInfo.spaceWidth,
A
Alex Dima 已提交
2074 2075 2076
			options.get(EditorOption.stopRenderingLineAfter),
			options.get(EditorOption.renderWhitespace),
			options.get(EditorOption.renderControlCharacters),
2077
			options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF,
2078
			null // Send no selections, original line cannot be selected
2079
		), sb);
E
Erich Gamma 已提交
2080

2081
		sb.appendASCIIString('</div>');
2082 2083 2084

		const absoluteOffsets = output.characterMapping.getAbsoluteOffsets();
		return absoluteOffsets.length > 0 ? absoluteOffsets[absoluteOffsets.length - 1] : 0;
E
Erich Gamma 已提交
2085 2086 2087
	}
}

J
Johannes Rieken 已提交
2088
function isChangeOrInsert(lineChange: editorCommon.IChange): boolean {
E
Erich Gamma 已提交
2089 2090 2091
	return lineChange.modifiedEndLineNumber > 0;
}

J
Johannes Rieken 已提交
2092
function isChangeOrDelete(lineChange: editorCommon.IChange): boolean {
E
Erich Gamma 已提交
2093 2094 2095 2096
	return lineChange.originalEndLineNumber > 0;
}

function createFakeLinesDiv(): HTMLElement {
A
Alex Dima 已提交
2097
	let r = document.createElement('div');
E
Erich Gamma 已提交
2098 2099 2100
	r.className = 'diagonal-fill';
	return r;
}
2101 2102

registerThemingParticipant((theme, collector) => {
2103
	const added = theme.getColor(diffInserted);
2104 2105
	if (added) {
		collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { background-color: ${added}; }`);
A
Alex Dima 已提交
2106
		collector.addRule(`.monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: ${added}; }`);
2107 2108
		collector.addRule(`.monaco-editor .inline-added-margin-view-zone { background-color: ${added}; }`);
	}
2109 2110

	const removed = theme.getColor(diffRemoved);
2111 2112
	if (removed) {
		collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { background-color: ${removed}; }`);
A
Alex Dima 已提交
2113
		collector.addRule(`.monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: ${removed}; }`);
2114 2115
		collector.addRule(`.monaco-editor .inline-deleted-margin-view-zone { background-color: ${removed}; }`);
	}
2116 2117

	const addedOutline = theme.getColor(diffInsertedOutline);
2118
	if (addedOutline) {
2119
		collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${addedOutline}; }`);
2120
	}
2121 2122

	const removedOutline = theme.getColor(diffRemovedOutline);
2123
	if (removedOutline) {
2124
		collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${removedOutline}; }`);
2125
	}
2126 2127

	const shadow = theme.getColor(scrollbarShadow);
2128 2129 2130
	if (shadow) {
		collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px ${shadow}; }`);
	}
2131

M
Matt Bierner 已提交
2132
	const border = theme.getColor(diffBorder);
2133 2134 2135
	if (border) {
		collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { border-left: 1px solid ${border}; }`);
	}
2136
});