From 633b665379c18a06959102c3857889b8bcbde4bb Mon Sep 17 00:00:00 2001 From: Auf keinen Fall Jens Date: Thu, 31 Oct 2024 09:39:57 +0100 Subject: [PATCH] Option to insert comment character(s) at the beginning of the line(s) (#19746) Closes #19459 This PR adds the optional setting to insert comment character(s) at the beginning of the line(s) instead of after the indentation. It can be enabled via keybindings: ``` "ctrl-/": ["editor::ToggleComments", { "ignore_indent": true }] ``` As suggested by @notpeter in #19459, this is implemented in `toggle_comments` (editor.rs) taking the existing `advance_downwards` option as example. There's also a test case for the setting, which mimics the test case for the regular comment toggling behavior. --- I am not entirely happy with the name `ignore_indent`. The default would be a double negative now `ignore_indent=false`. A positive wording would probably easier to understand, but I could not think of anything concise. `insert_at_line_start` or just `at_line_start` might work, but didn't convince me either. That said, I am happy to change the name if there are better ideas. --- Release Notes: - Added optional setting to insert comment character(s) at the beginning of the line(s) instead of after the indentation. It can be used by changing the default mapping to toggle comments like this: `"ctrl-/": ["editor::ToggleComments", { "ignore_indent": true }]` --- crates/editor/src/actions.rs | 2 + crates/editor/src/editor.rs | 23 +++++- crates/editor/src/editor_tests.rs | 126 ++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 83379e13aed32..474655700cf3f 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -80,6 +80,8 @@ pub struct ConfirmCodeAction { pub struct ToggleComments { #[serde(default)] pub advance_downwards: bool, + #[serde(default)] + pub ignore_indent: bool, } #[derive(PartialEq, Clone, Deserialize, Default)] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2e88df6b9236b..16ba3d8b8ba25 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8664,14 +8664,22 @@ impl Editor { let snapshot = this.buffer.read(cx).read(cx); let empty_str: Arc = Arc::default(); let mut suffixes_inserted = Vec::new(); + let ignore_indent = action.ignore_indent; fn comment_prefix_range( snapshot: &MultiBufferSnapshot, row: MultiBufferRow, comment_prefix: &str, comment_prefix_whitespace: &str, + ignore_indent: bool, ) -> Range { - let start = Point::new(row.0, snapshot.indent_size_for_line(row).len); + let indent_size = if ignore_indent { + 0 + } else { + snapshot.indent_size_for_line(row).len + }; + + let start = Point::new(row.0, indent_size); let mut line_bytes = snapshot .bytes_in_range(start..snapshot.max_point()) @@ -8767,7 +8775,16 @@ impl Editor { } // If the language has line comments, toggle those. - let full_comment_prefixes = language.line_comment_prefixes(); + let mut full_comment_prefixes = language.line_comment_prefixes().to_vec(); + + // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes + if ignore_indent { + full_comment_prefixes = full_comment_prefixes + .into_iter() + .map(|s| Arc::from(s.trim_end())) + .collect(); + } + if !full_comment_prefixes.is_empty() { let first_prefix = full_comment_prefixes .first() @@ -8794,6 +8811,7 @@ impl Editor { row, &prefix[..trimmed_prefix_len], &prefix[trimmed_prefix_len..], + ignore_indent, ) }) .max_by_key(|range| range.end.column - range.start.column) @@ -8834,6 +8852,7 @@ impl Editor { start_row, comment_prefix, comment_prefix_whitespace, + ignore_indent, ); let suffix_range = comment_suffix_range( snapshot.deref(), diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index d56b22b454208..02583889bca84 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -8533,6 +8533,131 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { "}); } +#[gpui::test] +async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx).await; + let language = Arc::new(Language::new( + LanguageConfig { + line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()], + ..Default::default() + }, + Some(tree_sitter_rust::LANGUAGE.into()), + )); + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); + + let toggle_comments = &ToggleComments { + advance_downwards: false, + ignore_indent: true, + }; + + // If multiple selections intersect a line, the line is only toggled once. + cx.set_state(indoc! {" + fn a() { + // «b(); + // c(); + // ˇ» d(); + } + "}); + + cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx)); + + cx.assert_editor_state(indoc! {" + fn a() { + «b(); + c(); + ˇ» d(); + } + "}); + + // The comment prefix is inserted at the beginning of each line + cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx)); + + cx.assert_editor_state(indoc! {" + fn a() { + // «b(); + // c(); + // ˇ» d(); + } + "}); + + // If a selection ends at the beginning of a line, that line is not toggled. + cx.set_selections_state(indoc! {" + fn a() { + // b(); + // «c(); + ˇ»// d(); + } + "}); + + cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx)); + + cx.assert_editor_state(indoc! {" + fn a() { + // b(); + «c(); + ˇ»// d(); + } + "}); + + // If a selection span a single line and is empty, the line is toggled. + cx.set_state(indoc! {" + fn a() { + a(); + b(); + ˇ + } + "}); + + cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx)); + + cx.assert_editor_state(indoc! {" + fn a() { + a(); + b(); + //ˇ + } + "}); + + // If a selection span multiple lines, empty lines are not toggled. + cx.set_state(indoc! {" + fn a() { + «a(); + + c();ˇ» + } + "}); + + cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx)); + + cx.assert_editor_state(indoc! {" + fn a() { + // «a(); + + // c();ˇ» + } + "}); + + // If a selection includes multiple comment prefixes, all lines are uncommented. + cx.set_state(indoc! {" + fn a() { + // «a(); + /// b(); + //! c();ˇ» + } + "}); + + cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx)); + + cx.assert_editor_state(indoc! {" + fn a() { + «a(); + b(); + c();ˇ» + } + "}); +} + #[gpui::test] async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); @@ -8554,6 +8679,7 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) let toggle_comments = &ToggleComments { advance_downwards: true, + ignore_indent: false, }; // Single cursor on one line -> advance