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

'use strict';

import 'vs/css!./media/diffEditor';
9
import * as nls from 'vs/nls';
J
Johannes Rieken 已提交
10
import { RunOnceScheduler } from 'vs/base/common/async';
A
Alex Dima 已提交
11
import { Disposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
12 13
import * as objects from 'vs/base/common/objects';
import * as dom from 'vs/base/browser/dom';
14
import Severity from 'vs/base/common/severity';
A
Alex Dima 已提交
15
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
J
Johannes Rieken 已提交
16 17 18
import { ISashEvent, IVerticalSashLayoutProvider, Sash } from 'vs/base/browser/ui/sash/sash';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
19
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
A
Alex Dima 已提交
20
import { Range, IRange } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
21
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
22
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
A
Alex Dima 已提交
23
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
A
Alex Dima 已提交
24
import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
A
Alex Dima 已提交
25
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
J
Johannes Rieken 已提交
26
import { CodeEditor } from 'vs/editor/browser/codeEditor';
A
Alex Dima 已提交
27
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
J
Johannes Rieken 已提交
28
import { Configuration } from 'vs/editor/browser/config/configuration';
A
Alex Dima 已提交
29
import { Position, IPosition } from 'vs/editor/common/core/position';
A
Alex Dima 已提交
30
import { Selection, ISelection } from 'vs/editor/common/core/selection';
31
import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel';
J
Joao Moreno 已提交
32
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
A
Alex Dima 已提交
33
import { ColorId, MetadataConsts, FontStyle } from 'vs/editor/common/modes';
A
Alex Dima 已提交
34
import Event, { Emitter } from 'vs/base/common/event';
35
import * as editorOptions from 'vs/editor/common/config/editorOptions';
36
import { registerThemingParticipant, IThemeService, ITheme, getThemeTypeSelector } from 'vs/platform/theme/common/themeService';
B
Benjamin Pasero 已提交
37 38
import { scrollbarShadow, diffInserted, diffRemoved, defaultInsertColor, defaultRemoveColor, diffInsertedOutline, diffRemovedOutline } from 'vs/platform/theme/common/colorRegistry';
import { Color } from 'vs/base/common/color';
39 40
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
41
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
B
Benjamin Pasero 已提交
42
import { DiffReview } from 'vs/editor/browser/widget/diffReview';
43
import URI from 'vs/base/common/uri';
B
Benjamin Pasero 已提交
44
import { IMessageService } from 'vs/platform/message/common/message';
45
import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder';
E
Erich Gamma 已提交
46 47

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

interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations {
J
Johannes Rieken 已提交
53
	zones: editorBrowser.IViewZone[];
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 {
J
Johannes Rieken 已提交
62 63
	original: editorBrowser.IViewZone[];
	modified: editorBrowser.IViewZone[];
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 {
J
Johannes Rieken 已提交
75 76 77
	private _zones: number[];
	private _zonesMap: { [zoneId: string]: boolean; };
	private _decorations: string[];
E
Erich Gamma 已提交
78 79 80 81 82 83 84

	constructor() {
		this._zones = [];
		this._zonesMap = {};
		this._decorations = [];
	}

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

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

		// (2) Model decorations
		if (this._decorations.length > 0) {
J
Johannes Rieken 已提交
103
			editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => {
E
Erich Gamma 已提交
104 105 106 107 108 109
				changeAccessor.deltaDecorations(this._decorations, []);
			});
		}
		this._decorations = [];
	}

J
Johannes Rieken 已提交
110
	public apply(editor: CodeEditor, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones): void {
E
Erich Gamma 已提交
111
		// view zones
J
Johannes Rieken 已提交
112
		editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => {
A
Alex Dima 已提交
113
			for (let i = 0, length = this._zones.length; i < length; i++) {
E
Erich Gamma 已提交
114 115 116 117
				viewChangeAccessor.removeZone(this._zones[i]);
			}
			this._zones = [];
			this._zonesMap = {};
A
Alex Dima 已提交
118
			for (let i = 0, length = newDecorations.zones.length; i < length; i++) {
E
Erich Gamma 已提交
119
				newDecorations.zones[i].suppressMouseDown = true;
A
Alex Dima 已提交
120
				let zoneId = viewChangeAccessor.addZone(newDecorations.zones[i]);
E
Erich Gamma 已提交
121 122 123 124 125 126 127 128 129
				this._zones.push(zoneId);
				this._zonesMap[String(zoneId)] = true;
			}
		});

		// decorations
		this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations);

		// overview ruler
130 131 132
		if (overviewRuler) {
			overviewRuler.setZones(newDecorations.overviewZones);
		}
E
Erich Gamma 已提交
133 134 135
	}
}

A
Alex Dima 已提交
136
let DIFF_EDITOR_ID = 0;
E
Erich Gamma 已提交
137

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

140
	private static readonly ONE_OVERVIEW_WIDTH = 15;
M
Matt Bierner 已提交
141
	public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = 30;
142
	private static readonly UPDATE_DIFF_DECORATIONS_DELAY = 200; // ms
E
Erich Gamma 已提交
143

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

A
Alex Dima 已提交
147 148 149 150
	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 已提交
151

J
Johannes Rieken 已提交
152
	private _domElement: HTMLElement;
A
Alex Dima 已提交
153 154 155
	protected readonly _containerDomElement: HTMLElement;
	private readonly _overviewDomElement: HTMLElement;
	private readonly _overviewViewportDomElement: FastDomNode<HTMLElement>;
E
Erich Gamma 已提交
156

J
Johannes Rieken 已提交
157 158
	private _width: number;
	private _height: number;
A
Alex Dima 已提交
159
	private _reviewHeight: number;
A
Alex Dima 已提交
160
	private readonly _measureDomElementToken: number;
E
Erich Gamma 已提交
161

J
Johannes Rieken 已提交
162 163 164 165
	private originalEditor: CodeEditor;
	private _originalDomNode: HTMLElement;
	private _originalEditorState: VisualEditorState;
	private _originalOverviewRuler: editorBrowser.IOverviewRuler;
E
Erich Gamma 已提交
166

J
Johannes Rieken 已提交
167 168 169 170
	private modifiedEditor: CodeEditor;
	private _modifiedDomNode: HTMLElement;
	private _modifiedEditorState: VisualEditorState;
	private _modifiedOverviewRuler: editorBrowser.IOverviewRuler;
E
Erich Gamma 已提交
171

J
Johannes Rieken 已提交
172 173 174 175
	private _currentlyChangingViewZones: boolean;
	private _beginUpdateDecorationsTimeout: number;
	private _diffComputationToken: number;
	private _lineChanges: editorCommon.ILineChange[];
E
Erich Gamma 已提交
176

J
Johannes Rieken 已提交
177 178
	private _isVisible: boolean;
	private _isHandlingScrollEvent: boolean;
E
Erich Gamma 已提交
179 180

	private _ignoreTrimWhitespace: boolean;
181
	private _originalIsEditable: boolean;
E
Erich Gamma 已提交
182

J
Johannes Rieken 已提交
183
	private _renderSideBySide: boolean;
184
	private _renderIndicators: boolean;
J
Johannes Rieken 已提交
185 186
	private _enableSplitViewResizing: boolean;
	private _strategy: IDiffEditorWidgetStyle;
E
Erich Gamma 已提交
187

J
Johannes Rieken 已提交
188
	private _updateDecorationsRunner: RunOnceScheduler;
E
Erich Gamma 已提交
189

190
	private _editorWorkerService: IEditorWorkerService;
191
	protected _contextKeyService: IContextKeyService;
192
	private _codeEditorService: ICodeEditorService;
193
	private _themeService: IThemeService;
194
	private readonly _messageService: IMessageService;
195

A
Alex Dima 已提交
196 197
	private _reviewPane: DiffReview;

