Skip to content

Commit 7273d71

Browse files
authored
Merge pull request #19259 from Veykril/push-skmvrmtorqso
Add flip or-pattern assist
2 parents 1ce1f08 + f7569a4 commit 7273d71

File tree

7 files changed

+162
-45
lines changed

7 files changed

+162
-45
lines changed

crates/ide-assists/src/assist_context.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ pub(crate) struct AssistContext<'a> {
5252
frange: FileRange,
5353
trimmed_range: TextRange,
5454
source_file: SourceFile,
55+
// We cache this here to speed up things slightly
56+
token_at_offset: TokenAtOffset<SyntaxToken>,
57+
// We cache this here to speed up things slightly
58+
covering_element: SyntaxElement,
5559
}
5660

5761
impl<'a> AssistContext<'a> {
@@ -78,8 +82,18 @@ impl<'a> AssistContext<'a> {
7882
// Selection solely consists of whitespace so just fall back to the original
7983
_ => frange.range,
8084
};
81-
82-
AssistContext { config, sema, frange, source_file, trimmed_range }
85+
let token_at_offset = source_file.syntax().token_at_offset(frange.range.start());
86+
let covering_element = source_file.syntax().covering_element(trimmed_range);
87+
88+
AssistContext {
89+
config,
90+
sema,
91+
frange,
92+
source_file,
93+
trimmed_range,
94+
token_at_offset,
95+
covering_element,
96+
}
8397
}
8498

8599
pub(crate) fn db(&self) -> &RootDatabase {
@@ -114,7 +128,7 @@ impl<'a> AssistContext<'a> {
114128
}
115129

116130
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
117-
self.source_file.syntax().token_at_offset(self.offset())
131+
self.token_at_offset.clone()
118132
}
119133
pub(crate) fn find_token_syntax_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
120134
self.token_at_offset().find(|it| it.kind() == kind)
@@ -136,7 +150,7 @@ impl<'a> AssistContext<'a> {
136150
}
137151
/// Returns the element covered by the selection range, this excludes trailing whitespace in the selection.
138152
pub(crate) fn covering_element(&self) -> SyntaxElement {
139-
self.source_file.syntax().covering_element(self.selection_trimmed())
153+
self.covering_element.clone()
140154
}
141155
}
142156

crates/ide-assists/src/handlers/flip_comma.rs

+20-33
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use syntax::{
22
algo::non_trivia_sibling,
33
ast::{self, syntax_factory::SyntaxFactory},
4-
syntax_editor::{Element, SyntaxMapping},
5-
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxToken, T,
4+
syntax_editor::SyntaxMapping,
5+
AstNode, Direction, NodeOrToken, SyntaxKind, SyntaxToken, T,
66
};
77

88
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -39,37 +39,24 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
3939
return None;
4040
}
4141

42-
let prev = match prev {
43-
SyntaxElement::Node(node) => node.syntax_element(),
44-
_ => prev,
45-
};
46-
let next = match next {
47-
SyntaxElement::Node(node) => node.syntax_element(),
48-
_ => next,
49-
};
50-
51-
acc.add(
52-
AssistId("flip_comma", AssistKind::RefactorRewrite),
53-
"Flip comma",
54-
comma.text_range(),
55-
|builder| {
56-
let parent = comma.parent().unwrap();
57-
let mut editor = builder.make_editor(&parent);
58-
59-
if let Some(parent) = ast::TokenTree::cast(parent) {
60-
// An attribute. It often contains a path followed by a
61-
// token tree (e.g. `align(2)`), so we have to be smarter.
62-
let (new_tree, mapping) = flip_tree(parent.clone(), comma);
63-
editor.replace(parent.syntax(), new_tree.syntax());
64-
editor.add_mappings(mapping);
65-
} else {
66-
editor.replace(prev.clone(), next.clone());
67-
editor.replace(next.clone(), prev.clone());
68-
}
69-
70-
builder.add_file_edits(ctx.file_id(), editor);
71-
},
72-
)
42+
let target = comma.text_range();
43+
acc.add(AssistId("flip_comma", AssistKind::RefactorRewrite), "Flip comma", target, |builder| {
44+
let parent = comma.parent().unwrap();
45+
let mut editor = builder.make_editor(&parent);
46+
47+
if let Some(parent) = ast::TokenTree::cast(parent) {
48+
// An attribute. It often contains a path followed by a
49+
// token tree (e.g. `align(2)`), so we have to be smarter.
50+
let (new_tree, mapping) = flip_tree(parent.clone(), comma);
51+
editor.replace(parent.syntax(), new_tree.syntax());
52+
editor.add_mappings(mapping);
53+
} else {
54+
editor.replace(prev.clone(), next.clone());
55+
editor.replace(next.clone(), prev.clone());
56+
}
57+
58+
builder.add_file_edits(ctx.file_id(), editor);
59+
})
7360
}
7461

