From 1be3c44550012c9c7657de0450044c973f8c2d10 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 26 Sep 2024 23:52:07 -0600 Subject: [PATCH] vim: Support za (#18421) Closes #6822 Updates #5142 Release Notes: - Added new fold actions to toggle folds (`cmd-k cmd-l`), fold every fold (`cmd-k cmd-0`) unfold every fold (`cmd-k cmd-j`) to fold recursively (`cmd-k cmd-[`) and unfold recursively (`cmd-k cmd-]`). - vim: Added `za` to toggle fold under cursor. - vim: Added `zO`/`zC`/`zA` to open, close and toggle folds recursively (and fixed `zc` to not recurse into selections). - vim: Added `zR`/`zM` to open/close all folds in the buffer. --- assets/keymaps/default-linux.json | 5 + assets/keymaps/default-macos.json | 5 + assets/keymaps/vim.json | 6 ++ crates/editor/src/actions.rs | 6 ++ crates/editor/src/editor.rs | 151 +++++++++++++++++++++++++++++- crates/editor/src/editor_tests.rs | 6 +- crates/editor/src/element.rs | 6 ++ 7 files changed, 179 insertions(+), 6 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index f15c4dfe22b6c049681d9f9573e16a674268ab24..8d4871d95648dba53c30f78df65595a6de243f0f 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -310,6 +310,11 @@ "ctrl-shift-\\": "editor::MoveToEnclosingBracket", "ctrl-shift-[": "editor::Fold", "ctrl-shift-]": "editor::UnfoldLines", + "ctrl-k ctrl-l": "editor::ToggleFold", + "ctrl-k ctrl-[": "editor::FoldRecursive", + "ctrl-k ctrl-]": "editor::UnfoldRecursive", + "ctrl-k ctrl-0": "editor::FoldAll", + "ctrl-k ctrl-j": "editor::UnfoldAll", "ctrl-space": "editor::ShowCompletions", "ctrl-.": "editor::ToggleCodeActions", "alt-ctrl-r": "editor::RevealInFileManager", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index a58112b3c0b927fcf5cafa9e32e8d474b2075062..a980ae14e22a956ec68864e69baed7ab4238e8bf 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -347,6 +347,11 @@ "cmd-shift-\\": "editor::MoveToEnclosingBracket", "alt-cmd-[": "editor::Fold", "alt-cmd-]": "editor::UnfoldLines", + "cmd-k cmd-l": "editor::ToggleFold", + "cmd-k cmd-[": "editor::FoldRecursive", + "cmd-k cmd-]": "editor::UnfoldRecursive", + "cmd-k cmd-0": "editor::FoldAll", + "cmd-k cmd-j": "editor::UnfoldAll", "ctrl-space": "editor::ShowCompletions", "cmd-.": "editor::ToggleCodeActions", "alt-cmd-r": "editor::RevealInFileManager", diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 6656ea0ddf22c3d1addcba031aff8ea68ce2ee8d..f3a088f11e5d272be5e7e29e9da3d71755cb5304 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -132,9 +132,15 @@ "z z": "editor::ScrollCursorCenter", "z .": ["workspace::SendKeystrokes", "z z ^"], "z b": "editor::ScrollCursorBottom", + "z a": "editor::ToggleFold", + "z A": "editor::ToggleFoldRecursive", "z c": "editor::Fold", + "z C": "editor::FoldRecursive", "z o": "editor::UnfoldLines", + "z O": "editor::UnfoldRecursive", "z f": "editor::FoldSelectedRanges", + "z M": "editor::FoldAll", + "z R": "editor::UnfoldAll", "shift-z shift-q": ["pane::CloseActiveItem", { "saveIntent": "skip" }], "shift-z shift-z": ["pane::CloseActiveItem", { "saveIntent": "saveAll" }], // Count support diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 2383c7f71af8a96fa6cae32ef83dbf31c8444895..b5935782580ba3f1e8b93f16358e34405564c428 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -230,7 +230,11 @@ gpui::actions!( ExpandMacroRecursively, FindAllReferences, Fold, + FoldAll, + FoldRecursive, FoldSelectedRanges, + ToggleFold, + ToggleFoldRecursive, Format, GoToDeclaration, GoToDeclarationSplit, @@ -340,7 +344,9 @@ gpui::actions!( Transpose, Undo, UndoSelection, + UnfoldAll, UnfoldLines, + UnfoldRecursive, UniqueLinesCaseInsensitive, UniqueLinesCaseSensitive, ] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b7f825df9eec606296b49eb64ed3cc2aa6908ff7..44de6014ec8137f0d01af507771df237f3e2da4c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10551,17 +10551,79 @@ impl Editor { } } - pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext) { - let mut fold_ranges = Vec::new(); + pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext) { + let selection = self.selections.newest::(cx); + + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let range = if selection.is_empty() { + let point = selection.head().to_display_point(&display_map); + let start = DisplayPoint::new(point.row(), 0).to_point(&display_map); + let end = DisplayPoint::new(point.row(), display_map.line_len(point.row())) + .to_point(&display_map); + start..end + } else { + selection.range() + }; + if display_map.folds_in_range(range).next().is_some() { + self.unfold_lines(&Default::default(), cx) + } else { + self.fold(&Default::default(), cx) + } + } + + pub fn toggle_fold_recursive( + &mut self, + _: &actions::ToggleFoldRecursive, + cx: &mut ViewContext, + ) { + let selection = self.selections.newest::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let range = if selection.is_empty() { + let point = selection.head().to_display_point(&display_map); + let start = DisplayPoint::new(point.row(), 0).to_point(&display_map); + let end = DisplayPoint::new(point.row(), display_map.line_len(point.row())) + .to_point(&display_map); + start..end + } else { + selection.range() + }; + if display_map.folds_in_range(range).next().is_some() { + self.unfold_recursive(&Default::default(), cx) + } else { + self.fold_recursive(&Default::default(), cx) + } + } + pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext) { + let mut fold_ranges = Vec::new(); + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let selections = self.selections.all_adjusted(cx); + for selection in selections { let range = selection.range().sorted(); let buffer_start_row = range.start.row; - for row in (0..=range.end.row).rev() { + if range.start.row != range.end.row { + let mut found = false; + let mut row = range.start.row; + while row <= range.end.row { + if let Some((foldable_range, fold_text)) = + { display_map.foldable_range(MultiBufferRow(row)) } + { + found = true; + row = foldable_range.end.row + 1; + fold_ranges.push((foldable_range, fold_text)); + } else { + row += 1 + } + } + if found { + continue; + } + } + + for row in (0..=range.start.row).rev() { if let Some((foldable_range, fold_text)) = display_map.foldable_range(MultiBufferRow(row)) { @@ -10578,6 +10640,61 @@ impl Editor { self.fold_ranges(fold_ranges, true, cx); } + pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext) { + let mut fold_ranges = Vec::new(); + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + + for row in 0..display_map.max_buffer_row().0 { + if let Some((foldable_range, fold_text)) = + display_map.foldable_range(MultiBufferRow(row)) + { + fold_ranges.push((foldable_range, fold_text)); + } + } + + self.fold_ranges(fold_ranges, true, cx); + } + + pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext) { + let mut fold_ranges = Vec::new(); + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let selections = self.selections.all_adjusted(cx); + + for selection in selections { + let range = selection.range().sorted(); + let buffer_start_row = range.start.row; + + if range.start.row != range.end.row { + let mut found = false; + for row in range.start.row..=range.end.row { + if let Some((foldable_range, fold_text)) = + { display_map.foldable_range(MultiBufferRow(row)) } + { + found = true; + fold_ranges.push((foldable_range, fold_text)); + } + } + if found { + continue; + } + } + + for row in (0..=range.start.row).rev() { + if let Some((foldable_range, fold_text)) = + display_map.foldable_range(MultiBufferRow(row)) + { + if foldable_range.end.row >= buffer_start_row { + fold_ranges.push((foldable_range, fold_text)); + } else { + break; + } + } + } + } + + self.fold_ranges(fold_ranges, true, cx); + } + pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext) { let buffer_row = fold_at.buffer_row; let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -10612,6 +10729,24 @@ impl Editor { self.unfold_ranges(ranges, true, true, cx); } + pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext) { + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let selections = self.selections.all::(cx); + let ranges = selections + .iter() + .map(|s| { + let mut range = s.display_range(&display_map).sorted(); + *range.start.column_mut() = 0; + *range.end.column_mut() = display_map.line_len(range.end.row()); + let start = range.start.to_point(&display_map); + let end = range.end.to_point(&display_map); + start..end + }) + .collect::>(); + + self.unfold_ranges(ranges, true, true, cx); + } + pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -10630,6 +10765,16 @@ impl Editor { self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx) } + pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext) { + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + self.unfold_ranges( + [Point::zero()..display_map.max_point().to_point(&display_map)], + true, + true, + cx, + ); + } + pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext) { let selections = self.selections.all::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index de1b12abe00778fe9762454876e129502a51a6c0..31a69918026f729c9c06ce81b322616399182929 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -852,7 +852,7 @@ fn test_fold_action(cx: &mut TestAppContext) { _ = view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ - DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0) + DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0) ]); }); view.fold(&Fold, cx); @@ -940,7 +940,7 @@ fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) { _ = view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ - DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0) + DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0) ]); }); view.fold(&Fold, cx); @@ -1022,7 +1022,7 @@ fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) { _ = view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ - DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0) + DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0) ]); }); view.fold(&Fold, cx); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index bad16b225f3298be30d9ca99b26a1e2940245cdb..e5c067e37ec3dc79ea3ade76a98ce7bb3b38497b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -335,8 +335,14 @@ impl EditorElement { register_action(view, cx, Editor::open_url); register_action(view, cx, Editor::open_file); register_action(view, cx, Editor::fold); + register_action(view, cx, Editor::fold_all); register_action(view, cx, Editor::fold_at); + register_action(view, cx, Editor::fold_recursive); + register_action(view, cx, Editor::toggle_fold); + register_action(view, cx, Editor::toggle_fold_recursive); register_action(view, cx, Editor::unfold_lines); + register_action(view, cx, Editor::unfold_recursive); + register_action(view, cx, Editor::unfold_all); register_action(view, cx, Editor::unfold_at); register_action(view, cx, Editor::fold_selected_ranges); register_action(view, cx, Editor::show_completions);