diff --git a/src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackService.ts b/src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackService.ts index 4462874619c..9d03437a572 100644 --- a/src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackService.ts +++ b/src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackService.ts @@ -14,7 +14,7 @@ import { IChatEditingService } from '../../../../workbench/contrib/chat/common/e import { IChatSessionFileChange, IChatSessionFileChange2, isIChatSessionFileChange2 } from '../../../../workbench/contrib/chat/common/chatSessionsService.js'; import { IAgentSessionsService } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js'; import { agentSessionContainsResource, editingEntriesContainResource } from '../../../../workbench/contrib/chat/browser/sessionResourceMatching.js'; -import { IEditorService, MODAL_GROUP } from '../../../../workbench/services/editor/common/editorService.js'; +import { IEditorService } from '../../../../workbench/services/editor/common/editorService.js'; import { IChatWidgetService } from '../../../../workbench/contrib/chat/browser/chat.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { ILogService } from '../../../../platform/log/common/log.js'; @@ -299,7 +299,7 @@ export class AgentFeedbackService extends Disposable implements IAgentFeedbackSe revealIfVisible: true, selection, } - }, MODAL_GROUP); + }); } else if (sessionChange?.originalUri) { await this._editorService.openEditor({ original: { resource: sessionChange.originalUri }, @@ -310,7 +310,7 @@ export class AgentFeedbackService extends Disposable implements IAgentFeedbackSe revealIfVisible: true, selection, } - }, MODAL_GROUP); + }); } else { await this._editorService.openEditor({ resource: sessionChange?.modifiedUri ?? resourceUri, @@ -320,7 +320,7 @@ export class AgentFeedbackService extends Disposable implements IAgentFeedbackSe revealIfVisible: true, selection, } - }, MODAL_GROUP); + }); } this.setNavigationAnchor(sessionResource, commentId); diff --git a/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationOverviewView.ts b/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationOverviewView.ts index 2bcd3717a81..b9fd774f52a 100644 --- a/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationOverviewView.ts +++ b/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationOverviewView.ts @@ -27,7 +27,7 @@ import { AICustomizationManagementEditor } from '../../../../workbench/contrib/c import { agentIcon, instructionsIcon, mcpServerIcon, pluginIcon, promptIcon, skillIcon } from '../../../../workbench/contrib/chat/browser/aiCustomization/aiCustomizationIcons.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IAICustomizationWorkspaceService } from '../../../../workbench/contrib/chat/common/aiCustomizationWorkspaceService.js'; -import { IEditorService, MODAL_GROUP } from '../../../../workbench/services/editor/common/editorService.js'; +import { IEditorService } from '../../../../workbench/services/editor/common/editorService.js'; import { IMcpService } from '../../../../workbench/contrib/mcp/common/mcpTypes.js'; import { IAgentPluginService } from '../../../../workbench/contrib/chat/common/plugins/agentPluginService.js'; @@ -213,7 +213,7 @@ export class AICustomizationOverviewView extends ViewPane { private async openSection(sectionId: AICustomizationManagementSection): Promise { const input = AICustomizationManagementEditorInput.getOrCreate(); - const editor = await this.editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + const editor = await this.editorService.openEditor(input, { pinned: true }); // Deep-link to the section if (editor instanceof AICustomizationManagementEditor) { diff --git a/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts b/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts index 4b92d3f59a8..ab51ad600a2 100644 --- a/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts +++ b/src/vs/sessions/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts @@ -37,7 +37,7 @@ import { AICustomizationManagementEditor } from '../../../../workbench/contrib/c import { IAsyncDataSource, ITreeNode, ITreeRenderer, ITreeContextMenuEvent } from '../../../../base/browser/ui/tree/tree.js'; import { FuzzyScore } from '../../../../base/common/filters.js'; import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; -import { IEditorService, MODAL_GROUP } from '../../../../workbench/services/editor/common/editorService.js'; +import { IEditorService } from '../../../../workbench/services/editor/common/editorService.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IAICustomizationWorkspaceService } from '../../../../workbench/contrib/chat/common/aiCustomizationWorkspaceService.js'; @@ -674,7 +674,7 @@ export class AICustomizationViewPane extends ViewPane { }); } else if (e.element && e.element.type === 'link') { const input = AICustomizationManagementEditorInput.getOrCreate(); - const editor = await this.editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + const editor = await this.editorService.openEditor(input, { pinned: true }); if (editor instanceof AICustomizationManagementEditor) { editor.selectSectionById(e.element.section); } diff --git a/src/vs/sessions/contrib/changes/browser/changesView.ts b/src/vs/sessions/contrib/changes/browser/changesView.ts index a19220e9e0d..91621567dff 100644 --- a/src/vs/sessions/contrib/changes/browser/changesView.ts +++ b/src/vs/sessions/contrib/changes/browser/changesView.ts @@ -54,7 +54,7 @@ import { IChatSessionFileChange, IChatSessionFileChange2, isIChatSessionFileChan import { chatEditingWidgetFileStateContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, ModifiedFileEntryState } from '../../../../workbench/contrib/chat/common/editing/chatEditingService.js'; import { createFileIconThemableTreeContainerScope } from '../../../../workbench/contrib/files/browser/views/explorerView.js'; import { IActivityService, NumberBadge } from '../../../../workbench/services/activity/common/activity.js'; -import { IEditorService, MODAL_GROUP, SIDE_GROUP } from '../../../../workbench/services/editor/common/editorService.js'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../../workbench/services/editor/common/editorService.js'; import { IExtensionService } from '../../../../workbench/services/extensions/common/extensions.js'; import { IWorkbenchLayoutService } from '../../../../workbench/services/layout/browser/layoutService.js'; import { IActiveSessionItem, ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js'; @@ -945,7 +945,7 @@ export class ChangesViewPane extends ViewPane { } }; - const group = sideBySide ? SIDE_GROUP : MODAL_GROUP; + const group = sideBySide ? SIDE_GROUP : ACTIVE_GROUP; if (isDeletion && originalUri) { this.editorService.openEditor({ diff --git a/src/vs/sessions/contrib/sessions/browser/customizationsToolbar.contribution.ts b/src/vs/sessions/contrib/sessions/browser/customizationsToolbar.contribution.ts index 07c6cf93903..d8c4b413b2c 100644 --- a/src/vs/sessions/contrib/sessions/browser/customizationsToolbar.contribution.ts +++ b/src/vs/sessions/contrib/sessions/browser/customizationsToolbar.contribution.ts @@ -31,7 +31,7 @@ import { ISessionsManagementService } from './sessionsManagementService.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { getSourceCounts, getSourceCountsTotal } from './customizationCounts.js'; -import { IEditorService, MODAL_GROUP } from '../../../../workbench/services/editor/common/editorService.js'; +import { IEditorService } from '../../../../workbench/services/editor/common/editorService.js'; import { IAICustomizationWorkspaceService } from '../../../../workbench/contrib/chat/common/aiCustomizationWorkspaceService.js'; import { IAgentPluginService } from '../../../../workbench/contrib/chat/common/plugins/agentPluginService.js'; @@ -248,7 +248,7 @@ export class CustomizationsToolbarContribution extends Disposable implements IWo async run(accessor: ServicesAccessor): Promise { const editorService = accessor.get(IEditorService); const input = AICustomizationManagementEditorInput.getOrCreate(); - const editor = await editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + const editor = await editorService.openEditor(input, { pinned: true }); if (editor instanceof AICustomizationManagementEditor) { editor.selectSectionById(config.section); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 31f83c70a11..9bb098c68f8 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -237,9 +237,14 @@ export class EditorPart extends Part implements IEditorPart, } readonly sideGroup: IEditorSideGroup = { - openEditor: (editor, options) => { - const [group] = this.scopedInstantiationService.invokeFunction(accessor => findGroup(accessor, { editor, options }, SIDE_GROUP)); - + openEditor: async (editor, options) => { + const findGroupResult = this.scopedInstantiationService.invokeFunction(accessor => findGroup(accessor, { editor, options }, SIDE_GROUP)); + let group; + if (findGroupResult instanceof Promise) { + ([group] = await findGroupResult); + } else { + ([group] = findGroupResult); + } return group.openEditor(editor, options); } }; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 058693c7cfd..c19e3074e3f 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -1056,16 +1056,3 @@ Registry.as(Extensions.ConfigurationMigration) return result; } }]); - -Registry.as(Extensions.ConfigurationMigration) - .registerConfigurationMigrations([{ - key: 'workbench.editor.useModal', migrateFn: (value: unknown) => { - const result: ConfigurationKeyValuePairs = []; - if (value === 'default') { - result.push(['workbench.editor.useModal', { value: 'some' }]); - } else if (value === 'on') { - result.push(['workbench.editor.useModal', { value: 'all' }]); - } - return result; - } - }]); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index e02204c16c4..65ef6b48d66 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -855,7 +855,13 @@ export const enum EditorInputCapabilities { * Signals that the editor should be revealed when being * opened if it is already opened in any editor group. */ - ForceReveal = 1 << 10 + ForceReveal = 1 << 10, + + /** + * Signals that the editor must be opened in a modal editor + * part, overriding the `workbench.editor.useModal` setting. + */ + RequiresModal = 1 << 11 } export type IUntypedEditorInput = IResourceEditorInput | ITextResourceEditorInput | IUntitledTextResourceEditorInput | IResourceDiffEditorInput | IResourceMultiDiffEditorInput | IResourceSideBySideEditorInput | IResourceMergeEditorInput; diff --git a/src/vs/workbench/contrib/chat/browser/agentPluginEditor/agentPluginEditorInput.ts b/src/vs/workbench/contrib/chat/browser/agentPluginEditor/agentPluginEditorInput.ts index d2acd9f94f7..adddd57b417 100644 --- a/src/vs/workbench/contrib/chat/browser/agentPluginEditor/agentPluginEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/agentPluginEditor/agentPluginEditorInput.ts @@ -31,7 +31,7 @@ export class AgentPluginEditorInput extends EditorInput { } override get capabilities(): EditorInputCapabilities { - return EditorInputCapabilities.Readonly | EditorInputCapabilities.Singleton; + return super.capabilities | EditorInputCapabilities.Singleton | EditorInputCapabilities.RequiresModal; } override get resource() { diff --git a/src/vs/workbench/contrib/chat/browser/agentPluginsView.ts b/src/vs/workbench/contrib/chat/browser/agentPluginsView.ts index 3629f397974..07192928483 100644 --- a/src/vs/workbench/contrib/chat/browser/agentPluginsView.ts +++ b/src/vs/workbench/contrib/chat/browser/agentPluginsView.ts @@ -36,7 +36,7 @@ import { getLocationBasedViewColors } from '../../../browser/parts/views/viewPan import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewDescriptorService, IViewsRegistry, Extensions as ViewExtensions } from '../../../common/views.js'; -import { IEditorService, MODAL_GROUP } from '../../../services/editor/common/editorService.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; import { VIEW_CONTAINER } from '../../extensions/browser/extensions.contribution.js'; import { manageExtensionIcon } from '../../extensions/browser/extensionsIcons.js'; import { AbstractExtensionsListView } from '../../extensions/browser/extensionsViews.js'; @@ -358,8 +358,7 @@ export class AgentPluginsListView extends AbstractExtensionsListView e.element !== null), (_, event) => event, 75, true)(options => { this.editorService.openEditor( this.instantiationService.createInstance(AgentPluginEditorInput, options.element!), - options.editorOptions, - MODAL_GROUP + options.editorOptions ); })); } diff --git a/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagement.contribution.ts b/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagement.contribution.ts index d9c682145d1..1d46e138e1a 100644 --- a/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagement.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagement.contribution.ts @@ -12,7 +12,7 @@ import { Registry } from '../../../../../platform/registry/common/platform.js'; import { IEditorPaneRegistry, EditorPaneDescriptor } from '../../../../browser/editor.js'; import { EditorExtensions, IEditorFactoryRegistry, IEditorSerializer } from '../../../../common/editor.js'; import { EditorInput } from '../../../../common/editor/editorInput.js'; -import { IEditorService, MODAL_GROUP } from '../../../../services/editor/common/editorService.js'; +import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { ChatContextKeys } from '../../common/actions/chatContextKeys.js'; import { CHAT_CATEGORY } from '../actions/chatActions.js'; import { AICustomizationManagementEditor } from './aiCustomizationManagementEditor.js'; @@ -384,7 +384,7 @@ class AICustomizationManagementActionsContribution extends Disposable implements async run(accessor: ServicesAccessor, section?: AICustomizationManagementSection): Promise { const editorService = accessor.get(IEditorService); const input = AICustomizationManagementEditorInput.getOrCreate(); - const pane = await editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + const pane = await editorService.openEditor(input, { pinned: true }); if (section && pane instanceof AICustomizationManagementEditor) { pane.selectSectionById(section); } diff --git a/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagementEditorInput.ts b/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagementEditorInput.ts index 819107dfceb..f317182e7c6 100644 --- a/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagementEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagementEditorInput.ts @@ -6,7 +6,7 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { localize } from '../../../../../nls.js'; -import { IUntypedEditorInput } from '../../../../common/editor.js'; +import { IUntypedEditorInput, EditorInputCapabilities } from '../../../../common/editor.js'; import { EditorInput } from '../../../../common/editor/editorInput.js'; import { AI_CUSTOMIZATION_MANAGEMENT_EDITOR_INPUT_ID } from './aiCustomizationManagement.js'; @@ -20,6 +20,10 @@ export class AICustomizationManagementEditorInput extends EditorInput { readonly resource = undefined; + override get capabilities(): EditorInputCapabilities { + return super.capabilities | EditorInputCapabilities.Singleton | EditorInputCapabilities.RequiresModal; + } + private static _instance: AICustomizationManagementEditorInput | undefined; /** diff --git a/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagement.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagement.contribution.ts index 4a779592644..ed535454fa6 100644 --- a/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagement.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagement.contribution.ts @@ -15,7 +15,7 @@ import { Registry } from '../../../../../platform/registry/common/platform.js'; import { IEditorPaneRegistry, EditorPaneDescriptor } from '../../../../browser/editor.js'; import { EditorExtensions, IEditorFactoryRegistry, IEditorSerializer } from '../../../../common/editor.js'; import { EditorInput } from '../../../../common/editor/editorInput.js'; -import { IEditorService, MODAL_GROUP } from '../../../../services/editor/common/editorService.js'; +import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { ResourceContextKey } from '../../../../common/contextkeys.js'; import { ChatContextKeys } from '../../common/actions/chatContextKeys.js'; import { CONTEXT_MODELS_EDITOR, CONTEXT_MODELS_SEARCH_FOCUS, MANAGE_CHAT_COMMAND_ID } from '../../common/constants.js'; @@ -142,7 +142,7 @@ class ChatManagementActionsContribution extends Disposable implements IWorkbench async run(accessor: ServicesAccessor, args: string | IOpenManageCopilotEditorActionOptions) { const editorService = accessor.get(IEditorService); args = sanitizeOpenManageCopilotEditorArgs(args); - return editorService.openEditor(new ModelsManagementEditorInput(), { pinned: true }, MODAL_GROUP); + return editorService.openEditor(new ModelsManagementEditorInput(), { pinned: true }); } })); diff --git a/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagementEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagementEditorInput.ts index 1ab25995221..dd1f06bcee5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagementEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatManagement/chatManagementEditorInput.ts @@ -7,7 +7,7 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import * as nls from '../../../../../nls.js'; import { registerIcon } from '../../../../../platform/theme/common/iconRegistry.js'; -import { IUntypedEditorInput } from '../../../../common/editor.js'; +import { EditorInputCapabilities, IUntypedEditorInput } from '../../../../common/editor.js'; import { EditorInput } from '../../../../common/editor/editorInput.js'; const ChatManagementEditorIcon = registerIcon('ai-management-editor-label-icon', Codicon.copilot, nls.localize('aiManagementEditorLabelIcon', 'Icon of the AI Management editor label.')); @@ -53,6 +53,10 @@ export class ModelsManagementEditorInput extends EditorInput { readonly resource = undefined; + override get capabilities(): EditorInputCapabilities { + return super.capabilities | EditorInputCapabilities.Singleton | EditorInputCapabilities.RequiresModal; + } + constructor() { super(); } diff --git a/src/vs/workbench/contrib/imageCarousel/browser/imageCarousel.contribution.ts b/src/vs/workbench/contrib/imageCarousel/browser/imageCarousel.contribution.ts index 5000f1745be..ad4e2f193d6 100644 --- a/src/vs/workbench/contrib/imageCarousel/browser/imageCarousel.contribution.ts +++ b/src/vs/workbench/contrib/imageCarousel/browser/imageCarousel.contribution.ts @@ -10,7 +10,7 @@ import { ServicesAccessor } from '../../../../platform/instantiation/common/inst import { Registry } from '../../../../platform/registry/common/platform.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; import { EditorExtensions, IEditorFactoryRegistry, IEditorSerializer } from '../../../common/editor.js'; -import { IEditorService, MODAL_GROUP } from '../../../services/editor/common/editorService.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { ImageCarouselEditor } from './imageCarouselEditor.js'; @@ -146,7 +146,7 @@ class OpenImageInCarouselAction extends Action2 { } const input = new ImageCarouselEditorInput(collection, startIndex); - await editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + await editorService.openEditor(input, { pinned: true }); } } @@ -316,7 +316,7 @@ class OpenImagesInCarouselFromExplorerAction extends Action2 { }; const input = new ImageCarouselEditorInput(collection, startIndex); - await editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + await editorService.openEditor(input, { pinned: true }); } } diff --git a/src/vs/workbench/contrib/imageCarousel/browser/imageCarouselEditorInput.ts b/src/vs/workbench/contrib/imageCarousel/browser/imageCarouselEditorInput.ts index 5d0b17ba2e5..50f76b0bff0 100644 --- a/src/vs/workbench/contrib/imageCarousel/browser/imageCarouselEditorInput.ts +++ b/src/vs/workbench/contrib/imageCarousel/browser/imageCarouselEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { EditorInput } from '../../../common/editor/editorInput.js'; -import { IUntypedEditorInput } from '../../../common/editor.js'; +import { EditorInputCapabilities, IUntypedEditorInput } from '../../../common/editor.js'; import { URI } from '../../../../base/common/uri.js'; import { Schemas } from '../../../../base/common/network.js'; import { IImageCarouselCollection } from './imageCarouselTypes.js'; @@ -15,6 +15,10 @@ export class ImageCarouselEditorInput extends EditorInput { private _resource: URI; private _name: string; + override get capabilities(): EditorInputCapabilities { + return super.capabilities | EditorInputCapabilities.Singleton | EditorInputCapabilities.RequiresModal; + } + constructor( public readonly collection: IImageCarouselCollection, public readonly startIndex: number = 0 diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 1159e4ce41e..d13453552f1 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -17,7 +17,7 @@ import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWo import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { Codicon } from '../../../../base/common/codicons.js'; -import { IEditorService, MODAL_GROUP } from '../../../services/editor/common/editorService.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; @@ -747,7 +747,7 @@ registerAction2(class extends Action2 { const input = instantiationService.createInstance(WorkspaceTrustEditorInput); - editorService.openEditor(input, { pinned: true }, MODAL_GROUP); + editorService.openEditor(input, { pinned: true }); return; } }); diff --git a/src/vs/workbench/services/editor/common/editorGroupFinder.ts b/src/vs/workbench/services/editor/common/editorGroupFinder.ts index e7f6ae7e6bc..470c9edb760 100644 --- a/src/vs/workbench/services/editor/common/editorGroupFinder.ts +++ b/src/vs/workbench/services/editor/common/editorGroupFinder.ts @@ -11,19 +11,21 @@ import { EditorInput } from '../../../common/editor/editorInput.js'; import { IEditorGroup, GroupsOrder, preferredSideBySideGroupDirection, IEditorGroupsService, IModalEditorPart } from './editorGroupsService.js'; import { AUX_WINDOW_GROUP, AUX_WINDOW_GROUP_TYPE, MODAL_GROUP, MODAL_GROUP_TYPE, PreferredGroup, SIDE_GROUP } from './editorService.js'; +type FindGroupResult = Promise<[IEditorGroup, EditorActivation | undefined]> | [IEditorGroup, EditorActivation | undefined]; + /** * Finds the target `IEditorGroup` given the instructions provided * that is best for the editor and matches the preferred group if * possible. */ -export function findGroup(accessor: ServicesAccessor, editor: IUntypedEditorInput, preferredGroup: Exclude | undefined): [IEditorGroup, EditorActivation | undefined]; -export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions, preferredGroup: Exclude | undefined): [IEditorGroup, EditorActivation | undefined]; -export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: Exclude | undefined): [IEditorGroup, EditorActivation | undefined]; +export function findGroup(accessor: ServicesAccessor, editor: IUntypedEditorInput, preferredGroup: Exclude | undefined): FindGroupResult; +export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions, preferredGroup: Exclude | undefined): FindGroupResult; +export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: Exclude | undefined): FindGroupResult; export function findGroup(accessor: ServicesAccessor, editor: IUntypedEditorInput, preferredGroup: AUX_WINDOW_GROUP_TYPE | MODAL_GROUP_TYPE): Promise<[IEditorGroup, EditorActivation | undefined]>; export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions, preferredGroup: AUX_WINDOW_GROUP_TYPE | MODAL_GROUP_TYPE): Promise<[IEditorGroup, EditorActivation | undefined]>; export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: AUX_WINDOW_GROUP_TYPE | MODAL_GROUP_TYPE): Promise<[IEditorGroup, EditorActivation | undefined]>; -export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): Promise<[IEditorGroup, EditorActivation | undefined]> | [IEditorGroup, EditorActivation | undefined]; -export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): Promise<[IEditorGroup, EditorActivation | undefined]> | [IEditorGroup, EditorActivation | undefined] { +export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): FindGroupResult; +export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): FindGroupResult { const editorGroupService = accessor.get(IEditorGroupsService); const configurationService = accessor.get(IConfigurationService); @@ -38,8 +40,12 @@ export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOpt function handleGroupResult(group: IEditorGroup, editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: PreferredGroup | undefined, editorGroupService: IEditorGroupsService, configurationService: IConfigurationService): [IEditorGroup, EditorActivation | undefined] { const modalEditorPart = editorGroupService.activeModalEditorPart; const modalEditorMode = configurationService.getValue('workbench.editor.useModal'); - if (modalEditorPart && preferredGroup !== MODAL_GROUP && modalEditorMode !== 'all') { + const editorInput = isEditorInputWithOptions(editor) ? editor.editor : isEditorInput(editor) ? editor : undefined; + const requiresModal = editorInput instanceof EditorInput && editorInput.hasCapability(EditorInputCapabilities.RequiresModal); + if (modalEditorPart && preferredGroup !== MODAL_GROUP && modalEditorMode !== 'all' && !requiresModal) { // Only allow to open in modal group if MODAL_GROUP is explicitly requested + // or when the setting is configured to open all editors modal or when the + // editor has the RequiresModal capability. group = handleModalEditorPart(group, editor, modalEditorPart, editorGroupService); } @@ -94,8 +100,14 @@ function doFindGroup(input: EditorInputWithOptions | IUntypedEditorInput, prefer const editor = isEditorInputWithOptions(input) ? input.editor : input; const options = input.options; + // Group: Force modal if the editor has the RequiresModal capability + if (isEditorInput(editor) && editor.hasCapability(EditorInputCapabilities.RequiresModal)) { + group = editorGroupService.createModalEditorPart(options?.modal) + .then(part => part.activeGroup); + } + // Group: Instance of Group - if (preferredGroup && typeof preferredGroup !== 'number') { + else if (preferredGroup && typeof preferredGroup !== 'number') { group = preferredGroup; } diff --git a/src/vs/workbench/services/editor/test/browser/modalEditorGroup.test.ts b/src/vs/workbench/services/editor/test/browser/modalEditorGroup.test.ts index 7d37cde03e3..730eca2a292 100644 --- a/src/vs/workbench/services/editor/test/browser/modalEditorGroup.test.ts +++ b/src/vs/workbench/services/editor/test/browser/modalEditorGroup.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { workbenchInstantiationService, registerTestEditor, TestFileEditorInput, createEditorParts } from '../../../../test/browser/workbenchTestServices.js'; import { GroupsOrder, IEditorGroupsService } from '../../common/editorGroupsService.js'; -import { EditorExtensions, IEditorFactoryRegistry } from '../../../../common/editor.js'; +import { EditorExtensions, EditorInputCapabilities, IEditorFactoryRegistry } from '../../../../common/editor.js'; import { URI } from '../../../../../base/common/uri.js'; import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; @@ -412,7 +412,7 @@ suite('Modal Editor Group', () => { // findGroup without MODAL_GROUP should return main part group, not modal group const newInput = createTestFileEditorInput(URI.file('foo/baz'), TEST_EDITOR_INPUT_ID); - const [group] = instantiationService.invokeFunction(accessor => findGroup(accessor, { resource: newInput.resource }, undefined)); + const [group] = await instantiationService.invokeFunction(accessor => findGroup(accessor, { resource: newInput.resource }, undefined)); assert.strictEqual(group.id, mainGroup.id); }); @@ -432,7 +432,7 @@ suite('Modal Editor Group', () => { // findGroup without MODAL_GROUP and without preserveFocus should close the modal const newInput = createTestFileEditorInput(URI.file('foo/baz'), TEST_EDITOR_INPUT_ID); - instantiationService.invokeFunction(accessor => findGroup(accessor, { resource: newInput.resource }, undefined)); + await instantiationService.invokeFunction(accessor => findGroup(accessor, { resource: newInput.resource }, undefined)); assert.strictEqual(parts.activeModalEditorPart, undefined); }); @@ -452,7 +452,7 @@ suite('Modal Editor Group', () => { // findGroup with preserveFocus should keep the modal open const newInput = createTestFileEditorInput(URI.file('foo/baz'), TEST_EDITOR_INPUT_ID); - instantiationService.invokeFunction(accessor => findGroup(accessor, { resource: newInput.resource, options: { preserveFocus: true } }, undefined)); + await instantiationService.invokeFunction(accessor => findGroup(accessor, { resource: newInput.resource, options: { preserveFocus: true } }, undefined)); assert.strictEqual(parts.activeModalEditorPart, modalPart); @@ -766,5 +766,61 @@ suite('Modal Editor Group', () => { modalPart.close(); }); + suite('RequiresModal capability', () => { + + test('findGroup opens modal for editor with RequiresModal even when setting is off', async () => { + const instantiationService = workbenchInstantiationService({ contextKeyService: instantiationService => instantiationService.createInstance(MockScopableContextKeyService) }, disposables); + instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorFactory).start(accessor)); + const configurationService = new TestConfigurationService(); + await configurationService.setUserConfiguration('workbench.editor.useModal', 'off'); + instantiationService.stub(IConfigurationService, configurationService); + const parts = await createEditorParts(instantiationService, disposables); + instantiationService.stub(IEditorGroupsService, parts); + + const input = createTestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + input.capabilities = EditorInputCapabilities.RequiresModal; + + const result = instantiationService.invokeFunction(accessor => findGroup(accessor, { editor: input, options: {} }, undefined)); + + assert.ok(result instanceof Promise); + const [group] = await result; + + assert.ok(parts.activeModalEditorPart); + assert.strictEqual(group.id, parts.activeModalEditorPart.activeGroup.id); + + parts.activeModalEditorPart.close(); + }); + + test('findGroup does not close modal for RequiresModal editor when modal is already open', async () => { + const instantiationService = workbenchInstantiationService({ contextKeyService: instantiationService => instantiationService.createInstance(MockScopableContextKeyService) }, disposables); + instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorFactory).start(accessor)); + const configurationService = new TestConfigurationService(); + await configurationService.setUserConfiguration('workbench.editor.useModal', 'some'); + instantiationService.stub(IConfigurationService, configurationService); + const parts = await createEditorParts(instantiationService, disposables); + instantiationService.stub(IEditorGroupsService, parts); + + // Create a modal part first + const modalPart = await parts.createModalEditorPart(); + const existingInput = createTestFileEditorInput(URI.file('foo/existing'), TEST_EDITOR_INPUT_ID); + await modalPart.activeGroup.openEditor(existingInput, { pinned: true }); + + // Now open a RequiresModal editor — modal should stay open + const input = createTestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + input.capabilities = EditorInputCapabilities.RequiresModal; + + const result = instantiationService.invokeFunction(accessor => findGroup(accessor, { editor: input, options: {} }, undefined)); + + assert.ok(result instanceof Promise); + const [group] = await result; + + assert.ok(parts.activeModalEditorPart); + assert.strictEqual(parts.activeModalEditorPart, modalPart); + assert.strictEqual(group.id, modalPart.activeGroup.id); + + modalPart.close(); + }); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts b/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts index 2c9e8f3f8e8..8ed090ac5b3 100644 --- a/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts @@ -18,7 +18,7 @@ export class WorkspaceTrustEditorInput extends EditorInput { static readonly ID: string = 'workbench.input.workspaceTrust'; override get capabilities(): EditorInputCapabilities { - return EditorInputCapabilities.Readonly | EditorInputCapabilities.Singleton; + return super.capabilities | EditorInputCapabilities.Singleton | EditorInputCapabilities.RequiresModal; } override get typeId(): string {