Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements inline diff view in diff editor v2. #184735

Merged
merged 1 commit into from Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/vs/code/electron-sandbox/workbench/workbench-dev.html
Expand Up @@ -49,6 +49,7 @@
cellRendererEditorText
defaultWorkerFactory
diffEditorWidget
diffEditorWidget2
diffReview
domLineBreaksComputer
dompurify
Expand Down
1 change: 1 addition & 0 deletions src/vs/code/electron-sandbox/workbench/workbench.html
Expand Up @@ -49,6 +49,7 @@
cellRendererEditorText
defaultWorkerFactory
diffEditorWidget
diffEditorWidget2
diffReview
domLineBreaksComputer
dompurify
Expand Down
19 changes: 0 additions & 19 deletions src/vs/editor/browser/editorBrowser.ts
Expand Up @@ -1095,13 +1095,6 @@ export interface IActiveCodeEditor extends ICodeEditor {
getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number };
}

/**
* Information about a line in the diff editor
*/
export interface IDiffLineInformation {
readonly equivalentLineNumber: number;
}

/**
* @internal
*/
Expand Down Expand Up @@ -1195,18 +1188,6 @@ export interface IDiffEditor extends editorCommon.IEditor {
*/
getDiffComputationResult(): IDiffComputationResult | null;

/**
* Get information based on computed diff about a line number from the original model.
* If the diff computation is not finished or the model is missing, will return null.
*/
getDiffLineInformationForOriginal(lineNumber: number): IDiffLineInformation | null;

/**
* Get information based on computed diff about a line number from the modified model.
* If the diff computation is not finished or the model is missing, will return null.
*/
getDiffLineInformationForModified(lineNumber: number): IDiffLineInformation | null;

/**
* Update the editor's options after the editor has been created.
*/
Expand Down
91 changes: 0 additions & 91 deletions src/vs/editor/browser/widget/diffEditorWidget.ts
Expand Up @@ -1461,97 +1461,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._doLayout();
}

private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: ILineChange) => number): 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;
let max = lineChanges.length - 1;
while (min < max) {
const mid = Math.floor((min + max) / 2);
const midStart = startLineNumberExtractor(lineChanges[mid]);
const midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Constants.MAX_SAFE_SMALL_INTEGER);

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 {
const lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber);

if (!lineChange) {
return lineNumber;
}

const originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0);
const modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0);
const lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0);
const lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0);


const delta = lineNumber - originalEquivalentLineNumber;

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

return modifiedEquivalentLineNumber + lineChangeModifiedLength - lineChangeOriginalLength + delta;
}

private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number {
const lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber);

if (!lineChange) {
return lineNumber;
}

const originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0);
const modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0);
const lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0);
const lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0);


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

public goToDiff(target: 'previous' | 'next'): void {
if (target === 'next') {
this._diffNavigator?.next();
Expand Down
11 changes: 11 additions & 0 deletions src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts
Expand Up @@ -3,18 +3,29 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Codicon } from 'vs/base/common/codicons';
import { ThemeIcon } from 'vs/base/common/themables';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { localize } from 'vs/nls';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';

export const diffInsertIcon = registerIcon('diff-insert', Codicon.add, localize('diffInsertIcon', 'Line decoration for inserts in the diff editor.'));
export const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove, localize('diffRemoveIcon', 'Line decoration for removals in the diff editor.'));

export const diffFullLineAddDecoration = ModelDecorationOptions.register({
className: 'line-insert',
description: 'line-insert',
isWholeLine: true,
linesDecorationsClassName: 'insert-sign ' + ThemeIcon.asClassName(diffInsertIcon),
marginClassName: 'gutter-insert',
});

export const diffFullLineDeleteDecoration = ModelDecorationOptions.register({
className: 'line-delete',
description: 'line-delete',
isWholeLine: true,
linesDecorationsClassName: 'delete-sign ' + ThemeIcon.asClassName(diffRemoveIcon),
marginClassName: 'gutter-delete',
});

export const diffAddDecoration = ModelDecorationOptions.register({
Expand Down
70 changes: 40 additions & 30 deletions src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts
Expand Up @@ -7,13 +7,13 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash';
import { findLast } from 'vs/base/common/arrays';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { IObservable, ISettableObservable, derived, keepAlive, observableValue, waitForState } from 'vs/base/common/observable';
import { disposableObservableValue } from 'vs/base/common/observableImpl/base';
import { IObservable, ISettableObservable, autorun, derived, keepAlive, observableValue, waitForState } from 'vs/base/common/observable';
import { disposableObservableValue, transaction } from 'vs/base/common/observableImpl/base';
import { isDefined } from 'vs/base/common/types';
import { Constants } from 'vs/base/common/uint';
import 'vs/css!./style';
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IDiffLineInformation } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser';
import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
Expand Down Expand Up @@ -79,7 +79,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
);
private readonly _rootSizeObserver: ObservableElementSizeObserver;
private readonly _options: ISettableObservable<ValidDiffEditorBaseOptions>;
private readonly _sash: DiffEditorSash;
private readonly _sash: IObservable<DiffEditorSash | undefined>;
private readonly _renderOverviewRuler: IObservable<boolean>;

