diff --git a/.vscode/settings.json b/.vscode/settings.json index e525b17dbb3..417af3874f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -171,7 +171,7 @@ // Temporarily enabled for self-hosting "scm.experimental.showSyncInformation": { - "incoming": false, - "outgoing": false + "incoming": true, + "outgoing": true } } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 9b857f559e2..77ba56af13e 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1595,22 +1595,22 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi viewSize = this.workbenchGrid.getViewSize(this.editorPartView); // Single Editor Group - if (this.editorGroupService.count === 1) { + if (this.editorGroupService.mainPart.count === 1) { this.workbenchGrid.resizeView(this.editorPartView, { width: viewSize.width + sizeChangePxWidth, height: viewSize.height + sizeChangePxHeight }); } else { - const activeGroup = this.editorGroupService.activeGroup; + const activeGroup = this.editorGroupService.mainPart.activeGroup; - const { width, height } = this.editorGroupService.getSize(activeGroup); - this.editorGroupService.setSize(activeGroup, { width: width + sizeChangePxWidth, height: height + sizeChangePxHeight }); + const { width, height } = this.editorGroupService.mainPart.getSize(activeGroup); + this.editorGroupService.mainPart.setSize(activeGroup, { width: width + sizeChangePxWidth, height: height + sizeChangePxHeight }); // After resizing the editor group // if it does not change in either direction // try resizing the full editor part - const { width: newWidth, height: newHeight } = this.editorGroupService.getSize(activeGroup); + const { width: newWidth, height: newHeight } = this.editorGroupService.mainPart.getSize(activeGroup); if ((sizeChangePxHeight && height === newHeight) || (sizeChangePxWidth && width === newWidth)) { this.workbenchGrid.resizeView(this.editorPartView, { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index d548a7a3295..9b8d619d2bf 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -759,11 +759,13 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { // Remove empty group if (groupView.isEmpty) { - return this.doRemoveEmptyGroup(groupView, preserveFocus); + this.doRemoveEmptyGroup(groupView, preserveFocus); } // Remove group with editors - this.doRemoveGroupWithEditors(groupView); + else { + this.doRemoveGroupWithEditors(groupView); + } } private doRemoveGroupWithEditors(groupView: IEditorGroupView): void { @@ -809,7 +811,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { this.updateContainer(); // Update locked state: clear when we are at just 1 group - if (this.count === 1) { + // in case we are in the main editor part + if (this.count === 1 && !this.isAuxiliary) { firstOrDefault(this.groups)?.lock(false); } @@ -918,7 +921,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { return target; } - private assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { + protected assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { let groupView: IEditorGroupView | undefined; if (typeof group === 'number') { groupView = this.editorPartsView.getGroup(group); @@ -1405,6 +1408,20 @@ export class AuxiliaryEditorPart extends EditorPart implements IAuxiliaryEditorP super(editorPartsView, `workbench.parts.auxiliaryEditor.${id}`, groupsLabel, true, instantiationService, themeService, configurationService, storageService, layoutService, hostService, contextKeyService); } + override removeGroup(group: number | IEditorGroupView, preserveFocus?: boolean | undefined): void { + + // Close aux window when last group removed + const groupView = this.assertGroupView(group); + if (this.count === 1 && this.activeGroup === groupView) { + this.close(); + } + + // Otherwise delegate to parent implementation + else { + super.removeGroup(group, preserveFocus); + } + } + protected override saveState(): void { return; // TODO support auxiliary editor state } diff --git a/src/vs/workbench/browser/parts/editor/editorParts.ts b/src/vs/workbench/browser/parts/editor/editorParts.ts index b1895de1bc2..4c6247a3687 100644 --- a/src/vs/workbench/browser/parts/editor/editorParts.ts +++ b/src/vs/workbench/browser/parts/editor/editorParts.ts @@ -51,7 +51,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd partContainer.setAttribute('role', 'main'); auxiliaryWindow.container.appendChild(partContainer); - const editorPart = disposables.add(this.instantiationService.createInstance(AuxiliaryEditorPart, this, this.getGroupsLabel(this.parts.size))); + const editorPart = disposables.add(this.instantiationService.createInstance(AuxiliaryEditorPart, this, this.getGroupsLabel(this._parts.size))); disposables.add(this.registerEditorPart(editorPart)); disposables.add(Event.once(editorPart.onDidClose)(() => disposables.dispose())); @@ -73,10 +73,11 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd //#region Registration - private readonly parts = new Set(); + private readonly _parts = new Set(); + get parts() { return Array.from(this._parts); } private registerEditorPart(part: EditorPart): IDisposable { - this.parts.add(part); + this._parts.add(part); const disposables = this._register(new DisposableStore()); disposables.add(toDisposable(() => this.unregisterEditorPart(part))); @@ -87,12 +88,12 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd } private unregisterEditorPart(part: EditorPart): void { - this.parts.delete(part); + this._parts.delete(part); // Notify all parts about a groups label change // given it is computed based on the index - Array.from(this.parts).forEach((part, index) => { + this.parts.forEach((part, index) => { if (part === this.mainPart) { return; } @@ -103,7 +104,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd private registerEditorPartListeners(part: EditorPart, disposables: DisposableStore): void { disposables.add(part.onDidFocus(() => { - if (this.parts.size > 1) { + if (this._parts.size > 1) { this._onDidActiveGroupChange.fire(this.activeGroup); // this can only happen when we have more than 1 editor part } })); @@ -132,8 +133,8 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd } private getPartByDocument(document: Document): EditorPart { - if (this.parts.size > 1) { - for (const part of this.parts) { + if (this._parts.size > 1) { + for (const part of this._parts) { if (part.element?.ownerDocument === document) { return part; } @@ -146,7 +147,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd getPart(group: IEditorGroupView | GroupIdentifier): EditorPart; getPart(element: HTMLElement): EditorPart; getPart(groupOrElement: IEditorGroupView | GroupIdentifier | HTMLElement): EditorPart { - if (this.parts.size > 1) { + if (this._parts.size > 1) { if (groupOrElement instanceof HTMLElement) { const element = groupOrElement; @@ -161,7 +162,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd id = group.id; } - for (const part of this.parts) { + for (const part of this._parts) { if (part.hasGroup(id)) { return part; } @@ -221,17 +222,17 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd } getGroups(order = GroupsOrder.CREATION_TIME): IEditorGroupView[] { - if (this.parts.size > 1) { + if (this._parts.size > 1) { // TODO@bpasero support non-creation-time group orders across parts - return [...this.parts].map(part => part.getGroups(order)).flat(); + return [...this._parts].map(part => part.getGroups(order)).flat(); } return this.mainPart.getGroups(order); } getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined { - if (this.parts.size > 1) { - for (const part of this.parts) { + if (this._parts.size > 1) { + for (const part of this._parts) { const group = part.getGroup(identifier); if (group) { return group; diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 5623d25571b..b541beee97f 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -84,8 +84,6 @@ export class NativeWindow extends Disposable { private readonly addFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddFolders(), 100)); private pendingFoldersToAdd: URI[] = []; - private readonly closeEmptyWindowScheduler = this._register(new RunOnceScheduler(() => this.onDidAllEditorsClose(), 50)); - private isDocumentedEdited = false; private readonly mainPartEditorService: IEditorService; @@ -340,8 +338,8 @@ export class NativeWindow extends Disposable { } })); - // Listen to visible editor changes - this._register(this.mainPartEditorService.onDidVisibleEditorsChange(() => this.onDidChangeVisibleEditors())); + // Listen to visible editor changes (debounced) + this._register(Event.debounce(this.editorService.onDidVisibleEditorsChange, () => undefined, 50, undefined, undefined, undefined, this._store)(() => this.onDidChangeVisibleEditors())); // Listen to editor closing (if we run with --wait) const filesToWait = this.environmentService.filesToWait; @@ -601,22 +599,25 @@ export class NativeWindow extends Disposable { private onDidChangeVisibleEditors(): void { - // Close when empty: check if we should close the window based on the setting - // Overruled by: window has a workspace opened or this window is for extension development - // or setting is disabled. Also enabled when running with --wait from the command line. - const visibleEditorPanes = this.mainPartEditorService.visibleEditorPanes; - if (visibleEditorPanes.length === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) { - const closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty'); - if (closeWhenEmpty || this.environmentService.args.wait) { - this.closeEmptyWindowScheduler.schedule(); + // Close empty editor groups based on setting and environment + for (const editorPart of this.editorGroupService.parts) { + if (editorPart.groups.some(group => !group.isEmpty)) { + continue; // not empty } - } - } - private onDidAllEditorsClose(): void { - const visibleEditorPanes = this.mainPartEditorService.visibleEditorPanes.length; - if (visibleEditorPanes === 0) { - this.nativeHostService.closeWindow(); + let closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty') || this.environmentService.args.wait; + if (editorPart === this.editorGroupService.mainPart && (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY || this.environmentService.isExtensionDevelopment)) { + closeWhenEmpty = false; // disabled for main part when window is not empty or extension development + } + if (!closeWhenEmpty) { + continue; // not enabled to close when empty + } + + if (editorPart === this.editorGroupService.mainPart) { + this.nativeHostService.closeWindow(); + } else { + editorPart.removeGroup(editorPart.activeGroup); + } } } diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 2fed550260d..03aeb6ec3fb 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -489,6 +489,11 @@ export interface IEditorGroupsService extends IEditorGroupsContainer { */ readonly mainPart: IEditorPart; + /** + * Provides access to all editor parts. + */ + readonly parts: ReadonlyArray; + /** * Get the editor part that contains the group with the provided identifier. */ diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index a7c4ffa8ad4..47df81d91ed 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -824,6 +824,8 @@ export class TestEditorGroupsService implements IEditorGroupsService { constructor(public groups: TestEditorGroupView[] = []) { } + readonly parts: readonly IEditorPart[] = [this]; + onDidChangeActiveGroup: Event = Event.None; onDidActivateGroup: Event = Event.None; onDidAddGroup: Event = Event.None; @@ -1753,6 +1755,7 @@ export class TestEditorPart extends MainEditorPart implements IEditorGroupsServi readonly activePart = this; readonly mainPart = this; + readonly parts: readonly IEditorPart[] = [this]; testSaveState(): void { return super.saveState();