about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-03-02 07:41:42 +0000
committerGitHub <noreply@github.com>2025-03-02 07:41:42 +0000
commit768749f41dcfb11a77a160de704cecc83be28a0f (patch)
tree70f8ff0909c4fc1ecd02277a456c50fa76dffffd
parent7d8e95806ce9f5f352d8bae7972cdae89d1af9bb (diff)
parent1b85befbd9ffd44b8effc9607baec40a483c7d28 (diff)
downloadrust-768749f41dcfb11a77a160de704cecc83be28a0f.tar.gz
rust-768749f41dcfb11a77a160de704cecc83be28a0f.zip
Merge pull request #19259 from Veykril/push-skmvrmtorqso
Add flip or-pattern assist
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs80
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs9
-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.rs17
-rw-r--r--src/tools/rust-analyzer/docs/book/src/assists_generated.md24
7 files changed, 162 insertions, 45 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
index 64e77b2d698..b1189f0d0b0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
@@ -52,6 +52,10 @@ pub(crate) struct AssistContext<'a> {
     frange: FileRange,
     trimmed_range: TextRange,
     source_file: SourceFile,
+    // We cache this here to speed up things slightly
+    token_at_offset: TokenAtOffset<SyntaxToken>,
+    // We cache this here to speed up things slightly
+    covering_element: SyntaxElement,
 }
 
 impl<'a> AssistContext<'a> {
@@ -78,8 +82,18 @@ impl<'a> AssistContext<'a> {
             // Selection solely consists of whitespace so just fall back to the original
             _ => frange.range,
         };
-
-        AssistContext { config, sema, frange, source_file, trimmed_range }
+        let token_at_offset = source_file.syntax().token_at_offset(frange.range.start());
+        let covering_element = source_file.syntax().covering_element(trimmed_range);
+
+        AssistContext {
+            config,
+            sema,
+            frange,
+            source_file,
+            trimmed_range,
+            token_at_offset,
+            covering_element,
+        }
     }
 
     pub(crate) fn db(&self) -> &RootDatabase {
@@ -114,7 +128,7 @@ impl<'a> AssistContext<'a> {
     }
 
     pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
-        self.source_file.syntax().token_at_offset(self.offset())
+        self.token_at_offset.clone()
     }
     pub(crate) fn find_token_syntax_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
         self.token_at_offset().find(|it| it.kind() == kind)
