Merge pull request #19259 from Veykril/push-skmvrmtorqso
Add flip or-pattern assist
This commit is contained in:
commit
768749f41d
7 changed files with 159 additions and 42 deletions
|
@ -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,
|
||||
};
|
||||
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 }
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
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);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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);
|
||||
},
|
||||
)
|
||||
builder.add_file_edits(ctx.file_id(), editor);
|
||||
})
|
||||
}
|
||||
|
||||
fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, SyntaxMapping) {
|
||||
|
|
|
@ -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 => () }}",
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1195,6 +1195,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(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue