diff options
3 files changed, 224 insertions, 122 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 3cdf2112835..0ac7578d259 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -11,6 +11,7 @@ use ide_db::{ text_edit::TextEdit, ty_filter::TryEnum, }; +use itertools::Either; use stdx::never; use syntax::{ SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR}, @@ -86,98 +87,10 @@ pub(crate) fn complete_postfix( } } - let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references()); - if let Some(try_enum) = &try_enum { - match try_enum { - TryEnum::Result => { - postfix_snippet( - "ifl", - "if let Ok {}", - &format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"), - ) - .add_to(acc, ctx.db); - - postfix_snippet( - "lete", - "let Ok else {}", - &format!("let Ok($1) = {receiver_text} else {{\n $2\n}};\n$0"), - ) - .add_to(acc, ctx.db); - - postfix_snippet( - "while", - "while let Ok {}", - &format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"), - ) - .add_to(acc, ctx.db); - } - TryEnum::Option => { - postfix_snippet( - "ifl", - "if let Some {}", - &format!("if let Some($1) = {receiver_text} {{\n $0\n}}"), - ) - .add_to(acc, ctx.db); - - postfix_snippet( - "lete", - "let Some else {}", - &format!("let Some($1) = {receiver_text} else {{\n $2\n}};\n$0"), - ) - .add_to(acc, ctx.db); - - postfix_snippet( - "while", - "while let Some {}", - &format!("while let Some($1) = {receiver_text} {{\n $0\n}}"), - ) - .add_to(acc, ctx.db); - } - } - } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { - postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) - .add_to(acc, ctx.db); - postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n $0\n}}")) - .add_to(acc, ctx.db); - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); - } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { - if receiver_ty.impls_trait(ctx.db, trait_, &[]) { - postfix_snippet( - "for", - "for ele in expr {}", - &format!("for ele in {receiver_text} {{\n $0\n}}"), - ) - .add_to(acc, ctx.db); - } - } - postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db); postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db); - let mut block_should_be_wrapped = true; - if dot_receiver.syntax().kind() == BLOCK_EXPR { - block_should_be_wrapped = false; - if let Some(parent) = dot_receiver.syntax().parent() { - if matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) { - block_should_be_wrapped = true; - } - } - }; - let unsafe_completion_string = if block_should_be_wrapped { - format!("unsafe {{ {receiver_text} }}") - } else { - format!("unsafe {receiver_text}") - }; - postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); - - let const_completion_string = if block_should_be_wrapped { - format!("const {{ {receiver_text} }}") - } else { - format!("const {receiver_text}") - }; - postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); - // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation @@ -195,18 +108,81 @@ pub(crate) fn complete_postfix( add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); } - match try_enum { - Some(try_enum) => match try_enum { - TryEnum::Result => { - postfix_snippet( + postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) + .add_to(acc, ctx.db); + postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme + postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); + postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) + .add_to(acc, ctx.db); + + let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references()); + let mut is_in_cond = false; + if let Some(parent) = dot_receiver_including_refs.syntax().parent() { + if let Some(second_ancestor) = parent.parent() { + let sec_ancestor_kind = second_ancestor.kind(); + if let Some(expr) = <Either<ast::IfExpr, ast::WhileExpr>>::cast(second_ancestor) { + is_in_cond = match expr { + Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent), + Either::Right(it) => { + it.condition().is_some_and(|cond| *cond.syntax() == parent) + } + } + } + match &try_enum { + Some(try_enum) if is_in_cond => match try_enum { + TryEnum::Result => { + postfix_snippet( + "let", + "let Ok(_)", + &format!("let Ok($0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); + postfix_snippet( + "letm", + "let Ok(mut _)", + &format!("let Ok(mut $0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); + } + TryEnum::Option => { + postfix_snippet( + "let", + "let Some(_)", + &format!("let Some($0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); + postfix_snippet( + "letm", + "let Some(mut _)", + &format!("let Some(mut $0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); + } + }, + _ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => { + postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) + .add_to(acc, ctx.db); + postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) + .add_to(acc, ctx.db); + } + _ => (), + } + } + } + + if !is_in_cond { + match try_enum { + Some(try_enum) => match try_enum { + TryEnum::Result => { + postfix_snippet( "match", "match expr {}", &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), ) .add_to(acc, ctx.db); - } - TryEnum::Option => { - postfix_snippet( + } + TryEnum::Option => { + postfix_snippet( "match", "match expr {}", &format!( @@ -214,32 +190,110 @@ pub(crate) fn complete_postfix( ), ) .add_to(acc, ctx.db); + } + }, + None => { + postfix_snippet( + "match", + "match expr {}", + &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), + ) + .add_to(acc, ctx.db); } - }, - None => { + } + if let Some(try_enum) = &try_enum { + match try_enum { + TryEnum::Result => { + postfix_snippet( + "ifl", + "if let Ok {}", + &format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"), + ) + .add_to(acc, ctx.db); + + postfix_snippet( + "lete", + "let Ok else {}", + &format!("let Ok($1) = {receiver_text} else {{\n $2\n}};\n$0"), + ) + .add_to(acc, ctx.db); + + postfix_snippet( + "while", + "while let Ok {}", + &format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"), + ) + .add_to(acc, ctx.db); + } + TryEnum::Option => { + postfix_snippet( + "ifl", + "if let Some {}", + &format!("if let Some($1) = {receiver_text} {{\n $0\n}}"), + ) + .add_to(acc, ctx.db); + + postfix_snippet( + "lete", + "let Some else {}", + &format!("let Some($1) = {receiver_text} else {{\n $2\n}};\n$0"), + ) + .add_to(acc, ctx.db); + + postfix_snippet( + "while", + "while let Some {}", + &format!("while let Some($1) = {receiver_text} {{\n $0\n}}"), + ) + .add_to(acc, ctx.db); + } + } + } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { + postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) + .add_to(acc, ctx.db); postfix_snippet( - "match", - "match expr {}", - &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), + "while", + "while expr {}", + &format!("while {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); + postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); + } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { + if receiver_ty.impls_trait(ctx.db, trait_, &[]) { + postfix_snippet( + "for", + "for ele in expr {}", + &format!("for ele in {receiver_text} {{\n $0\n}}"), + ) + .add_to(acc, ctx.db); + } } } - postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) - .add_to(acc, ctx.db); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme - postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); - postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) - .add_to(acc, ctx.db); - - if let Some(parent) = dot_receiver_including_refs.syntax().parent().and_then(|p| p.parent()) { - if matches!(parent.kind(), STMT_LIST | EXPR_STMT) { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) - .add_to(acc, ctx.db); - postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) - .add_to(acc, ctx.db); + let mut block_should_be_wrapped = true; + if dot_receiver.syntax().kind() == BLOCK_EXPR { + block_should_be_wrapped = false; + if let Some(parent) = dot_receiver.syntax().parent() { + if matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) { + block_should_be_wrapped = true; + } } + }; + { + let (open_brace, close_brace) = + if block_should_be_wrapped { ("{ ", " }") } else { ("", "") }; + let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") }; + let unsafe_completion_string = format!( + "{}unsafe {}{receiver_text}{}{}", + open_paren, open_brace, close_brace, close_paren + ); + postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); + + let const_completion_string = format!( + "{}const {}{receiver_text}{}{}", + open_paren, open_brace, close_brace, close_paren + ); + postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); } if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() { @@ -568,6 +622,54 @@ fn main() { } #[test] + fn option_iflet_cond() { + check( + r#" +//- minicore: option +fn main() { + let bar = Some(true); + if bar.$0 +} +"#, + expect![[r#" + me and(…) fn(self, Option<U>) -> Option<U> + me as_ref() const fn(&self) -> Option<&T> + me ok_or(…) const fn(self, E) -> Result<T, E> + me unwrap() const fn(self) -> T + me unwrap_or(…) fn(self, T) -> T + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let Some(_) + sn letm let Some(mut _) + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + check_edit( + "let", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + if bar.$0 +} +"#, + r#" +fn main() { + let bar = Some(true); + if let Some($0) = bar +} +"#, + ); + } + + #[test] fn option_letelse() { check_edit( "lete", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 00c0b470f98..ba1f96a5b6e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3000,18 +3000,18 @@ fn main() { expect![[r#" sn not !expr [snippet] me not() fn(self) -> <Self as Not>::Output [type_could_unify+requires_import] - sn if if expr {} [] - sn while while expr {} [] sn ref &expr [] sn refm &mut expr [] sn deref *expr [] - sn unsafe unsafe {} [] - sn const const {} [] - sn match match expr {} [] sn box Box::new(expr) [] sn dbg dbg!(expr) [] sn dbgr dbg!(&expr) [] sn call function(expr) [] + sn match match expr {} [] + sn if if expr {} [] + sn while while expr {} [] + sn unsafe unsafe {} [] + sn const const {} [] sn return return expr [] "#]], ); @@ -3036,15 +3036,15 @@ fn main() { sn ref &expr [] sn refm &mut expr [] sn deref *expr [] - sn unsafe unsafe {} [] - sn const const {} [] - sn match match expr {} [] sn box Box::new(expr) [] sn dbg dbg!(expr) [] sn dbgr dbg!(&expr) [] sn call function(expr) [] sn let let [] sn letm let mut [] + sn match match expr {} [] + sn unsafe unsafe {} [] + sn const const {} [] sn return return expr [] "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs index 63ce0ddbb8f..2665dabb666 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs @@ -10,7 +10,7 @@ use syntax::ast::{self, Pat, make}; use crate::RootDatabase; /// Enum types that implement `std::ops::Try` trait. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum TryEnum { Result, Option, |
