Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/vs/workbench/api/common/extHostTypeConverters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2297,17 +2297,17 @@ export namespace InteractiveEditorResponseFeedbackKind {
export namespace ChatResponseProgress {
export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatProgressDto {
if ('placeholder' in progress && 'resolvedContent' in progress) {
return { placeholder: progress.placeholder, kind: 'asyncContent' } satisfies extHostProtocol.IChatAsyncContentDto;
return { content: progress.placeholder, kind: 'asyncContent' } satisfies extHostProtocol.IChatAsyncContentDto;
} else if ('markdownContent' in progress) {
checkProposedApiEnabled(extension, 'chatAgents2Additions');
return { content: MarkdownString.from(progress.markdownContent), kind: 'content' };
return { content: MarkdownString.from(progress.markdownContent), kind: 'markdownContent' };
} else if ('content' in progress) {
if (typeof progress.content === 'string') {
return { content: progress.content, kind: 'content' };
}

checkProposedApiEnabled(extension, 'chatAgents2Additions');
return { content: MarkdownString.from(progress.content), kind: 'content' };
return { content: MarkdownString.from(progress.content), kind: 'markdownContent' };
} else if ('documents' in progress) {
return {
documents: progress.documents.map(d => ({
Expand Down
16 changes: 12 additions & 4 deletions src/vs/workbench/contrib/chat/browser/chat.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { ChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
import { ChatWelcomeMessageModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
import { ChatProviderService, IChatProviderService } from 'vs/workbench/contrib/chat/common/chatProvider';
import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
import { alertFocusChange } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions';
Expand Down Expand Up @@ -243,7 +243,11 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable {
const defaultAgent = chatAgentService.getDefaultAgent();
const agents = chatAgentService.getAgents();
if (defaultAgent?.metadata.helpTextPrefix) {
progress.report({ content: defaultAgent.metadata.helpTextPrefix, kind: 'content' });
if (isMarkdownString(defaultAgent.metadata.helpTextPrefix)) {
progress.report({ content: defaultAgent.metadata.helpTextPrefix, kind: 'markdownContent' });
} else {
progress.report({ content: defaultAgent.metadata.helpTextPrefix, kind: 'content' });
}
progress.report({ content: '\n\n', kind: 'content' });
}

Expand All @@ -263,10 +267,14 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable {

return agentLine + '\n' + commandText;
}))).join('\n');
progress.report({ content: new MarkdownString(agentText, { isTrusted: { enabledCommands: [SubmitAction.ID] } }), kind: 'content' });
progress.report({ content: new MarkdownString(agentText, { isTrusted: { enabledCommands: [SubmitAction.ID] } }), kind: 'markdownContent' });
if (defaultAgent?.metadata.helpTextPostfix) {
progress.report({ content: '\n\n', kind: 'content' });
progress.report({ content: defaultAgent.metadata.helpTextPostfix, kind: 'content' });
if (isMarkdownString(defaultAgent.metadata.helpTextPostfix)) {
progress.report({ content: defaultAgent.metadata.helpTextPostfix, kind: 'markdownContent' });
} else {
progress.report({ content: defaultAgent.metadata.helpTextPostfix, kind: 'content' });
}
}
}));
}
Expand Down
37 changes: 19 additions & 18 deletions src/vs/workbench/contrib/chat/browser/chatListRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions
import { CodeBlockPart, ICodeBlockData, ICodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents';
import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IPlaceholderMarkdownString } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatContentInlineReference, IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView';
Expand Down Expand Up @@ -319,7 +319,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
const markdown = 'kind' in element.message ?
element.message.message :
convertParsedRequestToMarkdown(element.message);
this.basicRenderElement([new MarkdownString(markdown)], element, index, templateData);
this.basicRenderElement([{ content: new MarkdownString(markdown), kind: 'markdownContent' }], element, index, templateData);
} else {
this.renderWelcomeMessage(element, templateData);
}
Expand Down Expand Up @@ -400,7 +400,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
}
}

private basicRenderElement(value: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData>, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) {
private basicRenderElement(value: ReadonlyArray<Exclude<IChatProgressResponseContent, IChatContentInlineReference>>, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) {
const fillInIncompleteTokens = isResponseVM(element) && (!element.isComplete || element.isCanceled || element.errorDetails?.responseIsFiltered || element.errorDetails?.responseIsIncomplete);

dom.clearNode(templateData.value);
Expand All @@ -414,9 +414,11 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch

let fileTreeIndex = 0;
for (const data of value) {
const result = 'value' in data
? this.renderMarkdown(data, element, templateData, fillInIncompleteTokens)
: this.renderTreeData(data, element, templateData, fileTreeIndex++);
const result = data.kind === 'treeData'
? this.renderTreeData(data.treeData, element, templateData, fileTreeIndex++)
: data.kind === 'markdownContent'
? this.renderMarkdown(data.content, element, templateData, fillInIncompleteTokens)
: this.renderPlaceholder(new MarkdownString(data.content), templateData);
templateData.value.appendChild(result.element);
templateData.elementDisposables.add(result);
}
Expand Down Expand Up @@ -519,10 +521,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
const renderedPart = renderedParts[index];
// Is this part completely new?
if (!renderedPart) {
if (isInteractiveProgressTreeData(part)) {
partsToRender[index] = part;
if (part.kind === 'treeData') {
partsToRender[index] = part.treeData;
} else {
const wordCountResult = this.getDataForProgressiveRender(element, part, { renderedWordCount: 0, lastRenderTime: 0 });
const wordCountResult = this.getDataForProgressiveRender(element, contentToMarkdown(part.content), { renderedWordCount: 0, lastRenderTime: 0 });
if (wordCountResult !== undefined) {
partsToRender[index] = {
renderedWordCount: wordCountResult.actualWordCount,
Expand All @@ -535,13 +537,13 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
}

// Did this part go from being a placeholder string to resolved tree data?
else if (isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart)) {
partsToRender[index] = part;
else if (part.kind === 'treeData' && !isInteractiveProgressTreeData(renderedPart)) {
partsToRender[index] = part.treeData;
}

// Did this part's content change?
else if (!isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart)) {
const wordCountResult = this.getDataForProgressiveRender(element, part, renderedPart);
else if (part.kind !== 'treeData' && !isInteractiveProgressTreeData(renderedPart)) {
const wordCountResult = this.getDataForProgressiveRender(element, contentToMarkdown(part.content), renderedPart);
// Check if there are any new words to render
if (wordCountResult !== undefined && renderedPart.renderedWordCount !== wordCountResult?.actualWordCount) {
partsToRender[index] = {
Expand Down Expand Up @@ -578,8 +580,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
// Avoid doing progressive rendering for multiple markdown parts simultaneously
else if (!hasRenderedOneMarkdownBlock) {
const { value } = wordCountResults[index];
const isPlaceholder = isPlaceholderMarkdown(renderableResponse[index]);
result = isPlaceholder
result = renderableResponse[index].kind === 'asyncContent'
? this.renderPlaceholder(new MarkdownString(value), templateData)
: this.renderMarkdown(new MarkdownString(value), element, templateData, true);
hasRenderedOneMarkdownBlock = true;
Expand Down Expand Up @@ -1278,6 +1279,6 @@ function isInteractiveProgressTreeData(item: IChatResponseProgressFileTreeData |
return 'label' in item;
}

function isPlaceholderMarkdown(item: IPlaceholderMarkdownString | IMarkdownString | IChatResponseProgressFileTreeData): item is IPlaceholderMarkdownString {
return 'isPlaceholder' in item;
function contentToMarkdown(str: string | IMarkdownString): IMarkdownString {
return typeof str === 'string' ? { value: str } : str;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { revive } from 'vs/base/common/marshalling';
import { basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Location } from 'vs/editor/common/languages';
import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatRequestTextPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatContentInlineReference, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatContentInlineReference } from 'vs/workbench/contrib/chat/common/chatService';

const variableRefUrl = 'http://_vscodedecoration_';

Expand Down Expand Up @@ -59,21 +60,21 @@ function renderFileWidget(href: string, a: HTMLAnchorElement): void {

const contentRefUrl = 'http://_vscodecontentref_'; // must be lowercase for URI

export function reduceInlineContentReferences(response: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData | IChatContentInlineReference>): ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData> {
const result: (IMarkdownString | IChatResponseProgressFileTreeData)[] = [];
export function reduceInlineContentReferences(response: ReadonlyArray<IChatProgressResponseContent>): ReadonlyArray<Exclude<IChatProgressResponseContent, IChatContentInlineReference>> {
const result: Exclude<IChatProgressResponseContent, IChatContentInlineReference>[] = [];
for (const item of response) {
const previousItem = result[result.length - 1];
if ('inlineReference' in item) {
if (item.kind === 'inlineReference') {
const location = 'uri' in item.inlineReference ? item.inlineReference : { uri: item.inlineReference };
const printUri = URI.parse(contentRefUrl).with({ fragment: JSON.stringify(location) });
const markdownText = `[${item.name || basename(location.uri)}](${printUri.toString()})`;
if (isMarkdownString(previousItem)) {
result[result.length - 1] = new MarkdownString(previousItem.value + markdownText, { isTrusted: previousItem.isTrusted });
if (previousItem?.kind === 'markdownContent') {
result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + markdownText, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' };
} else {
result.push(new MarkdownString(markdownText));
result.push({ content: new MarkdownString(markdownText), kind: 'markdownContent' });
}
} else if (isMarkdownString(item) && isMarkdownString(previousItem)) {
result[result.length - 1] = new MarkdownString(previousItem.value + item.value, { isTrusted: previousItem.isTrusted });
} else if (item.kind === 'markdownContent' && 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);
}
Expand Down
Loading