Fixes #219006. Open file now opens the related file on disk, instead of just the right side.

This also applies to staged files.
This commit is contained in:
Henning Dieterichs 2024-07-05 12:43:24 +02:00 committed by Henning Dieterichs
parent 701145d5e4
commit 84c007cbb2
11 changed files with 90 additions and 42 deletions

View file

@ -69,8 +69,9 @@ export class DocumentDiffItemViewModel extends Disposable {
{ contentHeight: 500, selections: undefined, }
);
public get originalUri(): URI | undefined { return this.entry.value!.original?.uri; }
public get modifiedUri(): URI | undefined { return this.entry.value!.modified?.uri; }
public get documentDiffItem(): IDocumentDiffItem { return this.entry.value!; }
public get originalUri(): URI | undefined { return this.documentDiffItem.original?.uri; }
public get modifiedUri(): URI | undefined { return this.documentDiffItem.modified?.uri; }
public readonly isActive: IObservable<boolean> = derived(this, reader => this._editorViewModel.activeDiffItem.read(reader) === this);

View file

@ -7,7 +7,7 @@ import { Dimension } from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable';
import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers';
import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditor/model';
import { IDocumentDiffItem, IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditor/model';
import { IMultiDiffEditorViewState, IMultiDiffResourceId, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl';
import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -81,6 +81,10 @@ export class MultiDiffEditorWidget extends Disposable {
public tryGetCodeEditor(resource: URI): { diffEditor: IDiffEditor; editor: ICodeEditor } | undefined {
return this._widgetImpl.get().tryGetCodeEditor(resource);
}
public findDocumentDiffItem(resource: URI): IDocumentDiffItem | undefined {
return this._widgetImpl.get().findDocumentDiffItem(resource);
}
}
export interface RevealOptions {

View file

@ -31,6 +31,7 @@ import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate';
import { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from './multiDiffEditorViewModel';
import { ObjectPool } from './objectPool';
import { localize } from 'vs/nls';
import { IDocumentDiffItem } from 'vs/editor/browser/widget/multiDiffEditor/model';
export class MultiDiffEditorWidgetImpl extends Disposable {
private readonly _scrollableElements = h('div.scrollContent', [
@ -263,6 +264,14 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
});
}
public findDocumentDiffItem(resource: URI): IDocumentDiffItem | undefined {
const item = this._viewItems.get().find(v =>
v.viewModel.diffEditorViewModel.model.modified.uri.toString() === resource.toString()
|| v.viewModel.diffEditorViewModel.model.original.uri.toString() === resource.toString()
);
return item?.viewModel.documentDiffItem;
}
public tryGetCodeEditor(resource: URI): { diffEditor: IDiffEditor; editor: ICodeEditor } | undefined {
const item = this._viewItems.get().find(v =>
v.viewModel.diffEditorViewModel.model.modified.uri.toString() === resource.toString()
@ -272,6 +281,7 @@ export class MultiDiffEditorWidgetImpl extends Disposable {
if (!editor) {
return undefined;
}
if (item.viewModel.diffEditorViewModel.model.modified.uri.toString() === resource.toString()) {
return { diffEditor: editor, editor: editor.getModifiedEditor() };
} else {

View file

@ -207,11 +207,11 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape {
if (editor instanceof MultiDiffEditorInput) {
const diffEditors: TextDiffInputDto[] = [];
for (const resource of (editor?.resources.get() ?? [])) {
if (resource.original && resource.modified) {
if (resource.originalUri && resource.modifiedUri) {
diffEditors.push({
kind: TabInputKind.TextDiffInput,
original: resource.original,
modified: resource.modified
original: resource.originalUri,
modified: resource.modifiedUri
});
}
}

View file

@ -547,7 +547,7 @@ export interface IResourceMultiDiffEditorInput extends IBaseUntypedEditorInput {
* The list of resources to compare.
* If not set, the resources are dynamically derived from the {@link multiDiffSource}.
*/
readonly resources?: IResourceDiffEditorInput[];
readonly resources?: IMultiDiffEditorResource[];
/**
* Whether the editor should be serialized and stored for subsequent sessions.
@ -555,6 +555,9 @@ export interface IResourceMultiDiffEditorInput extends IBaseUntypedEditorInput {
readonly isTransient?: boolean;
}
export interface IMultiDiffEditorResource extends IResourceDiffEditorInput {
readonly goToFileResource?: URI;
}
export type IResourceMergeEditorInputSide = (IResourceEditorInput | ITextResourceEditorInput) & { detail?: string };
/**

View file

@ -36,7 +36,7 @@ import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService';
import { ButtonBar } from 'vs/base/browser/ui/button/button';
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
import { Mutable } from 'vs/base/common/types';
import { IResourceDiffEditorInput } from 'vs/workbench/common/editor';
import { IMultiDiffEditorResource, IResourceDiffEditorInput, IResourceMultiDiffEditorInput } from 'vs/workbench/common/editor';
import { IMultiDiffEditorOptions, IMultiDiffResourceId } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl';
import { IRange } from 'vs/editor/common/core/range';
import { CachedFunction, LRUCachedFunction } from 'vs/base/common/cache';
@ -369,16 +369,20 @@ export class BulkEditPane extends ViewPane {
}, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
}
private readonly _computeResourceDiffEditorInputs = new LRUCachedFunction(async (fileOperations: BulkFileOperation[]) => {
const computeDiffEditorInput = new CachedFunction<BulkFileOperation, Promise<IResourceDiffEditorInput>>(async (fileOperation) => {
private readonly _computeResourceDiffEditorInputs = new LRUCachedFunction<
BulkFileOperation[],
Promise<{ resources: IMultiDiffEditorResource[]; getResourceDiffEditorInputIdOfOperation: (operation: BulkFileOperation) => Promise<IMultiDiffResourceId> }>
>(async (fileOperations) => {
const computeDiffEditorInput = new CachedFunction<BulkFileOperation, Promise<IMultiDiffEditorResource>>(async (fileOperation) => {
const fileOperationUri = fileOperation.uri;
const previewUri = this._currentProvider!.asPreviewUri(fileOperationUri);
// delete
if (fileOperation.type & BulkFileOperationType.Delete) {
return {
original: { resource: URI.revive(previewUri) },
modified: { resource: undefined }
};
modified: { resource: undefined },
goToFileResource: fileOperation.uri,
} satisfies IMultiDiffEditorResource;
}
// rename, create, edits
@ -392,8 +396,9 @@ export class BulkEditPane extends ViewPane {
}
return {
original: { resource: URI.revive(leftResource) },
modified: { resource: URI.revive(previewUri) }
};
modified: { resource: URI.revive(previewUri) },
goToFileResource: leftResource,
} satisfies IMultiDiffEditorResource;
}
});

View file

@ -10,9 +10,9 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { localize2 } from 'vs/nls';
import { Action2, MenuId } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ITextEditorOptions, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext';
import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor';
import { MultiDiffEditor } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor';
import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@ -38,21 +38,28 @@ export class GoToFileAction extends Action2 {
const editorService = accessor.get(IEditorService);
const activeEditorPane = editorService.activeEditorPane;
let selections: Selection[] | undefined = undefined;
if (activeEditorPane instanceof MultiDiffEditor) {
const editor = activeEditorPane.tryGetCodeEditor(uri);
if (editor) {
selections = editor.editor.getSelections() ?? undefined;
}
if (!(activeEditorPane instanceof MultiDiffEditor)) {
return;
}
const editor = await editorService.openEditor({ resource: uri });
if (selections && (editor instanceof TextFileEditor)) {
const c = editor.getControl();
if (c) {
c.setSelections(selections);
c.revealLineInCenter(selections[0].selectionStartLineNumber);
}
const editor = activeEditorPane.tryGetCodeEditor(uri);
if (editor) {
selections = editor.editor.getSelections() ?? undefined;
}
let targetUri = uri;
const item = activeEditorPane.findDocumentDiffItem(uri);
if (item && item.goToFileUri) {
targetUri = item.goToFileUri;
}
await editorService.openEditor({
resource: targetUri,
options: {
selection: selections?.[0],
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
} satisfies ITextEditorOptions,
});
}
}

View file

@ -18,7 +18,7 @@ import { AbstractEditorWithViewState } from 'vs/workbench/browser/parts/editor/e
import { ICompositeControl } from 'vs/workbench/common/composite';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput';
import { IDocumentDiffItemWithMultiDiffEditorItem, MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { URI } from 'vs/base/common/uri';
@ -27,6 +27,7 @@ import { IMultiDiffEditorOptions, IMultiDiffEditorViewState } from 'vs/editor/br
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IDiffEditor } from 'vs/editor/common/editorCommon';
import { Range } from 'vs/editor/common/core/range';
import { MultiDiffEditorItem } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService';
export class MultiDiffEditor extends AbstractEditorWithViewState<IMultiDiffEditorViewState> {
static readonly ID = 'multiDiffEditor';
@ -139,6 +140,13 @@ export class MultiDiffEditor extends AbstractEditorWithViewState<IMultiDiffEdito
public tryGetCodeEditor(resource: URI): { diffEditor: IDiffEditor; editor: ICodeEditor } | undefined {
return this._multiDiffEditorWidget!.tryGetCodeEditor(resource);
}
public findDocumentDiffItem(resource: URI): MultiDiffEditorItem | undefined {
const i = this._multiDiffEditorWidget!.findDocumentDiffItem(resource);
if (!i) { return undefined; }
const i2 = i as IDocumentDiffItemWithMultiDiffEditorItem;
return i2.multiDiffEditorItem;
}
}

View file

@ -46,6 +46,7 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
return new MultiDiffEditorItem(
resource.original.resource,
resource.modified.resource,
resource.goToFileResource,
);
}),
input.isTransient ?? false
@ -60,6 +61,7 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
data.resources?.map(resource => new MultiDiffEditorItem(
resource.originalUri ? URI.parse(resource.originalUri) : undefined,
resource.modifiedUri ? URI.parse(resource.modifiedUri) : undefined,
resource.goToFileUri ? URI.parse(resource.goToFileUri) : undefined,
)),
false
);
@ -112,8 +114,9 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
label: this.label,
multiDiffSourceUri: this.multiDiffSource.toString(),
resources: this.initialResources?.map(resource => ({
originalUri: resource.original?.toString(),
modifiedUri: resource.modified?.toString(),
originalUri: resource.originalUri?.toString(),
modifiedUri: resource.modifiedUri?.toString(),
goToFileUri: resource.goToFileUri?.toString(),
})),
};
}
@ -159,8 +162,8 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
try {
[original, modified] = await Promise.all([
r.original ? this._textModelService.createModelReference(r.original) : undefined,
r.modified ? this._textModelService.createModelReference(r.modified) : undefined,
r.originalUri ? this._textModelService.createModelReference(r.originalUri) : undefined,
r.modifiedUri ? this._textModelService.createModelReference(r.modifiedUri) : undefined,
]);
if (original) { store2.add(original); }
if (modified) { store2.add(modified); }
@ -171,8 +174,9 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
return undefined;
}
const uri = (r.modified ?? r.original)!;
return new ConstLazyPromise<IDocumentDiffItem>({
const uri = (r.modifiedUri ?? r.originalUri)!;
return new ConstLazyPromise<IDocumentDiffItemWithMultiDiffEditorItem>({
multiDiffEditorItem: r,
original: original?.object.textEditorModel,
modified: modified?.object.textEditorModel,
get options() {
@ -187,7 +191,7 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
}
}),
});
}, i => JSON.stringify([i.modified?.toString(), i.original?.toString()]));
}, i => JSON.stringify([i.modifiedUri?.toString(), i.originalUri?.toString()]));
const documents = observableValue<readonly LazyPromise<IDocumentDiffItem>[]>('documents', []);
@ -239,8 +243,8 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
public readonly resources = derived(this, reader => this._resolvedSource.cachedPromiseResult.read(reader)?.data?.resources.read(reader));
private readonly _isDirtyObservables = mapObservableArrayCached(this, this.resources.map(r => r ?? []), res => {
const isModifiedDirty = res.modified ? isUriDirty(this._textFileService, res.modified) : constObservable(false);
const isOriginalDirty = res.original ? isUriDirty(this._textFileService, res.original) : constObservable(false);
const isModifiedDirty = res.modifiedUri ? isUriDirty(this._textFileService, res.modifiedUri) : constObservable(false);
const isOriginalDirty = res.originalUri ? isUriDirty(this._textFileService, res.originalUri) : constObservable(false);
return derived(reader => /** @description modifiedDirty||originalDirty */ isModifiedDirty.read(reader) || isOriginalDirty.read(reader));
}, i => i.getKey());
private readonly _isDirtyObservable = derived(this, reader => this._isDirtyObservables.read(reader).some(isDirty => isDirty.read(reader)))
@ -291,6 +295,10 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
};
}
export interface IDocumentDiffItemWithMultiDiffEditorItem extends IDocumentDiffItem {
multiDiffEditorItem: MultiDiffEditorItem;
}
function isUriDirty(textFileService: ITextFileService, uri: URI) {
return observableFromEvent(
Event.filter(textFileService.files.onDidChangeDirty, e => e.resource.toString() === uri.toString()),
@ -361,6 +369,7 @@ interface ISerializedMultiDiffEditorInput {
resources: {
originalUri: string | undefined;
modifiedUri: string | undefined;
goToFileUri: string | undefined;
}[] | undefined;
}

View file

@ -33,16 +33,17 @@ export interface IResolvedMultiDiffSource {
export class MultiDiffEditorItem {
constructor(
readonly original: URI | undefined,
readonly modified: URI | undefined,
readonly originalUri: URI | undefined,
readonly modifiedUri: URI | undefined,
readonly goToFileUri: URI | undefined,
) {
if (!original && !modified) {
if (!originalUri && !modifiedUri) {
throw new BugIndicatingError('Invalid arguments');
}
}
getKey(): string {
return JSON.stringify([this.modified?.toString(), this.original?.toString()]);
return JSON.stringify([this.modifiedUri?.toString(), this.originalUri?.toString()]);
}
}

View file

@ -76,7 +76,7 @@ export class ScmMultiDiffSourceResolver implements IMultiDiffSourceResolver {
class ScmResolvedMultiDiffSource implements IResolvedMultiDiffSource {
private readonly _resources = observableFromEvent<MultiDiffEditorItem[]>(
this._group.onDidChangeResources,
() => /** @description resources */ this._group.resources.map(e => new MultiDiffEditorItem(e.multiDiffEditorOriginalUri, e.multiDiffEditorModifiedUri))
() => /** @description resources */ this._group.resources.map(e => new MultiDiffEditorItem(e.multiDiffEditorOriginalUri, e.multiDiffEditorModifiedUri, e.sourceUri))
);
readonly resources = new ValueWithChangeEventFromObservable(this._resources);