about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-27 12:25:43 +0000
committerbors <bors@rust-lang.org>2024-08-27 12:25:43 +0000
commit199c01df7a138694387a8635e7b10a0ff1578d14 (patch)
tree06ead91569b6e7771f190e69cce1a8eaeb9a2d0a
parent2dce25020e412fbb6a33b94def67e2958e6f2dbf (diff)
parentc9a3b022818fb61b7af1e2bcdb6b707ea9f6df6f (diff)
downloadrust-199c01df7a138694387a8635e7b10a0ff1578d14.tar.gz
rust-199c01df7a138694387a8635e7b10a0ff1578d14.zip
Auto merge of #17757 - alibektas:toggle_macro_delimiters, r=Veykril
assist: Add new assist toggle_macro_delimiter

Closes #17716
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs256
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs21
3 files changed, 279 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
new file mode 100644
index 00000000000..eedb2ea3b9a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -0,0 +1,256 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::{
+    ast::{self, make},
+    ted, AstNode, T,
+};
+
+use crate::{AssistContext, Assists};
+
+// Assist: toggle_macro_delimiter
+//
+// Change macro delimiters in the order of `( -> { -> [ -> (`.
+//
+// ```
+// macro_rules! sth {
+//     () => {};
+// }
+//
+// sth!$0( );
+// ```
+// ->
+// ```
+// macro_rules! sth {
+//     () => {};
+// }
+//
+// sth!{ }
+// ```
+pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    #[derive(Debug)]
+    enum MacroDelims {
+        LPar,
+        RPar,
+        LBra,
+        RBra,
+        LCur,
+        RCur,
+    }
+
+    let makro = ctx.find_node_at_offset::<ast::MacroCall>()?.clone_for_update();
+    let makro_text_range = makro.syntax().text_range();
+
+    let cursor_offset = ctx.offset();
+    let semicolon = makro.semicolon_token();
+    let token_tree = makro.token_tree()?;
+
+    let ltoken = token_tree.left_delimiter_token()?;
+    let rtoken = token_tree.right_delimiter_token()?;
+
+    if !ltoken.text_range().contains(cursor_offset) && !rtoken.text_range().contains(cursor_offset)
+    {
+        return None;
+    }
+
+    let token = match ltoken.kind() {
+        T!['{'] => MacroDelims::LCur,
+        T!['('] => MacroDelims::LPar,
+        T!['['] => MacroDelims::LBra,
+        T!['}'] => MacroDelims::RBra,
+        T![')'] => MacroDelims::RPar,
+        T!['}'] => MacroDelims::RCur,
+        _ => return None,
+    };
+
+    acc.add(
+        AssistId("toggle_macro_delimiter", AssistKind::Refactor),
+        match token {
+            MacroDelims::LPar => "Replace delimiters with braces",
+            MacroDelims::RPar => "Replace delimiters with braces",
+            MacroDelims::LBra => "Replace delimiters with parentheses",
+            MacroDelims::RBra => "Replace delimiters with parentheses",
+            MacroDelims::LCur => "Replace delimiters with brackets",
+            MacroDelims::RCur => "Replace delimiters with brackets",
+        },
+        token_tree.syntax().text_range(),
+        |builder| {
+            match token {
+                MacroDelims::LPar | MacroDelims::RPar => {
+                    ted::replace(ltoken, make::token(T!['{']));
+                    ted::replace(rtoken, make::token(T!['}']));
+                    if let Some(sc) = semicolon {
+                        ted::remove(sc);
+                    }
+                }
+                MacroDelims::LBra | MacroDelims::RBra => {
+                    ted::replace(ltoken, make::token(T!['(']));
+                    ted::replace(rtoken, make::token(T![')']));
+                }
+                MacroDelims::LCur | MacroDelims::RCur => {
+                    ted::replace(ltoken, make::token(T!['[']));
+                    ted::replace(rtoken, make::token(T![']']));
+                }
+            }
+            builder.replace(makro_text_range, makro.syntax().text());
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn test_par() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0( );
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!{ }
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_braces() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0{ };
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth![ ];
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_brackets() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0[ ];
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!( );
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_indent() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+mod abc {
+    macro_rules! sth {
+        () => {};
+    }
+
+    sth!$0{ };
+}
+            "#,
+            r#"
+mod abc {
+    macro_rules! sth {
+        () => {};
+    }
+
+    sth![ ];
+}
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_unrelated_par() {
+        check_assist_not_applicable(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!(($03 + 5));
+
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_longer_macros() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!$0((3 + 5));
+"#,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!{(3 + 5)}
+"#,
+        )
+    }
+
+    // FIXME @alibektas : Inner macro_call is not seen as such. So this doesn't work.
+    #[test]
+    fn test_nested_macros() {
+        check_assist_not_applicable(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+macro_rules! abc {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!{abc!($03 + 5)};
+"#,
+        )
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index a9399ba6b7f..b97f0f27398 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -213,6 +213,7 @@ mod handlers {
     mod term_search;
     mod toggle_async_sugar;
     mod toggle_ignore;
+    mod toggle_macro_delimiter;
     mod unmerge_match_arm;
     mod unmerge_use;
     mod unnecessary_async;
@@ -343,6 +344,7 @@ mod handlers {
             split_import::split_import,
             term_search::term_search,
             toggle_ignore::toggle_ignore,
+            toggle_macro_delimiter::toggle_macro_delimiter,
             unmerge_match_arm::unmerge_match_arm,
             unmerge_use::unmerge_use,
             unnecessary_async::unnecessary_async,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index a2287b2977d..595ce1affb0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -3092,6 +3092,27 @@ fn arithmetics {
 }
 
 #[test]
+fn doctest_toggle_macro_delimiter() {
+    check_doc_test(
+        "toggle_macro_delimiter",
+        r#####"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0( );
+"#####,
+        r#####"
+macro_rules! sth {
+    () => {};
+}
+
+sth!{ }
+"#####,
+    )
+}
+
+#[test]
 fn doctest_unmerge_match_arm() {
     check_doc_test(
         "unmerge_match_arm",