Skip to content

Commit 0f8e239

Browse files
authored
Implements settings useMixedLinesDiff and useInterleavedLinesDiff (microsoft#233102)
1 parent da42cd4 commit 0f8e239

File tree

10 files changed

+198
-96
lines changed

10 files changed

+198
-96
lines changed

src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati
8383
export class LineSource {
8484
constructor(
8585
public readonly lineTokens: LineTokens[],
86-
public readonly lineBreakData: (ModelLineProjectionData | null)[],
87-
public readonly mightContainNonBasicASCII: boolean,
88-
public readonly mightContainRTL: boolean,
86+
public readonly lineBreakData: (ModelLineProjectionData | null)[] = lineTokens.map(t => null),
87+
public readonly mightContainNonBasicASCII: boolean = true,
88+
public readonly mightContainRTL: boolean = true,
8989
) { }
9090
}
9191

src/vs/editor/common/config/editorOptions.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4152,8 +4152,8 @@ export interface IInlineSuggestOptions {
41524152
edits?: {
41534153
experimental?: {
41544154
enabled?: boolean;
4155-
// side-by-side, mixed lines diff, interleaved lines diff
4156-
useMixedLinesDiff?: 'never' | 'whenPossible';
4155+
useMixedLinesDiff?: 'never' | 'whenPossible' | 'afterJumpWhenPossible';
4156+
useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump';
41574157
};
41584158
};
41594159
}
@@ -4184,6 +4184,7 @@ class InlineEditorSuggest extends BaseEditorOption<EditorOption.inlineSuggest, I
41844184
experimental: {
41854185
enabled: true,
41864186
useMixedLinesDiff: 'never',
4187+
useInterleavedLinesDiff: 'never',
41874188
},
41884189
},
41894190
};
@@ -4233,6 +4234,12 @@ class InlineEditorSuggest extends BaseEditorOption<EditorOption.inlineSuggest, I
42334234
description: nls.localize('inlineSuggest.edits.experimental.useMixedLinesDiff', "Controls whether to enable experimental edits in inline suggestions."),
42344235
enum: ['never', 'whenPossible'],
42354236
},
4237+
'editor.inlineSuggest.edits.experimental.useInterleavedLinesDiff': {
4238+
type: 'string',
4239+
default: defaults.edits.experimental.useInterleavedLinesDiff,
4240+
description: nls.localize('inlineSuggest.edits.experimental.useInterleavedLinesDiff', "Controls whether to enable experimental interleaved lines diff in inline suggestions."),
4241+
enum: ['never', 'always', 'afterJump'],
4242+
},
42364243
}
42374244
);
42384245
}
@@ -4253,7 +4260,8 @@ class InlineEditorSuggest extends BaseEditorOption<EditorOption.inlineSuggest, I
42534260
edits: {
42544261
experimental: {
42554262
enabled: boolean(input.edits?.experimental?.enabled, this.defaultValue.edits.experimental.enabled),
4256-
useMixedLinesDiff: stringSet(input.edits?.experimental?.useMixedLinesDiff, this.defaultValue.edits.experimental.useMixedLinesDiff, ['never', 'whenPossible']),
4263+
useMixedLinesDiff: stringSet(input.edits?.experimental?.useMixedLinesDiff, this.defaultValue.edits.experimental.useMixedLinesDiff, ['never', 'whenPossible', 'afterJumpWhenPossible']),
4264+
useInterleavedLinesDiff: stringSet(input.edits?.experimental?.useInterleavedLinesDiff, this.defaultValue.edits.experimental.useInterleavedLinesDiff, ['never', 'always', 'afterJump']),
42574265
},
42584266
},
42594267
};

src/vs/editor/common/tokens/lineTokens.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@ export class LineTokens implements IViewLineTokens {
280280
callback(tokenIndex);
281281
}
282282
}
283+
284+
toString(): string {
285+
let result = '';
286+
this.forEach((i) => {
287+
result += `[${this.getTokenText(i)}]{${this.getClassName(i)}}`;
288+
});
289+
return result;
290+
}
283291
}
284292

