From 092071a2f07819b9efe66610074a63121e5fade0 Mon Sep 17 00:00:00 2001 From: Josh Piasecki <138541977+FloppyDisco@users.noreply.github.com> Date: Fri, 14 Nov 2025 07:47:46 -0600 Subject: [PATCH] git_ui: Allow opening a file with the diff hunks expanded (#40616) So i just discovered `editor::ExpandAllDiffHunks` I have been really missing the ability to look at changes NOT in a multi buffer so i was very pleased to finally figure out that this is already possible in Zed. i have seen alot of discussion/issues requesting this feature so i think it is safe to say i'm not the only one that is not aware it exists. i think the wording in the docs could better communicate what this feature actually is, however, i think an even better way to show users that this feature exists would be to just put it in front of them. In the `GitPanel`: - `menu::Confirm` opens the project diff - `menu::SecondaryConfirm` opens the selected file in a new editor. I think it would be REALLY nice if opening a file with `SecondaryConfirm` opened the file with the diff hunks already expanded and scrolled the editor to the first hunk. ideally i see this being toggle-able in settings something like `GitPanel - Open File with Diffs Expanded` or something. so the user could turn this off if they preferred. I tried creating a new keybinding using the new `actions::Sequence` it was something like: ```json { "context": "GitPanel && ChangesList", "bindings": { "cmd-enter" : [ "actions::Sequence", ["menu:SecondaryConfirm", "editor::ToggleFocus", "editor::ExpandAllDiffHunks", "editor::GoToHunk"]] } } ``` but the action sequence does not work. i think because opening the file is an async task. i have a first attempt here, of just trying to get the diff hunks to expand after opening the file. i tried to copy and paste the logic/structure as best i could from the confirm method in file_finder.rs:1432 it compiles, but it does not work, and i do not have enough experience in rust or in this project to figure out anything further. if anyone was interested in working on this with me i would enjoy learning more and i think this would be a nice way to showcase this tool! --- crates/git_ui/src/git_panel.rs | 51 ++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 6a80f22773f154f32907d2bbadfa91c2eec53108..e2a4a26b320284fed727a7f7e60acf807c39abf0 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -12,7 +12,9 @@ use agent_settings::AgentSettings; use anyhow::Context as _; use askpass::AskPassDelegate; use db::kvp::KEY_VALUE_STORE; -use editor::{Editor, EditorElement, EditorMode, MultiBuffer}; +use editor::{ + Direction, Editor, EditorElement, EditorMode, MultiBuffer, actions::ExpandAllDiffHunks, +}; use futures::StreamExt as _; use git::blame::ParsedCommitMessage; use git::repository::{ @@ -69,7 +71,7 @@ use cloud_llm_client::CompletionIntent; use workspace::{ Workspace, dock::{DockPosition, Panel, PanelEvent}, - notifications::{DetachAndPromptErr, ErrorMessagePrompt, NotificationId}, + notifications::{DetachAndPromptErr, ErrorMessagePrompt, NotificationId, NotifyResultExt}, }; actions!( @@ -792,15 +794,46 @@ impl GitPanel { return None; } - self.workspace + let open_task = self + .workspace .update(cx, |workspace, cx| { - workspace - .open_path_preview(path, None, false, false, true, window, cx) - .detach_and_prompt_err("Failed to open file", window, cx, |e, _, _| { - Some(format!("{e}")) - }); + workspace.open_path_preview(path, None, false, false, true, window, cx) }) - .ok() + .ok()?; + + cx.spawn_in(window, async move |_, mut cx| { + let item = open_task + .await + .notify_async_err(&mut cx) + .ok_or_else(|| anyhow::anyhow!("Failed to open file"))?; + if let Some(active_editor) = item.downcast::() { + if let Some(diff_task) = + active_editor.update(cx, |editor, _cx| editor.wait_for_diff_to_load())? + { + diff_task.await; + } + + cx.update(|window, cx| { + active_editor.update(cx, |editor, cx| { + editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx); + + let snapshot = editor.snapshot(window, cx); + editor.go_to_hunk_before_or_after_position( + &snapshot, + language::Point::new(0, 0), + Direction::Next, + window, + cx, + ); + }) + })?; + } + + anyhow::Ok(()) + }) + .detach(); + + Some(()) }); }