diff options
Diffstat (limited to 'src/vs/workbench/api/common/extHostEditorTabs.ts')
-rw-r--r-- | src/vs/workbench/api/common/extHostEditorTabs.ts | 165 |
1 files changed, 126 insertions, 39 deletions
diff --git a/src/vs/workbench/api/common/extHostEditorTabs.ts b/src/vs/workbench/api/common/extHostEditorTabs.ts index cbd2effae0c..1fbc2d487de 100644 --- a/src/vs/workbench/api/common/extHostEditorTabs.ts +++ b/src/vs/workbench/api/common/extHostEditorTabs.ts @@ -9,9 +9,10 @@ import { IEditorTabDto, IEditorTabGroupDto, IExtHostEditorTabsShape, MainContext import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { CustomEditorTabInput, NotebookDiffEditorTabInput, NotebookEditorTabInput, TerminalEditorTabInput, TextDiffTabInput, TextTabInput, ViewColumn, WebviewEditorTabInput } from 'vs/workbench/api/common/extHostTypes'; +import { CustomEditorTabInput, NotebookDiffEditorTabInput, NotebookEditorTabInput, TerminalEditorTabInput, TextDiffTabInput, TextTabInput, WebviewEditorTabInput } from 'vs/workbench/api/common/extHostTypes'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { assertIsDefined } from 'vs/base/common/types'; +import { diffSets } from 'vs/base/common/collections'; export interface IExtHostEditorTabs extends IExtHostEditorTabsShape { readonly _serviceBrand: undefined; @@ -47,7 +48,7 @@ class ExtHostEditorTab { get label() { return that._dto.label; }, - get kind() { + get input() { return that._input; }, get isDirty() { @@ -174,6 +175,17 @@ class ExtHostEditorTabGroup { this._activeTabId = ''; } return tab; + } else if (operation.kind === TabModelOperationKind.TAB_MOVE) { + if (operation.oldIndex === undefined) { + throw new Error('Invalid old index on move IPC'); + } + // Splice to remove at old index and insert at new index === moving the tab + const tab = this._tabs.splice(operation.oldIndex, 1)[0]; + if (!tab) { + throw new Error(`Tab move updated received for index ${operation.oldIndex} which does not exist`); + } + this._tabs.splice(operation.index, 0, tab); + return tab; } const tab = this._tabs.find(extHostTab => extHostTab.tabId === operation.tabDto.id); if (!tab) { @@ -201,8 +213,8 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { readonly _serviceBrand: undefined; private readonly _proxy: MainThreadEditorTabsShape; - private readonly _onDidChangeTabs = new Emitter<vscode.Tab[]>(); - private readonly _onDidChangeTabGroups = new Emitter<vscode.TabGroup[]>(); + private readonly _onDidChangeTabs = new Emitter<vscode.TabChangeEvent>(); + private readonly _onDidChangeTabGroups = new Emitter<vscode.TabGroupChangeEvent>(); // Have to use ! because this gets initialized via an RPC proxy private _activeGroupId!: number; @@ -223,7 +235,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { onDidChangeTabGroups: that._onDidChangeTabGroups.event, onDidChangeTabs: that._onDidChangeTabs.event, // dynamic -> getters - get groups() { + get all() { return Object.freeze(that._extHostTabGroups.map(group => group.apiObject)); }, get activeTabGroup() { @@ -231,47 +243,51 @@ 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 | readonly vscode.Tab[] | vscode.TabGroup | readonly vscode.TabGroup[], preserveFocus?: boolean) => { + const tabsOrTabGroups = Array.isArray(tabOrTabGroup) ? tabOrTabGroup : [tabOrTabGroup]; + if (!tabsOrTabGroups.length) { + return true; } - return this._proxy.$closeTab(extHostTabIds, preserveFocus); - }, - move: async (tab: vscode.Tab, viewColumn: ViewColumn, index: number, preservceFocus?: boolean) => { - const extHostTab = this._findExtHostTabFromApi(tab); - if (!extHostTab) { - throw new Error('Invalid tab'); + // 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); } - this._proxy.$moveTab(extHostTab.tabId, index, typeConverters.ViewColumn.from(viewColumn), preservceFocus); - return; - } + }, + // move: async (tab: vscode.Tab, viewColumn: ViewColumn, index: number, preservceFocus?: boolean) => { + // const extHostTab = this._findExtHostTabFromApi(tab); + // if (!extHostTab) { + // throw new Error('Invalid tab'); + // } + // this._proxy.$moveTab(extHostTab.tabId, index, typeConverters.ViewColumn.from(viewColumn), preservceFocus); + // return; + // } }; this._apiObject = Object.freeze(obj); } 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 { + const groupIdsBefore = new Set(this._extHostTabGroups.map(group => group.groupId)); + const groupIdsAfter = new Set(tabGroups.map(dto => dto.groupId)); + const diff = diffSets(groupIdsBefore, groupIdsAfter); + + const closed: vscode.TabGroup[] = this._extHostTabGroups.filter(group => diff.removed.includes(group.groupId)).map(group => group.apiObject); + const opened: vscode.TabGroup[] = []; + const changed: vscode.TabGroup[] = []; + + this._extHostTabGroups = tabGroups.map(tabGroup => { const group = new ExtHostEditorTabGroup(tabGroup, this._proxy, () => this._activeGroupId); + if (diff.added.includes(group.groupId)) { + opened.push(group.apiObject); + } else { + changed.push(group.apiObject); + } return group; }); @@ -280,7 +296,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { if (activeTabGroupId !== undefined && this._activeGroupId !== activeTabGroupId) { this._activeGroupId = activeTabGroupId; } - this._onDidChangeTabGroups.fire(this._extHostTabGroups.map(g => g.apiObject)); + this._onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed })); } $acceptTabGroupUpdate(groupDto: IEditorTabGroupDto) { @@ -292,7 +308,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { if (groupDto.isActive) { this._activeGroupId = groupDto.groupId; } - this._onDidChangeTabGroups.fire([group.apiObject]); + this._onDidChangeTabGroups.fire(Object.freeze({ changed: [group.apiObject], opened: [], closed: [] })); } $acceptTabOperation(operation: TabOperation) { @@ -301,9 +317,80 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { throw new Error('Update Tabs IPC call received before group creation.'); } const tab = group.acceptTabOperation(operation); - // We don't want to fire a change event with a closed tab to prevent an invalid tabs from being received - if (operation.kind !== TabModelOperationKind.TAB_CLOSE) { - this._onDidChangeTabs.fire([tab.apiObject]); + + // Construct the tab change event based on the operation + switch (operation.kind) { + case TabModelOperationKind.TAB_OPEN: + this._onDidChangeTabs.fire(Object.freeze({ + opened: [tab.apiObject], + closed: [], + changed: [] + })); + return; + case TabModelOperationKind.TAB_CLOSE: + this._onDidChangeTabs.fire(Object.freeze({ + opened: [], + closed: [tab.apiObject], + changed: [] + })); + return; + case TabModelOperationKind.TAB_MOVE: + case TabModelOperationKind.TAB_UPDATE: + this._onDidChangeTabs.fire(Object.freeze({ + opened: [], + closed: [], + changed: [tab.apiObject] + })); + return; } } + + 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<boolean> { + 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<boolean> { + 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 |