Display and jump to tool locations (#34304)

Release Notes:

- N/A
This commit is contained in:
Agus Zubiaga 2025-07-11 14:52:21 -03:00 committed by GitHub
parent 0bd65829f7
commit 6f6c2915b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 115 additions and 13 deletions

View file

@ -176,6 +176,14 @@ impl AgentThreadEntry {
None None
} }
} }
pub fn locations(&self) -> Option<&[acp::ToolCallLocation]> {
if let AgentThreadEntry::ToolCall(ToolCall { locations, .. }) = self {
Some(locations)
} else {
None
}
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -925,10 +925,43 @@ impl AcpThreadView {
.size(IconSize::Small) .size(IconSize::Small)
.color(Color::Muted), .color(Color::Muted),
) )
.child(self.render_markdown( .child(if tool_call.locations.len() == 1 {
tool_call.label.clone(), let name = tool_call.locations[0]
default_markdown_style(needs_confirmation, window, cx), .path
)), .file_name()
.unwrap_or_default()
.display()
.to_string();
h_flex()
.id(("open-tool-call-location", entry_ix))
.child(name)
.w_full()
.max_w_full()
.pr_1()
.gap_0p5()
.cursor_pointer()
.rounded_sm()
.opacity(0.8)
.hover(|label| {
label.opacity(1.).bg(cx
.theme()
.colors()
.element_hover
.opacity(0.5))
})
.tooltip(Tooltip::text("Jump to File"))
.on_click(cx.listener(move |this, _, window, cx| {
this.open_tool_call_location(entry_ix, 0, window, cx);
}))
.into_any_element()
} else {
self.render_markdown(
tool_call.label.clone(),
default_markdown_style(needs_confirmation, window, cx),
)
.into_any()
}),
) )
.child( .child(
h_flex() h_flex()
@ -988,15 +1021,19 @@ impl AcpThreadView {
cx: &Context<Self>, cx: &Context<Self>,
) -> AnyElement { ) -> AnyElement {
match content { match content {
ToolCallContent::Markdown { markdown } => self ToolCallContent::Markdown { markdown } => {
.render_markdown(markdown.clone(), default_markdown_style(false, window, cx)) div()
.into_any_element(), .p_2()
.child(self.render_markdown(
markdown.clone(),
default_markdown_style(false, window, cx),
))
.into_any_element()
}
ToolCallContent::Diff { ToolCallContent::Diff {
diff: Diff { diff: Diff { multibuffer, .. },
path, multibuffer, ..
},
.. ..
} => self.render_diff_editor(multibuffer, path), } => self.render_diff_editor(multibuffer),
} }
} }
@ -1416,10 +1453,9 @@ impl AcpThreadView {
} }
} }
fn render_diff_editor(&self, multibuffer: &Entity<MultiBuffer>, path: &Path) -> AnyElement { fn render_diff_editor(&self, multibuffer: &Entity<MultiBuffer>) -> AnyElement {
v_flex() v_flex()
.h_full() .h_full()
.child(path.to_string_lossy().to_string())
.child( .child(
if let Some(editor) = self.diff_editors.get(&multibuffer.entity_id()) { if let Some(editor) = self.diff_editors.get(&multibuffer.entity_id()) {
editor.clone().into_any_element() editor.clone().into_any_element()
@ -2076,6 +2112,64 @@ impl AcpThreadView {
} }
} }
fn open_tool_call_location(
&self,
entry_ix: usize,
location_ix: usize,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<()> {
let location = self
.thread()?
.read(cx)
.entries()
.get(entry_ix)?
.locations()?
.get(location_ix)?;
let project_path = self
.project
.read(cx)
.find_project_path(&location.path, cx)?;
let open_task = self
.workspace
.update(cx, |worskpace, cx| {
worskpace.open_path(project_path, None, true, window, cx)
})
.log_err()?;
window
.spawn(cx, async move |cx| {
let item = open_task.await?;
let Some(active_editor) = item.downcast::<Editor>() else {
return anyhow::Ok(());
};
active_editor.update_in(cx, |editor, window, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let first_hunk = editor
.diff_hunks_in_ranges(
&[editor::Anchor::min()..editor::Anchor::max()],
&snapshot,
)
.next();
if let Some(first_hunk) = first_hunk {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(Default::default(), window, cx, |selections| {
selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
})
}
})?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
None
}
pub fn open_thread_as_markdown( pub fn open_thread_as_markdown(
&self, &self,
workspace: Entity<Workspace>, workspace: Entity<Workspace>,