198
	constructor(
J
Johannes Rieken 已提交
199
		domElement: HTMLElement,
200
		options: editorOptions.IDiffEditorOptions,
201
		@IEditorWorkerService editorWorkerService: IEditorWorkerService,
202
		@IContextKeyService contextKeyService: IContextKeyService,
203
		@IInstantiationService instantiationService: IInstantiationService,
204
		@ICodeEditorService codeEditorService: ICodeEditorService,
205 206
		@IThemeService themeService: IThemeService,
		@IMessageService messageService: IMessageService
207
	) {
E
Erich Gamma 已提交
208
		super();
A
Alex Dima 已提交
209

210
		this._editorWorkerService = editorWorkerService;
211
		this._codeEditorService = codeEditorService;
J
Joao Moreno 已提交
212 213
		this._contextKeyService = contextKeyService.createScoped(domElement);
		this._contextKeyService.createKey('isInDiffEditor', true);
214
		this._themeService = themeService;
215
		this._messageService = messageService;
E
Erich Gamma 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

		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;
		}

234 235 236 237 238 239
		// renderIndicators
		this._renderIndicators = true;
		if (typeof options.renderIndicators !== 'undefined') {
			this._renderIndicators = options.renderIndicators;
		}

240 241 242 243 244
		this._originalIsEditable = false;
		if (typeof options.originalEditable !== 'undefined') {
			this._originalIsEditable = Boolean(options.originalEditable);
		}

A
Alex Dima 已提交
245
		this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0));
E
Erich Gamma 已提交
246 247

		this._containerDomElement = document.createElement('div');
248
		this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
E
Erich Gamma 已提交
249 250 251 252
		this._containerDomElement.style.position = 'relative';
		this._containerDomElement.style.height = '100%';
		this._domElement.appendChild(this._containerDomElement);

A
Alex Dima 已提交
253 254 255
		this._overviewViewportDomElement = createFastDomNode(document.createElement('div'));
		this._overviewViewportDomElement.setClassName('diffViewport');
		this._overviewViewportDomElement.setPosition('absolute');
E
Erich Gamma 已提交
256 257 258 259 260

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

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

263
		this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => {
E
Erich Gamma 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
			this.modifiedEditor.delegateVerticalScrollbarMouseDown(e);
		}));
		this._containerDomElement.appendChild(this._overviewDomElement);

		this._createLeftHandSide();
		this._createRightHandSide();

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

		this._originalEditorState = new VisualEditorState();
		this._modifiedEditorState = new VisualEditorState();

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

		this._width = 0;
		this._height = 0;
A
Alex Dima 已提交
283
		this._reviewHeight = 0;
E
Erich Gamma 已提交
284 285 286

		this._lineChanges = null;

J
Joao Moreno 已提交
287 288 289 290 291 292 293
		const services = new ServiceCollection();
		services.set(IContextKeyService, this._contextKeyService);

		const scopedInstantiationService = instantiationService.createChild(services);

		this._createLeftHandSideEditor(options, scopedInstantiationService);
		this._createRightHandSideEditor(options, scopedInstantiationService);
E
Erich Gamma 已提交
294

295 296 297
		this._reviewPane = new DiffReview(this);
		this._containerDomElement.appendChild(this._reviewPane.domNode.domNode);
		this._containerDomElement.appendChild(this._reviewPane.shadow.domNode);
298
		this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode);
299

E
Erich Gamma 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
		if (options.automaticLayout) {
			this._measureDomElementToken = window.setInterval(() => this._measureDomElement(false), 100);
		}

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

		if (this._renderSideBySide) {
			this._setStrategy(new DiffEdtorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
		} else {
			this._setStrategy(new DiffEdtorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
		}
315 316

		this._codeEditorService.addDiffEditor(this);
317

318
		this._register(themeService.onThemeChange(t => {
319 320 321
			if (this._strategy && this._strategy.applyColors(t)) {
				this._updateDecorationsRunner.schedule();
			}
322 323
			this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
		}));
E
Erich Gamma 已提交
324 325
	}

326 327 328 329 330 331 332 333
	public get ignoreTrimWhitespace(): boolean {
		return this._ignoreTrimWhitespace;
	}

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

334 335 336 337
	public get renderIndicators(): boolean {
		return this._renderIndicators;
	}

A
Alex Dima 已提交
338 339 340 341 342 343 344 345 346 347 348 349
	public hasWidgetFocus(): boolean {
		return dom.isAncestor(document.activeElement, this._domElement);
	}

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

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

350
	private static _getClassName(theme: ITheme, renderSideBySide: boolean): string {
A
Alex Dima 已提交
351
		let result = 'monaco-diff-editor monaco-editor-background ';
E
Erich Gamma 已提交
352 353 354
		if (renderSideBySide) {
			result += 'side-by-side ';
		}
355
		result += getThemeTypeSelector(theme.type);
E
Erich Gamma 已提交
356 357 358 359 360 361 362 363
		return result;
	}

	private _recreateOverviewRulers(): void {
		if (this._originalOverviewRuler) {
			this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode());
			this._originalOverviewRuler.dispose();
		}
364
		this._originalOverviewRuler = this.originalEditor.createOverviewRuler('original diffOverviewRuler');
E
Erich Gamma 已提交
365 366 367 368 369 370
		this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode());

		if (this._modifiedOverviewRuler) {
			this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode());
			this._modifiedOverviewRuler.dispose();
		}
371
		this._modifiedOverviewRuler = this.modifiedEditor.createOverviewRuler('modified diffOverviewRuler');
E
Erich Gamma 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
		this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode());

		this._layoutOverviewRulers();
	}

	private _createLeftHandSide(): void {
		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);
	}

	private _createRightHandSide(): void {
		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);
	}

393
	private _createLeftHandSideEditor(options: editorOptions.IDiffEditorOptions, instantiationService: IInstantiationService): void {
A
Alex Dima 已提交
394
		this.originalEditor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable));
A
Alex Dima 已提交
395

A
Alex Dima 已提交
396
		this._register(this.originalEditor.onDidScrollChange((e) => {
A
Alex Dima 已提交
397 398 399
			if (this._isHandlingScrollEvent) {
				return;
			}
400
			if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) {
A
Alex Dima 已提交
401 402 403 404 405 406 407 408
				return;
			}
			this._isHandlingScrollEvent = true;
			this.modifiedEditor.setScrollPosition({
				scrollLeft: e.scrollLeft,
				scrollTop: e.scrollTop
			});
			this._isHandlingScrollEvent = false;
409 410

			this._layoutOverviewViewport();
A
Alex Dima 已提交
411 412
		}));

A
Alex Dima 已提交
413
		this._register(this.originalEditor.onDidChangeViewZones(() => {
A
Alex Dima 已提交
414 415 416
			this._onViewZonesChanged();
		}));

A
Alex Dima 已提交
417
		this._register(this.originalEditor.onDidChangeModelContent(() => {
A
Alex Dima 已提交
418 419 420 421
			if (this._isVisible) {
				this._beginUpdateDecorationsSoon();
			}
		}));
E
Erich Gamma 已提交
422 423
	}

424
	private _createRightHandSideEditor(options: editorOptions.IDiffEditorOptions, instantiationService: IInstantiationService): void {
A
Alex Dima 已提交
425
		this.modifiedEditor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options));
A
Alex Dima 已提交
426

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

			this._layoutOverviewViewport();
		}));

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

A
Alex Dima 已提交
448
		this._register(this.modifiedEditor.onDidChangeConfiguration((e) => {
A
Alex Dima 已提交
449 450 451 452 453
			if (e.fontInfo && this.modifiedEditor.getModel()) {
				this._onViewZonesChanged();
			}
		}));

A
Alex Dima 已提交
454
		this._register(this.modifiedEditor.onDidChangeModelContent(() => {
A
Alex Dima 已提交
455 456 457 458
			if (this._isVisible) {
				this._beginUpdateDecorationsSoon();
			}
		}));
E
Erich Gamma 已提交
459 460
	}

461
	protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: editorOptions.IEditorOptions): CodeEditor {
A
Alex Dima 已提交
462 463 464
		return instantiationService.createInstance(CodeEditor, container, options);
	}

