diff --git a/src/extension/chatSessions/vscode-node/chatSessions.ts b/src/extension/chatSessions/vscode-node/chatSessions.ts index ad69e529b8..7753eec7d0 100644 --- a/src/extension/chatSessions/vscode-node/chatSessions.ts +++ b/src/extension/chatSessions/vscode-node/chatSessions.ts @@ -126,7 +126,6 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib const copilotcliSessionIsolationManager = copilotcliAgentInstaService.createInstance(CopilotCLISessionIsolationManager); const copilotcliSessionItemProvider = this._register(copilotcliAgentInstaService.createInstance(CopilotCLIChatSessionItemProvider)); - this._register(vscode.chat.registerChatSessionItemProvider(this.copilotcliSessionType, copilotcliSessionItemProvider)); const imageSupport = copilotcliAgentInstaService.createInstance(CopilotCLIImageSupport); const promptResolver = copilotcliAgentInstaService.createInstance(CopilotCLIPromptResolver, imageSupport); const copilotcliChatSessionContentProvider = copilotcliAgentInstaService.createInstance(CopilotCLIChatSessionContentProvider, copilotcliSessionIsolationManager); diff --git a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts index 5aa7812a13..228123f131 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts @@ -17,7 +17,7 @@ import { ITelemetryService } from '../../../platform/telemetry/common/telemetry' import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService'; import { disposableTimeout, raceCancellation } from '../../../util/vs/base/common/async'; import { isCancellationError } from '../../../util/vs/base/common/errors'; -import { Emitter, Event } from '../../../util/vs/base/common/event'; +import { Emitter } from '../../../util/vs/base/common/event'; import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from '../../../util/vs/base/common/lifecycle'; import { autorun } from '../../../util/vs/base/common/observable'; import { extUri, isEqual } from '../../../util/vs/base/common/resources'; @@ -113,12 +113,8 @@ function escapeXml(text: string): string { .replace(/'/g, '''); } -export class CopilotCLIChatSessionItemProvider extends Disposable implements vscode.ChatSessionItemProvider { - private readonly _onDidChangeChatSessionItems = this._register(new Emitter()); - public readonly onDidChangeChatSessionItems: Event = this._onDidChangeChatSessionItems.event; - - private readonly _onDidCommitChatSessionItem = this._register(new Emitter<{ original: vscode.ChatSessionItem; modified: vscode.ChatSessionItem }>()); - public readonly onDidCommitChatSessionItem: Event<{ original: vscode.ChatSessionItem; modified: vscode.ChatSessionItem }> = this._onDidCommitChatSessionItem.event; +export class CopilotCLIChatSessionItemProvider extends Disposable { + private readonly controller: vscode.ChatSessionItemController; constructor( @ICopilotCLISessionService private readonly copilotcliSessionService: ICopilotCLISessionService, @@ -130,27 +126,45 @@ export class CopilotCLIChatSessionItemProvider extends Disposable implements vsc ) { super(); this._register(this.terminalIntegration); + + this.controller = this._register(vscode.chat.createChatSessionItemController( + 'copilotcli', + () => this.refresh() + )); + this._register(this.copilotcliSessionService.onDidChangeSessions(() => { this.notifySessionsChange(); })); } public notifySessionsChange(): void { - this._onDidChangeChatSessionItems.fire(); + void this.controller.refreshHandler(); } public swap(original: vscode.ChatSessionItem, modified: vscode.ChatSessionItem): void { - this._onDidCommitChatSessionItem.fire({ original, modified }); + this.controller.items.delete(original.resource); + this.controller.items.add(modified); } - public async provideChatSessionItems(token: vscode.CancellationToken): Promise { - const sessions = await this.copilotcliSessionService.getAllSessions(this.shouldShowSession.bind(this), token); - const diskSessions = await Promise.all(sessions.map(async session => this._toChatSessionItem(session))); + private async refresh(): Promise { + const ctx = new vscode.CancellationTokenSource(); + try { + const sessions = await this.copilotcliSessionService.getAllSessions(this.shouldShowSession.bind(this), ctx.token); + const items: vscode.ChatSessionItem[] = []; + + for (const session of sessions) { + const item = await this._toChatSessionItem(session); + items.push(item); + } - const count = diskSessions.length; - this.commandExecutionService.executeCommand('setContext', 'github.copilot.chat.cliSessionsEmpty', count === 0); + this.controller.items.replace(items); - return diskSessions; + const count = items.length; + this.commandExecutionService.executeCommand('setContext', 'github.copilot.chat.cliSessionsEmpty', count === 0); + } + finally { + ctx.dispose(); + } } private shouldShowSession(sessionId: string): boolean | undefined { @@ -177,27 +191,25 @@ export class CopilotCLIChatSessionItemProvider extends Disposable implements vsc const label = session.label; + const item = this.controller.createChatSessionItem(resource, label); + // Badge - let badge: vscode.MarkdownString | undefined; if (worktreeProperties?.branchName) { - badge = new vscode.MarkdownString(`$(worktree) ${worktreeProperties.branchName}`); + const badge = new vscode.MarkdownString(`$(worktree) ${worktreeProperties.branchName}`); badge.supportThemeIcons = true; + item.badge = badge; } // Statistics - const changes = await this.worktreeManager.getWorktreeChanges(session.id); + item.changes = await this.worktreeManager.getWorktreeChanges(session.id); // Status - const status = session.status ?? vscode.ChatSessionStatus.Completed; + item.status = session.status ?? vscode.ChatSessionStatus.Completed; - return { - resource, - label, - badge, - timing: session.timing, - changes, - status - } satisfies vscode.ChatSessionItem; + // Timing + item.timing = session.timing; + + return item; } public async createCopilotCLITerminal(): Promise {