Skip to content

Commit

Permalink
Fixes #198432
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Nov 17, 2023
1 parent c2e58d5 commit 2f0ec17
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { h } from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { Codicon } from 'vs/base/common/codicons';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { autorun, derived } from 'vs/base/common/observable';
import { globalTransaction, observableValue } from 'vs/base/common/observableInternal/base';
import { autorun, derived, observableFromEvent } from 'vs/base/common/observable';
import { IObservable, globalTransaction, observableValue } from 'vs/base/common/observableInternal/base';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
import { IDocumentDiffItem } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory';
Expand All @@ -16,6 +16,7 @@ import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IObjectData, IPooledObject } from './objectPool';
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';

export class TemplateData implements IObjectData {
constructor(
Expand Down Expand Up @@ -85,10 +86,14 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject<
])
]);

private readonly _editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._elements.editor, {
public readonly editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._elements.editor, {
overflowWidgetsDomNode: this._overflowWidgetsDomNode,
}, {}));

private readonly isModifedFocused = isFocused(this.editor.getModifiedEditor());
private readonly isOriginalFocused = isFocused(this.editor.getOriginalEditor());
public readonly isFocused = derived(this, reader => this.isModifedFocused.read(reader) || this.isOriginalFocused.read(reader));

private readonly _resourceLabel = this._workbenchUIElementFactory.createResourceLabel
? this._register(this._workbenchUIElementFactory.createResourceLabel(this._elements.title))
: undefined;
Expand All @@ -114,21 +119,21 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject<
this._elements.editor.style.display = this._collapsed.get() ? 'none' : 'block';
}));

