Skip to content

Commit 18948aa

Browse files
authored
Wait for chat session contribution registration (microsoft#259641)
* revert microsoft#259126 * move extensionPoint handler callback * await chatSessionsService#canResolveContentProvider when loading in chat session editor * fix test
1 parent b829292 commit 18948aa

File tree

7 files changed

+41
-59
lines changed

7 files changed

+41
-59
lines changed

src/vs/workbench/contrib/chat/browser/actions/chatActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ export function registerChatActions() {
567567
// Use the new Promise-based API to get chat sessions
568568
const cancellationToken = new CancellationTokenSource();
569569
try {
570-
const providers = await chatSessionsService.getChatSessionContributions();
570+
const providers = chatSessionsService.getChatSessionContributions();
571571
const providerNSessions: { providerType: string; session: IChatSessionItem }[] = [];
572572

573573
for (const provider of providers) {

src/vs/workbench/contrib/chat/browser/chatEditor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class ChatEditor extends EditorPane {
130130
if (input.resource.scheme === Schemas.vscodeChatSession) {
131131
const identifier = ChatSessionUri.parse(input.resource);
132132
if (identifier) {
133-
const contributions = await this.chatSessionsService.getChatSessionContributions([input.resource.authority]);
133+
await this.chatSessionsService.canResolveContentProvider(input.resource.authority);
134+
const contributions = this.chatSessionsService.getChatSessionContributions();
134135
const contribution = contributions.find(c => c.type === identifier.chatSessionType);
135136
if (contribution) {
136137
this.widget.lockToCodingAgent(contribution.name, contribution.displayName);

src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@ import { ContextKeyExpr, IContextKeyService } from '../../../../platform/context
1313
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
1414
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
1515
import { ILogService } from '../../../../platform/log/common/log.js';
16-
import { Registry } from '../../../../platform/registry/common/platform.js';
17-
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js';
1816
import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';
1917
import { IExtensionService, isProposedApiEnabled } from '../../../services/extensions/common/extensions.js';
2018
import { ExtensionsRegistry } from '../../../services/extensions/common/extensionsRegistry.js';
21-
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
2219
import { IChatWidgetService } from '../browser/chat.js';
2320
import { ChatEditorInput } from '../browser/chatEditorInput.js';
2421
import { IChatAgentData, IChatAgentImplementation, IChatAgentRequest, IChatAgentResult, IChatAgentService } from '../common/chatAgents.js';
@@ -68,42 +65,6 @@ const extensionPoint = ExtensionsRegistry.registerExtensionPoint<IChatSessionsEx
6865
}
6966
});
7067

71-
export class ChatSessionsContribution extends Disposable implements IWorkbenchContribution {
72-
constructor(
73-
@ILogService private readonly logService: ILogService,
74-
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
75-
) {
76-
super();
77-
78-
extensionPoint.setHandler(extensions => {
79-
for (const ext of extensions) {
80-
if (!isProposedApiEnabled(ext.description, 'chatSessionsProvider')) {
81-
continue;
82-
}
83-
if (!Array.isArray(ext.value)) {
84-
continue;
85-
}
86-
for (const contribution of ext.value) {
87-
const c: IChatSessionsExtensionPoint = {
88-
id: contribution.id,
89-
type: contribution.type,
90-
name: contribution.name,
91-
displayName: contribution.displayName,
92-
description: contribution.description,
93-
when: contribution.when,
94-
extensionDescription: ext.description,
95-
};
96-
this.logService.info(`Registering chat session from extension contribution: ${c.displayName} (id='${c.type}' name='${c.name}')`);
97-
this._register(this.chatSessionsService.registerContribution(c)); // TODO: Is it for contribution to own this? I think not
98-
}
99-
}
100-
});
101-
}
102-
}
103-
104-
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
105-
workbenchRegistry.registerWorkbenchContribution(ChatSessionsContribution, LifecyclePhase.Restored);
106-
10768
class ContributedChatSessionData implements IDisposable {
10869
private readonly _disposableStore: DisposableStore;
10970

@@ -148,6 +109,29 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
148109
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
149110
) {
150111
super();
112+
this._register(extensionPoint.setHandler(extensions => {
113+
for (const ext of extensions) {
114+
if (!isProposedApiEnabled(ext.description, 'chatSessionsProvider')) {
115+
continue;
116+
}
117+
if (!Array.isArray(ext.value)) {
118+
continue;
119+
}
120+
for (const contribution of ext.value) {
121+
const c: IChatSessionsExtensionPoint = {
122+
id: contribution.id,
123+
type: contribution.type,
124+
name: contribution.name,
125+
displayName: contribution.displayName,
126+
description: contribution.description,
127+
when: contribution.when,
128+
extensionDescription: ext.description,
129+
};
130+
this._logService.info(`Registering chat session from extension contribution: ${c.displayName} (id='${c.type}' name='${c.name}')`);
131+
this._register(this.registerContribution(c));
132+
}
133+
}
134+
}));
151135

152136
// Listen for context changes and re-evaluate contributions
153137
this._register(Event.filter(this._contextKeyService.onDidChangeContext, e => e.affectsSome(this._contextKeys))(() => {
@@ -295,11 +279,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
295279
return disposable;
296280
}
297281

298-
async getChatSessionContributions(waitForActivation?: string[]): Promise<IChatSessionsExtensionPoint[]> {
299-
await this._extensionService.whenInstalledExtensionsRegistered();
300-
if (waitForActivation) {
301-
await Promise.all(waitForActivation.map(id => this._extensionService.activateByEvent(`onChatSession:${id}`)));
302-
}
282+
getChatSessionContributions(): IChatSessionsExtensionPoint[] {
303283
return Array.from(this._contributions.values()).filter(contribution =>
304284
this._isContributionAvailable(contribution)
305285
);
@@ -314,7 +294,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
314294
}
315295

316296
async canResolveItemProvider(chatViewType: string) {
317-
// First check if the contribution is available based on its when clause
297+
await this._extensionService.whenInstalledExtensionsRegistered();
318298
const contribution = this._contributions.get(chatViewType);
319299
if (contribution && !this._isContributionAvailable(contribution)) {
320300
return false;
@@ -324,7 +304,6 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
324304
return true;
325305
}
326306

327-
await this._extensionService.whenInstalledExtensionsRegistered();
328307
await this._extensionService.activateByEvent(`onChatSession:${chatViewType}`);
329308

330309
return this._itemsProviders.has(chatViewType);
@@ -335,7 +314,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
335314
}
336315

337316
async canResolveContentProvider(chatViewType: string) {
338-
// First check if the contribution is available based on its when clause
317+
await this._extensionService.whenInstalledExtensionsRegistered();
339318
const contribution = this._contributions.get(chatViewType);
340319
if (contribution && !this._isContributionAvailable(contribution)) {
341320
return false;
@@ -345,7 +324,6 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
345324
return true;
346325
}
347326

348-
await this._extensionService.whenInstalledExtensionsRegistered();
349327
await this._extensionService.activateByEvent(`onChatSession:${chatViewType}`);
350328

351329
return this._contentProviders.has(chatViewType);

src/vs/workbench/contrib/chat/browser/chatSessions.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -715,16 +715,16 @@ class SessionsViewPane extends ViewPane {
715715
}
716716
}
717717

718-
private async getProviderDisplayName(): Promise<string> {
719-
const contributions = await this.chatSessionsService.getChatSessionContributions();
718+
private getProviderDisplayName(): string {
719+
const contributions = this.chatSessionsService.getChatSessionContributions();
720720
const contribution = contributions.find(c => c.type === this.provider.chatSessionType);
721721
if (contribution) {
722722
return contribution.displayName;
723723
}
724724
return '';
725725
}
726726

727-
private async showEmptyMessage(): Promise<void> {
727+
private showEmptyMessage(): void {
728728
if (!this.messageElement) {
729729
return;
730730
}
@@ -735,7 +735,7 @@ class SessionsViewPane extends ViewPane {
735735
return;
736736
}
737737

738-
const providerName = await this.getProviderDisplayName();
738+
const providerName = this.getProviderDisplayName();
739739
if (!providerName) {
740740
return;
741741
}
@@ -775,15 +775,15 @@ class SessionsViewPane extends ViewPane {
775775
* Updates the empty state message based on current tree data.
776776
* Uses the tree's existing data to avoid redundant provider calls.
777777
*/
778-
private async updateEmptyStateMessage(): Promise<void> {
778+
private updateEmptyStateMessage(): void {
779779
try {
780780
// Check if the tree has the provider node and get its children count
781781
if (this.tree?.hasNode(this.provider)) {
782782
const providerNode = this.tree.getNode(this.provider);
783783
const childCount = providerNode.children?.length || 0;
784784

785785
if (childCount === 0) {
786-
await this.showEmptyMessage();
786+
this.showEmptyMessage();
787787
} else {
788788
this.hideMessage();
789789
}
@@ -814,7 +814,7 @@ class SessionsViewPane extends ViewPane {
814814
);
815815

816816
// Check for empty state after refresh using tree data
817-
await this.updateEmptyStateMessage();
817+
this.updateEmptyStateMessage();
818818
} catch (error) {
819819
// Log error but don't throw to avoid breaking the UI
820820
this.logService.error('Error refreshing chat sessions tree:', error);
@@ -842,7 +842,7 @@ class SessionsViewPane extends ViewPane {
842842
);
843843

844844
// Check for empty state after loading using tree data
845-
await this.updateEmptyStateMessage();
845+
this.updateEmptyStateMessage();
846846
} catch (error) {
847847
// Log error but don't throw to avoid breaking the UI
848848
this.logService.error('Error loading chat sessions data:', error);

src/vs/workbench/contrib/chat/common/chatSessionsService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export interface IChatSessionsService {
6868
readonly onDidChangeSessionItems: Event<string>;
6969
readonly onDidChangeAvailability: Event<void>;
7070
registerContribution(contribution: IChatSessionsExtensionPoint): IDisposable;
71-
getChatSessionContributions(waitForActivation?: string[]): Promise<IChatSessionsExtensionPoint[]>;
71+
getChatSessionContributions(): IChatSessionsExtensionPoint[];
7272
canResolveItemProvider(chatSessionType: string): Promise<boolean>;
7373
canResolveContentProvider(chatSessionType: string): Promise<boolean>;
7474
getChatSessionItemProviders(): IChatSessionItemProvider[];

src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ suite('ChatEditingService', function () {
104104

105105
chatService = insta.get(IChatService);
106106

107+
store.add(insta.get(IChatSessionsService) as ChatSessionsService); // Needs to be disposed in between test runs to clear extensionPoint contribution
108+
107109
const chatAgentService = insta.get(IChatAgentService);
108110

109111
const agent: IChatAgentImplementation = {

src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ suite('InlineChatSession', function () {
141141

142142
instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection));
143143
inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService));
144+
store.add(instaService.get(IChatSessionsService) as ChatSessionsService); // Needs to be disposed in between test runs to clear extensionPoint contribution
144145

145146
instaService.get(IChatAgentService).registerDynamicAgent({
146147
extensionId: nullExtensionDescription.identifier,

0 commit comments

Comments
 (0)