diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 9bdf109bcf3..992d7eabd8a 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -18,7 +18,7 @@ use syntax::{ use crate::{ completions::module_or_attr, - context::{CompletionContext, IdentContext, PathCompletionCtx, PathKind, Qualified}, + context::{CompletionContext, PathCompletionCtx, PathKind, Qualified}, item::CompletionItem, Completions, }; @@ -34,11 +34,9 @@ pub(crate) use self::derive::complete_derive; pub(crate) fn complete_known_attribute_input( acc: &mut Completions, ctx: &CompletionContext, + fake_attribute_under_caret: &ast::Attr, ) -> Option<()> { - let attribute = match &ctx.ident_ctx { - IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(it) } => it, - _ => return None, - }; + let attribute = fake_attribute_under_caret; let name_ref = match attribute.path() { Some(p) => Some(p.as_single_name_ref()?), None => None, @@ -71,13 +69,17 @@ pub(crate) fn complete_known_attribute_input( Some(()) } -pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { - let (qualified, is_inner, annotated_item_kind) = match ctx.path_context() { - Some(&PathCompletionCtx { +pub(crate) fn complete_attribute( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { + let (qualified, is_inner, annotated_item_kind) = match path_ctx { + &PathCompletionCtx { kind: PathKind::Attr { kind, annotated_item_kind }, ref qualified, .. - }) => (qualified, kind == AttrKind::Inner, annotated_item_kind), + } => (qualified, kind == AttrKind::Inner, annotated_item_kind), _ => return, }; diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs index fcc77d545bf..0927d2f7643 100644 --- a/crates/ide-completion/src/completions/attribute/derive.rs +++ b/crates/ide-completion/src/completions/attribute/derive.rs @@ -10,11 +10,15 @@ use crate::{ Completions, }; -pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) { - let (qualified, existing_derives) = match ctx.path_context() { - Some(PathCompletionCtx { - kind: PathKind::Derive { existing_derives }, qualified, .. - }) => (qualified, existing_derives), +pub(crate) fn complete_derive( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { + let (qualified, existing_derives) = match path_ctx { + PathCompletionCtx { kind: PathKind::Derive { existing_derives }, qualified, .. } => { + (qualified, existing_derives) + } _ => return, }; diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index bdafdf4152e..a315d616d55 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -4,21 +4,16 @@ use ide_db::FxHashSet; use crate::{ context::{ - CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, Qualified, + CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified, }, CompletionItem, CompletionItemKind, Completions, }; /// Complete dot accesses, i.e. fields or methods. -pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { - let (dot_access, receiver_ty) = match ctx.nameref_ctx() { - Some(NameRefContext { - kind: - Some(NameRefKind::DotAccess(access @ DotAccess { receiver_ty: Some(receiver_ty), .. })), - .. - }) => (access, &receiver_ty.original), - _ => return complete_undotted_self(acc, ctx), +pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext, dot_access: &DotAccess) { + let receiver_ty = match dot_access { + DotAccess { receiver_ty: Some(receiver_ty), .. } => &receiver_ty.original, + _ => return, }; // Suggest .await syntax for types that implement Future trait @@ -43,18 +38,17 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); } -fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { +pub(crate) fn complete_undotted_self( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { if !ctx.config.enable_self_on_the_fly { return; } - match ctx.path_context() { - Some( - path_ctx @ PathCompletionCtx { - qualified: Qualified::No, - kind: PathKind::Expr { .. }, - .. - }, - ) if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {} + match path_ctx { + PathCompletionCtx { qualified: Qualified::No, kind: PathKind::Expr { .. }, .. } + if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {} _ => return, } diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 446f2f31942..afc929d68d8 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -8,7 +8,11 @@ use crate::{ CompletionContext, Completions, }; -pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) { +pub(crate) fn complete_expr_path( + acc: &mut Completions, + ctx: &CompletionContext, + name_ref_ctx: &NameRefContext, +) { let _p = profile::span("complete_expr_path"); let ( @@ -19,8 +23,8 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) after_if_expr, wants_mut_token, in_condition, - ) = match ctx.nameref_ctx() { - Some(&NameRefContext { + ) = match name_ref_ctx { + &NameRefContext { kind: Some(NameRefKind::Path(PathCompletionCtx { kind: @@ -36,7 +40,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) .. })), .. - }) if ctx.qualifier_ctx.none() => ( + } if ctx.qualifier_ctx.none() => ( qualified, in_block_expr, in_loop_body, diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs index ae8c199f0c4..3007b3c319c 100644 --- a/crates/ide-completion/src/completions/extern_abi.rs +++ b/crates/ide-completion/src/completions/extern_abi.rs @@ -5,9 +5,7 @@ use syntax::{ }; use crate::{ - completions::Completions, - context::{CompletionContext, IdentContext}, - CompletionItem, CompletionItemKind, + completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind, }; // Most of these are feature gated, we should filter/add feature gate completions once we have them. @@ -42,15 +40,15 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ "unadjusted", ]; -pub(crate) fn complete_extern_abi(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let abi_str = match &ctx.ident_ctx { - IdentContext::String { expanded: Some(expanded), .. } - if expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) => - { - expanded - } - _ => return None, - }; +pub(crate) fn complete_extern_abi( + acc: &mut Completions, + _ctx: &CompletionContext, + expanded: &ast::String, +) -> Option<()> { + if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) { + return None; + } + let abi_str = expanded; let source_range = abi_str.text_range_between_quotes()?; for &abi in SUPPORTED_CALLING_CONVENTIONS { CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc); diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs index a9f598fffd3..93263f61cf1 100644 --- a/crates/ide-completion/src/completions/field.rs +++ b/crates/ide-completion/src/completions/field.rs @@ -2,16 +2,19 @@ use crate::{ context::{ - IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx, - PathKind, Qualified, TypeLocation, + NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, Qualified, + TypeLocation, }, CompletionContext, Completions, }; -pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext) { - match &ctx.ident_ctx { - IdentContext::Name(NameContext { kind: NameKind::RecordField, .. }) - | IdentContext::NameRef(NameRefContext { +pub(crate) fn complete_field_list_tuple_variant( + acc: &mut Completions, + ctx: &CompletionContext, + name_ref_ctx: &NameRefContext, +) { + match name_ref_ctx { + NameRefContext { kind: Some(NameRefKind::Path(PathCompletionCtx { has_macro_bang: false, @@ -22,7 +25,7 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext .. })), .. - }) => { + } => { if ctx.qualifier_ctx.vis_node.is_none() { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); add_keyword("pub(crate)", "pub(crate)"); @@ -30,6 +33,21 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext add_keyword("pub", "pub"); } } - _ => return, + _ => (), + } +} + +pub(crate) fn complete_field_list_record_variant( + acc: &mut Completions, + ctx: &CompletionContext, + name_ctx: &NameContext, +) { + if let NameContext { kind: NameKind::RecordField, .. } = name_ctx { + if ctx.qualifier_ctx.vis_node.is_none() { + let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); + add_keyword("pub(crate)", "pub(crate)"); + add_keyword("pub(super)", "pub(super)"); + add_keyword("pub", "pub"); + } } } diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs index 67648ac17b5..a62b966e019 100644 --- a/crates/ide-completion/src/completions/fn_param.rs +++ b/crates/ide-completion/src/completions/fn_param.rs @@ -19,9 +19,13 @@ use crate::{ /// `spam: &mut Spam` insert text/label will be suggested. /// /// Also complete parameters for closure or local functions from the surrounding defined locals. -pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let (param_list, _, param_kind) = match &ctx.pattern_ctx { - Some(PatternContext { param_ctx: Some(kind), .. }) => kind, +pub(crate) fn complete_fn_param( + acc: &mut Completions, + ctx: &CompletionContext, + pattern_ctx: &PatternContext, +) -> Option<()> { + let (param_list, _, param_kind) = match pattern_ctx { + PatternContext { param_ctx: Some(kind), .. } => kind, _ => return None, }; diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs index 132599906af..f7a75b2dd58 100644 --- a/crates/ide-completion/src/completions/format_string.rs +++ b/crates/ide-completion/src/completions/format_string.rs @@ -2,28 +2,25 @@ use ide_db::syntax_helpers::format_string::is_format_string; use itertools::Itertools; -use syntax::{AstToken, TextRange, TextSize}; +use syntax::{ast, AstToken, TextRange, TextSize}; -use crate::{ - context::{CompletionContext, IdentContext}, - CompletionItem, CompletionItemKind, Completions, -}; +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions}; /// Complete identifiers in format strings. -pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) { - let string = match &ctx.ident_ctx { - IdentContext::String { expanded: Some(expanded), original } - if is_format_string(&expanded) => - { - original - } - _ => return, - }; +pub(crate) fn format_string( + acc: &mut Completions, + ctx: &CompletionContext, + original: &ast::String, + expanded: &ast::String, +) { + if !is_format_string(&expanded) { + return; + } let cursor = ctx.position.offset; let lit_start = ctx.original_token.text_range().start(); let cursor_in_lit = cursor - lit_start; - let prefix = &string.text()[..cursor_in_lit.into()]; + let prefix = &original.text()[..cursor_in_lit.into()]; let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple(); let brace_offset = match braces { // escaped brace diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 0b95d7a3e43..e613f2d25b1 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -2,43 +2,50 @@ use crate::{ completions::module_or_fn_macro, - context::{ItemListKind, PathCompletionCtx, PathKind, Qualified}, + context::{ItemListKind, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, Qualified}, CompletionContext, Completions, }; -mod trait_impl; +pub(crate) mod trait_impl; -pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) { +pub(crate) fn complete_item_list( + acc: &mut Completions, + ctx: &CompletionContext, + name_ref_ctx: &NameRefContext, +) { let _p = profile::span("complete_item_list"); - if let Some(_) = ctx.name_ctx() { - trait_impl::complete_trait_impl(acc, ctx); - return; - } - - let (qualified, kind, is_trivial_path) = match ctx.path_context() { - Some(ctx @ PathCompletionCtx { kind: PathKind::Item { kind }, qualified, .. }) => { - (qualified, Some(kind), ctx.is_trivial_path()) - } - Some( - ctx @ PathCompletionCtx { - kind: PathKind::Expr { in_block_expr: true, .. }, - qualified, - .. - }, - ) => (qualified, None, ctx.is_trivial_path()), + let (qualified, item_list_kind, is_trivial_path) = match name_ref_ctx { + NameRefContext { + kind: + Some(NameRefKind::Path( + ctx @ PathCompletionCtx { kind: PathKind::Item { kind }, qualified, .. }, + )), + .. + } => (qualified, Some(kind), ctx.is_trivial_path()), + NameRefContext { + kind: + Some(NameRefKind::Path( + ctx @ PathCompletionCtx { + kind: PathKind::Expr { in_block_expr: true, .. }, + qualified, + .. + }, + )), + .. + } => (qualified, None, ctx.is_trivial_path()), _ => return, }; - if matches!(kind, Some(ItemListKind::TraitImpl)) { - trait_impl::complete_trait_impl(acc, ctx); + if matches!(item_list_kind, Some(ItemListKind::TraitImpl)) { + trait_impl::complete_trait_impl_name_ref(acc, ctx, name_ref_ctx); } if is_trivial_path { - add_keywords(acc, ctx, kind); + add_keywords(acc, ctx, item_list_kind); } - if kind.is_none() { + if item_list_kind.is_none() { // this is already handled by expression return; } diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 846d5f09028..701985d507d 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -44,8 +44,8 @@ use text_edit::TextEdit; use crate::{ context::{ - IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, + ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx, + PathKind, }, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions, }; @@ -58,53 +58,41 @@ enum ImplCompletionKind { Const, } -pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { - if let Some((kind, replacement_range, impl_def)) = completion_match(ctx) { - if let Some(hir_impl) = ctx.sema.to_def(&impl_def) { - get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| { - use self::ImplCompletionKind::*; - match (item, kind) { - (hir::AssocItem::Function(func), All | Fn) => { - add_function_impl(acc, ctx, replacement_range, func, hir_impl) - } - (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias) - } - (hir::AssocItem::Const(const_), All | Const) => { - add_const_impl(acc, ctx, replacement_range, const_, hir_impl) - } - _ => {} - } - }); - } - } +pub(crate) fn complete_trait_impl_name( + acc: &mut Completions, + ctx: &CompletionContext, + NameContext { name, kind, .. }: &NameContext, +) -> Option<()> { + let kind = match kind { + NameKind::Const => ImplCompletionKind::Const, + NameKind::Function => ImplCompletionKind::Fn, + NameKind::TypeAlias => ImplCompletionKind::TypeAlias, + _ => return None, + }; + let token = ctx.token.clone(); + let item = match name { + Some(name) => name.syntax().parent(), + None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token } + .parent(), + }?; + complete_trait_impl( + acc, + ctx, + kind, + replacement_range(ctx, &item), + // item -> ASSOC_ITEM_LIST -> IMPL + ast::Impl::cast(item.parent()?.parent()?)?, + ); + Some(()) } -fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, TextRange, ast::Impl)> { - match &ctx.ident_ctx { - IdentContext::Name(NameContext { name, kind, .. }) => { - let kind = match kind { - NameKind::Const => ImplCompletionKind::Const, - NameKind::Function => ImplCompletionKind::Fn, - NameKind::TypeAlias => ImplCompletionKind::TypeAlias, - _ => return None, - }; - let token = ctx.token.clone(); - let item = match name { - Some(name) => name.syntax().parent(), - None => { - if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token } - .parent() - } - }?; - Some(( - kind, - replacement_range(ctx, &item), - // item -> ASSOC_ITEM_LIST -> IMPL - ast::Impl::cast(item.parent()?.parent()?)?, - )) - } - IdentContext::NameRef(NameRefContext { +pub(crate) fn complete_trait_impl_name_ref( + acc: &mut Completions, + ctx: &CompletionContext, + name_ref_ctx: &NameRefContext, +) -> Option<()> { + match name_ref_ctx { + NameRefContext { nameref, kind: Some(NameRefKind::Path( @@ -113,15 +101,44 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Text .. }, )), - }) if path_ctx.is_trivial_path() => Some(( + } if path_ctx.is_trivial_path() => complete_trait_impl( + acc, + ctx, ImplCompletionKind::All, match nameref { Some(name) => name.syntax().text_range(), None => ctx.source_range(), }, ctx.impl_def.clone()?, - )), - _ => None, + ), + _ => (), + } + Some(()) +} + +fn complete_trait_impl( + acc: &mut Completions, + ctx: &CompletionContext, + kind: ImplCompletionKind, + replacement_range: TextRange, + impl_def: ast::Impl, +) { + if let Some(hir_impl) = ctx.sema.to_def(&impl_def) { + get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| { + use self::ImplCompletionKind::*; + match (item, kind) { + (hir::AssocItem::Function(func), All | Fn) => { + add_function_impl(acc, ctx, replacement_range, func, hir_impl) + } + (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { + add_type_alias_impl(acc, ctx, replacement_range, type_alias) + } + (hir::AssocItem::Const(const_), All | Const) => { + add_const_impl(acc, ctx, replacement_range, const_, hir_impl) + } + _ => {} + } + }); } } diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 2e266b7714c..57d545ab8fe 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -1,23 +1,17 @@ -//! Completes keywords, except: -//! - `self`, `super` and `crate`, as these are considered part of path completions. -//! - `await`, as this is a postfix completion we handle this in the postfix completions. +//! Completes `where` and `for` keywords. -use syntax::ast::Item; +use syntax::ast::{self, Item}; -use crate::{ - context::{NameRefContext, NameRefKind}, - CompletionContext, Completions, -}; - -pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { - let item = match ctx.nameref_ctx() { - Some(NameRefContext { kind: Some(NameRefKind::Keyword(item)), .. }) => item, - _ => return, - }; +use crate::{CompletionContext, Completions}; +pub(crate) fn complete_special_keywords( + acc: &mut Completions, + ctx: &CompletionContext, + keyword_item: &ast::Item, +) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); - match item { + match keyword_item { Item::Impl(it) => { if it.for_token().is_none() && it.trait_().is_none() && it.self_ty().is_some() { add_keyword("for", "for"); diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs index 12fcc8920a1..76e1c2e510a 100644 --- a/crates/ide-completion/src/completions/lifetime.rs +++ b/crates/ide-completion/src/completions/lifetime.rs @@ -16,13 +16,17 @@ use crate::{ }; /// Completes lifetimes. -pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) { - let (lp, lifetime) = match ctx.lifetime_ctx() { - Some(LifetimeContext { kind: LifetimeKind::Lifetime, lifetime }) => (None, lifetime), - Some(LifetimeContext { +pub(crate) fn complete_lifetime( + acc: &mut Completions, + ctx: &CompletionContext, + lifetime_ctx: &LifetimeContext, +) { + let (lp, lifetime) = match lifetime_ctx { + LifetimeContext { kind: LifetimeKind::Lifetime, lifetime } => (None, lifetime), + LifetimeContext { kind: LifetimeKind::LifetimeParam { is_decl: false, param }, lifetime, - }) => (Some(param), lifetime), + } => (Some(param), lifetime), _ => return, }; let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) { @@ -48,8 +52,12 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) } /// Completes labels. -pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) { - if !matches!(ctx.lifetime_ctx(), Some(LifetimeContext { kind: LifetimeKind::LabelRef, .. })) { +pub(crate) fn complete_label( + acc: &mut Completions, + ctx: &CompletionContext, + lifetime_ctx: &LifetimeContext, +) { + if !matches!(lifetime_ctx, LifetimeContext { kind: LifetimeKind::LabelRef, .. }) { return; } ctx.process_all_names_raw(&mut |name, res| { diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index 3e03528f0cd..8dd1d1d8ac8 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -15,9 +15,13 @@ use crate::{ }; /// Complete mod declaration, i.e. `mod $0;` -pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let mod_under_caret = match ctx.name_ctx() { - Some(NameContext { kind: NameKind::Module(mod_under_caret), .. }) => mod_under_caret, +pub(crate) fn complete_mod( + acc: &mut Completions, + ctx: &CompletionContext, + name_ctx: &NameContext, +) -> Option<()> { + let mod_under_caret = match name_ctx { + NameContext { kind: NameKind::Module(mod_under_caret), .. } => mod_under_caret, _ => return None, }; if mod_under_caret.item_list().is_some() { diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index b032bf03aab..1ecae1c4846 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -5,22 +5,16 @@ use ide_db::FxHashSet; use syntax::ast::Pat; use crate::{ - context::{PathCompletionCtx, PatternRefutability, Qualified}, + context::{PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified}, CompletionContext, Completions, }; /// Completes constants and paths in unqualified patterns. -pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { - let patctx = match &ctx.pattern_ctx { - Some(ctx) => ctx, - _ => return, - }; - - if let Some(path_ctx) = ctx.path_context() { - pattern_path_completion(acc, ctx, path_ctx); - return; - } - +pub(crate) fn complete_pattern( + acc: &mut Completions, + ctx: &CompletionContext, + patctx: &PatternContext, +) { match patctx.parent_pat.as_ref() { Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (), Some(Pat::RefPat(r)) => { @@ -108,11 +102,14 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { }); } -fn pattern_path_completion( +pub(crate) fn pattern_path_completion( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, .. }: &PathCompletionCtx, + PathCompletionCtx { qualified, kind, .. }: &PathCompletionCtx, ) { + if !matches!(kind, PathKind::Pat) { + return; + } match qualified { Qualified::With { resolution: Some(resolution), is_super_chain, .. } => { if *is_super_chain { diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 888b8f34884..5af44aa4b68 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -13,27 +13,22 @@ use text_edit::TextEdit; use crate::{ completions::postfix::format_like::add_format_like_completions, - context::{CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind}, + context::{CompletionContext, DotAccess, DotAccessKind}, item::{Builder, CompletionRelevancePostfixMatch}, CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope, }; -pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { +pub(crate) fn complete_postfix( + acc: &mut Completions, + ctx: &CompletionContext, + dot_access: &DotAccess, +) { if !ctx.config.enable_postfix_completions { return; } - let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() { - Some(NameRefContext { - kind: - Some(NameRefKind::DotAccess(DotAccess { - receiver_ty: Some(ty), - receiver: Some(it), - kind, - .. - })), - .. - }) => ( + let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match dot_access { + DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. } => ( it, &ty.original, match *kind { diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 5ba355f6ae8..eaab4cb4eea 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -1,36 +1,34 @@ //! Complete fields in record literals and patterns. use ide_db::SymbolKind; -use syntax::{ast::Expr, T}; +use syntax::{ + ast::{self, Expr}, + T, +}; use crate::{ - context::{ - NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PatternContext, Qualified, - }, + context::{PathCompletionCtx, PathKind, PatternContext, Qualified}, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, Completions, }; -pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let missing_fields = if let Some(PatternContext { record_pat: Some(record_pat), .. }) = - &ctx.pattern_ctx - { - ctx.sema.record_pattern_missing_fields(record_pat) - } else if let Some(NameRefContext { - kind: - Some( - NameRefKind::RecordExpr(record_expr) - | NameRefKind::Path(PathCompletionCtx { - kind: PathKind::Expr { is_func_update: Some(record_expr), .. }, - qualified: Qualified::No, - .. - }), - ), - .. - }) = ctx.nameref_ctx() - { - let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); +pub(crate) fn complete_record_pattern_fields( + acc: &mut Completions, + ctx: &CompletionContext, + pattern_ctx: &PatternContext, +) { + if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx { + complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat)); + } +} +pub(crate) fn complete_record_expr_fields_record_expr( + acc: &mut Completions, + ctx: &CompletionContext, + record_expr: &ast::RecordExpr, +) { + let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); - if let Some(hir::Adt::Union(un)) = ty.as_ref().and_then(|t| t.original.as_adt()) { + let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) { + Some(hir::Adt::Union(un)) => { // ctx.sema.record_literal_missing_fields will always return // an empty Vec on a union literal. This is normally // reasonable, but here we'd like to present the full list @@ -40,47 +38,80 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> match were_fields_specified { false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), - true => vec![], + true => return, } - } else { + } + _ => { let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); - let default_trait = ctx.famous_defs().core_default_Default(); - let impl_default_trait = - default_trait.zip(ty.as_ref()).map_or(false, |(default_trait, ty)| { - ty.original.impls_trait(ctx.db, default_trait, &[]) - }); - - if impl_default_trait && !missing_fields.is_empty() { - let completion_text = "..Default::default()"; - let mut item = - CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text); - let completion_text = - completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text); - item.insert_text(completion_text).set_relevance(CompletionRelevance { - postfix_match: Some(CompletionRelevancePostfixMatch::Exact), - ..Default::default() - }); - item.add_to(acc); - } + add_default_update(acc, ctx, ty, &missing_fields); if ctx.previous_token_is(T![.]) { let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), ".."); item.insert_text("."); item.add_to(acc); - return None; + return; } missing_fields } - } else { - return None; }; + complete_fields(acc, ctx, missing_fields); +} +fn add_default_update( + acc: &mut Completions, + ctx: &CompletionContext, + ty: Option, + missing_fields: &[(hir::Field, hir::Type)], +) { + let default_trait = ctx.famous_defs().core_default_Default(); + let impl_default_trait = default_trait + .zip(ty.as_ref()) + .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); + if impl_default_trait && !missing_fields.is_empty() { + let completion_text = "..Default::default()"; + let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text); + let completion_text = + completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text); + item.insert_text(completion_text).set_relevance(CompletionRelevance { + postfix_match: Some(CompletionRelevancePostfixMatch::Exact), + ..Default::default() + }); + item.add_to(acc); + } +} + +pub(crate) fn complete_record_expr_func_update( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { + if let PathCompletionCtx { + kind: PathKind::Expr { is_func_update: Some(record_expr), .. }, + qualified: Qualified::No, + .. + } = path_ctx + { + let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); + + match ty.as_ref().and_then(|t| t.original.as_adt()) { + Some(hir::Adt::Union(_)) => (), + _ => { + let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); + add_default_update(acc, ctx, ty, &missing_fields); + } + }; + } +} + +fn complete_fields( + acc: &mut Completions, + ctx: &CompletionContext, + missing_fields: Vec<(hir::Field, hir::Type)>, +) { for (field, ty) in missing_fields { acc.add_field(ctx, None, field, &ty); } - - Some(()) } #[cfg(test)] diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs index 293311fe910..48366987f40 100644 --- a/crates/ide-completion/src/completions/snippet.rs +++ b/crates/ide-completion/src/completions/snippet.rs @@ -15,13 +15,17 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) item } -pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { - let &can_be_stmt = match ctx.path_context() { - Some(PathCompletionCtx { +pub(crate) fn complete_expr_snippet( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { + let &can_be_stmt = match path_ctx { + PathCompletionCtx { qualified: Qualified::No, kind: PathKind::Expr { in_block_expr, .. }, .. - }) => in_block_expr, + } => in_block_expr, _ => return, }; @@ -40,13 +44,17 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte } } -pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { - let path_kind = match ctx.path_context() { - Some(PathCompletionCtx { +pub(crate) fn complete_item_snippet( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { + let path_kind = match path_ctx { + PathCompletionCtx { qualified: Qualified::No, kind: kind @ (PathKind::Item { .. } | PathKind::Expr { in_block_expr: true, .. }), .. - }) => kind, + } => kind, _ => return, }; if !ctx.qualifier_ctx.none() { diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index ed021e018bf..b8d172696d7 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -10,11 +10,15 @@ use crate::{ CompletionContext, Completions, }; -pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) { +pub(crate) fn complete_type_path( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { let _p = profile::span("complete_type_path"); - let (location, qualified) = match ctx.path_context() { - Some(PathCompletionCtx { kind: PathKind::Type { location }, qualified, .. }) => { + let (location, qualified) = match path_ctx { + PathCompletionCtx { kind: PathKind::Type { location }, qualified, .. } => { (location, qualified) } _ => return, @@ -84,7 +88,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db), - _ => unreachable!(), + _ => return, }; // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. @@ -187,14 +191,16 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) } } -pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let pat = match ctx.path_context() { - Some( - ctx @ PathCompletionCtx { - kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. }, - .. - }, - ) if ctx.is_trivial_path() => ascription, +pub(crate) fn complete_inferred_type( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) -> Option<()> { + let pat = match path_ctx { + PathCompletionCtx { + kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. }, + .. + } if path_ctx.is_trivial_path() => ascription, _ => return None, }; let x = match pat { diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index bafb1d974e0..9ffcee400d9 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -12,9 +12,13 @@ use crate::{ CompletionItem, CompletionItemKind, CompletionRelevance, Completions, }; -pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) { - let (qualified, name_ref, use_tree_parent) = match ctx.nameref_ctx() { - Some(NameRefContext { +pub(crate) fn complete_use_tree( + acc: &mut Completions, + ctx: &CompletionContext, + name_ref_ctx: &NameRefContext, +) { + let (qualified, name_ref, use_tree_parent) = match name_ref_ctx { + NameRefContext { kind: Some(NameRefKind::Path(PathCompletionCtx { kind: PathKind::Use, @@ -24,7 +28,7 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) })), nameref, .. - }) => (qualified, nameref, use_tree_parent), + } => (qualified, nameref, use_tree_parent), _ => return, }; diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs index 7362e191385..18513039e67 100644 --- a/crates/ide-completion/src/completions/vis.rs +++ b/crates/ide-completion/src/completions/vis.rs @@ -7,9 +7,13 @@ use crate::{ Completions, }; -pub(crate) fn complete_vis_path(acc: &mut Completions, ctx: &CompletionContext) { - let (qualified, &has_in_token) = match ctx.path_context() { - Some(PathCompletionCtx { kind: PathKind::Vis { has_in_token }, qualified, .. }) => { +pub(crate) fn complete_vis_path( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, +) { + let (qualified, &has_in_token) = match path_ctx { + PathCompletionCtx { kind: PathKind::Vis { has_in_token }, qualified, .. } => { (qualified, has_in_token) } _ => return, diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 6af24f87489..825047c5cf9 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -362,6 +362,7 @@ impl<'a> CompletionContext<'a> { FamousDefs(&self.sema, self.krate) } + // FIXME: This shouldn't exist pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> { match &self.ident_ctx { IdentContext::NameRef(it) => Some(it), @@ -369,20 +370,7 @@ impl<'a> CompletionContext<'a> { } } - pub(super) fn name_ctx(&self) -> Option<&NameContext> { - match &self.ident_ctx { - IdentContext::Name(it) => Some(it), - _ => None, - } - } - - pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> { - match &self.ident_ctx { - IdentContext::Lifetime(it) => Some(it), - _ => None, - } - } - + // FIXME: This shouldn't exist pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { match self.nameref_ctx() { Some(NameRefContext { @@ -393,10 +381,7 @@ impl<'a> CompletionContext<'a> { } } - pub(crate) fn has_dot_receiver(&self) -> bool { - self.dot_receiver().is_some() - } - + // FIXME: This shouldn't exist pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> { self.nameref_ctx().and_then(|ctx| match &ctx.kind { Some(NameRefKind::Path(path)) => Some(path), @@ -404,6 +389,7 @@ impl<'a> CompletionContext<'a> { }) } + // FIXME: This shouldn't exist pub(crate) fn path_qual(&self) -> Option<&ast::Path> { self.path_context().and_then(|it| match &it.qualified { Qualified::With { path, .. } => Some(path), diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index f53c5f6747a..e0e4926463c 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -23,7 +23,14 @@ use ide_db::{ use syntax::algo; use text_edit::TextEdit; -use crate::{completions::Completions, context::CompletionContext}; +use crate::{ + completions::Completions, + context::{ + CompletionContext, + IdentContext::{self, NameRef}, + NameRefContext, NameRefKind, + }, +}; pub use crate::{ config::{CallableSnippets, CompletionConfig}, @@ -145,40 +152,94 @@ pub fn completions( trigger_character: Option, ) -> Option { let ctx = &CompletionContext::new(db, position, config)?; - let mut acc = Completions::default(); + let mut completions = Completions::default(); - { - let acc = &mut acc; - // prevent `(` from triggering unwanted completion noise - if trigger_character != Some('(') { - completions::attribute::complete_attribute(acc, ctx); - completions::attribute::complete_derive(acc, ctx); - completions::attribute::complete_known_attribute_input(acc, ctx); - completions::dot::complete_dot(acc, ctx); - completions::expr::complete_expr_path(acc, ctx); - completions::extern_abi::complete_extern_abi(acc, ctx); - completions::field::complete_field_list(acc, ctx); - completions::flyimport::import_on_the_fly(acc, ctx); - completions::fn_param::complete_fn_param(acc, ctx); - completions::format_string::format_string(acc, ctx); - completions::item_list::complete_item_list(acc, ctx); - completions::keyword::complete_expr_keyword(acc, ctx); - completions::lifetime::complete_label(acc, ctx); - completions::lifetime::complete_lifetime(acc, ctx); - completions::mod_::complete_mod(acc, ctx); - completions::pattern::complete_pattern(acc, ctx); - completions::postfix::complete_postfix(acc, ctx); - completions::record::complete_record(acc, ctx); - completions::snippet::complete_expr_snippet(acc, ctx); - completions::snippet::complete_item_snippet(acc, ctx); - completions::r#type::complete_type_path(acc, ctx); - completions::r#type::complete_inferred_type(acc, ctx); - completions::use_::complete_use_tree(acc, ctx); + // prevent `(` from triggering unwanted completion noise + if trigger_character == Some('(') { + if let NameRef(NameRefContext { kind: Some(NameRefKind::Path(path_ctx)), .. }) = + &ctx.ident_ctx + { + completions::vis::complete_vis_path(&mut completions, ctx, path_ctx); } - completions::vis::complete_vis_path(acc, ctx); + // prevent `(` from triggering unwanted completion noise + return Some(completions); } - Some(acc) + { + let acc = &mut completions; + + match &ctx.ident_ctx { + IdentContext::Name(name_ctx) => { + completions::field::complete_field_list_record_variant(acc, ctx, name_ctx); + completions::mod_::complete_mod(acc, ctx, name_ctx); + completions::item_list::trait_impl::complete_trait_impl_name(acc, ctx, name_ctx); + } + NameRef(name_ref_ctx @ NameRefContext { kind, .. }) => { + completions::expr::complete_expr_path(acc, ctx, name_ref_ctx); + completions::field::complete_field_list_tuple_variant(acc, ctx, name_ref_ctx); + completions::use_::complete_use_tree(acc, ctx, name_ref_ctx); + completions::item_list::complete_item_list(acc, ctx, name_ref_ctx); + match kind { + Some(NameRefKind::Path(path_ctx)) => { + completions::record::complete_record_expr_func_update(acc, ctx, path_ctx); + completions::attribute::complete_attribute(acc, ctx, path_ctx); + completions::attribute::complete_derive(acc, ctx, path_ctx); + completions::dot::complete_undotted_self(acc, ctx, path_ctx); + completions::pattern::pattern_path_completion(acc, ctx, path_ctx); + completions::r#type::complete_inferred_type(acc, ctx, path_ctx); + completions::r#type::complete_type_path(acc, ctx, path_ctx); + completions::snippet::complete_expr_snippet(acc, ctx, path_ctx); + completions::snippet::complete_item_snippet(acc, ctx, path_ctx); + completions::vis::complete_vis_path(acc, ctx, path_ctx); + } + Some(NameRefKind::DotAccess(dot_access)) => { + completions::dot::complete_dot(acc, ctx, dot_access); + completions::postfix::complete_postfix(acc, ctx, dot_access); + } + Some(NameRefKind::Keyword(item)) => { + completions::keyword::complete_special_keywords(acc, ctx, item); + } + Some(NameRefKind::RecordExpr(record_expr)) => { + completions::record::complete_record_expr_fields_record_expr( + acc, + ctx, + record_expr, + ); + } + None => (), + } + } + IdentContext::Lifetime(lifetime_ctx) => { + completions::lifetime::complete_label(acc, ctx, lifetime_ctx); + completions::lifetime::complete_lifetime(acc, ctx, lifetime_ctx); + } + IdentContext::String { original, expanded: Some(expanded) } => { + completions::extern_abi::complete_extern_abi(acc, ctx, expanded); + completions::format_string::format_string(acc, ctx, original, expanded); + } + IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(attr) } => { + completions::attribute::complete_known_attribute_input(acc, ctx, attr); + } + IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (), + } + + if let Some(pattern_ctx) = &ctx.pattern_ctx { + completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx); + completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx); + // FIXME: this check is odd, we shouldn't need this? + if !matches!( + ctx.ident_ctx, + IdentContext::NameRef(NameRefContext { kind: Some(NameRefKind::Path(_)), .. }) + ) { + completions::pattern::complete_pattern(acc, ctx, pattern_ctx); + } + } + + // FIXME: This should be split + completions::flyimport::import_on_the_fly(acc, ctx); + } + + Some(completions) } /// Resolves additional completion data at the position given. diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 942dc033687..68908e330a4 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -17,7 +17,7 @@ use ide_db::{ use syntax::{SmolStr, SyntaxKind, TextRange}; use crate::{ - context::{PathCompletionCtx, PathKind}, + context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind}, item::{Builder, CompletionRelevanceTypeMatch}, render::{function::render_fn, literal::render_variant_lit, macro_::render_macro}, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, @@ -75,7 +75,13 @@ impl<'a> RenderContext<'a> { } pub(crate) fn path_is_call(&self) -> bool { - self.completion.path_context().map_or(false, |it| it.has_call_parens) + matches!( + self.completion.ident_ctx, + IdentContext::NameRef(NameRefContext { + kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })), + .. + }) + ) } fn is_deprecated(&self, def: impl HasAttrs) -> bool { @@ -285,8 +291,15 @@ fn render_resolution_simple_( // Add `<>` for generic types let type_path_no_ty_args = matches!( - ctx.completion.path_context(), - Some(PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. }) + ctx.completion.ident_ctx, + IdentContext::NameRef(NameRefContext { + kind: Some(NameRefKind::Path(PathCompletionCtx { + kind: PathKind::Type { .. }, + has_type_args: false, + .. + })), + .. + }) ) && ctx.completion.config.callable.is_some(); if type_path_no_ty_args { if let Some(cap) = ctx.snippet_cap() { @@ -937,7 +950,6 @@ fn main() -> RawIdentTable { #[test] fn no_parens_in_use_item() { - cov_mark::check!(no_parens_in_use_item); check_edit( "foo", r#" diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 566eaa575d4..ad240503c14 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -8,7 +8,7 @@ use syntax::SmolStr; use crate::{ context::{ - CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind, + CompletionContext, DotAccess, DotAccessKind, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, }, item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance}, @@ -91,9 +91,10 @@ fn render( .lookup_by(name.to_smol_str()); match completion.config.snippet_cap { - Some(cap) if should_add_parens(completion) => { - let (self_param, params) = params(completion, func, &func_kind); - add_call_parens(&mut item, completion, cap, call, self_param, params); + Some(cap) => { + if let Some((self_param, params)) = params(completion, func, &func_kind) { + add_call_parens(&mut item, completion, cap, call, self_param, params); + } } _ => (), } @@ -194,48 +195,6 @@ fn ref_of_param(ctx: &CompletionContext, arg: &str, ty: &hir::Type) -> &'static "" } -fn should_add_parens(ctx: &CompletionContext) -> bool { - if ctx.config.callable.is_none() { - return false; - } - - match ctx.path_context() { - Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => { - return false - } - Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }) => { - cov_mark::hit!(no_parens_in_use_item); - return false; - } - _ => {} - }; - - if matches!( - ctx.nameref_ctx(), - Some(NameRefContext { - kind: Some(NameRefKind::DotAccess(DotAccess { - kind: DotAccessKind::Method { has_parens: true }, - .. - })), - .. - }) - ) { - return false; - } - - // Don't add parentheses if the expected type is some function reference. - if let Some(ty) = &ctx.expected_type { - // FIXME: check signature matches? - if ty.is_fn() { - cov_mark::hit!(no_call_parens_if_fn_ptr_needed); - return false; - } - } - - // Nothing prevents us from adding parentheses - true -} - fn detail(db: &dyn HirDatabase, func: hir::Function) -> String { let mut ret_ty = func.ret_type(db); let mut detail = String::new(); @@ -285,13 +244,52 @@ fn params( ctx: &CompletionContext<'_>, func: hir::Function, func_kind: &FuncKind, -) -> (Option, Vec) { - let self_param = if ctx.has_dot_receiver() || matches!(func_kind, FuncKind::Method(Some(_))) { +) -> Option<(Option, Vec)> { + if ctx.config.callable.is_none() { + return None; + } + + let has_dot_receiver = match ctx.ident_ctx { + IdentContext::NameRef(NameRefContext { + kind: + Some(NameRefKind::DotAccess(DotAccess { + kind: DotAccessKind::Method { has_parens: true }, + .. + })), + .. + }) => return None, + IdentContext::NameRef(NameRefContext { + kind: Some(NameRefKind::DotAccess(DotAccess { .. })), + .. + }) => true, + IdentContext::NameRef(NameRefContext { + kind: + Some(NameRefKind::Path( + PathCompletionCtx { + kind: PathKind::Expr { .. }, has_call_parens: true, .. + } + | PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }, + )), + .. + }) => return None, + _ => false, + }; + + // Don't add parentheses if the expected type is some function reference. + if let Some(ty) = &ctx.expected_type { + // FIXME: check signature matches? + if ty.is_fn() { + cov_mark::hit!(no_call_parens_if_fn_ptr_needed); + return None; + } + } + + let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(Some(_))) { None } else { func.self_param(ctx.db) }; - (self_param, func.params_without_self(ctx.db)) + Some((self_param, func.params_without_self(ctx.db))) } #[cfg(test)] diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index fd76dedad06..1e03d066d14 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -4,7 +4,9 @@ use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind}; use ide_db::SymbolKind; use crate::{ - context::{CompletionContext, PathCompletionCtx, PathKind}, + context::{ + CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, + }, item::{Builder, CompletionItem}, render::{ compute_ref_match, compute_type_match, @@ -51,12 +53,19 @@ fn render( ) -> Option { let db = completion.db; let mut kind = thing.kind(db); - let should_add_parens = match completion.path_context() { - Some(PathCompletionCtx { has_call_parens: true, .. }) => false, - Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }) => { - cov_mark::hit!(no_parens_in_use_item); - false - } + let should_add_parens = match &completion.ident_ctx { + IdentContext::NameRef(NameRefContext { + kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })), + .. + }) => false, + IdentContext::NameRef(NameRefContext { + kind: + Some(NameRefKind::Path(PathCompletionCtx { + kind: PathKind::Use | PathKind::Type { .. }, + .. + })), + .. + }) => false, _ => true, }; diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index de527860d8c..26690d22abe 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -5,7 +5,7 @@ use ide_db::SymbolKind; use syntax::SmolStr; use crate::{ - context::{PathCompletionCtx, PathKind}, + context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind}, item::{Builder, CompletionItem}, render::RenderContext, }; @@ -33,10 +33,11 @@ fn render( let is_fn_like = macro_.is_fn_like(completion.db); let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; - let needs_bang = match completion.path_context() { - Some(PathCompletionCtx { kind, has_macro_bang, .. }) => { - is_fn_like && *kind != PathKind::Use && !has_macro_bang - } + let needs_bang = match &completion.ident_ctx { + IdentContext::NameRef(NameRefContext { + kind: Some(NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. })), + .. + }) => is_fn_like && *kind != PathKind::Use && !has_macro_bang, _ => is_fn_like, }; diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 5b403ae8ccc..33f169c1b9b 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -6,7 +6,9 @@ use itertools::Itertools; use syntax::SmolStr; use crate::{ - context::{ParamKind, PathCompletionCtx, PatternContext}, + context::{ + IdentContext, NameRefContext, NameRefKind, ParamKind, PathCompletionCtx, PatternContext, + }, render::{variant::visible_fields, RenderContext}, CompletionItem, CompletionItemKind, }; @@ -78,8 +80,11 @@ fn render_pat( fields_omitted: bool, ) -> Option { let has_call_parens = matches!( - ctx.completion.path_context(), - Some(PathCompletionCtx { has_call_parens: true, .. }) + ctx.completion.ident_ctx, + IdentContext::NameRef(NameRefContext { + kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })), + .. + }) ); let mut pat = match kind { StructKind::Tuple if !has_call_parens => { diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index c7514e1b578..d9b31832085 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -160,8 +160,6 @@ fn main() { "#, expect![[r#" fd ..Default::default() - fd foo1 u32 - fd foo2 u32 fn main() fn() lc foo Foo lc thing i32 diff --git a/crates/ide-completion/src/tests/use_tree.rs b/crates/ide-completion/src/tests/use_tree.rs index c169a62077c..3134915bdab 100644 --- a/crates/ide-completion/src/tests/use_tree.rs +++ b/crates/ide-completion/src/tests/use_tree.rs @@ -179,7 +179,6 @@ impl Foo { #[test] fn enum_no_parens_in_qualified_use_tree() { - cov_mark::check!(no_parens_in_use_item); cov_mark::check!(enum_plain_qualified_use_tree); check( r#"