E
Erich Gamma 已提交
465
	public dispose(): void {
466 467
		this._codeEditorService.removeDiffEditor(this);

E
Erich Gamma 已提交
468 469 470 471 472 473 474
		window.clearInterval(this._measureDomElementToken);

		this._cleanViewZonesAndDecorations();

		this._originalOverviewRuler.dispose();
		this._modifiedOverviewRuler.dispose();

A
Alex Dima 已提交
475 476
		this.originalEditor.dispose();
		this.modifiedEditor.dispose();
E
Erich Gamma 已提交
477 478 479

		this._strategy.dispose();

480 481
		this._reviewPane.dispose();

A
Alex Dima 已提交
482
		this._onDidDispose.fire();
A
Alex Dima 已提交
483

E
Erich Gamma 已提交
484 485 486 487 488 489 490 491 492 493
		super.dispose();
	}

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

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

	public getEditorType(): string {
A
Alex Dima 已提交
494
		return editorCommon.EditorType.IDiffEditor;
E
Erich Gamma 已提交
495 496
	}

A
Alex Dima 已提交
497
	public getLineChanges(): editorCommon.ILineChange[] {
E
Erich Gamma 已提交
498 499 500
		return this._lineChanges;
	}

A
Alex Dima 已提交
501
	public getOriginalEditor(): editorBrowser.ICodeEditor {
E
Erich Gamma 已提交
502 503 504
		return this.originalEditor;
	}

A
Alex Dima 已提交
505
	public getModifiedEditor(): editorBrowser.ICodeEditor {
E
Erich Gamma 已提交
506 507 508
		return this.modifiedEditor;
	}

509
	public updateOptions(newOptions: editorOptions.IDiffEditorOptions): void {
E
Erich Gamma 已提交
510 511

		// Handle side by side
A
Alex Dima 已提交
512
		let renderSideBySideChanged = false;
E
Erich Gamma 已提交
513 514 515 516 517 518 519
		if (typeof newOptions.renderSideBySide !== 'undefined') {
			if (this._renderSideBySide !== newOptions.renderSideBySide) {
				this._renderSideBySide = newOptions.renderSideBySide;
				renderSideBySideChanged = true;
			}
		}

520 521
		let beginUpdateDecorations = false;

E
Erich Gamma 已提交
522 523 524 525
		if (typeof newOptions.ignoreTrimWhitespace !== 'undefined') {
			if (this._ignoreTrimWhitespace !== newOptions.ignoreTrimWhitespace) {
				this._ignoreTrimWhitespace = newOptions.ignoreTrimWhitespace;
				// Begin comparing
526 527 528 529 530 531 532 533
				beginUpdateDecorations = true;
			}
		}

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

537 538 539 540
		if (beginUpdateDecorations) {
			this._beginUpdateDecorations();
		}

541 542 543 544
		if (typeof newOptions.originalEditable !== 'undefined') {
			this._originalIsEditable = Boolean(newOptions.originalEditable);
		}

E
Erich Gamma 已提交
545
		this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions));
546
		this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable));
E
Erich Gamma 已提交
547 548 549 550 551 552 553 554 555 556

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

		// renderSideBySide
		if (renderSideBySideChanged) {
			if (this._renderSideBySide) {
557
				this._setStrategy(new DiffEdtorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing, ));
E
Erich Gamma 已提交
558 559 560
			} else {
				this._setStrategy(new DiffEdtorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
			}
561 562
			// Update class name
			this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
E
Erich Gamma 已提交
563 564 565
		}
	}

A
Alex Dima 已提交
566
	public getModel(): editorCommon.IDiffEditorModel {
E
Erich Gamma 已提交
567 568 569 570 571 572
		return {
			original: this.originalEditor.getModel(),
			modified: this.modifiedEditor.getModel()
		};
	}

J
Johannes Rieken 已提交
573
	public setModel(model: editorCommon.IDiffEditorModel): void {
E
Erich Gamma 已提交
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
		// 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
		this._lineChanges = null;
		this._diffComputationToken++;

		if (model) {
			this._recreateOverviewRulers();

			// Begin comparing
			this._beginUpdateDecorations();
		} else {
			this._lineChanges = null;
		}

		this._layoutOverviewViewport();
	}

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

A
Alex Dima 已提交
612
	public getVisibleColumnFromPosition(position: IPosition): number {
E
Erich Gamma 已提交
613 614 615
		return this.modifiedEditor.getVisibleColumnFromPosition(position);
	}

A
Alex Dima 已提交
616
	public getPosition(): Position {
E
Erich Gamma 已提交
617 618 619
		return this.modifiedEditor.getPosition();
	}

620 621
	public setPosition(position: IPosition): void {
		this.modifiedEditor.setPosition(position);
E
Erich Gamma 已提交
622 623
	}

624 625
	public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLine(lineNumber, scrollType);
E
Erich Gamma 已提交
626 627
	}

628 629
	public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLineInCenter(lineNumber, scrollType);
E
Erich Gamma 已提交
630 631
	}

632 633
	public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType);
E
Erich Gamma 已提交
634 635
	}

636 637
	public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealPosition(position, scrollType);
E
Erich Gamma 已提交
638 639
	}

640 641
	public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealPositionInCenter(position, scrollType);
E
Erich Gamma 已提交
642 643
	}

644 645
	public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType);
E
Erich Gamma 已提交
646 647
	}

648
	public getSelection(): Selection {
E
Erich Gamma 已提交
649 650 651
		return this.modifiedEditor.getSelection();
	}

652
	public getSelections(): Selection[] {
E
Erich Gamma 已提交
653 654 655
		return this.modifiedEditor.getSelections();
	}

656 657 658 659 660 661
	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 已提交
662 663
	}

A
Alex Dima 已提交
664
	public setSelections(ranges: ISelection[]): void {
E
Erich Gamma 已提交
665 666 667
		this.modifiedEditor.setSelections(ranges);
	}

668 669
	public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType);
E
Erich Gamma 已提交
670 671
	}

672 673
	public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType);
E
Erich Gamma 已提交
674 675
	}

676 677
	public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType);
E
Erich Gamma 已提交
678 679
	}

680 681
	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 已提交
682 683
	}

684 685
	public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealRangeInCenter(range, scrollType);
E
Erich Gamma 已提交
686 687
	}

688 689
	public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType);
E
Erich Gamma 已提交
690 691
	}

692 693
	public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
		this.modifiedEditor.revealRangeAtTop(range, scrollType);
694 695
	}

A
Alex Dima 已提交
696
	public getSupportedActions(): editorCommon.IEditorAction[] {
697 698 699
		return this.modifiedEditor.getSupportedActions();
	}

A
Alex Dima 已提交
700
	public saveViewState(): editorCommon.IDiffEditorViewState {
A
Alex Dima 已提交
701 702
		let originalViewState = this.originalEditor.saveViewState();
		let modifiedViewState = this.modifiedEditor.saveViewState();
E
Erich Gamma 已提交
703 704 705 706 707 708
		return {
			original: originalViewState,
			modified: modifiedViewState
		};
	}

A
Alex Dima 已提交
709
	public restoreViewState(s: editorCommon.IDiffEditorViewState): void {
E
Erich Gamma 已提交
710
		if (s.original && s.original) {
A
Alex Dima 已提交
711
			let diffEditorState = <editorCommon.IDiffEditorViewState>s;
E
Erich Gamma 已提交
712 713 714 715 716
			this.originalEditor.restoreViewState(diffEditorState.original);
			this.modifiedEditor.restoreViewState(diffEditorState.modified);
		}
	}

J
Johannes Rieken 已提交
717
	public layout(dimension?: editorCommon.IDimension): void {
E
Erich Gamma 已提交
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
		this._measureDomElement(false, dimension);
	}

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

	public isFocused(): boolean {
		return this.originalEditor.isFocused() || this.modifiedEditor.isFocused();
	}

	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 已提交
745
	public trigger(source: string, handlerId: string, payload: any): void {
E
Erich Gamma 已提交
746 747 748
		this.modifiedEditor.trigger(source, handlerId, payload);
	}

