From 053084e592da79cb750399bcf2490c2fb43a062d Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 4 Apr 2022 15:01:54 -0400 Subject: [PATCH] Implement group closing tabs API --- .../api/browser/mainThreadEditorTabs.ts | 14 ++++ .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostEditorTabs.ts | 81 ++++++++++++++----- src/vscode-dts/vscode.proposed.tabs.d.ts | 15 +++- 4 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts index f3677aec085..bf643f7e1dc 100644 --- a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts +++ b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts @@ -561,5 +561,19 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape { // TODO @jrieken This isn't quite right how can we say true for some but not others? return results.every(result => result); } + + async $closeGroup(groupIds: number[], preserveFocus?: boolean): Promise { + const groupCloseResults: boolean[] = []; + for (const groupId of groupIds) { + const group = this._editorGroupsService.getGroup(groupId); + if (group) { + // TODO @lramos15 change this to use group.closeAllEditors once it + // is enriched to return a boolean + groupCloseResults.push(await group.closeEditors([...group.editors], { preserveFocus })); + this._editorGroupsService.removeGroup(group); + } + } + return groupCloseResults.every(result => result); + } //#endregion } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c33567f8d99..94630f2404a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -677,6 +677,7 @@ export interface MainThreadEditorTabsShape extends IDisposable { // manage tabs: move, close, rearrange etc $moveTab(tabId: string, index: number, viewColumn: EditorGroupColumn, preserveFocus?: boolean): void; $closeTab(tabIds: string[], preserveFocus?: boolean): Promise; + $closeGroup(groupIds: number[], preservceFocus?: boolean): Promise; } export interface IEditorTabGroupDto { diff --git a/src/vs/workbench/api/common/extHostEditorTabs.ts b/src/vs/workbench/api/common/extHostEditorTabs.ts index cbd2effae0c..c22362969bd 100644 --- a/src/vs/workbench/api/common/extHostEditorTabs.ts +++ b/src/vs/workbench/api/common/extHostEditorTabs.ts @@ -231,17 +231,18 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { const activeTabGroup = assertIsDefined(that._extHostTabGroups.find(candidate => candidate.groupId === activeTabGroupId)?.apiObject); return activeTabGroup; }, - close: async (tab: vscode.Tab | vscode.Tab[], preserveFocus?: boolean) => { - const tabs = Array.isArray(tab) ? tab : [tab]; - const extHostTabIds: string[] = []; - for (const tab of tabs) { - const extHostTab = this._findExtHostTabFromApi(tab); - if (!extHostTab) { - throw new Error('Tab close: Invalid tab not found!'); - } - extHostTabIds.push(extHostTab.tabId); + close: async (tabOrTabGroup: vscode.Tab | vscode.Tab[] | vscode.TabGroup | vscode.TabGroup[], preserveFocus?: boolean) => { + const tabsOrTabGroups = Array.isArray(tabOrTabGroup) ? tabOrTabGroup : [tabOrTabGroup]; + if (!tabsOrTabGroups.length) { + return true; + } + // Check which type was passed in and call the appropriate close + // Casting is needed as typescript doesn't seem to infer enough from this + if (isTabGroup(tabsOrTabGroups[0])) { + return this._closeGroups(tabsOrTabGroups as vscode.TabGroup[], preserveFocus); + } else { + return this._closeTabs(tabsOrTabGroups as vscode.Tab[], preserveFocus); } - return this._proxy.$closeTab(extHostTabIds, preserveFocus); }, move: async (tab: vscode.Tab, viewColumn: ViewColumn, index: number, preservceFocus?: boolean) => { const extHostTab = this._findExtHostTabFromApi(tab); @@ -257,17 +258,6 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { return this._apiObject; } - private _findExtHostTabFromApi(apiTab: vscode.Tab): ExtHostEditorTab | undefined { - for (const group of this._extHostTabGroups) { - for (const tab of group.tabs) { - if (tab.apiObject === apiTab) { - return tab; - } - } - } - return; - } - $acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void { this._extHostTabGroups = tabGroups.map(tabGroup => { @@ -306,4 +296,53 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { this._onDidChangeTabs.fire([tab.apiObject]); } } + + private _findExtHostTabFromApi(apiTab: vscode.Tab): ExtHostEditorTab | undefined { + for (const group of this._extHostTabGroups) { + for (const tab of group.tabs) { + if (tab.apiObject === apiTab) { + return tab; + } + } + } + return; + } + + private _findExtHostTabGroupFromApi(apiTabGroup: vscode.TabGroup): ExtHostEditorTabGroup | undefined { + return this._extHostTabGroups.find(candidate => candidate.apiObject === apiTabGroup); + } + + private async _closeTabs(tabs: vscode.Tab[], preserveFocus?: boolean): Promise { + const extHostTabIds: string[] = []; + for (const tab of tabs) { + const extHostTab = this._findExtHostTabFromApi(tab); + if (!extHostTab) { + throw new Error('Tab close: Invalid tab not found!'); + } + extHostTabIds.push(extHostTab.tabId); + } + return this._proxy.$closeTab(extHostTabIds, preserveFocus); + } + + private async _closeGroups(groups: vscode.TabGroup[], preserverFoucs?: boolean): Promise { + const extHostGroupIds: number[] = []; + for (const group of groups) { + const extHostGroup = this._findExtHostTabGroupFromApi(group); + if (!extHostGroup) { + throw new Error('Group close: Invalid group not found!'); + } + extHostGroupIds.push(extHostGroup.groupId); + } + return this._proxy.$closeGroup(extHostGroupIds, preserverFoucs); + } } + +//#region Utils +function isTabGroup(obj: unknown): obj is vscode.TabGroup { + const tabGroup = obj as vscode.TabGroup; + if (tabGroup.tabs !== undefined) { + return true; + } + return false; +} +//#endregion diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts index 04c770ed780..0a13dd98773 100644 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -53,8 +53,8 @@ declare module 'vscode' { /** * Represents a tab within a {@link TabGroup group of tabs}. - * - * TODO@API explain the tab is a button, not editor concept + * Tabs are merely the grapihcal repesentation within the editor area. + * A backing editor is not a gurantee. */ export interface Tab { @@ -160,8 +160,15 @@ declare module 'vscode' { * @returns A promise that resolves to `true` when all tabs have been closed */ close(tab: Tab | Tab[], preserveFocus?: boolean): Thenable; - // TODO@API support to close "all" - // close(tab: TabGroup | TabGroup[], preserveFocus?: boolean): Thenable; + + /** + * Closes the tab group. This makes the tab group object invalid and the tab group + * should no longer be used for furhter actions. + * @param tabGroup The tab group to close. + * @param preserveFocus When `true` focus will remain in its current position. + * @returns A promise that resolves to `true` when all tab groups have been closed + */ + close(tabGroup: TabGroup | TabGroup[], preserveFocus?: boolean): Thenable; /** * Moves a tab to the given index within the column.