From 1f4a2d50a01b1e197df92647c20d78bfd32b2a87 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Apr 2025 11:30:35 +0200 Subject: [PATCH] Scroll to first hunk when clicking on a file to review in Agent Panel (#28075) Release Notes: - Added the ability to scroll to a file when clicking on it in the Agent Panel review section. --- crates/agent/src/agent_diff.rs | 36 ++++++++++++++++++++++++------ crates/agent/src/message_editor.rs | 32 +++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/crates/agent/src/agent_diff.rs b/crates/agent/src/agent_diff.rs index ccbe1b437b840c7bb5b8837b1b83b42603236e8c..ec1043e6fbacadd41d5f6eb6fcf39d702ab08e3c 100644 --- a/crates/agent/src/agent_diff.rs +++ b/crates/agent/src/agent_diff.rs @@ -44,7 +44,7 @@ impl AgentDiff { workspace: WeakEntity, window: &mut Window, cx: &mut App, - ) -> Result<()> { + ) -> Result> { let existing_diff = workspace.update(cx, |workspace, cx| { workspace .items_of_type::(cx) @@ -53,13 +53,15 @@ impl AgentDiff { if let Some(existing_diff) = existing_diff { workspace.update(cx, |workspace, cx| { workspace.activate_item(&existing_diff, true, true, window, cx); - }) + })?; + Ok(existing_diff) } else { let agent_diff = cx.new(|cx| AgentDiff::new(thread.clone(), workspace.clone(), window, cx)); workspace.update(cx, |workspace, cx| { - workspace.add_item_to_center(Box::new(agent_diff), window, cx); - }) + workspace.add_item_to_center(Box::new(agent_diff.clone()), window, cx); + })?; + Ok(agent_diff) } } @@ -134,11 +136,11 @@ impl AgentDiff { let mut paths_to_delete = self.multibuffer.read(cx).paths().collect::>(); for (buffer, diff_handle) in changed_buffers { - let Some(file) = buffer.read(cx).file().cloned() else { + if buffer.read(cx).file().is_none() { continue; - }; + } - let path_key = PathKey::namespaced(0, file.full_path(cx).into()); + let path_key = PathKey::for_buffer(&buffer, cx); paths_to_delete.remove(&path_key); let snapshot = buffer.read(cx).snapshot(); @@ -241,6 +243,26 @@ impl AgentDiff { } } + pub fn move_to_path(&mut self, path_key: PathKey, window: &mut Window, cx: &mut Context) { + if let Some(position) = self.multibuffer.read(cx).location_for_path(&path_key, cx) { + self.editor.update(cx, |editor, cx| { + let first_hunk = editor + .diff_hunks_in_ranges( + &[position..editor::Anchor::max()], + &self.multibuffer.read(cx).read(cx), + ) + .next(); + + if let Some(first_hunk) = first_hunk { + let first_hunk_start = first_hunk.multi_buffer_range().start; + editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| { + selections.select_anchor_ranges([first_hunk_start..first_hunk_start]); + }) + } + }); + } + } + fn keep(&mut self, _: &crate::Keep, window: &mut Window, cx: &mut Context) { let ranges = self .editor diff --git a/crates/agent/src/message_editor.rs b/crates/agent/src/message_editor.rs index b1e98bf49dad37e36c4b0d7bfbdddee297fbc6df..7cc5b44026764a63ac92a36aad54fd09316609c0 100644 --- a/crates/agent/src/message_editor.rs +++ b/crates/agent/src/message_editor.rs @@ -9,8 +9,10 @@ use gpui::{ Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription, TextStyle, WeakEntity, linear_color_stop, linear_gradient, point, }; +use language::Buffer; use language_model::LanguageModelRegistry; use language_model_selector::ToggleModelSelector; +use multi_buffer; use project::Project; use settings::Settings; use std::time::Duration; @@ -320,6 +322,19 @@ impl MessageEditor { fn handle_review_click(&self, window: &mut Window, cx: &mut Context) { AgentDiff::deploy(self.thread.clone(), self.workspace.clone(), window, cx).log_err(); } + + fn handle_file_click( + &self, + buffer: Entity, + window: &mut Window, + cx: &mut Context, + ) { + if let Ok(diff) = AgentDiff::deploy(self.thread.clone(), self.workspace.clone(), window, cx) + { + let path_key = multi_buffer::PathKey::for_buffer(&buffer, cx); + diff.update(cx, |diff, cx| diff.move_to_path(path_key, window, cx)); + } + } } impl Focusable for MessageEditor { @@ -487,11 +502,16 @@ impl Render for MessageEditor { }]) .child( h_flex() + .id("edits-container") .p_1p5() .justify_between() .when(self.edits_expanded, |this| { this.border_b_1().border_color(border_color) }) + .cursor_pointer() + .on_click(cx.listener(|this, _, window, cx| { + this.handle_review_click(window, cx) + })) .child( h_flex() .gap_1() @@ -605,11 +625,21 @@ impl Render for MessageEditor { .justify_between() .child( h_flex() - .id("file-container") + .id(("file-container", index)) .pr_8() .gap_1p5() .max_w_full() .overflow_x_scroll() + .cursor_pointer() + .on_click({ + let buffer = buffer.clone(); + cx.listener(move |this, _, window, cx| { + this.handle_file_click(buffer.clone(), window, cx); + }) + }) + .tooltip( + Tooltip::text(format!("Review {}", path.display())) + ) .child(file_icon) .child( h_flex()