J
Johannes Rieken 已提交
749
	public changeDecorations(callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => any): any {
E
Erich Gamma 已提交
750 751 752 753 754 755 756 757 758
		return this.modifiedEditor.changeDecorations(callback);
	}

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



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

J
Johannes Rieken 已提交
759
	private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void {
760 761 762 763
		dimensions = dimensions || {
			width: this._containerDomElement.clientWidth,
			height: this._containerDomElement.clientHeight
		};
E
Erich Gamma 已提交
764 765

		if (dimensions.width <= 0) {
766 767
			this._width = 0;
			this._height = 0;
A
Alex Dima 已提交
768
			this._reviewHeight = 0;
E
Erich Gamma 已提交
769 770 771 772 773 774 775 776 777 778
			return;
		}

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

		this._width = dimensions.width;
		this._height = dimensions.height;
779
		this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0;
E
Erich Gamma 已提交
780 781 782 783 784

		this._doLayout();
	}

	private _layoutOverviewRulers(): void {
A
Alex Dima 已提交
785 786
		let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH;
		let layoutInfo = this.modifiedEditor.getLayoutInfo();
E
Erich Gamma 已提交
787
		if (layoutInfo) {
A
Alex Dima 已提交
788
			this._originalOverviewRuler.setLayout({
E
Erich Gamma 已提交
789 790 791
				top: 0,
				width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
				right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH,
A
Alex Dima 已提交
792
				height: (this._height - this._reviewHeight)
A
Alex Dima 已提交
793 794
			});
			this._modifiedOverviewRuler.setLayout({
E
Erich Gamma 已提交
795 796 797
				top: 0,
				right: 0,
				width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
A
Alex Dima 已提交
798
				height: (this._height - this._reviewHeight)
A
Alex Dima 已提交
799
			});
E
Erich Gamma 已提交
800 801 802 803 804 805 806 807 808 809 810 811
		}
	}

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

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

A
Alex Dima 已提交
812 813 814 815 816 817 818 819 820
	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);
	}

821 822 823 824 825 826 827 828 829 830 831 832 833
	private _lastOriginalWarning: URI = null;
	private _lastModifiedWarning: URI = null;

	private static _equals(a: URI, b: URI): boolean {
		if (!a && !b) {
			return true;
		}
		if (!a || !b) {
			return false;
		}
		return (a.toString() === b.toString());
	}

E
Erich Gamma 已提交
834 835
	private _beginUpdateDecorations(): void {
		this._beginUpdateDecorationsTimeout = -1;
836 837 838
		const currentOriginalModel = this.originalEditor.getModel();
		const currentModifiedModel = this.modifiedEditor.getModel();
		if (!currentOriginalModel || !currentModifiedModel) {
E
Erich Gamma 已提交
839 840 841 842 843 844 845
			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 已提交
846
		let currentToken = this._diffComputationToken;
E
Erich Gamma 已提交
847

848 849 850 851 852 853 854 855 856 857 858 859
		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;
				this._messageService.show(Severity.Warning, nls.localize("diff.tooLarge", "Cannot compare files because one file is too large."));
			}
			return;
		}

860
		this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace).then((result) => {
861 862 863
			if (currentToken === this._diffComputationToken
				&& currentOriginalModel === this.originalEditor.getModel()
				&& currentModifiedModel === this.modifiedEditor.getModel()
J
Johannes Rieken 已提交
864
			) {
865 866
				this._lineChanges = result;
				this._updateDecorationsRunner.schedule();
A
Alex Dima 已提交
867
				this._onDidUpdateDiff.fire();
868 869 870 871 872
			}
		}, (error) => {
			if (currentToken === this._diffComputationToken
				&& currentOriginalModel === this.originalEditor.getModel()
				&& currentModifiedModel === this.modifiedEditor.getModel()
J
Johannes Rieken 已提交
873
			) {
E
Erich Gamma 已提交
874 875 876
				this._lineChanges = null;
				this._updateDecorationsRunner.schedule();
			}
877
		});
E
Erich Gamma 已提交
878 879 880 881 882 883 884 885
	}

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

	private _updateDecorations(): void {
886 887 888
		if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel()) {
			return;
		}
A
Alex Dima 已提交
889
		let lineChanges = this._lineChanges || [];
E
Erich Gamma 已提交
890

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

894
		let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor);
E
Erich Gamma 已提交
895 896 897 898 899 900 901 902 903 904

		try {
			this._currentlyChangingViewZones = true;
			this._originalEditorState.apply(this.originalEditor, this._originalOverviewRuler, diffDecorations.original);
			this._modifiedEditorState.apply(this.modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified);
		} finally {
			this._currentlyChangingViewZones = false;
		}
	}

905
	private _adjustOptionsForSubEditor(options: editorOptions.IDiffEditorOptions): editorOptions.IDiffEditorOptions {
J
Johannes Rieken 已提交
906
		let clonedOptions: editorOptions.IDiffEditorOptions = objects.deepClone(options || {});
A
Alex Dima 已提交
907
		clonedOptions.inDiffEditor = true;
908
		clonedOptions.wordWrap = 'off';
909
		clonedOptions.wordWrapMinified = false;
E
Erich Gamma 已提交
910 911 912
		clonedOptions.automaticLayout = false;
		clonedOptions.scrollbar = clonedOptions.scrollbar || {};
		clonedOptions.scrollbar.vertical = 'visible';
A
Alex Dima 已提交
913
		clonedOptions.folding = false;
914
		clonedOptions.codeLens = false;
J
Joao Moreno 已提交
915
		clonedOptions.fixedOverflowWidgets = true;
916
		clonedOptions.lineDecorationsWidth = '2ch';
917 918 919
		if (!clonedOptions.minimap) {
			clonedOptions.minimap = {};
		}
920
		clonedOptions.minimap.enabled = false;
E
Erich Gamma 已提交
921 922 923
		return clonedOptions;
	}

924
	private _adjustOptionsForLeftHandSide(options: editorOptions.IDiffEditorOptions, isEditable: boolean): editorOptions.IEditorOptions {
925 926 927
		let result = this._adjustOptionsForSubEditor(options);
		result.readOnly = !isEditable;
		result.overviewRulerLanes = 1;
928
		result.extraEditorClassName = 'original-in-monaco-diff-editor';
929 930 931
		return result;
	}

932
	private _adjustOptionsForRightHandSide(options: editorOptions.IDiffEditorOptions): editorOptions.IEditorOptions {
933
		let result = this._adjustOptionsForSubEditor(options);
A
Alex Dima 已提交
934
		result.revealHorizontalRightPadding = editorOptions.EDITOR_DEFAULTS.viewInfo.revealHorizontalRightPadding + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
935
		result.scrollbar.verticalHasArrows = false;
936
		result.extraEditorClassName = 'modified-in-monaco-diff-editor';
937
		return result;
E
Erich Gamma 已提交
938 939
	}

940 941 942 943
	public doLayout(): void {
		this._measureDomElement(true);
	}

E
Erich Gamma 已提交
944
	private _doLayout(): void {
A
Alex Dima 已提交
945
		let splitPoint = this._strategy.layout();
E
Erich Gamma 已提交
946 947 948 949 950 951 952 953

		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 已提交
954
		this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px';
E
Erich Gamma 已提交
955 956
		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 已提交
957 958
		this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH);
		this._overviewViewportDomElement.setHeight(30);
E
Erich Gamma 已提交
959

A
Alex Dima 已提交
960 961
		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 已提交
962 963 964 965 966

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

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

E
Erich Gamma 已提交
969 970 971 972
		this._layoutOverviewViewport();
	}

	private _layoutOverviewViewport(): void {
A
Alex Dima 已提交
973
		let layout = this._computeOverviewViewport();
E
Erich Gamma 已提交
974
		if (!layout) {
A
Alex Dima 已提交
975 976
			this._overviewViewportDomElement.setTop(0);
			this._overviewViewportDomElement.setHeight(0);
E
Erich Gamma 已提交
977
		} else {
A
Alex Dima 已提交
978 979
			this._overviewViewportDomElement.setTop(layout.top);
			this._overviewViewportDomElement.setHeight(layout.height);
E
Erich Gamma 已提交
980 981 982
		}
	}

