diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index b076358443..f8747b0724 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -110,12 +110,18 @@ impl Completions { ["self", "super", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); } - pub(crate) fn add_keyword_snippet(&mut self, ctx: &CompletionContext, kw: &str, snippet: &str) { + pub(crate) fn add_keyword_snippet_expr( + &mut self, + ctx: &CompletionContext, + kw: &str, + snippet: &str, + incomplete_let: bool, + ) { let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw); match ctx.config.snippet_cap { Some(cap) => { - if snippet.ends_with('}') && ctx.incomplete_let { + if snippet.ends_with('}') && incomplete_let { // complete block expression snippets with a trailing semicolon, if inside an incomplete let cov_mark::hit!(let_semi); item.insert_snippet(cap, format!("{};", snippet)); @@ -130,6 +136,16 @@ impl Completions { item.add_to(self); } + pub(crate) fn add_keyword_snippet(&mut self, ctx: &CompletionContext, kw: &str, snippet: &str) { + let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw); + + match ctx.config.snippet_cap { + Some(cap) => item.insert_snippet(cap, snippet), + None => item.insert_text(if snippet.contains('$') { kw } else { snippet }), + }; + item.add_to(self); + } + pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) { ctx.process_all_names(&mut |name, res| match res { ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index ecc1442bfc..e4d1c290c0 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -26,6 +26,7 @@ pub(crate) fn complete_expr_path( wants_mut_token, in_condition, ty, + incomplete_let, ) = match path_ctx { &PathCompletionCtx { kind: @@ -34,6 +35,7 @@ pub(crate) fn complete_expr_path( in_loop_body, after_if_expr, in_condition, + incomplete_let, ref ref_expr_parent, ref is_func_update, ref innermost_ret_ty, @@ -50,6 +52,7 @@ pub(crate) fn complete_expr_path( ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false), in_condition, innermost_ret_ty, + incomplete_let, ), _ => return, }; @@ -220,7 +223,8 @@ pub(crate) fn complete_expr_path( }); if !is_func_update { - let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); + let mut add_keyword = + |kw, snippet| acc.add_keyword_snippet_expr(ctx, kw, snippet, incomplete_let); if !in_block_expr { add_keyword("unsafe", "unsafe {\n $0\n}"); diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 7df9b4921a..bc2c2fc713 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -93,6 +93,7 @@ pub(super) enum PathKind { after_if_expr: bool, /// Whether this expression is the direct condition of an if or while expression in_condition: bool, + incomplete_let: bool, ref_expr_parent: Option, is_func_update: Option, self_param: Option, @@ -322,9 +323,6 @@ pub(crate) struct CompletionContext<'a> { /// The parent impl of the cursor position if it exists. // FIXME: This probably doesn't belong here pub(super) impl_def: Option, - /// Are we completing inside a let statement with a missing semicolon? - // FIXME: This should be part of PathKind::Expr - pub(super) incomplete_let: bool, // FIXME: This shouldn't exist pub(super) previous_token: Option, @@ -500,7 +498,6 @@ impl<'a> CompletionContext<'a> { expected_name: None, expected_type: None, impl_def: None, - incomplete_let: false, previous_token: None, // dummy value, will be overwritten ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None }, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 4f65ae402e..d416d8251d 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -330,11 +330,6 @@ impl<'a> CompletionContext<'a> { self.previous_token = syntax_element.clone().into_token().and_then(previous_non_trivia_token); - self.incomplete_let = - syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { - it.syntax().text_range().end() == syntax_element.text_range().end() - }); - (self.expected_type, self.expected_name) = self.expected_type_and_name(); // Overwrite the path kind for derives @@ -767,6 +762,10 @@ impl<'a> CompletionContext<'a> { }; let is_func_update = func_update_record(it); let in_condition = is_in_condition(&expr); + let incomplete_let = it + .parent() + .and_then(ast::LetStmt::cast) + .map_or(false, |it| it.semicolon_token().is_none()); PathKind::Expr { in_block_expr, @@ -777,6 +776,7 @@ impl<'a> CompletionContext<'a> { is_func_update, innermost_ret_ty, self_param, + incomplete_let, } }; let make_path_kind_type = |ty: ast::Type| {