From cfe17d64861cede9d4618378e88a27c3af5e2185 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 22 Jan 2026 21:31:40 -0800 Subject: [PATCH] Don't keep textmodels for all codeblocks in a chat session (#289821) Revert "Revert "Only keep around text models for live code blocks" (#289603)" This reverts commit f3a7ce35470b1ae3fb47177d35e3964a24094372. --- .../chatMarkdownContentPart.ts | 10 +++---- .../widget/chatContentParts/codeBlockPart.ts | 4 +-- .../chat/common/model/chatViewModel.ts | 26 ------------------ .../contrib/chat/common/widget/annotations.ts | 27 +------------------ 4 files changed, 7 insertions(+), 60 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownContentPart.ts index d060546f925..4951f32d5f9 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownContentPart.ts @@ -382,12 +382,10 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP private renderCodeBlock(data: ICodeBlockData, text: string, isComplete: boolean, currentWidth: number): IDisposableReference { const ref = this.editorPool.get(); const editorInfo = ref.object; - if (isResponseVM(data.element)) { - this.codeBlockModelCollection.update(data.element.sessionResource, data.element, data.codeBlockIndex, { text, languageId: data.languageId, isComplete }).then((e) => { - // Update the existing object's codemapperUri - this._codeblocks[data.codeBlockPartIndex].codemapperUri = e.codemapperUri; - }); - } + this.codeBlockModelCollection.update(data.element.sessionResource, data.element, data.codeBlockIndex, { text, languageId: data.languageId, isComplete }).then((e) => { + // Update the existing object's codemapperUri + this._codeblocks[data.codeBlockPartIndex].codemapperUri = e.codemapperUri; + }); editorInfo.render(data, currentWidth); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/codeBlockPart.ts index 45b20d570ee..5286bcef152 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/codeBlockPart.ts @@ -65,7 +65,7 @@ import { getSimpleEditorOptions } from '../../../../codeEditor/browser/simpleEdi import { IMarkdownVulnerability } from '../../../common/widget/annotations.js'; import { ChatContextKeys } from '../../../common/actions/chatContextKeys.js'; import { IChatResponseModel, IChatTextEditGroup } from '../../../common/model/chatModel.js'; -import { IChatResponseViewModel, isRequestVM, isResponseVM } from '../../../common/model/chatViewModel.js'; +import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from '../../../common/model/chatViewModel.js'; import { ChatTreeItem } from '../../chat.js'; import { IChatRendererDelegate } from '../chatListRenderer.js'; import { ChatEditorOptions } from '../chatOptions.js'; @@ -78,7 +78,7 @@ const $ = dom.$; export interface ICodeBlockData { readonly codeBlockIndex: number; readonly codeBlockPartIndex: number; - readonly element: unknown; + readonly element: IChatRequestViewModel | IChatResponseViewModel; readonly textModel: Promise | undefined; readonly languageId: string; diff --git a/src/vs/workbench/contrib/chat/common/model/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/model/chatViewModel.ts index f788be67742..2e75fe9d44e 100644 --- a/src/vs/workbench/contrib/chat/common/model/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/model/chatViewModel.ts @@ -7,7 +7,6 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; import { Disposable, dispose } from '../../../../../base/common/lifecycle.js'; -import * as marked from '../../../../../base/common/marked/marked.js'; import { IObservable } from '../../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; @@ -16,7 +15,6 @@ import { IChatRequestVariableEntry } from '../attachments/chatVariableEntries.js import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatCodeCitation, IChatContentReference, IChatFollowup, IChatMcpServersStarting, IChatProgressMessage, IChatResponseErrorDetails, IChatTask, IChatUsedContext } from '../chatService/chatService.js'; import { getFullyQualifiedId, IChatAgentCommand, IChatAgentData, IChatAgentNameService, IChatAgentResult } from '../participants/chatAgents.js'; import { IParsedChatRequest } from '../requestParser/chatParserTypes.js'; -import { annotateVulnerabilitiesInText } from '../widget/annotations.js'; import { CodeBlockModelCollection } from '../widget/codeBlockModelCollection.js'; import { IChatModel, IChatProgressRenderableResponseContent, IChatRequestDisablement, IChatRequestModel, IChatResponseModel, IChatTextEditGroup, IResponse } from './chatModel.js'; import { ChatStreamStatsTracker, IChatStreamStats } from './chatStreamStats.js'; @@ -270,7 +268,6 @@ export class ChatViewModel extends Disposable implements IChatViewModel { _model.getRequests().forEach((request, i) => { const requestModel = this.instantiationService.createInstance(ChatRequestViewModel, request); this._items.push(requestModel); - this.updateCodeBlockTextModels(requestModel); if (request.response) { this.onAddResponse(request.response); @@ -282,7 +279,6 @@ export class ChatViewModel extends Disposable implements IChatViewModel { if (e.kind === 'addRequest') { const requestModel = this.instantiationService.createInstance(ChatRequestViewModel, e.request); this._items.push(requestModel); - this.updateCodeBlockTextModels(requestModel); if (e.request.response) { this.onAddResponse(e.request.response); @@ -317,13 +313,9 @@ export class ChatViewModel extends Disposable implements IChatViewModel { private onAddResponse(responseModel: IChatResponseModel) { const response = this.instantiationService.createInstance(ChatResponseViewModel, responseModel, this); this._register(response.onDidChange(() => { - if (response.isComplete) { - this.updateCodeBlockTextModels(response); - } return this._onDidChange.fire(null); })); this._items.push(response); - this.updateCodeBlockTextModels(response); } getItems(): (IChatRequestViewModel | IChatResponseViewModel)[] { @@ -352,24 +344,6 @@ export class ChatViewModel extends Disposable implements IChatViewModel { super.dispose(); dispose(this._items.filter((item): item is ChatResponseViewModel => item instanceof ChatResponseViewModel)); } - - updateCodeBlockTextModels(model: IChatRequestViewModel | IChatResponseViewModel) { - let content: string; - if (isRequestVM(model)) { - content = model.messageText; - } else { - content = annotateVulnerabilitiesInText(model.response.value).map(x => x.content.value).join(''); - } - - let codeBlockIndex = 0; - marked.walkTokens(marked.lexer(content), token => { - if (token.type === 'code') { - const lang = token.lang || ''; - const text = token.text; - this.codeBlockModelCollection.update(this._model.sessionResource, model, codeBlockIndex++, { text, languageId: lang, isComplete: true }); - } - }); - } } export class ChatRequestViewModel implements IChatRequestViewModel { diff --git a/src/vs/workbench/contrib/chat/common/widget/annotations.ts b/src/vs/workbench/contrib/chat/common/widget/annotations.ts index 600decb9f36..a1dbed9eb89 100644 --- a/src/vs/workbench/contrib/chat/common/widget/annotations.ts +++ b/src/vs/workbench/contrib/chat/common/widget/annotations.ts @@ -9,7 +9,7 @@ import { URI } from '../../../../../base/common/uri.js'; import { IRange } from '../../../../../editor/common/core/range.js'; import { isLocation } from '../../../../../editor/common/languages.js'; import { IChatProgressRenderableResponseContent, IChatProgressResponseContent, appendMarkdownString, canMergeMarkdownStrings } from '../model/chatModel.js'; -import { IChatAgentVulnerabilityDetails, IChatMarkdownContent } from '../chatService/chatService.js'; +import { IChatAgentVulnerabilityDetails } from '../chatService/chatService.js'; export const contentRefUrl = 'http://_vscodecontentref_'; // must be lowercase for URI @@ -79,31 +79,6 @@ export interface IMarkdownVulnerability { readonly description: string; readonly range: IRange; } - -export function annotateVulnerabilitiesInText(response: ReadonlyArray): readonly IChatMarkdownContent[] { - const result: IChatMarkdownContent[] = []; - for (const item of response) { - const previousItem = result[result.length - 1]; - if (item.kind === 'markdownContent') { - if (previousItem?.kind === 'markdownContent') { - result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + item.content.value, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' }; - } else { - result.push(item); - } - } else if (item.kind === 'markdownVuln') { - const vulnText = encodeURIComponent(JSON.stringify(item.vulnerabilities)); - const markdownText = `${item.content.value}`; - if (previousItem?.kind === 'markdownContent') { - result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + markdownText, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' }; - } else { - result.push({ content: new MarkdownString(markdownText), kind: 'markdownContent' }); - } - } - } - - return result; -} - export function extractCodeblockUrisFromText(text: string): { uri: URI; isEdit?: boolean; textWithoutResult: string } | undefined { const match = /(.*?)<\/vscode_codeblock_uri>/ms.exec(text); if (match) {