J
Johannes Rieken 已提交
983
	private _computeOverviewViewport(): { height: number; top: number; } {
A
Alex Dima 已提交
984
		let layoutInfo = this.modifiedEditor.getLayoutInfo();
E
Erich Gamma 已提交
985 986 987 988
		if (!layoutInfo) {
			return null;
		}

A
Alex Dima 已提交
989 990
		let scrollTop = this.modifiedEditor.getScrollTop();
		let scrollHeight = this.modifiedEditor.getScrollHeight();
E
Erich Gamma 已提交
991

A
Alex Dima 已提交
992 993 994
		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 已提交
995

996
		let computedSliderSize = Math.max(0, Math.floor(layoutInfo.contentHeight * computedRatio));
A
Alex Dima 已提交
997
		let computedSliderPosition = Math.floor(scrollTop * computedRatio);
E
Erich Gamma 已提交
998 999 1000 1001 1002 1003 1004

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

J
Johannes Rieken 已提交
1005
	private _createDataSource(): IDataSource {
E
Erich Gamma 已提交
1006 1007 1008 1009 1010 1011
		return {
			getWidth: () => {
				return this._width;
			},

			getHeight: () => {
A
Alex Dima 已提交
1012
				return (this._height - this._reviewHeight);
E
Erich Gamma 已提交
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
			},

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

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

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

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

J
Johannes Rieken 已提交
1033
	private _setStrategy(newStrategy: IDiffEditorWidgetStyle): void {
E
Erich Gamma 已提交
1034 1035 1036 1037 1038
		if (this._strategy) {
			this._strategy.dispose();
		}

		this._strategy = newStrategy;
1039
		newStrategy.applyColors(this._themeService.getTheme());
E
Erich Gamma 已提交
1040 1041 1042 1043 1044 1045 1046 1047 1048

		if (this._lineChanges) {
			this._updateDecorations();
		}

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

J
Johannes Rieken 已提交
1049
	private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange {
E
Erich Gamma 已提交
1050 1051 1052 1053 1054
		if (this._lineChanges.length === 0 || lineNumber < startLineNumberExtractor(this._lineChanges[0])) {
			// There are no changes or `lineNumber` is before the first change
			return null;
		}

A
Alex Dima 已提交
1055
		let min = 0, max = this._lineChanges.length - 1;
E
Erich Gamma 已提交
1056
		while (min < max) {
A
Alex Dima 已提交
1057 1058 1059
			let mid = Math.floor((min + max) / 2);
			let midStart = startLineNumberExtractor(this._lineChanges[mid]);
			let midEnd = (mid + 1 <= max ? startLineNumberExtractor(this._lineChanges[mid + 1]) : Number.MAX_VALUE);
E
Erich Gamma 已提交
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074

			if (lineNumber < midStart) {
				max = mid - 1;
			} else if (lineNumber >= midEnd) {
				min = mid + 1;
			} else {
				// HIT!
				min = mid;
				max = mid;
			}
		}
		return this._lineChanges[min];
	}

	private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number {
A
Alex Dima 已提交
1075
		let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber);
E
Erich Gamma 已提交
1076 1077 1078 1079 1080

		if (!lineChange) {
			return lineNumber;
		}

A
Alex Dima 已提交
1081 1082 1083 1084
		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 已提交
1085 1086


A
Alex Dima 已提交
1087
		let delta = lineNumber - originalEquivalentLineNumber;
E
Erich Gamma 已提交
1088 1089 1090 1091 1092

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

J
Johannes Rieken 已提交
1093
		return modifiedEquivalentLineNumber + lineChangeModifiedLength - lineChangeOriginalLength + delta;
E
Erich Gamma 已提交
1094 1095 1096
	}

	private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number {
A
Alex Dima 已提交
1097
		let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber);
E
Erich Gamma 已提交
1098 1099 1100 1101 1102

		if (!lineChange) {
			return lineNumber;
		}

A
Alex Dima 已提交
1103 1104 1105 1106
		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 已提交
1107 1108


A
Alex Dima 已提交
1109
		let delta = lineNumber - modifiedEquivalentLineNumber;
E
Erich Gamma 已提交
1110 1111 1112 1113 1114

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

J
Johannes Rieken 已提交
1115
		return originalEquivalentLineNumber + lineChangeOriginalLength - lineChangeModifiedLength + delta;
E
Erich Gamma 已提交
1116 1117
	}

A
Alex Dima 已提交
1118
	public getDiffLineInformationForOriginal(lineNumber: number): editorBrowser.IDiffLineInformation {
E
Erich Gamma 已提交
1119 1120 1121 1122 1123 1124 1125 1126 1127
		if (!this._lineChanges) {
			// Cannot answer that which I don't know
			return null;
		}
		return {
			equivalentLineNumber: this._getEquivalentLineForOriginalLineNumber(lineNumber)
		};
	}

A
Alex Dima 已提交
1128
	public getDiffLineInformationForModified(lineNumber: number): editorBrowser.IDiffLineInformation {
E
Erich Gamma 已提交
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
		if (!this._lineChanges) {
			// 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 已提交
1145 1146
	getOriginalEditor(): editorBrowser.ICodeEditor;
	getModifiedEditor(): editorBrowser.ICodeEditor;
E
Erich Gamma 已提交
1147 1148
}

A
Alex Dima 已提交
1149
class DiffEditorWidgetStyle extends Disposable {
E
Erich Gamma 已提交
1150

J
Johannes Rieken 已提交
1151
	_dataSource: IDataSource;
1152 1153
	_insertColor: Color;
	_removeColor: Color;
E
Erich Gamma 已提交
1154

J
Johannes Rieken 已提交
1155
	constructor(dataSource: IDataSource) {
A
Alex Dima 已提交
1156
		super();
E
Erich Gamma 已提交
1157 1158 1159
		this._dataSource = dataSource;
	}

1160 1161 1162 1163 1164 1165 1166 1167 1168
	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 已提交
1169
	public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones {
E
Erich Gamma 已提交
1170 1171 1172 1173 1174 1175 1176
		// Get view zones
		modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => {
			return a.afterLineNumber - b.afterLineNumber;
		});
		originalWhitespaces = originalWhitespaces.sort((a, b) => {
			return a.afterLineNumber - b.afterLineNumber;
		});
1177
		let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators);
E
Erich Gamma 已提交
1178 1179

		// Get decorations & overview ruler zones
1180 1181
		let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor);
		let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor);
E
Erich Gamma 已提交
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196

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

A
Alex Dima 已提交
1197
	_getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones {
E
Erich Gamma 已提交
1198 1199 1200
		return null;
	}

1201
	_getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
E
Erich Gamma 已提交
1202 1203 1204
		return null;
	}

1205
	_getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
E
Erich Gamma 已提交
1206 1207 1208 1209
		return null;
	}
}

A
Alex Dima 已提交
1210
interface IMyViewZone extends editorBrowser.IViewZone {
E
Erich Gamma 已提交
1211 1212 1213 1214 1215 1216
	shouldNotShrink?: boolean;
}

class ForeignViewZonesIterator {

	private _index: number;
A
Alex Dima 已提交
1217 1218
	private _source: IEditorWhitespace[];
	public current: IEditorWhitespace;
E
Erich Gamma 已提交
1219

A
Alex Dima 已提交
1220
	constructor(source: IEditorWhitespace[]) {
E
Erich Gamma 已提交
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
		this._source = source;
		this._index = -1;
		this.advance();
	}

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

1236
abstract class ViewZonesComputer {
E
Erich Gamma 已提交
1237

J
Johannes Rieken 已提交
1238
	private lineChanges: editorCommon.ILineChange[];
A
Alex Dima 已提交
1239 1240
	private originalForeignVZ: IEditorWhitespace[];
	private modifiedForeignVZ: IEditorWhitespace[];
E
Erich Gamma 已提交
1241

A
Alex Dima 已提交
1242
	constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
E
Erich Gamma 已提交
1243 1244 1245 1246 1247 1248
		this.lineChanges = lineChanges;
		this.originalForeignVZ = originalForeignVZ;
		this.modifiedForeignVZ = modifiedForeignVZ;
	}

