From c2f6ece4e2cc4fba20c3c36fc314936f1472edae Mon Sep 17 00:00:00 2001 From: $H!NDGEKYUME Date: Thu, 3 Sep 2026 12:55:21 +0000 Subject: [PATCH] Add a history list key context to the Git panel (#63689) # Objective The Git Panel exposes the `ChangesList` key context while its list is focused, including when the History tab is active. This causes Changes-only keybindings to match in History and provides no context for History-specific bindings. This is a fresh submission of the revised change from #63669, as requested in that PR's discussion. Fixes #61012 ## Solution Select the list key context from the active Git Panel tab. Changes exposes `ChangesList`, while History exposes `HistoryList`. The shared `GitPanel` and `menu` contexts remain unchanged. Preserve compatible keyboard behavior by sharing navigation, confirmation, and cancellation bindings between `ChangesList` and `HistoryList` on macOS, Linux, Windows, and Vim. File, staging, restore, and commit-editor bindings remain exclusive to `ChangesList`. Extend the dispatch-context test to verify that the list contexts are mutually exclusive and absent while the commit editor is focused. ## Testing - `cargo fmt --package git_ui -- --check` - `./script/check-keymaps` - `git diff --check` - `cargo test -p git_ui test_dispatch_context_with_focus_states -- --nocapture` - `cargo test -p git_ui` (153 passed) - `cargo build -p zed` - Manually verified on macOS that History reports `HistoryList` without `ChangesList`, arrow navigation selects commits, and Enter opens the selected commit - Linux and Windows keymaps were updated and checked, but not manually exercised on those platforms ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase The Keyboard Context view shows `GitPanel`, `menu`, and `HistoryList` while the History tab is focused. `ChangesList` is absent. ![Keyboard Context view showing the HistoryList context](https://github.com/user-attachments/assets/63422131-b16d-4d30-997c-17aec7924011) --- Release Notes: - Fixed Git Panel keybindings matching the Changes tab while the History tab is active. --- assets/keymaps/default-linux.json | 15 +++++++--- assets/keymaps/default-macos.json | 16 ++++++++--- assets/keymaps/default-windows.json | 16 ++++++++--- assets/keymaps/vim.json | 12 ++++++-- crates/git_ui/src/git_panel.rs | 44 ++++++++++++++++++++++++----- 5 files changed, 81 insertions(+), 22 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 15d44ded5f..553b9a2a6b 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -1035,6 +1035,17 @@ "ctrl-2": "git_panel::ActivateHistoryTab", }, }, + { + "context": "GitPanel && (ChangesList || HistoryList) && !GitBranchSelector && !GitRepositorySelector", + "bindings": { + "up": "menu::SelectPrevious", + "down": "menu::SelectNext", + "shift-up": "menu::SelectPrevious", + "shift-down": "menu::SelectNext", + "enter": "menu::Confirm", + "escape": "menu::Cancel", + }, + }, { "context": "GitPanel && ChangesList && !GitBranchSelector && !GitRepositorySelector", "bindings": { @@ -1042,9 +1053,6 @@ "right": "git_panel::ExpandSelectedEntry", "up": "git_panel::PreviousEntry", "down": "git_panel::NextEntry", - "shift-up": "menu::SelectPrevious", - "shift-down": "menu::SelectNext", - "enter": "menu::Confirm", "alt-y": "git::StageFile", "alt-shift-y": "git::UnstageFile", "ctrl-alt-y": "git::ToggleStaged", @@ -1056,7 +1064,6 @@ "alt-ctrl-shift-c": "workspace::CopyRelativePath", "tab": "git_panel::FocusEditor", "shift-tab": "git_panel::FocusEditor", - "escape": "menu::Cancel", "alt-enter": "menu::SecondaryConfirm", "delete": ["git::RestoreFile", { "skip_prompt": false }], "backspace": ["git::RestoreFile", { "skip_prompt": false }], diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 636c3c0fe8..4b5dd97951 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -1094,19 +1094,28 @@ "cmd-2": "git_panel::ActivateHistoryTab", }, }, + { + "context": "GitPanel && (ChangesList || HistoryList) && !GitBranchSelector && !GitRepositorySelector", + "use_key_equivalents": true, + "bindings": { + "up": "menu::SelectPrevious", + "down": "menu::SelectNext", + "shift-up": "menu::SelectPrevious", + "shift-down": "menu::SelectNext", + "enter": "menu::Confirm", + "escape": "menu::Cancel", + }, + }, { "context": "GitPanel && ChangesList && !GitBranchSelector && !GitRepositorySelector", "use_key_equivalents": true, "bindings": { "up": "git_panel::PreviousEntry", "down": "git_panel::NextEntry", - "shift-up": "menu::SelectPrevious", - "shift-down": "menu::SelectNext", "cmd-up": "git_panel::FirstEntry", "cmd-down": "git_panel::LastEntry", "left": "git_panel::CollapseSelectedEntry", "right": "git_panel::ExpandSelectedEntry", - "enter": "menu::Confirm", "cmd-alt-y": "git::ToggleStaged", "space": "git::ToggleStaged", "shift-space": "git::StageRange", @@ -1117,7 +1126,6 @@ "alt-down": "git_panel::FocusEditor", "tab": "git_panel::FocusEditor", "shift-tab": "git_panel::FocusEditor", - "escape": "menu::Cancel", "backspace": ["git::RestoreFile", { "skip_prompt": false }], "delete": ["git::RestoreFile", { "skip_prompt": false }], "cmd-backspace": ["git::RestoreFile", { "skip_prompt": true }], diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json index b11fd86d18..3752597005 100644 --- a/assets/keymaps/default-windows.json +++ b/assets/keymaps/default-windows.json @@ -1028,17 +1028,26 @@ "ctrl-2": "git_panel::ActivateHistoryTab", }, }, + { + "context": "GitPanel && (ChangesList || HistoryList) && !GitBranchSelector && !GitRepositorySelector", + "use_key_equivalents": true, + "bindings": { + "up": "menu::SelectPrevious", + "down": "menu::SelectNext", + "shift-up": "menu::SelectPrevious", + "shift-down": "menu::SelectNext", + "enter": "menu::Confirm", + "escape": "menu::Cancel", + }, + }, { "context": "GitPanel && ChangesList && !GitBranchSelector && !GitRepositorySelector", "use_key_equivalents": true, "bindings": { "up": "git_panel::PreviousEntry", "down": "git_panel::NextEntry", - "shift-up": "menu::SelectPrevious", - "shift-down": "menu::SelectNext", "left": "git_panel::CollapseSelectedEntry", "right": "git_panel::ExpandSelectedEntry", - "enter": "menu::Confirm", "alt-y": "git::StageFile", "shift-alt-y": "git::UnstageFile", "space": "git::ToggleStaged", @@ -1047,7 +1056,6 @@ "ctrl-k ctrl-shift-c": "workspace::CopyRelativePath", "tab": "git_panel::FocusEditor", "shift-tab": "git_panel::FocusEditor", - "escape": "menu::Cancel", "alt-enter": "menu::SecondaryConfirm", "delete": ["git::RestoreFile", { "skip_prompt": false }], "backspace": ["git::RestoreFile", { "skip_prompt": false }], diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index f5ce59ce27..a1d74fc12d 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -1123,17 +1123,23 @@ "g g": "menu::SelectFirst", }, }, + { + "context": "GitPanel && (ChangesList || HistoryList) && !GitBranchSelector && !GitRepositorySelector", + "use_key_equivalents": true, + "bindings": { + "j": "menu::SelectNext", + "k": "menu::SelectPrevious", + "g f": "menu::Confirm", + }, + }, { "context": "GitPanel && ChangesList && !GitBranchSelector && !GitRepositorySelector", "use_key_equivalents": true, "bindings": { "h": "git_panel::CollapseSelectedEntry", - "j": "menu::SelectNext", - "k": "menu::SelectPrevious", "l": "git_panel::ExpandSelectedEntry", "g g": "menu::SelectFirst", "shift-g": "menu::SelectLast", - "g f": "menu::Confirm", "i": "git_panel::FocusEditor", "x": "git::ToggleStaged", "shift-x": "git::StageAll", diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 4dd2416159..0d6167298d 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1858,12 +1858,15 @@ impl GitPanel { if self.commit_editor.read(cx).is_focused(window) { dispatch_context.add("CommitEditor"); } else if self.focus_handle.contains_focused(window, cx) || self.context_menu.is_some() { - // Preserve the panel's `ChangesList` context while a context menu - // is open. Its focus handle may not appear as a descendant of the - // panel until the next frame, so `FocusHandle::contains_focused` - // would return `false`. + // Preserve the panel's list context while a context menu is open. + // Its focus handle may not appear as a descendant of the panel + // until the next frame, so `FocusHandle::contains_focused` would + // return `false`. dispatch_context.add("menu"); - dispatch_context.add("ChangesList"); + match self.active_tab { + GitPanelTab::Changes => dispatch_context.add("ChangesList"), + GitPanelTab::History => dispatch_context.add("HistoryList"), + } } dispatch_context @@ -13506,6 +13509,10 @@ mod tests { !context.contains("ChangesList"), "should not have ChangesList context when commit editor is focused" ); + assert!( + !context.contains("HistoryList"), + "should not have HistoryList context when commit editor is focused" + ); }); // Case 2: Focus the panel's focus handle directly — should have "menu" and "ChangesList". @@ -13530,13 +13537,36 @@ mod tests { context.contains("ChangesList"), "should have ChangesList context when changes list is focused" ); + assert!( + !context.contains("HistoryList"), + "should not have HistoryList context when changes list is focused" + ); assert!( !context.contains("CommitEditor"), "should not have CommitEditor context when changes list is focused" ); }); - // Case 3: Switch back to commit editor and verify context switches correctly + // Case 3: Switch to the History tab and verify its list context. + panel.update_in(cx, |panel, window, cx| { + panel.active_tab = GitPanelTab::History; + let context = panel.dispatch_context(window, cx); + assert!( + context.contains("menu"), + "should have menu context when history list is focused" + ); + assert!( + context.contains("HistoryList"), + "should have HistoryList context when history list is focused" + ); + assert!( + !context.contains("ChangesList"), + "should not have ChangesList context when history list is focused" + ); + panel.active_tab = GitPanelTab::Changes; + }); + + // Case 4: Switch back to commit editor and verify context switches correctly panel.update_in(cx, |panel, window, cx| { panel.focus_editor(&FocusEditor, window, cx); }); @@ -13553,7 +13583,7 @@ mod tests { ); }); - // Case 4: Re-focus changes list and verify it transitions back correctly + // Case 5: Re-focus changes list and verify it transitions back correctly panel.update_in(cx, |panel, window, cx| { panel.focus_handle.focus(window, cx); });