From 33f68882c131bc0214de9420ae28290db7afae95 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 16 Jul 2024 22:56:44 -0600 Subject: [PATCH] vim: Fix ctrl-d/u going to top bottom (#14620) Release Notes: - vim: Fixed ctrl-d/ctrl-u getting to top/bottom of buffer (#13250) --- crates/editor/src/scroll.rs | 5 +- crates/editor/src/scroll/scroll_amount.rs | 20 ++--- crates/vim/src/normal/scroll.rs | 97 ++++++++++++++--------- crates/vim/test_data/test_ctrl_d_u.json | 6 ++ 4 files changed, 76 insertions(+), 52 deletions(-) diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index cc39645e7c..a7a40300ad 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -476,7 +476,10 @@ impl Editor { } let cur_position = self.scroll_position(cx); - let new_pos = cur_position + point(0., amount.lines(self)); + let Some(visible_line_count) = self.visible_line_count() else { + return; + }; + let new_pos = cur_position + point(0., amount.lines(visible_line_count)); self.set_scroll_position(new_pos, cx); } diff --git a/crates/editor/src/scroll/scroll_amount.rs b/crates/editor/src/scroll/scroll_amount.rs index 6f8cc1c2e5..d115be68a0 100644 --- a/crates/editor/src/scroll/scroll_amount.rs +++ b/crates/editor/src/scroll/scroll_amount.rs @@ -1,4 +1,3 @@ -use crate::Editor; use serde::Deserialize; use ui::{px, Pixels}; @@ -11,19 +10,16 @@ pub enum ScrollAmount { } impl ScrollAmount { - pub fn lines(&self, editor: &mut Editor) -> f32 { + pub fn lines(&self, mut visible_line_count: f32) -> f32 { match self { Self::Line(count) => *count, - Self::Page(count) => editor - .visible_line_count() - .map(|mut l| { - // for full pages subtract one to leave an anchor line - if count.abs() == 1.0 { - l -= 1.0 - } - (l * count).trunc() - }) - .unwrap_or(0.), + Self::Page(count) => { + // for full pages subtract one to leave an anchor line + if count.abs() == 1.0 { + visible_line_count -= 1.0 + } + (visible_line_count * count).trunc() + } } } diff --git a/crates/vim/src/normal/scroll.rs b/crates/vim/src/normal/scroll.rs index 79ac082673..b01b287f21 100644 --- a/crates/vim/src/normal/scroll.rs +++ b/crates/vim/src/normal/scroll.rs @@ -74,46 +74,60 @@ fn scroll_editor( } editor.scroll_screen(amount, cx); - if should_move_cursor { - let visible_rows = if let Some(visible_rows) = editor.visible_line_count() { - visible_rows as u32 - } else { - return; - }; - - let top_anchor = editor.scroll_manager.anchor().anchor; - let vertical_scroll_margin = EditorSettings::get_global(cx).vertical_scroll_margin; - - editor.change_selections(None, cx, |s| { - s.move_with(|map, selection| { - let mut head = selection.head(); - let top = top_anchor.to_display_point(map); - - if preserve_cursor_position { - let old_top = old_top_anchor.to_display_point(map); - let new_row = - DisplayRow(top.row().0 + selection.head().row().0 - old_top.row().0); - head = map.clip_point(DisplayPoint::new(new_row, head.column()), Bias::Left) - } - let min_row = DisplayRow(top.row().0 + vertical_scroll_margin as u32); - let max_row = - DisplayRow(top.row().0 + visible_rows - vertical_scroll_margin as u32 - 1); - - let new_head = if head.row() < min_row { - map.clip_point(DisplayPoint::new(min_row, head.column()), Bias::Left) - } else if head.row() > max_row { - map.clip_point(DisplayPoint::new(max_row, head.column()), Bias::Left) - } else { - head - }; - if selection.is_empty() { - selection.collapse_to(new_head, selection.goal) - } else { - selection.set_head(new_head, selection.goal) - }; - }) - }); + if !should_move_cursor { + return; } + + let visible_line_count = if let Some(visible_line_count) = editor.visible_line_count() { + visible_line_count + } else { + return; + }; + + let top_anchor = editor.scroll_manager.anchor().anchor; + let vertical_scroll_margin = EditorSettings::get_global(cx).vertical_scroll_margin; + + editor.change_selections(None, cx, |s| { + s.move_with(|map, selection| { + let mut head = selection.head(); + let top = top_anchor.to_display_point(map); + + if preserve_cursor_position { + let old_top = old_top_anchor.to_display_point(map); + let new_row = if old_top.row() == top.row() { + DisplayRow( + top.row() + .0 + .saturating_add_signed(amount.lines(visible_line_count) as i32), + ) + } else { + DisplayRow(top.row().0 + selection.head().row().0 - old_top.row().0) + }; + head = map.clip_point(DisplayPoint::new(new_row, head.column()), Bias::Left) + } + let min_row = if top.row().0 == 0 { + DisplayRow(0) + } else { + DisplayRow(top.row().0 + vertical_scroll_margin as u32) + }; + let max_row = DisplayRow( + top.row().0 + visible_line_count as u32 - vertical_scroll_margin as u32 - 1, + ); + + let new_head = if head.row() < min_row { + map.clip_point(DisplayPoint::new(min_row, head.column()), Bias::Left) + } else if head.row() > max_row { + map.clip_point(DisplayPoint::new(max_row, head.column()), Bias::Left) + } else { + head + }; + if selection.is_empty() { + selection.collapse_to(new_head, selection.goal) + } else { + selection.set_head(new_head, selection.goal) + }; + }) + }); } #[cfg(test)] @@ -251,5 +265,10 @@ mod test { cx.simulate_shared_keystrokes("ctrl-d ctrl-d 4 j ctrl-u ctrl-u") .await; cx.shared_state().await.assert_matches(); + + // test returning to top + cx.simulate_shared_keystrokes("g g ctrl-d ctrl-u ctrl-u") + .await; + cx.shared_state().await.assert_matches(); } } diff --git a/crates/vim/test_data/test_ctrl_d_u.json b/crates/vim/test_data/test_ctrl_d_u.json index 4235d8d278..77c35f24b9 100644 --- a/crates/vim/test_data/test_ctrl_d_u.json +++ b/crates/vim/test_data/test_ctrl_d_u.json @@ -20,3 +20,9 @@ {"Key":"ctrl-u"} {"Key":"ctrl-u"} {"Get":{"state":"aa\nbb\ncc\ndd\nee\nff\ngg\nˇhh\nii\njj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}} +{"Key":"g"} +{"Key":"g"} +{"Key":"ctrl-d"} +{"Key":"ctrl-u"} +{"Key":"ctrl-u"} +{"Get":{"state":"ˇaa\nbb\ncc\ndd\nee\nff\ngg\nhh\nii\njj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}}