Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/postfix.rs')
| -rw-r--r-- | crates/ide-completion/src/completions/postfix.rs | 213 |
1 files changed, 123 insertions, 90 deletions
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index d0023852ac..ab3f619fd7 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -11,12 +11,12 @@ 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}, - TextRange, TextSize, + T, TextRange, TextSize, ast::{self, AstNode, AstToken}, + match_ast, }; use crate::{ @@ -43,7 +43,7 @@ pub(crate) fn complete_postfix( DotAccessKind::Field { receiver_is_ambiguous_float_literal } => { receiver_is_ambiguous_float_literal } - DotAccessKind::Method { .. } => false, + DotAccessKind::Method => false, }, ), _ => return, @@ -63,28 +63,21 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(ctx.is_nightly); - - if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { - if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { - if let Some(drop_fn) = ctx.famous_defs().core_mem_drop() { - if let Some(path) = - ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg) - { - cov_mark::hit!(postfix_drop_completion); - let mut item = postfix_snippet( - "drop", - "fn drop(&mut self)", - &format!( - "{path}($0{receiver_text})", - path = path.display(ctx.db, ctx.edition) - ), - ); - item.set_documentation(drop_fn.docs(ctx.db)); - item.add_to(acc, ctx.db); - } - } - } + let cfg = ctx.config.find_path_config(ctx.is_nightly); + + if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() + && receiver_ty.impls_trait(ctx.db, drop_trait, &[]) + && let Some(drop_fn) = ctx.famous_defs().core_mem_drop() + && let Some(path) = ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg) + { + cov_mark::hit!(postfix_drop_completion); + let mut item = postfix_snippet( + "drop", + "fn drop(&mut self)", + &format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)), + ); + item.set_documentation(drop_fn.docs(ctx.db)); + item.add_to(acc, ctx.db); } postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); @@ -117,56 +110,46 @@ pub(crate) fn complete_postfix( 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};")) + if let Some(parent) = dot_receiver_including_refs.syntax().parent() + && let Some(second_ancestor) = parent.parent() + { + if let Some(parent_expr) = ast::Expr::cast(parent) { + is_in_cond = is_in_condition(&parent_expr); + } + 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!(second_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); } + _ => (), } } @@ -258,25 +241,25 @@ pub(crate) fn complete_postfix( ) .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); - } + } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() + && 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); } } 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; - } + if let Some(parent) = dot_receiver.syntax().parent() + && matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) + { + block_should_be_wrapped = true; } }; { @@ -292,10 +275,10 @@ pub(crate) fn complete_postfix( postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); } - if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() { - if let Some(literal_text) = ast::String::cast(literal.token()) { - add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); - } + if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() + && let Some(literal_text) = ast::String::cast(literal.token()) + { + add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); } postfix_snippet( @@ -308,7 +291,7 @@ pub(crate) fn complete_postfix( ) .add_to(acc, ctx.db); - if let BreakableKind::Block | BreakableKind::Loop = expr_ctx.in_breakable { + if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable { postfix_snippet( "break", "break expr", @@ -467,6 +450,24 @@ fn add_custom_postfix_completions( None } +pub(crate) fn is_in_condition(it: &ast::Expr) -> bool { + it.syntax() + .parent() + .and_then(|parent| { + Some(match_ast! { match parent { + ast::IfExpr(expr) => expr.condition()? == *it, + ast::WhileExpr(expr) => expr.condition()? == *it, + ast::MatchGuard(guard) => guard.condition()? == *it, + ast::BinExpr(bin_expr) => (bin_expr.op_token()?.kind() == T![&&]) + .then(|| is_in_condition(&bin_expr.into()))?, + ast::Expr(expr) => (expr.syntax().text_range().start() == it.syntax().text_range().start()) + .then(|| is_in_condition(&expr))?, + _ => return None, + } }) + }) + .unwrap_or(false) +} + #[cfg(test)] mod tests { use expect_test::expect; @@ -663,6 +664,38 @@ fn main() { } "#, ); + check_edit( + "let", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + if true && bar.$0 +} +"#, + r#" +fn main() { + let bar = Some(true); + if true && let Some($0) = bar +} +"#, + ); + check_edit( + "let", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + if true && true && bar.$0 +} +"#, + r#" +fn main() { + let bar = Some(true); + if true && true && let Some($0) = bar +} +"#, + ); } #[test] |