Make file paths in backticks clickable in agent panel#57303
Merged
Conversation
Inline code spans the agent emits that resolve to a worktree file (e.g. `src/main.rs` or `src/main.rs:42`) now render as clickable links that open the referenced file at the given line. Path resolution is shared with the terminal's path-hyperlink logic via a new workspace::path_link module, extracted from terminal_view::terminal_path_like_target. The markdown crate gained an on_code_span_link hook so consumers can opt into turning code spans into links without baking workspace knowledge into the crate. The agent panel wires this up via an AgentCodeSpanResolver that snapshots worktree files at render time and synchronously resolves candidate paths.
Previously `AgentCodeSpanResolver` was rebuilt on every render, snapshotting all worktree files each time and re-resolving every code span on every layout pass. Scrolling a thread with code spans triggered tens of these calls per second.
Make the resolver an Arc-wrapped, cloneable handle owned by ConversationView and cloned into ThreadView. The worktree snapshot is now built once and refreshed only when project::Event::Worktree{Added,Removed,UpdatedEntries} fires; resolutions are memoized in an lru::LruCache capped at 2048 entries so memory cannot grow unboundedly across long sessions. Memory is freed when the ConversationView is dropped, which happens on thread switch.
…k-backticked-file-paths
Reject obviously-not-a-path text before the cache lookup instead of after, so prose like 'String', 'await', or regex snippets are filtered out without any cache or snapshot work. Also tighten is_path_like to reject text containing '|' (regex alternation, never valid in a path) and text shorter than 3 bytes (single- and two-char identifiers). The cache miss path now reuses the already-acquired snapshot and trimmed text instead of recomputing them.
Match worktree::Snapshot's storage so the agent's code-span path resolver shares the same handle Worktree::abs_path() returns, rather than cloning into a PathBuf. The duplicated absolutize logic now mirrors worktree::Snapshot::absolutize exactly, so any future improvement there carries over.
probably-neb
approved these changes
May 21, 2026
TomPlanche
pushed a commit
to TomPlanche/zed
that referenced
this pull request
Jun 2, 2026
…#57303) When the agent mentions a file path inside `backticks` (e.g. `` `src/main.rs` `` or `` `src/main.rs:42` ``), the rendered code span now becomes a clickable link in the agent panel. Clicking opens the referenced file in the workspace, jumping to the right line and column when present. ## How it works - **Shared path resolution.** Extracted `OpenTarget` and the workspace/worktree resolution logic out of `terminal_view::terminal_path_like_target` into a new `workspace::path_link` module so both the terminal and the agent panel can use the same code. Includes a `sanitize_path_text` helper ported from the terminal's URL/punctuation handling. Pure refactor — terminal behavior is unchanged. - **`markdown` crate hook.** Added `MarkdownElement::on_code_span_link(callback)`. When the callback returns `Some(url)` for a given code span's contents, the existing `push_link` machinery wires up cmd-hover, hit testing, and the existing `on_url_click` callback. When it returns `None`, the code span renders as before. The hook is opt-in, so `markdown` stays workspace-agnostic. - **Agent panel wiring.** `render_agent_markdown` constructs an `AgentCodeSpanResolver` that snapshots the project's visible worktree entries plus their file extensions. `try_resolve` does a cheap synchronous heuristic check (path must contain `/`/`\` or end in an extension present in the workspace, can't be a URL, can't be all digits, etc.) and then looks the candidate up in the per-worktree `HashSet<Arc<RelPath>>`. On a hit it returns a `MentionUri::File` or `MentionUri::Selection` URI, which the existing `thread_view::open_link` already knows how to open at the right line. ## Edge cases handled - Code spans inside fenced code blocks stay plain (gated on `builder.code_block_stack.is_empty()`, matching how regular markdown links behave). - Trailing prose punctuation (`` `src/main.rs.` ``) is stripped before lookup. - Identifiers like `` `String` ``, `` `await` ``, `` `npm run dev` `` stay plain — they don't pass the path-like heuristic. - Cross-platform path separators handled via the per-worktree `PathStyle`. ## Tests - `crates/markdown` — unit test asserting code spans become links when the callback returns `Some`, and stay plain when it doesn't. - `crates/agent_ui` — unit test for `AgentCodeSpanResolver::try_resolve` covering hits with and without a `:line` suffix, misses, identifiers, and trailing punctuation. - Existing `terminal_view` tests cover the moved resolution code (unchanged behavior). ## Notes - There's currently a temporary `log::info!` in `AgentCodeSpanResolver::try_resolve` that reports per-call worktree-walk timing and a cumulative total. Kept in for now to verify the feature isn't being called excessively during streaming renders. Can be removed before merge. - Resolution is sync-only against worktree entries; absolute paths outside the workspace are not resolved (would require an async re-render path). Closes AI-277 Release Notes: - Made file paths in `backticks` clickable in the agent panel; clicking opens the referenced file at the given line when present.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When the agent mentions a file path inside
backticks(e.g.`src/main.rs`or`src/main.rs:42`), the rendered code span now becomes a clickable link in the agent panel. Clicking opens the referenced file in the workspace, jumping to the right line and column when present.How it works
OpenTargetand the workspace/worktree resolution logic out ofterminal_view::terminal_path_like_targetinto a newworkspace::path_linkmodule so both the terminal and the agent panel can use the same code. Includes asanitize_path_texthelper ported from the terminal's URL/punctuation handling. Pure refactor — terminal behavior is unchanged.markdowncrate hook. AddedMarkdownElement::on_code_span_link(callback). When the callback returnsSome(url)for a given code span's contents, the existingpush_linkmachinery wires up cmd-hover, hit testing, and the existingon_url_clickcallback. When it returnsNone, the code span renders as before. The hook is opt-in, somarkdownstays workspace-agnostic.render_agent_markdownconstructs anAgentCodeSpanResolverthat snapshots the project's visible worktree entries plus their file extensions.try_resolvedoes a cheap synchronous heuristic check (path must contain//\or end in an extension present in the workspace, can't be a URL, can't be all digits, etc.) and then looks the candidate up in the per-worktreeHashSet<Arc<RelPath>>. On a hit it returns aMentionUri::FileorMentionUri::SelectionURI, which the existingthread_view::open_linkalready knows how to open at the right line.Edge cases handled
builder.code_block_stack.is_empty(), matching how regular markdown links behave).`src/main.rs.`) is stripped before lookup.`String`,`await`,`npm run dev`stay plain — they don't pass the path-like heuristic.PathStyle.Tests
crates/markdown— unit test asserting code spans become links when the callback returnsSome, and stay plain when it doesn't.crates/agent_ui— unit test forAgentCodeSpanResolver::try_resolvecovering hits with and without a:linesuffix, misses, identifiers, and trailing punctuation.terminal_viewtests cover the moved resolution code (unchanged behavior).Notes
log::info!inAgentCodeSpanResolver::try_resolvethat reports per-call worktree-walk timing and a cumulative total. Kept in for now to verify the feature isn't being called excessively during streaming renders. Can be removed before merge.Closes AI-277
Release Notes:
backticksclickable in the agent panel; clicking opens the referenced file at the given line when present.