constructor(
Expand Down Expand Up @@ -114,18 +114,33 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
this._register(applyObservableDecorations(this._modifiedEditor, this._decorations.map(d => d?.modifiedDecorations || [])));

this._renderOverviewRuler = this._options.map(o => o.renderOverviewRuler);
this._sash = this._register(new DiffEditorSash(
this._options.map(o => o.enableSplitViewResizing),
this._options.map(o => o.splitViewDefaultRatio),
this.elements.root,
{
height: this._rootSizeObserver.height,
width: this._rootSizeObserver.width.map((w, reader) => w - (this._renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),
}
));
const sash = this._register(disposableObservableValue<DiffEditorSash | undefined>('sash', undefined));
this._sash = sash;

this._register(autorun('update sash', reader => {
const showSash = this._options.read(reader).renderSideBySide;

this.elements.root.classList.toggle('side-by-side', showSash);

transaction(tx => {
sash.set(undefined, tx);
if (showSash) {
sash.set(new DiffEditorSash(
this._options.map(o => o.enableSplitViewResizing),
this._options.map(o => o.splitViewDefaultRatio),
this.elements.root,
{
height: this._rootSizeObserver.height,
width: this._rootSizeObserver.width.map((w, reader) => w - (this._renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),
}
), tx);
}
});
}));


this._register(new UnchangedRangesFeature(this._originalEditor, this._modifiedEditor, this._diffModel));
this._register(new ViewZoneAlignment(this._originalEditor, this._modifiedEditor, this._diffModel));
this._register(this._instantiationService.createInstance(ViewZoneAlignment, this._originalEditor, this._modifiedEditor, this._diffModel, this._options.map(o => o.renderSideBySide)));

this._register(this._instantiationService.createInstance(OverviewRulerPart,
this._originalEditor,
Expand Down Expand Up @@ -157,17 +172,19 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
private readonly _layoutInfo = derived('modifiedEditorLayoutInfo', (reader) => {
const width = this._rootSizeObserver.width.read(reader);
const height = this._rootSizeObserver.height.read(reader);
const sashLeft = this._sash.sashLeft.read(reader);
const sashLeft = this._sash.read(reader)?.sashLeft.read(reader);

this.elements.original.style.width = sashLeft + 'px';
const originalWidth = sashLeft ?? Math.max(5, this._originalEditor.getLayoutInfo().decorationsLeft);

this.elements.original.style.width = originalWidth + 'px';
this.elements.original.style.left = '0px';

this.elements.modified.style.width = (width - sashLeft) + 'px';
this.elements.modified.style.left = sashLeft + 'px';
this.elements.modified.style.width = (width - originalWidth) + 'px';
this.elements.modified.style.left = originalWidth + 'px';

this._originalEditor.layout({ width: sashLeft, height: height });
this._originalEditor.layout({ width: originalWidth, height: height });
this._modifiedEditor.layout({
width: width - sashLeft -
width: width - originalWidth -
(this._renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0),
height
});
Expand Down Expand Up @@ -447,7 +464,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
this._options.map(o => o.ignoreTrimWhitespace),
this._options.map(o => o.maxComputationTime),
this._options.map(o => o.experimental.collapseUnchangedRegions!),
this._options.map(o => o.experimental.showMoves!),
this._options.map(o => o.experimental.showMoves! && o.renderSideBySide),
this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider, this._options.get())
) : undefined, undefined);
}
Expand All @@ -465,7 +482,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
getModifiedEditor(): ICodeEditor { return this._modifiedEditor; }

setBoundarySashes(sashes: IBoundarySashes): void {
this._sash.setBoundarySashes(sashes);
// TODO
this._sash.get()?.setBoundarySashes(sashes);
}

readonly onDidUpdateDiff: Event<void> = e => {
Expand All @@ -492,14 +510,6 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
return null;
//throw new Error('Method not implemented.');
}
getDiffLineInformationForOriginal(lineNumber: number): IDiffLineInformation | null {
return null;
//throw new Error('Method not implemented.');
}
getDiffLineInformationForModified(lineNumber: number): IDiffLineInformation | null {
return null;
//throw new Error('Method not implemented.');
}

private _goTo(diff: DiffMapping): void {
this._modifiedEditor.setPosition(new Position(diff.lineRangeMapping.modifiedRange.startLineNumber, 1));
Expand Down
Expand Up @@ -33,7 +33,7 @@ export class DiffModel extends Disposable {
public readonly syncedMovedTexts = observableValue<MovedText | undefined>('syncedMovedText', undefined);

constructor(
model: IDiffEditorModel,
public readonly model: IDiffEditorModel,
ignoreTrimWhitespace: IObservable<boolean>,
maxComputationTimeMs: IObservable<number>,
private readonly _hideUnchangedRegions: IObservable<boolean>,
Expand Down