this._editor.getModifiedEditor().onDidLayoutChange(e => {
const width = this._editor.getModifiedEditor().getLayoutInfo().contentWidth;
this.editor.getModifiedEditor().onDidLayoutChange(e => {
const width = this.editor.getModifiedEditor().getLayoutInfo().contentWidth;
this._modifiedWidth.set(width, undefined);
});

this._editor.getOriginalEditor().onDidLayoutChange(e => {
const width = this._editor.getOriginalEditor().getLayoutInfo().contentWidth;
this.editor.getOriginalEditor().onDidLayoutChange(e => {
const width = this.editor.getOriginalEditor().getLayoutInfo().contentWidth;
this._originalWidth.set(width, undefined);
});

this._register(this._editor.onDidContentSizeChange(e => {
this._register(this.editor.onDidContentSizeChange(e => {
globalTransaction(tx => {
this._contentHeight.set(e.contentHeight, tx);
this._modifiedContentWidth.set(this._editor.getModifiedEditor().getContentWidth(), tx);
this._originalContentWidth.set(this._editor.getOriginalEditor().getContentWidth(), tx);
this._modifiedContentWidth.set(this.editor.getModifiedEditor().getContentWidth(), tx);
this._originalContentWidth.set(this.editor.getOriginalEditor().getContentWidth(), tx);
});
}));

Expand All @@ -138,9 +143,9 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject<

public setScrollLeft(left: number): void {
if (this._modifiedContentWidth.get() - this._modifiedWidth.get() > this._originalContentWidth.get() - this._originalWidth.get()) {
this._editor.getModifiedEditor().setScrollLeft(left);
this.editor.getModifiedEditor().setScrollLeft(left);
} else {
this._editor.getOriginalEditor().setScrollLeft(left);
this.editor.getOriginalEditor().setScrollLeft(left);
}
}

Expand Down Expand Up @@ -169,12 +174,12 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject<

if (data.entry.onOptionsDidChange) {
this._dataStore.add(data.entry.onOptionsDidChange(() => {
this._editor.updateOptions(updateOptions(data.entry.options ?? {}));
this.editor.updateOptions(updateOptions(data.entry.options ?? {}));
}));
}
globalTransaction(tx => {
this._editor.setModel(data.viewModel, tx);
this._editor.updateOptions(updateOptions(data.entry.options ?? {}));
this.editor.setModel(data.viewModel, tx);
this.editor.updateOptions(updateOptions(data.entry.options ?? {}));
});
}

Expand All @@ -189,16 +194,28 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject<
this._elements.header.style.transform = `translateY(${Math.max(0, Math.min(verticalRange.length - this._elements.header.clientHeight, viewPort.start - verticalRange.start))}px)`;

globalTransaction(tx => {
this._editor.layout({
this.editor.layout({
width: width,
height: verticalRange.length - this._outerEditorHeight,
});
});
this._editor.getOriginalEditor().setScrollTop(editorScroll);
this.editor.getOriginalEditor().setScrollTop(editorScroll);
}

public hide(): void {
this._elements.root.style.top = `-100000px`;
this._elements.root.style.visibility = 'hidden'; // Some editor parts are still visible
}
}

function isFocused(editor: ICodeEditor): IObservable<boolean> {
return observableFromEvent(
h => {
const store = new DisposableStore();
store.add(editor.onDidFocusEditorWidget(() => h(true)));
store.add(editor.onDidBlurEditorWidget(() => h(false)));
return store;
},
() => editor.hasWidgetFocus()
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

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 { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon';

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);

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

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

export 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!,
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@

import { Dimension } from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import { derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable';
import { derived, 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 { MultiDiffEditorViewModel, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl';
import { MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl';
import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel';
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';
import { Event } from 'vs/base/common/event';

export class MultiDiffEditorWidget extends Disposable {
private readonly _dimension = observableValue<Dimension | undefined>(this, undefined);
Expand Down Expand Up @@ -50,4 +52,12 @@ export class MultiDiffEditorWidget extends Disposable {
public layout(dimension: Dimension): void {
this._dimension.set(dimension, undefined);
}

private readonly _activeControl = derived(this, (reader) => this._widgetImpl.read(reader).activeControl.read(reader));

public getActiveControl(): any | undefined {
return this._activeControl.get();
}

public readonly onDidChangeActiveControl = Event.fromObservableLight(this._activeControl);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
import { Dimension, getWindow, h, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { Disposable, IReference, toDisposable } from 'vs/base/common/lifecycle';
import { IObservable, IReader, autorun, derived, derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable';
import { IObservable, IReader, autorun, derived, derivedObservableWithCache, derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable';
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 { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate';
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 @@ -85,6 +85,9 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
);

private readonly _totalHeight = this._viewItems.map(this, (items, reader) => items.reduce((r, i) => r + i.contentHeight.read(reader), 0));
public readonly activeDiffItem = derived(this, reader => this._viewItems.read(reader).find(i => i.template.read(reader)?.isFocused.read(reader)));
public readonly lastActiveDiffItem = derivedObservableWithCache<VirtualizedViewItem | undefined>((reader, lastValue) => this.activeDiffItem.read(reader) ?? lastValue);
public readonly activeControl = derived(this, reader => this.lastActiveDiffItem.read(reader)?.template.read(reader)?.editor);

constructor(
private readonly _element: HTMLElement,
Expand All @@ -95,6 +98,13 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
) {
super();

this._register(autorun((reader) => {
const lastActiveDiffItem = this.lastActiveDiffItem.read(reader);
transaction(tx => {
this._viewModel.read(reader)?.activeDiffItem.set(lastActiveDiffItem?.viewModel, tx);
});
}));

this._register(autorun((reader) => {
/** @description Update widget dimension */
const dimension = this._dimension.read(reader);
Expand Down Expand Up @@ -179,37 +189,6 @@ export class MultiDiffEditorWidgetImpl 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 } }>(
Expand All @@ -224,8 +203,10 @@ class VirtualizedViewItem extends Disposable {

public readonly maxScroll = derived(this, reader => this._templateRef.read(reader)?.object.maxScroll.read(reader) ?? this._lastTemplateData.read(reader).maxScroll);

public readonly template = derived(this, reader => this._templateRef.read(reader)?.object);

constructor(
private readonly _viewModel: DocumentDiffItemViewModel,
public readonly viewModel: DocumentDiffItemViewModel,
private readonly _objectPool: ObjectPool<TemplateData, DiffEditorItemTemplate>,
private readonly _scrollLeft: IObservable<number>,
) {
Expand All @@ -243,7 +224,7 @@ class VirtualizedViewItem extends Disposable {
}

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

public hide(): void {
Expand All @@ -263,7 +244,7 @@ class VirtualizedViewItem 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._viewModel.diffEditorViewModel, this._viewModel.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 @@ -15,6 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ICompositeControl } from 'vs/workbench/common/composite';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput';

Expand All @@ -38,6 +39,10 @@ export class MultiDiffEditor extends EditorPane {
parent,
this.instantiationService.createInstance(WorkbenchUIElementFactory),
));

this._register(this._multiDiffEditorWidget.onDidChangeActiveControl(() => {
this._onDidChangeControl.fire();
}));
}

override async setInput(input: MultiDiffEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
Expand All @@ -51,11 +56,15 @@ export class MultiDiffEditor extends EditorPane {

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

layout(dimension: DOM.Dimension): void {
this._multiDiffEditorWidget?.layout(dimension);
this._multiDiffEditorWidget!.layout(dimension);
}

override getControl(): ICompositeControl | undefined {
return this._multiDiffEditorWidget!.getActiveControl();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ 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 { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel';
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { localize } from 'vs/nls';
import { IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ILanguageSupport } from 'vs/workbench/services/textfile/common/textfiles';

export class MultiDiffEditorInput extends EditorInput {
export class MultiDiffEditorInput extends EditorInput implements ILanguageSupport {
static readonly ID: string = 'workbench.input.multiDiffEditor';

get resource(): URI | undefined {
Expand Down Expand Up @@ -54,6 +55,15 @@ export class MultiDiffEditorInput extends EditorInput {
super();
}

setLanguageId(languageId: string, source?: string | undefined): void {
const activeDiffItem = this.viewModel?.activeDiffItem.get();
const value = activeDiffItem?.entry?.value;
if (!value) { return; }
const target = value.modified ?? value.original;
if (!target) { return; }
target.setLanguage(languageId, source);
}

// TODO this should return the view model
async getModel(): Promise<IMultiDiffEditorModel> {
if (!this._model) {
Expand Down

0 comments on commit 2f0ec17

Please sign in to comment.