Skip to content

Commit 02d738f

Browse files
committed
Refactors multi diff editor
1 parent c352e0d commit 02d738f

File tree

10 files changed

+121
-57
lines changed

10 files changed

+121
-57
lines changed

src/vs/base/common/async.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
7-
import { CancellationError } from 'vs/base/common/errors';
7+
import { BugIndicatingError, CancellationError } from 'vs/base/common/errors';
88
import { Emitter, Event } from 'vs/base/common/event';
99
import { Disposable, DisposableMap, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
1010
import { extUri as defaultExtUri, IExtUri } from 'vs/base/common/resources';
1111
import { URI } from 'vs/base/common/uri';
1212
import { setTimeout0 } from 'vs/base/common/platform';
1313
import { MicrotaskDelay } from './symbols';
14+
import { Lazy } from 'vs/base/common/lazy';
1415

1516
export function isThenable<T>(obj: unknown): obj is Promise<T> {
1617
return !!obj && typeof (obj as unknown as Promise<T>).then === 'function';
@@ -1624,6 +1625,67 @@ export namespace Promises {
16241625
}
16251626
}
16261627

1628+
export class StatefulPromise<T> {
1629+
private _value: T | undefined = undefined;
1630+
get value(): T | undefined { return this._value; }
1631+
1632+
private _error: unknown = undefined;
1633+
get error(): unknown { return this._error; }
1634+
1635+
private _isResolved = false;
1636+
get isResolved() { return this._isResolved; }
1637+
1638+
public readonly promise: Promise<T>;
1639+
1640+
constructor(promise: Promise<T>) {
1641+
this.promise = promise.then(
1642+
value => {
1643+
this._value = value;
1644+
this._isResolved = true;
1645+
return value;
1646+
},
1647+
error => {
1648+
this._error = error;
1649+
this._isResolved = true;
1650+
throw error;
1651+
}
1652+
);
1653+
}
1654+
1655+
public requireValue(): T {
1656+
if (!this._isResolved) {
1657+
throw new BugIndicatingError('Promise is not resolved yet');
1658+
}
1659+
if (this._error) {
1660+
throw this._error;
1661+
}
1662+
return this._value!;
1663+
}
1664+
}
1665+
1666+
export class LazyStatefulPromise<T> {
1667+
private _promise = new Lazy(() => new StatefulPromise(this._compute()));
1668+
1669+
constructor(
1670+
private readonly _compute: () => Promise<T>,
1671+
) { }
1672+
1673+
/**
1674+
* Returns the resolved value.
1675+
* Crashes if the promise is not resolved yet.
1676+
*/
1677+
public requireValue(): T {
1678+
return this._promise.value.requireValue();
1679+
}
1680+
1681+
/**
1682+
* Returns the promise (and triggers a computation of the promise if not yet done so).
1683+
*/
1684+
public getPromise(): Promise<T> {
1685+
return this._promise.value.promise;
1686+
}
1687+
}
1688+
16271689
//#endregion
16281690

16291691
//#region

src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { IObservable, ISettableObservable, derived, observableValue } from 'vs/base/common/observable';
77
import { Constants } from 'vs/base/common/uint';
8-
import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser';
98
import { diffEditorDefaultOptions } from 'vs/editor/common/config/diffEditor';
109
import { IDiffEditorBaseOptions, IDiffEditorOptions, IEditorOptions, ValidDiffEditorBaseOptions, clampedFloat, clampedInt, boolean as validateBooleanOption, stringSet as validateStringSetOption } from 'vs/editor/common/config/editorOptions';
1110

@@ -14,16 +13,17 @@ export class DiffEditorOptions {
1413

1514
public get editorOptions(): IObservable<IEditorOptions, { changedOptions: IEditorOptions }> { return this._options; }
1615

16+
private readonly _diffEditorWidth = observableValue<number>(this, 0);
17+
1718
constructor(
18-
options: Readonly<IDiffEditorConstructionOptions>,
19-
private readonly diffEditorWidth: IObservable<number>,
19+
options: Readonly<IDiffEditorOptions>,
2020
) {
2121
const optionsCopy = { ...options, ...validateDiffEditorOptions(options, diffEditorDefaultOptions) };
2222
this._options = observableValue(this, optionsCopy);
2323
}
2424

2525
public readonly couldShowInlineViewBecauseOfSize = derived(this, reader =>
26-
this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint
26+
this._options.read(reader).renderSideBySide && this._diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint
2727
);
2828

2929
public readonly renderOverviewRuler = derived(this, reader => this._options.read(reader).renderOverviewRuler);
@@ -63,6 +63,10 @@ export class DiffEditorOptions {
6363
const newOptions = { ...this._options.get(), ...changedOptions, ...newDiffEditorOptions };
6464
this._options.set(newOptions, undefined, { changedOptions: changedOptions });
6565
}
66+
67+
public setWidth(width: number): void {
68+
this._diffEditorWidth.set(width, undefined);
69+
}
6670
}
6771

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

src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { RunOnceScheduler } from 'vs/base/common/async';
77
import { CancellationTokenSource } from 'vs/base/common/cancellation';
88
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
99
import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable';
10-
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
1110
import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService';
1211
import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils';
1312
import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange';
@@ -68,7 +67,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
6867
private readonly _cancellationTokenSource = new CancellationTokenSource();
6968

7069
private readonly _diffProvider = derived(this, reader => {
71-
const diffProvider = this._diffProviderFactoryService.createDiffProvider(this._editor, {
70+
const diffProvider = this._diffProviderFactoryService.createDiffProvider({
7271
diffAlgorithm: this._options.diffAlgorithm.read(reader)
7372
});
7473
const onChangeSignal = observableSignalFromEvent('onDidChange', diffProvider.onDidChange);
@@ -81,7 +80,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
8180
constructor(
8281
public readonly model: IDiffEditorModel,
8382
private readonly _options: DiffEditorOptions,
84-
private readonly _editor: IDiffEditor,
8583
@IDiffProviderFactoryService private readonly _diffProviderFactoryService: IDiffProviderFactoryService,
8684
) {
8785
super();

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { findLast } from 'vs/base/common/arraysFind';
88
import { onUnexpectedError } from 'vs/base/common/errors';
99
import { Event } from 'vs/base/common/event';
1010
import { toDisposable } from 'vs/base/common/lifecycle';
11-
import { IObservable, ITransaction, autorunWithStore, derived, observableFromEvent, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from 'vs/base/common/observable';
11+
import { IObservable, ITransaction, autorun, autorunWithStore, derived, observableFromEvent, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from 'vs/base/common/observable';
1212
import { derivedDisposable } from 'vs/base/common/observableInternal/derived';
1313
import 'vs/css!./style';
1414
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
@@ -111,7 +111,10 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor {
111111
this._rootSizeObserver = this._register(new ObservableElementSizeObserver(this.elements.root, options.dimension));
112112
this._rootSizeObserver.setAutomaticLayout(options.automaticLayout ?? false);
113113

114-
this._options = new DiffEditorOptions(options, this._rootSizeObserver.width);
114+
this._options = new DiffEditorOptions(options);
115+
this._register(autorun(reader => {
116+
this._options.setWidth(this._rootSizeObserver.width.read(reader));
117+
}));
115118

116119
this._contextKeyService.createKey(EditorContextKeys.isEmbeddedDiffEditor.key, false);
117120
this._register(bindContextKey(EditorContextKeys.isEmbeddedDiffEditor, this._contextKeyService,
@@ -386,7 +389,7 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor {
386389
}
387390

388391
public createViewModel(model: IDiffEditorModel): IDiffEditorViewModel {
389-
return this._instantiationService.createInstance(DiffEditorViewModel, model, this._options, this);
392+
return this._instantiationService.createInstance(DiffEditorViewModel, model, this._options);
390393
}
391394

392395
override getModel(): IDiffEditorModel | null { return this._diffModel.get()?.model ?? null; }

src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
76
import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/diffEditor/workerBasedDocumentDiffProvider';
87
import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
98
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -17,8 +16,7 @@ export interface IDocumentDiffProviderOptions {
1716

1817
export interface IDiffProviderFactoryService {
1918
readonly _serviceBrand: undefined;
20-
// TODO, don't include IDiffEditor
21-
createDiffProvider(editor: IDiffEditor, options: IDocumentDiffProviderOptions): IDocumentDiffProvider;
19+
createDiffProvider(options: IDocumentDiffProviderOptions): IDocumentDiffProvider;
2220
}
2321

2422
export class DiffProviderFactoryService implements IDiffProviderFactoryService {
@@ -28,7 +26,7 @@ export class DiffProviderFactoryService implements IDiffProviderFactoryService {
2826
@IInstantiationService private readonly instantiationService: IInstantiationService,
2927
) { }
3028

31-
createDiffProvider(editor: IDiffEditor, options: IDocumentDiffProviderOptions): IDocumentDiffProvider {
29+
createDiffProvider(options: IDocumentDiffProviderOptions): IDocumentDiffProvider {
3230
return this.instantiationService.createInstance(WorkerBasedDocumentDiffProvider, options);
3331
}
3432
}

src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts

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

66
import { Disposable } from 'vs/base/common/lifecycle';
77
import { derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable';
8-
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
8+
import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorOptions';
9+
import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel';
910
import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
1011
import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon';
12+
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
1113

1214
export class MultiDiffEditorViewModel extends Disposable {
1315
private readonly _documents = observableFromEvent(this._model.onDidChange, /** @description MultiDiffEditorViewModel.documents */() => this._model.documents);
1416

1517
public readonly items = derivedWithStore<readonly DocumentDiffItemViewModel[]>(this,
16-
(reader, store) => this._documents.read(reader).map(d => store.add(new DocumentDiffItemViewModel(d, this._diffEditorViewModelFactory)))
18+
(reader, store) => this._documents.read(reader).map(d => store.add(new DocumentDiffItemViewModel(d, this._instantiationService)))
1719
).recomputeInitiallyAndOnChange(this._store);
1820

1921
public readonly activeDiffItem = observableValue<DocumentDiffItemViewModel | undefined>(this, undefined);
2022

23+
public async waitForDiffs(): Promise<void> {
24+
for (const d of this.items.get()) {
25+
await d.diffEditorViewModel.waitForDiff();
26+
}
27+
}
28+
2129
constructor(
2230
private readonly _model: IMultiDiffEditorModel,
23-
private readonly _diffEditorViewModelFactory: DiffEditorWidget,
31+
private readonly _instantiationService: IInstantiationService,
2432
) {
2533
super();
2634
}
2735
}
2836

2937
export class DocumentDiffItemViewModel extends Disposable {
3038
public readonly diffEditorViewModel: IDiffEditorViewModel;
31-
3239
public readonly collapsed = observableValue<boolean>(this, false);
3340

3441
constructor(
3542
public readonly entry: LazyPromise<IDocumentDiffItem>,
36-
diffEditorViewModelFactory: DiffEditorWidget
43+
private readonly _instantiationService: IInstantiationService,
3744
) {
3845
super();
3946

40-
this.diffEditorViewModel = this._register(diffEditorViewModelFactory.createViewModel({
47+
const options = new DiffEditorOptions(this.entry.value!.options || {});
48+
if (this.entry.value!.onOptionsDidChange) {
49+
this._register(this.entry.value!.onOptionsDidChange(() => {
50+
options.updateOptions(this.entry.value!.options || {});
51+
}));
52+
}
53+
54+
this.diffEditorViewModel = this._register(this._instantiationService.createInstance(DiffEditorViewModel, {
4155
original: entry.value!.original!,
4256
modified: entry.value!.modified!,
43-
}));
57+
}, options));
4458
}
4559
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class MultiDiffEditorWidget extends Disposable {
4242
}
4343

4444
public createViewModel(model: IMultiDiffEditorModel): MultiDiffEditorViewModel {
45-
return this._widgetImpl.get().createViewModel(model);
45+
return new MultiDiffEditorViewModel(model, this._instantiationService);
4646
}
4747

4848
public setViewModel(viewModel: MultiDiffEditorViewModel | undefined): void {

src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@
55

66
import { Dimension, getWindow, h, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
77
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
8+
import { findFirstMaxBy } from 'vs/base/common/arraysFind';
89
import { Disposable, IReference, toDisposable } from 'vs/base/common/lifecycle';
910
import { IObservable, IReader, autorun, derived, derivedObservableWithCache, derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable';
11+
import { disposableObservableValue, globalTransaction, transaction } from 'vs/base/common/observableInternal/base';
1012
import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
1113
import 'vs/css!./style';
12-
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
1314
import { ObservableElementSizeObserver } from 'vs/editor/browser/widget/diffEditor/utils';
14-
import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
15+
import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory';
1516
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
1617
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
1718
import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate';
19+
import { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from './multiDiffEditorViewModel';
1820
import { ObjectPool } from './objectPool';
19-
import { disposableObservableValue, globalTransaction, transaction } from 'vs/base/common/observableInternal/base';
20-
import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory';
21-
import { findFirstMaxBy } from 'vs/base/common/arraysFind';
22-
import { MultiDiffEditorViewModel, DocumentDiffItemViewModel } from './multiDiffEditorViewModel';
2321

2422
export class MultiDiffEditorWidgetImpl extends Disposable {
2523
private readonly _elements = h('div', {
@@ -49,14 +47,6 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
4947
return template;
5048
}));
5149

52-
private readonly _hiddenContainer = document.createElement('div');
53-
54-
private readonly _editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._hiddenContainer, {
55-
hideUnchangedRegions: {
56-
enabled: true,
57-
},
58-
}, {}));
59-
6050
private readonly _scrollable = this._register(new Scrollable({
6151
forceIntegerValues: false,
6252
scheduleAtNextAnimationFrame: (cb) => scheduleAtNextAnimationFrame(getWindow(this._element), cb),
@@ -149,10 +139,6 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
149139
})));
150140
}
151141

152-
public createViewModel(model: IMultiDiffEditorModel): MultiDiffEditorViewModel {
153-
return new MultiDiffEditorViewModel(model, this._editor);
154-
}
155-
156142
private render(reader: IReader | undefined) {
157143
const scrollTop = this._scrollTop.read(reader);
158144
let contentScrollOffsetToScrollOffset = 0;

src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,8 @@ export class MultiDiffEditor extends EditorPane {
4747

4848
override async setInput(input: MultiDiffEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
4949
await super.setInput(input, options, context, token);
50-
if (!input.viewModel) {
51-
const vm = await input.getModel();
52-
input.viewModel = this._multiDiffEditorWidget!.createViewModel(vm);
53-
}
54-
this._multiDiffEditorWidget!.setViewModel(input.viewModel);
50+
const vm = await input.getViewModel();
51+
this._multiDiffEditorWidget!.setViewModel(vm);
5552
}
5653

5754
override async clearInput(): Promise<void> {

0 commit comments

Comments
 (0)