diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bdad9ba..dc41871c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added +* vim-like navigation: `j`/`k` for up/down, `l` to open preview + ### Changed * use [tombi](https://github.com/tombi-toml/tombi) for all toml file formatting * open the external editor from the status diff view [[@WaterWhisperer](https://github.com/WaterWhisperer)] ([#2805](https://github.com/gitui-org/gitui/issues/2805)) diff --git a/src/components/commit_details/details.rs b/src/components/commit_details/details.rs index 102f9522..1f3d7dfb 100644 --- a/src/components/commit_details/details.rs +++ b/src/components/commit_details/details.rs @@ -361,37 +361,26 @@ impl Component for DetailsComponent { fn event(&mut self, event: &Event) -> Result { if self.focused { if let Event::Key(e) = event { - return Ok( - if self.key_config.is_nav_up(e) { - self.move_scroll_top(ScrollType::Up).into() - } else if self.key_config.is_nav_down(e) { - self.move_scroll_top(ScrollType::Down).into() - } else if key_match( - e, - self.key_config.keys.page_up, - ) { - self.move_scroll_top(ScrollType::PageUp) - .into() - } else if key_match( - e, - self.key_config.keys.page_down, - ) { - self.move_scroll_top(ScrollType::PageDown) - .into() - } else if key_match(e, self.key_config.keys.home) - || key_match(e, self.key_config.keys.shift_up) - { - self.move_scroll_top(ScrollType::Home).into() - } else if key_match(e, self.key_config.keys.end) - || key_match( - e, - self.key_config.keys.shift_down, - ) { - self.move_scroll_top(ScrollType::End).into() - } else { - EventState::NotConsumed - }, - ); + return Ok(if self.key_config.is_nav_up(e) { + self.move_scroll_top(ScrollType::Up).into() + } else if self.key_config.is_nav_down(e) { + self.move_scroll_top(ScrollType::Down).into() + } else if key_match(e, self.key_config.keys.page_up) { + self.move_scroll_top(ScrollType::PageUp).into() + } else if key_match(e, self.key_config.keys.page_down) + { + self.move_scroll_top(ScrollType::PageDown).into() + } else if key_match(e, self.key_config.keys.home) + || key_match(e, self.key_config.keys.shift_up) + { + self.move_scroll_top(ScrollType::Home).into() + } else if key_match(e, self.key_config.keys.end) + || key_match(e, self.key_config.keys.shift_down) + { + self.move_scroll_top(ScrollType::End).into() + } else { + EventState::NotConsumed + }); } } diff --git a/src/components/commit_details/mod.rs b/src/components/commit_details/mod.rs index 5f863ec9..972443c5 100644 --- a/src/components/commit_details/mod.rs +++ b/src/components/commit_details/mod.rs @@ -7,10 +7,7 @@ use super::{ Component, DrawableComponent, EventState, StatusTreeComponent, }; use crate::{ - accessors, - app::Environment, - keys::SharedKeyConfig, - strings, + accessors, app::Environment, keys::SharedKeyConfig, strings, }; use anyhow::Result; use asyncgit::{ diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 5ff58c95..7a55386e 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -830,41 +830,37 @@ impl DrawableComponent for CommitList { impl Component for CommitList { fn event(&mut self, ev: &Event) -> Result { if let Event::Key(k) = ev { - let selection_changed = - if self.key_config.is_nav_up(k) { - self.move_selection(ScrollType::Up)? - } else if self.key_config.is_nav_down(k) { - self.move_selection(ScrollType::Down)? - } else if key_match(k, self.key_config.keys.shift_up) - || key_match(k, self.key_config.keys.home) - { - self.move_selection(ScrollType::Home)? - } else if key_match( - k, - self.key_config.keys.shift_down, - ) || key_match(k, self.key_config.keys.end) - { - self.move_selection(ScrollType::End)? - } else if key_match(k, self.key_config.keys.page_up) { - self.move_selection(ScrollType::PageUp)? - } else if key_match(k, self.key_config.keys.page_down) - { - self.move_selection(ScrollType::PageDown)? - } else if key_match( - k, - self.key_config.keys.log_mark_commit, - ) { - self.mark(); - true - } else if key_match( - k, - self.key_config.keys.log_checkout_commit, - ) { - self.checkout(); - true - } else { - false - }; + let selection_changed = if self.key_config.is_nav_up(k) { + self.move_selection(ScrollType::Up)? + } else if self.key_config.is_nav_down(k) { + self.move_selection(ScrollType::Down)? + } else if key_match(k, self.key_config.keys.shift_up) + || key_match(k, self.key_config.keys.home) + { + self.move_selection(ScrollType::Home)? + } else if key_match(k, self.key_config.keys.shift_down) + || key_match(k, self.key_config.keys.end) + { + self.move_selection(ScrollType::End)? + } else if key_match(k, self.key_config.keys.page_up) { + self.move_selection(ScrollType::PageUp)? + } else if key_match(k, self.key_config.keys.page_down) { + self.move_selection(ScrollType::PageDown)? + } else if key_match( + k, + self.key_config.keys.log_mark_commit, + ) { + self.mark(); + true + } else if key_match( + k, + self.key_config.keys.log_checkout_commit, + ) { + self.checkout(); + true + } else { + false + }; return Ok(selection_changed.into()); } diff --git a/src/components/diff.rs b/src/components/diff.rs index d28a0599..35a04a77 100644 --- a/src/components/diff.rs +++ b/src/components/diff.rs @@ -864,7 +864,8 @@ impl Component for DiffComponent { self.horizontal_scroll .move_right(HorizontalScrollType::Right); Ok(EventState::Consumed) - } else if key_match(e, self.key_config.keys.move_left) { + } else if key_match(e, self.key_config.keys.move_left) + { self.horizontal_scroll .move_right(HorizontalScrollType::Left); Ok(EventState::Consumed) diff --git a/src/components/status_tree.rs b/src/components/status_tree.rs index 7c01ac38..eb15cacf 100644 --- a/src/components/status_tree.rs +++ b/src/components/status_tree.rs @@ -543,7 +543,8 @@ impl Component for StatusTreeComponent { Ok(self .move_selection(MoveSelection::PageDown) .into()) - } else if key_match(e, self.key_config.keys.move_left) { + } else if key_match(e, self.key_config.keys.move_left) + { Ok(self .move_selection(MoveSelection::Left) .into()) diff --git a/src/keys/key_config.rs b/src/keys/key_config.rs index 07b2b6f5..4f5ebe55 100644 --- a/src/keys/key_config.rs +++ b/src/keys/key_config.rs @@ -153,6 +153,71 @@ mod tests { use std::io::Write; use tempfile::NamedTempFile; + fn key_event(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent { + KeyEvent::new(code, modifiers) + } + + #[test] + fn test_vim_nav_up() { + let config = KeyConfig::default(); + assert!(config + .is_nav_up(&key_event(KeyCode::Up, KeyModifiers::NONE))); + assert!(config.is_nav_up(&key_event( + KeyCode::Char('k'), + KeyModifiers::NONE + ))); + assert!(!config.is_nav_up(&key_event( + KeyCode::Char('k'), + KeyModifiers::CONTROL + ))); + assert!(!config.is_nav_up(&key_event( + KeyCode::Char('j'), + KeyModifiers::NONE + ))); + } + + #[test] + fn test_vim_nav_down() { + let config = KeyConfig::default(); + assert!(config.is_nav_down(&key_event( + KeyCode::Down, + KeyModifiers::NONE + ))); + assert!(config.is_nav_down(&key_event( + KeyCode::Char('j'), + KeyModifiers::NONE + ))); + assert!(!config.is_nav_down(&key_event( + KeyCode::Char('j'), + KeyModifiers::CONTROL + ))); + assert!(!config.is_nav_down(&key_event( + KeyCode::Char('k'), + KeyModifiers::NONE + ))); + } + + #[test] + fn test_vim_nav_right() { + let config = KeyConfig::default(); + assert!(config.is_nav_right(&key_event( + KeyCode::Right, + KeyModifiers::NONE + ))); + assert!(config.is_nav_right(&key_event( + KeyCode::Char('l'), + KeyModifiers::NONE + ))); + assert!(!config.is_nav_right(&key_event( + KeyCode::Char('l'), + KeyModifiers::CONTROL + ))); + assert!(!config.is_nav_right(&key_event( + KeyCode::Char('j'), + KeyModifiers::NONE + ))); + } + #[test] fn test_get_hint() { let config = KeyConfig::default(); diff --git a/src/popups/compare_commits.rs b/src/popups/compare_commits.rs index f70f93bc..5694720b 100644 --- a/src/popups/compare_commits.rs +++ b/src/popups/compare_commits.rs @@ -129,7 +129,8 @@ impl Component for CompareCommitsPopup { { self.details.focus(false); self.diff.focus(true); - } else if key_match(e, self.key_config.keys.move_left) { + } else if key_match(e, self.key_config.keys.move_left) + { self.hide_stacked(false); } diff --git a/src/popups/inspect_commit.rs b/src/popups/inspect_commit.rs index a7b2d83e..1396debd 100644 --- a/src/popups/inspect_commit.rs +++ b/src/popups/inspect_commit.rs @@ -166,7 +166,8 @@ impl Component for InspectCommitPopup { { self.details.focus(false); self.diff.focus(true); - } else if key_match(e, self.key_config.keys.move_left) { + } else if key_match(e, self.key_config.keys.move_left) + { self.hide_stacked(false); } else if key_match( e, diff --git a/src/popups/options.rs b/src/popups/options.rs index 28ac17aa..5ae1abae 100644 --- a/src/popups/options.rs +++ b/src/popups/options.rs @@ -328,7 +328,10 @@ impl Component for OptionsPopup { self.move_selection(false); } else if self.key_config.is_nav_right(key) { self.switch_option(true); - } else if key_match(key, self.key_config.keys.move_left) { + } else if key_match( + key, + self.key_config.keys.move_left, + ) { self.switch_option(false); } }