7562
fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, SyntaxMapping) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use syntax::{
2+
algo::non_trivia_sibling,
3+
ast::{self, AstNode},
4+
Direction, T,
5+
};
6+
7+
use crate::{AssistContext, AssistId, AssistKind, Assists};
8+
9+
// Assist: flip_or_pattern
10+
//
11+
// Flips two patterns in an or-pattern.
12+
//
13+
// ```
14+
// fn foo() {
15+
// let (a |$0 b) = 1;
16+
// }
17+
// ```
18+
// ->
19+
// ```
20+
// fn foo() {
21+
// let (b | a) = 1;
22+
// }
23+
// ```
24+
pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
25+
// Only flip on the `|` token
26+
let pipe = ctx.find_token_syntax_at_offset(T![|])?;
27+
28+
let parent = ast::OrPat::cast(pipe.parent()?)?;
29+
30+
let before = non_trivia_sibling(pipe.clone().into(), Direction::Prev)?.into_node()?;
31+
let after = non_trivia_sibling(pipe.clone().into(), Direction::Next)?.into_node()?;
32+
33+
let target = pipe.text_range();
34+
acc.add(
35+
AssistId("flip_or_pattern", AssistKind::RefactorRewrite),
36+
"Flip patterns",
37+
target,
38+
|builder| {
39+
let mut editor = builder.make_editor(parent.syntax());
40+
editor.replace(before.clone(), after.clone());
41+
editor.replace(after, before);
42+
builder.add_file_edits(ctx.file_id(), editor);
43+
},
44+
)
45+
}
46+
47+
#[cfg(test)]
48+
mod tests {
49+
use super::*;
50+
51+
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
52+
53+
#[test]
54+
fn flip_or_pattern_assist_available() {
55+
check_assist_target(flip_or_pattern, "fn main(a |$0 b: ()) {}", "|")
56+
}
57+
58+
#[test]
59+
fn flip_or_pattern_not_applicable_for_leading_pipe() {
60+
check_assist_not_applicable(flip_or_pattern, "fn main(|$0 b: ()) {}")
61+
}
62+
63+
#[test]
64+
fn flip_or_pattern_works() {
65+
check_assist(
66+
flip_or_pattern,
67+
"fn foo() { let (a | b |$0 c | d) = 1; }",
68+
"fn foo() { let (a | c | b | d) = 1; }",
69+
)
70+
}
71+
72+
#[test]
73+
fn flip_or_pattern_works_match_guard() {
74+
check_assist(
75+
flip_or_pattern,
76+
"fn foo() { match() { a |$0 b if true => () }}",
77+
"fn foo() { match() { b | a if true => () }}",
78+
)
79+
}
80+
}

crates/ide-assists/src/handlers/flip_trait_bound.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,14 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
1818
// fn foo<T: Copy + Clone>() { }
1919
// ```
2020
pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
21-
// We want to replicate the behavior of `flip_binexpr` by only suggesting
22-
// the assist when the cursor is on a `+`
21+
// Only flip on the `+` token
2322
let plus = ctx.find_token_syntax_at_offset(T![+])?;
2423

2524
// Make sure we're in a `TypeBoundList`
2625
let parent = ast::TypeBoundList::cast(plus.parent()?)?;
2726

28-
let (before, after) = (
29-
non_trivia_sibling(plus.clone().into(), Direction::Prev)?.into_node()?,
30-
non_trivia_sibling(plus.clone().into(), Direction::Next)?.into_node()?,
31-
);
27+
let before = non_trivia_sibling(plus.clone().into(), Direction::Prev)?.into_node()?;
28+
let after = non_trivia_sibling(plus.clone().into(), Direction::Next)?.into_node()?;
3229

3330
let target = plus.text_range();
3431
acc.add(

crates/ide-assists/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ mod handlers {
149149
mod fix_visibility;
150150
mod flip_binexpr;
151151
mod flip_comma;
152+
mod flip_or_pattern;
152153
mod flip_trait_bound;
153154
mod generate_constant;
154155
mod generate_default_from_enum_variant;
@@ -279,6 +280,7 @@ mod handlers {
279280
fix_visibility::fix_visibility,
280281
flip_binexpr::flip_binexpr,
281282
flip_comma::flip_comma,
283+
flip_or_pattern::flip_or_pattern,
282284
flip_trait_bound::flip_trait_bound,
283285
generate_constant::generate_constant,
284286
generate_default_from_enum_variant::generate_default_from_enum_variant,

crates/ide-assists/src/tests/generated.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,23 @@ fn main() {
11951195
)
11961196
}
11971197

1198+
#[test]
1199+
fn doctest_flip_or_pattern() {
1200+
check_doc_test(
1201+
"flip_or_pattern",
1202+
r#####"
1203+
fn foo() {
1204+
let (a |$0 b) = 1;
1205+
}
1206+
"#####,
1207+
r#####"
1208+
fn foo() {
1209+
let (b | a) = 1;
1210+
}
1211+
"#####,
1212+
)
1213+
}
1214+
11981215
#[test]
11991216
fn doctest_flip_trait_bound() {
12001217
check_doc_test(

docs/book/src/assists_generated.md

+22-2
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ fn main() {
257257

258258

259259
### `apply_demorgan`
260-
**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L16)
260+
**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L23)
261261

262262
Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
263263
This transforms expressions of the form `!l || !r` into `!(l && r)`.
@@ -280,7 +280,7 @@ fn main() {
280280

281281

282282
### `apply_demorgan_iterator`
283-
**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L147)
283+
**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L154)
284284

285285
Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
286286
`Iterator::all` and `Iterator::any`.
@@ -1345,6 +1345,26 @@ fn main() {
13451345
```
13461346

13471347

1348+
### `flip_or_pattern`
1349+
**Source:** [flip_or_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_or_pattern.rs#L9)
1350+
1351+
Flips two trait bounds.
1352+
1353+
#### Before
1354+
```rust
1355+
fn foo() {
1356+
let (a |b) = 1;
1357+
}
1358+
```
1359+
1360+
#### After
1361+
```rust
1362+
fn foo() {
1363+
let (b | a) = 1;
1364+
}
1365+
```
1366+
1367+
13481368
### `flip_trait_bound`
13491369
**Source:** [flip_trait_bound.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_trait_bound.rs#L9)
13501370

0 commit comments

Comments
 (0)