diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 6ad27655a91d7..ab50bb17a5a2b 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -69,6 +69,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { restoreViewState: true, splitInGroupLayout: 'horizontal', revealIfOpen: false, + moveToActiveGroupIfOpen: false, // Properties that are Objects have to be defined as getters // to ensure no consumer modifies the default values get limit(): IEditorPartLimitOptions { return { enabled: false, value: 10, perEditorGroup: false, excludeDirty: false }; }, @@ -138,6 +139,7 @@ function validateEditorPartOptions(options: IEditorPartOptions): IEditorPartOpti 'closeOnFileDelete': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['closeOnFileDelete']), 'closeEmptyGroups': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['closeEmptyGroups']), 'revealIfOpen': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['revealIfOpen']), + 'moveToActiveGroupIfOpen': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['moveToActiveGroupIfOpen']), 'swipeToNavigate': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['swipeToNavigate']), 'mouseBackForwardToNavigate': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['mouseBackForwardToNavigate']), 'restoreViewState': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['restoreViewState']), diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 4a2a5883a5510..9afaae04e978d 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -349,6 +349,11 @@ const registry = Registry.as(ConfigurationExtensions.Con 'description': localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, such as when forcing an editor to open in a specific group or to the side of the currently active group."), 'default': false }, + 'workbench.editor.moveToActiveGroupIfOpen': { + 'type': 'boolean', + 'markdownDescription': localize('moveToActiveGroupIfOpen', "Moves already opened editors to the active editor group when they are revealed. Note that this setting is ignored when the currently active group is locked."), + 'default': false + }, 'workbench.editor.swipeToNavigate': { 'type': 'boolean', 'description': localize('swipeToNavigate', "Navigate between open files using three-finger swipe horizontally. Note that System Preferences > Trackpad > More Gestures > 'Swipe between pages' must be set to 'Swipe with two or three fingers'."), diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index eded82c117156..87d3e55007e5a 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1254,6 +1254,7 @@ interface IEditorPartConfiguration { closeEmptyGroups?: boolean; autoLockGroups?: Set; revealIfOpen?: boolean; + moveToActiveGroupIfOpen?: boolean; swipeToNavigate?: boolean; mouseBackForwardToNavigate?: boolean; labelFormat?: 'default' | 'short' | 'medium' | 'long'; diff --git a/src/vs/workbench/services/editor/browser/editorResolverService.ts b/src/vs/workbench/services/editor/browser/editorResolverService.ts index 521ed3ede8b46..f8c4ff5d5b2b0 100644 --- a/src/vs/workbench/services/editor/browser/editorResolverService.ts +++ b/src/vs/workbench/services/editor/browser/editorResolverService.ts @@ -503,12 +503,16 @@ export class EditorResolverService extends Disposable implements IEditorResolver throw new Error(`Undefined resource on non untitled editor input.`); } + const singleEditorPerResource = typeof selectedEditor.options?.singlePerResource === 'function' ? selectedEditor.options.singlePerResource() : selectedEditor.options?.singlePerResource ?? false; + const moveToActiveGroupIfOpen = this.configurationService.getValue('workbench.editor.moveToActiveGroupIfOpen'); + // If the editor states it can only be opened once per resource we must close all existing ones except one and move the new one into the group - const singleEditorPerResource = typeof selectedEditor.options?.singlePerResource === 'function' ? selectedEditor.options.singlePerResource() : selectedEditor.options?.singlePerResource; - if (singleEditorPerResource) { + // We also move the active editor if moveToActiveGroupIfOpen is enabled, although in this case we don't close other editors + if (singleEditorPerResource || (moveToActiveGroupIfOpen && !group.isLocked)) { const existingEditors = this.findExistingEditorsForResource(resource, selectedEditor.editorInfo.id); + const shouldCloseOtherEditors = singleEditorPerResource; if (existingEditors.length) { - const editor = await this.moveExistingEditorForResource(existingEditors, group); + const editor = await this.moveExistingEditorForResource(existingEditors, group, shouldCloseOtherEditors); if (editor) { return { editor, options }; } else { @@ -531,30 +535,44 @@ export class EditorResolverService extends Disposable implements IEditorResolver } /** - * Moves the first existing editor for a resource to the target group unless already opened there. - * Additionally will close any other editors that are open for that resource and viewtype besides the first one found - * @param resource The resource of the editor - * @param viewType the viewtype of the editor - * @param targetGroup The group to move it to - * @returns The moved editor input or `undefined` if the editor could not be moved + * Moves the first existing editor for a resource to the target group unless one is already opened there. + * Optionally will close any other editors that are open for that resource and viewtype + * @param existingEditorsForResource All the editors for a resource (must be non-empty) + * @param targetGroup The group to move it the first editor to + * @param shouldCloseOtherEditors If `true`, attempt to close all other editors + * @returns The moved or already opened editor input or `undefined` if the editor could not be moved */ private async moveExistingEditorForResource( existingEditorsForResource: Array<{ editor: EditorInput; group: IEditorGroup }>, targetGroup: IEditorGroup, + shouldCloseOtherEditors: boolean, ): Promise { - const editorToUse = existingEditorsForResource[0]; - - // We should only have one editor but if there are multiple we close the others - for (const { editor, group } of existingEditorsForResource) { - if (editor !== editorToUse.editor) { - const closed = await group.closeEditor(editor); - if (!closed) { - return; + let editorToUse: { editor: EditorInput; group: IEditorGroup } | undefined = undefined; + + // Prefer an editor that is already in the target group + for (const editor of existingEditorsForResource) { + if (targetGroup.id === editor.group.id) { + editorToUse = editor; + break; + } + } + + if (editorToUse === undefined) { + editorToUse = existingEditorsForResource[0]; + } + + if (shouldCloseOtherEditors) { + for (const { editor, group } of existingEditorsForResource) { + if (editor !== editorToUse.editor) { + const closed = await group.closeEditor(editor); + if (!closed) { + return; + } } } } - // Move the editor already opened to the target group + // Move the already opened editor to the target group if it is outside it if (targetGroup.id !== editorToUse.group.id) { const moved = editorToUse.group.moveEditor(editorToUse.editor, targetGroup); if (!moved) { diff --git a/src/vs/workbench/services/editor/common/editorGroupFinder.ts b/src/vs/workbench/services/editor/common/editorGroupFinder.ts index afcc959e8b162..9efebcf402e1d 100644 --- a/src/vs/workbench/services/editor/common/editorGroupFinder.ts +++ b/src/vs/workbench/services/editor/common/editorGroupFinder.ts @@ -113,12 +113,15 @@ function doFindGroup(input: EditorInputWithOptions | IUntypedEditorInput, prefer } } + // moveToActiveGroupIfOpen disables revealIfOpen here and performs a superset of its behavior in editorResolverService + const revealIfOpen = !configurationService.getValue('workbench.editor.moveToActiveGroupIfOpen') && configurationService.getValue('workbench.editor.revealIfOpen'); + // Respect option to reveal an editor if it is open (not necessarily visible) // Still prefer to reveal an editor in a group where the editor is active though. // We also try to reveal an editor if it has the `Singleton` capability which // indicates that the same editor cannot be opened across groups. if (!group) { - if (options?.revealIfOpened || configurationService.getValue('workbench.editor.revealIfOpen') || (isEditorInput(editor) && editor.hasCapability(EditorInputCapabilities.Singleton))) { + if (options?.revealIfOpened || revealIfOpen || (isEditorInput(editor) && editor.hasCapability(EditorInputCapabilities.Singleton))) { let groupWithInputActive: IEditorGroup | undefined = undefined; let groupWithInputOpened: IEditorGroup | undefined = undefined;