From dcf56144b5378c422070fadb5f9bd9cf99b01238 Mon Sep 17 00:00:00 2001 From: Dino Date: Tue, 11 Nov 2025 10:04:30 +0000 Subject: [PATCH] vim: Sort whole buffer when no range is specified (#42376) - Introduce a `default_range` field to `VimCommand`, to be optionally used when no range is specified for the command - Update `VimCommand.parse` to take into consideration the `default_range` - Introduce `CommandRange::buffer` to obtain the `CommandRange` which corresponds to the whole buffer - Update the `VimCommand` definitions for both `sort` and `sort i` to default to the whole buffer when no range is specified Closes #41750 Release Notes: - Improved vim's `:sort` command to sort the buffer's content when no selection is used --- crates/vim/src/command.rs | 133 +++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index b44ec8907399a7d744f91d9e90b397a174d5f85a..cba8351e8d36e784c77c20b15ac0dead41f84a13 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -725,6 +725,8 @@ struct VimCommand { args: Option< Box, String) -> Option> + Send + Sync + 'static>, >, + /// Optional range Range to use if no range is specified. + default_range: Option, range: Option< Box< dyn Fn(Box, &CommandRange) -> Option> @@ -793,6 +795,11 @@ impl VimCommand { self } + fn default_range(mut self, range: CommandRange) -> Self { + self.default_range = Some(range); + self + } + fn count(mut self) -> Self { self.has_count = true; self @@ -923,6 +930,7 @@ impl VimCommand { self.args.as_ref()?(action, args)? }; + let range = range.as_ref().or(self.default_range.as_ref()); if let Some(range) = range { self.range.as_ref().and_then(|f| f(action, range)) } else { @@ -1121,6 +1129,7 @@ impl CommandRange { self.end.as_ref().unwrap_or(&self.start) } + /// Convert the `CommandRange` into a `Range`. pub(crate) fn buffer_range( &self, vim: &Vim, @@ -1152,6 +1161,14 @@ impl CommandRange { None } } + + /// The `CommandRange` representing the entire buffer. + fn buffer() -> Self { + Self { + start: Position::Line { row: 1, offset: 0 }, + end: Some(Position::LastLine { offset: 0 }), + } + } } fn generate_commands(_: &App) -> Vec { @@ -1421,8 +1438,12 @@ fn generate_commands(_: &App) -> Vec { VimCommand::new(("delm", "arks"), ArgumentRequired) .bang(DeleteMarks::AllLocal) .args(|_, args| Some(DeleteMarks::Marks(args).boxed_clone())), - VimCommand::new(("sor", "t"), SortLinesCaseSensitive).range(select_range), - VimCommand::new(("sort i", ""), SortLinesCaseInsensitive).range(select_range), + VimCommand::new(("sor", "t"), SortLinesCaseSensitive) + .range(select_range) + .default_range(CommandRange::buffer()), + VimCommand::new(("sort i", ""), SortLinesCaseInsensitive) + .range(select_range) + .default_range(CommandRange::buffer()), VimCommand::str(("E", "xplore"), "project_panel::ToggleFocus"), VimCommand::str(("H", "explore"), "project_panel::ToggleFocus"), VimCommand::str(("L", "explore"), "project_panel::ToggleFocus"), @@ -2898,4 +2919,112 @@ mod test { ); }); } + + #[gpui::test] + async fn test_sort_commands(cx: &mut TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state( + indoc! {" + «hornet + quirrel + elderbug + cornifer + idaˇ» + "}, + Mode::Visual, + ); + + cx.simulate_keystrokes(": sort"); + cx.simulate_keystrokes("enter"); + + cx.assert_state( + indoc! {" + ˇcornifer + elderbug + hornet + ida + quirrel + "}, + Mode::Normal, + ); + + // Assert that, by default, `:sort` takes case into consideration. + cx.set_state( + indoc! {" + «hornet + quirrel + Elderbug + cornifer + idaˇ» + "}, + Mode::Visual, + ); + + cx.simulate_keystrokes(": sort"); + cx.simulate_keystrokes("enter"); + + cx.assert_state( + indoc! {" + ˇElderbug + cornifer + hornet + ida + quirrel + "}, + Mode::Normal, + ); + + // Assert that, if the `i` option is passed, `:sort` ignores case. + cx.set_state( + indoc! {" + «hornet + quirrel + Elderbug + cornifer + idaˇ» + "}, + Mode::Visual, + ); + + cx.simulate_keystrokes(": sort space i"); + cx.simulate_keystrokes("enter"); + + cx.assert_state( + indoc! {" + ˇcornifer + Elderbug + hornet + ida + quirrel + "}, + Mode::Normal, + ); + + // When no range is provided, sorts the whole buffer. + cx.set_state( + indoc! {" + ˇhornet + quirrel + elderbug + cornifer + ida + "}, + Mode::Normal, + ); + + cx.simulate_keystrokes(": sort"); + cx.simulate_keystrokes("enter"); + + cx.assert_state( + indoc! {" + ˇcornifer + elderbug + hornet + ida + quirrel + "}, + Mode::Normal, + ); + } }