	public getViewZones(): IEditorsZones {
A
Alex Dima 已提交
1249
		let result: IEditorsZones = {
E
Erich Gamma 已提交
1250 1251 1252 1253
			original: [],
			modified: []
		};

A
Alex Dima 已提交
1254 1255 1256 1257 1258 1259 1260 1261
		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 已提交
1262 1263 1264
			return a.afterLineNumber - b.afterLineNumber;
		};

A
Alex Dima 已提交
1265
		let addAndCombineIfPossible = (destination: editorBrowser.IViewZone[], item: IMyViewZone) => {
E
Erich Gamma 已提交
1266
			if (item.domNode === null && destination.length > 0) {
A
Alex Dima 已提交
1267
				let lastItem = destination[destination.length - 1];
E
Erich Gamma 已提交
1268 1269 1270 1271 1272 1273 1274 1275
				if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) {
					lastItem.heightInLines += item.heightInLines;
					return;
				}
			}
			destination.push(item);
		};

A
Alex Dima 已提交
1276 1277
		let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ);
		let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ);
E
Erich Gamma 已提交
1278 1279

		// 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 已提交
1280 1281
		for (let i = 0, length = this.lineChanges.length; i <= length; i++) {
			let lineChange = (i < length ? this.lineChanges[i] : null);
E
Erich Gamma 已提交
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298

			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 已提交
1299 1300
			let stepOriginal: IMyViewZone[] = [];
			let stepModified: IMyViewZone[] = [];
E
Erich Gamma 已提交
1301 1302 1303 1304 1305

			// ---------------------------- 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 已提交
1306
				let viewZoneLineNumber: number;
E
Erich Gamma 已提交
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
				if (modifiedForeignVZ.current.afterLineNumber <= modifiedEquivalentLineNumber) {
					viewZoneLineNumber = originalEquivalentLineNumber - modifiedEquivalentLineNumber + modifiedForeignVZ.current.afterLineNumber;
				} else {
					viewZoneLineNumber = originalEndEquivalentLineNumber;
				}
				stepOriginal.push({
					afterLineNumber: viewZoneLineNumber,
					heightInLines: modifiedForeignVZ.current.heightInLines,
					domNode: null
				});
				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 已提交
1322
				let viewZoneLineNumber: number;
E
Erich Gamma 已提交
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
				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 已提交
1337
				let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength);
E
Erich Gamma 已提交
1338 1339 1340 1341 1342 1343
				if (r) {
					stepOriginal.push(r);
				}
			}

			if (lineChange !== null && isChangeOrDelete(lineChange)) {
A
Alex Dima 已提交
1344
				let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength);
E
Erich Gamma 已提交
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
				if (r) {
					stepModified.push(r);
				}
			}

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


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

			// [CANCEL & EMIT] Try to cancel view zones out
A
Alex Dima 已提交
1356 1357
			let stepOriginalIndex = 0;
			let stepModifiedIndex = 0;
E
Erich Gamma 已提交
1358 1359 1360 1361 1362

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

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

A
Alex Dima 已提交
1366 1367
				let originalDelta = original.afterLineNumber - originalEquivalentLineNumber;
				let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber;
E
Erich Gamma 已提交
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408

				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 已提交
1409
		let ensureDomNode = (z: IMyViewZone) => {
E
Erich Gamma 已提交
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420
			if (!z.domNode) {
				z.domNode = createFakeLinesDiv();
			}
		};

		result.original.forEach(ensureDomNode);
		result.modified.forEach(ensureDomNode);

		return result;
	}

J
Johannes Rieken 已提交
1421
	protected abstract _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone;
E
Erich Gamma 已提交
1422

J
Johannes Rieken 已提交
1423
	protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone;
E
Erich Gamma 已提交
1424 1425
}

1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 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
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',
	})

};

A
Alex Dima 已提交
1481
class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
E
Erich Gamma 已提交
1482 1483 1484 1485

	static MINIMUM_EDITOR_WIDTH = 100;

	private _disableSash: boolean;
J
Johannes Rieken 已提交
1486 1487 1488 1489
	private _sash: Sash;
	private _sashRatio: number;
	private _sashPosition: number;
	private _startSashPosition: number;
E
Erich Gamma 已提交
1490

J
Johannes Rieken 已提交
1491
	constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) {
E
Erich Gamma 已提交
1492 1493 1494 1495 1496
		super(dataSource);

		this._disableSash = (enableSplitViewResizing === false);
		this._sashRatio = null;
		this._sashPosition = null;
A
Alex Dima 已提交
1497
		this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this));
E
Erich Gamma 已提交
1498 1499 1500 1501 1502

		if (this._disableSash) {
			this._sash.disable();
		}

I
isidor 已提交
1503 1504 1505 1506
		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 已提交
1507 1508 1509
	}

	public dispose(): void {
A
Alex Dima 已提交
1510
		super.dispose();
E
Erich Gamma 已提交
1511 1512
	}

J
Johannes Rieken 已提交
1513
	public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void {
A
Alex Dima 已提交
1514
		let newDisableSash = (enableSplitViewResizing === false);
E
Erich Gamma 已提交
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
		if (this._disableSash !== newDisableSash) {
			this._disableSash = newDisableSash;

			if (this._disableSash) {
				this._sash.disable();
			} else {
				this._sash.enable();
			}
		}
	}

J
Johannes Rieken 已提交
1526
	public layout(sashRatio: number = this._sashRatio): number {
A
Alex Dima 已提交
1527 1528
		let w = this._dataSource.getWidth();
		let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
E
Erich Gamma 已提交
1529

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

A
Alex Dima 已提交
1533
		sashPosition = this._disableSash ? midPoint : sashPosition || midPoint;
E
Erich Gamma 已提交
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554

		if (contentWidth > DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) {
			if (sashPosition < DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
				sashPosition = DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
			}

			if (sashPosition > contentWidth - DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
				sashPosition = contentWidth - DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
			}
		} else {
			sashPosition = midPoint;
		}

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

		return this._sashPosition;
	}

M
Maxime Quandalle 已提交
1555
	private onSashDragStart(): void {
E
Erich Gamma 已提交
1556 1557 1558
		this._startSashPosition = this._sashPosition;
	}

J
Johannes Rieken 已提交
1559
	private onSashDrag(e: ISashEvent): void {
A
Alex Dima 已提交
1560 1561 1562
		let w = this._dataSource.getWidth();
		let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
		let sashPosition = this.layout((this._startSashPosition + (e.currentX - e.startX)) / contentWidth);
E
Erich Gamma 已提交
1563 1564 1565 1566 1567 1568

		this._sashRatio = sashPosition / contentWidth;

		this._dataSource.relayoutEditors();
	}

M
Maxime Quandalle 已提交
1569 1570 1571 1572 1573 1574 1575
	private onSashDragEnd(): void {
		this._sash.layout();
	}

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

A
Alex Dima 已提交
1579
	public getVerticalSashTop(sash: Sash): number {
E
Erich Gamma 已提交
1580 1581 1582
		return 0;
	}

A
Alex Dima 已提交
1583
	public getVerticalSashLeft(sash: Sash): number {
E
Erich Gamma 已提交
1584 1585 1586
		return this._sashPosition;
	}

A
Alex Dima 已提交
1587
	public getVerticalSashHeight(sash: Sash): number {
E
Erich Gamma 已提交
1588 1589 1590
		return this._dataSource.getHeight();
	}

A
Alex Dima 已提交
1591
	_getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones {
A
Alex Dima 已提交
1592
		let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ);
E
Erich Gamma 已提交
1593 1594 1595
		return c.getViewZones();
	}

1596
	_getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
E
Erich Gamma 已提交
1597

A
Alex Dima 已提交
1598
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1599 1600
			decorations: [],
			overviewZones: []
P
Peng Lyu 已提交
1601
		};
