Skip to content

Commit

Permalink
Use custom hover for slash command and variable descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Jul 29, 2024
1 parent 9014109 commit ebaf073
Showing 1 changed file with 35 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import * as dom from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Lazy } from 'vs/base/common/lazy';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { revive } from 'vs/base/common/marshalling';
import { URI } from 'vs/base/common/uri';
import { Location } from 'vs/editor/common/languages';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IHoverService } from 'vs/platform/hover/browser/hover';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
Expand All @@ -21,11 +23,10 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
import { ChatAgentHover, getChatAgentHoverOptions } from 'vs/workbench/contrib/chat/browser/chatAgentHover';
import { getFullyQualifiedId, IChatAgentCommand, IChatAgentData, IChatAgentNameService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors';
import { chatAgentLeader, ChatRequestAgentPart, ChatRequestDynamicVariablePart, ChatRequestTextPart, chatSubcommandLeader, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { chatAgentLeader, ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicVariablePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, chatSubcommandLeader, IParsedChatRequest, IParsedChatRequestPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { contentRefUrl } from '../common/annotations';
import { Lazy } from 'vs/base/common/lazy';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';

/** For rendering slash commands, variables */
const decorationRefUrl = `http://_vscodedecoration_`;
Expand Down Expand Up @@ -68,6 +69,10 @@ interface ISlashCommandWidgetArgs {
command: string;
}

interface IDecorationWidgetArgs {
title?: string;
}

export class ChatMarkdownDecorationsRenderer {
constructor(
@IKeybindingService private readonly keybindingService: IKeybindingService,
Expand All @@ -79,6 +84,7 @@ export class ChatMarkdownDecorationsRenderer {
@IChatService private readonly chatService: IChatService,
@IChatWidgetService private readonly chatWidgetService: IChatWidgetService,
@ICommandService private readonly commandService: ICommandService,
@IChatVariablesService private readonly chatVariablesService: IChatVariablesService
) { }

convertParsedRequestToMarkdown(parsedRequest: IParsedChatRequest): string {
Expand All @@ -89,21 +95,28 @@ export class ChatMarkdownDecorationsRenderer {
} else if (part instanceof ChatRequestAgentPart) {
result += this.instantiationService.invokeFunction(accessor => agentToMarkdown(part.agent, false, accessor));
} else {
const uri = part instanceof ChatRequestDynamicVariablePart && part.data instanceof URI ?
part.data :
undefined;
const title = uri ? encodeURIComponent(this.labelService.getUriLabel(uri, { relative: true })) :
part instanceof ChatRequestAgentPart ? part.agent.id :
'';

const text = part.text;
result += `[${text}](${decorationRefUrl}?${title})`;
result += this.genericDecorationToMarkdown(part);
}
}

return result;
}

private genericDecorationToMarkdown(part: IParsedChatRequestPart): string {
const uri = part instanceof ChatRequestDynamicVariablePart && part.data instanceof URI ?
part.data :
undefined;
const title = uri ? encodeURIComponent(this.labelService.getUriLabel(uri, { relative: true })) :
part instanceof ChatRequestSlashCommandPart ? part.slashCommand.detail :
part instanceof ChatRequestAgentSubcommandPart ? part.command.description :
part instanceof ChatRequestVariablePart ? (this.chatVariablesService.getVariable(part.variableName)?.description ?? '') :
'';

const args: IDecorationWidgetArgs = { title };
const text = part.text;
return `[${text}](${decorationRefUrl}?${encodeURIComponent(JSON.stringify(args))})`;
}

walkTreeAndAnnotateReferenceLinks(element: HTMLElement): IDisposable {
const store = new DisposableStore();
element.querySelectorAll('a').forEach(a => {
Expand Down Expand Up @@ -136,9 +149,13 @@ export class ChatMarkdownDecorationsRenderer {
a);
}
} else if (href.startsWith(decorationRefUrl)) {
const title = decodeURIComponent(href.slice(decorationRefUrl.length + 1));
let args: IDecorationWidgetArgs | undefined;
try {
args = JSON.parse(decodeURIComponent(href.slice(decorationRefUrl.length + 1)));
} catch (e) { }

a.parentElement!.replaceChild(
this.renderResourceWidget(a.textContent!, title),
this.renderResourceWidget(a.textContent!, args, store),
a);
} else if (href.startsWith(contentRefUrl)) {
this.renderFileWidget(href, a);
Expand Down Expand Up @@ -172,7 +189,7 @@ export class ChatMarkdownDecorationsRenderer {
this.chatService.sendRequest(widget.viewModel!.sessionId, agent.metadata.sampleRequest ?? '', { location: widget.location, agentId: agent.id });
}));
} else {
container = this.renderResourceWidget(nameWithLeader, undefined);
container = this.renderResourceWidget(nameWithLeader, undefined, store);
}

const agent = this.chatAgentService.getAgent(args.agentId);
Expand Down Expand Up @@ -232,11 +249,11 @@ export class ChatMarkdownDecorationsRenderer {
}


private renderResourceWidget(name: string, title: string | undefined): HTMLElement {
private renderResourceWidget(name: string, args: IDecorationWidgetArgs | undefined, store: DisposableStore): HTMLElement {
const container = dom.$('span.chat-resource-widget');
const alias = dom.$('span', undefined, name);
if (title) {
alias.title = title;
if (args?.title) {
store.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), container, args.title));
}

container.appendChild(alias);
Expand Down

0 comments on commit ebaf073

Please sign in to comment.