Skip to content

Commit 20d8459

Browse files
authored
Implements simple caching for diff editor v2 (microsoft#185060)
1 parent 34911a2 commit 20d8459

File tree

11 files changed

+92
-30
lines changed

11 files changed

+92
-30
lines changed

src/vs/editor/browser/editorBrowser.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,8 @@ export interface IDiffEditor extends editorCommon.IEditor {
11571157
*/
11581158
getModel(): editorCommon.IDiffEditorModel | null;
11591159

1160+
createViewModel(model: editorCommon.IDiffEditorModel): editorCommon.IDiffEditorViewModel;
1161+
11601162
/**
11611163
* Sets the current model attached to this editor.
11621164
* If the previous model was created by the editor via the value key in the options
@@ -1165,7 +1167,7 @@ export interface IDiffEditor extends editorCommon.IEditor {
11651167
* will not be destroyed.
11661168
* It is safe to call setModel(null) to simply detach the current model from the editor.
11671169
*/
1168-
setModel(model: editorCommon.IDiffEditorModel | null): void;
1170+
setModel(model: editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null): void;
11691171

11701172
/**
11711173
* Get the `original` editor.

src/vs/editor/browser/widget/codeEditorWidget.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
488488
return this._modelData.model;
489489
}
490490

491-
public setModel(_model: ITextModel | editorCommon.IDiffEditorModel | null = null): void {
491+
public setModel(_model: ITextModel | editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null = null): void {
492492
const model = <ITextModel | null>_model;
493493
if (this._modelData === null && model === null) {
494494
// Current model is the new model

src/vs/editor/browser/widget/diffEditorWidget.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,20 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
829829
};
830830
}
831831

832-
public setModel(model: editorCommon.IDiffEditorModel | null): void {
832+
public createViewModel(model: editorCommon.IDiffEditorModel): editorCommon.IDiffEditorViewModel {
833+
return {
834+
model,
835+
async waitForDiff() {
836+
// noop
837+
},
838+
};
839+
}
840+
841+
public setModel(model: editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null): void {
842+
if (model && 'model' in model) {
843+
model = model.model;
844+
}
845+
833846
// Guard us against partial null model
834847
if (model && (!model.original || !model.modified)) {
835848
throw new Error(!model.original ? 'DiffEditorWidget.setModel: Original model is null' : 'DiffEditorWidget.setModel: Modified model is null');

src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { IDimension } from 'vs/editor/common/core/dimension';
1111
import { IPosition, Position } from 'vs/editor/common/core/position';
1212
import { IRange, Range } from 'vs/editor/common/core/range';
1313
import { ISelection, Selection } from 'vs/editor/common/core/selection';
14-
import { IEditor, IEditorAction, IEditorDecorationsCollection, IEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
14+
import { IDiffEditorViewModel, IEditor, IEditorAction, IEditorDecorationsCollection, IEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
1515
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model';
1616

1717
export abstract class DelegatingEditor extends Disposable implements IEditor {
@@ -34,7 +34,7 @@ export abstract class DelegatingEditor extends Disposable implements IEditor {
3434
abstract saveViewState(): IEditorViewState | null;
3535
abstract restoreViewState(state: IEditorViewState | null): void;
3636
abstract getModel(): IEditorModel | null;
37-
abstract setModel(model: IEditorModel | null): void;
37+
abstract setModel(model: IEditorModel | null | IDiffEditorViewModel): void;
3838

3939
// #region editorBrowser.IDiffEditor: Delegating to modified Editor
4040

src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash';
77
import { findLast } from 'vs/base/common/arrays';
88
import { onUnexpectedError } from 'vs/base/common/errors';
99
import { Emitter, Event } from 'vs/base/common/event';
10-
import { IObservable, ISettableObservable, autorun, derived, keepAlive, observableValue, waitForState } from 'vs/base/common/observable';
11-
import { disposableObservableValue } from 'vs/base/common/observableImpl/base';
10+
import { IObservable, ISettableObservable, autorun, derived, keepAlive, observableValue } from 'vs/base/common/observable';
11+
import { disposableObservableValue, transaction } from 'vs/base/common/observableImpl/base';
1212
import { derivedWithStore } from 'vs/base/common/observableImpl/derived';
1313
import { isDefined } from 'vs/base/common/types';
1414
import { Constants } from 'vs/base/common/uint';
@@ -32,7 +32,7 @@ import { IDimension } from 'vs/editor/common/core/dimension';
3232
import { LineRange } from 'vs/editor/common/core/lineRange';
3333
import { Position } from 'vs/editor/common/core/position';
3434
import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
35-
import { EditorType, IContentSizeChangedEvent, IDiffEditorModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon';
35+
import { EditorType, IContentSizeChangedEvent, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon';
3636
import { IModelDeltaDecoration } from 'vs/editor/common/model';
3737
import { localize } from 'vs/nls';
3838
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -482,22 +482,27 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
482482
}
483483
}
484484

485-
override getModel(): IDiffEditorModel | null { return this._model.get(); }
486-
487-
override setModel(model: IDiffEditorModel | null): void {
488-
this._originalEditor.setModel(model ? model.original : null);
489-
this._modifiedEditor.setModel(model ? model.modified : null);
490-
491-
this._model.set(model, undefined);
492-
493-
this._diffModel.set(model ? new DiffModel(
485+
public createViewModel(model: IDiffEditorModel): IDiffEditorViewModel {
486+
return new DiffModel(
494487
model,
495488
this._options.map(o => o.ignoreTrimWhitespace),
496489
this._options.map(o => o.maxComputationTime),
497490
this._options.map(o => o.experimental.collapseUnchangedRegions!),
498491
this._options.map(o => o.experimental.showMoves! && o.renderSideBySide),
499492
this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider, this._options.get())
500-
) : undefined, undefined);
493+
);
494+
}
495+
496+
override getModel(): IDiffEditorModel | null { return this._model.get(); }
497+
498+
override setModel(model: IDiffEditorModel | null | IDiffEditorViewModel): void {
499+
const vm = model ? ('model' in model) ? model : this.createViewModel(model) : undefined;
500+
this._originalEditor.setModel(vm ? vm.model.original : null);
501+
this._modifiedEditor.setModel(vm ? vm.model.modified : null);
502+
transaction(tx => {
503+
this._model.set(vm?.model ?? null, tx);
504+
this._diffModel.set(vm as (DiffModel | undefined), tx);
505+
});
501506
}
502507

503508
override updateOptions(_newOptions: IDiffEditorOptions): void {
@@ -639,14 +644,23 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
639644
return;
640645
}
641646
// wait for the diff computation to finish
642-
waitForState(diffModel.isDiffUpToDate, s => s).then(() => {
647+
this.waitForDiff().then(() => {
643648
const diffs = diffModel.diff.get()?.mappings;
644649
if (!diffs || diffs.length === 0) {
645650
return;
646651
}
647652
this._goTo(diffs[0]);
648653
});
649654
}
655+
656+
657+
public async waitForDiff(): Promise<void> {
658+
const diffModel = this._diffModel.get();
659+
if (!diffModel) {
660+
return;
661+
}
662+
await diffModel.waitForDiff();
663+
}
650664
}
651665

652666
function validateDiffEditorOptions(options: Readonly<IDiffEditorOptions>, defaults: ValidDiffEditorBaseOptions): ValidDiffEditorBaseOptions {

src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55

66
import { RunOnceScheduler } from 'vs/base/common/async';
77
import { Disposable } from 'vs/base/common/lifecycle';
8-
import { IObservable, IReader, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable';
8+
import { IObservable, IReader, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable';
99
import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun';
1010
import { isDefined } from 'vs/base/common/types';
1111
import { LineRange } from 'vs/editor/common/core/lineRange';
1212
import { Range } from 'vs/editor/common/core/range';
1313
import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
1414
import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
1515
import { lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer';
16-
import { IDiffEditorModel } from 'vs/editor/common/editorCommon';
16+
import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon';
1717
import { ITextModel } from 'vs/editor/common/model';
1818
import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper';
1919
import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos';
2020
import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length';
2121

22-
export class DiffModel extends Disposable {
22+
export class DiffModel extends Disposable implements IDiffEditorViewModel {
2323
private readonly _isDiffUpToDate = observableValue<boolean>('isDiffUpToDate', false);
2424
public readonly isDiffUpToDate: IObservable<boolean> = this._isDiffUpToDate;
2525

@@ -184,6 +184,10 @@ export class DiffModel extends Disposable {
184184
}
185185
}
186186
}
187+
188+
public async waitForDiff(): Promise<void> {
189+
await waitForState(this.isDiffUpToDate, s => s);
190+
}
187191
}
188192

189193
export class DiffState {

src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ export class MovedBlocksLinesPart extends Disposable {
4242
const originalScrollTop = observableFromEvent(this._originalEditor.onDidScrollChange, () => this._originalEditor.getScrollTop());
4343
const modifiedScrollTop = observableFromEvent(this._modifiedEditor.onDidScrollChange, () => this._modifiedEditor.getScrollTop());
4444

45-
4645
this._register(autorun('update', (reader) => {
46+
element.replaceChildren();
47+
4748
const info = this._originalEditorLayoutInfo.read(reader);
4849
const info2 = this._modifiedEditorLayoutInfo.read(reader);
4950
if (!info || !info2) {
@@ -56,8 +57,6 @@ export class MovedBlocksLinesPart extends Disposable {
5657
return;
5758
}
5859

59-
element.replaceChildren();
60-
6160
let idx = 0;
6261
for (const m of moves) {
6362
function computeLineStart(range: LineRange, editor: ICodeEditor) {

src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I
2020
private diffAlgorithm: DiffAlgorithmName | IDocumentDiffProvider = 'advanced';
2121
private diffAlgorithmOnDidChangeSubscription: IDisposable | undefined = undefined;
2222

23+
private static readonly diffCache = new Map<string, { result: IDocumentDiff; context: string }>();
24+
2325
constructor(
2426
options: IWorkerBasedDocumentDiffProviderOptions,
2527
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService,
@@ -58,6 +60,13 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I
5860
};
5961
}
6062

63+
const uriKey = JSON.stringify([original.uri.toString(), modified.uri.toString()]);
64+
const context = JSON.stringify([original.id, modified.id, original.getAlternativeVersionId(), modified.getAlternativeVersionId(), JSON.stringify(options)]);
65+
const c = WorkerBasedDocumentDiffProvider.diffCache.get(uriKey);
66+
if (c && c.context === context) {
67+
return c.result;
68+
}
69+
6170
const sw = StopWatch.create(true);
6271
const result = await this.editorWorkerService.computeDiff(original.uri, modified.uri, options, this.diffAlgorithm);
6372
const timeMs = sw.elapsed();
@@ -81,6 +90,12 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I
8190
throw new Error('no diff result available');
8291
}
8392

93+
// max 10 items in cache
94+
if (WorkerBasedDocumentDiffProvider.diffCache.size > 10) {
95+
WorkerBasedDocumentDiffProvider.diffCache.delete(WorkerBasedDocumentDiffProvider.diffCache.keys().next().value);
96+
}
97+
98+
WorkerBasedDocumentDiffProvider.diffCache.set(uriKey, { result, context });
8499
return result;
85100
}
86101

src/vs/editor/common/editorCommon.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ export interface IDiffEditorModel {
104104
modified: ITextModel;
105105
}
106106

107+
export interface IDiffEditorViewModel {
108+
readonly model: IDiffEditorModel;
109+
110+
waitForDiff(): Promise<void>;
111+
}
112+
107113
/**
108114
* An event describing that an editor has had its model reset (i.e. `editor.setModel()`).
109115
*/
@@ -153,7 +159,7 @@ export interface IEditorAction {
153159
run(args?: unknown): Promise<void>;
154160
}
155161

156-
export type IEditorModel = ITextModel | IDiffEditorModel;
162+
export type IEditorModel = ITextModel | IDiffEditorModel | IDiffEditorViewModel;
157163

158164
/**
159165
* A (serializable) state of the cursors.

src/vs/monaco.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2623,6 +2623,11 @@ declare namespace monaco.editor {
26232623
modified: ITextModel;
26242624
}
26252625

2626+
export interface IDiffEditorViewModel {
2627+
readonly model: IDiffEditorModel;
2628+
waitForDiff(): Promise<void>;
2629+
}
2630+
26262631
/**
26272632
* An event describing that an editor has had its model reset (i.e. `editor.setModel()`).
26282633
*/
@@ -2657,7 +2662,7 @@ declare namespace monaco.editor {
26572662
run(args?: unknown): Promise<void>;
26582663
}
26592664

2660-
export type IEditorModel = ITextModel | IDiffEditorModel;
2665+
export type IEditorModel = ITextModel | IDiffEditorModel | IDiffEditorViewModel;
26612666

26622667
/**
26632668
* A (serializable) state of the cursors.
@@ -6026,6 +6031,7 @@ declare namespace monaco.editor {
60266031
* Type the getModel() of IEditor.
60276032
*/
60286033
getModel(): IDiffEditorModel | null;
6034+
createViewModel(model: IDiffEditorModel): IDiffEditorViewModel;
60296035
/**
60306036
* Sets the current model attached to this editor.
60316037
* If the previous model was created by the editor via the value key in the options
@@ -6034,7 +6040,7 @@ declare namespace monaco.editor {
60346040
* will not be destroyed.
60356041
* It is safe to call setModel(null) to simply detach the current model from the editor.
60366042
*/
6037-
setModel(model: IDiffEditorModel | null): void;
6043+
setModel(model: IDiffEditorModel | IDiffEditorViewModel | null): void;
60386044
/**
60396045
* Get the `original` editor.
60406046
*/

0 commit comments

Comments
 (0)