Skip to content

Commit

Permalink
Multi diff editor: Introduce view model
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Nov 16, 2023
1 parent 4e7a877 commit 917b056
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable';
import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils';
import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl';
import { MultiDiffEditorViewModel, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import './colors';
import { DiffEditorItemTemplate } from 'vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate';
import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory';

export class MultiDiffEditorWidget extends Disposable {
private readonly _dimension = observableValue<Dimension | undefined>(this, undefined);
private readonly _model = observableValue<IMultiDiffEditorModel | undefined>(this, undefined);
private readonly _viewModel = observableValue<MultiDiffEditorViewModel | undefined>(this, undefined);

private readonly widgetImpl = derivedWithStore(this, (reader, store) => {
private readonly _widgetImpl = derivedWithStore(this, (reader, store) => {
readHotReloadableExport(DiffEditorItemTemplate, reader);
return store.add(this._instantiationService.createInstance((
readHotReloadableExport(MultiDiffEditorWidgetImpl, reader)),
this._element,
this._dimension,
this._model,
this._viewModel,
this._workbenchUIElementFactory,
));
});
Expand All @@ -36,11 +36,15 @@ export class MultiDiffEditorWidget extends Disposable {
) {
super();

this._register(recomputeInitiallyAndOnChange(this.widgetImpl));
this._register(recomputeInitiallyAndOnChange(this._widgetImpl));
}

public setModel(model: IMultiDiffEditorModel | undefined): void {
this._model.set(model, undefined);
public createViewModel(model: IMultiDiffEditorModel): MultiDiffEditorViewModel {
return this._widgetImpl.get().createViewModel(model);
}

public setViewModel(viewModel: MultiDiffEditorViewModel | undefined): void {
this._viewModel.set(viewModel, undefined);
}

public layout(dimension: Dimension): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
]);

private readonly _sizeObserver = this._register(new ObservableElementSizeObserver(this._element, undefined));
private readonly _documentsObs = this._model.map(this, m => !m ? constObservable([]) : observableFromEvent(m.onDidChange, /** @description Documents changed */() => m.documents));
private readonly _documents = this._documentsObs.map(this, (m, reader) => m.read(reader));

private readonly _objectPool = this._register(new ObjectPool<TemplateData, DiffEditorItemTemplate>((data) => {
const template = this._instantiationService.createInstance(
Expand Down Expand Up @@ -75,16 +73,23 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
private readonly _scrollTop = observableFromEvent(this._scrollableElement.onScroll, () => /** @description scrollTop */ this._scrollableElement.getScrollPosition().scrollTop);
private readonly _scrollLeft = observableFromEvent(this._scrollableElement.onScroll, () => /** @description scrollLeft */ this._scrollableElement.getScrollPosition().scrollLeft);

private readonly _viewItems = derivedWithStore<DiffEditorItem[]>(this,
(reader, store) => this._documents.read(reader).map(d => store.add(new DiffEditorItem(this._objectPool, d, this._editor, this._scrollLeft)))
private readonly _viewItems = derivedWithStore<readonly VirtualizedViewItem[]>(this,
(reader, store) => {
const vm = this._viewModel.read(reader);
if (!vm) {
return [];
}
const items = vm.items.read(reader);
return items.map(d => store.add(new VirtualizedViewItem(d, this._objectPool, this._scrollLeft)));
}
);

private readonly _totalHeight = this._viewItems.map(this, (items, reader) => items.reduce((r, i) => r + i.contentHeight.read(reader), 0));

constructor(
private readonly _element: HTMLElement,
private readonly _dimension: IObservable<Dimension | undefined>,
private readonly _model: IObservable<IMultiDiffEditorModel | undefined>,
private readonly _viewModel: IObservable<MultiDiffEditorViewModel | undefined>,
private readonly _workbenchUIElementFactory: IWorkbenchUIElementFactory,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
Expand Down Expand Up @@ -134,6 +139,10 @@ 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 Expand Up @@ -170,13 +179,44 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
}
}

class DiffEditorItem extends Disposable {
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)))
).recomputeInitiallyAndOnChange(this._store);

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

class DocumentDiffItemViewModel extends Disposable {
public readonly diffEditorViewModel: IDiffEditorViewModel;

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

this.diffEditorViewModel = this._register(diffEditorViewModelFactory.createViewModel({
original: entry.value!.original!,
modified: entry.value!.modified!,
}));
}
}

class VirtualizedViewItem extends Disposable {
// TODO this should be in the view model
private readonly _lastTemplateData = observableValue<{ contentHeight: number; maxScroll: { maxScroll: number; width: number } }>(
this,
{ contentHeight: 500, maxScroll: { maxScroll: 0, width: 0 }, }
);
private readonly _templateRef = this._register(disposableObservableValue<IReference<DiffEditorItemTemplate> | undefined>(this, undefined));
private _vm: IDiffEditorViewModel | undefined;

public readonly contentHeight = derived(this, reader =>
this._templateRef.read(reader)?.object.height?.read(reader) ?? this._lastTemplateData.read(reader).contentHeight
Expand All @@ -185,18 +225,12 @@ class DiffEditorItem extends Disposable {
public readonly maxScroll = derived(this, reader => this._templateRef.read(reader)?.object.maxScroll.read(reader) ?? this._lastTemplateData.read(reader).maxScroll);

constructor(
private readonly _viewModel: DocumentDiffItemViewModel,
private readonly _objectPool: ObjectPool<TemplateData, DiffEditorItemTemplate>,
private readonly _entry: LazyPromise<IDocumentDiffItem>,
baseDiffEditorWidget: DiffEditorWidget,
private readonly _scrollLeft: IObservable<number>,
) {
super();

this._vm = this._register(baseDiffEditorWidget.createViewModel({
original: _entry.value!.original!,
modified: _entry.value!.modified!,
}));

this._register(autorun((reader) => {
const scrollLeft = this._scrollLeft.read(reader);
this._templateRef.read(reader)?.object.setScrollLeft(scrollLeft);
Expand All @@ -209,7 +243,7 @@ class DiffEditorItem extends Disposable {
}

public override toString(): string {
return `ViewItem(${this._entry.value!.title})`;
return `VirtualViewItem(${this._viewModel.entry.value!.title})`;
}

public hide(): void {
Expand All @@ -229,7 +263,7 @@ class DiffEditorItem extends Disposable {
public render(verticalSpace: OffsetRange, offset: number, width: number, viewPort: OffsetRange): void {
let ref = this._templateRef.get();
if (!ref) {
ref = this._objectPool.getUnusedObj(new TemplateData(this._vm!, this._entry.value!));
ref = this._objectPool.getUnusedObj(new TemplateData(this._viewModel.diffEditorViewModel, this._viewModel.entry.value!));
this._templateRef.set(ref, undefined);
}
ref.object.render(verticalSpace, width, offset, viewPort);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@ 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);
const vm = await input.getViewModel();
this._multiDiffEditorWidget?.setModel(vm);
if (!input.viewModel) {
const vm = await input.getModel();
input.viewModel = this._multiDiffEditorWidget!.createViewModel(vm);
}
this._multiDiffEditorWidget!.setViewModel(input.viewModel);
}

override async clearInput(): Promise<void> {
await super.clearInput();
this._multiDiffEditorWidget?.setModel(undefined);
this._multiDiffEditorWidget?.setViewModel(undefined);
}

layout(dimension: DOM.Dimension): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { deepClone } from 'vs/base/common/objects';
import { isObject } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ConstLazyPromise, IDocumentDiffItem, IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl';
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
Expand Down Expand Up @@ -39,7 +40,10 @@ export class MultiDiffEditorInput extends EditorInput {
return DEFAULT_EDITOR_ASSOCIATION.id;
}

private _viewModel: IMultiDiffEditorModel | undefined;
private _model: IMultiDiffEditorModel | undefined;

// TODO dont make this public
public viewModel: MultiDiffEditorViewModel | undefined;

constructor(
readonly label: string | undefined,
Expand All @@ -50,11 +54,12 @@ export class MultiDiffEditorInput extends EditorInput {
super();
}

async getViewModel(): Promise<IMultiDiffEditorModel> {
if (!this._viewModel) {
this._viewModel = await this._createViewModel();
// TODO this should return the view model
async getModel(): Promise<IMultiDiffEditorModel> {
if (!this._model) {
this._model = await this._createViewModel();
}
return this._viewModel;
return this._model;
}

private async _createViewModel(): Promise<IMultiDiffEditorModel> {
Expand Down

0 comments on commit 917b056

Please sign in to comment.