Skip to content

Commit 381a22e

Browse files
committed
Add onDidChangeActiveTab
1 parent 35b84e2 commit 381a22e

File tree

3 files changed

+98
-14
lines changed

3 files changed

+98
-14
lines changed

src/vs/workbench/api/common/extHostEditorTabs.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import type * as vscode from 'vscode';
6+
import * as vscode from 'vscode';
77
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
88
import { IEditorTabDto, IEditorTabGroupDto, IExtHostEditorTabsShape, MainContext, MainThreadEditorTabsShape, TabInputKind } from 'vs/workbench/api/common/extHost.protocol';
99
import { URI } from 'vs/base/common/uri';
@@ -162,6 +162,11 @@ class ExtHostEditorTabGroup {
162162
}
163163
if (dto.isActive) {
164164
this._activeTabId = dto.id;
165+
} else if (this._activeTabId === dto.id && !dto.isActive) {
166+
// Events aren't guaranteed to be in order so if we receive a dto that matches the active tab id
167+
// but isn't active we mark the active tab id as empty. This prevent onDidActiveTabChange frorm
168+
// firing incorrectly
169+
this._activeTabId = '';
165170
}
166171
tab.acceptDtoUpdate(dto);
167172
return tab;
@@ -177,8 +182,9 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
177182
readonly _serviceBrand: undefined;
178183

179184
private readonly _proxy: MainThreadEditorTabsShape;
180-
private readonly _onDidChangeTab = new Emitter<vscode.Tab[]>();
181-
private readonly _onDidChangeTabGroup = new Emitter<void>();
185+
private readonly _onDidChangeTabs = new Emitter<vscode.Tab[]>();
186+
private readonly _onDidChangeActiveTab = new Emitter<vscode.Tab>();
187+
private readonly _onDidChangeTabGroups = new Emitter<vscode.TabGroup[]>();
182188
private readonly _onDidChangeActiveTabGroup = new Emitter<vscode.TabGroup>();
183189

184190
// Have to use ! because this gets initialized via an RPC proxy
@@ -197,9 +203,10 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
197203
const that = this;
198204
const obj: vscode.TabGroups = {
199205
// never changes -> simple value
200-
onDidChangeTabGroup: that._onDidChangeTabGroup.event,
206+
onDidChangeTabGroups: that._onDidChangeTabGroups.event,
201207
onDidChangeActiveTabGroup: that._onDidChangeActiveTabGroup.event,
202-
onDidChangeTabs: that._onDidChangeTab.event,
208+
onDidChangeTabs: that._onDidChangeTabs.event,
209+
onDidChangeActiveTab: that._onDidChangeActiveTab.event,
203210
// dynamic -> getters
204211
get groups() {
205212
return Object.freeze(that._extHostTabGroups.map(group => group.apiObject));
@@ -259,7 +266,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
259266
this._activeGroupId = activeTabGroupId;
260267
this._onDidChangeActiveTabGroup.fire(this.tabGroups.activeTabGroup);
261268
}
262-
this._onDidChangeTabGroup.fire();
269+
this._onDidChangeTabGroups.fire(this._extHostTabGroups.map(g => g.apiObject));
263270
}
264271

265272
$acceptTabGroupUpdate(groupDto: IEditorTabGroupDto) {
@@ -275,7 +282,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
275282
this._onDidChangeActiveTabGroup.fire(group.apiObject);
276283
}
277284
}
278-
this._onDidChangeTabGroup.fire();
285+
this._onDidChangeTabGroups.fire([group.apiObject]);
279286
}
280287

281288
$acceptTabUpdate(groupId: number, tabDto: IEditorTabDto) {
@@ -284,6 +291,9 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
284291
throw new Error('Update Tabs IPC call received before group creation.');
285292
}
286293
const tab = group.acceptTabDtoUpdate(tabDto);
287-
this._onDidChangeTab.fire([tab.apiObject]);
294+
this._onDidChangeTabs.fire([tab.apiObject]);
295+
if (tab.apiObject.isActive) {
296+
this._onDidChangeActiveTab.fire(tab.apiObject);
297+
}
288298
}
289299
}

src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ suite('ExtHostEditorTabs', function () {
109109
);
110110

111111
let count = 0;
112-
extHostEditorTabs.tabGroups.onDidChangeTabGroup(() => count++);
112+
extHostEditorTabs.tabGroups.onDidChangeTabGroups(() => count++);
113113

114114
assert.strictEqual(count, 0);
115115

@@ -339,7 +339,7 @@ suite('ExtHostEditorTabs', function () {
339339
});
340340
assert.throws(() => {
341341
// @ts-expect-error write to readonly prop
342-
extHostEditorTabs.tabGroups.onDidChangeTabGroup = undefined;
342+
extHostEditorTabs.tabGroups.onDidChangeTabGroups = undefined;
343343
});
344344
});
345345

