diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 8254b6131f237..460d7fb6cd1b3 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -282,6 +282,18 @@ export interface IEditorOptions { */ source?: EditorOpenSource; + /** + * Indicates whether the editor is being opened due to an explicit user + * action (`true`) or automatically (`false`) as a side effect of another + * action (e.g. the chat agent opening files it has edited). + * + * When omitted, callers should be treated as explicit. Layout logic may + * use this to decide whether to react to the visibility change (for + * example, by leaving the auxiliary side bar maximized when the change + * was not initiated by the user). + */ + isExplicit?: boolean; + /** * An optional property to signal that certain view state should be * applied when opening the editor. diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index a8808a5c4e277..3480283a2177b 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -341,7 +341,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private registerLayoutListeners(): void { // Restore editor if hidden and an editor is to show - const showEditorIfHidden = () => { + const showEditorIfHidden = (explicitUserAction?: boolean) => { if ( this.isVisible(Parts.EDITOR_PART, mainWindow) || // already visible this.mainPartEditorService.visibleEditors.length === 0 // no editor to show @@ -350,7 +350,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (this.isAuxiliaryBarMaximized()) { - this.toggleMaximizedAuxiliaryBar(); + // Do not unmaximize the auxiliary side bar when the editor was + // opened automatically (e.g. by the chat agent applying edits). + // Only an explicit user action should disrupt the chosen layout. + if (explicitUserAction !== false) { + this.toggleMaximizedAuxiliaryBar(); + } } else { this.toggleMaximizedPanel(); } @@ -375,10 +380,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.editorGroupService.whenRestored.then(() => { // Handle visible editors changing for parts visibility - this._register(this.mainPartEditorService.onDidVisibleEditorsChange(() => { + this._register(this.mainPartEditorService.onDidVisibleEditorsChange(e => { const handled = maybeMaximizeAuxiliaryBar(); if (!handled) { - showEditorIfHidden(); + showEditorIfHidden(e.isExplicit); } })); this._register(this.editorGroupService.mainPart.onDidActivateGroup(e => { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index a2344eaf8f200..7b3e372a0772a 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1266,7 +1266,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Editor change event if (changed) { - this._onDidActiveEditorChange.fire({ editor }); + this._onDidActiveEditorChange.fire({ editor, isExplicit: options?.isExplicit }); } // Indicate error as an event but do not bubble them up diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 65ef6b48d662f..054d02dbfcaa3 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1166,6 +1166,15 @@ export interface IActiveEditorChangeEvent { * The new active editor or `undefined` if the group is empty. */ editor: EditorInput | undefined; + + /** + * Indicates whether the editor change is the result of an explicit + * user action (`true`) or happened automatically as a side effect + * (e.g. the chat agent opening files it has edited). + * + * When omitted, callers should treat the change as explicit. + */ + isExplicit?: boolean; } export interface IEditorWillMoveEvent extends IEditorIdentifier { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts index 4f7446e4736b1..dee9c180fcec5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts @@ -256,7 +256,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic || this._editorService.activeEditorPane?.input instanceof ChatEditorInput && isEqual(this._editorService.activeEditorPane.input.sessionResource, session.chatSessionResource) || Boolean(activeUri && session.entries.get().find(entry => isEqual(activeUri, entry.modifiedURI))); - this._editorService.openEditor({ resource: uri, options: { inactive, preserveFocus: true, pinned: true } }); + this._editorService.openEditor({ resource: uri, options: { inactive, preserveFocus: true, pinned: true, isExplicit: false } }); } })()); }; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index 6aa021ef3a82f..b04028c300d61 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -1209,7 +1209,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio // this file does not exist yet, create it and try again await this._bulkEditService.apply({ edits: [{ newResource: resource }] }); if (this.configurationService.getValue('accessibility.openChatEditedFiles')) { - this._editorService.openEditor({ resource, options: { inactive: true, preserveFocus: true, pinned: true } }); + this._editorService.openEditor({ resource, options: { inactive: true, preserveFocus: true, pinned: true, isExplicit: false } }); } // Record file creation operation diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 29189be6086de..3edcd7ea3532f 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -16,7 +16,7 @@ import { joinPath } from '../../../../base/common/resources.js'; import { DiffEditorInput } from '../../../common/editor/diffEditorInput.js'; import { SideBySideEditor as SideBySideEditorPane } from '../../../browser/parts/editor/sideBySideEditor.js'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, isEditorReplacement, ICloseEditorOptions, IEditorGroupsContainer } from '../common/editorGroupsService.js'; -import { IUntypedEditorReplacement, IEditorService, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorsOptions, PreferredGroup, isPreferredGroup, IEditorsChangeEvent, ISaveEditorsResult } from '../common/editorService.js'; +import { IUntypedEditorReplacement, IEditorService, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorsOptions, PreferredGroup, isPreferredGroup, IEditorsChangeEvent, ISaveEditorsResult, IVisibleEditorsChangeEvent } from '../common/editorService.js'; import { IConfigurationChangeEvent, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { Disposable, IDisposable, dispose, DisposableStore } from '../../../../base/common/lifecycle.js'; import { coalesce, distinct } from '../../../../base/common/arrays.js'; @@ -45,7 +45,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { private readonly _onDidActiveEditorChange = this._register(new Emitter()); readonly onDidActiveEditorChange = this._onDidActiveEditorChange.event; - private readonly _onDidVisibleEditorsChange = this._register(new Emitter()); + private readonly _onDidVisibleEditorsChange = this._register(new Emitter()); readonly onDidVisibleEditorsChange = this._onDidVisibleEditorsChange.event; private readonly _onDidEditorsChange = this._register(new Emitter()); @@ -135,7 +135,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Fire initial set of editor events if there is an active editor if (this.activeEditor) { this.doHandleActiveEditorChangeEvent(); - this._onDidVisibleEditorsChange.fire(); + this._onDidVisibleEditorsChange.fire({ isExplicit: false }); } } @@ -168,9 +168,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { this._onDidEditorsChange.fire({ groupId: group.id, event: e }); })); - groupDisposables.add(group.onDidActiveEditorChange(() => { + groupDisposables.add(group.onDidActiveEditorChange(e => { this.handleActiveEditorChange(group); - this._onDidVisibleEditorsChange.fire(); + this._onDidVisibleEditorsChange.fire({ isExplicit: e.isExplicit !== false /* treat undefined as explicit */ }); })); groupDisposables.add(group.onWillOpenEditor(e => { diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index c0e94a215a2ac..ac9788f7d5cc7 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -133,6 +133,16 @@ export interface IEditorsChangeEvent { event: IGroupModelChangeEvent; } +export interface IVisibleEditorsChangeEvent { + + /** + * Indicates whether the visibility change is the result of an explicit + * user action (`true`) or happened automatically as a side effect + * (e.g. the chat agent opening files it has edited). + */ + readonly isExplicit: boolean; +} + export interface IEditorService { readonly _serviceBrand: undefined; @@ -149,7 +159,7 @@ export interface IEditorService { * * @see {@link IEditorService.visibleEditorPanes} */ - readonly onDidVisibleEditorsChange: Event; + readonly onDidVisibleEditorsChange: Event; /** * An aggregated event for any change to any editor across diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0747566766ce0..62f841bd9da5f 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -153,7 +153,7 @@ import { CustomEditorLabelService, ICustomEditorLabelService } from '../../servi import { EditorGroupLayout, GroupDirection, GroupOrientation, GroupsArrangement, GroupsOrder, IAuxiliaryEditorPart, ICloseAllEditorsOptions, ICloseEditorOptions, ICloseEditorsFilter, IEditorDropTargetDelegate, IEditorGroup, IEditorGroupActivationEvent, IEditorGroupContextKeyProvider, IEditorGroupsContainer, IEditorGroupsService, IEditorPart, IEditorReplacement, IEditorWorkingSet, IEditorWorkingSetOptions, IFindGroupScope, IMergeGroupOptions, IModalEditorPart } from '../../services/editor/common/editorGroupsService.js'; import { IEditorPaneService } from '../../services/editor/common/editorPaneService.js'; import { IEditorResolverService } from '../../services/editor/common/editorResolverService.js'; -import { IEditorsChangeEvent, IEditorService, IRevertAllEditorsOptions, ISaveEditorsOptions, ISaveEditorsResult, PreferredGroup } from '../../services/editor/common/editorService.js'; +import { IEditorsChangeEvent, IEditorService, IRevertAllEditorsOptions, ISaveEditorsOptions, ISaveEditorsResult, IVisibleEditorsChangeEvent, PreferredGroup } from '../../services/editor/common/editorService.js'; import { BrowserWorkbenchEnvironmentService } from '../../services/environment/browser/environmentService.js'; import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js'; import { EnablementState, IExtensionManagementServer, IResourceExtension, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../services/extensionManagement/common/extensionManagement.js'; @@ -1050,7 +1050,7 @@ export class TestEditorService extends Disposable implements EditorServiceImpl { declare readonly _serviceBrand: undefined; readonly onDidActiveEditorChange: Event = Event.None; - readonly onDidVisibleEditorsChange: Event = Event.None; + readonly onDidVisibleEditorsChange: Event = Event.None; readonly onDidEditorsChange: Event = Event.None; readonly onWillOpenEditor: Event = Event.None; readonly onDidCloseEditor: Event = Event.None;