use itertools::Itertools;
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
+use test_utils::mark;
-use crate::{Assist, AssistCtx, AssistId};
+use crate::{
+ utils::{render_snippet, Cursor, FamousDefs},
+ AssistContext, AssistId, Assists,
+};
// Assist: fill_match_arms
//
//
// fn handle(action: Action) {
// match action {
-// Action::Move { distance } => {}
+// $0Action::Move { distance } => {}
// Action::Stop => {}
// }
// }
// ```
-pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
let match_arm_list = match_expr.match_arm_list()?;
let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
let variants = enum_def.variants(ctx.db);
- variants
+ let mut variants = variants
.into_iter()
.filter_map(|variant| build_pat(ctx.db, module, variant))
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
- .collect()
+ .collect::<Vec<_>>();
+ if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() {
+ // Match `Some` variant first.
+ mark::hit!(option_order);
+ variants.reverse()
+ }
+ variants
} else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
// Partial fill not currently supported for tuple of enums.
if !arms.is_empty() {
}
let target = match_expr.syntax().text_range();
- ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", target, |edit| {
- let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
- edit.set_cursor(expr.syntax().text_range().start());
- edit.replace_ast(match_arm_list, new_arm_list);
+ acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| {
+ let new_arm_list = match_arm_list.remove_placeholder();
+ let n_old_arms = new_arm_list.arms().count();
+ let new_arm_list = new_arm_list.append_arms(missing_arms);
+ let first_new_arm = new_arm_list.arms().nth(n_old_arms);
+ let old_range = match_arm_list.syntax().text_range();
+ match (first_new_arm, ctx.config.snippet_cap) {
+ (Some(first_new_arm), Some(cap)) => {
+ let snippet = render_snippet(
+ cap,
+ new_arm_list.syntax(),
+ Cursor::Before(first_new_arm.syntax()),
+ );
+ builder.replace_snippet(cap, old_range, snippet);
+ }
+ _ => builder.replace(old_range, new_arm_list.to_string()),
+ }
})
}
}
fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
- let pat_head = pat.syntax().first_child().map(|node| node.text());
- let var_head = var.syntax().first_child().map(|node| node.text());
+ let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
+
+ let pat_head = match pat {
+ Pat::BindPat(bind_pat) => {
+ if let Some(p) = bind_pat.pat() {
+ first_node_text(&p)
+ } else {
+ return false;
+ }
+ }
+ pat => first_node_text(pat),
+ };
+
+ let var_head = first_node_text(var);
pat_head == var_head
}
#[cfg(test)]
mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
+ use test_utils::mark;
+
+ use crate::{
+ tests::{check_assist, check_assist_not_applicable, check_assist_target},
+ utils::FamousDefs,
+ };
use super::fill_match_arms;
r#"
enum A {
As,
- Bs{x:i32, y:Option<i32>},
+ Bs { x: i32, y: Option<i32> },
Cs(i32, Option<i32>),
}
fn main() {
match A::As<|> {
- A::Bs{x,y:Some(_)} => {}
+ A::Bs { x, y: Some(_) } => {}
A::Cs(_, Some(_)) => {}
}
}
r#"
enum A {
As,
- Bs{x:i32, y:Option<i32>},
+ Bs { x: i32, y: Option<i32> },
Cs(i32, Option<i32>),
}
fn main() {
- match <|>A::As {
- A::Bs{x,y:Some(_)} => {}
+ match A::As {
+ A::Bs { x, y: Some(_) } => {}
A::Cs(_, Some(_)) => {}
- A::As => {}
+ $0A::As => {}
}
}
"#,
Cs(Option<i32>),
}
fn main() {
- match <|>A::As {
+ match A::As {
A::Cs(_) | A::Bs => {}
- A::As => {}
+ $0A::As => {}
}
}
"#,
Ys,
}
fn main() {
- match <|>A::As {
+ match A::As {
A::Bs if 0 < 1 => {}
A::Ds(_value) => { let x = 1; }
A::Es(B::Xs) => (),
- A::As => {}
+ $0A::As => {}
A::Cs => {}
}
}
);
}
+ #[test]
+ fn partial_fill_bind_pat() {
+ check_assist(
+ fill_match_arms,
+ r#"
+ enum A {
+ As,
+ Bs,
+ Cs(Option<i32>),
+ }
+ fn main() {
+ match A::As<|> {
+ A::As(_) => {}
+ a @ A::Bs(_) => {}
+ }
+ }
+ "#,
+ r#"
+ enum A {
+ As,
+ Bs,
+ Cs(Option<i32>),
+ }
+ fn main() {
+ match A::As {
+ A::As(_) => {}
+ a @ A::Bs(_) => {}
+ $0A::Cs(_) => {}
+ }
+ }
+ "#,
+ );
+ }
+
#[test]
fn fill_match_arms_empty_body() {
check_assist(
Bs,
Cs(String),
Ds(String, String),
- Es{ x: usize, y: usize }
+ Es { x: usize, y: usize }
}
fn main() {
Bs,
Cs(String),
Ds(String, String),
- Es{ x: usize, y: usize }
+ Es { x: usize, y: usize }
}
fn main() {
let a = A::As;
- match <|>a {
- A::As => {}
+ match a {
+ $0A::As => {}
A::Bs => {}
A::Cs(_) => {}
A::Ds(_, _) => {}
check_assist(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
- enum B {
- One,
- Two,
- }
+ enum A { One, Two }
+ enum B { One, Two }
fn main() {
let a = A::One;
}
"#,
r#"
- enum A {
- One,
- Two,
- }
- enum B {
- One,
- Two,
- }
+ enum A { One, Two }
+ enum B { One, Two }
fn main() {
let a = A::One;
let b = B::One;
- match <|>(a, b) {
- (A::One, B::One) => {}
+ match (a, b) {
+ $0(A::One, B::One) => {}
(A::One, B::Two) => {}
(A::Two, B::One) => {}
(A::Two, B::Two) => {}
check_assist(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
- enum B {
- One,
- Two,
- }
+ enum A { One, Two }
+ enum B { One, Two }
fn main() {
let a = A::One;
}
"#,
r#"
- enum A {
- One,
- Two,
- }
- enum B {
- One,
- Two,
- }
+ enum A { One, Two }
+ enum B { One, Two }
fn main() {
let a = A::One;
let b = B::One;
- match <|>(&a, &b) {
- (A::One, B::One) => {}
+ match (&a, &b) {
+ $0(A::One, B::One) => {}
(A::One, B::Two) => {}
(A::Two, B::One) => {}
(A::Two, B::Two) => {}
check_assist_not_applicable(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
- enum B {
- One,
- Two,
- }
+ enum A { One, Two }
+ enum B { One, Two }
fn main() {
let a = A::One;
check_assist_not_applicable(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
- enum B {
- One,
- Two,
- }
+ enum A { One, Two }
+ enum B { One, Two }
fn main() {
let a = A::One;
check_assist_not_applicable(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
+ enum A { One, Two }
fn main() {
let a = A::One;
check_assist(
fill_match_arms,
r#"
- enum A {
- As,
- }
+ enum A { As }
fn foo(a: &A) {
match a<|> {
}
"#,
r#"
- enum A {
- As,
- }
+ enum A { As }
fn foo(a: &A) {
- match <|>a {
- A::As => {}
+ match a {
+ $0A::As => {}
}
}
"#,
fill_match_arms,
r#"
enum A {
- Es{ x: usize, y: usize }
+ Es { x: usize, y: usize }
}
fn foo(a: &mut A) {
"#,
r#"
enum A {
- Es{ x: usize, y: usize }
+ Es { x: usize, y: usize }
}
fn foo(a: &mut A) {
- match <|>a {
- A::Es { x, y } => {}
+ match a {
+ $0A::Es { x, y } => {}
}
}
"#,
enum E { X, Y }
fn main() {
- match <|>E::X {
- E::X => {}
+ match E::X {
+ $0E::X => {}
E::Y => {}
}
}
use foo::E::X;
fn main() {
- match <|>X {
- X => {}
+ match X {
+ $0X => {}
foo::E::Y => {}
}
}
check_assist(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
+ enum A { One, Two }
fn foo(a: A) {
match a {
// foo bar baz<|>
}
"#,
r#"
- enum A {
- One,
- Two,
- }
+ enum A { One, Two }
fn foo(a: A) {
- match <|>a {
+ match a {
// foo bar baz
A::One => {}
// This is where the rest should be
- A::Two => {}
+ $0A::Two => {}
}
}
"#,
check_assist(
fill_match_arms,
r#"
- enum A {
- One,
- Two,
- }
+ enum A { One, Two }
fn foo(a: A) {
match a {
// foo bar baz<|>
}
"#,
r#"
- enum A {
- One,
- Two,
- }
+ enum A { One, Two }
fn foo(a: A) {
- match <|>a {
+ match a {
// foo bar baz
- A::One => {}
+ $0A::One => {}
A::Two => {}
}
}
r#"
enum A { One, Two, }
fn foo(a: A) {
- match <|>a {
- A::One => {}
+ match a {
+ $0A::One => {}
A::Two => {}
}
}
"#,
);
}
+
+ #[test]
+ fn option_order() {
+ mark::check!(option_order);
+ let before = r#"
+fn foo(opt: Option<i32>) {
+ match opt<|> {
+ }
+}"#;
+ let before =
+ &format!("//- /main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE);
+
+ check_assist(
+ fill_match_arms,
+ before,
+ r#"
+fn foo(opt: Option<i32>) {
+ match opt {
+ $0Some(_) => {}
+ None => {}
+ }
+}
+"#,
+ );
+ }
}