Skip to content

Commit

Permalink
Refactors multi diff editor
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Nov 21, 2023
1 parent c352e0d commit 02d738f
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 57 deletions.
64 changes: 63 additions & 1 deletion src/vs/base/common/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
*--------------------------------------------------------------------------------------------*/

import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationError } from 'vs/base/common/errors';
import { BugIndicatingError, CancellationError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableMap, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { extUri as defaultExtUri, IExtUri } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { setTimeout0 } from 'vs/base/common/platform';
import { MicrotaskDelay } from './symbols';
import { Lazy } from 'vs/base/common/lazy';

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

export class StatefulPromise<T> {
private _value: T | undefined = undefined;
get value(): T | undefined { return this._value; }

private _error: unknown = undefined;
get error(): unknown { return this._error; }

private _isResolved = false;
get isResolved() { return this._isResolved; }

public readonly promise: Promise<T>;

constructor(promise: Promise<T>) {
this.promise = promise.then(
value => {
this._value = value;
this._isResolved = true;
return value;
},
error => {
this._error = error;
this._isResolved = true;
throw error;
}
);
}

public requireValue(): T {
if (!this._isResolved) {
throw new BugIndicatingError('Promise is not resolved yet');
}
if (this._error) {
throw this._error;
}
return this._value!;
}
}

export class LazyStatefulPromise<T> {
private _promise = new Lazy(() => new StatefulPromise(this._compute()));

constructor(
private readonly _compute: () => Promise<T>,
) { }

/**
* Returns the resolved value.
* Crashes if the promise is not resolved yet.
*/
public requireValue(): T {
return this._promise.value.requireValue();
}

/**
* Returns the promise (and triggers a computation of the promise if not yet done so).
*/
public getPromise(): Promise<T> {
return this._promise.value.promise;
}
}

//#endregion

//#region
Expand Down
12 changes: 8 additions & 4 deletions src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

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

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

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

private readonly _diffEditorWidth = observableValue<number>(this, 0);

constructor(
options: Readonly<IDiffEditorConstructionOptions>,
private readonly diffEditorWidth: IObservable<number>,
options: Readonly<IDiffEditorOptions>,
) {
const optionsCopy = { ...options, ...validateDiffEditorOptions(options, diffEditorDefaultOptions) };
this._options = observableValue(this, optionsCopy);
}

public readonly couldShowInlineViewBecauseOfSize = derived(this, reader =>
this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint
this._options.read(reader).renderSideBySide && this._diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint
);

public readonly renderOverviewRuler = derived(this, reader => this._options.read(reader).renderOverviewRuler);
Expand Down Expand Up @@ -63,6 +63,10 @@ export class DiffEditorOptions {
const newOptions = { ...this._options.get(), ...changedOptions, ...newDiffEditorOptions };
this._options.set(newOptions, undefined, { changedOptions: changedOptions });
}

public setWidth(width: number): void {
this._diffEditorWidth.set(width, undefined);
}
}

function validateDiffEditorOptions(options: Readonly<IDiffEditorOptions>, defaults: ValidDiffEditorBaseOptions): ValidDiffEditorBaseOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable';
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService';
import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils';
import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange';
Expand Down Expand Up @@ -68,7 +67,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
private readonly _cancellationTokenSource = new CancellationTokenSource();

private readonly _diffProvider = derived(this, reader => {
const diffProvider = this._diffProviderFactoryService.createDiffProvider(this._editor, {
const diffProvider = this._diffProviderFactoryService.createDiffProvider({
diffAlgorithm: this._options.diffAlgorithm.read(reader)
});
const onChangeSignal = observableSignalFromEvent('onDidChange', diffProvider.onDidChange);
Expand All @@ -81,7 +80,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
constructor(
public readonly model: IDiffEditorModel,
private readonly _options: DiffEditorOptions,
private readonly _editor: IDiffEditor,
@IDiffProviderFactoryService private readonly _diffProviderFactoryService: IDiffProviderFactoryService,
) {
super();
Expand Down
9 changes: 6 additions & 3 deletions src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { findLast } from 'vs/base/common/arraysFind';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IObservable, ITransaction, autorunWithStore, derived, observableFromEvent, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from 'vs/base/common/observable';
import { IObservable, ITransaction, autorun, autorunWithStore, derived, observableFromEvent, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from 'vs/base/common/observable';
import { derivedDisposable } from 'vs/base/common/observableInternal/derived';
import 'vs/css!./style';
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
Expand Down Expand Up @@ -111,7 +111,10 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor {
this._rootSizeObserver = this._register(new ObservableElementSizeObserver(this.elements.root, options.dimension));
this._rootSizeObserver.setAutomaticLayout(options.automaticLayout ?? false);

this._options = new DiffEditorOptions(options, this._rootSizeObserver.width);
this._options = new DiffEditorOptions(options);
this._register(autorun(reader => {
this._options.setWidth(this._rootSizeObserver.width.read(reader));
}));

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

public createViewModel(model: IDiffEditorModel): IDiffEditorViewModel {
return this._instantiationService.createInstance(DiffEditorViewModel, model, this._options, this);
return this._instantiationService.createInstance(DiffEditorViewModel, model, this._options);
}

override getModel(): IDiffEditorModel | null { return this._diffModel.get()?.model ?? null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

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

export interface IDiffProviderFactoryService {
readonly _serviceBrand: undefined;
// TODO, don't include IDiffEditor
createDiffProvider(editor: IDiffEditor, options: IDocumentDiffProviderOptions): IDocumentDiffProvider;
createDiffProvider(options: IDocumentDiffProviderOptions): IDocumentDiffProvider;
}

export class DiffProviderFactoryService implements IDiffProviderFactoryService {
Expand All @@ -28,7 +26,7 @@ export class DiffProviderFactoryService implements IDiffProviderFactoryService {
@IInstantiationService private readonly instantiationService: IInstantiationService,
) { }

createDiffProvider(editor: IDiffEditor, options: IDocumentDiffProviderOptions): IDocumentDiffProvider {
createDiffProvider(options: IDocumentDiffProviderOptions): IDocumentDiffProvider {
return this.instantiationService.createInstance(WorkerBasedDocumentDiffProvider, options);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,55 @@

import { Disposable } from 'vs/base/common/lifecycle';
import { derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorOptions';
import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel';
import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

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

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

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

public async waitForDiffs(): Promise<void> {
for (const d of this.items.get()) {
await d.diffEditorViewModel.waitForDiff();
}
}

constructor(
private readonly _model: IMultiDiffEditorModel,
private readonly _diffEditorViewModelFactory: DiffEditorWidget,
private readonly _instantiationService: IInstantiationService,
) {
super();
}
}

export class DocumentDiffItemViewModel extends Disposable {
public readonly diffEditorViewModel: IDiffEditorViewModel;

public readonly collapsed = observableValue<boolean>(this, false);

constructor(
public readonly entry: LazyPromise<IDocumentDiffItem>,
diffEditorViewModelFactory: DiffEditorWidget
private readonly _instantiationService: IInstantiationService,
) {
super();

this.diffEditorViewModel = this._register(diffEditorViewModelFactory.createViewModel({
const options = new DiffEditorOptions(this.entry.value!.options || {});
if (this.entry.value!.onOptionsDidChange) {
this._register(this.entry.value!.onOptionsDidChange(() => {
options.updateOptions(this.entry.value!.options || {});
}));
}

this.diffEditorViewModel = this._register(this._instantiationService.createInstance(DiffEditorViewModel, {
original: entry.value!.original!,
modified: entry.value!.modified!,
}));
}, options));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class MultiDiffEditorWidget extends Disposable {
}

public createViewModel(model: IMultiDiffEditorModel): MultiDiffEditorViewModel {
return this._widgetImpl.get().createViewModel(model);
return new MultiDiffEditorViewModel(model, this._instantiationService);
}

public setViewModel(viewModel: MultiDiffEditorViewModel | undefined): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@

import { Dimension, getWindow, h, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { findFirstMaxBy } from 'vs/base/common/arraysFind';
import { Disposable, IReference, toDisposable } from 'vs/base/common/lifecycle';
import { IObservable, IReader, autorun, derived, derivedObservableWithCache, derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable';
import { disposableObservableValue, globalTransaction, transaction } from 'vs/base/common/observableInternal/base';
import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import 'vs/css!./style';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
import { ObservableElementSizeObserver } from 'vs/editor/browser/widget/diffEditor/utils';
import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory';
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate';
import { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from './multiDiffEditorViewModel';
import { ObjectPool } from './objectPool';
import { disposableObservableValue, globalTransaction, transaction } from 'vs/base/common/observableInternal/base';
import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory';
import { findFirstMaxBy } from 'vs/base/common/arraysFind';
import { MultiDiffEditorViewModel, DocumentDiffItemViewModel } from './multiDiffEditorViewModel';

export class MultiDiffEditorWidgetImpl extends Disposable {
private readonly _elements = h('div', {
Expand Down Expand Up @@ -49,14 +47,6 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
return template;
}));

private readonly _hiddenContainer = document.createElement('div');

private readonly _editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._hiddenContainer, {
hideUnchangedRegions: {
enabled: true,
},
}, {}));

private readonly _scrollable = this._register(new Scrollable({
forceIntegerValues: false,
scheduleAtNextAnimationFrame: (cb) => scheduleAtNextAnimationFrame(getWindow(this._element), cb),
Expand Down Expand Up @@ -149,10 +139,6 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
})));
}

public createViewModel(model: IMultiDiffEditorModel): MultiDiffEditorViewModel {
return new MultiDiffEditorViewModel(model, this._editor);
}

private render(reader: IReader | undefined) {
const scrollTop = this._scrollTop.read(reader);
let contentScrollOffsetToScrollOffset = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,8 @@ export class MultiDiffEditor extends EditorPane {

override async setInput(input: MultiDiffEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
await super.setInput(input, options, context, token);
if (!input.viewModel) {
const vm = await input.getModel();
input.viewModel = this._multiDiffEditorWidget!.createViewModel(vm);
}
this._multiDiffEditorWidget!.setViewModel(input.viewModel);
const vm = await input.getViewModel();
this._multiDiffEditorWidget!.setViewModel(vm);
}

override async clearInput(): Promise<void> {
Expand Down
Loading

0 comments on commit 02d738f

Please sign in to comment.