@@ -493,4 +493,72 @@ suite('ExtHostEditorTabs', function () {
493493
assert.strictEqual(extHostEditorTabs.tabGroups.groups.map(g => g.tabs).flat().length, 0);
494494
assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup?.activeTab, undefined);
495495
});
496+
497+
test('Active tab change event', function () {
498+
499+
const extHostEditorTabs = new ExtHostEditorTabs(
500+
SingleProxyRPCProtocol(new class extends mock<MainThreadEditorTabsShape>() {
501+
// override/implement $moveTab or $closeTab
502+
})
503+
);
504+
505+
let activeTabChangeCount = 0;
506+
extHostEditorTabs.tabGroups.onDidChangeActiveTab((activeTab) => {
507+
if (activeTab.isActive === false) {
508+
throw new Error('Active tab changed fired on inactive tab');
509+
}
510+
activeTabChangeCount++;
511+
});
512+
513+
const tab1: IEditorTabDto = createTabDto({
514+
id: 'uniquestring',
515+
isActive: true,
516+
label: 'label1',
517+
});
518+
519+
const tab2: IEditorTabDto = createTabDto({
520+
isActive: false,
521+
id: 'uniquestring2',
522+
label: 'label2',
523+
});
524+
525+
const tab3: IEditorTabDto = createTabDto({
526+
isActive: false,
527+
id: 'uniquestring3',
528+
label: 'label3',
529+
});
530+
531+
extHostEditorTabs.$acceptEditorTabModel([{
532+
isActive: true,
533+
viewColumn: 0,
534+
groupId: 12,
535+
tabs: [tab1, tab2, tab3]
536+
}]);
537+
538+
// Accepting a model doesn't fire an active tab change event
539+
assert.strictEqual(activeTabChangeCount, 0);
540+
541+
// Switching active tab works
542+
tab1.isActive = false;
543+
tab2.isActive = true;
544+
extHostEditorTabs.$acceptTabUpdate(12, tab1);
545+
extHostEditorTabs.$acceptTabUpdate(12, tab2);
546+
// The active tab changed so it is fired once
547+
assert.strictEqual(activeTabChangeCount, 1);
548+
549+
//Closing tabs out works
550+
tab3.isActive = true;
551+
extHostEditorTabs.$acceptEditorTabModel([{
552+
isActive: true,
553+
viewColumn: 0,
554+
groupId: 12,
555+
tabs: [tab3]
556+
}]);
557+
// Accepting a model doesn't fire an active tab change event
558+
assert.strictEqual(activeTabChangeCount, 1);
559+
tab3.label = 'Foobar';
560+
extHostEditorTabs.$acceptTabUpdate(12, tab3);
561+
// Something related to the active tab changed
562+
assert.strictEqual(activeTabChangeCount, 2);
563+
});
496564
});

src/vscode-dts/vscode.proposed.tabs.d.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ declare module 'vscode' {
7171
readonly kind: TabKindText | TabKindTextDiff | TabKindCustom | TabKindWebview | TabKindNotebook | TabKindNotebookDiff | TabKindTerminal | unknown;
7272

7373
/**
74-
* Whether or not the tab is currently active
75-
* Dictated by being the selected tab in the group
74+
* Whether or not the tab is currently active.
75+
* This is dictated by being the selected tab in the group
7676
*/
7777
readonly isActive: boolean;
7878

@@ -137,14 +137,20 @@ declare module 'vscode' {
137137
/**
138138
* An {@link Event} which fires when a group changes.
139139
*/
140-
// TODO@API add TabGroup instance
141-
readonly onDidChangeTabGroup: Event<void>;
140+
readonly onDidChangeTabGroups: Event<TabGroup[]>;
142141

143142
/**
144143
* An {@link Event} which fires when a tab changes.
145144
*/
146145
readonly onDidChangeTabs: Event<Tab[]>;
147146

147+
/**
148+
* An {@link Event} which fires when an active tab changes.
149+
* Similar to {@link TabGroup.onDidChangeTabs} but only on tabs
150+
* with isActive equal to true.
151+
*/
152+
readonly onDidChangeActiveTab: Event<Tab>;
153+
148154
/**
149155
* An {@link Event} which fires when the active group changes.
150156
* This does not fire when the properties within the group change.

0 commit comments

Comments
 (0)