diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 3f9314bbb3..a0725198f7 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -23,13 +23,13 @@ pub(crate) mod vis; use std::iter; use hir::{known, ScopeDef}; -use ide_db::SymbolKind; +use ide_db::{imports::import_assets::LocatedImport, SymbolKind}; use syntax::ast; use crate::{ context::{ - ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, PathKind, PatternContext, - TypeLocation, Visible, + DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, + PathCompletionCtx, PathKind, PatternContext, TypeLocation, Visible, }, item::Builder, render::{ @@ -38,7 +38,7 @@ use crate::{ literal::{render_struct_literal, render_variant_lit}, macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, - render_field, render_resolution, render_resolution_simple, render_tuple_field, + render_field, render_path_resolution, render_resolution_simple, render_tuple_field, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, RenderContext, @@ -137,15 +137,16 @@ impl Completions { 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) => { - self.add_resolution(ctx, name, res); + self.add_module(ctx, m, name); } _ => (), }); } - pub(crate) fn add_resolution( + pub(crate) fn add_path_resolution( &mut self, ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, local_name: hir::Name, resolution: hir::ScopeDef, ) { @@ -153,7 +154,10 @@ impl Completions { cov_mark::hit!(qualified_path_doc_hidden); return; } - self.add(render_resolution(RenderContext::new(ctx), local_name, resolution).build()); + self.add( + render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution) + .build(), + ); } pub(crate) fn add_resolution_simple( @@ -174,12 +178,13 @@ impl Completions { module: hir::Module, local_name: hir::Name, ) { - self.add_resolution(ctx, local_name, hir::ScopeDef::ModuleDef(module.into())); + self.add_resolution_simple(ctx, local_name, hir::ScopeDef::ModuleDef(module.into())); } pub(crate) fn add_macro( &mut self, ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, mac: hir::Macro, local_name: hir::Name, ) { @@ -191,6 +196,7 @@ impl Completions { self.add( render_macro( RenderContext::new(ctx).private_editable(is_private_editable), + path_ctx, local_name, mac, ) @@ -201,6 +207,7 @@ impl Completions { pub(crate) fn add_function( &mut self, ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, func: hir::Function, local_name: Option, ) { @@ -212,6 +219,7 @@ impl Completions { self.add( render_fn( RenderContext::new(ctx).private_editable(is_private_editable), + path_ctx, local_name, func, ) @@ -222,6 +230,7 @@ impl Completions { pub(crate) fn add_method( &mut self, ctx: &CompletionContext, + dot_access: &DotAccess, func: hir::Function, receiver: Option, local_name: Option, @@ -234,6 +243,7 @@ impl Completions { self.add( render_method( RenderContext::new(ctx).private_editable(is_private_editable), + dot_access, receiver, local_name, func, @@ -242,6 +252,32 @@ impl Completions { ); } + pub(crate) fn add_method_with_import( + &mut self, + ctx: &CompletionContext, + dot_access: &DotAccess, + func: hir::Function, + import: LocatedImport, + ) { + let is_private_editable = match ctx.is_visible(&func) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + self.add( + render_method( + RenderContext::new(ctx) + .private_editable(is_private_editable) + .import_to_add(Some(import)), + dot_access, + None, + None, + func, + ) + .build(), + ); + } + pub(crate) fn add_const(&mut self, ctx: &CompletionContext, konst: hir::Const) { let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, @@ -277,11 +313,12 @@ impl Completions { pub(crate) fn add_qualified_enum_variant( &mut self, ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, variant: hir::Variant, path: hir::ModPath, ) { if let Some(builder) = - render_variant_lit(RenderContext::new(ctx), None, variant, Some(path)) + render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path)) { self.add(builder.build()); } @@ -290,11 +327,12 @@ impl Completions { pub(crate) fn add_enum_variant( &mut self, ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, variant: hir::Variant, local_name: Option, ) { if let Some(builder) = - render_variant_lit(RenderContext::new(ctx), local_name, variant, None) + render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None) { self.add(builder.build()); } @@ -324,12 +362,13 @@ impl Completions { pub(crate) fn add_struct_literal( &mut self, ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, strukt: hir::Struct, path: Option, local_name: Option, ) { if let Some(builder) = - render_struct_literal(RenderContext::new(ctx), strukt, path, local_name) + render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) { self.add(builder.build()); } @@ -369,11 +408,13 @@ impl Completions { pub(crate) fn add_variant_pat( &mut self, ctx: &CompletionContext, + pattern_ctx: &PatternContext, variant: hir::Variant, local_name: Option, ) { self.add_opt(render_variant_pat( RenderContext::new(ctx), + pattern_ctx, variant, local_name.clone(), None, @@ -383,20 +424,22 @@ impl Completions { pub(crate) fn add_qualified_variant_pat( &mut self, ctx: &CompletionContext, + pattern_ctx: &PatternContext, variant: hir::Variant, path: hir::ModPath, ) { let path = Some(&path); - self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path)); + self.add_opt(render_variant_pat(RenderContext::new(ctx), pattern_ctx, variant, None, path)); } pub(crate) fn add_struct_pat( &mut self, ctx: &CompletionContext, + pattern_ctx: &PatternContext, strukt: hir::Struct, local_name: Option, ) { - self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); + self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } } diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 3115971c85..1c4f9a3113 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -74,7 +74,7 @@ pub(crate) fn complete_known_attribute_input( pub(crate) fn complete_attribute_path( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, .. }: &PathCompletionCtx, + path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, &AttrCtx { kind, annotated_item_kind }: &AttrCtx, ) { let is_inner = kind == AttrKind::Inner; @@ -92,7 +92,7 @@ pub(crate) fn complete_attribute_path( for (name, def) in module.scope(ctx.db, Some(ctx.module)) { match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { - acc.add_macro(ctx, m, name) + acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { acc.add_module(ctx, m, name) @@ -108,7 +108,7 @@ pub(crate) fn complete_attribute_path( Qualified::No => { ctx.process_all_names(&mut |name, def| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { - acc.add_macro(ctx, m, name) + acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name), _ => (), diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs index 0e10f38153..21298b6ca5 100644 --- a/crates/ide-completion/src/completions/attribute/derive.rs +++ b/crates/ide-completion/src/completions/attribute/derive.rs @@ -13,7 +13,7 @@ use crate::{ pub(crate) fn complete_derive_path( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, .. }: &PathCompletionCtx, + path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, existing_derives: &ExistingDerives, ) { let core = ctx.famous_defs().core(); @@ -33,7 +33,7 @@ pub(crate) fn complete_derive_path( ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) => { - acc.add_macro(ctx, mac, name) + acc.add_macro(ctx, path_ctx, mac, name) } ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name), _ => (), @@ -59,7 +59,7 @@ pub(crate) fn complete_derive_path( match (core, mac.module(ctx.db).krate()) { // show derive dependencies for `core`/`std` derives (Some(core), mac_krate) if core == mac_krate => {} - _ => return acc.add_macro(ctx, mac, name), + _ => return acc.add_macro(ctx, path_ctx, mac, name), }; let name_ = name.to_smol_str(); @@ -92,7 +92,7 @@ pub(crate) fn complete_derive_path( item.lookup_by(lookup); item.add_to(acc); } - None => acc.add_macro(ctx, mac, name), + None => acc.add_macro(ctx, path_ctx, mac, name), } }); acc.add_nameref_keywords_with_colon(ctx); diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index a8a57c0c7d..b58a9f39f2 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -33,7 +33,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext, dot_a |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty), ); } - complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); + complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None)); } pub(crate) fn complete_undotted_self( @@ -68,7 +68,17 @@ pub(crate) fn complete_undotted_self( |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty), ); complete_methods(ctx, &ty, |func| { - acc.add_method(ctx, func, Some(hir::known::SELF_PARAM), None) + acc.add_method( + ctx, + &DotAccess { + receiver: None, + receiver_ty: None, + kind: DotAccessKind::Method { has_parens: false }, + }, + func, + Some(hir::known::SELF_PARAM), + None, + ) }); } diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 84ae596a8d..9c003be6af 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -11,7 +11,7 @@ use crate::{ pub(crate) fn complete_expr_path( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, .. }: &PathCompletionCtx, + path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, &ExprCtx { in_block_expr, in_loop_body, @@ -34,11 +34,12 @@ pub(crate) fn complete_expr_path( ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false); let scope_def_applicable = |def| { - use hir::{GenericParam::*, ModuleDef::*}; match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => { + false + } // Don't suggest attribute macros and derives. - ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), + ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), _ => true, } }; @@ -49,7 +50,7 @@ pub(crate) fn complete_expr_path( .0 .into_iter() .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db)) - .for_each(|item| add_assoc_item(acc, ctx, item)), + .for_each(|item| add_assoc_item(acc, ctx, path_ctx, item)), Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. @@ -62,7 +63,7 @@ pub(crate) fn complete_expr_path( let module_scope = module.scope(ctx.db, Some(ctx.module)); for (name, def) in module_scope { if scope_def_applicable(def) { - acc.add_resolution(ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def); } } } @@ -73,7 +74,7 @@ pub(crate) fn complete_expr_path( | hir::ModuleDef::BuiltinType(_)), ) => { if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { - add_enum_variants(acc, ctx, e); + add_enum_variants(acc, ctx, path_ctx, e); } let ty = match def { hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), @@ -81,7 +82,7 @@ pub(crate) fn complete_expr_path( let ty = a.ty(ctx.db); if let Some(hir::Adt::Enum(e)) = ty.as_adt() { cov_mark::hit!(completes_variant_through_alias); - add_enum_variants(acc, ctx, e); + add_enum_variants(acc, ctx, path_ctx, e); } ty } @@ -102,7 +103,7 @@ pub(crate) fn complete_expr_path( Some(ctx.module), None, |item| { - add_assoc_item(acc, ctx, item); + add_assoc_item(acc, ctx, path_ctx, item); None::<()> }, ); @@ -118,7 +119,7 @@ pub(crate) fn complete_expr_path( hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { // Handles `Trait::assoc` as well as `::assoc`. for item in t.items(ctx.db) { - add_assoc_item(acc, ctx, item); + add_assoc_item(acc, ctx, path_ctx, item); } } hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { @@ -129,7 +130,7 @@ pub(crate) fn complete_expr_path( }; if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - add_enum_variants(acc, ctx, e); + add_enum_variants(acc, ctx, path_ctx, e); } let mut seen = FxHashSet::default(); ty.iterate_path_candidates( @@ -142,7 +143,7 @@ pub(crate) fn complete_expr_path( // We might iterate candidates of a trait multiple times here, so deduplicate // them. if seen.insert(item) { - add_assoc_item(acc, ctx, item); + add_assoc_item(acc, ctx, path_ctx, item); } None::<()> }, @@ -167,10 +168,16 @@ pub(crate) fn complete_expr_path( .find_use_path(ctx.db, hir::ModuleDef::from(strukt)) .filter(|it| it.len() > 1); - acc.add_struct_literal(ctx, strukt, path, None); + acc.add_struct_literal(ctx, path_ctx, strukt, path, None); if complete_self { - acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE)); + acc.add_struct_literal( + ctx, + path_ctx, + strukt, + None, + Some(hir::known::SELF_TYPE), + ); } } hir::Adt::Union(un) => { @@ -191,7 +198,7 @@ pub(crate) fn complete_expr_path( e, impl_, |acc, ctx, variant, path| { - acc.add_qualified_enum_variant(ctx, variant, path) + acc.add_qualified_enum_variant(ctx, path_ctx, variant, path) }, ); } @@ -199,7 +206,7 @@ pub(crate) fn complete_expr_path( } ctx.process_all_names(&mut |name, def| { if scope_def_applicable(def) { - acc.add_resolution(ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def); } }); @@ -259,14 +266,26 @@ pub(crate) fn complete_expr_path( } } -fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) { +fn add_assoc_item( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, + item: hir::AssocItem, +) { match item { - hir::AssocItem::Function(func) => acc.add_function(ctx, func, None), + hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None), hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), } } -fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) { - e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None)); +fn add_enum_variants( + acc: &mut Completions, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, + e: hir::Enum, +) { + e.variants(ctx.db) + .into_iter() + .for_each(|variant| acc.add_enum_variant(ctx, path_ctx, variant, None)); } diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 608a74dc15..129910465c 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -116,17 +116,17 @@ pub(crate) fn import_on_the_fly_path( if !ctx.config.enable_imports_on_the_fly { return None; } - let (kind, qualified) = match path_ctx { + let qualified = match path_ctx { PathCompletionCtx { kind: - kind @ (PathKind::Expr { .. } + PathKind::Expr { .. } | PathKind::Type { .. } | PathKind::Attr { .. } | PathKind::Derive { .. } - | PathKind::Pat { .. }), + | PathKind::Pat { .. }, qualified, .. - } => (Some(kind), qualified), + } => qualified, _ => return None, }; let potential_import_name = import_name(ctx); @@ -139,13 +139,46 @@ pub(crate) fn import_on_the_fly_path( import_on_the_fly( acc, ctx, - kind, + path_ctx, import_assets, qualifier.map(|it| it.syntax().clone()).or_else(|| ctx.original_token.parent())?, potential_import_name, ) } +pub(crate) fn import_on_the_fly_pat( + acc: &mut Completions, + ctx: &CompletionContext, + pat_ctx: &PatternContext, +) -> Option<()> { + if !ctx.config.enable_imports_on_the_fly { + return None; + } + if let PatternContext { record_pat: Some(_), .. } = pat_ctx { + return None; + } + + let potential_import_name = import_name(ctx); + let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?; + + import_on_the_fly( + acc, + ctx, + &PathCompletionCtx { + has_call_parens: false, + has_macro_bang: false, + qualified: Qualified::No, + parent: None, + kind: crate::context::PathKind::Pat { pat_ctx: pat_ctx.clone() }, + has_type_args: false, + use_tree_parent: false, + }, + import_assets, + ctx.original_token.parent()?, + potential_import_name, + ) +} + pub(crate) fn import_on_the_fly_dot( acc: &mut Completions, ctx: &CompletionContext, @@ -164,46 +197,20 @@ pub(crate) fn import_on_the_fly_dot( receiver.syntax().clone(), )?; - import_on_the_fly( + import_on_the_fly_method( acc, ctx, - None, + dot_access, import_assets, receiver.syntax().clone(), potential_import_name, ) } -pub(crate) fn import_on_the_fly_pat( - acc: &mut Completions, - ctx: &CompletionContext, - pat_ctx: &PatternContext, -) -> Option<()> { - if !ctx.config.enable_imports_on_the_fly { - return None; - } - let kind = match pat_ctx { - PatternContext { record_pat: None, .. } => PathKind::Pat { pat_ctx: pat_ctx.clone() }, - _ => return None, - }; - - let potential_import_name = import_name(ctx); - let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?; - - import_on_the_fly( - acc, - ctx, - Some(&kind), - import_assets, - ctx.original_token.parent()?, - potential_import_name, - ) -} - fn import_on_the_fly( acc: &mut Completions, ctx: &CompletionContext, - path_kind: Option<&PathKind>, + path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx, import_assets: ImportAssets, position: SyntaxNode, potential_import_name: String, @@ -215,11 +222,7 @@ fn import_on_the_fly( } let ns_filter = |import: &LocatedImport| { - let path_kind = match path_kind { - Some(it) => it, - None => return true, - }; - match (path_kind, import.original_item) { + match (kind, import.original_item) { // Aren't handled in flyimport (PathKind::Vis { .. } | PathKind::Use, _) => false, // modules are always fair game @@ -276,12 +279,49 @@ fn import_on_the_fly( &user_input_lowercased, ) }) - .filter_map(|import| render_resolution_with_import(RenderContext::new(ctx), import)) + .filter_map(|import| { + render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) + }) .map(|builder| builder.build()), ); Some(()) } +fn import_on_the_fly_method( + acc: &mut Completions, + ctx: &CompletionContext, + dot_access: &DotAccess, + import_assets: ImportAssets, + position: SyntaxNode, + potential_import_name: String, +) -> Option<()> { + let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.clone()); + + if ImportScope::find_insert_use_container(&position, &ctx.sema).is_none() { + return None; + } + + let user_input_lowercased = potential_import_name.to_lowercase(); + + import_assets + .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) + .into_iter() + .filter(|import| { + !ctx.is_item_hidden(&import.item_to_import) + && !ctx.is_item_hidden(&import.original_item) + }) + .sorted_by_key(|located_import| { + compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) + }) + .for_each(|import| match import.original_item { + ItemInNs::Values(hir::ModuleDef::Function(f)) => { + acc.add_method_with_import(ctx, dot_access, f, import); + } + _ => (), + }); + Some(()) +} + fn import_name(ctx: &CompletionContext) -> String { let token_kind = ctx.token.kind(); if matches!(token_kind, T![.] | T![::]) { diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index e697e1971e..329d08a9e7 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -42,7 +42,7 @@ pub(crate) fn complete_item_list( for (name, def) in module.scope(ctx.db, Some(ctx.module)) { match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => { - acc.add_macro(ctx, m, name) + acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { acc.add_module(ctx, m, name) @@ -59,7 +59,7 @@ pub(crate) fn complete_item_list( Qualified::No if ctx.qualifier_ctx.none() => { ctx.process_all_names(&mut |name, def| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => { - acc.add_macro(ctx, m, name) + acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name), _ => (), diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index e2e8d3f205..91d5356541 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -13,9 +13,9 @@ use crate::{ pub(crate) fn complete_pattern( acc: &mut Completions, ctx: &CompletionContext, - patctx: &PatternContext, + pattern_ctx: &PatternContext, ) { - match patctx.parent_pat.as_ref() { + match pattern_ctx.parent_pat.as_ref() { Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (), Some(Pat::RefPat(r)) => { if r.mut_token().is_none() { @@ -24,7 +24,7 @@ pub(crate) fn complete_pattern( } _ => { let tok = ctx.token.text_range().start(); - match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) { + match (pattern_ctx.ref_token.as_ref(), pattern_ctx.mut_token.as_ref()) { (None, None) => { acc.add_keyword(ctx, "ref"); acc.add_keyword(ctx, "mut"); @@ -40,11 +40,11 @@ pub(crate) fn complete_pattern( } } - if patctx.record_pat.is_some() { + if pattern_ctx.record_pat.is_some() { return; } - let refutable = patctx.refutability == PatternRefutability::Refutable; + let refutable = pattern_ctx.refutability == PatternRefutability::Refutable; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; if let Some(hir::Adt::Enum(e)) = @@ -55,9 +55,9 @@ pub(crate) fn complete_pattern( acc, ctx, e, - &patctx.impl_, + &pattern_ctx.impl_, |acc, ctx, variant, path| { - acc.add_qualified_variant_pat(ctx, variant, path); + acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path); }, ); } @@ -69,26 +69,39 @@ pub(crate) fn complete_pattern( let add_simple_path = match res { hir::ScopeDef::ModuleDef(def) => match def { hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { - acc.add_struct_pat(ctx, strukt, Some(name.clone())); + acc.add_struct_pat(ctx, pattern_ctx, strukt, Some(name.clone())); true } hir::ModuleDef::Variant(variant) if refutable || single_variant_enum(variant.parent_enum(ctx.db)) => { - acc.add_variant_pat(ctx, variant, Some(name.clone())); + acc.add_variant_pat(ctx, pattern_ctx, variant, Some(name.clone())); true } hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), hir::ModuleDef::Const(..) => refutable, hir::ModuleDef::Module(..) => true, hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => { - return acc.add_macro(ctx, mac, name) + return acc.add_macro( + ctx, + &PathCompletionCtx { + has_call_parens: false, + has_macro_bang: false, + qualified: Qualified::No, + parent: None, + kind: crate::context::PathKind::Pat { pat_ctx: pattern_ctx.clone() }, + has_type_args: false, + use_tree_parent: false, + }, + mac, + name, + ) } _ => false, }, hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { Some(hir::Adt::Struct(strukt)) => { - acc.add_struct_pat(ctx, strukt, Some(name.clone())); + acc.add_struct_pat(ctx, pattern_ctx, strukt, Some(name.clone())); true } Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), @@ -111,7 +124,7 @@ pub(crate) fn complete_pattern( pub(crate) fn complete_pattern_path( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, .. }: &PathCompletionCtx, + path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, ) { match qualified { Qualified::With { resolution: Some(resolution), is_super_chain, .. } => { @@ -132,7 +145,7 @@ pub(crate) fn complete_pattern_path( }; if add_resolution { - acc.add_resolution(ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def); } } } @@ -150,9 +163,9 @@ pub(crate) fn complete_pattern_path( } hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { cov_mark::hit!(enum_plain_qualified_use_tree); - e.variants(ctx.db) - .into_iter() - .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); + e.variants(ctx.db).into_iter().for_each(|variant| { + acc.add_enum_variant(ctx, path_ctx, variant, None) + }); e.ty(ctx.db) } hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => { @@ -197,7 +210,7 @@ pub(crate) fn complete_pattern_path( ctx.process_all_names(&mut |name, res| { // FIXME: properly filter here if let ScopeDef::ModuleDef(_) = res { - acc.add_resolution(ctx, name, res); + acc.add_path_resolution(ctx, path_ctx, name, res); } }); diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 0f7ca75868..dea0c701b8 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -13,7 +13,7 @@ use crate::{ pub(crate) fn complete_type_path( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, .. }: &PathCompletionCtx, + path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, location: &TypeLocation, ) { let _p = profile::span("complete_type_path"); @@ -69,7 +69,7 @@ pub(crate) fn complete_type_path( let module_scope = module.scope(ctx.db, Some(ctx.module)); for (name, def) in module_scope { if scope_def_applicable(def) { - acc.add_resolution(ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def); } } } @@ -154,7 +154,7 @@ pub(crate) fn complete_type_path( _ => false, }; if add_resolution { - acc.add_resolution(ctx, name, res); + acc.add_path_resolution(ctx, path_ctx, name, res); } }); return; @@ -178,7 +178,7 @@ pub(crate) fn complete_type_path( } ctx.process_all_names(&mut |name, def| { if scope_def_applicable(def) { - acc.add_resolution(ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def); } }); } diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index f262355fc0..2c039d5018 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -13,7 +13,7 @@ use crate::{ pub(crate) fn complete_use_path( acc: &mut Completions, ctx: &CompletionContext, - PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx, + path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx, name_ref: &Option, ) { match qualified { @@ -68,7 +68,7 @@ pub(crate) fn complete_use_path( }; if add_resolution { - let mut builder = Builder::from_resolution(ctx, name, def); + let mut builder = Builder::from_resolution(ctx, path_ctx, name, def); builder.set_relevance(CompletionRelevance { is_name_already_imported, ..Default::default() @@ -81,7 +81,7 @@ pub(crate) fn complete_use_path( cov_mark::hit!(enum_plain_qualified_use_tree); e.variants(ctx.db) .into_iter() - .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); + .for_each(|variant| acc.add_enum_variant(ctx, path_ctx, variant, None)); } _ => {} } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 9aee1e8b49..441f7ad70b 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -269,7 +269,7 @@ pub(super) enum NameRefKind { /// The identifier we are currently completing. #[derive(Debug)] -pub(super) enum IdentContext { +pub(super) enum CompletionAnalysis { Name(NameContext), NameRef(NameRefContext), Lifetime(LifetimeContext), @@ -338,8 +338,6 @@ pub(crate) struct CompletionContext<'a> { /// The expected type of what we are completing. pub(super) expected_type: Option, - // We might wanna split these out of CompletionContext - pub(super) ident_ctx: IdentContext, pub(super) qualifier_ctx: QualifierCtx, pub(super) locals: FxHashMap, @@ -461,7 +459,7 @@ impl<'a> CompletionContext<'a> { db: &'a RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, config: &'a CompletionConfig, - ) -> Option> { + ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> { let _p = profile::span("CompletionContext::new"); let sema = Semantics::new(db); @@ -503,21 +501,16 @@ impl<'a> CompletionContext<'a> { module, expected_name: None, expected_type: None, - // dummy value, will be overwritten - ident_ctx: IdentContext::UnexpandedAttrTT { - fake_attribute_under_caret: None, - colon_prefix: false, - }, qualifier_ctx: Default::default(), locals, }; - ctx.expand_and_fill( + let ident_ctx = ctx.expand_and_analyze( original_file.syntax().clone(), file_with_fake_ident.syntax().clone(), offset, fake_ident_token, )?; - Some(ctx) + Some((ctx, ident_ctx)) } } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 1f691f3baf..7e6c842b1e 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -11,23 +11,23 @@ use syntax::{ }; use crate::context::{ - AttrCtx, CompletionContext, DotAccess, DotAccessKind, ExprCtx, IdentContext, ItemListKind, - LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind, - PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, - TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER, + AttrCtx, CompletionAnalysis, CompletionContext, DotAccess, DotAccessKind, ExprCtx, + ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext, + NameRefKind, ParamKind, PathCompletionCtx, PathKind, PatternContext, PatternRefutability, + Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER, }; impl<'a> CompletionContext<'a> { /// Expand attributes and macro calls at the current cursor position for both the original file /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and speculative states stay in sync. - pub(super) fn expand_and_fill( + pub(super) fn expand_and_analyze( &mut self, mut original_file: SyntaxNode, mut speculative_file: SyntaxNode, mut offset: TextSize, mut fake_ident_token: SyntaxToken, - ) -> Option<()> { + ) -> Option { let _p = profile::span("CompletionContext::expand_and_fill"); let mut derive_ctx = None; @@ -157,7 +157,7 @@ impl<'a> CompletionContext<'a> { break 'expansion; } - self.fill(&original_file, speculative_file, offset, derive_ctx) + self.analyze(&original_file, speculative_file, offset, derive_ctx) } /// Calculate the expected type and name of the cursor position. @@ -311,13 +311,13 @@ impl<'a> CompletionContext<'a> { /// Fill the completion context, this is what does semantic reasoning about the surrounding context /// of the completion location. - fn fill( + fn analyze( &mut self, original_file: &SyntaxNode, file_with_fake_ident: SyntaxNode, offset: TextSize, derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, - ) -> Option<()> { + ) -> Option { let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?; let syntax_element = NodeOrToken::Token(fake_ident_token); if is_in_token_of_for_loop(syntax_element.clone()) { @@ -350,8 +350,7 @@ impl<'a> CompletionContext<'a> { .collect(), }; } - self.ident_ctx = IdentContext::NameRef(nameref_ctx); - return Some(()); + return Some(CompletionAnalysis::NameRef(nameref_ctx)); } return None; } @@ -359,58 +358,54 @@ impl<'a> CompletionContext<'a> { let name_like = match find_node_at_offset(&file_with_fake_ident, offset) { Some(it) => it, None => { - if let Some(original) = ast::String::cast(self.original_token.clone()) { - self.ident_ctx = IdentContext::String { - original, - expanded: ast::String::cast(self.token.clone()), - }; - } else { - // Fix up trailing whitespace problem - // #[attr(foo = $0 - let token = - syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?; - let p = token.parent()?; - if p.kind() == SyntaxKind::TOKEN_TREE - && p.ancestors().any(|it| it.kind() == SyntaxKind::META) - { - let colon_prefix = previous_non_trivia_token(self.token.clone()) - .map_or(false, |it| T![:] == it.kind()); - self.ident_ctx = IdentContext::UnexpandedAttrTT { - fake_attribute_under_caret: syntax_element - .ancestors() - .find_map(ast::Attr::cast), - colon_prefix, - }; + let analysis = + if let Some(original) = ast::String::cast(self.original_token.clone()) { + CompletionAnalysis::String { + original, + expanded: ast::String::cast(self.token.clone()), + } } else { - return None; - } - } - return Some(()); + // Fix up trailing whitespace problem + // #[attr(foo = $0 + let token = + syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?; + let p = token.parent()?; + if p.kind() == SyntaxKind::TOKEN_TREE + && p.ancestors().any(|it| it.kind() == SyntaxKind::META) + { + let colon_prefix = previous_non_trivia_token(self.token.clone()) + .map_or(false, |it| T![:] == it.kind()); + CompletionAnalysis::UnexpandedAttrTT { + fake_attribute_under_caret: syntax_element + .ancestors() + .find_map(ast::Attr::cast), + colon_prefix, + } + } else { + return None; + } + }; + return Some(analysis); } }; - - match name_like { - ast::NameLike::Lifetime(lifetime) => { - self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime( - &self.sema, - original_file, - lifetime, - )?); - } + let analysis = match name_like { + ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime( + Self::classify_lifetime(&self.sema, original_file, lifetime)?, + ), ast::NameLike::NameRef(name_ref) => { let parent = name_ref.syntax().parent()?; let (nameref_ctx, qualifier_ctx) = Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?; self.qualifier_ctx = qualifier_ctx; - self.ident_ctx = IdentContext::NameRef(nameref_ctx); + CompletionAnalysis::NameRef(nameref_ctx) } ast::NameLike::Name(name) => { let name_ctx = Self::classify_name(&self.sema, original_file, name)?; - self.ident_ctx = IdentContext::Name(name_ctx); + CompletionAnalysis::Name(name_ctx) } - } - Some(()) + }; + Some(analysis) } fn classify_lifetime( diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index ce9357270b..c5557bdafb 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -9,7 +9,7 @@ use crate::{ fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { let (db, pos) = position(ra_fixture); let config = TEST_CONFIG; - let completion_context = CompletionContext::new(&db, pos, &config).unwrap(); + let (completion_context, _analysis) = CompletionContext::new(&db, pos, &config).unwrap(); let ty = completion_context .expected_type diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 6e5c2a9386..4774fe9db7 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -10,8 +10,8 @@ use syntax::{SmolStr, TextRange}; use text_edit::TextEdit; use crate::{ - context::CompletionContext, - render::{render_resolution, RenderContext}, + context::{CompletionContext, PathCompletionCtx}, + render::{render_path_resolution, RenderContext}, }; /// `CompletionItem` describes a single completion variant in the editor pop-up. @@ -434,10 +434,11 @@ pub(crate) struct Builder { impl Builder { pub(crate) fn from_resolution( ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, local_name: hir::Name, resolution: hir::ScopeDef, ) -> Self { - render_resolution(RenderContext::new(ctx), local_name, resolution) + render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution) } pub(crate) fn build(self) -> CompletionItem { diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index bf75dcf26b..fe02f05fd1 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -25,7 +25,8 @@ use text_edit::TextEdit; use crate::{ completions::Completions, context::{ - CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, + CompletionAnalysis, CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, + PathKind, }, }; @@ -148,12 +149,12 @@ pub fn completions( position: FilePosition, trigger_character: Option, ) -> Option { - let ctx = &CompletionContext::new(db, position, config)?; + let (ctx, analysis) = &CompletionContext::new(db, position, config)?; let mut completions = Completions::default(); // prevent `(` from triggering unwanted completion noise if trigger_character == Some('(') { - if let IdentContext::NameRef(NameRefContext { kind, .. }) = &ctx.ident_ctx { + if let CompletionAnalysis::NameRef(NameRefContext { kind, .. }) = &analysis { if let NameRefKind::Path( path_ctx @ PathCompletionCtx { kind: PathKind::Vis { has_in_token }, .. }, ) = kind @@ -168,20 +169,20 @@ pub fn completions( { let acc = &mut completions; - match &ctx.ident_ctx { - IdentContext::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), - IdentContext::NameRef(name_ref_ctx) => { + match &analysis { + CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), + CompletionAnalysis::NameRef(name_ref_ctx) => { completions::complete_name_ref(acc, ctx, name_ref_ctx) } - IdentContext::Lifetime(lifetime_ctx) => { + CompletionAnalysis::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) } => { + CompletionAnalysis::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 { + CompletionAnalysis::UnexpandedAttrTT { colon_prefix, fake_attribute_under_caret: Some(attr), } => { @@ -192,7 +193,7 @@ pub fn completions( attr, ); } - IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (), + CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), } } @@ -204,22 +205,26 @@ pub fn completions( pub fn resolve_completion_edits( db: &RootDatabase, config: &CompletionConfig, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, imports: impl IntoIterator, ) -> Option> { let _p = profile::span("resolve_completion_edits"); - let ctx = CompletionContext::new(db, position, config)?; - let position_for_import = &ctx.original_token.parent()?; - let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; + let sema = hir::Semantics::new(db); - let current_module = ctx.sema.scope(position_for_import)?.module(); + let original_file = sema.parse(file_id); + let original_token = + syntax::AstNode::syntax(&original_file).token_at_offset(offset).left_biased()?; + let position_for_import = &original_token.parent()?; + let scope = ImportScope::find_insert_use_container(position_for_import, &sema)?; + + let current_module = sema.scope(position_for_import)?.module(); let current_crate = current_module.krate(); let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); imports.into_iter().for_each(|(full_import_path, imported_name)| { let items_with_name = items_locator::items_with_name( - &ctx.sema, + &sema, current_crate, NameToImport::exact_case_sensitive(imported_name), items_locator::AssocItemSearch::Include, diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index c6091645ca..9c339a13e7 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::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind}, + context::{PathCompletionCtx, PathKind}, item::{Builder, CompletionRelevanceTypeMatch}, render::{function::render_fn, literal::render_variant_lit, macro_::render_macro}, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, @@ -74,16 +74,6 @@ impl<'a> RenderContext<'a> { .map_or(false, |it| it.kind() == SyntaxKind::MACRO_CALL) } - pub(crate) fn path_is_call(&self) -> bool { - matches!( - self.completion.ident_ctx, - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }), - .. - }) - ) - } - fn is_deprecated(&self, def: impl HasAttrs) -> bool { let attrs = def.attrs(self.db()); attrs.by_key("deprecated").exists() @@ -163,12 +153,13 @@ pub(crate) fn render_tuple_field( item.build() } -pub(crate) fn render_resolution( +pub(crate) fn render_path_resolution( ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, local_name: hir::Name, resolution: ScopeDef, ) -> Builder { - render_resolution_(ctx, local_name, None, resolution) + render_resolution_(ctx, path_ctx, local_name, None, resolution) } pub(crate) fn render_resolution_simple( @@ -181,6 +172,7 @@ pub(crate) fn render_resolution_simple( pub(crate) fn render_resolution_with_import( ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, import_edit: LocatedImport, ) -> Option { let resolution = ScopeDef::from(import_edit.original_item); @@ -190,7 +182,7 @@ pub(crate) fn render_resolution_with_import( ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), _ => item_name(ctx.db(), import_edit.original_item)?, }; - Some(render_resolution_(ctx, local_name, Some(import_edit), resolution)) + Some(render_resolution_(ctx, path_ctx, local_name, Some(import_edit), resolution)) } pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext) -> CompletionItem { @@ -202,6 +194,7 @@ pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext) fn render_resolution_( ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, local_name: hir::Name, import_to_add: Option, resolution: ScopeDef, @@ -212,21 +205,61 @@ fn render_resolution_( match resolution { ScopeDef::ModuleDef(Macro(mac)) => { let ctx = ctx.import_to_add(import_to_add); - return render_macro(ctx, local_name, mac); + return render_macro(ctx, path_ctx, local_name, mac); } ScopeDef::ModuleDef(Function(func)) => { let ctx = ctx.import_to_add(import_to_add); - return render_fn(ctx, Some(local_name), func); + return render_fn(ctx, path_ctx, Some(local_name), func); } ScopeDef::ModuleDef(Variant(var)) => { let ctx = ctx.clone().import_to_add(import_to_add.clone()); - if let Some(item) = render_variant_lit(ctx, Some(local_name.clone()), var, None) { + if let Some(item) = + render_variant_lit(ctx, path_ctx, Some(local_name.clone()), var, None) + { return item; } } _ => (), } - render_resolution_simple_(ctx, local_name, import_to_add, resolution) + render_resolution_simple_type(ctx, path_ctx, local_name, import_to_add, resolution) +} + +fn render_resolution_simple_type( + ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, + local_name: hir::Name, + import_to_add: Option, + resolution: ScopeDef, +) -> Builder { + let cap = ctx.snippet_cap(); + let db = ctx.completion.db; + let config = ctx.completion.config; + let name = local_name.to_smol_str(); + let mut item = render_resolution_simple_(ctx, local_name, import_to_add, resolution); + // Add `<>` for generic types + let type_path_no_ty_args = matches!( + path_ctx, + PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. } + ) && config.callable.is_some(); + if type_path_no_ty_args { + if let Some(cap) = cap { + let has_non_default_type_params = match resolution { + ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db), + ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(it)) => { + it.has_non_default_type_params(db) + } + _ => false, + }; + if has_non_default_type_params { + cov_mark::hit!(inserts_angle_brackets_for_generics); + item.lookup_by(name.clone()) + .label(SmolStr::from_iter([&name, "<…>"])) + .trigger_call_info() + .insert_snippet(cap, format!("{}<$0>", name)); + } + } + } + item } fn render_resolution_simple_( @@ -289,34 +322,6 @@ fn render_resolution_simple_( } }; - // Add `<>` for generic types - let type_path_no_ty_args = matches!( - ctx.completion.ident_ctx, - IdentContext::NameRef(NameRefContext { - kind: 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() { - let has_non_default_type_params = match resolution { - ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(db), - ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(db), - _ => false, - }; - if has_non_default_type_params { - cov_mark::hit!(inserts_angle_brackets_for_generics); - item.lookup_by(local_name.clone()) - .label(SmolStr::from_iter([&local_name, "<…>"])) - .trigger_call_info() - .insert_snippet(cap, format!("{}<$0>", local_name)); - } - } - } item.set_documentation(scope_def_docs(db, resolution)) .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 48539a03b1..3666ee40e2 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -8,8 +8,7 @@ use syntax::SmolStr; use crate::{ context::{ - CompletionContext, DotAccess, DotAccessKind, IdentContext, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, Qualified, + CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified, }, item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance}, render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext}, @@ -23,21 +22,54 @@ enum FuncKind { pub(crate) fn render_fn( ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, local_name: Option, func: hir::Function, ) -> Builder { let _p = profile::span("render_fn"); - render(ctx, local_name, func, FuncKind::Function) + let func_kind = FuncKind::Function; + let params = match ctx.completion.config.snippet_cap { + Some(_) => { + if !matches!( + path_ctx, + PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. } + | PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. } + ) { + params(ctx.completion, func, &func_kind, false) + } else { + None + } + } + _ => None, + }; + render( + ctx, + local_name, + func, + func_kind, + params, + matches!(path_ctx.qualified, Qualified::With { .. }), + ) } pub(crate) fn render_method( ctx: RenderContext<'_>, + dot_access: &DotAccess, receiver: Option, local_name: Option, func: hir::Function, ) -> Builder { let _p = profile::span("render_method"); - render(ctx, local_name, func, FuncKind::Method(receiver)) + let func_kind = FuncKind::Method(receiver); + let params = match ctx.completion.config.snippet_cap { + Some(_) => match dot_access { + DotAccess { kind: DotAccessKind::Method { has_parens: true }, .. } => None, + _ => params(ctx.completion, func, &func_kind, true), + }, + _ => None, + }; + + render(ctx, local_name, func, func_kind, params, false) } fn render( @@ -45,6 +77,8 @@ fn render( local_name: Option, func: hir::Function, func_kind: FuncKind, + params: Option<(Option, Vec)>, + qualified_path: bool, ) -> Builder { let db = completion.db; @@ -80,16 +114,6 @@ fn render( // FIXME For now we don't properly calculate the edits for ref match // completions on methods or qualified paths, so we've disabled them. // See #8058. - let qualified_path = matches!( - ctx.completion.ident_ctx, - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::Path(PathCompletionCtx { - qualified: Qualified::With { .. }, - .. - }), - .. - }) - ); if matches!(func_kind, FuncKind::Function) && !qualified_path { item.ref_match(ref_match); } @@ -100,11 +124,9 @@ fn render( .detail(detail(db, func)) .lookup_by(name.to_smol_str()); - match completion.config.snippet_cap { - Some(cap) => { - if let Some((self_param, params)) = params(completion, func, &func_kind) { - add_call_parens(&mut item, completion, cap, call, self_param, params); - } + match completion.config.snippet_cap.zip(params) { + Some((cap, (self_param, params))) => { + add_call_parens(&mut item, completion, cap, call, self_param, params); } _ => (), } @@ -254,37 +276,12 @@ fn params( ctx: &CompletionContext<'_>, func: hir::Function, func_kind: &FuncKind, + has_dot_receiver: bool, ) -> Option<(Option, Vec)> { if ctx.config.callable.is_none() { return None; } - let has_dot_receiver = match ctx.ident_ctx { - IdentContext::NameRef(NameRefContext { - kind: - NameRefKind::DotAccess(DotAccess { - kind: DotAccessKind::Method { has_parens: true }, - .. - }), - .. - }) => return None, - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::DotAccess(DotAccess { .. }), - .. - }) => true, - IdentContext::NameRef(NameRefContext { - kind: - 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? diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index 042c974257..b89030990e 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -4,9 +4,7 @@ use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind}; use ide_db::SymbolKind; use crate::{ - context::{ - CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, - }, + context::{CompletionContext, PathCompletionCtx, PathKind}, item::{Builder, CompletionItem}, render::{ compute_ref_match, compute_type_match, @@ -21,6 +19,7 @@ use crate::{ pub(crate) fn render_variant_lit( ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, local_name: Option, variant: hir::Variant, path: Option, @@ -29,11 +28,12 @@ pub(crate) fn render_variant_lit( let db = ctx.db(); let name = local_name.unwrap_or_else(|| variant.name(db)); - render(ctx, Variant::EnumVariant(variant), name, path) + render(ctx, path_ctx, Variant::EnumVariant(variant), name, path) } pub(crate) fn render_struct_literal( ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, strukt: hir::Struct, path: Option, local_name: Option, @@ -42,29 +42,21 @@ pub(crate) fn render_struct_literal( let db = ctx.db(); let name = local_name.unwrap_or_else(|| strukt.name(db)); - render(ctx, Variant::Struct(strukt), name, path) + render(ctx, path_ctx, Variant::Struct(strukt), name, path) } fn render( ctx @ RenderContext { completion, .. }: RenderContext<'_>, + path_ctx: &PathCompletionCtx, thing: Variant, name: hir::Name, path: Option, ) -> Option { let db = completion.db; let mut kind = thing.kind(db); - let should_add_parens = match &completion.ident_ctx { - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }), - .. - }) => false, - IdentContext::NameRef(NameRefContext { - kind: - NameRefKind::Path(PathCompletionCtx { - kind: PathKind::Use | PathKind::Type { .. }, .. - }), - .. - }) => false, + let should_add_parens = match &path_ctx { + PathCompletionCtx { has_call_parens: true, .. } => false, + 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 0c9c65f423..6da7bb3193 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -5,18 +5,24 @@ use ide_db::SymbolKind; use syntax::SmolStr; use crate::{ - context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind}, + context::{PathCompletionCtx, PathKind}, item::{Builder, CompletionItem}, render::RenderContext, }; -pub(crate) fn render_macro(ctx: RenderContext<'_>, name: hir::Name, macro_: hir::Macro) -> Builder { +pub(crate) fn render_macro( + ctx: RenderContext<'_>, + path_ctx: &PathCompletionCtx, + name: hir::Name, + macro_: hir::Macro, +) -> Builder { let _p = profile::span("render_macro"); - render(ctx, name, macro_) + render(ctx, path_ctx, name, macro_) } fn render( ctx @ RenderContext { completion, .. }: RenderContext<'_>, + PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx, name: hir::Name, macro_: hir::Macro, ) -> Builder { @@ -33,13 +39,7 @@ 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.ident_ctx { - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. }), - .. - }) => is_fn_like && *kind != PathKind::Use && !has_macro_bang, - _ => is_fn_like, - }; + let needs_bang = is_fn_like && *kind != PathKind::Use && !has_macro_bang; let mut item = CompletionItem::new( SymbolKind::from(macro_.kind(completion.db)), @@ -53,7 +53,7 @@ fn render( let name = &*name; match ctx.snippet_cap() { - Some(cap) if needs_bang && !ctx.path_is_call() => { + Some(cap) if needs_bang && !has_call_parens => { let snippet = format!("{}!{}$0{}", name, bra, ket); let lookup = banged_name(name); item.insert_snippet(cap, snippet).lookup_by(lookup); diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 74564bb3aa..463d292955 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -6,16 +6,14 @@ use itertools::Itertools; use syntax::SmolStr; use crate::{ - context::{ - IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind, - PathCompletionCtx, PathKind, PatternContext, - }, + context::{ParamKind, PatternContext}, render::{variant::visible_fields, RenderContext}, CompletionItem, CompletionItemKind, }; pub(crate) fn render_struct_pat( ctx: RenderContext<'_>, + pattern_ctx: &PatternContext, strukt: hir::Struct, local_name: Option, ) -> Option { @@ -30,13 +28,21 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str(); - let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?; + let pat = render_pat( + &ctx, + pattern_ctx, + &name, + strukt.kind(ctx.db()), + &visible_fields, + fields_omitted, + )?; Some(build_completion(ctx, name, pat, strukt)) } pub(crate) fn render_variant_pat( ctx: RenderContext<'_>, + pattern_ctx: &PatternContext, variant: hir::Variant, local_name: Option, path: Option<&hir::ModPath>, @@ -50,7 +56,14 @@ pub(crate) fn render_variant_pat( Some(path) => path.to_string().into(), None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(), }; - let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; + let pat = render_pat( + &ctx, + pattern_ctx, + &name, + variant.kind(ctx.db()), + &visible_fields, + fields_omitted, + )?; Some(build_completion(ctx, name, pat, variant)) } @@ -75,49 +88,28 @@ fn build_completion( fn render_pat( ctx: &RenderContext<'_>, + pattern_ctx: &PatternContext, name: &str, kind: StructKind, fields: &[hir::Field], fields_omitted: bool, ) -> Option { - let has_call_parens = matches!( - ctx.completion.ident_ctx, - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }), - .. - }) - ); let mut pat = match kind { - StructKind::Tuple if !has_call_parens => { - render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted) - } - StructKind::Record if !has_call_parens => { + StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted), + StructKind::Record => { render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted) } StructKind::Unit => return None, - _ => name.to_owned(), }; - let needs_ascription = !has_call_parens - && matches!( - &ctx.completion.ident_ctx, - IdentContext::NameRef(NameRefContext { - kind: NameRefKind::Path(PathCompletionCtx { - kind: PathKind::Pat { - pat_ctx - }, - .. - }), - .. - }) | IdentContext::Name(NameContext { - kind: NameKind::IdentPat(pat_ctx), ..} - ) - if matches!(pat_ctx, PatternContext { - param_ctx: Some((.., ParamKind::Function(_))), - has_type_ascription: false, - .. - }) - ); + let needs_ascription = matches!( + pattern_ctx, + PatternContext { + param_ctx: Some((.., ParamKind::Function(_))), + has_type_ascription: false, + .. + } + ); if needs_ascription { pat.push(':'); pat.push(' '); diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index a3bc1025e9..c393e16e7a 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1,22 +1,22 @@ use expect_test::{expect, Expect}; use crate::{ - context::{IdentContext, NameContext, NameKind, NameRefKind}, + context::{CompletionAnalysis, NameContext, NameKind, NameRefKind}, tests::{check_edit, check_edit_with_config, TEST_CONFIG}, }; fn check(ra_fixture: &str, expect: Expect) { let config = TEST_CONFIG; let (db, position) = crate::tests::position(ra_fixture); - let ctx = crate::context::CompletionContext::new(&db, position, &config).unwrap(); + let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); let mut acc = crate::completions::Completions::default(); - if let IdentContext::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = - &ctx.ident_ctx + if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = + &analysis { crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); } - if let IdentContext::NameRef(name_ref_ctx) = &ctx.ident_ctx { + if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { match &name_ref_ctx.kind { NameRefKind::Path(path) => { crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path);