@@ -136,7 +150,7 @@ impl<'a> AssistContext<'a> {
     }
     /// Returns the element covered by the selection range, this excludes trailing whitespace in the selection.
     pub(crate) fn covering_element(&self) -> SyntaxElement {
-        self.source_file.syntax().covering_element(self.selection_trimmed())
+        self.covering_element.clone()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs
index 95e035c0537..dd27269b001 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs
@@ -1,8 +1,8 @@
 use syntax::{
     algo::non_trivia_sibling,
     ast::{self, syntax_factory::SyntaxFactory},
-    syntax_editor::{Element, SyntaxMapping},
-    AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxToken, T,
+    syntax_editor::SyntaxMapping,
+    AstNode, Direction, NodeOrToken, SyntaxKind, SyntaxToken, T,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -39,37 +39,24 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
         return None;
     }
 
-    let prev = match prev {
-        SyntaxElement::Node(node) => node.syntax_element(),
-        _ => prev,
-    };
-    let next = match next {
-        SyntaxElement::Node(node) => node.syntax_element(),
-        _ => next,
-    };
-
-    acc.add(
-        AssistId("flip_comma", AssistKind::RefactorRewrite),
-        "Flip comma",
-        comma.text_range(),
-        |builder| {
-            let parent = comma.parent().unwrap();
-            let mut editor = builder.make_editor(&parent);
-
-            if let Some(parent) = ast::TokenTree::cast(parent) {
-                // An attribute. It often contains a path followed by a
-                // token tree (e.g. `align(2)`), so we have to be smarter.
-                let (new_tree, mapping) = flip_tree(parent.clone(), comma);
-                editor.replace(parent.syntax(), new_tree.syntax());
-                editor.add_mappings(mapping);
-            } else {
-                editor.replace(prev.clone(), next.clone());
-                editor.replace(next.clone(), prev.clone());
-            }
-
-            builder.add_file_edits(ctx.file_id(), editor);
-        },
-    )
+    let target = comma.text_range();
+    acc.add(AssistId("flip_comma", AssistKind::RefactorRewrite), "Flip comma", target, |builder| {
+        let parent = comma.parent().unwrap();
+        let mut editor = builder.make_editor(&parent);
+
+        if let Some(parent) = ast::TokenTree::cast(parent) {
+            // An attribute. It often contains a path followed by a
+            // token tree (e.g. `align(2)`), so we have to be smarter.
+            let (new_tree, mapping) = flip_tree(parent.clone(), comma);
+            editor.replace(parent.syntax(), new_tree.syntax());
+            editor.add_mappings(mapping);
+        } else {
+            editor.replace(prev.clone(), next.clone());
+            editor.replace(next.clone(), prev.clone());
+        }
+
+        builder.add_file_edits(ctx.file_id(), editor);
+    })
 }
 
 fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, SyntaxMapping) {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs
new file mode 100644
index 00000000000..d9fa03e7191
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs
@@ -0,0 +1,80 @@
+use syntax::{
+    algo::non_trivia_sibling,
+    ast::{self, AstNode},
+    Direction, T,
+};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: flip_or_pattern
+//
+// Flips two patterns in an or-pattern.
+//
+// ```
+// fn foo() {
+//     let (a |$0 b) = 1;
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+//     let (b | a) = 1;
+// }
+// ```
+pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    // Only flip on the `|` token
+    let pipe = ctx.find_token_syntax_at_offset(T![|])?;
+
+    let parent = ast::OrPat::cast(pipe.parent()?)?;
+
+    let before = non_trivia_sibling(pipe.clone().into(), Direction::Prev)?.into_node()?;
+    let after = non_trivia_sibling(pipe.clone().into(), Direction::Next)?.into_node()?;
+
+    let target = pipe.text_range();
+    acc.add(
+        AssistId("flip_or_pattern", AssistKind::RefactorRewrite),
+        "Flip patterns",
+        target,
+        |builder| {
+            let mut editor = builder.make_editor(parent.syntax());
+            editor.replace(before.clone(), after.clone());
+            editor.replace(after, before);
+            builder.add_file_edits(ctx.file_id(), editor);
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
+
+    #[test]
+    fn flip_or_pattern_assist_available() {
+        check_assist_target(flip_or_pattern, "fn main(a |$0 b: ()) {}", "|")
+    }
+
+    #[test]
+    fn flip_or_pattern_not_applicable_for_leading_pipe() {
+        check_assist_not_applicable(flip_or_pattern, "fn main(|$0 b: ()) {}")
+    }
+
+    #[test]
+    fn flip_or_pattern_works() {
+        check_assist(
+            flip_or_pattern,
+            "fn foo() { let (a | b |$0 c | d) = 1; }",
+            "fn foo() { let (a | c | b | d) = 1; }",
+        )
+    }
+
+    #[test]
+    fn flip_or_pattern_works_match_guard() {
+        check_assist(
+            flip_or_pattern,
+            "fn foo() { match() { a |$0 b if true => () }}",
+            "fn foo() { match() { b | a if true => () }}",
+        )
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs
index 298e5bd82c9..3528f5e8132 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs
@@ -18,17 +18,14 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // fn foo<T: Copy + Clone>() { }
 // ```
 pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    // We want to replicate the behavior of `flip_binexpr` by only suggesting
-    // the assist when the cursor is on a `+`
+    // Only flip on the `+` token
     let plus = ctx.find_token_syntax_at_offset(T![+])?;
 
     // Make sure we're in a `TypeBoundList`
     let parent = ast::TypeBoundList::cast(plus.parent()?)?;
 
-    let (before, after) = (
-        non_trivia_sibling(plus.clone().into(), Direction::Prev)?.into_node()?,
-        non_trivia_sibling(plus.clone().into(), Direction::Next)?.into_node()?,
-    );
+    let before = non_trivia_sibling(plus.clone().into(), Direction::Prev)?.into_node()?;
+    let after = non_trivia_sibling(plus.clone().into(), Direction::Next)?.into_node()?;
 
     let target = plus.text_range();
     acc.add(
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 179742f91b4..448bcadb8ef 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -149,6 +149,7 @@ mod handlers {
     mod fix_visibility;
     mod flip_binexpr;
     mod flip_comma;
+    mod flip_or_pattern;
     mod flip_trait_bound;
     mod generate_constant;
     mod generate_default_from_enum_variant;
@@ -279,6 +280,7 @@ mod handlers {
             fix_visibility::fix_visibility,
             flip_binexpr::flip_binexpr,
             flip_comma::flip_comma,
+            flip_or_pattern::flip_or_pattern,
             flip_trait_bound::flip_trait_bound,
             generate_constant::generate_constant,
             generate_default_from_enum_variant::generate_default_from_enum_variant,
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 74ae126adae..91c1a3e1bd7 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
@@ -1196,6 +1196,23 @@ fn main() {
 }
 
 #[test]
+fn doctest_flip_or_pattern() {
+    check_doc_test(
+        "flip_or_pattern",
+        r#####"
+fn foo() {
+    let (a |$0 b) = 1;
+}
+"#####,
+        r#####"
+fn foo() {
+    let (b | a) = 1;
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_flip_trait_bound() {
     check_doc_test(
         "flip_trait_bound",
diff --git a/src/tools/rust-analyzer/docs/book/src/assists_generated.md b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
index 72cecc2b02d..c2063df5068 100644
--- a/src/tools/rust-analyzer/docs/book/src/assists_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
@@ -257,7 +257,7 @@ fn main() {
 
 
 ### `apply_demorgan`
-**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L16) 
+**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L23) 
 
 Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
 This transforms expressions of the form `!l || !r` into `!(l && r)`.
@@ -280,7 +280,7 @@ fn main() {
 
 
 ### `apply_demorgan_iterator`
-**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L147) 
+**Source:**  [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L154) 
 
 Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
 `Iterator::all` and `Iterator::any`.
@@ -1345,6 +1345,26 @@ fn main() {
 ```
 
 
+### `flip_or_pattern`
+**Source:**  [flip_or_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_or_pattern.rs#L9) 
+
+Flips two trait bounds.
+
+#### Before
+```rust
+fn foo() {
+    let (a |┃ b) = 1;
+}
+```
+
+#### After
+```rust
+fn foo() {
+    let (b | a) = 1;
+}
+```
+
+
 ### `flip_trait_bound`
 **Source:**  [flip_trait_bound.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_trait_bound.rs#L9)