feat: clickable code references in model responses navigate to source lines (#3087)

Co-authored-by: Eric Wheeler <roo-code@z.ewheeler.org>
This commit is contained in:
KJ7LNW 2025-05-05 11:33:18 -07:00 committed by GitHub
parent 01fec4c1db
commit 305185cd88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 141 additions and 6 deletions

View file

@ -5,6 +5,12 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -472,6 +478,12 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -939,6 +951,12 @@ exports[`SYSTEM_PROMPT should explicitly handle undefined mcpHub 1`] = `
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -1406,6 +1424,12 @@ exports[`SYSTEM_PROMPT should handle different browser viewport sizes 1`] = `
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -1929,6 +1953,12 @@ exports[`SYSTEM_PROMPT should include MCP server info when mcpHub is provided 1`
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -2464,6 +2494,12 @@ exports[`SYSTEM_PROMPT should include browser actions when supportsComputerUse i
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -2987,6 +3023,12 @@ exports[`SYSTEM_PROMPT should include diff strategy tool description when diffEn
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -3544,6 +3586,12 @@ exports[`SYSTEM_PROMPT should maintain consistent system prompt 1`] = `
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -4053,6 +4101,12 @@ exports[`addCustomInstructions should exclude MCP server creation info when disa
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -4597,6 +4651,12 @@ exports[`addCustomInstructions should generate correct prompt for architect mode
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -5055,6 +5115,12 @@ exports[`addCustomInstructions should generate correct prompt for ask mode 1`] =
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
@ -5430,6 +5496,12 @@ exports[`addCustomInstructions should include MCP server creation info when enab
====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>
====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.

View file

@ -7,3 +7,4 @@ export { getMcpServersSection } from "./mcp-servers"
export { getToolUseGuidelinesSection } from "./tool-use-guidelines"
export { getCapabilitiesSection } from "./capabilities"
export { getModesSection } from "./modes"
export { markdownFormattingSection } from "./markdown-formatting"

View file

@ -0,0 +1,7 @@
export function markdownFormattingSection(): string {
return `====
MARKDOWN RULES
ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in <attempt_completion>`
}

View file

@ -24,6 +24,7 @@ import {
getCapabilitiesSection,
getModesSection,
addCustomInstructions,
markdownFormattingSection,
} from "./sections"
import { loadSystemPromptFile } from "./sections/custom-system-prompt"
import { formatLanguage } from "../../shared/language"
@ -65,6 +66,8 @@ async function generatePrompt(
const basePrompt = `${roleDefinition}
${markdownFormattingSection()}
${getSharedToolUseSection()}
${getToolDescriptionsForMode(

View file

@ -336,7 +336,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
openImage(message.text!)
break
case "openFile":
openFile(message.text!, message.values as { create?: boolean; content?: string })
openFile(message.text!, message.values as { create?: boolean; content?: string; line?: number })
break
case "openMention":
openMention(message.text)

View file

@ -23,6 +23,7 @@ export async function openImage(dataUri: string) {
interface OpenFileOptions {
create?: boolean
content?: string
line?: number
}
export async function openFile(filePath: string, options: OpenFileOptions = {}) {
@ -75,7 +76,12 @@ export async function openFile(filePath: string, options: OpenFileOptions = {})
} catch {} // not essential, sometimes tab operations fail
const document = await vscode.workspace.openTextDocument(uri)
await vscode.window.showTextDocument(document, { preview: false })
const selection =
options.line !== undefined ? new vscode.Selection(options.line - 1, 0, options.line - 1, 0) : undefined
await vscode.window.showTextDocument(document, {
preview: false,
selection,
})
} catch (error) {
if (error instanceof Error) {
vscode.window.showErrorMessage(`Could not open file: ${error.message}`)

View file

@ -3,6 +3,7 @@ import { useRemark } from "react-remark"
import styled from "styled-components"
import { visit } from "unist-util-visit"
import { vscode } from "@src/utils/vscode"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import CodeBlock from "./CodeBlock"
@ -108,11 +109,14 @@ const StyledMarkdown = styled.div`
}
a {
text-decoration: none;
}
a {
color: var(--vscode-textLink-foreground);
text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-color: var(--vscode-textLink-foreground);
&:hover {
text-decoration: underline;
color: var(--vscode-textLink-activeForeground);
text-decoration-style: solid;
text-decoration-color: var(--vscode-textLink-activeForeground);
}
}
`
@ -137,6 +141,48 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
rehypePlugins: [],
rehypeReactOptions: {
components: {
a: ({ href, children }: any) => {
return (
<a
href={href}
title={href}
onClick={(e) => {
// Only process file:// protocol or local file paths
const isLocalPath =
href.startsWith("file://") || href.startsWith("/") || !href.includes("://")
if (!isLocalPath) {
return
}
e.preventDefault()
// Handle absolute vs project-relative paths
let filePath = href.replace("file://", "")
// Extract line number if present
const match = filePath.match(/(.*):(\d+)(-\d+)?$/)
let values = undefined
if (match) {
filePath = match[1]
values = { line: parseInt(match[2]) }
}
// Add ./ prefix if needed
if (!filePath.startsWith("/") && !filePath.startsWith("./")) {
filePath = "./" + filePath
}
vscode.postMessage({
type: "openFile",
text: filePath,
values,
})
}}>
{children}
</a>
)
},
pre: ({ node: _, children }: any) => {
// Check for Mermaid diagrams first
if (Array.isArray(children) && children.length === 1 && React.isValidElement(children[0])) {