mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
* Merge from vscode e3b9b8eefc062d68ba8a4b6a817162d132f3b533 * skip failing test * add comment
2152 lines
80 KiB
TypeScript
2152 lines
80 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import 'vs/css!./media/diffEditor';
|
|
import * as nls from 'vs/nls';
|
|
import * as dom from 'vs/base/browser/dom';
|
|
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
|
import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash';
|
|
import { RunOnceScheduler, IntervalTimer } from 'vs/base/common/async';
|
|
import { Color } from 'vs/base/common/color';
|
|
import { Emitter, Event } from 'vs/base/common/event';
|
|
import { Disposable } from 'vs/base/common/lifecycle';
|
|
import * as objects from 'vs/base/common/objects';
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { Configuration } from 'vs/editor/browser/config/configuration';
|
|
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
|
|
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
|
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
|
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
|
import { DiffReview } from 'vs/editor/browser/widget/diffReview';
|
|
import * as editorOptions from 'vs/editor/common/config/editorOptions';
|
|
import { IPosition, Position } from 'vs/editor/common/core/position';
|
|
import { IRange, Range } from 'vs/editor/common/core/range';
|
|
import { ISelection, Selection } from 'vs/editor/common/core/selection';
|
|
import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder';
|
|
import * as editorCommon from 'vs/editor/common/editorCommon';
|
|
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
|
|
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
|
import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
|
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
|
|
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
|
|
import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer';
|
|
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
|
import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
|
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
|
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
|
|
import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
|
import { reverseLineChanges } from 'sql/editor/browser/diffEditorHelper'; // {{SQL CARBON EDIT}}
|
|
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
|
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
|
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
|
|
|
interface IEditorDiffDecorations {
|
|
decorations: IModelDeltaDecoration[];
|
|
overviewZones: OverviewRulerZone[];
|
|
}
|
|
|
|
interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations {
|
|
zones: IMyViewZone[];
|
|
}
|
|
|
|
interface IEditorsDiffDecorationsWithZones {
|
|
original: IEditorDiffDecorationsWithZones;
|
|
modified: IEditorDiffDecorationsWithZones;
|
|
}
|
|
|
|
interface IEditorsZones {
|
|
original: IMyViewZone[];
|
|
modified: IMyViewZone[];
|
|
}
|
|
|
|
interface IDiffEditorWidgetStyle {
|
|
// {{SQL CARBON EDIT}}
|
|
getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, reverse?: boolean): IEditorsDiffDecorationsWithZones;
|
|
setEnableSplitViewResizing(enableSplitViewResizing: boolean): void;
|
|
applyColors(theme: ITheme): boolean;
|
|
layout(): number;
|
|
dispose(): void;
|
|
}
|
|
|
|
class VisualEditorState {
|
|
private _zones: string[];
|
|
private inlineDiffMargins: InlineDiffMargin[];
|
|
private _zonesMap: { [zoneId: string]: boolean; };
|
|
private _decorations: string[];
|
|
|
|
constructor(
|
|
private _contextMenuService: IContextMenuService,
|
|
private _clipboardService: IClipboardService
|
|
) {
|
|
this._zones = [];
|
|
this.inlineDiffMargins = [];
|
|
this._zonesMap = {};
|
|
this._decorations = [];
|
|
}
|
|
|
|
public getForeignViewZones(allViewZones: IEditorWhitespace[]): IEditorWhitespace[] {
|
|
return allViewZones.filter((z) => !this._zonesMap[String(z.id)]);
|
|
}
|
|
|
|
public clean(editor: CodeEditorWidget): void {
|
|
// (1) View zones
|
|
if (this._zones.length > 0) {
|
|
editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => {
|
|
for (let i = 0, length = this._zones.length; i < length; i++) {
|
|
viewChangeAccessor.removeZone(this._zones[i]);
|
|
}
|
|
});
|
|
}
|
|
this._zones = [];
|
|
this._zonesMap = {};
|
|
|
|
// (2) Model decorations
|
|
this._decorations = editor.deltaDecorations(this._decorations, []);
|
|
}
|
|
|
|
public apply(editor: CodeEditorWidget, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones, restoreScrollState: boolean): void {
|
|
|
|
const scrollState = restoreScrollState ? StableEditorScrollState.capture(editor) : null;
|
|
|
|
// view zones
|
|
editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => {
|
|
for (let i = 0, length = this._zones.length; i < length; i++) {
|
|
viewChangeAccessor.removeZone(this._zones[i]);
|
|
}
|
|
for (let i = 0, length = this.inlineDiffMargins.length; i < length; i++) {
|
|
this.inlineDiffMargins[i].dispose();
|
|
}
|
|
this._zones = [];
|
|
this._zonesMap = {};
|
|
this.inlineDiffMargins = [];
|
|
for (let i = 0, length = newDecorations.zones.length; i < length; i++) {
|
|
const viewZone = <editorBrowser.IViewZone>newDecorations.zones[i];
|
|
viewZone.suppressMouseDown = false;
|
|
let zoneId = viewChangeAccessor.addZone(viewZone);
|
|
this._zones.push(zoneId);
|
|
this._zonesMap[String(zoneId)] = true;
|
|
|
|
if (newDecorations.zones[i].diff && viewZone.marginDomNode) {
|
|
this.inlineDiffMargins.push(new InlineDiffMargin(viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService));
|
|
}
|
|
}
|
|
});
|
|
|
|
if (scrollState) {
|
|
scrollState.restore(editor);
|
|
}
|
|
|
|
// decorations
|
|
this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations);
|
|
|
|
// overview ruler
|
|
if (overviewRuler) {
|
|
overviewRuler.setZones(newDecorations.overviewZones);
|
|
}
|
|
}
|
|
}
|
|
|
|
let DIFF_EDITOR_ID = 0;
|
|
|
|
export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor {
|
|
|
|
private static readonly ONE_OVERVIEW_WIDTH = 15;
|
|
public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = 30;
|
|
private static readonly UPDATE_DIFF_DECORATIONS_DELAY = 200; // ms
|
|
|
|
private readonly _onDidDispose: Emitter<void> = this._register(new Emitter<void>());
|
|
public readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
|
|
|
private readonly _onDidUpdateDiff: Emitter<void> = this._register(new Emitter<void>());
|
|
public readonly onDidUpdateDiff: Event<void> = this._onDidUpdateDiff.event;
|
|
|
|
private readonly id: number;
|
|
|
|
private readonly _domElement: HTMLElement;
|
|
protected readonly _containerDomElement: HTMLElement;
|
|
private readonly _overviewDomElement: HTMLElement;
|
|
private readonly _overviewViewportDomElement: FastDomNode<HTMLElement>;
|
|
|
|
private _width: number;
|
|
private _height: number;
|
|
private _reviewHeight: number;
|
|
private readonly _measureDomElementToken: IntervalTimer | null;
|
|
|
|
private readonly originalEditor: CodeEditorWidget;
|
|
private readonly _originalDomNode: HTMLElement;
|
|
private readonly _originalEditorState: VisualEditorState;
|
|
private _originalOverviewRuler: editorBrowser.IOverviewRuler | null;
|
|
|
|
private readonly modifiedEditor: CodeEditorWidget;
|
|
private readonly _modifiedDomNode: HTMLElement;
|
|
private readonly _modifiedEditorState: VisualEditorState;
|
|
private _modifiedOverviewRuler: editorBrowser.IOverviewRuler | null;
|
|
|
|
private _currentlyChangingViewZones: boolean;
|
|
private _beginUpdateDecorationsTimeout: number;
|
|
private _diffComputationToken: number;
|
|
private _diffComputationResult: IDiffComputationResult | null;
|
|
|
|
private _isVisible: boolean;
|
|
private _isHandlingScrollEvent: boolean;
|
|
|
|
private _ignoreTrimWhitespace: boolean;
|
|
private _originalIsEditable: boolean;
|
|
|
|
private _renderSideBySide: boolean;
|
|
private _renderIndicators: boolean;
|
|
private _enableSplitViewResizing: boolean;
|
|
private _strategy!: IDiffEditorWidgetStyle;
|
|
|
|
private readonly _updateDecorationsRunner: RunOnceScheduler;
|
|
|
|
private readonly _editorWorkerService: IEditorWorkerService;
|
|
protected _contextKeyService: IContextKeyService;
|
|
private readonly _codeEditorService: ICodeEditorService;
|
|
private readonly _themeService: IThemeService;
|
|
private readonly _notificationService: INotificationService;
|
|
|
|
private readonly _reviewPane: DiffReview;
|
|
// {{SQL CARBON EDIT}}
|
|
private _options: editorOptions.IDiffEditorOptions;
|
|
|
|
constructor(
|
|
domElement: HTMLElement,
|
|
options: editorOptions.IDiffEditorOptions,
|
|
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
|
|
@IContextKeyService contextKeyService: IContextKeyService,
|
|
@IInstantiationService instantiationService: IInstantiationService,
|
|
@ICodeEditorService codeEditorService: ICodeEditorService,
|
|
@IThemeService themeService: IThemeService,
|
|
@INotificationService notificationService: INotificationService,
|
|
@IContextMenuService contextMenuService: IContextMenuService,
|
|
@IClipboardService clipboardService: IClipboardService
|
|
) {
|
|
super();
|
|
|
|
this._editorWorkerService = editorWorkerService;
|
|
this._codeEditorService = codeEditorService;
|
|
this._contextKeyService = this._register(contextKeyService.createScoped(domElement));
|
|
this._contextKeyService.createKey('isInDiffEditor', true);
|
|
this._themeService = themeService;
|
|
this._notificationService = notificationService;
|
|
// {{SQL CARBON EDIT}}
|
|
this._options = options;
|
|
|
|
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;
|
|
}
|
|
|
|
// renderIndicators
|
|
this._renderIndicators = true;
|
|
if (typeof options.renderIndicators !== 'undefined') {
|
|
this._renderIndicators = options.renderIndicators;
|
|
}
|
|
|
|
this._originalIsEditable = false;
|
|
if (typeof options.originalEditable !== 'undefined') {
|
|
this._originalIsEditable = Boolean(options.originalEditable);
|
|
}
|
|
|
|
this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0));
|
|
|
|
this._containerDomElement = document.createElement('div');
|
|
this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
|
|
this._containerDomElement.style.position = 'relative';
|
|
this._containerDomElement.style.height = '100%';
|
|
this._domElement.appendChild(this._containerDomElement);
|
|
|
|
this._overviewViewportDomElement = createFastDomNode(document.createElement('div'));
|
|
this._overviewViewportDomElement.setClassName('diffViewport');
|
|
this._overviewViewportDomElement.setPosition('absolute');
|
|
|
|
this._overviewDomElement = document.createElement('div');
|
|
this._overviewDomElement.className = 'diffOverview';
|
|
this._overviewDomElement.style.position = 'absolute';
|
|
|
|
this._overviewDomElement.appendChild(this._overviewViewportDomElement.domNode);
|
|
|
|
this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => {
|
|
this.modifiedEditor.delegateVerticalScrollbarMouseDown(e);
|
|
}));
|
|
this._containerDomElement.appendChild(this._overviewDomElement);
|
|
|
|
// Create left side
|
|
this._originalDomNode = document.createElement('div');
|
|
this._originalDomNode.className = 'editor original';
|
|
this._originalDomNode.style.position = 'absolute';
|
|
this._originalDomNode.style.height = '100%';
|
|
this._containerDomElement.appendChild(this._originalDomNode);
|
|
|
|
// Create right side
|
|
this._modifiedDomNode = document.createElement('div');
|
|
this._modifiedDomNode.className = 'editor modified';
|
|
this._modifiedDomNode.style.position = 'absolute';
|
|
this._modifiedDomNode.style.height = '100%';
|
|
this._containerDomElement.appendChild(this._modifiedDomNode);
|
|
|
|
this._beginUpdateDecorationsTimeout = -1;
|
|
this._currentlyChangingViewZones = false;
|
|
this._diffComputationToken = 0;
|
|
|
|
this._originalEditorState = new VisualEditorState(contextMenuService, clipboardService);
|
|
this._modifiedEditorState = new VisualEditorState(contextMenuService, clipboardService);
|
|
|
|
this._isVisible = true;
|
|
this._isHandlingScrollEvent = false;
|
|
|
|
this._width = 0;
|
|
this._height = 0;
|
|
this._reviewHeight = 0;
|
|
|
|
this._diffComputationResult = null;
|
|
|
|
const leftContextKeyService = this._contextKeyService.createScoped();
|
|
leftContextKeyService.createKey('isInDiffLeftEditor', true);
|
|
|
|
const leftServices = new ServiceCollection();
|
|
leftServices.set(IContextKeyService, leftContextKeyService);
|
|
const leftScopedInstantiationService = instantiationService.createChild(leftServices);
|
|
|
|
const rightContextKeyService = this._contextKeyService.createScoped();
|
|
rightContextKeyService.createKey('isInDiffRightEditor', true);
|
|
|
|
const rightServices = new ServiceCollection();
|
|
rightServices.set(IContextKeyService, rightContextKeyService);
|
|
const rightScopedInstantiationService = instantiationService.createChild(rightServices);
|
|
|
|
this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService);
|
|
this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService);
|
|
|
|
this._originalOverviewRuler = null;
|
|
this._modifiedOverviewRuler = null;
|
|
|
|
this._reviewPane = new DiffReview(this);
|
|
this._containerDomElement.appendChild(this._reviewPane.domNode.domNode);
|
|
this._containerDomElement.appendChild(this._reviewPane.shadow.domNode);
|
|
this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode);
|
|
|
|
if (options.automaticLayout) {
|
|
this._measureDomElementToken = new IntervalTimer();
|
|
this._measureDomElementToken.cancelAndSet(() => this._measureDomElement(false), 100);
|
|
} else {
|
|
this._measureDomElementToken = null;
|
|
}
|
|
|
|
// enableSplitViewResizing
|
|
this._enableSplitViewResizing = true;
|
|
if (typeof options.enableSplitViewResizing !== 'undefined') {
|
|
this._enableSplitViewResizing = options.enableSplitViewResizing;
|
|
}
|
|
|
|
if (this._renderSideBySide) {
|
|
this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
|
|
} else {
|
|
this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
|
|
}
|
|
|
|
this._register(themeService.onThemeChange(t => {
|
|
if (this._strategy && this._strategy.applyColors(t)) {
|
|
this._updateDecorationsRunner.schedule();
|
|
}
|
|
this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
|
|
}));
|
|
|
|
this._codeEditorService.addDiffEditor(this);
|
|
}
|
|
|
|
public get ignoreTrimWhitespace(): boolean {
|
|
return this._ignoreTrimWhitespace;
|
|
}
|
|
|
|
public get renderSideBySide(): boolean {
|
|
return this._renderSideBySide;
|
|
}
|
|
|
|
public get renderIndicators(): boolean {
|
|
return this._renderIndicators;
|
|
}
|
|
|
|
public hasWidgetFocus(): boolean {
|
|
return dom.isAncestor(document.activeElement, this._domElement);
|
|
}
|
|
|
|
public diffReviewNext(): void {
|
|
this._reviewPane.next();
|
|
}
|
|
|
|
public diffReviewPrev(): void {
|
|
this._reviewPane.prev();
|
|
}
|
|
|
|
private static _getClassName(theme: ITheme, renderSideBySide: boolean): string {
|
|
let result = 'monaco-diff-editor monaco-editor-background ';
|
|
if (renderSideBySide) {
|
|
result += 'side-by-side ';
|
|
}
|
|
result += getThemeTypeSelector(theme.type);
|
|
return result;
|
|
}
|
|
|
|
private _recreateOverviewRulers(): void {
|
|
if (this._originalOverviewRuler) {
|
|
this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode());
|
|
this._originalOverviewRuler.dispose();
|
|
}
|
|
if (this.originalEditor.hasModel()) {
|
|
this._originalOverviewRuler = this.originalEditor.createOverviewRuler('original diffOverviewRuler')!;
|
|
this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode());
|
|
}
|
|
|
|
if (this._modifiedOverviewRuler) {
|
|
this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode());
|
|
this._modifiedOverviewRuler.dispose();
|
|
}
|
|
if (this.modifiedEditor.hasModel()) {
|
|
this._modifiedOverviewRuler = this.modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!;
|
|
this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode());
|
|
}
|
|
|
|
this._layoutOverviewRulers();
|
|
}
|
|
|
|
private _createLeftHandSideEditor(options: editorOptions.IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget {
|
|
const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable));
|
|
|
|
this._register(editor.onDidScrollChange((e) => {
|
|
if (this._isHandlingScrollEvent) {
|
|
return;
|
|
}
|
|
if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) {
|
|
return;
|
|
}
|
|
this._isHandlingScrollEvent = true;
|
|
this.modifiedEditor.setScrollPosition({
|
|
scrollLeft: e.scrollLeft,
|
|
scrollTop: e.scrollTop
|
|
});
|
|
this._isHandlingScrollEvent = false;
|
|
|
|
this._layoutOverviewViewport();
|
|
}));
|
|
|
|
this._register(editor.onDidChangeViewZones(() => {
|
|
this._onViewZonesChanged();
|
|
}));
|
|
|
|
this._register(editor.onDidChangeModelContent(() => {
|
|
if (this._isVisible) {
|
|
this._beginUpdateDecorationsSoon();
|
|
}
|
|
}));
|
|
|
|
return editor;
|
|
}
|
|
|
|
private _createRightHandSideEditor(options: editorOptions.IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget {
|
|
const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options));
|
|
|
|
this._register(editor.onDidScrollChange((e) => {
|
|
if (this._isHandlingScrollEvent) {
|
|
return;
|
|
}
|
|
if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) {
|
|
return;
|
|
}
|
|
this._isHandlingScrollEvent = true;
|
|
this.originalEditor.setScrollPosition({
|
|
scrollLeft: e.scrollLeft,
|
|
scrollTop: e.scrollTop
|
|
});
|
|
this._isHandlingScrollEvent = false;
|
|
|
|
this._layoutOverviewViewport();
|
|
}));
|
|
|
|
this._register(editor.onDidChangeViewZones(() => {
|
|
this._onViewZonesChanged();
|
|
}));
|
|
|
|
this._register(editor.onDidChangeConfiguration((e) => {
|
|
if (e.fontInfo && editor.getModel()) {
|
|
this._onViewZonesChanged();
|
|
}
|
|
}));
|
|
|
|
this._register(editor.onDidChangeModelContent(() => {
|
|
if (this._isVisible) {
|
|
this._beginUpdateDecorationsSoon();
|
|
}
|
|
}));
|
|
|
|
return editor;
|
|
}
|
|
|
|
protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: editorOptions.IEditorOptions): CodeEditorWidget {
|
|
return instantiationService.createInstance(CodeEditorWidget, container, options, {});
|
|
}
|
|
|
|
public dispose(): void {
|
|
this._codeEditorService.removeDiffEditor(this);
|
|
|
|
if (this._beginUpdateDecorationsTimeout !== -1) {
|
|
window.clearTimeout(this._beginUpdateDecorationsTimeout);
|
|
this._beginUpdateDecorationsTimeout = -1;
|
|
}
|
|
|
|
if (this._measureDomElementToken) {
|
|
this._measureDomElementToken.dispose();
|
|
}
|
|
|
|
this._cleanViewZonesAndDecorations();
|
|
|
|
if (this._originalOverviewRuler) {
|
|
this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode());
|
|
this._originalOverviewRuler.dispose();
|
|
}
|
|
if (this._modifiedOverviewRuler) {
|
|
this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode());
|
|
this._modifiedOverviewRuler.dispose();
|
|
}
|
|
this._overviewDomElement.removeChild(this._overviewViewportDomElement.domNode);
|
|
this._containerDomElement.removeChild(this._overviewDomElement);
|
|
|
|
this._containerDomElement.removeChild(this._originalDomNode);
|
|
this.originalEditor.dispose();
|
|
|
|
this._containerDomElement.removeChild(this._modifiedDomNode);
|
|
this.modifiedEditor.dispose();
|
|
|
|
this._strategy.dispose();
|
|
|
|
this._containerDomElement.removeChild(this._reviewPane.domNode.domNode);
|
|
this._containerDomElement.removeChild(this._reviewPane.shadow.domNode);
|
|
this._containerDomElement.removeChild(this._reviewPane.actionBarContainer.domNode);
|
|
this._reviewPane.dispose();
|
|
|
|
this._domElement.removeChild(this._containerDomElement);
|
|
|
|
this._onDidDispose.fire();
|
|
|
|
super.dispose();
|
|
}
|
|
|
|
//------------ begin IDiffEditor methods
|
|
|
|
public getId(): string {
|
|
return this.getEditorType() + ':' + this.id;
|
|
}
|
|
|
|
public getEditorType(): string {
|
|
return editorCommon.EditorType.IDiffEditor;
|
|
}
|
|
|
|
public getLineChanges(): editorCommon.ILineChange[] | null {
|
|
if (!this._diffComputationResult) {
|
|
return null;
|
|
}
|
|
return this._diffComputationResult.changes;
|
|
}
|
|
|
|
public getOriginalEditor(): editorBrowser.ICodeEditor {
|
|
return this.originalEditor;
|
|
}
|
|
|
|
public getModifiedEditor(): editorBrowser.ICodeEditor {
|
|
return this.modifiedEditor;
|
|
}
|
|
|
|
public updateOptions(newOptions: editorOptions.IDiffEditorOptions): void {
|
|
|
|
// Handle side by side
|
|
let renderSideBySideChanged = false;
|
|
if (typeof newOptions.renderSideBySide !== 'undefined') {
|
|
if (this._renderSideBySide !== newOptions.renderSideBySide) {
|
|
this._renderSideBySide = newOptions.renderSideBySide;
|
|
renderSideBySideChanged = true;
|
|
}
|
|
}
|
|
|
|
let beginUpdateDecorations = false;
|
|
|
|
if (typeof newOptions.ignoreTrimWhitespace !== 'undefined') {
|
|
if (this._ignoreTrimWhitespace !== newOptions.ignoreTrimWhitespace) {
|
|
this._ignoreTrimWhitespace = newOptions.ignoreTrimWhitespace;
|
|
// Begin comparing
|
|
beginUpdateDecorations = true;
|
|
}
|
|
}
|
|
|
|
if (typeof newOptions.renderIndicators !== 'undefined') {
|
|
if (this._renderIndicators !== newOptions.renderIndicators) {
|
|
this._renderIndicators = newOptions.renderIndicators;
|
|
beginUpdateDecorations = true;
|
|
}
|
|
}
|
|
|
|
if (beginUpdateDecorations) {
|
|
this._beginUpdateDecorations();
|
|
}
|
|
|
|
if (typeof newOptions.originalEditable !== 'undefined') {
|
|
this._originalIsEditable = Boolean(newOptions.originalEditable);
|
|
}
|
|
|
|
this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions));
|
|
this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable));
|
|
|
|
// enableSplitViewResizing
|
|
if (typeof newOptions.enableSplitViewResizing !== 'undefined') {
|
|
this._enableSplitViewResizing = newOptions.enableSplitViewResizing;
|
|
}
|
|
this._strategy.setEnableSplitViewResizing(this._enableSplitViewResizing);
|
|
|
|
// renderSideBySide
|
|
if (renderSideBySideChanged) {
|
|
if (this._renderSideBySide) {
|
|
this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
|
|
} else {
|
|
this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
|
|
}
|
|
// Update class name
|
|
this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
|
|
}
|
|
}
|
|
|
|
public getModel(): editorCommon.IDiffEditorModel {
|
|
return {
|
|
original: this.originalEditor.getModel()!,
|
|
modified: this.modifiedEditor.getModel()!
|
|
};
|
|
}
|
|
|
|
public setModel(model: editorCommon.IDiffEditorModel): void {
|
|
// 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._diffComputationResult = null;
|
|
this._diffComputationToken++;
|
|
|
|
if (model) {
|
|
this._recreateOverviewRulers();
|
|
|
|
// Begin comparing
|
|
this._beginUpdateDecorations();
|
|
} else {
|
|
this._diffComputationResult = null;
|
|
}
|
|
|
|
this._layoutOverviewViewport();
|
|
}
|
|
|
|
public getDomNode(): HTMLElement {
|
|
return this._domElement;
|
|
}
|
|
|
|
public getVisibleColumnFromPosition(position: IPosition): number {
|
|
return this.modifiedEditor.getVisibleColumnFromPosition(position);
|
|
}
|
|
|
|
public getPosition(): Position | null {
|
|
return this.modifiedEditor.getPosition();
|
|
}
|
|
|
|
public setPosition(position: IPosition): void {
|
|
this.modifiedEditor.setPosition(position);
|
|
}
|
|
|
|
public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealLine(lineNumber, scrollType);
|
|
}
|
|
|
|
public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealLineInCenter(lineNumber, scrollType);
|
|
}
|
|
|
|
public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType);
|
|
}
|
|
|
|
public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealPosition(position, scrollType);
|
|
}
|
|
|
|
public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealPositionInCenter(position, scrollType);
|
|
}
|
|
|
|
public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType);
|
|
}
|
|
|
|
public getSelection(): Selection | null {
|
|
return this.modifiedEditor.getSelection();
|
|
}
|
|
|
|
public getSelections(): Selection[] | null {
|
|
return this.modifiedEditor.getSelections();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public setSelections(ranges: ISelection[]): void {
|
|
this.modifiedEditor.setSelections(ranges);
|
|
}
|
|
|
|
public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType);
|
|
}
|
|
|
|
public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType);
|
|
}
|
|
|
|
public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType);
|
|
}
|
|
|
|
public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void {
|
|
this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal);
|
|
}
|
|
|
|
public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealRangeInCenter(range, scrollType);
|
|
}
|
|
|
|
public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType);
|
|
}
|
|
|
|
public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
|
|
this.modifiedEditor.revealRangeAtTop(range, scrollType);
|
|
}
|
|
|
|
public getSupportedActions(): editorCommon.IEditorAction[] {
|
|
return this.modifiedEditor.getSupportedActions();
|
|
}
|
|
|
|
public saveViewState(): editorCommon.IDiffEditorViewState {
|
|
let originalViewState = this.originalEditor.saveViewState();
|
|
let modifiedViewState = this.modifiedEditor.saveViewState();
|
|
return {
|
|
original: originalViewState,
|
|
modified: modifiedViewState
|
|
};
|
|
}
|
|
|
|
public restoreViewState(s: editorCommon.IDiffEditorViewState): void {
|
|
if (s.original && s.modified) {
|
|
let diffEditorState = <editorCommon.IDiffEditorViewState>s;
|
|
this.originalEditor.restoreViewState(diffEditorState.original);
|
|
this.modifiedEditor.restoreViewState(diffEditorState.modified);
|
|
}
|
|
}
|
|
|
|
public layout(dimension?: editorCommon.IDimension): void {
|
|
this._measureDomElement(false, dimension);
|
|
}
|
|
|
|
public focus(): void {
|
|
this.modifiedEditor.focus();
|
|
}
|
|
|
|
public hasTextFocus(): boolean {
|
|
return this.originalEditor.hasTextFocus() || this.modifiedEditor.hasTextFocus();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
public trigger(source: string, handlerId: string, payload: any): void {
|
|
this.modifiedEditor.trigger(source, handlerId, payload);
|
|
}
|
|
|
|
public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any {
|
|
return this.modifiedEditor.changeDecorations(callback);
|
|
}
|
|
|
|
//------------ end IDiffEditor methods
|
|
|
|
|
|
|
|
//------------ begin layouting methods
|
|
|
|
private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void {
|
|
dimensions = dimensions || {
|
|
width: this._containerDomElement.clientWidth,
|
|
height: this._containerDomElement.clientHeight
|
|
};
|
|
|
|
if (dimensions.width <= 0) {
|
|
this._width = 0;
|
|
this._height = 0;
|
|
this._reviewHeight = 0;
|
|
return;
|
|
}
|
|
|
|
if (!forceDoLayoutCall && dimensions.width === this._width && dimensions.height === this._height) {
|
|
// Nothing has changed
|
|
return;
|
|
}
|
|
|
|
this._width = dimensions.width;
|
|
this._height = dimensions.height;
|
|
this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0;
|
|
|
|
this._doLayout();
|
|
}
|
|
|
|
private _layoutOverviewRulers(): void {
|
|
if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) {
|
|
return;
|
|
}
|
|
let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH;
|
|
let layoutInfo = this.modifiedEditor.getLayoutInfo();
|
|
if (layoutInfo) {
|
|
this._originalOverviewRuler.setLayout({
|
|
top: 0,
|
|
width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
|
|
right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH,
|
|
height: (this._height - this._reviewHeight)
|
|
});
|
|
this._modifiedOverviewRuler.setLayout({
|
|
top: 0,
|
|
right: 0,
|
|
width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
|
|
height: (this._height - this._reviewHeight)
|
|
});
|
|
}
|
|
}
|
|
|
|
//------------ end layouting methods
|
|
|
|
private _onViewZonesChanged(): void {
|
|
if (this._currentlyChangingViewZones) {
|
|
return;
|
|
}
|
|
this._updateDecorationsRunner.schedule();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private _lastOriginalWarning: URI | null = null;
|
|
private _lastModifiedWarning: URI | null = null;
|
|
|
|
private static _equals(a: URI | null, b: URI | null): boolean {
|
|
if (!a && !b) {
|
|
return true;
|
|
}
|
|
if (!a || !b) {
|
|
return false;
|
|
}
|
|
return (a.toString() === b.toString());
|
|
}
|
|
|
|
private _beginUpdateDecorations(): void {
|
|
this._beginUpdateDecorationsTimeout = -1;
|
|
const currentOriginalModel = this.originalEditor.getModel();
|
|
const currentModifiedModel = this.modifiedEditor.getModel();
|
|
if (!currentOriginalModel || !currentModifiedModel) {
|
|
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++;
|
|
let currentToken = this._diffComputationToken;
|
|
|
|
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._notificationService.warn(nls.localize("diff.tooLarge", "Cannot compare files because one file is too large."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace).then((result) => {
|
|
if (currentToken === this._diffComputationToken
|
|
&& currentOriginalModel === this.originalEditor.getModel()
|
|
&& currentModifiedModel === this.modifiedEditor.getModel()
|
|
) {
|
|
this._diffComputationResult = result;
|
|
this._updateDecorationsRunner.schedule();
|
|
this._onDidUpdateDiff.fire();
|
|
}
|
|
}, (error) => {
|
|
if (currentToken === this._diffComputationToken
|
|
&& currentOriginalModel === this.originalEditor.getModel()
|
|
&& currentModifiedModel === this.modifiedEditor.getModel()
|
|
) {
|
|
this._diffComputationResult = null;
|
|
this._updateDecorationsRunner.schedule();
|
|
}
|
|
});
|
|
}
|
|
|
|
private _cleanViewZonesAndDecorations(): void {
|
|
this._originalEditorState.clean(this.originalEditor);
|
|
this._modifiedEditorState.clean(this.modifiedEditor);
|
|
}
|
|
|
|
private _updateDecorations(): void {
|
|
if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) {
|
|
return;
|
|
}
|
|
const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []);
|
|
|
|
let foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces());
|
|
let foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces());
|
|
|
|
// {{SQL CARBON EDIT}}
|
|
let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor, this._options.reverse);
|
|
|
|
try {
|
|
this._currentlyChangingViewZones = true;
|
|
this._originalEditorState.apply(this.originalEditor, this._originalOverviewRuler, diffDecorations.original, false);
|
|
this._modifiedEditorState.apply(this.modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true);
|
|
} finally {
|
|
this._currentlyChangingViewZones = false;
|
|
}
|
|
}
|
|
|
|
private _adjustOptionsForSubEditor(options: editorOptions.IDiffEditorOptions): editorOptions.IDiffEditorOptions {
|
|
let clonedOptions: editorOptions.IDiffEditorOptions = objects.deepClone(options || {});
|
|
clonedOptions.inDiffEditor = true;
|
|
clonedOptions.wordWrap = 'off';
|
|
clonedOptions.wordWrapMinified = false;
|
|
clonedOptions.automaticLayout = false;
|
|
clonedOptions.scrollbar = clonedOptions.scrollbar || {};
|
|
clonedOptions.scrollbar.vertical = 'visible';
|
|
clonedOptions.folding = false;
|
|
clonedOptions.codeLens = false;
|
|
clonedOptions.fixedOverflowWidgets = true;
|
|
// clonedOptions.lineDecorationsWidth = '2ch';
|
|
if (!clonedOptions.minimap) {
|
|
clonedOptions.minimap = {};
|
|
}
|
|
clonedOptions.minimap.enabled = false;
|
|
return clonedOptions;
|
|
}
|
|
|
|
private _adjustOptionsForLeftHandSide(options: editorOptions.IDiffEditorOptions, isEditable: boolean): editorOptions.IEditorOptions {
|
|
let result = this._adjustOptionsForSubEditor(options);
|
|
result.readOnly = !isEditable;
|
|
result.overviewRulerLanes = 1;
|
|
result.extraEditorClassName = 'original-in-monaco-diff-editor';
|
|
return result;
|
|
}
|
|
|
|
private _adjustOptionsForRightHandSide(options: editorOptions.IDiffEditorOptions): editorOptions.IEditorOptions {
|
|
let result = this._adjustOptionsForSubEditor(options);
|
|
result.revealHorizontalRightPadding = editorOptions.EDITOR_DEFAULTS.viewInfo.revealHorizontalRightPadding + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
|
|
result.scrollbar!.verticalHasArrows = false;
|
|
result.extraEditorClassName = 'modified-in-monaco-diff-editor';
|
|
return result;
|
|
}
|
|
|
|
public doLayout(): void {
|
|
this._measureDomElement(true);
|
|
}
|
|
|
|
private _doLayout(): void {
|
|
let splitPoint = this._strategy.layout();
|
|
|
|
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';
|
|
this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px';
|
|
this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px';
|
|
this._overviewDomElement.style.left = (this._width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px';
|
|
this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH);
|
|
this._overviewViewportDomElement.setHeight(30);
|
|
|
|
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) });
|
|
|
|
if (this._originalOverviewRuler || this._modifiedOverviewRuler) {
|
|
this._layoutOverviewRulers();
|
|
}
|
|
|
|
this._reviewPane.layout(this._height - this._reviewHeight, this._width, this._reviewHeight);
|
|
|
|
this._layoutOverviewViewport();
|
|
}
|
|
|
|
private _layoutOverviewViewport(): void {
|
|
let layout = this._computeOverviewViewport();
|
|
if (!layout) {
|
|
this._overviewViewportDomElement.setTop(0);
|
|
this._overviewViewportDomElement.setHeight(0);
|
|
} else {
|
|
this._overviewViewportDomElement.setTop(layout.top);
|
|
this._overviewViewportDomElement.setHeight(layout.height);
|
|
}
|
|
}
|
|
|
|
private _computeOverviewViewport(): { height: number; top: number; } | null {
|
|
let layoutInfo = this.modifiedEditor.getLayoutInfo();
|
|
if (!layoutInfo) {
|
|
return null;
|
|
}
|
|
|
|
let scrollTop = this.modifiedEditor.getScrollTop();
|
|
let scrollHeight = this.modifiedEditor.getScrollHeight();
|
|
|
|
let computedAvailableSize = Math.max(0, layoutInfo.contentHeight);
|
|
let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0);
|
|
let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0;
|
|
|
|
let computedSliderSize = Math.max(0, Math.floor(layoutInfo.contentHeight * computedRatio));
|
|
let computedSliderPosition = Math.floor(scrollTop * computedRatio);
|
|
|
|
return {
|
|
height: computedSliderSize,
|
|
top: computedSliderPosition
|
|
};
|
|
}
|
|
|
|
private _createDataSource(): IDataSource {
|
|
return {
|
|
getWidth: () => {
|
|
return this._width;
|
|
},
|
|
|
|
getHeight: () => {
|
|
return (this._height - this._reviewHeight);
|
|
},
|
|
|
|
getContainerDomNode: () => {
|
|
return this._containerDomElement;
|
|
},
|
|
|
|
relayoutEditors: () => {
|
|
this._doLayout();
|
|
},
|
|
|
|
getOriginalEditor: () => {
|
|
return this.originalEditor;
|
|
},
|
|
|
|
getModifiedEditor: () => {
|
|
return this.modifiedEditor;
|
|
}
|
|
};
|
|
}
|
|
|
|
private _setStrategy(newStrategy: IDiffEditorWidgetStyle): void {
|
|
if (this._strategy) {
|
|
this._strategy.dispose();
|
|
}
|
|
|
|
this._strategy = newStrategy;
|
|
newStrategy.applyColors(this._themeService.getTheme());
|
|
|
|
if (this._diffComputationResult) {
|
|
this._updateDecorations();
|
|
}
|
|
|
|
// Just do a layout, the strategy might need it
|
|
this._measureDomElement(true);
|
|
}
|
|
|
|
private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange | null {
|
|
const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []);
|
|
if (lineChanges.length === 0 || lineNumber < startLineNumberExtractor(lineChanges[0])) {
|
|
// There are no changes or `lineNumber` is before the first change
|
|
return null;
|
|
}
|
|
|
|
let min = 0, max = lineChanges.length - 1;
|
|
while (min < max) {
|
|
let mid = Math.floor((min + max) / 2);
|
|
let midStart = startLineNumberExtractor(lineChanges[mid]);
|
|
let midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Number.MAX_VALUE);
|
|
|
|
if (lineNumber < midStart) {
|
|
max = mid - 1;
|
|
} else if (lineNumber >= midEnd) {
|
|
min = mid + 1;
|
|
} else {
|
|
// HIT!
|
|
min = mid;
|
|
max = mid;
|
|
}
|
|
}
|
|
return lineChanges[min];
|
|
}
|
|
|
|
private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number {
|
|
let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber);
|
|
|
|
if (!lineChange) {
|
|
return lineNumber;
|
|
}
|
|
|
|
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);
|
|
|
|
|
|
let delta = lineNumber - originalEquivalentLineNumber;
|
|
|
|
if (delta <= lineChangeOriginalLength) {
|
|
return modifiedEquivalentLineNumber + Math.min(delta, lineChangeModifiedLength);
|
|
}
|
|
|
|
return modifiedEquivalentLineNumber + lineChangeModifiedLength - lineChangeOriginalLength + delta;
|
|
}
|
|
|
|
private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number {
|
|
let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber);
|
|
|
|
if (!lineChange) {
|
|
return lineNumber;
|
|
}
|
|
|
|
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);
|
|
|
|
|
|
let delta = lineNumber - modifiedEquivalentLineNumber;
|
|
|
|
if (delta <= lineChangeModifiedLength) {
|
|
return originalEquivalentLineNumber + Math.min(delta, lineChangeOriginalLength);
|
|
}
|
|
|
|
return originalEquivalentLineNumber + lineChangeOriginalLength - lineChangeModifiedLength + delta;
|
|
}
|
|
|
|
public getDiffLineInformationForOriginal(lineNumber: number): editorBrowser.IDiffLineInformation | null {
|
|
if (!this._diffComputationResult) {
|
|
// Cannot answer that which I don't know
|
|
return null;
|
|
}
|
|
return {
|
|
equivalentLineNumber: this._getEquivalentLineForOriginalLineNumber(lineNumber)
|
|
};
|
|
}
|
|
|
|
public getDiffLineInformationForModified(lineNumber: number): editorBrowser.IDiffLineInformation | null {
|
|
if (!this._diffComputationResult) {
|
|
// 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;
|
|
|
|
getOriginalEditor(): editorBrowser.ICodeEditor;
|
|
getModifiedEditor(): editorBrowser.ICodeEditor;
|
|
}
|
|
|
|
abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWidgetStyle {
|
|
|
|
_dataSource: IDataSource;
|
|
_insertColor: Color | null;
|
|
_removeColor: Color | null;
|
|
|
|
constructor(dataSource: IDataSource) {
|
|
super();
|
|
this._dataSource = dataSource;
|
|
this._insertColor = null;
|
|
this._removeColor = null;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// {{SQL CARBON EDIT}}
|
|
public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, reverse?: boolean): IEditorsDiffDecorationsWithZones {
|
|
// Get view zones
|
|
modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => {
|
|
return a.afterLineNumber - b.afterLineNumber;
|
|
});
|
|
originalWhitespaces = originalWhitespaces.sort((a, b) => {
|
|
return a.afterLineNumber - b.afterLineNumber;
|
|
});
|
|
let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators);
|
|
|
|
// {{SQL CARBON EDIT}}
|
|
if (reverse) {
|
|
lineChanges = reverseLineChanges(lineChanges);
|
|
[originalEditor, modifiedEditor] = [modifiedEditor, originalEditor];
|
|
}
|
|
|
|
// Get decorations & overview ruler zones
|
|
let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor);
|
|
let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor);
|
|
|
|
// {{SQL CARBON EDIT}}
|
|
if (reverse) {
|
|
[originalDecorations, modifiedDecorations] = [modifiedDecorations, originalDecorations];
|
|
}
|
|
|
|
return {
|
|
original: {
|
|
decorations: originalDecorations.decorations,
|
|
overviewZones: originalDecorations.overviewZones,
|
|
zones: zones.original
|
|
},
|
|
modified: {
|
|
decorations: modifiedDecorations.decorations,
|
|
overviewZones: modifiedDecorations.overviewZones,
|
|
zones: zones.modified
|
|
}
|
|
};
|
|
}
|
|
|
|
protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones;
|
|
protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations;
|
|
protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations;
|
|
|
|
public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void;
|
|
public abstract layout(): number;
|
|
}
|
|
|
|
interface IMyViewZone {
|
|
shouldNotShrink?: boolean;
|
|
afterLineNumber: number;
|
|
heightInLines: number;
|
|
minWidthInPx?: number;
|
|
domNode: HTMLElement | null;
|
|
marginDomNode?: HTMLElement | null;
|
|
diff?: IDiffLinesChange;
|
|
}
|
|
|
|
class ForeignViewZonesIterator {
|
|
|
|
private _index: number;
|
|
private readonly _source: IEditorWhitespace[];
|
|
public current: IEditorWhitespace | null;
|
|
|
|
constructor(source: IEditorWhitespace[]) {
|
|
this._source = source;
|
|
this._index = -1;
|
|
this.current = null;
|
|
this.advance();
|
|
}
|
|
|
|
public advance(): void {
|
|
this._index++;
|
|
if (this._index < this._source.length) {
|
|
this.current = this._source[this._index];
|
|
} else {
|
|
this.current = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class ViewZonesComputer {
|
|
|
|
private readonly lineChanges: editorCommon.ILineChange[];
|
|
private readonly originalForeignVZ: IEditorWhitespace[];
|
|
private readonly modifiedForeignVZ: IEditorWhitespace[];
|
|
|
|
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
|
|
this.lineChanges = lineChanges;
|
|
this.originalForeignVZ = originalForeignVZ;
|
|
this.modifiedForeignVZ = modifiedForeignVZ;
|
|
}
|
|
|
|
public getViewZones(): IEditorsZones {
|
|
let result: { original: IMyViewZone[]; modified: IMyViewZone[]; } = {
|
|
original: [],
|
|
modified: []
|
|
};
|
|
|
|
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) => {
|
|
return a.afterLineNumber - b.afterLineNumber;
|
|
};
|
|
|
|
let addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => {
|
|
if (item.domNode === null && destination.length > 0) {
|
|
let lastItem = destination[destination.length - 1];
|
|
if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) {
|
|
lastItem.heightInLines += item.heightInLines;
|
|
return;
|
|
}
|
|
}
|
|
destination.push(item);
|
|
};
|
|
|
|
let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ);
|
|
let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ);
|
|
|
|
// 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
|
|
for (let i = 0, length = this.lineChanges.length; i <= length; i++) {
|
|
let lineChange = (i < length ? this.lineChanges[i] : null);
|
|
|
|
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
|
|
let stepOriginal: IMyViewZone[] = [];
|
|
let stepModified: IMyViewZone[] = [];
|
|
|
|
// ---------------------------- 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) {
|
|
let viewZoneLineNumber: number;
|
|
if (modifiedForeignVZ.current.afterLineNumber <= modifiedEquivalentLineNumber) {
|
|
viewZoneLineNumber = originalEquivalentLineNumber - modifiedEquivalentLineNumber + modifiedForeignVZ.current.afterLineNumber;
|
|
} else {
|
|
viewZoneLineNumber = originalEndEquivalentLineNumber;
|
|
}
|
|
|
|
let marginDomNode: HTMLDivElement | null = null;
|
|
if (lineChange && lineChange.modifiedStartLineNumber <= modifiedForeignVZ.current.afterLineNumber && modifiedForeignVZ.current.afterLineNumber <= lineChange.modifiedEndLineNumber) {
|
|
marginDomNode = this._createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion();
|
|
}
|
|
|
|
stepOriginal.push({
|
|
afterLineNumber: viewZoneLineNumber,
|
|
heightInLines: modifiedForeignVZ.current.heightInLines,
|
|
domNode: null,
|
|
marginDomNode: marginDomNode
|
|
});
|
|
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) {
|
|
let viewZoneLineNumber: number;
|
|
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)) {
|
|
let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength);
|
|
if (r) {
|
|
stepOriginal.push(r);
|
|
}
|
|
}
|
|
|
|
if (lineChange !== null && isChangeOrDelete(lineChange)) {
|
|
let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength);
|
|
if (r) {
|
|
stepModified.push(r);
|
|
}
|
|
}
|
|
|
|
// ---------------------------- END PRODUCE VIEW ZONES
|
|
|
|
|
|
// ---------------------------- EMIT MINIMAL VIEW ZONES
|
|
|
|
// [CANCEL & EMIT] Try to cancel view zones out
|
|
let stepOriginalIndex = 0;
|
|
let stepModifiedIndex = 0;
|
|
|
|
stepOriginal = stepOriginal.sort(sortMyViewZones);
|
|
stepModified = stepModified.sort(sortMyViewZones);
|
|
|
|
while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) {
|
|
let original = stepOriginal[stepOriginalIndex];
|
|
let modified = stepModified[stepModifiedIndex];
|
|
|
|
let originalDelta = original.afterLineNumber - originalEquivalentLineNumber;
|
|
let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber;
|
|
|
|
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
|
|
}
|
|
|
|
return {
|
|
original: ViewZonesComputer._ensureDomNodes(result.original),
|
|
modified: ViewZonesComputer._ensureDomNodes(result.modified),
|
|
};
|
|
}
|
|
|
|
private static _ensureDomNodes(zones: IMyViewZone[]): IMyViewZone[] {
|
|
return zones.map((z) => {
|
|
if (!z.domNode) {
|
|
z.domNode = createFakeLinesDiv();
|
|
}
|
|
return z;
|
|
});
|
|
}
|
|
|
|
protected abstract _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null;
|
|
|
|
protected abstract _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null;
|
|
|
|
protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null;
|
|
}
|
|
|
|
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',
|
|
})
|
|
|
|
};
|
|
|
|
class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
|
|
|
|
static MINIMUM_EDITOR_WIDTH = 100;
|
|
|
|
private _disableSash: boolean;
|
|
private readonly _sash: Sash;
|
|
private _sashRatio: number | null;
|
|
private _sashPosition: number | null;
|
|
private _startSashPosition: number | null;
|
|
|
|
constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) {
|
|
super(dataSource);
|
|
|
|
this._disableSash = (enableSplitViewResizing === false);
|
|
this._sashRatio = null;
|
|
this._sashPosition = null;
|
|
this._startSashPosition = null;
|
|
this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this));
|
|
|
|
if (this._disableSash) {
|
|
this._sash.state = SashState.Disabled;
|
|
}
|
|
|
|
this._sash.onDidStart(() => this.onSashDragStart());
|
|
this._sash.onDidChange((e: ISashEvent) => this.onSashDrag(e));
|
|
this._sash.onDidEnd(() => this.onSashDragEnd());
|
|
this._sash.onDidReset(() => this.onSashReset());
|
|
}
|
|
|
|
public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void {
|
|
let newDisableSash = (enableSplitViewResizing === false);
|
|
if (this._disableSash !== newDisableSash) {
|
|
this._disableSash = newDisableSash;
|
|
this._sash.state = this._disableSash ? SashState.Disabled : SashState.Enabled;
|
|
}
|
|
}
|
|
|
|
public layout(sashRatio: number | null = this._sashRatio): number {
|
|
let w = this._dataSource.getWidth();
|
|
let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
|
|
|
|
let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth);
|
|
let midPoint = Math.floor(0.5 * contentWidth);
|
|
|
|
sashPosition = this._disableSash ? midPoint : sashPosition || midPoint;
|
|
|
|
if (contentWidth > DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) {
|
|
if (sashPosition < DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
|
|
sashPosition = DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
|
|
}
|
|
|
|
if (sashPosition > contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
|
|
sashPosition = contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
|
|
}
|
|
} else {
|
|
sashPosition = midPoint;
|
|
}
|
|
|
|
if (this._sashPosition !== sashPosition) {
|
|
this._sashPosition = sashPosition;
|
|
this._sash.layout();
|
|
}
|
|
|
|
return this._sashPosition;
|
|
}
|
|
|
|
private onSashDragStart(): void {
|
|
this._startSashPosition = this._sashPosition!;
|
|
}
|
|
|
|
private onSashDrag(e: ISashEvent): void {
|
|
let w = this._dataSource.getWidth();
|
|
let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
|
|
let sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth);
|
|
|
|
this._sashRatio = sashPosition / contentWidth;
|
|
|
|
this._dataSource.relayoutEditors();
|
|
}
|
|
|
|
private onSashDragEnd(): void {
|
|
this._sash.layout();
|
|
}
|
|
|
|
private onSashReset(): void {
|
|
this._sashRatio = 0.5;
|
|
this._dataSource.relayoutEditors();
|
|
this._sash.layout();
|
|
}
|
|
|
|
public getVerticalSashTop(sash: Sash): number {
|
|
return 0;
|
|
}
|
|
|
|
public getVerticalSashLeft(sash: Sash): number {
|
|
return this._sashPosition!;
|
|
}
|
|
|
|
public getVerticalSashHeight(sash: Sash): number {
|
|
return this._dataSource.getHeight();
|
|
}
|
|
|
|
protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones {
|
|
let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ);
|
|
return c.getViewZones();
|
|
}
|
|
|
|
protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
|
|
const overviewZoneColor = String(this._removeColor);
|
|
|
|
let result: IEditorDiffDecorations = {
|
|
decorations: [],
|
|
overviewZones: []
|
|
};
|
|
|
|
let originalModel = originalEditor.getModel()!;
|
|
|
|
for (let i = 0, length = lineChanges.length; i < length; i++) {
|
|
let lineChange = lineChanges[i];
|
|
|
|
if (isChangeOrDelete(lineChange)) {
|
|
result.decorations.push({
|
|
range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE),
|
|
options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete)
|
|
});
|
|
if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) {
|
|
result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE, DECORATIONS.charDeleteWholeLine));
|
|
}
|
|
|
|
result.overviewZones.push(new OverviewRulerZone(
|
|
lineChange.originalStartLineNumber,
|
|
lineChange.originalEndLineNumber,
|
|
overviewZoneColor
|
|
));
|
|
|
|
if (lineChange.charChanges) {
|
|
for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
|
|
let charChange = lineChange.charChanges[j];
|
|
if (isChangeOrDelete(charChange)) {
|
|
if (ignoreTrimWhitespace) {
|
|
for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) {
|
|
let startColumn: number;
|
|
let endColumn: number;
|
|
if (lineNumber === charChange.originalStartLineNumber) {
|
|
startColumn = charChange.originalStartColumn;
|
|
} else {
|
|
startColumn = originalModel.getLineFirstNonWhitespaceColumn(lineNumber);
|
|
}
|
|
if (lineNumber === charChange.originalEndLineNumber) {
|
|
endColumn = charChange.originalEndColumn;
|
|
} else {
|
|
endColumn = originalModel.getLineLastNonWhitespaceColumn(lineNumber);
|
|
}
|
|
result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charDelete));
|
|
}
|
|
} else {
|
|
result.decorations.push(createDecoration(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn, DECORATIONS.charDelete));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
|
|
const overviewZoneColor = String(this._insertColor);
|
|
|
|
let result: IEditorDiffDecorations = {
|
|
decorations: [],
|
|
overviewZones: []
|
|
};
|
|
|
|
let modifiedModel = modifiedEditor.getModel()!;
|
|
|
|
for (let i = 0, length = lineChanges.length; i < length; i++) {
|
|
let lineChange = lineChanges[i];
|
|
|
|
if (isChangeOrInsert(lineChange)) {
|
|
|
|
result.decorations.push({
|
|
range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE),
|
|
options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert)
|
|
});
|
|
if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) {
|
|
result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine));
|
|
}
|
|
result.overviewZones.push(new OverviewRulerZone(
|
|
lineChange.modifiedStartLineNumber,
|
|
lineChange.modifiedEndLineNumber,
|
|
overviewZoneColor
|
|
));
|
|
|
|
if (lineChange.charChanges) {
|
|
for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
|
|
let charChange = lineChange.charChanges[j];
|
|
if (isChangeOrInsert(charChange)) {
|
|
if (ignoreTrimWhitespace) {
|
|
for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) {
|
|
let startColumn: number;
|
|
let endColumn: number;
|
|
if (lineNumber === charChange.modifiedStartLineNumber) {
|
|
startColumn = charChange.modifiedStartColumn;
|
|
} else {
|
|
startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber);
|
|
}
|
|
if (lineNumber === charChange.modifiedEndLineNumber) {
|
|
endColumn = charChange.modifiedEndColumn;
|
|
} else {
|
|
endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber);
|
|
}
|
|
result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert));
|
|
}
|
|
} else {
|
|
result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class SideBySideViewZonesComputer extends ViewZonesComputer {
|
|
|
|
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) {
|
|
super(lineChanges, originalForeignVZ, modifiedForeignVZ);
|
|
}
|
|
|
|
protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null {
|
|
return null;
|
|
}
|
|
|
|
protected _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
|
|
if (lineChangeModifiedLength > lineChangeOriginalLength) {
|
|
return {
|
|
afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber),
|
|
heightInLines: (lineChangeModifiedLength - lineChangeOriginalLength),
|
|
domNode: null
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
|
|
if (lineChangeOriginalLength > lineChangeModifiedLength) {
|
|
return {
|
|
afterLineNumber: Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber),
|
|
heightInLines: (lineChangeOriginalLength - lineChangeModifiedLength),
|
|
domNode: null
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle {
|
|
|
|
private decorationsLeft: number;
|
|
|
|
constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) {
|
|
super(dataSource);
|
|
|
|
this.decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft;
|
|
|
|
this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: editorOptions.EditorLayoutInfo) => {
|
|
if (this.decorationsLeft !== layoutInfo.decorationsLeft) {
|
|
this.decorationsLeft = layoutInfo.decorationsLeft;
|
|
dataSource.relayoutEditors();
|
|
}
|
|
}));
|
|
}
|
|
|
|
public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void {
|
|
// Nothing to do..
|
|
}
|
|
|
|
protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones {
|
|
let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators);
|
|
return computer.getViewZones();
|
|
}
|
|
|
|
protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
|
|
const overviewZoneColor = String(this._removeColor);
|
|
|
|
let result: IEditorDiffDecorations = {
|
|
decorations: [],
|
|
overviewZones: []
|
|
};
|
|
|
|
for (let i = 0, length = lineChanges.length; i < length; i++) {
|
|
let lineChange = lineChanges[i];
|
|
|
|
// Add overview zones in the overview ruler
|
|
if (isChangeOrDelete(lineChange)) {
|
|
result.decorations.push({
|
|
range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE),
|
|
options: DECORATIONS.lineDeleteMargin
|
|
});
|
|
|
|
result.overviewZones.push(new OverviewRulerZone(
|
|
lineChange.originalStartLineNumber,
|
|
lineChange.originalEndLineNumber,
|
|
overviewZoneColor
|
|
));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations {
|
|
const overviewZoneColor = String(this._insertColor);
|
|
|
|
let result: IEditorDiffDecorations = {
|
|
decorations: [],
|
|
overviewZones: []
|
|
};
|
|
|
|
let modifiedModel = modifiedEditor.getModel()!;
|
|
|
|
for (let i = 0, length = lineChanges.length; i < length; i++) {
|
|
let lineChange = lineChanges[i];
|
|
|
|
// Add decorations & overview zones
|
|
if (isChangeOrInsert(lineChange)) {
|
|
result.decorations.push({
|
|
range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE),
|
|
options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert)
|
|
});
|
|
|
|
result.overviewZones.push(new OverviewRulerZone(
|
|
lineChange.modifiedStartLineNumber,
|
|
lineChange.modifiedEndLineNumber,
|
|
overviewZoneColor
|
|
));
|
|
|
|
if (lineChange.charChanges) {
|
|
for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
|
|
let charChange = lineChange.charChanges[j];
|
|
if (isChangeOrInsert(charChange)) {
|
|
if (ignoreTrimWhitespace) {
|
|
for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) {
|
|
let startColumn: number;
|
|
let endColumn: number;
|
|
if (lineNumber === charChange.modifiedStartLineNumber) {
|
|
startColumn = charChange.modifiedStartColumn;
|
|
} else {
|
|
startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber);
|
|
}
|
|
if (lineNumber === charChange.modifiedEndLineNumber) {
|
|
endColumn = charChange.modifiedEndColumn;
|
|
} else {
|
|
endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber);
|
|
}
|
|
result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert));
|
|
}
|
|
} else {
|
|
result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public layout(): number {
|
|
// An editor should not be smaller than 5px
|
|
return Math.max(5, this.decorationsLeft);
|
|
}
|
|
|
|
}
|
|
|
|
class InlineViewZonesComputer extends ViewZonesComputer {
|
|
|
|
private readonly originalModel: ITextModel;
|
|
private readonly modifiedEditorConfiguration: editorOptions.InternalEditorOptions;
|
|
private readonly modifiedEditorTabSize: number;
|
|
private readonly renderIndicators: boolean;
|
|
|
|
constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) {
|
|
super(lineChanges, originalForeignVZ, modifiedForeignVZ);
|
|
this.originalModel = originalEditor.getModel()!;
|
|
this.modifiedEditorConfiguration = modifiedEditor.getConfiguration();
|
|
this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize;
|
|
this.renderIndicators = renderIndicators;
|
|
}
|
|
|
|
protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null {
|
|
let result = document.createElement('div');
|
|
result.className = 'inline-added-margin-view-zone';
|
|
return result;
|
|
}
|
|
|
|
protected _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
|
|
let marginDomNode = document.createElement('div');
|
|
marginDomNode.className = 'inline-added-margin-view-zone';
|
|
|
|
return {
|
|
afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber),
|
|
heightInLines: lineChangeModifiedLength,
|
|
domNode: document.createElement('div'),
|
|
marginDomNode: marginDomNode
|
|
};
|
|
}
|
|
|
|
protected _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null {
|
|
let decorations: InlineDecoration[] = [];
|
|
if (lineChange.charChanges) {
|
|
for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) {
|
|
let charChange = lineChange.charChanges[j];
|
|
if (isChangeOrDelete(charChange)) {
|
|
decorations.push(new InlineDecoration(
|
|
new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn),
|
|
'char-delete',
|
|
InlineDecorationType.Regular
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
let sb = createStringBuilder(10000);
|
|
let marginHTML: string[] = [];
|
|
let lineDecorationsWidth = this.modifiedEditorConfiguration.layoutInfo.decorationsWidth;
|
|
let lineHeight = this.modifiedEditorConfiguration.lineHeight;
|
|
const typicalHalfwidthCharacterWidth = this.modifiedEditorConfiguration.fontInfo.typicalHalfwidthCharacterWidth;
|
|
let maxCharsPerLine = 0;
|
|
const originalContent: string[] = [];
|
|
for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) {
|
|
maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations, sb));
|
|
originalContent.push(this.originalModel.getLineContent(lineNumber));
|
|
|
|
if (this.renderIndicators) {
|
|
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>`
|
|
]);
|
|
}
|
|
}
|
|
maxCharsPerLine += this.modifiedEditorConfiguration.viewInfo.scrollBeyondLastColumn;
|
|
|
|
let domNode = document.createElement('div');
|
|
domNode.className = 'view-lines line-delete';
|
|
domNode.innerHTML = sb.build();
|
|
Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo);
|
|
|
|
let marginDomNode = document.createElement('div');
|
|
marginDomNode.className = 'inline-deleted-margin-view-zone';
|
|
marginDomNode.innerHTML = marginHTML.join('');
|
|
Configuration.applyFontInfoSlow(marginDomNode, this.modifiedEditorConfiguration.fontInfo);
|
|
|
|
return {
|
|
shouldNotShrink: true,
|
|
afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1),
|
|
heightInLines: lineChangeOriginalLength,
|
|
minWidthInPx: (maxCharsPerLine * typicalHalfwidthCharacterWidth),
|
|
domNode: domNode,
|
|
marginDomNode: marginDomNode,
|
|
diff: {
|
|
originalStartLineNumber: lineChange.originalStartLineNumber,
|
|
originalEndLineNumber: lineChange.originalEndLineNumber,
|
|
modifiedStartLineNumber: lineChange.modifiedStartLineNumber,
|
|
modifiedEndLineNumber: lineChange.modifiedEndLineNumber,
|
|
originalContent: originalContent
|
|
}
|
|
};
|
|
}
|
|
|
|
private _renderOriginalLine(count: number, originalModel: ITextModel, config: editorOptions.InternalEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): number {
|
|
const lineTokens = originalModel.getLineTokens(lineNumber);
|
|
const lineContent = lineTokens.getLineContent();
|
|
|
|
const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1);
|
|
|
|
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;">');
|
|
|
|
const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, originalModel.mightContainNonBasicASCII());
|
|
const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL());
|
|
const output = renderViewLine(new RenderLineInput(
|
|
(config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations),
|
|
config.fontInfo.canUseHalfwidthRightwardsArrow,
|
|
lineContent,
|
|
false,
|
|
isBasicASCII,
|
|
containsRTL,
|
|
0,
|
|
lineTokens,
|
|
actualDecorations,
|
|
tabSize,
|
|
config.fontInfo.spaceWidth,
|
|
config.viewInfo.stopRenderingLineAfter,
|
|
config.viewInfo.renderWhitespace,
|
|
config.viewInfo.renderControlCharacters,
|
|
config.viewInfo.fontLigatures,
|
|
null // Send no selections, original line cannot be selected
|
|
), sb);
|
|
|
|
sb.appendASCIIString('</div>');
|
|
|
|
const absoluteOffsets = output.characterMapping.getAbsoluteOffsets();
|
|
return absoluteOffsets.length > 0 ? absoluteOffsets[absoluteOffsets.length - 1] : 0;
|
|
}
|
|
}
|
|
|
|
function isChangeOrInsert(lineChange: editorCommon.IChange): boolean {
|
|
return lineChange.modifiedEndLineNumber > 0;
|
|
}
|
|
|
|
function isChangeOrDelete(lineChange: editorCommon.IChange): boolean {
|
|
return lineChange.originalEndLineNumber > 0;
|
|
}
|
|
|
|
function createFakeLinesDiv(): HTMLElement {
|
|
let r = document.createElement('div');
|
|
r.className = 'diagonal-fill';
|
|
return r;
|
|
}
|
|
|
|
registerThemingParticipant((theme, collector) => {
|
|
const added = theme.getColor(diffInserted);
|
|
if (added) {
|
|
collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { background-color: ${added}; }`);
|
|
collector.addRule(`.monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: ${added}; }`);
|
|
collector.addRule(`.monaco-editor .inline-added-margin-view-zone { background-color: ${added}; }`);
|
|
}
|
|
|
|
const removed = theme.getColor(diffRemoved);
|
|
if (removed) {
|
|
collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { background-color: ${removed}; }`);
|
|
collector.addRule(`.monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: ${removed}; }`);
|
|
collector.addRule(`.monaco-editor .inline-deleted-margin-view-zone { background-color: ${removed}; }`);
|
|
}
|
|
|
|
const addedOutline = theme.getColor(diffInsertedOutline);
|
|
if (addedOutline) {
|
|
collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${addedOutline}; }`);
|
|
}
|
|
|
|
const removedOutline = theme.getColor(diffRemovedOutline);
|
|
if (removedOutline) {
|
|
collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${removedOutline}; }`);
|
|
}
|
|
|
|
const shadow = theme.getColor(scrollbarShadow);
|
|
if (shadow) {
|
|
collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px ${shadow}; }`);
|
|
}
|
|
|
|
const border = theme.getColor(diffBorder);
|
|
if (border) {
|
|
collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { border-left: 1px solid ${border}; }`);
|
|
}
|
|
});
|