agent: Navigate to line when clicking on filepath in markdown codeblock (#28329)

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner 2025-04-08 09:41:34 -06:00 committed by GitHub
parent d39e1e03b8
commit 14b43d573c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -25,6 +25,7 @@ use language_model::{ConfiguredModel, LanguageModelRegistry, LanguageModelToolUs
use markdown::parser::CodeBlockKind; use markdown::parser::CodeBlockKind;
use markdown::{Markdown, MarkdownElement, MarkdownStyle, ParsedMarkdown, without_fences}; use markdown::{Markdown, MarkdownElement, MarkdownStyle, ParsedMarkdown, without_fences};
use project::ProjectItem as _; use project::ProjectItem as _;
use rope::Point;
use settings::{Settings as _, update_settings_file}; use settings::{Settings as _, update_settings_file};
use std::ops::Range; use std::ops::Range;
use std::path::Path; use std::path::Path;
@ -58,7 +59,7 @@ pub struct ActiveThread {
expanded_thinking_segments: HashMap<(MessageId, usize), bool>, expanded_thinking_segments: HashMap<(MessageId, usize), bool>,
last_error: Option<ThreadError>, last_error: Option<ThreadError>,
notifications: Vec<WindowHandle<AgentNotification>>, notifications: Vec<WindowHandle<AgentNotification>>,
copied_code_block_ids: HashSet<usize>, copied_code_block_ids: HashSet<(MessageId, usize)>,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
notification_subscriptions: HashMap<WindowHandle<AgentNotification>, Vec<Subscription>>, notification_subscriptions: HashMap<WindowHandle<AgentNotification>, Vec<Subscription>>,
feedback_message_editor: Option<Entity<Editor>>, feedback_message_editor: Option<Entity<Editor>>,
@ -289,7 +290,8 @@ fn tool_use_markdown_style(window: &Window, cx: &mut App) -> MarkdownStyle {
} }
fn render_markdown_code_block( fn render_markdown_code_block(
id: usize, message_id: MessageId,
ix: usize,
kind: &CodeBlockKind, kind: &CodeBlockKind,
parsed_markdown: &ParsedMarkdown, parsed_markdown: &ParsedMarkdown,
codeblock_range: Range<usize>, codeblock_range: Range<usize>,
@ -366,7 +368,7 @@ fn render_markdown_code_block(
}; };
h_flex() h_flex()
.id(("code-block-header-label", id)) .id(("code-block-header-label", ix))
.w_full() .w_full()
.max_w_full() .max_w_full()
.px_1() .px_1()
@ -397,8 +399,40 @@ fn render_markdown_code_block(
.read(cx) .read(cx)
.find_project_path(&path_range.path, cx) .find_project_path(&path_range.path, cx)
{ {
workspace let target = path_range.range.as_ref().map(|range| {
.open_path(project_path, None, true, window, cx) Point::new(
// Line number is 1-based
range.start.line.saturating_sub(1),
range.start.col.unwrap_or(0),
)
});
let open_task = workspace.open_path(
project_path,
None,
true,
window,
cx,
);
window
.spawn(cx, async move |cx| {
let item = open_task.await?;
if let Some(target) = target {
if let Some(active_editor) =
item.downcast::<Editor>()
{
active_editor
.downgrade()
.update_in(cx, |editor, window, cx| {
editor
.go_to_singleton_buffer_point(
target, window, cx,
);
})
.log_err();
}
}
anyhow::Ok(())
})
.detach_and_log_err(cx); .detach_and_log_err(cx);
} }
} }
@ -416,7 +450,10 @@ fn render_markdown_code_block(
.element_background .element_background
.blend(cx.theme().colors().editor_foreground.opacity(0.01)); .blend(cx.theme().colors().editor_foreground.opacity(0.01));
let codeblock_was_copied = active_thread.read(cx).copied_code_block_ids.contains(&id); let codeblock_was_copied = active_thread
.read(cx)
.copied_code_block_ids
.contains(&(message_id, ix));
let codeblock_header = h_flex() let codeblock_header = h_flex()
.p_1() .p_1()
@ -429,7 +466,7 @@ fn render_markdown_code_block(
.children(label) .children(label)
.child( .child(
IconButton::new( IconButton::new(
("copy-markdown-code", id), ("copy-markdown-code", ix),
if codeblock_was_copied { if codeblock_was_copied {
IconName::Check IconName::Check
} else { } else {
@ -444,7 +481,7 @@ fn render_markdown_code_block(
let parsed_markdown = parsed_markdown.clone(); let parsed_markdown = parsed_markdown.clone();
move |_event, _window, cx| { move |_event, _window, cx| {
active_thread.update(cx, |this, cx| { active_thread.update(cx, |this, cx| {
this.copied_code_block_ids.insert(id); this.copied_code_block_ids.insert((message_id, ix));
let code = let code =
without_fences(&parsed_markdown.source()[codeblock_range.clone()]) without_fences(&parsed_markdown.source()[codeblock_range.clone()])
@ -457,7 +494,7 @@ fn render_markdown_code_block(
cx.update(|cx| { cx.update(|cx| {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.copied_code_block_ids.remove(&id); this.copied_code_block_ids.remove(&(message_id, ix));
cx.notify(); cx.notify();
}) })
}) })
@ -1699,6 +1736,7 @@ impl ActiveThread {
let active_thread = cx.entity(); let active_thread = cx.entity();
move |id, kind, parsed_markdown, range, window, cx| { move |id, kind, parsed_markdown, range, window, cx| {
render_markdown_code_block( render_markdown_code_block(
message_id,
id, id,
kind, kind,
parsed_markdown, parsed_markdown,