Skip to content

Commit 9564ab1

Browse files
vijayupadyavijay upadya
andauthored
Add tooltip for pr chat items in chat sessions view (#1842)
* Enable tooltip for CP chat pr items * minor update * update comment * comment update * Copilot PR feedback --------- Co-authored-by: vijay upadya <vj@example.com>
1 parent e1670ed commit 9564ab1

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import MarkdownIt from 'markdown-it';
67
import * as pathLib from 'path';
78
import * as vscode from 'vscode';
89
import { Uri } from 'vscode';
@@ -60,6 +61,72 @@ const AGENTS_OPTION_GROUP_ID = 'agents';
6061
const DEFAULT_AGENT_ID = '___vscode_default___';
6162
const BACKGROUND_REFRESH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
6263

64+
/**
65+
* Custom renderer for markdown-it that converts markdown to plain text
66+
*/
67+
class PlainTextRenderer {
68+
private md: MarkdownIt;
69+
70+
constructor() {
71+
this.md = new MarkdownIt();
72+
}
73+
74+
/**
75+
* Renders markdown text as plain text by extracting text content from all tokens
76+
*/
77+
render(markdown: string): string {
78+
const tokens = this.md.parse(markdown, {});
79+
return this.renderTokens(tokens).trim();
80+
}
81+
82+
private renderTokens(tokens: MarkdownIt.Token[]): string {
83+
let result = '';
84+
for (const token of tokens) {
85+
// Process child tokens recursively
86+
if (token.children) {
87+
result += this.renderTokens(token.children);
88+
}
89+
90+
// Handle different token types
91+
switch (token.type) {
92+
case 'text':
93+
case 'code_inline':
94+
// Only add content if no children were processed
95+
if (!token.children) {
96+
result += token.content;
97+
}
98+
break;
99+
100+
case 'softbreak':
101+
case 'hardbreak':
102+
result += ' '; // Space instead of newline to match original
103+
break;
104+
105+
case 'paragraph_close':
106+
result += '\n'; // Newline after paragraphs for separation
107+
break;
108+
109+
case 'heading_close':
110+
result += '\n'; // Newline after headings
111+
break;
112+
113+
case 'list_item_close':
114+
result += '\n'; // Newline after list items
115+
break;
116+
117+
case 'fence':
118+
case 'code_block':
119+
case 'hr':
120+
// Skip these entirely
121+
break;
122+
123+
// Don't add default case - only explicitly handle what we want
124+
}
125+
}
126+
return result;
127+
}
128+
}
129+
63130
export class CopilotCloudSessionsProvider extends Disposable implements vscode.ChatSessionContentProvider, vscode.ChatSessionItemProvider {
64131
public static readonly TYPE = 'copilot-cloud-agent';
65132
private readonly DELEGATE_MODAL_DETAILS = vscode.l10n.t('The agent will work asynchronously to create a pull request with your requested changes. This chat\'s history will be summarized and appended to the pull request as context.');
@@ -74,6 +141,7 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
74141
await this.chatParticipantImpl(request, context, stream, token)
75142
);
76143
private cachedSessionsSize: number = 0;
144+
private readonly plainTextRenderer = new PlainTextRenderer();
77145

78146
constructor(
79147
@IOctoKitService private readonly _octoKitService: IOctoKitService,
@@ -203,12 +271,14 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
203271
const uri = await toOpenPullRequestWebviewUri({ owner: repoId.org, repo: repoId.repo, pullRequestNumber: pr.number });
204272
const prLinkTitle = vscode.l10n.t('Open pull request in VS Code');
205273
const description = new vscode.MarkdownString(`[#${pr.number}](${uri.toString()} "${prLinkTitle}")`);
274+
const tooltip = this.createPullRequestTooltip(pr);
206275

207276
const session = {
208277
resource: vscode.Uri.from({ scheme: CopilotCloudSessionsProvider.TYPE, path: '/' + pr.number }),
209278
label: pr.title,
210279
status: this.getSessionStatusFromSession(sessionItem),
211280
description,
281+
tooltip,
212282
timing: {
213283
startTime: new Date(sessionItem.last_updated_at).getTime(),
214284
},
@@ -415,6 +485,46 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
415485
}
416486
}
417487

488+
private createPullRequestTooltip(pr: PullRequestSearchItem): vscode.MarkdownString {
489+
const markdown = new vscode.MarkdownString(undefined, true);
490+
markdown.supportHtml = true;
491+
492+
// Repository and date
493+
const date = new Date(pr.createdAt);
494+
const ownerName = `${pr.repository.owner.login}/${pr.repository.name}`;
495+
markdown.appendMarkdown(
496+
`[${ownerName}](https://github.com/${ownerName}) on ${date.toLocaleString('default', {
497+
day: 'numeric',
498+
month: 'short',
499+
year: 'numeric',
500+
})} \n`
501+
);
502+
503+
// Icon, title, and PR number
504+
const icon = this.getIconMarkdown(pr);
505+
// Strip markdown from title for plain text display
506+
const title = this.plainTextRenderer.render(pr.title);
507+
markdown.appendMarkdown(
508+
`${icon} **${title}** [#${pr.number}](${pr.url}) \n`
509+
);
510+
511+
// Body/Description (truncated if too long)
512+
markdown.appendMarkdown(' \n');
513+
const maxBodyLength = 200;
514+
let body = this.plainTextRenderer.render(pr.body || '');
515+
// Convert plain text newlines to markdown line breaks (two spaces + newline)
516+
body = body.replace(/\n/g, ' \n');
517+
body = body.length > maxBodyLength ? body.substring(0, maxBodyLength) + '...' : body;
518+
markdown.appendMarkdown(body + ' \n');
519+
520+
return markdown;
521+
}
522+
523+
private getIconMarkdown(pr: PullRequestSearchItem): string {
524+
const state = pr.state.toUpperCase();
525+
return state === 'MERGED' ? '$(git-merge)' : '$(git-pull-request)';
526+
}
527+
418528
private async startSession(stream: vscode.ChatResponseStream, token: vscode.CancellationToken, source: string, prompt: string, history?: string, references?: readonly vscode.ChatPromptReference[], customAgentName?: string) {
419529
/* __GDPR__
420530
"copilot.codingAgent.editor.invoke" : {

0 commit comments

Comments
 (0)