internal: Lift out IdentContext from CompletionContext

This commit is contained in:
Lukas Wirth 2022-06-20 17:41:04 +02:00
parent bcf10cde13
commit 6e9c963348
21 changed files with 456 additions and 351 deletions

View file

@ -23,13 +23,13 @@ pub(crate) mod vis;
use std::iter; use std::iter;
use hir::{known, ScopeDef}; use hir::{known, ScopeDef};
use ide_db::SymbolKind; use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
use syntax::ast; use syntax::ast;
use crate::{ use crate::{
context::{ context::{
ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, PathKind, PatternContext, DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind,
TypeLocation, Visible, PathCompletionCtx, PathKind, PatternContext, TypeLocation, Visible,
}, },
item::Builder, item::Builder,
render::{ render::{
@ -38,7 +38,7 @@ use crate::{
literal::{render_struct_literal, render_variant_lit}, literal::{render_struct_literal, render_variant_lit},
macro_::render_macro, macro_::render_macro,
pattern::{render_struct_pat, render_variant_pat}, 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}, type_alias::{render_type_alias, render_type_alias_with_eq},
union_literal::render_union_literal, union_literal::render_union_literal,
RenderContext, RenderContext,
@ -137,15 +137,16 @@ impl Completions {
pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) { pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
ctx.process_all_names(&mut |name, res| match res { ctx.process_all_names(&mut |name, res| match res {
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { 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, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
local_name: hir::Name, local_name: hir::Name,
resolution: hir::ScopeDef, resolution: hir::ScopeDef,
) { ) {
@ -153,7 +154,10 @@ impl Completions {
cov_mark::hit!(qualified_path_doc_hidden); cov_mark::hit!(qualified_path_doc_hidden);
return; 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( pub(crate) fn add_resolution_simple(
@ -174,12 +178,13 @@ impl Completions {
module: hir::Module, module: hir::Module,
local_name: hir::Name, 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( pub(crate) fn add_macro(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
mac: hir::Macro, mac: hir::Macro,
local_name: hir::Name, local_name: hir::Name,
) { ) {
@ -191,6 +196,7 @@ impl Completions {
self.add( self.add(
render_macro( render_macro(
RenderContext::new(ctx).private_editable(is_private_editable), RenderContext::new(ctx).private_editable(is_private_editable),
path_ctx,
local_name, local_name,
mac, mac,
) )
@ -201,6 +207,7 @@ impl Completions {
pub(crate) fn add_function( pub(crate) fn add_function(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
func: hir::Function, func: hir::Function,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
) { ) {
@ -212,6 +219,7 @@ impl Completions {
self.add( self.add(
render_fn( render_fn(
RenderContext::new(ctx).private_editable(is_private_editable), RenderContext::new(ctx).private_editable(is_private_editable),
path_ctx,
local_name, local_name,
func, func,
) )
@ -222,6 +230,7 @@ impl Completions {
pub(crate) fn add_method( pub(crate) fn add_method(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
dot_access: &DotAccess,
func: hir::Function, func: hir::Function,
receiver: Option<hir::Name>, receiver: Option<hir::Name>,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
@ -234,6 +243,7 @@ impl Completions {
self.add( self.add(
render_method( render_method(
RenderContext::new(ctx).private_editable(is_private_editable), RenderContext::new(ctx).private_editable(is_private_editable),
dot_access,
receiver, receiver,
local_name, local_name,
func, 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) { pub(crate) fn add_const(&mut self, ctx: &CompletionContext, konst: hir::Const) {
let is_private_editable = match ctx.is_visible(&konst) { let is_private_editable = match ctx.is_visible(&konst) {
Visible::Yes => false, Visible::Yes => false,
@ -277,11 +313,12 @@ impl Completions {
pub(crate) fn add_qualified_enum_variant( pub(crate) fn add_qualified_enum_variant(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
variant: hir::Variant, variant: hir::Variant,
path: hir::ModPath, path: hir::ModPath,
) { ) {
if let Some(builder) = 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()); self.add(builder.build());
} }
@ -290,11 +327,12 @@ impl Completions {
pub(crate) fn add_enum_variant( pub(crate) fn add_enum_variant(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
variant: hir::Variant, variant: hir::Variant,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
) { ) {
if let Some(builder) = 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()); self.add(builder.build());
} }
@ -324,12 +362,13 @@ impl Completions {
pub(crate) fn add_struct_literal( pub(crate) fn add_struct_literal(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
strukt: hir::Struct, strukt: hir::Struct,
path: Option<hir::ModPath>, path: Option<hir::ModPath>,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
) { ) {
if let Some(builder) = 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()); self.add(builder.build());
} }
@ -369,11 +408,13 @@ impl Completions {
pub(crate) fn add_variant_pat( pub(crate) fn add_variant_pat(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
pattern_ctx: &PatternContext,
variant: hir::Variant, variant: hir::Variant,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
) { ) {
self.add_opt(render_variant_pat( self.add_opt(render_variant_pat(
RenderContext::new(ctx), RenderContext::new(ctx),
pattern_ctx,
variant, variant,
local_name.clone(), local_name.clone(),
None, None,
@ -383,20 +424,22 @@ impl Completions {
pub(crate) fn add_qualified_variant_pat( pub(crate) fn add_qualified_variant_pat(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
pattern_ctx: &PatternContext,
variant: hir::Variant, variant: hir::Variant,
path: hir::ModPath, path: hir::ModPath,
) { ) {
let path = Some(&path); 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( pub(crate) fn add_struct_pat(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
pattern_ctx: &PatternContext,
strukt: hir::Struct, strukt: hir::Struct,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
) { ) {
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));
} }
} }

View file

@ -74,7 +74,7 @@ pub(crate) fn complete_known_attribute_input(
pub(crate) fn complete_attribute_path( pub(crate) fn complete_attribute_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
PathCompletionCtx { qualified, .. }: &PathCompletionCtx, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
&AttrCtx { kind, annotated_item_kind }: &AttrCtx, &AttrCtx { kind, annotated_item_kind }: &AttrCtx,
) { ) {
let is_inner = kind == AttrKind::Inner; 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)) { for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
match def { match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { 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)) => { hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
acc.add_module(ctx, m, name) acc.add_module(ctx, m, name)
@ -108,7 +108,7 @@ pub(crate) fn complete_attribute_path(
Qualified::No => { Qualified::No => {
ctx.process_all_names(&mut |name, def| match def { ctx.process_all_names(&mut |name, def| match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { 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), hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
_ => (), _ => (),

View file

@ -13,7 +13,7 @@ use crate::{
pub(crate) fn complete_derive_path( pub(crate) fn complete_derive_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
PathCompletionCtx { qualified, .. }: &PathCompletionCtx, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
existing_derives: &ExistingDerives, existing_derives: &ExistingDerives,
) { ) {
let core = ctx.famous_defs().core(); let core = ctx.famous_defs().core();
@ -33,7 +33,7 @@ pub(crate) fn complete_derive_path(
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) => 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), 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()) { match (core, mac.module(ctx.db).krate()) {
// show derive dependencies for `core`/`std` derives // show derive dependencies for `core`/`std` derives
(Some(core), mac_krate) if core == mac_krate => {} (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(); let name_ = name.to_smol_str();
@ -92,7 +92,7 @@ pub(crate) fn complete_derive_path(
item.lookup_by(lookup); item.lookup_by(lookup);
item.add_to(acc); 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); acc.add_nameref_keywords_with_colon(ctx);

View file

@ -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), |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( 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), |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty),
); );
complete_methods(ctx, &ty, |func| { 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,
)
}); });
} }

View file

@ -11,7 +11,7 @@ use crate::{
pub(crate) fn complete_expr_path( pub(crate) fn complete_expr_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
PathCompletionCtx { qualified, .. }: &PathCompletionCtx, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
&ExprCtx { &ExprCtx {
in_block_expr, in_block_expr,
in_loop_body, 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); ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
let scope_def_applicable = |def| { let scope_def_applicable = |def| {
use hir::{GenericParam::*, ModuleDef::*};
match def { match def {
ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => {
false
}
// Don't suggest attribute macros and derives. // 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, _ => true,
} }
}; };
@ -49,7 +50,7 @@ pub(crate) fn complete_expr_path(
.0 .0
.into_iter() .into_iter()
.flat_map(|it| hir::Trait::from(it).items(ctx.sema.db)) .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: None, .. } => {}
Qualified::With { resolution: Some(resolution), .. } => { Qualified::With { resolution: Some(resolution), .. } => {
// Add associated types on type parameters and `Self`. // 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)); let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope { for (name, def) in module_scope {
if scope_def_applicable(def) { 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(_)), | hir::ModuleDef::BuiltinType(_)),
) => { ) => {
if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { 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 { let ty = match def {
hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
@ -81,7 +82,7 @@ pub(crate) fn complete_expr_path(
let ty = a.ty(ctx.db); let ty = a.ty(ctx.db);
if let Some(hir::Adt::Enum(e)) = ty.as_adt() { if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
cov_mark::hit!(completes_variant_through_alias); cov_mark::hit!(completes_variant_through_alias);
add_enum_variants(acc, ctx, e); add_enum_variants(acc, ctx, path_ctx, e);
} }
ty ty
} }
@ -102,7 +103,7 @@ pub(crate) fn complete_expr_path(
Some(ctx.module), Some(ctx.module),
None, None,
|item| { |item| {
add_assoc_item(acc, ctx, item); add_assoc_item(acc, ctx, path_ctx, item);
None::<()> None::<()>
}, },
); );
@ -118,7 +119,7 @@ pub(crate) fn complete_expr_path(
hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
// Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
for item in t.items(ctx.db) { 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(_) => { 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() { 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(); let mut seen = FxHashSet::default();
ty.iterate_path_candidates( 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 // We might iterate candidates of a trait multiple times here, so deduplicate
// them. // them.
if seen.insert(item) { if seen.insert(item) {
add_assoc_item(acc, ctx, item); add_assoc_item(acc, ctx, path_ctx, item);
} }
None::<()> None::<()>
}, },
@ -167,10 +168,16 @@ pub(crate) fn complete_expr_path(
.find_use_path(ctx.db, hir::ModuleDef::from(strukt)) .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
.filter(|it| it.len() > 1); .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 { 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) => { hir::Adt::Union(un) => {
@ -191,7 +198,7 @@ pub(crate) fn complete_expr_path(
e, e,
impl_, impl_,
|acc, ctx, variant, path| { |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| { ctx.process_all_names(&mut |name, def| {
if scope_def_applicable(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 { 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::Const(ct) => acc.add_const(ctx, ct),
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
} }
} }
fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) { fn add_enum_variants(
e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None)); 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));
} }

View file

@ -116,17 +116,17 @@ pub(crate) fn import_on_the_fly_path(
if !ctx.config.enable_imports_on_the_fly { if !ctx.config.enable_imports_on_the_fly {
return None; return None;
} }
let (kind, qualified) = match path_ctx { let qualified = match path_ctx {
PathCompletionCtx { PathCompletionCtx {
kind: kind:
kind @ (PathKind::Expr { .. } PathKind::Expr { .. }
| PathKind::Type { .. } | PathKind::Type { .. }
| PathKind::Attr { .. } | PathKind::Attr { .. }
| PathKind::Derive { .. } | PathKind::Derive { .. }
| PathKind::Pat { .. }), | PathKind::Pat { .. },
qualified, qualified,
.. ..
} => (Some(kind), qualified), } => qualified,
_ => return None, _ => return None,
}; };
let potential_import_name = import_name(ctx); let potential_import_name = import_name(ctx);
@ -139,13 +139,46 @@ pub(crate) fn import_on_the_fly_path(
import_on_the_fly( import_on_the_fly(
acc, acc,
ctx, ctx,
kind, path_ctx,
import_assets, import_assets,
qualifier.map(|it| it.syntax().clone()).or_else(|| ctx.original_token.parent())?, qualifier.map(|it| it.syntax().clone()).or_else(|| ctx.original_token.parent())?,
potential_import_name, 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( pub(crate) fn import_on_the_fly_dot(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
@ -164,46 +197,20 @@ pub(crate) fn import_on_the_fly_dot(
receiver.syntax().clone(), receiver.syntax().clone(),
)?; )?;
import_on_the_fly( import_on_the_fly_method(
acc, acc,
ctx, ctx,
None, dot_access,
import_assets, import_assets,
receiver.syntax().clone(), receiver.syntax().clone(),
potential_import_name, 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( fn import_on_the_fly(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
path_kind: Option<&PathKind>, path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx,
import_assets: ImportAssets, import_assets: ImportAssets,
position: SyntaxNode, position: SyntaxNode,
potential_import_name: String, potential_import_name: String,
@ -215,11 +222,7 @@ fn import_on_the_fly(
} }
let ns_filter = |import: &LocatedImport| { let ns_filter = |import: &LocatedImport| {
let path_kind = match path_kind { match (kind, import.original_item) {
Some(it) => it,
None => return true,
};
match (path_kind, import.original_item) {
// Aren't handled in flyimport // Aren't handled in flyimport
(PathKind::Vis { .. } | PathKind::Use, _) => false, (PathKind::Vis { .. } | PathKind::Use, _) => false,
// modules are always fair game // modules are always fair game
@ -276,12 +279,49 @@ fn import_on_the_fly(
&user_input_lowercased, &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()), .map(|builder| builder.build()),
); );
Some(()) 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 { fn import_name(ctx: &CompletionContext) -> String {
let token_kind = ctx.token.kind(); let token_kind = ctx.token.kind();
if matches!(token_kind, T![.] | T![::]) { if matches!(token_kind, T![.] | T![::]) {

View file

@ -42,7 +42,7 @@ pub(crate) fn complete_item_list(
for (name, def) in module.scope(ctx.db, Some(ctx.module)) { for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
match def { match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => { 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)) => { hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
acc.add_module(ctx, m, name) acc.add_module(ctx, m, name)
@ -59,7 +59,7 @@ pub(crate) fn complete_item_list(
Qualified::No if ctx.qualifier_ctx.none() => { Qualified::No if ctx.qualifier_ctx.none() => {
ctx.process_all_names(&mut |name, def| match def { ctx.process_all_names(&mut |name, def| match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => { 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), hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
_ => (), _ => (),

View file

@ -13,9 +13,9 @@ use crate::{
pub(crate) fn complete_pattern( pub(crate) fn complete_pattern(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, 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::RangePat(_) | Pat::BoxPat(_)) => (),
Some(Pat::RefPat(r)) => { Some(Pat::RefPat(r)) => {
if r.mut_token().is_none() { if r.mut_token().is_none() {
@ -24,7 +24,7 @@ pub(crate) fn complete_pattern(
} }
_ => { _ => {
let tok = ctx.token.text_range().start(); 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) => { (None, None) => {
acc.add_keyword(ctx, "ref"); acc.add_keyword(ctx, "ref");
acc.add_keyword(ctx, "mut"); 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; 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; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
if let Some(hir::Adt::Enum(e)) = if let Some(hir::Adt::Enum(e)) =
@ -55,9 +55,9 @@ pub(crate) fn complete_pattern(
acc, acc,
ctx, ctx,
e, e,
&patctx.impl_, &pattern_ctx.impl_,
|acc, ctx, variant, path| { |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 { let add_simple_path = match res {
hir::ScopeDef::ModuleDef(def) => match def { hir::ScopeDef::ModuleDef(def) => match def {
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { 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 true
} }
hir::ModuleDef::Variant(variant) hir::ModuleDef::Variant(variant)
if refutable || single_variant_enum(variant.parent_enum(ctx.db)) => 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 true
} }
hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
hir::ModuleDef::Const(..) => refutable, hir::ModuleDef::Const(..) => refutable,
hir::ModuleDef::Module(..) => true, hir::ModuleDef::Module(..) => true,
hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => { 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, _ => false,
}, },
hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
Some(hir::Adt::Struct(strukt)) => { 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 true
} }
Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), 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( pub(crate) fn complete_pattern_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
PathCompletionCtx { qualified, .. }: &PathCompletionCtx, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
) { ) {
match qualified { match qualified {
Qualified::With { resolution: Some(resolution), is_super_chain, .. } => { Qualified::With { resolution: Some(resolution), is_super_chain, .. } => {
@ -132,7 +145,7 @@ pub(crate) fn complete_pattern_path(
}; };
if add_resolution { 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))) => { hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
cov_mark::hit!(enum_plain_qualified_use_tree); cov_mark::hit!(enum_plain_qualified_use_tree);
e.variants(ctx.db) e.variants(ctx.db).into_iter().for_each(|variant| {
.into_iter() acc.add_enum_variant(ctx, path_ctx, variant, None)
.for_each(|variant| acc.add_enum_variant(ctx, variant, None)); });
e.ty(ctx.db) e.ty(ctx.db)
} }
hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => { 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| { ctx.process_all_names(&mut |name, res| {
// FIXME: properly filter here // FIXME: properly filter here
if let ScopeDef::ModuleDef(_) = res { if let ScopeDef::ModuleDef(_) = res {
acc.add_resolution(ctx, name, res); acc.add_path_resolution(ctx, path_ctx, name, res);
} }
}); });

View file

@ -13,7 +13,7 @@ use crate::{
pub(crate) fn complete_type_path( pub(crate) fn complete_type_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
PathCompletionCtx { qualified, .. }: &PathCompletionCtx, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
location: &TypeLocation, location: &TypeLocation,
) { ) {
let _p = profile::span("complete_type_path"); 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)); let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope { for (name, def) in module_scope {
if scope_def_applicable(def) { 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, _ => false,
}; };
if add_resolution { if add_resolution {
acc.add_resolution(ctx, name, res); acc.add_path_resolution(ctx, path_ctx, name, res);
} }
}); });
return; return;
@ -178,7 +178,7 @@ pub(crate) fn complete_type_path(
} }
ctx.process_all_names(&mut |name, def| { ctx.process_all_names(&mut |name, def| {
if scope_def_applicable(def) { if scope_def_applicable(def) {
acc.add_resolution(ctx, name, def); acc.add_path_resolution(ctx, path_ctx, name, def);
} }
}); });
} }

View file

@ -13,7 +13,7 @@ use crate::{
pub(crate) fn complete_use_path( pub(crate) fn complete_use_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext, ctx: &CompletionContext,
PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx, path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx,
name_ref: &Option<ast::NameRef>, name_ref: &Option<ast::NameRef>,
) { ) {
match qualified { match qualified {
@ -68,7 +68,7 @@ pub(crate) fn complete_use_path(
}; };
if add_resolution { 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 { builder.set_relevance(CompletionRelevance {
is_name_already_imported, is_name_already_imported,
..Default::default() ..Default::default()
@ -81,7 +81,7 @@ pub(crate) fn complete_use_path(
cov_mark::hit!(enum_plain_qualified_use_tree); cov_mark::hit!(enum_plain_qualified_use_tree);
e.variants(ctx.db) e.variants(ctx.db)
.into_iter() .into_iter()
.for_each(|variant| acc.add_enum_variant(ctx, variant, None)); .for_each(|variant| acc.add_enum_variant(ctx, path_ctx, variant, None));
} }
_ => {} _ => {}
} }

View file

@ -269,7 +269,7 @@ pub(super) enum NameRefKind {
/// The identifier we are currently completing. /// The identifier we are currently completing.
#[derive(Debug)] #[derive(Debug)]
pub(super) enum IdentContext { pub(super) enum CompletionAnalysis {
Name(NameContext), Name(NameContext),
NameRef(NameRefContext), NameRef(NameRefContext),
Lifetime(LifetimeContext), Lifetime(LifetimeContext),
@ -338,8 +338,6 @@ pub(crate) struct CompletionContext<'a> {
/// The expected type of what we are completing. /// The expected type of what we are completing.
pub(super) expected_type: Option<Type>, pub(super) expected_type: Option<Type>,
// We might wanna split these out of CompletionContext
pub(super) ident_ctx: IdentContext,
pub(super) qualifier_ctx: QualifierCtx, pub(super) qualifier_ctx: QualifierCtx,
pub(super) locals: FxHashMap<Name, Local>, pub(super) locals: FxHashMap<Name, Local>,
@ -461,7 +459,7 @@ impl<'a> CompletionContext<'a> {
db: &'a RootDatabase, db: &'a RootDatabase,
position @ FilePosition { file_id, offset }: FilePosition, position @ FilePosition { file_id, offset }: FilePosition,
config: &'a CompletionConfig, config: &'a CompletionConfig,
) -> Option<CompletionContext<'a>> { ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
let _p = profile::span("CompletionContext::new"); let _p = profile::span("CompletionContext::new");
let sema = Semantics::new(db); let sema = Semantics::new(db);
@ -503,21 +501,16 @@ impl<'a> CompletionContext<'a> {
module, module,
expected_name: None, expected_name: None,
expected_type: 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(), qualifier_ctx: Default::default(),
locals, locals,
}; };
ctx.expand_and_fill( let ident_ctx = ctx.expand_and_analyze(
original_file.syntax().clone(), original_file.syntax().clone(),
file_with_fake_ident.syntax().clone(), file_with_fake_ident.syntax().clone(),
offset, offset,
fake_ident_token, fake_ident_token,
)?; )?;
Some(ctx) Some((ctx, ident_ctx))
} }
} }

View file

@ -11,23 +11,23 @@ use syntax::{
}; };
use crate::context::{ use crate::context::{
AttrCtx, CompletionContext, DotAccess, DotAccessKind, ExprCtx, IdentContext, ItemListKind, AttrCtx, CompletionAnalysis, CompletionContext, DotAccess, DotAccessKind, ExprCtx,
LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext,
PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, NameRefKind, ParamKind, PathCompletionCtx, PathKind, PatternContext, PatternRefutability,
TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER,
}; };
impl<'a> CompletionContext<'a> { impl<'a> CompletionContext<'a> {
/// Expand attributes and macro calls at the current cursor position for both the original file /// 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 fake file repeatedly. As soon as one of the two expansions fail we stop so the original
/// and speculative states stay in sync. /// and speculative states stay in sync.
pub(super) fn expand_and_fill( pub(super) fn expand_and_analyze(
&mut self, &mut self,
mut original_file: SyntaxNode, mut original_file: SyntaxNode,
mut speculative_file: SyntaxNode, mut speculative_file: SyntaxNode,
mut offset: TextSize, mut offset: TextSize,
mut fake_ident_token: SyntaxToken, mut fake_ident_token: SyntaxToken,
) -> Option<()> { ) -> Option<CompletionAnalysis> {
let _p = profile::span("CompletionContext::expand_and_fill"); let _p = profile::span("CompletionContext::expand_and_fill");
let mut derive_ctx = None; let mut derive_ctx = None;
@ -157,7 +157,7 @@ impl<'a> CompletionContext<'a> {
break 'expansion; 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. /// 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 /// Fill the completion context, this is what does semantic reasoning about the surrounding context
/// of the completion location. /// of the completion location.
fn fill( fn analyze(
&mut self, &mut self,
original_file: &SyntaxNode, original_file: &SyntaxNode,
file_with_fake_ident: SyntaxNode, file_with_fake_ident: SyntaxNode,
offset: TextSize, offset: TextSize,
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
) -> Option<()> { ) -> Option<CompletionAnalysis> {
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?; let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?;
let syntax_element = NodeOrToken::Token(fake_ident_token); let syntax_element = NodeOrToken::Token(fake_ident_token);
if is_in_token_of_for_loop(syntax_element.clone()) { if is_in_token_of_for_loop(syntax_element.clone()) {
@ -350,8 +350,7 @@ impl<'a> CompletionContext<'a> {
.collect(), .collect(),
}; };
} }
self.ident_ctx = IdentContext::NameRef(nameref_ctx); return Some(CompletionAnalysis::NameRef(nameref_ctx));
return Some(());
} }
return None; return None;
} }
@ -359,58 +358,54 @@ impl<'a> CompletionContext<'a> {
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) { let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
Some(it) => it, Some(it) => it,
None => { None => {
if let Some(original) = ast::String::cast(self.original_token.clone()) { let analysis =
self.ident_ctx = IdentContext::String { if let Some(original) = ast::String::cast(self.original_token.clone()) {
original, CompletionAnalysis::String {
expanded: ast::String::cast(self.token.clone()), 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,
};
} else { } else {
return None; // Fix up trailing whitespace problem
} // #[attr(foo = $0
} let token =
return Some(()); 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);
} }
}; };
let analysis = match name_like {
match name_like { ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
ast::NameLike::Lifetime(lifetime) => { Self::classify_lifetime(&self.sema, original_file, lifetime)?,
self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime( ),
&self.sema,
original_file,
lifetime,
)?);
}
ast::NameLike::NameRef(name_ref) => { ast::NameLike::NameRef(name_ref) => {
let parent = name_ref.syntax().parent()?; let parent = name_ref.syntax().parent()?;
let (nameref_ctx, qualifier_ctx) = let (nameref_ctx, qualifier_ctx) =
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?; Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?;
self.qualifier_ctx = qualifier_ctx; self.qualifier_ctx = qualifier_ctx;
self.ident_ctx = IdentContext::NameRef(nameref_ctx); CompletionAnalysis::NameRef(nameref_ctx)
} }
ast::NameLike::Name(name) => { ast::NameLike::Name(name) => {
let name_ctx = Self::classify_name(&self.sema, original_file, 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( fn classify_lifetime(

View file

@ -9,7 +9,7 @@ use crate::{
fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
let (db, pos) = position(ra_fixture); let (db, pos) = position(ra_fixture);
let config = TEST_CONFIG; 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 let ty = completion_context
.expected_type .expected_type

View file

@ -10,8 +10,8 @@ use syntax::{SmolStr, TextRange};
use text_edit::TextEdit; use text_edit::TextEdit;
use crate::{ use crate::{
context::CompletionContext, context::{CompletionContext, PathCompletionCtx},
render::{render_resolution, RenderContext}, render::{render_path_resolution, RenderContext},
}; };
/// `CompletionItem` describes a single completion variant in the editor pop-up. /// `CompletionItem` describes a single completion variant in the editor pop-up.
@ -434,10 +434,11 @@ pub(crate) struct Builder {
impl Builder { impl Builder {
pub(crate) fn from_resolution( pub(crate) fn from_resolution(
ctx: &CompletionContext, ctx: &CompletionContext,
path_ctx: &PathCompletionCtx,
local_name: hir::Name, local_name: hir::Name,
resolution: hir::ScopeDef, resolution: hir::ScopeDef,
) -> Self { ) -> 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 { pub(crate) fn build(self) -> CompletionItem {

View file

@ -25,7 +25,8 @@ use text_edit::TextEdit;
use crate::{ use crate::{
completions::Completions, completions::Completions,
context::{ context::{
CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, CompletionAnalysis, CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx,
PathKind,
}, },
}; };
@ -148,12 +149,12 @@ pub fn completions(
position: FilePosition, position: FilePosition,
trigger_character: Option<char>, trigger_character: Option<char>,
) -> Option<Completions> { ) -> Option<Completions> {
let ctx = &CompletionContext::new(db, position, config)?; let (ctx, analysis) = &CompletionContext::new(db, position, config)?;
let mut completions = Completions::default(); let mut completions = Completions::default();
// prevent `(` from triggering unwanted completion noise // prevent `(` from triggering unwanted completion noise
if trigger_character == Some('(') { if trigger_character == Some('(') {
if let IdentContext::NameRef(NameRefContext { kind, .. }) = &ctx.ident_ctx { if let CompletionAnalysis::NameRef(NameRefContext { kind, .. }) = &analysis {
if let NameRefKind::Path( if let NameRefKind::Path(
path_ctx @ PathCompletionCtx { kind: PathKind::Vis { has_in_token }, .. }, path_ctx @ PathCompletionCtx { kind: PathKind::Vis { has_in_token }, .. },
) = kind ) = kind
@ -168,20 +169,20 @@ pub fn completions(
{ {
let acc = &mut completions; let acc = &mut completions;
match &ctx.ident_ctx { match &analysis {
IdentContext::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx),
IdentContext::NameRef(name_ref_ctx) => { CompletionAnalysis::NameRef(name_ref_ctx) => {
completions::complete_name_ref(acc, ctx, 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_label(acc, ctx, lifetime_ctx);
completions::lifetime::complete_lifetime(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::extern_abi::complete_extern_abi(acc, ctx, expanded);
completions::format_string::format_string(acc, ctx, original, expanded); completions::format_string::format_string(acc, ctx, original, expanded);
} }
IdentContext::UnexpandedAttrTT { CompletionAnalysis::UnexpandedAttrTT {
colon_prefix, colon_prefix,
fake_attribute_under_caret: Some(attr), fake_attribute_under_caret: Some(attr),
} => { } => {
@ -192,7 +193,7 @@ pub fn completions(
attr, attr,
); );
} }
IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (), CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
} }
} }
@ -204,22 +205,26 @@ pub fn completions(
pub fn resolve_completion_edits( pub fn resolve_completion_edits(
db: &RootDatabase, db: &RootDatabase,
config: &CompletionConfig, config: &CompletionConfig,
position: FilePosition, FilePosition { file_id, offset }: FilePosition,
imports: impl IntoIterator<Item = (String, String)>, imports: impl IntoIterator<Item = (String, String)>,
) -> Option<Vec<TextEdit>> { ) -> Option<Vec<TextEdit>> {
let _p = profile::span("resolve_completion_edits"); let _p = profile::span("resolve_completion_edits");
let ctx = CompletionContext::new(db, position, config)?; let sema = hir::Semantics::new(db);
let position_for_import = &ctx.original_token.parent()?;
let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
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 current_crate = current_module.krate();
let new_ast = scope.clone_for_update(); let new_ast = scope.clone_for_update();
let mut import_insert = TextEdit::builder(); let mut import_insert = TextEdit::builder();
imports.into_iter().for_each(|(full_import_path, imported_name)| { imports.into_iter().for_each(|(full_import_path, imported_name)| {
let items_with_name = items_locator::items_with_name( let items_with_name = items_locator::items_with_name(
&ctx.sema, &sema,
current_crate, current_crate,
NameToImport::exact_case_sensitive(imported_name), NameToImport::exact_case_sensitive(imported_name),
items_locator::AssocItemSearch::Include, items_locator::AssocItemSearch::Include,

View file

@ -17,7 +17,7 @@ use ide_db::{
use syntax::{SmolStr, SyntaxKind, TextRange}; use syntax::{SmolStr, SyntaxKind, TextRange};
use crate::{ use crate::{
context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind}, context::{PathCompletionCtx, PathKind},
item::{Builder, CompletionRelevanceTypeMatch}, item::{Builder, CompletionRelevanceTypeMatch},
render::{function::render_fn, literal::render_variant_lit, macro_::render_macro}, render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
@ -74,16 +74,6 @@ impl<'a> RenderContext<'a> {
.map_or(false, |it| it.kind() == SyntaxKind::MACRO_CALL) .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 { fn is_deprecated(&self, def: impl HasAttrs) -> bool {
let attrs = def.attrs(self.db()); let attrs = def.attrs(self.db());
attrs.by_key("deprecated").exists() attrs.by_key("deprecated").exists()
@ -163,12 +153,13 @@ pub(crate) fn render_tuple_field(
item.build() item.build()
} }
pub(crate) fn render_resolution( pub(crate) fn render_path_resolution(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
local_name: hir::Name, local_name: hir::Name,
resolution: ScopeDef, resolution: ScopeDef,
) -> Builder { ) -> Builder {
render_resolution_(ctx, local_name, None, resolution) render_resolution_(ctx, path_ctx, local_name, None, resolution)
} }
pub(crate) fn render_resolution_simple( pub(crate) fn render_resolution_simple(
@ -181,6 +172,7 @@ pub(crate) fn render_resolution_simple(
pub(crate) fn render_resolution_with_import( pub(crate) fn render_resolution_with_import(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
import_edit: LocatedImport, import_edit: LocatedImport,
) -> Option<Builder> { ) -> Option<Builder> {
let resolution = ScopeDef::from(import_edit.original_item); 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), ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
_ => item_name(ctx.db(), import_edit.original_item)?, _ => 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 { 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_( fn render_resolution_(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
local_name: hir::Name, local_name: hir::Name,
import_to_add: Option<LocatedImport>, import_to_add: Option<LocatedImport>,
resolution: ScopeDef, resolution: ScopeDef,
@ -212,21 +205,61 @@ fn render_resolution_(
match resolution { match resolution {
ScopeDef::ModuleDef(Macro(mac)) => { ScopeDef::ModuleDef(Macro(mac)) => {
let ctx = ctx.import_to_add(import_to_add); 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)) => { ScopeDef::ModuleDef(Function(func)) => {
let ctx = ctx.import_to_add(import_to_add); 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)) => { ScopeDef::ModuleDef(Variant(var)) => {
let ctx = ctx.clone().import_to_add(import_to_add.clone()); 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; 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<LocatedImport>,
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_( 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)) item.set_documentation(scope_def_docs(db, resolution))
.set_deprecated(scope_def_is_deprecated(&ctx, resolution)); .set_deprecated(scope_def_is_deprecated(&ctx, resolution));

View file

@ -8,8 +8,7 @@ use syntax::SmolStr;
use crate::{ use crate::{
context::{ context::{
CompletionContext, DotAccess, DotAccessKind, IdentContext, NameRefContext, NameRefKind, CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified,
PathCompletionCtx, PathKind, Qualified,
}, },
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance}, item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext}, render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
@ -23,21 +22,54 @@ enum FuncKind {
pub(crate) fn render_fn( pub(crate) fn render_fn(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
func: hir::Function, func: hir::Function,
) -> Builder { ) -> Builder {
let _p = profile::span("render_fn"); 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( pub(crate) fn render_method(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
dot_access: &DotAccess,
receiver: Option<hir::Name>, receiver: Option<hir::Name>,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
func: hir::Function, func: hir::Function,
) -> Builder { ) -> Builder {
let _p = profile::span("render_method"); 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( fn render(
@ -45,6 +77,8 @@ fn render(
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
func: hir::Function, func: hir::Function,
func_kind: FuncKind, func_kind: FuncKind,
params: Option<(Option<hir::SelfParam>, Vec<hir::Param>)>,
qualified_path: bool,
) -> Builder { ) -> Builder {
let db = completion.db; let db = completion.db;
@ -80,16 +114,6 @@ fn render(
// FIXME For now we don't properly calculate the edits for ref match // FIXME For now we don't properly calculate the edits for ref match
// completions on methods or qualified paths, so we've disabled them. // completions on methods or qualified paths, so we've disabled them.
// See #8058. // 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 { if matches!(func_kind, FuncKind::Function) && !qualified_path {
item.ref_match(ref_match); item.ref_match(ref_match);
} }
@ -100,11 +124,9 @@ fn render(
.detail(detail(db, func)) .detail(detail(db, func))
.lookup_by(name.to_smol_str()); .lookup_by(name.to_smol_str());
match completion.config.snippet_cap { match completion.config.snippet_cap.zip(params) {
Some(cap) => { Some((cap, (self_param, params))) => {
if let Some((self_param, params)) = params(completion, func, &func_kind) { add_call_parens(&mut item, completion, cap, call, self_param, params);
add_call_parens(&mut item, completion, cap, call, self_param, params);
}
} }
_ => (), _ => (),
} }
@ -254,37 +276,12 @@ fn params(
ctx: &CompletionContext<'_>, ctx: &CompletionContext<'_>,
func: hir::Function, func: hir::Function,
func_kind: &FuncKind, func_kind: &FuncKind,
has_dot_receiver: bool,
) -> Option<(Option<hir::SelfParam>, Vec<hir::Param>)> { ) -> Option<(Option<hir::SelfParam>, Vec<hir::Param>)> {
if ctx.config.callable.is_none() { if ctx.config.callable.is_none() {
return 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. // Don't add parentheses if the expected type is some function reference.
if let Some(ty) = &ctx.expected_type { if let Some(ty) = &ctx.expected_type {
// FIXME: check signature matches? // FIXME: check signature matches?

View file

@ -4,9 +4,7 @@ use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
use ide_db::SymbolKind; use ide_db::SymbolKind;
use crate::{ use crate::{
context::{ context::{CompletionContext, PathCompletionCtx, PathKind},
CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind,
},
item::{Builder, CompletionItem}, item::{Builder, CompletionItem},
render::{ render::{
compute_ref_match, compute_type_match, compute_ref_match, compute_type_match,
@ -21,6 +19,7 @@ use crate::{
pub(crate) fn render_variant_lit( pub(crate) fn render_variant_lit(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
variant: hir::Variant, variant: hir::Variant,
path: Option<hir::ModPath>, path: Option<hir::ModPath>,
@ -29,11 +28,12 @@ pub(crate) fn render_variant_lit(
let db = ctx.db(); let db = ctx.db();
let name = local_name.unwrap_or_else(|| variant.name(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( pub(crate) fn render_struct_literal(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
strukt: hir::Struct, strukt: hir::Struct,
path: Option<hir::ModPath>, path: Option<hir::ModPath>,
local_name: Option<hir::Name>, local_name: Option<hir::Name>,
@ -42,29 +42,21 @@ pub(crate) fn render_struct_literal(
let db = ctx.db(); let db = ctx.db();
let name = local_name.unwrap_or_else(|| strukt.name(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( fn render(
ctx @ RenderContext { completion, .. }: RenderContext<'_>, ctx @ RenderContext { completion, .. }: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
thing: Variant, thing: Variant,
name: hir::Name, name: hir::Name,
path: Option<hir::ModPath>, path: Option<hir::ModPath>,
) -> Option<Builder> { ) -> Option<Builder> {
let db = completion.db; let db = completion.db;
let mut kind = thing.kind(db); let mut kind = thing.kind(db);
let should_add_parens = match &completion.ident_ctx { let should_add_parens = match &path_ctx {
IdentContext::NameRef(NameRefContext { PathCompletionCtx { has_call_parens: true, .. } => false,
kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }), PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. } => false,
..
}) => false,
IdentContext::NameRef(NameRefContext {
kind:
NameRefKind::Path(PathCompletionCtx {
kind: PathKind::Use | PathKind::Type { .. }, ..
}),
..
}) => false,
_ => true, _ => true,
}; };

View file

@ -5,18 +5,24 @@ use ide_db::SymbolKind;
use syntax::SmolStr; use syntax::SmolStr;
use crate::{ use crate::{
context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind}, context::{PathCompletionCtx, PathKind},
item::{Builder, CompletionItem}, item::{Builder, CompletionItem},
render::RenderContext, 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"); let _p = profile::span("render_macro");
render(ctx, name, macro_) render(ctx, path_ctx, name, macro_)
} }
fn render( fn render(
ctx @ RenderContext { completion, .. }: RenderContext<'_>, ctx @ RenderContext { completion, .. }: RenderContext<'_>,
PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx,
name: hir::Name, name: hir::Name,
macro_: hir::Macro, macro_: hir::Macro,
) -> Builder { ) -> Builder {
@ -33,13 +39,7 @@ fn render(
let is_fn_like = macro_.is_fn_like(completion.db); 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 (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
let needs_bang = match &completion.ident_ctx { let needs_bang = is_fn_like && *kind != PathKind::Use && !has_macro_bang;
IdentContext::NameRef(NameRefContext {
kind: NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. }),
..
}) => is_fn_like && *kind != PathKind::Use && !has_macro_bang,
_ => is_fn_like,
};
let mut item = CompletionItem::new( let mut item = CompletionItem::new(
SymbolKind::from(macro_.kind(completion.db)), SymbolKind::from(macro_.kind(completion.db)),
@ -53,7 +53,7 @@ fn render(
let name = &*name; let name = &*name;
match ctx.snippet_cap() { 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 snippet = format!("{}!{}$0{}", name, bra, ket);
let lookup = banged_name(name); let lookup = banged_name(name);
item.insert_snippet(cap, snippet).lookup_by(lookup); item.insert_snippet(cap, snippet).lookup_by(lookup);

View file

@ -6,16 +6,14 @@ use itertools::Itertools;
use syntax::SmolStr; use syntax::SmolStr;
use crate::{ use crate::{
context::{ context::{ParamKind, PatternContext},
IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind,
PathCompletionCtx, PathKind, PatternContext,
},
render::{variant::visible_fields, RenderContext}, render::{variant::visible_fields, RenderContext},
CompletionItem, CompletionItemKind, CompletionItem, CompletionItemKind,
}; };
pub(crate) fn render_struct_pat( pub(crate) fn render_struct_pat(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
pattern_ctx: &PatternContext,
strukt: hir::Struct, strukt: hir::Struct,
local_name: Option<Name>, local_name: Option<Name>,
) -> Option<CompletionItem> { ) -> Option<CompletionItem> {
@ -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 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)) Some(build_completion(ctx, name, pat, strukt))
} }
pub(crate) fn render_variant_pat( pub(crate) fn render_variant_pat(
ctx: RenderContext<'_>, ctx: RenderContext<'_>,
pattern_ctx: &PatternContext,
variant: hir::Variant, variant: hir::Variant,
local_name: Option<Name>, local_name: Option<Name>,
path: Option<&hir::ModPath>, path: Option<&hir::ModPath>,
@ -50,7 +56,14 @@ pub(crate) fn render_variant_pat(
Some(path) => path.to_string().into(), Some(path) => path.to_string().into(),
None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(), 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)) Some(build_completion(ctx, name, pat, variant))
} }
@ -75,49 +88,28 @@ fn build_completion(
fn render_pat( fn render_pat(
ctx: &RenderContext<'_>, ctx: &RenderContext<'_>,
pattern_ctx: &PatternContext,
name: &str, name: &str,
kind: StructKind, kind: StructKind,
fields: &[hir::Field], fields: &[hir::Field],
fields_omitted: bool, fields_omitted: bool,
) -> Option<String> { ) -> Option<String> {
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 { let mut pat = match kind {
StructKind::Tuple if !has_call_parens => { StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted) StructKind::Record => {
}
StructKind::Record if !has_call_parens => {
render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted) render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
} }
StructKind::Unit => return None, StructKind::Unit => return None,
_ => name.to_owned(),
}; };
let needs_ascription = !has_call_parens let needs_ascription = matches!(
&& matches!( pattern_ctx,
&ctx.completion.ident_ctx, PatternContext {
IdentContext::NameRef(NameRefContext { param_ctx: Some((.., ParamKind::Function(_))),
kind: NameRefKind::Path(PathCompletionCtx { has_type_ascription: false,
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,
..
})
);
if needs_ascription { if needs_ascription {
pat.push(':'); pat.push(':');
pat.push(' '); pat.push(' ');

View file

@ -1,22 +1,22 @@
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use crate::{ use crate::{
context::{IdentContext, NameContext, NameKind, NameRefKind}, context::{CompletionAnalysis, NameContext, NameKind, NameRefKind},
tests::{check_edit, check_edit_with_config, TEST_CONFIG}, tests::{check_edit, check_edit_with_config, TEST_CONFIG},
}; };
fn check(ra_fixture: &str, expect: Expect) { fn check(ra_fixture: &str, expect: Expect) {
let config = TEST_CONFIG; let config = TEST_CONFIG;
let (db, position) = crate::tests::position(ra_fixture); 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(); let mut acc = crate::completions::Completions::default();
if let IdentContext::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) =
&ctx.ident_ctx &analysis
{ {
crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); 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 { match &name_ref_ctx.kind {
NameRefKind::Path(path) => { NameRefKind::Path(path) => {
crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path);