Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to move editors with workbench.editor.moveToActiveGroupIfOpen #205442

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/vs/workbench/browser/parts/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,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 }; },
Expand Down Expand Up @@ -130,6 +131,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']),
'mouseBackForwardToNavigate': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['mouseBackForwardToNavigate']),
'restoreViewState': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['restoreViewState']),
'splitOnDragAndDrop': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['splitOnDragAndDrop']),
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/browser/workbench.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ const registry = Registry.as<IConfigurationRegistry>(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.mouseBackForwardToNavigate': {
'type': 'boolean',
'description': localize('mouseBackForwardToNavigate', "Enables the use of mouse buttons four and five for commands 'Go Back' and 'Go Forward'."),
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/common/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,7 @@ interface IEditorPartConfiguration {
closeEmptyGroups?: boolean;
autoLockGroups?: Set<string>;
revealIfOpen?: boolean;
moveToActiveGroupIfOpen?: boolean;
mouseBackForwardToNavigate?: boolean;
labelFormat?: 'default' | 'short' | 'medium' | 'long';
restoreViewState?: boolean;
Expand Down
54 changes: 36 additions & 18 deletions src/vs/workbench/services/editor/browser/editorResolverService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,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<boolean>('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 {
Expand All @@ -529,30 +533,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<EditorInput | undefined> {
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) {
Expand Down
5 changes: 4 additions & 1 deletion src/vs/workbench/services/editor/common/editorGroupFinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,15 @@ function doFindGroup(input: EditorInputWithOptions | IUntypedEditorInput, prefer
}
}

// moveToActiveGroupIfOpen disables revealIfOpen here and performs a superset of its behavior in editorResolverService
const revealIfOpen = !configurationService.getValue<boolean>('workbench.editor.moveToActiveGroupIfOpen') && configurationService.getValue<boolean>('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<boolean>('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;

Expand Down