Skip to content

Commit

Permalink
Diff Editor v2 Bugfixes (#184797)
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Jun 10, 2023
1 parent 7e1125f commit 7ff66b3
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/wi
import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget';
import { diffAddDecoration, diffDeleteDecoration, diffFullLineAddDecoration, diffFullLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorSash';
import { ViewZoneAlignment } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment';
import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment';
import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines';
import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart';
import { UnchangedRangesFeature } from 'vs/editor/browser/widget/diffEditorWidget2/unchangedRanges';
Expand Down Expand Up @@ -140,7 +140,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {


this._register(new UnchangedRangesFeature(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(ViewZoneManager, this._originalEditor, this._modifiedEditor, this._diffModel, this._options.map(o => o.renderSideBySide)));

this._register(this._instantiationService.createInstance(OverviewRulerPart,
this._originalEditor,
Expand Down Expand Up @@ -442,13 +442,21 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
return this._originalEditor.hasTextFocus() || this._modifiedEditor.hasTextFocus();
}

override saveViewState(): IDiffEditorViewState | null {
return null;
//throw new Error('Method not implemented.');
public override saveViewState(): IDiffEditorViewState {
const originalViewState = this._originalEditor.saveViewState();
const modifiedViewState = this._modifiedEditor.saveViewState();
return {
original: originalViewState,
modified: modifiedViewState
};
}

override restoreViewState(state: IDiffEditorViewState | null): void {
//throw new Error('Method not implemented.');
public override restoreViewState(s: IDiffEditorViewState): void {
if (s && s.original && s.modified) {
const diffEditorState = s as IDiffEditorViewState;
this._originalEditor.restoreViewState(diffEditorState.original);
this._modifiedEditor.restoreViewState(diffEditorState.modified);
}
}

override getModel(): IDiffEditorModel | null { return this._model.get(); }
Expand Down
107 changes: 56 additions & 51 deletions src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IObservable, derived, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable';
import { autorun, autorunWithStore2 } from 'vs/base/common/observableImpl/autorun';
import { ThemeIcon } from 'vs/base/common/themables';
import { assertIsDefined } from 'vs/base/common/types';
import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo';
import { IViewZone } from 'vs/editor/browser/editorBrowser';
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { diffDeleteDecoration, diffRemoveIcon } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
import { DiffMapping, DiffModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffModel';
Expand All @@ -26,14 +28,18 @@ import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewMod
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';

export class ViewZoneAlignment extends Disposable {
private readonly _origExtraHeight = observableValue('origExtraHeight', 0);
private readonly _modExtraHeight = observableValue('modExtraHeight', 0);

/**
* Ensures both editors have the same height by aligning unchanged lines.
* In inline view mode, inserts viewzones to show deleted code from the original text model in the modified code editor.
* Synchronizes scrolling.
*/
export class ViewZoneManager extends Disposable {
private readonly _originalTopPadding = observableValue('originalTopPadding', 0);
private readonly _originalScrollTop: IObservable<number>;
private readonly _originalScrollOffset = observableValue<number, boolean>('originalScrollOffset', 0);
private readonly _originalScrollOffsetAnimated = animatedObservable(this._originalScrollOffset, this._store);

private readonly _modifiedTopPadding = observableValue('modifiedTopPadding', 0);
private readonly _modifiedScrollTop: IObservable<number>;
private readonly _modifiedScrollOffset = observableValue<number, boolean>('modifiedScrollOffset', 0);
private readonly _modifiedScrollOffsetAnimated = animatedObservable(this._modifiedScrollOffset, this._store);
Expand All @@ -50,11 +56,11 @@ export class ViewZoneAlignment extends Disposable {

let isChangingViewZones = false;

const origViewZonesChanged = observableSignalFromEvent(
const originalViewZonesChanged = observableSignalFromEvent(
'origViewZonesChanged',
e => this._originalEditor.onDidChangeViewZones((args) => { if (!isChangingViewZones) { e(args); } })
);
const modViewZonesChanged = observableSignalFromEvent(
const modifiedViewZonesChanged = observableSignalFromEvent(
'modViewZonesChanged',
e => this._modifiedEditor.onDidChangeViewZones((args) => { if (!isChangingViewZones) { e(args); } })
);
Expand All @@ -70,24 +76,18 @@ export class ViewZoneAlignment extends Disposable {
const diffModel = this._diffModel.read(reader);
const diff = diffModel?.diff.read(reader);
if (!diffModel || !diff) { return null; }

origViewZonesChanged.read(reader);
modViewZonesChanged.read(reader);

originalViewZonesChanged.read(reader);
modifiedViewZonesChanged.read(reader);
return computeRangeAlignment(this._originalEditor, this._modifiedEditor, diff.mappings, alignmentViewZoneIdsOrig, alignmentViewZoneIdsMod);
});

const alignmentsSyncedMovedText = derived<ILineRangeAlignment[] | null>('alignments', (reader) => {
origViewZonesChanged.read(reader);
modViewZonesChanged.read(reader);

const syncedMovedText = this._diffModel.read(reader)?.syncedMovedTexts.read(reader);
if (!syncedMovedText) {
return null;
}
if (!syncedMovedText) { return null; }
originalViewZonesChanged.read(reader);
modifiedViewZonesChanged.read(reader);
const mappings = syncedMovedText.changes.map(c => new DiffMapping(c));

// TOD dont include alignments outside syncedMovedText
// TODO dont include alignments outside syncedMovedText
return computeRangeAlignment(this._originalEditor, this._modifiedEditor, mappings, alignmentViewZoneIdsOrig, alignmentViewZoneIdsMod);
});

Expand All @@ -101,33 +101,33 @@ export class ViewZoneAlignment extends Disposable {
const alignmentViewZones = derived<{ orig: IViewZoneWithZoneId[]; mod: IViewZoneWithZoneId[] }>('alignment viewzones', (reader) => {
alignmentViewZonesDisposables.clear();

const curAlignments = alignments.read(reader) || [];
const alignmentsVal = alignments.read(reader) || [];

const origViewZones: IViewZoneWithZoneId[] = [];
const modViewZones: IViewZoneWithZoneId[] = [];

const curModExtraHeight = this._modExtraHeight.read(reader);
if (curModExtraHeight > 0) {
const modifiedTopPaddingVal = this._modifiedTopPadding.read(reader);
if (modifiedTopPaddingVal > 0) {
modViewZones.push({
afterLineNumber: 0,
domNode: document.createElement('div'),
heightInPx: curModExtraHeight,
heightInPx: modifiedTopPaddingVal,
});
}
const curOrigExtraHeight = this._origExtraHeight.read(reader);
if (curOrigExtraHeight > 0) {
const originalTopPaddingVal = this._originalTopPadding.read(reader);
if (originalTopPaddingVal > 0) {
origViewZones.push({
afterLineNumber: 0,
domNode: document.createElement('div'),
heightInPx: curOrigExtraHeight,
heightInPx: originalTopPaddingVal,
});
}

const renderSideBySide = this._renderSideBySide.read(reader);

const deletedCodeLineBreaksComputer = !renderSideBySide ? this._modifiedEditor._getViewModel()?.createLineBreaksComputer() : undefined;
if (deletedCodeLineBreaksComputer) {
for (const a of curAlignments) {
for (const a of alignmentsVal) {
if (a.diff) {
for (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) {
deletedCodeLineBreaksComputer?.addRequest(this._originalEditor.getModel()!.getLineContent(i), null, null);
Expand All @@ -147,13 +147,13 @@ export class ViewZoneAlignment extends Disposable {
const mightContainRTL = this._originalEditor.getModel()?.mightContainRTL() ?? false;
const renderOptions = RenderOptions.fromEditor(this._modifiedEditor);

for (const a of curAlignments) {
for (const a of alignmentsVal) {
if (a.diff && !renderSideBySide) {
if (!a.originalRange.isEmpty) {
originalModelTokenizationCompleted.read(reader); // Update view-zones once tokenization completes

const domNode = document.createElement('div');
domNode.classList.add('view-lines', 'line-delete', 'monaco-mouse-cursor-text');
const deletedCodeDomNode = document.createElement('div');
deletedCodeDomNode.classList.add('view-lines', 'line-delete', 'monaco-mouse-cursor-text');
const source = new LineSource(
a.originalRange.mapToLineArray(l => this._originalEditor.getModel()!.tokenization.getLineTokens(l)),
a.originalRange.mapToLineArray(_ => lineBreakData[lineBreakDataIdx++]),
Expand All @@ -168,7 +168,7 @@ export class ViewZoneAlignment extends Disposable {
InlineDecorationType.Regular
));
}
const result = renderLines(source, renderOptions, decorations, domNode);
const result = renderLines(source, renderOptions, decorations, deletedCodeDomNode);

const marginDomNode = document.createElement('div');
marginDomNode.className = 'inline-deleted-margin-view-zone';
Expand All @@ -183,18 +183,10 @@ export class ViewZoneAlignment extends Disposable {
}
//}

let zoneId: string;
modViewZones.push({
afterLineNumber: a.modifiedRange.startLineNumber - 1,
domNode: domNode,
heightInPx: result.heightInLines * modLineHeight,
minWidthInPx: result.minWidthInPx,
marginDomNode,
setZoneId(id) { zoneId = id; },
});
let zoneId: string | undefined = undefined;
alignmentViewZonesDisposables.add(
new InlineDiffDeletedCodeMargin(
() => zoneId,
() => assertIsDefined(zoneId),
marginDomNode,
this._modifiedEditor,
a.diff,
Expand All @@ -207,7 +199,7 @@ export class ViewZoneAlignment extends Disposable {

for (let i = 0; i < result.viewLineCounts.length; i++) {
const count = result.viewLineCounts[i];
// Account for wrapped lines in the (collapsed) original editor (that does not have line wraps).
// Account for wrapped lines in the (collapsed) original editor (which doesn't wrap lines).
if (count > 1) {
origViewZones.push({
afterLineNumber: a.originalRange.startLineNumber + i,
Expand All @@ -216,6 +208,15 @@ export class ViewZoneAlignment extends Disposable {
});
}
}

modViewZones.push({
afterLineNumber: a.modifiedRange.startLineNumber - 1,
domNode: deletedCodeDomNode,
heightInPx: result.heightInLines * modLineHeight,
minWidthInPx: result.minWidthInPx,
marginDomNode,
setZoneId(id) { zoneId = id; },
});
}

const marginDomNode = document.createElement('div');
Expand Down Expand Up @@ -280,6 +281,8 @@ export class ViewZoneAlignment extends Disposable {
});

this._register(autorunWithStore2('alignment viewzones', (reader) => {
const scrollState = StableEditorScrollState.capture(this._modifiedEditor);

const alignmentViewZones_ = alignmentViewZones.read(reader);
isChangingViewZones = true;
this._originalEditor.changeViewZones((aOrig) => {
Expand All @@ -305,6 +308,8 @@ export class ViewZoneAlignment extends Disposable {
}
});
isChangingViewZones = false;

scrollState.restore(this._modifiedEditor);
}));

this._originalScrollTop = observableFromEvent(this._originalEditor.onDidScrollChange, () => this._originalEditor.getScrollTop());
Expand All @@ -321,7 +326,7 @@ export class ViewZoneAlignment extends Disposable {
this._register(autorun('update scroll modified', (reader) => {
const newScrollTopModified = this._originalScrollTop.read(reader)
- (this._originalScrollOffsetAnimated.get() - this._modifiedScrollOffsetAnimated.read(reader))
- (this._origExtraHeight.get() - this._modExtraHeight.read(reader));
- (this._originalTopPadding.get() - this._modifiedTopPadding.read(reader));
if (newScrollTopModified !== this._modifiedEditor.getScrollTop()) {
this._modifiedEditor.setScrollTop(newScrollTopModified, ScrollType.Immediate);
}
Expand All @@ -330,7 +335,7 @@ export class ViewZoneAlignment extends Disposable {
this._register(autorun('update scroll original', (reader) => {
const newScrollTopOriginal = this._modifiedScrollTop.read(reader)
- (this._modifiedScrollOffsetAnimated.get() - this._originalScrollOffsetAnimated.read(reader))
- (this._modExtraHeight.get() - this._origExtraHeight.read(reader));
- (this._modifiedTopPadding.get() - this._originalTopPadding.read(reader));
if (newScrollTopOriginal !== this._originalEditor.getScrollTop()) {
this._originalEditor.setScrollTop(newScrollTopOriginal, ScrollType.Immediate);
}
Expand All @@ -342,21 +347,21 @@ export class ViewZoneAlignment extends Disposable {

let deltaOrigToMod = 0;
if (m) {
const trueTopOriginal = this._originalEditor.getTopForLineNumber(m.lineRangeMapping.originalRange.startLineNumber, true) - this._origExtraHeight.get();
const trueTopModified = this._modifiedEditor.getTopForLineNumber(m.lineRangeMapping.modifiedRange.startLineNumber, true) - this._modExtraHeight.get();
const trueTopOriginal = this._originalEditor.getTopForLineNumber(m.lineRangeMapping.originalRange.startLineNumber, true) - this._originalTopPadding.get();
const trueTopModified = this._modifiedEditor.getTopForLineNumber(m.lineRangeMapping.modifiedRange.startLineNumber, true) - this._modifiedTopPadding.get();
deltaOrigToMod = trueTopModified - trueTopOriginal;
}

if (deltaOrigToMod > 0) {
this._modExtraHeight.set(0, undefined);
this._origExtraHeight.set(deltaOrigToMod, undefined);
this._modifiedTopPadding.set(0, undefined);
this._originalTopPadding.set(deltaOrigToMod, undefined);
} else if (deltaOrigToMod < 0) {
this._modExtraHeight.set(-deltaOrigToMod, undefined);
this._origExtraHeight.set(0, undefined);
this._modifiedTopPadding.set(-deltaOrigToMod, undefined);
this._originalTopPadding.set(0, undefined);
} else {
setTimeout(() => {
this._modExtraHeight.set(0, undefined);
this._origExtraHeight.set(0, undefined);
this._modifiedTopPadding.set(0, undefined);
this._originalTopPadding.set(0, undefined);
}, 400);
}

Expand Down

0 comments on commit 7ff66b3

Please sign in to comment.