285293
class SliceLineTokens implements IViewLineTokens {

src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,14 @@ export class InlineCompletionsModel extends Disposable {
299299
const currentItemIsCollapsed = !disableCollapsing && (cursorDist > 1 && this._collapsedInlineEditId.read(reader) === item.inlineEditCompletion.semanticId);
300300

301301
const commands = item.inlineEditCompletion.inlineCompletion.source.inlineCompletions.commands;
302-
const inlineEdit = new InlineEdit(edit, currentItemIsCollapsed, false, commands ?? []);
302+
const renderExplicitly = this._jumpedTo.read(reader);
303+
const inlineEdit = new InlineEdit(edit, currentItemIsCollapsed, renderExplicitly, commands ?? []);
303304

304305
return { kind: 'inlineEdit', inlineEdit, inlineCompletion: item.inlineEditCompletion, edits: [edit], cursorAtInlineEdit };
305306
}
306307

308+
this._jumpedTo.set(false, undefined);
309+
307310
const suggestItem = this.selectedSuggestItem.read(reader);
308311
if (suggestItem) {
309312
const suggestCompletionEdit = singleTextRemoveCommonPrefix(suggestItem.toSingleTextEdit(), model);
@@ -647,11 +650,14 @@ export class InlineCompletionsModel extends Disposable {
647650
};
648651
}
649652

653+
private _jumpedTo = observableValue(this, false);
654+
650655
public jump(): void {
651656
const s = this.inlineEditState.get();
652657
if (!s) { return; }
653658

654659
transaction(tx => {
660+
this._jumpedTo.set(true, tx);
655661
this.dontRefetchSignal.trigger(tx);
656662
this._editor.setPosition(s.inlineEdit.range.getStartPosition(), 'inlineCompletions.jump');
657663
this._editor.revealLine(s.inlineEdit.range.startLineNumber);

src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class InlineEdit {
1010
constructor(
1111
public readonly edit: SingleTextEdit,
1212
public readonly isCollapsed: boolean,
13-
public readonly showInlineIfPossible: boolean,
13+
public readonly renderExplicitly: boolean,
1414
public readonly commands: readonly Command[],
1515
) { }
1616

@@ -23,6 +23,6 @@ export class InlineEdit {
2323
}
2424

2525
public equals(other: InlineEdit): boolean {
26-
return this.edit.equals(other.edit) && this.isCollapsed === other.isCollapsed;
26+
return this.edit.equals(other.edit) && this.isCollapsed === other.isCollapsed && this.renderExplicitly === other.renderExplicitly;
2727
}
2828
}

src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineDiffView.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,27 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Disposable } from '../../../../../../base/common/lifecycle.js';
7-
import { autorunWithStore, derived, IObservable } from '../../../../../../base/common/observable.js';
7+
import { autorunWithStore, derived, IObservable, observableFromEvent } from '../../../../../../base/common/observable.js';
88
import { ICodeEditor } from '../../../../../browser/editorBrowser.js';
99
import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js';
1010
import { rangeIsSingleLine } from '../../../../../browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.js';
11+
import { LineSource, renderLines, RenderOptions } from '../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js';
1112
import { diffLineDeleteDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineAddDecorationBackground, diffWholeLineAddDecoration, diffAddDecorationEmpty, diffAddDecoration } from '../../../../../browser/widget/diffEditor/registrations.contribution.js';
13+
import { applyViewZones, IObservableViewZone } from '../../../../../browser/widget/diffEditor/utils.js';
14+
import { EditorOption } from '../../../../../common/config/editorOptions.js';
1215
import { Range } from '../../../../../common/core/range.js';
1316
import { AbstractText } from '../../../../../common/core/textEdit.js';
1417
import { DetailedLineRangeMapping } from '../../../../../common/diff/rangeMapping.js';
15-
import { IModelDeltaDecoration } from '../../../../../common/model.js';
18+
import { IModelDeltaDecoration, ITextModel } from '../../../../../common/model.js';
1619
import { ModelDecorationOptions } from '../../../../../common/model/textModel.js';
17-
import { classNames } from './inlineEditsView.js';
20+
import { InlineDecoration, InlineDecorationType } from '../../../../../common/viewModel.js';
21+
import { classNames } from './utils.js';
1822

1923
export interface IOriginalEditorInlineDiffViewState {
2024
diff: DetailedLineRangeMapping[];
2125
modifiedText: AbstractText;
22-
showInline: boolean;
26+
mode: 'mixedLines' | 'interleavedLines' | 'sideBySide';
27+
2328
modifiedCodeEditor: ICodeEditor;
2429
}
2530

@@ -31,6 +36,7 @@ export class OriginalEditorInlineDiffView extends Disposable {
3136
constructor(
3237
private readonly _originalEditor: ICodeEditor,
3338
private readonly _state: IObservable<IOriginalEditorInlineDiffViewState | undefined>,
39+
private readonly _modifiedTextModel: ITextModel,
3440
) {
3541
super();
3642

@@ -43,14 +49,69 @@ export class OriginalEditorInlineDiffView extends Disposable {
4349
store.add(observableCodeEditor(e).setDecorations(this._decorations.map(d => d?.modifiedDecorations ?? [])));
4450
}
4551
}));
52+
53+
const editor = observableCodeEditor(this._originalEditor);
54+
55+
const tokenizationFinished = modelTokenizationFinished(_modifiedTextModel);
56+
57+
const originalViewZones = derived(this, (reader) => {
58+
const originalModel = editor.model.read(reader);
59+
if (!originalModel) { return []; }
60+
61+
const origViewZones: IObservableViewZone[] = [];
62+
const renderOptions = RenderOptions.fromEditor(this._originalEditor);
63+
const modLineHeight = editor.getOption(EditorOption.lineHeight).read(reader);
64+
65+
const s = this._state.read(reader);
66+
if (!s) { return origViewZones; }
67+
68+
for (const diff of s.diff) {
69+
if (s.mode !== 'interleavedLines') {
70+
continue;
71+
}
72+
73+
tokenizationFinished.read(reader); // Update view-zones once tokenization completes
74+
75+
const source = new LineSource(diff.modified.mapToLineArray(l => this._modifiedTextModel.tokenization.getLineTokens(l)));
76+
77+
const decorations: InlineDecoration[] = [];
78+
for (const i of diff.innerChanges || []) {
79+
decorations.push(new InlineDecoration(
80+
i.modifiedRange.delta(-(diff.original.startLineNumber - 1)),
81+
diffAddDecoration.className!,
82+
InlineDecorationType.Regular,
83+
));
84+
}
85+
86+
const deletedCodeDomNode = document.createElement('div');
87+
deletedCodeDomNode.classList.add('view-lines', 'line-insert', 'monaco-mouse-cursor-text');
88+
// .inline-deleted-margin-view-zone
89+
90+
const result = renderLines(source, renderOptions, decorations, deletedCodeDomNode);
91+
92+
origViewZones.push({
93+
afterLineNumber: diff.original.endLineNumberExclusive - 1,
94+
domNode: deletedCodeDomNode,
95+
heightInPx: result.heightInLines * modLineHeight,
96+
minWidthInPx: result.minWidthInPx,
97+
98+
showInHiddenAreas: true,
99+
suppressMouseDown: true,
100+
});
101+
}
102+
103+
return origViewZones;
104+
});
105+
106+
this._register(applyViewZones(this._originalEditor, originalViewZones));
46107
}
47108

48109
private readonly _decorations = derived(this, reader => {
49110
const diff = this._state.read(reader);
50111
if (!diff) { return undefined; }
51112

52113
const modified = diff.modifiedText;
53-
const showInline = diff.showInline;
114+
const showInline = diff.mode === 'mixedLines';
54115

55116
const renderIndicators = false;
56117
const showEmptyDecorations = true;
@@ -59,7 +120,7 @@ export class OriginalEditorInlineDiffView extends Disposable {
59120
const modifiedDecorations: IModelDeltaDecoration[] = [];
60121

61122
for (const m of diff.diff) {
62-
const showFullLineDecorations = false;
123+
const showFullLineDecorations = true;
63124
if (showFullLineDecorations) {
64125
if (!m.original.isEmpty) {
65126
originalDecorations.push({ range: m.original.toInclusiveRange()!, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground });
@@ -133,3 +194,8 @@ function allowsTrueInlineDiffRendering(mapping: DetailedLineRangeMapping): boole
133194
return mapping.innerChanges.every(c =>
134195
(rangeIsSingleLine(c.modifiedRange) && rangeIsSingleLine(c.originalRange)));
135196
}
197+
198+
let i = 0;
199+
function modelTokenizationFinished(model: ITextModel): IObservable<number> {
200+
return observableFromEvent(model.onDidChangeTokens, () => i++);
201+
}

0 commit comments

Comments
 (0)