diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index efa98299bcd2cc8422b469b626f74adbf0a7e287..ed0cf894849601bcebcb0117689de2e69e27259d 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -228,7 +228,12 @@ "replace_newest": true } ], - "cmd-/": "editor::ToggleComments", + "cmd-/": [ + "editor::ToggleComments", + { + "advance_downwards": false + } + ], "alt-up": "editor::SelectLargerSyntaxNode", "alt-down": "editor::SelectSmallerSyntaxNode", "cmd-u": "editor::UndoSelection", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2cf0c9a36f5d2fcc3ac4776d11964ddd5e3ec49e..b8c2957db2af83d6cf33201c1b0d4d63a9fec0ba 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -154,6 +154,12 @@ pub struct ConfirmCodeAction { pub item_ix: Option, } +#[derive(Clone, Default, Deserialize, PartialEq)] +pub struct ToggleComments { + #[serde(default)] + pub advance_downwards: bool, +} + actions!( editor, [ @@ -216,7 +222,6 @@ actions!( AddSelectionBelow, Tab, TabPrev, - ToggleComments, ShowCharacterPalette, SelectLargerSyntaxNode, SelectSmallerSyntaxNode, @@ -251,6 +256,7 @@ impl_actions!( MovePageDown, ConfirmCompletion, ConfirmCodeAction, + ToggleComments, ] ); @@ -3804,7 +3810,7 @@ impl Editor { } } - if matches!(self.mode, EditorMode::SingleLine) { + if self.mode == EditorMode::SingleLine { cx.propagate_action(); return; } @@ -4466,7 +4472,7 @@ impl Editor { } } - pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { + pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext) { self.transact(cx, |this, cx| { let mut selections = this.selections.all::(cx); let mut edits = Vec::new(); @@ -4685,6 +4691,34 @@ impl Editor { drop(snapshot); this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections)); + + let selections = this.selections.all::(cx); + let selections_on_single_row = selections.windows(2).all(|selections| { + selections[0].start.row == selections[1].start.row + && selections[0].end.row == selections[1].end.row + && selections[0].start.row == selections[0].end.row + }); + let selections_selecting = selections + .iter() + .any(|selection| selection.start != selection.end); + let advance_downwards = action.advance_downwards + && selections_on_single_row + && !selections_selecting + && this.mode != EditorMode::SingleLine; + + if advance_downwards { + let snapshot = this.buffer.read(cx).snapshot(cx); + + this.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.move_cursors_with(|display_snapshot, display_point, _| { + let mut point = display_point.to_point(display_snapshot); + point.row += 1; + point = snapshot.clip_point(point, Bias::Left); + let display_point = point.to_display_point(display_snapshot); + (display_point, SelectionGoal::Column(display_point.column())) + }) + }); + } }); } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 34af0d2166a623b1bdc790ecccf5bbc5e077f7c4..9b5cececff3659921a1ccbc12754158c709ecaf7 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -4451,7 +4451,7 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6), ]) }); - editor.toggle_comments(&ToggleComments, cx); + editor.toggle_comments(&ToggleComments::default(), cx); assert_eq!( editor.text(cx), " @@ -4469,7 +4469,7 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)]) }); - editor.toggle_comments(&ToggleComments, cx); + editor.toggle_comments(&ToggleComments::default(), cx); assert_eq!( editor.text(cx), " @@ -4486,7 +4486,7 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)]) }); - editor.toggle_comments(&ToggleComments, cx); + editor.toggle_comments(&ToggleComments::default(), cx); assert_eq!( editor.text(cx), " @@ -4501,6 +4501,139 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { }); } +#[gpui::test] +async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + cx.update(|cx| cx.set_global(Settings::test(cx))); + + let language = Arc::new(Language::new( + LanguageConfig { + line_comment: Some("// ".into()), + ..Default::default() + }, + Some(tree_sitter_rust::language()), + )); + + let registry = Arc::new(LanguageRegistry::test()); + registry.add(language.clone()); + + cx.update_buffer(|buffer, cx| { + buffer.set_language_registry(registry); + buffer.set_language(Some(language), cx); + }); + + let toggle_comments = &ToggleComments { + advance_downwards: true, + }; + + // Single cursor on one line -> advance + // Cursor moves horizontally 3 characters as well on non-blank line + cx.set_state(indoc!( + "fn a() { + ˇdog(); + cat(); + }" + )); + cx.update_editor(|editor, cx| { + editor.toggle_comments(toggle_comments, cx); + }); + cx.assert_editor_state(indoc!( + "fn a() { + // dog(); + catˇ(); + }" + )); + + // Single selection on one line -> don't advance + cx.set_state(indoc!( + "fn a() { + «dog()ˇ»; + cat(); + }" + )); + cx.update_editor(|editor, cx| { + editor.toggle_comments(toggle_comments, cx); + }); + cx.assert_editor_state(indoc!( + "fn a() { + // «dog()ˇ»; + cat(); + }" + )); + + // Multiple cursors on one line -> advance + cx.set_state(indoc!( + "fn a() { + ˇdˇog(); + cat(); + }" + )); + cx.update_editor(|editor, cx| { + editor.toggle_comments(toggle_comments, cx); + }); + cx.assert_editor_state(indoc!( + "fn a() { + // dog(); + catˇ(ˇ); + }" + )); + + // Multiple cursors on one line, with selection -> don't advance + cx.set_state(indoc!( + "fn a() { + ˇdˇog«()ˇ»; + cat(); + }" + )); + cx.update_editor(|editor, cx| { + editor.toggle_comments(toggle_comments, cx); + }); + cx.assert_editor_state(indoc!( + "fn a() { + // ˇdˇog«()ˇ»; + cat(); + }" + )); + + // Single cursor on one line -> advance + // Cursor moves to column 0 on blank line + cx.set_state(indoc!( + "fn a() { + ˇdog(); + + cat(); + }" + )); + cx.update_editor(|editor, cx| { + editor.toggle_comments(toggle_comments, cx); + }); + cx.assert_editor_state(indoc!( + "fn a() { + // dog(); + ˇ + cat(); + }" + )); + + // Single cursor on one line -> advance + // Cursor starts and ends at column 0 + cx.set_state(indoc!( + "fn a() { + ˇ dog(); + cat(); + }" + )); + cx.update_editor(|editor, cx| { + editor.toggle_comments(toggle_comments, cx); + }); + cx.assert_editor_state(indoc!( + "fn a() { + // dog(); + ˇ cat(); + }" + )); +} + #[gpui::test] async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx); @@ -4551,7 +4684,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { "# .unindent(), ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx)); cx.assert_editor_state( &r#" @@ -4560,7 +4693,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { "# .unindent(), ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx)); cx.assert_editor_state( &r#"

A

ˇ @@ -4582,7 +4715,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { .unindent(), ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx)); cx.assert_editor_state( &r#" diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 09525f14e5a81f3c52bba8333a84183e46421ce6..52ca7d232494506d88fa5d978368267df0a3fb91 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -146,7 +146,7 @@ pub fn menus() -> Vec> { MenuItem::Separator, MenuItem::Action { name: "Toggle Line Comment", - action: Box::new(editor::ToggleComments), + action: Box::new(editor::ToggleComments::default()), }, MenuItem::Action { name: "Emoji & Symbols",