A
Alex Dima 已提交
1602 1603 1604 1605 1606

		let originalModel = originalEditor.getModel();

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

			if (isChangeOrDelete(lineChange)) {
1609 1610
				result.decorations.push({
					range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE),
1611
					options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete)
1612
				});
E
Erich Gamma 已提交
1613
				if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) {
1614
					result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE, DECORATIONS.charDeleteWholeLine));
E
Erich Gamma 已提交
1615 1616
				}

1617 1618
				let color = this._removeColor.toString();

A
Alex Dima 已提交
1619
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1620 1621
					lineChange.originalStartLineNumber,
					lineChange.originalEndLineNumber,
1622
					color
A
Alex Dima 已提交
1623
				));
E
Erich Gamma 已提交
1624 1625

				if (lineChange.charChanges) {
A
Alex Dima 已提交
1626 1627
					for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
						let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1628 1629
						if (isChangeOrDelete(charChange)) {
							if (ignoreTrimWhitespace) {
A
Alex Dima 已提交
1630 1631 1632
								for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) {
									let startColumn: number;
									let endColumn: number;
E
Erich Gamma 已提交
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
									if (lineNumber === charChange.originalStartLineNumber) {
										startColumn = charChange.originalStartColumn;
									} else {
										startColumn = originalModel.getLineFirstNonWhitespaceColumn(lineNumber);
									}
									if (lineNumber === charChange.originalEndLineNumber) {
										endColumn = charChange.originalEndColumn;
									} else {
										endColumn = originalModel.getLineLastNonWhitespaceColumn(lineNumber);
									}
1643
									result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charDelete));
E
Erich Gamma 已提交
1644 1645
								}
							} else {
1646
								result.decorations.push(createDecoration(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn, DECORATIONS.charDelete));
E
Erich Gamma 已提交
1647 1648 1649 1650 1651 1652 1653 1654 1655 1656
							}
						}
					}
				}
			}
		}

		return result;
	}

1657
	_getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
E
Erich Gamma 已提交
1658

A
Alex Dima 已提交
1659
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1660 1661
			decorations: [],
			overviewZones: []
A
Alex Dima 已提交
1662 1663 1664 1665 1666 1667
		};

		let modifiedModel = modifiedEditor.getModel();

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

			if (isChangeOrInsert(lineChange)) {

1671 1672
				result.decorations.push({
					range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE),
1673
					options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert)
1674
				});
E
Erich Gamma 已提交
1675
				if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) {
1676
					result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine));
E
Erich Gamma 已提交
1677
				}
1678
				let color = this._insertColor.toString();
A
Alex Dima 已提交
1679
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1680 1681
					lineChange.modifiedStartLineNumber,
					lineChange.modifiedEndLineNumber,
1682
					color
A
Alex Dima 已提交
1683
				));
E
Erich Gamma 已提交
1684 1685

				if (lineChange.charChanges) {
A
Alex Dima 已提交
1686 1687
					for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
						let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1688 1689
						if (isChangeOrInsert(charChange)) {
							if (ignoreTrimWhitespace) {
A
Alex Dima 已提交
1690 1691 1692
								for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) {
									let startColumn: number;
									let endColumn: number;
E
Erich Gamma 已提交
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
									if (lineNumber === charChange.modifiedStartLineNumber) {
										startColumn = charChange.modifiedStartColumn;
									} else {
										startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber);
									}
									if (lineNumber === charChange.modifiedEndLineNumber) {
										endColumn = charChange.modifiedEndColumn;
									} else {
										endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber);
									}
1703
									result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1704 1705
								}
							} else {
1706
								result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719
							}
						}
					}
				}

			}
		}
		return result;
	}
}

class SideBySideViewZonesComputer extends ViewZonesComputer {

A
Alex Dima 已提交
1720
	constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
E
Erich Gamma 已提交
1721 1722 1723
		super(lineChanges, originalForeignVZ, modifiedForeignVZ);
	}

J
Johannes Rieken 已提交
1724
	_produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone {
E
Erich Gamma 已提交
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734
		if (lineChangeModifiedLength > lineChangeOriginalLength) {
			return {
				afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber),
				heightInLines: (lineChangeModifiedLength - lineChangeOriginalLength),
				domNode: null
			};
		}
		return null;
	}

J
Johannes Rieken 已提交
1735
	_produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone {
E
Erich Gamma 已提交
1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
		if (lineChangeOriginalLength > lineChangeModifiedLength) {
			return {
				afterLineNumber: Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber),
				heightInLines: (lineChangeOriginalLength - lineChangeModifiedLength),
				domNode: null
			};
		}
		return null;
	}
}

class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle {

	private decorationsLeft: number;

J
Johannes Rieken 已提交
1751
	constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) {
E
Erich Gamma 已提交
1752 1753
		super(dataSource);

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

1756
		this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: editorOptions.EditorLayoutInfo) => {
E
Erich Gamma 已提交
1757 1758 1759 1760 1761 1762 1763 1764
			if (this.decorationsLeft !== layoutInfo.decorationsLeft) {
				this.decorationsLeft = layoutInfo.decorationsLeft;
				dataSource.relayoutEditors();
			}
		}));
	}

	public dispose(): void {
A
Alex Dima 已提交
1765
		super.dispose();
E
Erich Gamma 已提交
1766 1767
	}

J
Johannes Rieken 已提交
1768
	public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void {
E
Erich Gamma 已提交
1769 1770 1771
		// Nothing to do..
	}

A
Alex Dima 已提交
1772
	_getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones {
1773
		let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators);
E
Erich Gamma 已提交
1774 1775 1776
		return computer.getViewZones();
	}

1777
	_getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
A
Alex Dima 已提交
1778
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1779 1780
			decorations: [],
			overviewZones: []
A
Alex Dima 已提交
1781
		};
E
Erich Gamma 已提交
1782

A
Alex Dima 已提交
1783 1784
		for (let i = 0, length = lineChanges.length; i < length; i++) {
			let lineChange = lineChanges[i];
E
Erich Gamma 已提交
1785 1786 1787

			// Add overview zones in the overview ruler
			if (isChangeOrDelete(lineChange)) {
1788 1789
				result.decorations.push({
					range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE),
1790
					options: DECORATIONS.lineDeleteMargin
1791 1792
				});

1793
				let color = this._removeColor.toString();
A
Alex Dima 已提交
1794
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1795 1796
					lineChange.originalStartLineNumber,
					lineChange.originalEndLineNumber,
1797
					color
A
Alex Dima 已提交
1798
				));
E
Erich Gamma 已提交
1799 1800 1801 1802 1803 1804
			}
		}

		return result;
	}

1805
	_getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
E
Erich Gamma 已提交
1806

A
Alex Dima 已提交
1807
		let result: IEditorDiffDecorations = {
J
Johannes Rieken 已提交
1808 1809
			decorations: [],
			overviewZones: []
A
Alex Dima 已提交
1810 1811 1812 1813 1814 1815
		};

		let modifiedModel = modifiedEditor.getModel();

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

			// Add decorations & overview zones
			if (isChangeOrInsert(lineChange)) {
1819 1820
				result.decorations.push({
					range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE),
1821
					options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert)
1822
				});
E
Erich Gamma 已提交
1823

1824
				let color = this._insertColor.toString();
A
Alex Dima 已提交
1825
				result.overviewZones.push(new OverviewRulerZone(
A
Alex Dima 已提交
1826 1827
					lineChange.modifiedStartLineNumber,
					lineChange.modifiedEndLineNumber,
1828
					color
A
Alex Dima 已提交
1829
				));
E
Erich Gamma 已提交
1830 1831

				if (lineChange.charChanges) {
A
Alex Dima 已提交
1832 1833
					for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
						let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1834 1835
						if (isChangeOrInsert(charChange)) {
							if (ignoreTrimWhitespace) {
A
Alex Dima 已提交
1836 1837 1838
								for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) {
									let startColumn: number;
									let endColumn: number;
E
Erich Gamma 已提交
1839 1840 1841 1842 1843 1844 1845 1846 1847 1848
									if (lineNumber === charChange.modifiedStartLineNumber) {
										startColumn = charChange.modifiedStartColumn;
									} else {
										startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber);
									}
									if (lineNumber === charChange.modifiedEndLineNumber) {
										endColumn = charChange.modifiedEndColumn;
									} else {
										endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber);
									}
1849
									result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1850 1851
								}
							} else {
1852
								result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert));
