From fd2db103459c82eb77d36bb968d3b98137cda5b4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 Feb 2024 09:06:16 +0100 Subject: [PATCH] "workbench.action.compareEditor.swapSides" may create Untitled files unnecessarily (fix #204560) (#204565) --- .../browser/parts/editor/editorCommands.ts | 4 ++-- .../common/editor/diffEditorInput.ts | 6 ++--- src/vs/workbench/common/editor/editorInput.ts | 24 ++++++++++++++----- .../common/editor/sideBySideEditorInput.ts | 4 ++-- .../files/browser/editors/fileEditorInput.ts | 4 ++-- .../common/untitledTextEditorInput.ts | 8 +++---- .../test/browser/untitledTextEditor.test.ts | 4 ++++ 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 895db330ab8..68cd3a6b328 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -475,7 +475,7 @@ function registerDiffEditorCommands(): void { return; } - const untypedDiffInput = diffInput.toUntyped({ preserveViewState: activeGroup.id }); + const untypedDiffInput = diffInput.toUntyped({ preserveViewState: activeGroup.id, preserveResource: true }); if (!untypedDiffInput) { return; } @@ -484,7 +484,7 @@ function registerDiffEditorCommands(): void { // sure to first open the modified side if it is not // yet opened. This ensures that the swapping is not // bringing up a confirmation dialog to save. - if (diffInput.modified.isModified() && !editorService.isOpened({ resource: diffInput.modified.resource, typeId: diffInput.modified.typeId, editorId: diffInput.modified.editorId })) { + if (diffInput.modified.isModified() && editorService.findEditors({ resource: diffInput.modified.resource, typeId: diffInput.modified.typeId, editorId: diffInput.modified.editorId }).length === 0) { await editorService.openEditor({ ...untypedDiffInput.modified, options: { diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 1680cdd3d8c..1d6e6118751 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -5,9 +5,9 @@ import { localize } from 'vs/nls'; import { AbstractSideBySideEditorInputSerializer, SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { EditorInput, IUntypedEditorOptions } from 'vs/workbench/common/editor/editorInput'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity, IEditorDescriptor, IEditorPane, GroupIdentifier, IResourceDiffEditorInput, IUntypedEditorInput, isResourceDiffEditorInput, IDiffEditorInput, IResourceSideBySideEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; +import { TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity, IEditorDescriptor, IEditorPane, IResourceDiffEditorInput, IUntypedEditorInput, isResourceDiffEditorInput, IDiffEditorInput, IResourceSideBySideEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; @@ -210,7 +210,7 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito return new DiffEditorModel(isResolvedEditorModel(originalEditorModel) ? originalEditorModel : undefined, isResolvedEditorModel(modifiedEditorModel) ? modifiedEditorModel : undefined); } - override toUntyped(options?: { preserveViewState: GroupIdentifier }): (IResourceDiffEditorInput & IResourceSideBySideEditorInput) | undefined { + override toUntyped(options?: IUntypedEditorOptions): (IResourceDiffEditorInput & IResourceSideBySideEditorInput) | undefined { const untyped = super.toUntyped(options); if (untyped) { return { diff --git a/src/vs/workbench/common/editor/editorInput.ts b/src/vs/workbench/common/editor/editorInput.ts index 3aa427413f9..9bce607e387 100644 --- a/src/vs/workbench/common/editor/editorInput.ts +++ b/src/vs/workbench/common/editor/editorInput.ts @@ -39,6 +39,23 @@ export interface IEditorCloseHandler { confirm(editors: ReadonlyArray): Promise; } +export interface IUntypedEditorOptions { + + /** + * Implementations should try to preserve as much + * view state as possible from the typed input based + * on the group the editor is opened. + */ + readonly preserveViewState?: GroupIdentifier; + + /** + * Implementations should preserve the original + * resource of the typed input and not alter + * it. + */ + readonly preserveResource?: boolean; +} + /** * Editor inputs are lightweight objects that can be passed to the workbench API to open inside the editor part. * Each editor input is mapped to an editor that is capable of opening it through the Platform facade. @@ -310,13 +327,8 @@ export abstract class EditorInput extends AbstractEditorInput { * editor input into a form that it can be restored. * * May return `undefined` if an untyped representation is not supported. - * - * @param options additional configuration for the expected return type. - * When `preserveViewState` is provided, implementations should try to - * preserve as much view state as possible from the typed input based on - * the group the editor is opened. */ - toUntyped(options?: { preserveViewState: GroupIdentifier }): IUntypedEditorInput | undefined { + toUntyped(options?: IUntypedEditorOptions): IUntypedEditorInput | undefined { return undefined; } diff --git a/src/vs/workbench/common/editor/sideBySideEditorInput.ts b/src/vs/workbench/common/editor/sideBySideEditorInput.ts index eab54171e89..88585cb807c 100644 --- a/src/vs/workbench/common/editor/sideBySideEditorInput.ts +++ b/src/vs/workbench/common/editor/sideBySideEditorInput.ts @@ -10,7 +10,7 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorInputCapabilities, GroupIdentifier, ISaveOptions, IRevertOptions, EditorExtensions, IEditorFactoryRegistry, IEditorSerializer, ISideBySideEditorInput, IUntypedEditorInput, isResourceSideBySideEditorInput, isDiffEditorInput, isResourceDiffEditorInput, IResourceSideBySideEditorInput, findViewStateForEditor, IMoveResult, isEditorInput, isResourceEditorInput, Verbosity, isResourceMergeEditorInput, isResourceDiffListEditorInput } from 'vs/workbench/common/editor'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { EditorInput, IUntypedEditorOptions } from 'vs/workbench/common/editor/editorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; /** @@ -271,7 +271,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi return this.primary.isReadonly(); } - override toUntyped(options?: { preserveViewState: GroupIdentifier }): IResourceSideBySideEditorInput | undefined { + override toUntyped(options?: IUntypedEditorOptions): IResourceSideBySideEditorInput | undefined { const primaryResourceEditorInput = this.primary.toUntyped(options); const secondaryResourceEditorInput = this.secondary.toUntyped(options); diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts index 51684e02b56..506bf005000 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { IFileEditorInput, Verbosity, GroupIdentifier, IMoveResult, EditorInputCapabilities, IEditorDescriptor, IEditorPane, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, IUntypedFileEditorInput, findViewStateForEditor, isResourceEditorInput, IFileEditorInputOptions } from 'vs/workbench/common/editor'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { EditorInput, IUntypedEditorOptions } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; @@ -418,7 +418,7 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements }; } - override toUntyped(options?: { preserveViewState: GroupIdentifier }): ITextResourceEditorInput { + override toUntyped(options?: IUntypedEditorOptions): ITextResourceEditorInput { const untypedInput: IUntypedFileEditorInput = { resource: this.preferredResource, forceFile: true, diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts index 1d67b04088d..369a6994ed5 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { DEFAULT_EDITOR_ASSOCIATION, findViewStateForEditor, GroupIdentifier, isUntitledResourceEditorInput, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { DEFAULT_EDITOR_ASSOCIATION, findViewStateForEditor, isUntitledResourceEditorInput, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor'; +import { EditorInput, IUntypedEditorOptions } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { EncodingMode, IEncodingSupport, ILanguageSupport, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -165,7 +165,7 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp return this.model; } - override toUntyped(options?: { preserveViewState: GroupIdentifier }): IUntitledTextResourceEditorInput { + override toUntyped(options?: IUntypedEditorOptions): IUntitledTextResourceEditorInput { const untypedInput: IUntitledTextResourceEditorInput & { resource: URI | undefined; options: ITextEditorOptions } = { resource: this.model.hasAssociatedFilePath ? toLocalResource(this.model.resource, this.environmentService.remoteAuthority, this.pathService.defaultUriScheme) : this.resource, forceUntitled: true, @@ -180,7 +180,7 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp untypedInput.contents = this.model.isModified() ? this.model.textEditorModel?.getValue() : undefined; untypedInput.options.viewState = findViewStateForEditor(this, options.preserveViewState, this.editorService); - if (typeof untypedInput.contents === 'string' && !this.model.hasAssociatedFilePath) { + if (typeof untypedInput.contents === 'string' && !this.model.hasAssociatedFilePath && !options.preserveResource) { // Given how generic untitled resources in the system are, we // need to be careful not to set our resource into the untyped // editor if we want to transport contents too, because of diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 133ee3f3298..9ccf8fa049a 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -111,6 +111,10 @@ suite('Untitled text editors', () => { assert.strictEqual(dirtyUntypedInput.contents, 'foo bar'); assert.strictEqual(dirtyUntypedInput.resource, undefined); + const dirtyUntypedInputWithResource = input2.toUntyped({ preserveViewState: 0, preserveResource: true }); + assert.strictEqual(dirtyUntypedInputWithResource.contents, 'foo bar'); + assert.strictEqual(dirtyUntypedInputWithResource?.resource?.toString(), input2.resource.toString()); + const dirtyUntypedInputWithoutContent = input2.toUntyped(); assert.strictEqual(dirtyUntypedInputWithoutContent.resource?.toString(), input2.resource.toString()); assert.strictEqual(dirtyUntypedInputWithoutContent.contents, undefined);