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 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<hir::Name>,
) {
@ -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<hir::Name>,
local_name: Option<hir::Name>,
@ -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<hir::Name>,
) {
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<hir::ModPath>,
local_name: Option<hir::Name>,
) {
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<hir::Name>,
) {
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<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(
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),
_ => (),

View file

@ -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);

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),
);
}
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,
)
});
}

View file

@ -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 `<Ty as Trait>::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));
}

View file

@ -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![::]) {

View file

@ -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),
_ => (),

View file

@ -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);
}
});

View file

@ -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);
}
});
}

View file

@ -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<ast::NameRef>,
) {
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));
}
_ => {}
}

View file

@ -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<Type>,
// We might wanna split these out of CompletionContext
pub(super) ident_ctx: IdentContext,
pub(super) qualifier_ctx: QualifierCtx,
pub(super) locals: FxHashMap<Name, Local>,
@ -461,7 +459,7 @@ impl<'a> CompletionContext<'a> {
db: &'a RootDatabase,
position @ FilePosition { file_id, offset }: FilePosition,
config: &'a CompletionConfig,
) -> Option<CompletionContext<'a>> {
) -> 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))
}
}

View file

@ -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<CompletionAnalysis> {
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<CompletionAnalysis> {
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,11 +358,12 @@ impl<'a> CompletionContext<'a> {
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
Some(it) => it,
None => {
let analysis =
if let Some(original) = ast::String::cast(self.original_token.clone()) {
self.ident_ctx = IdentContext::String {
CompletionAnalysis::String {
original,
expanded: ast::String::cast(self.token.clone()),
};
}
} else {
// Fix up trailing whitespace problem
// #[attr(foo = $0
@ -375,42 +375,37 @@ impl<'a> CompletionContext<'a> {
{
let colon_prefix = previous_non_trivia_token(self.token.clone())
.map_or(false, |it| T![:] == it.kind());
self.ident_ctx = IdentContext::UnexpandedAttrTT {
CompletionAnalysis::UnexpandedAttrTT {
fake_attribute_under_caret: syntax_element
.ancestors()
.find_map(ast::Attr::cast),
colon_prefix,
};
}
} else {
return None;
}
}
return Some(());
};
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(

View file

@ -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

View file

@ -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 {

View file

@ -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<char>,
) -> Option<Completions> {
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<Item = (String, String)>,
) -> Option<Vec<TextEdit>> {
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,

View file

@ -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<Builder> {
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<LocatedImport>,
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<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_(
@ -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));

View file

@ -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<hir::Name>,
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<hir::Name>,
local_name: Option<hir::Name>,
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<hir::Name>,
func: hir::Function,
func_kind: FuncKind,
params: Option<(Option<hir::SelfParam>, Vec<hir::Param>)>,
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,12 +124,10 @@ 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) {
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<hir::SelfParam>, Vec<hir::Param>)> {
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?

View file

@ -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<hir::Name>,
variant: hir::Variant,
path: Option<hir::ModPath>,
@ -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<hir::ModPath>,
local_name: Option<hir::Name>,
@ -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<hir::ModPath>,
) -> Option<Builder> {
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,
};

View file

@ -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);

View file

@ -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<Name>,
) -> 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 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<Name>,
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,48 +88,27 @@ fn build_completion(
fn render_pat(
ctx: &RenderContext<'_>,
pattern_ctx: &PatternContext,
name: &str,
kind: StructKind,
fields: &[hir::Field],
fields_omitted: bool,
) -> 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 {
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 {
let needs_ascription = matches!(
pattern_ctx,
PatternContext {
param_ctx: Some((.., ParamKind::Function(_))),
has_type_ascription: false,
..
})
}
);
if needs_ascription {
pat.push(':');

View file

@ -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);