E
Erich Gamma 已提交
1853 1854 1855 1856
							}
						}
					}
				} else {
1857
					result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine));
E
Erich Gamma 已提交
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
				}
			}
		}

		return result;
	}

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

}

class InlineViewZonesComputer extends ViewZonesComputer {

J
Johannes Rieken 已提交
1874
	private originalModel: editorCommon.IModel;
1875
	private modifiedEditorConfiguration: editorOptions.InternalEditorOptions;
J
Johannes Rieken 已提交
1876
	private modifiedEditorTabSize: number;
R
rebornix 已提交
1877
	private renderIndicators: boolean;
E
Erich Gamma 已提交
1878

A
Alex Dima 已提交
1879
	constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) {
E
Erich Gamma 已提交
1880 1881 1882
		super(lineChanges, originalForeignVZ, modifiedForeignVZ);
		this.originalModel = originalEditor.getModel();
		this.modifiedEditorConfiguration = modifiedEditor.getConfiguration();
1883
		this.modifiedEditorTabSize = modifiedEditor.getModel().getOptions().tabSize;
R
rebornix 已提交
1884
		this.renderIndicators = renderIndicators;
E
Erich Gamma 已提交
1885 1886
	}

J
Johannes Rieken 已提交
1887
	_produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone {
1888 1889 1890 1891
		let marginDomNode = document.createElement('div');
		marginDomNode.className = 'inline-added-margin-view-zone';
		Configuration.applyFontInfoSlow(marginDomNode, this.modifiedEditorConfiguration.fontInfo);

E
Erich Gamma 已提交
1892
		return {
J
Johannes Rieken 已提交
1893
			afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber),
E
Erich Gamma 已提交
1894
			heightInLines: lineChangeModifiedLength,
1895 1896
			domNode: document.createElement('div'),
			marginDomNode: marginDomNode
E
Erich Gamma 已提交
1897 1898 1899
		};
	}

J
Johannes Rieken 已提交
1900
	_produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone {
A
Alex Dima 已提交
1901
		let decorations: InlineDecoration[] = [];
E
Erich Gamma 已提交
1902
		if (lineChange.charChanges) {
A
Alex Dima 已提交
1903 1904
			for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
				let charChange = lineChange.charChanges[j];
E
Erich Gamma 已提交
1905
				if (isChangeOrDelete(charChange)) {
1906 1907
					decorations.push(new InlineDecoration(
						new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn),
1908
						'char-delete',
1909
						InlineDecorationType.Regular
1910
					));
E
Erich Gamma 已提交
1911 1912 1913 1914
				}
			}
		}

1915
		let sb = createStringBuilder(10000);
1916 1917 1918
		let marginHTML: string[] = [];
		let lineDecorationsWidth = this.modifiedEditorConfiguration.layoutInfo.decorationsWidth;
		let lineHeight = this.modifiedEditorConfiguration.lineHeight;
A
Alex Dima 已提交
1919
		for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) {
1920
			this.renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations, sb);
1921

R
rebornix 已提交
1922
			if (this.renderIndicators) {
1923 1924 1925 1926 1927
				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 已提交
1928 1929
		}

A
Alex Dima 已提交
1930
		let domNode = document.createElement('div');
E
Erich Gamma 已提交
1931
		domNode.className = 'view-lines line-delete';
1932
		domNode.innerHTML = sb.build();
1933
		Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo);
E
Erich Gamma 已提交
1934

1935 1936 1937 1938 1939
		let marginDomNode = document.createElement('div');
		marginDomNode.className = 'inline-deleted-margin-view-zone';
		marginDomNode.innerHTML = marginHTML.join('');
		Configuration.applyFontInfoSlow(marginDomNode, this.modifiedEditorConfiguration.fontInfo);

E
Erich Gamma 已提交
1940 1941 1942 1943
		return {
			shouldNotShrink: true,
			afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1),
			heightInLines: lineChangeOriginalLength,
1944 1945
			domNode: domNode,
			marginDomNode: marginDomNode
E
Erich Gamma 已提交
1946 1947 1948
		};
	}

1949
	private renderOriginalLine(count: number, originalModel: editorCommon.IModel, config: editorOptions.InternalEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): void {
1950
		let lineContent = originalModel.getLineContent(lineNumber);
E
Erich Gamma 已提交
1951

A
Alex Dima 已提交
1952
		let actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1);
E
Erich Gamma 已提交
1953

A
Alex Dima 已提交
1954 1955 1956 1957 1958 1959
		const defaultMetadata = (
			(FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
			| (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET)
			| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
		) >>> 0;

1960 1961 1962 1963 1964 1965 1966 1967 1968 1969
		sb.appendASCIIString('<div class="view-line');
		if (decorations.length === 0) {
			// No char changes
			sb.appendASCIIString(' char-delete');
		}
		sb.appendASCIIString('" style="top:');
		sb.appendASCIIString(String(count * config.lineHeight));
		sb.appendASCIIString('px;width:1000000px;">');

		renderViewLine(new RenderLineInput(
1970
			(config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations),
A
Alex Dima 已提交
1971
			lineContent,
1972
			originalModel.mightContainRTL(),
1973
			0,
A
Alex Dima 已提交
1974
			[new ViewLineToken(lineContent.length, defaultMetadata)],
1975
			actualDecorations,
A
Alex Dima 已提交
1976
			tabSize,
1977
			config.fontInfo.spaceWidth,
1978 1979
			config.viewInfo.stopRenderingLineAfter,
			config.viewInfo.renderWhitespace,
1980 1981
			config.viewInfo.renderControlCharacters,
			config.viewInfo.fontLigatures
1982
		), sb);
E
Erich Gamma 已提交
1983

1984
		sb.appendASCIIString('</div>');
E
Erich Gamma 已提交
1985 1986 1987
	}
}

J
Johannes Rieken 已提交
1988
function isChangeOrInsert(lineChange: editorCommon.IChange): boolean {
E
Erich Gamma 已提交
1989 1990 1991
	return lineChange.modifiedEndLineNumber > 0;
}

J
Johannes Rieken 已提交
1992
function isChangeOrDelete(lineChange: editorCommon.IChange): boolean {
E
Erich Gamma 已提交
1993 1994 1995 1996
	return lineChange.originalEndLineNumber > 0;
}

function createFakeLinesDiv(): HTMLElement {
A
Alex Dima 已提交
1997
	let r = document.createElement('div');
E
Erich Gamma 已提交
1998 1999 2000
	r.className = 'diagonal-fill';
	return r;
}
2001 2002 2003 2004 2005

registerThemingParticipant((theme, collector) => {
	let added = theme.getColor(diffInserted);
	if (added) {
		collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { background-color: ${added}; }`);
A
Alex Dima 已提交
2006
		collector.addRule(`.monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: ${added}; }`);
2007 2008 2009 2010 2011
		collector.addRule(`.monaco-editor .inline-added-margin-view-zone { background-color: ${added}; }`);
	}
	let removed = theme.getColor(diffRemoved);
	if (removed) {
		collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { background-color: ${removed}; }`);
A
Alex Dima 已提交
2012
		collector.addRule(`.monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: ${removed}; }`);
2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
		collector.addRule(`.monaco-editor .inline-deleted-margin-view-zone { background-color: ${removed}; }`);
	}
	let addedOutline = theme.getColor(diffInsertedOutline);
	if (addedOutline) {
		collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px dashed ${addedOutline}; }`);
	}
	let removedOutline = theme.getColor(diffRemovedOutline);
	if (removedOutline) {
		collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px dashed ${removedOutline}; }`);
	}
2023 2024 2025 2026
	let shadow = theme.getColor(scrollbarShadow);
	if (shadow) {
		collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px ${shadow}; }`);
	}
2027
});