diff --git a/src/vs/workbench/api/browser/mainThreadChatSessions.ts b/src/vs/workbench/api/browser/mainThreadChatSessions.ts index da9a664e813..47bff4e1d83 100644 --- a/src/vs/workbench/api/browser/mainThreadChatSessions.ts +++ b/src/vs/workbench/api/browser/mainThreadChatSessions.ts @@ -354,13 +354,9 @@ class MainThreadChatSessionItemController extends Disposable implements IChatSes this._onDidChangeChatSessionItems.fire(); } - updateItem(item: IChatSessionItem): void { - if (this._items.has(item.resource)) { - this._items.set(item.resource, item); - this._onDidChangeChatSessionItems.fire(); - } else { - console.warn(`Item with resource ${item.resource.toString()} does not exist. Skipping update.`); - } + addOrUpdateItem(item: IChatSessionItem): void { + this._items.set(item.resource, item); + this._onDidChangeChatSessionItems.fire(); } fireOnDidChangeChatSessionItems(): void { @@ -440,8 +436,17 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat )); } + private getController(handle: number): MainThreadChatSessionItemController { + const registration = this._itemControllerRegistrations.get(handle); + if (!registration) { + throw new Error(`No chat session controller registered for handle ${handle}`); + } + return registration.controller; + } + $onDidChangeChatSessionItems(handle: number): void { - this._itemControllerRegistrations.get(handle)?.controller.fireOnDidChangeChatSessionItems(); + const controller = this.getController(handle); + controller.fireOnDidChangeChatSessionItems(); } private async _resolveSessionItem(item: Dto): Promise { @@ -474,31 +479,20 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat }; } - async $setChatSessionItems(handle: number, items: Dto[]): Promise { - const registration = this._itemControllerRegistrations.get(handle); - if (!registration) { - this._logService.warn(`No chat session controller registered for handle ${handle}`); - return; - } - + async $setChatSessionItems(controllerHandle: number, items: Dto[]): Promise { + const controller = this.getController(controllerHandle); const resolvedItems = await Promise.all(items.map(item => this._resolveSessionItem(item))); - registration.controller.setItems(resolvedItems); + controller.setItems(resolvedItems); } - async $updateChatSessionItem(controllerHandle: number, item: Dto): Promise { - const registration = this._itemControllerRegistrations.get(controllerHandle); - if (!registration) { - this._logService.warn(`No chat session controller registered for handle ${controllerHandle}`); - return; - } - + async $addOrUpdateChatSessionItem(controllerHandle: number, item: Dto): Promise { + const controller = this.getController(controllerHandle); const resolvedItem = await this._resolveSessionItem(item); - registration.controller.updateItem(resolvedItem); + controller.addOrUpdateItem(resolvedItem); } $onDidChangeChatSessionOptions(handle: number, sessionResourceComponents: UriComponents, updates: ReadonlyArray<{ optionId: string; value: string }>): void { const sessionResource = URI.revive(sessionResourceComponents); - this._chatSessionsService.notifySessionOptionsChange(sessionResource, updates); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 422aa1b5827..d12f99c0051 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -3418,7 +3418,7 @@ export interface MainThreadChatSessionsShape extends IDisposable { $registerChatSessionItemController(handle: number, chatSessionType: string): void; $unregisterChatSessionItemController(handle: number): void; $setChatSessionItems(handle: number, items: Dto[]): Promise; - $updateChatSessionItem(handle: number, item: Dto): Promise; + $addOrUpdateChatSessionItem(handle: number, item: Dto): Promise; $onDidChangeChatSessionItems(handle: number): void; $onDidCommitChatSessionItem(handle: number, original: UriComponents, modified: UriComponents): void; $registerChatSessionContentProvider(handle: number, chatSessionScheme: string): void; diff --git a/src/vs/workbench/api/common/extHostChatSessions.ts b/src/vs/workbench/api/common/extHostChatSessions.ts index cec3c62b015..8868ad66110 100644 --- a/src/vs/workbench/api/common/extHostChatSessions.ts +++ b/src/vs/workbench/api/common/extHostChatSessions.ts @@ -176,12 +176,17 @@ class ChatSessionItemImpl implements vscode.ChatSessionItem { } } +interface SessionCollectionListeners { + onItemsChanged(): void; + onItemAddedOrUpdated(item: vscode.ChatSessionItem): void; +} + class ChatSessionItemCollectionImpl implements vscode.ChatSessionItemCollection { readonly #items = new ResourceMap(); - #onItemsChanged: () => void; + readonly #callbacks: SessionCollectionListeners; - constructor(onItemsChanged: () => void) { - this.#onItemsChanged = onItemsChanged; + constructor(callbacks: SessionCollectionListeners) { + this.#callbacks = callbacks; } get size(): number { @@ -197,7 +202,7 @@ class ChatSessionItemCollectionImpl implements vscode.ChatSessionItemCollection for (const item of items) { this.#items.set(item.resource, item); } - this.#onItemsChanged(); + this.#callbacks.onItemsChanged(); } forEach(callback: (item: vscode.ChatSessionItem, collection: vscode.ChatSessionItemCollection) => unknown, thisArg?: any): void { @@ -207,13 +212,20 @@ class ChatSessionItemCollectionImpl implements vscode.ChatSessionItemCollection } add(item: vscode.ChatSessionItem): void { + const existing = this.#items.get(item.resource); + if (existing && existing === item) { + // We're adding the same item again + return; + } + this.#items.set(item.resource, item); - this.#onItemsChanged(); + this.#callbacks.onItemAddedOrUpdated(item); } delete(resource: vscode.Uri): void { - this.#items.delete(resource); - this.#onItemsChanged(); + if (this.#items.delete(resource)) { + this.#callbacks.onItemsChanged(); + } } get(resource: vscode.Uri): vscode.ChatSessionItem | undefined { @@ -324,8 +336,10 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio const onDidChangeChatSessionItemStateEmitter = disposables.add(new Emitter()); - const collection = new ChatSessionItemCollectionImpl(() => { + const collection = new ChatSessionItemCollectionImpl({ // Noop for providers + onItemsChanged: () => { }, + onItemAddedOrUpdated: () => { } }); // Helper to push items to main thread @@ -400,7 +414,12 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio void this._proxy.$setChatSessionItems(controllerHandle, items); }; - const collection = new ChatSessionItemCollectionImpl(onItemsChanged); + const collection = new ChatSessionItemCollectionImpl({ + onItemsChanged, + onItemAddedOrUpdated: (item: vscode.ChatSessionItem) => { + void this._proxy.$addOrUpdateChatSessionItem(controllerHandle, typeConvert.ChatSessionItem.from(item)); + } + }); const controller = Object.freeze({ id, @@ -420,7 +439,10 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio } const item = new ChatSessionItemImpl(resource, label, () => { - void this._proxy.$updateChatSessionItem(controllerHandle, typeConvert.ChatSessionItem.from(item)); + // Make sure the item really is in the collection. If not we don't need to transmit it to the main thread yet + if (collection.get(resource) === item) { + void this._proxy.$addOrUpdateChatSessionItem(controllerHandle, typeConvert.ChatSessionItem.from(item)); + } }); return item; },