mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge #11726
11726: internal: More ide_completion refactoring r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
641c6e5b32
17 changed files with 498 additions and 376 deletions
|
@ -69,24 +69,17 @@ pub(crate) fn inlay_hints(
|
||||||
|
|
||||||
let mut hints = Vec::new();
|
let mut hints = Vec::new();
|
||||||
|
|
||||||
if let Some(range_limit) = range_limit {
|
let get_hints = |node| get_hints(&mut hints, &sema, config, node);
|
||||||
let range_limit = range_limit.range;
|
match range_limit {
|
||||||
match file.covering_element(range_limit) {
|
Some(FileRange { range, .. }) => match file.covering_element(range) {
|
||||||
NodeOrToken::Token(_) => return hints,
|
NodeOrToken::Token(_) => return hints,
|
||||||
NodeOrToken::Node(n) => {
|
NodeOrToken::Node(n) => n
|
||||||
for node in n
|
.descendants()
|
||||||
.descendants()
|
.filter(|descendant| range.contains_range(descendant.text_range()))
|
||||||
.filter(|descendant| range_limit.contains_range(descendant.text_range()))
|
.for_each(get_hints),
|
||||||
{
|
},
|
||||||
get_hints(&mut hints, &sema, config, node);
|
None => file.descendants().for_each(get_hints),
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for node in file.descendants() {
|
|
||||||
get_hints(&mut hints, &sema, config, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hints
|
hints
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,11 @@ use crate::{
|
||||||
item::Builder,
|
item::Builder,
|
||||||
render::{
|
render::{
|
||||||
const_::render_const,
|
const_::render_const,
|
||||||
enum_variant::render_variant,
|
|
||||||
function::{render_fn, render_method},
|
function::{render_fn, render_method},
|
||||||
|
literal::{render_struct_literal, render_variant_lit},
|
||||||
|
macro_::render_macro,
|
||||||
pattern::{render_struct_pat, render_variant_pat},
|
pattern::{render_struct_pat, render_variant_pat},
|
||||||
render_field, render_resolution, render_tuple_field,
|
render_field, render_resolution, render_resolution_simple, render_tuple_field,
|
||||||
struct_literal::render_struct_literal,
|
|
||||||
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,
|
||||||
|
@ -124,7 +124,37 @@ 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, false), local_name, resolution));
|
self.add(render_resolution(RenderContext::new(ctx), local_name, resolution));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_resolution_simple(
|
||||||
|
&mut self,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
local_name: hir::Name,
|
||||||
|
resolution: hir::ScopeDef,
|
||||||
|
) {
|
||||||
|
if ctx.is_scope_def_hidden(resolution) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.add(render_resolution_simple(RenderContext::new(ctx), local_name, resolution));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_macro(
|
||||||
|
&mut self,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
mac: hir::Macro,
|
||||||
|
local_name: hir::Name,
|
||||||
|
) {
|
||||||
|
let is_private_editable = match ctx.is_visible(&mac) {
|
||||||
|
Visible::Yes => false,
|
||||||
|
Visible::Editable => true,
|
||||||
|
Visible::No => return,
|
||||||
|
};
|
||||||
|
self.add(render_macro(
|
||||||
|
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||||
|
local_name,
|
||||||
|
mac,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_function(
|
pub(crate) fn add_function(
|
||||||
|
@ -138,7 +168,11 @@ impl Completions {
|
||||||
Visible::Editable => true,
|
Visible::Editable => true,
|
||||||
Visible::No => return,
|
Visible::No => return,
|
||||||
};
|
};
|
||||||
self.add(render_fn(RenderContext::new(ctx, is_private_editable), None, local_name, func));
|
self.add(render_fn(
|
||||||
|
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||||
|
local_name,
|
||||||
|
func,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_method(
|
pub(crate) fn add_method(
|
||||||
|
@ -154,8 +188,7 @@ impl Completions {
|
||||||
Visible::No => return,
|
Visible::No => return,
|
||||||
};
|
};
|
||||||
self.add(render_method(
|
self.add(render_method(
|
||||||
RenderContext::new(ctx, is_private_editable),
|
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||||
None,
|
|
||||||
receiver,
|
receiver,
|
||||||
local_name,
|
local_name,
|
||||||
func,
|
func,
|
||||||
|
@ -168,7 +201,10 @@ impl Completions {
|
||||||
Visible::Editable => true,
|
Visible::Editable => true,
|
||||||
Visible::No => return,
|
Visible::No => return,
|
||||||
};
|
};
|
||||||
self.add_opt(render_const(RenderContext::new(ctx, is_private_editable), konst));
|
self.add_opt(render_const(
|
||||||
|
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||||
|
konst,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
|
pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
|
||||||
|
@ -177,7 +213,10 @@ impl Completions {
|
||||||
Visible::Editable => true,
|
Visible::Editable => true,
|
||||||
Visible::No => return,
|
Visible::No => return,
|
||||||
};
|
};
|
||||||
self.add_opt(render_type_alias(RenderContext::new(ctx, is_private_editable), type_alias));
|
self.add_opt(render_type_alias(
|
||||||
|
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||||
|
type_alias,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_type_alias_with_eq(
|
pub(crate) fn add_type_alias_with_eq(
|
||||||
|
@ -185,7 +224,7 @@ impl Completions {
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
type_alias: hir::TypeAlias,
|
type_alias: hir::TypeAlias,
|
||||||
) {
|
) {
|
||||||
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx, false), type_alias));
|
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_qualified_enum_variant(
|
pub(crate) fn add_qualified_enum_variant(
|
||||||
|
@ -194,8 +233,7 @@ impl Completions {
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
path: hir::ModPath,
|
path: hir::ModPath,
|
||||||
) {
|
) {
|
||||||
let item = render_variant(RenderContext::new(ctx, false), None, None, variant, Some(path));
|
self.add_opt(render_variant_lit(RenderContext::new(ctx), None, variant, Some(path)));
|
||||||
self.add(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_enum_variant(
|
pub(crate) fn add_enum_variant(
|
||||||
|
@ -204,8 +242,7 @@ impl Completions {
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
) {
|
) {
|
||||||
let item = render_variant(RenderContext::new(ctx, false), None, local_name, variant, None);
|
self.add_opt(render_variant_lit(RenderContext::new(ctx), local_name, variant, None));
|
||||||
self.add(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_field(
|
pub(crate) fn add_field(
|
||||||
|
@ -220,7 +257,12 @@ impl Completions {
|
||||||
Visible::Editable => true,
|
Visible::Editable => true,
|
||||||
Visible::No => return,
|
Visible::No => return,
|
||||||
};
|
};
|
||||||
let item = render_field(RenderContext::new(ctx, is_private_editable), receiver, field, ty);
|
let item = render_field(
|
||||||
|
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||||
|
receiver,
|
||||||
|
field,
|
||||||
|
ty,
|
||||||
|
);
|
||||||
self.add(item);
|
self.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +273,7 @@ impl Completions {
|
||||||
path: Option<hir::ModPath>,
|
path: Option<hir::ModPath>,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
) {
|
) {
|
||||||
let item = render_struct_literal(RenderContext::new(ctx, false), strukt, path, local_name);
|
let item = render_struct_literal(RenderContext::new(ctx), strukt, path, local_name);
|
||||||
self.add_opt(item);
|
self.add_opt(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +284,7 @@ impl Completions {
|
||||||
path: Option<hir::ModPath>,
|
path: Option<hir::ModPath>,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
) {
|
) {
|
||||||
let item = render_union_literal(RenderContext::new(ctx, false), un, path, local_name);
|
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
|
||||||
self.add_opt(item);
|
self.add_opt(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +295,7 @@ impl Completions {
|
||||||
field: usize,
|
field: usize,
|
||||||
ty: &hir::Type,
|
ty: &hir::Type,
|
||||||
) {
|
) {
|
||||||
let item = render_tuple_field(RenderContext::new(ctx, false), receiver, field, ty);
|
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
|
||||||
self.add(item);
|
self.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +314,12 @@ impl Completions {
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
) {
|
) {
|
||||||
self.add_opt(render_variant_pat(RenderContext::new(ctx, false), variant, local_name, None));
|
self.add_opt(render_variant_pat(
|
||||||
|
RenderContext::new(ctx),
|
||||||
|
variant,
|
||||||
|
local_name.clone(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_qualified_variant_pat(
|
pub(crate) fn add_qualified_variant_pat(
|
||||||
|
@ -281,7 +328,8 @@ impl Completions {
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
path: hir::ModPath,
|
path: hir::ModPath,
|
||||||
) {
|
) {
|
||||||
self.add_opt(render_variant_pat(RenderContext::new(ctx, false), variant, None, Some(path)));
|
let path = Some(&path);
|
||||||
|
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_struct_pat(
|
pub(crate) fn add_struct_pat(
|
||||||
|
@ -290,7 +338,7 @@ impl Completions {
|
||||||
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, false), strukt, local_name));
|
self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
})
|
})
|
||||||
.filter_map(|import| {
|
.filter_map(|import| {
|
||||||
render_resolution_with_import(
|
render_resolution_with_import(
|
||||||
RenderContext::new(ctx, false),
|
RenderContext::new(ctx),
|
||||||
ImportEdit { import, scope: import_scope.clone() },
|
ImportEdit { import, scope: import_scope.clone() },
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -54,8 +54,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
{
|
{
|
||||||
if refutable || single_variant_enum(e) {
|
if refutable || single_variant_enum(e) {
|
||||||
super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
|
super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
|
||||||
acc.add_qualified_variant_pat(ctx, variant, path.clone());
|
acc.add_qualified_variant_pat(ctx, variant, path);
|
||||||
acc.add_qualified_enum_variant(ctx, variant, path);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +62,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
// FIXME: ideally, we should look at the type we are matching against and
|
// FIXME: ideally, we should look at the type we are matching against and
|
||||||
// suggest variants + auto-imports
|
// suggest variants + auto-imports
|
||||||
ctx.process_all_names(&mut |name, res| {
|
ctx.process_all_names(&mut |name, res| {
|
||||||
let add_resolution = 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, strukt, Some(name.clone()));
|
||||||
|
@ -76,8 +75,11 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
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(..) | hir::ModuleDef::Module(..) => refutable,
|
hir::ModuleDef::Const(..) => refutable,
|
||||||
hir::ModuleDef::Macro(mac) => mac.is_fn_like(ctx.db),
|
hir::ModuleDef::Module(..) => true,
|
||||||
|
hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => {
|
||||||
|
return acc.add_macro(ctx, 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() {
|
||||||
|
@ -85,13 +87,19 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
acc.add_struct_pat(ctx, strukt, Some(name.clone()));
|
acc.add_struct_pat(ctx, strukt, Some(name.clone()));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Some(hir::Adt::Enum(_)) => refutable,
|
Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
|
||||||
_ => true,
|
Some(hir::Adt::Union(_)) => true,
|
||||||
|
_ => false,
|
||||||
},
|
},
|
||||||
_ => false,
|
ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => true,
|
||||||
|
ScopeDef::GenericParam(_)
|
||||||
|
| ScopeDef::AdtSelfType(_)
|
||||||
|
| ScopeDef::Local(_)
|
||||||
|
| ScopeDef::Label(_)
|
||||||
|
| ScopeDef::Unknown => false,
|
||||||
};
|
};
|
||||||
if add_resolution {
|
if add_simple_path {
|
||||||
acc.add_resolution(ctx, name, res);
|
acc.add_resolution_simple(ctx, name, res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,13 +84,16 @@ pub(crate) fn complete_record_literal(
|
||||||
match ctx.expected_type.as_ref()?.as_adt()? {
|
match ctx.expected_type.as_ref()?.as_adt()? {
|
||||||
hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
|
hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
|
||||||
let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
|
let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
|
||||||
let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
|
let path = module
|
||||||
|
.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, strukt, path, None);
|
||||||
}
|
}
|
||||||
hir::Adt::Union(un) if ctx.path_qual().is_none() => {
|
hir::Adt::Union(un) if ctx.path_qual().is_none() => {
|
||||||
let module = if let Some(module) = ctx.module { module } else { un.module(ctx.db) };
|
let module = if let Some(module) = ctx.module { module } else { un.module(ctx.db) };
|
||||||
let path = module.find_use_path(ctx.db, hir::ModuleDef::from(un));
|
let path =
|
||||||
|
module.find_use_path(ctx.db, hir::ModuleDef::from(un)).filter(|it| it.len() > 1);
|
||||||
|
|
||||||
acc.add_union_literal(ctx, un, path, None);
|
acc.add_union_literal(ctx, un, path, None);
|
||||||
}
|
}
|
||||||
|
@ -132,7 +135,7 @@ fn baz() {
|
||||||
#[test]
|
#[test]
|
||||||
fn literal_struct_completion_from_sub_modules() {
|
fn literal_struct_completion_from_sub_modules() {
|
||||||
check_edit(
|
check_edit(
|
||||||
"Struct {…}",
|
"submod::Struct {…}",
|
||||||
r#"
|
r#"
|
||||||
mod submod {
|
mod submod {
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
|
|
|
@ -58,7 +58,7 @@ pub(super) enum PathKind {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct PathCompletionCtx {
|
pub(crate) struct PathCompletionCtx {
|
||||||
/// If this is a call with () already there
|
/// If this is a call with () already there (or {} in case of record patterns)
|
||||||
pub(super) has_call_parens: bool,
|
pub(super) has_call_parens: bool,
|
||||||
/// Whether this path stars with a `::`.
|
/// Whether this path stars with a `::`.
|
||||||
pub(super) is_absolute_path: bool,
|
pub(super) is_absolute_path: bool,
|
||||||
|
@ -890,6 +890,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
Some(PathKind::Pat)
|
Some(PathKind::Pat)
|
||||||
},
|
},
|
||||||
ast::RecordPat(it) => {
|
ast::RecordPat(it) => {
|
||||||
|
path_ctx.has_call_parens = true;
|
||||||
pat_ctx = Some(pattern_context_for(original_file, it.into()));
|
pat_ctx = Some(pattern_context_for(original_file, it.into()));
|
||||||
Some(PathKind::Pat)
|
Some(PathKind::Pat)
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,13 +3,12 @@
|
||||||
|
|
||||||
pub(crate) mod macro_;
|
pub(crate) mod macro_;
|
||||||
pub(crate) mod function;
|
pub(crate) mod function;
|
||||||
pub(crate) mod enum_variant;
|
|
||||||
pub(crate) mod const_;
|
pub(crate) mod const_;
|
||||||
pub(crate) mod pattern;
|
pub(crate) mod pattern;
|
||||||
pub(crate) mod type_alias;
|
pub(crate) mod type_alias;
|
||||||
pub(crate) mod struct_literal;
|
pub(crate) mod variant;
|
||||||
pub(crate) mod compound;
|
|
||||||
pub(crate) mod union_literal;
|
pub(crate) mod union_literal;
|
||||||
|
pub(crate) mod literal;
|
||||||
|
|
||||||
use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
|
use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
|
||||||
use ide_db::{helpers::item_name, RootDatabase, SnippetCap, SymbolKind};
|
use ide_db::{helpers::item_name, RootDatabase, SnippetCap, SymbolKind};
|
||||||
|
@ -18,22 +17,30 @@ use syntax::{SmolStr, SyntaxKind, TextRange};
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PathCompletionCtx, PathKind},
|
context::{PathCompletionCtx, PathKind},
|
||||||
item::{CompletionRelevanceTypeMatch, ImportEdit},
|
item::{CompletionRelevanceTypeMatch, ImportEdit},
|
||||||
render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
|
render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
|
||||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
||||||
};
|
};
|
||||||
/// Interface for data and methods required for items rendering.
|
/// Interface for data and methods required for items rendering.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct RenderContext<'a> {
|
pub(crate) struct RenderContext<'a> {
|
||||||
completion: &'a CompletionContext<'a>,
|
completion: &'a CompletionContext<'a>,
|
||||||
is_private_editable: bool,
|
is_private_editable: bool,
|
||||||
|
import_to_add: Option<ImportEdit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RenderContext<'a> {
|
impl<'a> RenderContext<'a> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
|
||||||
completion: &'a CompletionContext<'a>,
|
RenderContext { completion, is_private_editable: false, import_to_add: None }
|
||||||
is_private_editable: bool,
|
}
|
||||||
) -> RenderContext<'a> {
|
|
||||||
RenderContext { completion, is_private_editable }
|
pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {
|
||||||
|
self.is_private_editable = private_editable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn import_to_add(mut self, import_to_add: Option<ImportEdit>) -> Self {
|
||||||
|
self.import_to_add = import_to_add;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snippet_cap(&self) -> Option<SnippetCap> {
|
fn snippet_cap(&self) -> Option<SnippetCap> {
|
||||||
|
@ -139,6 +146,14 @@ pub(crate) fn render_resolution(
|
||||||
render_resolution_(ctx, local_name, None, resolution)
|
render_resolution_(ctx, local_name, None, resolution)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn render_resolution_simple(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
local_name: hir::Name,
|
||||||
|
resolution: ScopeDef,
|
||||||
|
) -> CompletionItem {
|
||||||
|
render_resolution_simple_(ctx, local_name, None, resolution)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn render_resolution_with_import(
|
pub(crate) fn render_resolution_with_import(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
import_edit: ImportEdit,
|
import_edit: ImportEdit,
|
||||||
|
@ -162,31 +177,42 @@ fn render_resolution_(
|
||||||
let _p = profile::span("render_resolution");
|
let _p = profile::span("render_resolution");
|
||||||
use hir::ModuleDef::*;
|
use hir::ModuleDef::*;
|
||||||
|
|
||||||
let db = ctx.db();
|
match resolution {
|
||||||
|
ScopeDef::ModuleDef(Macro(mac)) => {
|
||||||
let kind = match resolution {
|
let ctx = ctx.import_to_add(import_to_add);
|
||||||
|
return render_macro(ctx, local_name, mac);
|
||||||
|
}
|
||||||
ScopeDef::ModuleDef(Function(func)) => {
|
ScopeDef::ModuleDef(Function(func)) => {
|
||||||
return render_fn(ctx, import_to_add, Some(local_name), func);
|
let ctx = ctx.import_to_add(import_to_add);
|
||||||
|
return render_fn(ctx, Some(local_name), func);
|
||||||
}
|
}
|
||||||
ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
|
ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
|
||||||
return render_variant(ctx, import_to_add, Some(local_name), var, None);
|
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) {
|
||||||
ScopeDef::ModuleDef(Macro(mac)) => {
|
return item;
|
||||||
return render_macro(ctx, import_to_add, local_name, mac)
|
|
||||||
}
|
|
||||||
ScopeDef::Unknown => {
|
|
||||||
let mut item = CompletionItem::new(
|
|
||||||
CompletionItemKind::UnresolvedReference,
|
|
||||||
ctx.source_range(),
|
|
||||||
local_name.to_smol_str(),
|
|
||||||
);
|
|
||||||
if let Some(import_to_add) = import_to_add {
|
|
||||||
item.add_import(import_to_add);
|
|
||||||
}
|
}
|
||||||
return item.build();
|
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
render_resolution_simple_(ctx, local_name, import_to_add, resolution)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_resolution_simple_(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
local_name: hir::Name,
|
||||||
|
import_to_add: Option<ImportEdit>,
|
||||||
|
resolution: ScopeDef,
|
||||||
|
) -> CompletionItem {
|
||||||
|
let _p = profile::span("render_resolution");
|
||||||
|
use hir::ModuleDef::*;
|
||||||
|
|
||||||
|
let db = ctx.db();
|
||||||
|
let ctx = ctx.import_to_add(import_to_add);
|
||||||
|
let kind = match resolution {
|
||||||
|
ScopeDef::Unknown => CompletionItemKind::UnresolvedReference,
|
||||||
|
ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function),
|
||||||
ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
|
ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
|
||||||
|
ScopeDef::ModuleDef(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro),
|
||||||
ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
|
ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
|
||||||
ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
|
ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
|
||||||
hir::Adt::Struct(_) => SymbolKind::Struct,
|
hir::Adt::Struct(_) => SymbolKind::Struct,
|
||||||
|
@ -253,7 +279,7 @@ fn render_resolution_(
|
||||||
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));
|
||||||
|
|
||||||
if let Some(import_to_add) = import_to_add {
|
if let Some(import_to_add) = ctx.import_to_add {
|
||||||
item.add_import(import_to_add);
|
item.add_import(import_to_add);
|
||||||
}
|
}
|
||||||
item.build()
|
item.build()
|
||||||
|
@ -577,7 +603,7 @@ fn main() { let _: m::Spam = S$0 }
|
||||||
kind: SymbolKind(
|
kind: SymbolKind(
|
||||||
Variant,
|
Variant,
|
||||||
),
|
),
|
||||||
lookup: "Spam::Bar",
|
lookup: "Spam::Bar(…)",
|
||||||
detail: "m::Spam::Bar(i32)",
|
detail: "m::Spam::Bar(i32)",
|
||||||
relevance: CompletionRelevance {
|
relevance: CompletionRelevance {
|
||||||
exact_name_match: false,
|
exact_name_match: false,
|
||||||
|
@ -1156,6 +1182,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
lc s [type+name+local]
|
lc s [type+name+local]
|
||||||
|
st S [type]
|
||||||
st S []
|
st S []
|
||||||
fn main() []
|
fn main() []
|
||||||
fn foo(…) []
|
fn foo(…) []
|
||||||
|
@ -1172,6 +1199,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
lc ssss [type+local]
|
lc ssss [type+local]
|
||||||
|
st S [type]
|
||||||
st S []
|
st S []
|
||||||
fn main() []
|
fn main() []
|
||||||
fn foo(…) []
|
fn foo(…) []
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
//! Renderer for `enum` variants.
|
|
||||||
|
|
||||||
use hir::{HasAttrs, StructKind};
|
|
||||||
use ide_db::SymbolKind;
|
|
||||||
use syntax::SmolStr;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
item::{CompletionItem, ImportEdit},
|
|
||||||
render::{
|
|
||||||
compound::{format_literal_label, render_record, render_tuple, RenderedCompound},
|
|
||||||
compute_ref_match, compute_type_match, RenderContext,
|
|
||||||
},
|
|
||||||
CompletionRelevance,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn render_variant(
|
|
||||||
ctx: RenderContext<'_>,
|
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
local_name: Option<hir::Name>,
|
|
||||||
variant: hir::Variant,
|
|
||||||
path: Option<hir::ModPath>,
|
|
||||||
) -> CompletionItem {
|
|
||||||
let _p = profile::span("render_enum_variant");
|
|
||||||
render(ctx, local_name, variant, path, import_to_add)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(
|
|
||||||
ctx @ RenderContext { completion, .. }: RenderContext<'_>,
|
|
||||||
local_name: Option<hir::Name>,
|
|
||||||
variant: hir::Variant,
|
|
||||||
path: Option<hir::ModPath>,
|
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
) -> CompletionItem {
|
|
||||||
let db = completion.db;
|
|
||||||
let name = local_name.unwrap_or_else(|| variant.name(db));
|
|
||||||
let variant_kind = variant.kind(db);
|
|
||||||
|
|
||||||
let (qualified_name, short_qualified_name, qualified) = match path {
|
|
||||||
Some(path) => {
|
|
||||||
let short = hir::ModPath::from_segments(
|
|
||||||
hir::PathKind::Plain,
|
|
||||||
path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
|
|
||||||
);
|
|
||||||
(path, short, true)
|
|
||||||
}
|
|
||||||
None => (name.clone().into(), name.into(), false),
|
|
||||||
};
|
|
||||||
let qualified_name = qualified_name.to_string();
|
|
||||||
let short_qualified_name: SmolStr = short_qualified_name.to_string().into();
|
|
||||||
|
|
||||||
let mut rendered = match variant_kind {
|
|
||||||
StructKind::Tuple => {
|
|
||||||
render_tuple(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name))
|
|
||||||
}
|
|
||||||
StructKind::Record => {
|
|
||||||
render_record(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name))
|
|
||||||
}
|
|
||||||
StructKind::Unit => {
|
|
||||||
RenderedCompound { literal: qualified_name.clone(), detail: qualified_name.clone() }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if ctx.snippet_cap().is_some() {
|
|
||||||
rendered.literal.push_str("$0");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut item = CompletionItem::new(
|
|
||||||
SymbolKind::Variant,
|
|
||||||
ctx.source_range(),
|
|
||||||
format_literal_label(&qualified_name, variant_kind),
|
|
||||||
);
|
|
||||||
|
|
||||||
item.set_documentation(variant.docs(db))
|
|
||||||
.set_deprecated(ctx.is_deprecated(variant))
|
|
||||||
.detail(rendered.detail);
|
|
||||||
|
|
||||||
match ctx.snippet_cap() {
|
|
||||||
Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
|
|
||||||
None => item.insert_text(rendered.literal),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(import_to_add) = import_to_add {
|
|
||||||
item.add_import(import_to_add);
|
|
||||||
}
|
|
||||||
|
|
||||||
if qualified {
|
|
||||||
item.lookup_by(short_qualified_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ty = variant.parent_enum(completion.db).ty(completion.db);
|
|
||||||
item.set_relevance(CompletionRelevance {
|
|
||||||
type_match: compute_type_match(completion, &ty),
|
|
||||||
..ctx.completion_relevance()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(ref_match) = compute_ref_match(completion, &ty) {
|
|
||||||
item.ref_match(ref_match);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.build()
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{CompletionContext, PathCompletionCtx, PathKind},
|
context::{CompletionContext, PathCompletionCtx, PathKind},
|
||||||
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
|
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
|
||||||
patterns::ImmediateLocation,
|
patterns::ImmediateLocation,
|
||||||
render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
|
render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
|
||||||
};
|
};
|
||||||
|
@ -20,23 +20,21 @@ enum FuncKind {
|
||||||
|
|
||||||
pub(crate) fn render_fn(
|
pub(crate) fn render_fn(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
let _p = profile::span("render_fn");
|
let _p = profile::span("render_fn");
|
||||||
render(ctx, local_name, func, FuncKind::Function, import_to_add)
|
render(ctx, local_name, func, FuncKind::Function)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render_method(
|
pub(crate) fn render_method(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<hir::Name>,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
let _p = profile::span("render_method");
|
let _p = profile::span("render_method");
|
||||||
render(ctx, local_name, func, FuncKind::Method(receiver), import_to_add)
|
render(ctx, local_name, func, FuncKind::Method(receiver))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
|
@ -44,7 +42,6 @@ fn render(
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
func_kind: FuncKind,
|
func_kind: FuncKind,
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
let db = completion.db;
|
let db = completion.db;
|
||||||
|
|
||||||
|
@ -98,17 +95,18 @@ fn render(
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if import_to_add.is_none() {
|
match ctx.import_to_add {
|
||||||
if let Some(actm) = func.as_assoc_item(db) {
|
Some(import_to_add) => {
|
||||||
if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
|
item.add_import(import_to_add);
|
||||||
item.trait_name(trt.name(db).to_smol_str());
|
}
|
||||||
|
None => {
|
||||||
|
if let Some(actm) = func.as_assoc_item(db) {
|
||||||
|
if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
|
||||||
|
item.trait_name(trt.name(db).to_smol_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(import_to_add) = import_to_add {
|
|
||||||
item.add_import(import_to_add);
|
|
||||||
}
|
|
||||||
item.build()
|
item.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +190,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
|
||||||
Some(PathCompletionCtx { kind: Some(PathKind::Expr), has_call_parens: true, .. }) => {
|
Some(PathCompletionCtx { kind: Some(PathKind::Expr), has_call_parens: true, .. }) => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
Some(PathCompletionCtx { kind: Some(PathKind::Use), .. }) => {
|
Some(PathCompletionCtx { kind: Some(PathKind::Use | PathKind::Type), .. }) => {
|
||||||
cov_mark::hit!(no_parens_in_use_item);
|
cov_mark::hit!(no_parens_in_use_item);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
174
crates/ide_completion/src/render/literal.rs
Normal file
174
crates/ide_completion/src/render/literal.rs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
//! Renderer for `enum` variants.
|
||||||
|
|
||||||
|
use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
|
||||||
|
use ide_db::SymbolKind;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
context::{CompletionContext, PathCompletionCtx},
|
||||||
|
item::CompletionItem,
|
||||||
|
render::{
|
||||||
|
compute_ref_match, compute_type_match,
|
||||||
|
variant::{
|
||||||
|
format_literal_label, render_record_lit, render_tuple_lit, visible_fields,
|
||||||
|
RenderedLiteral,
|
||||||
|
},
|
||||||
|
RenderContext,
|
||||||
|
},
|
||||||
|
CompletionItemKind, CompletionRelevance,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) fn render_variant_lit(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
local_name: Option<hir::Name>,
|
||||||
|
variant: hir::Variant,
|
||||||
|
path: Option<hir::ModPath>,
|
||||||
|
) -> Option<CompletionItem> {
|
||||||
|
let _p = profile::span("render_enum_variant");
|
||||||
|
let db = ctx.db();
|
||||||
|
|
||||||
|
let name = local_name.unwrap_or_else(|| variant.name(db));
|
||||||
|
render(ctx, Variant::EnumVariant(variant), name, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn render_struct_literal(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
strukt: hir::Struct,
|
||||||
|
path: Option<hir::ModPath>,
|
||||||
|
local_name: Option<hir::Name>,
|
||||||
|
) -> Option<CompletionItem> {
|
||||||
|
let _p = profile::span("render_struct_literal");
|
||||||
|
let db = ctx.db();
|
||||||
|
|
||||||
|
let name = local_name.unwrap_or_else(|| strukt.name(db));
|
||||||
|
render(ctx, Variant::Struct(strukt), name, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
ctx @ RenderContext { completion, .. }: RenderContext<'_>,
|
||||||
|
thing: Variant,
|
||||||
|
name: hir::Name,
|
||||||
|
path: Option<hir::ModPath>,
|
||||||
|
) -> Option<CompletionItem> {
|
||||||
|
if let Some(PathCompletionCtx { has_call_parens: true, .. }) = completion.path_context {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let db = completion.db;
|
||||||
|
let fields = thing.fields(completion)?;
|
||||||
|
|
||||||
|
let (qualified_name, short_qualified_name, qualified) = match path {
|
||||||
|
Some(path) => {
|
||||||
|
let short = hir::ModPath::from_segments(
|
||||||
|
hir::PathKind::Plain,
|
||||||
|
path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
|
||||||
|
);
|
||||||
|
(path, short, true)
|
||||||
|
}
|
||||||
|
None => (name.clone().into(), name.into(), false),
|
||||||
|
};
|
||||||
|
let qualified_name = qualified_name.to_string();
|
||||||
|
let snippet_cap = ctx.snippet_cap();
|
||||||
|
|
||||||
|
let kind = thing.kind(db);
|
||||||
|
let mut rendered = match kind {
|
||||||
|
StructKind::Tuple => render_tuple_lit(db, snippet_cap, &fields, &qualified_name),
|
||||||
|
StructKind::Record => render_record_lit(db, snippet_cap, &fields, &qualified_name),
|
||||||
|
StructKind::Unit => {
|
||||||
|
RenderedLiteral { literal: qualified_name.clone(), detail: qualified_name.clone() }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if snippet_cap.is_some() {
|
||||||
|
rendered.literal.push_str("$0");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut item = CompletionItem::new(
|
||||||
|
CompletionItemKind::SymbolKind(thing.symbol_kind()),
|
||||||
|
ctx.source_range(),
|
||||||
|
format_literal_label(&qualified_name, kind),
|
||||||
|
);
|
||||||
|
|
||||||
|
item.detail(rendered.detail);
|
||||||
|
|
||||||
|
match snippet_cap {
|
||||||
|
Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
|
||||||
|
None => item.insert_text(rendered.literal),
|
||||||
|
};
|
||||||
|
|
||||||
|
if qualified {
|
||||||
|
item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind));
|
||||||
|
}
|
||||||
|
item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx));
|
||||||
|
|
||||||
|
let ty = thing.ty(db);
|
||||||
|
item.set_relevance(CompletionRelevance {
|
||||||
|
type_match: compute_type_match(ctx.completion, &ty),
|
||||||
|
..ctx.completion_relevance()
|
||||||
|
});
|
||||||
|
if let Some(ref_match) = compute_ref_match(completion, &ty) {
|
||||||
|
item.ref_match(ref_match);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(import_to_add) = ctx.import_to_add {
|
||||||
|
item.add_import(import_to_add);
|
||||||
|
}
|
||||||
|
Some(item.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Variant {
|
||||||
|
Struct(hir::Struct),
|
||||||
|
EnumVariant(hir::Variant),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Variant {
|
||||||
|
fn fields(self, ctx: &CompletionContext) -> Option<Vec<hir::Field>> {
|
||||||
|
let fields = match self {
|
||||||
|
Variant::Struct(it) => it.fields(ctx.db),
|
||||||
|
Variant::EnumVariant(it) => it.fields(ctx.db),
|
||||||
|
};
|
||||||
|
let (visible_fields, fields_omitted) = match self {
|
||||||
|
Variant::Struct(it) => visible_fields(ctx, &fields, it)?,
|
||||||
|
Variant::EnumVariant(it) => visible_fields(ctx, &fields, it)?,
|
||||||
|
};
|
||||||
|
if !fields_omitted {
|
||||||
|
Some(visible_fields)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind(self, db: &dyn HirDatabase) -> StructKind {
|
||||||
|
match self {
|
||||||
|
Variant::Struct(it) => it.kind(db),
|
||||||
|
Variant::EnumVariant(it) => it.kind(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_kind(self) -> SymbolKind {
|
||||||
|
match self {
|
||||||
|
Variant::Struct(_) => SymbolKind::Struct,
|
||||||
|
Variant::EnumVariant(_) => SymbolKind::Variant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
|
||||||
|
match self {
|
||||||
|
Variant::Struct(it) => it.docs(db),
|
||||||
|
Variant::EnumVariant(it) => it.docs(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
|
||||||
|
match self {
|
||||||
|
Variant::Struct(it) => ctx.is_deprecated(it),
|
||||||
|
Variant::EnumVariant(it) => ctx.is_deprecated(it),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ty(self, db: &dyn HirDatabase) -> hir::Type {
|
||||||
|
match self {
|
||||||
|
Variant::Struct(it) => it.ty(db),
|
||||||
|
Variant::EnumVariant(it) => it.parent_enum(db).ty(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,27 +4,21 @@ use hir::{Documentation, HirDisplay};
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{context::PathKind, item::CompletionItem, render::RenderContext};
|
||||||
context::PathKind,
|
|
||||||
item::{CompletionItem, ImportEdit},
|
|
||||||
render::RenderContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn render_macro(
|
pub(crate) fn render_macro(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
name: hir::Name,
|
name: hir::Name,
|
||||||
macro_: hir::Macro,
|
macro_: hir::Macro,
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
let _p = profile::span("render_macro");
|
let _p = profile::span("render_macro");
|
||||||
render(ctx, name, macro_, import_to_add)
|
render(ctx, name, macro_)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
ctx @ RenderContext { completion, .. }: RenderContext<'_>,
|
ctx @ RenderContext { completion, .. }: RenderContext<'_>,
|
||||||
name: hir::Name,
|
name: hir::Name,
|
||||||
macro_: hir::Macro,
|
macro_: hir::Macro,
|
||||||
import_to_add: Option<ImportEdit>,
|
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
let source_range = if completion.is_immediately_after_macro_bang() {
|
let source_range = if completion.is_immediately_after_macro_bang() {
|
||||||
cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
|
cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
|
||||||
|
@ -52,12 +46,7 @@ fn render(
|
||||||
.set_documentation(docs)
|
.set_documentation(docs)
|
||||||
.set_relevance(ctx.completion_relevance());
|
.set_relevance(ctx.completion_relevance());
|
||||||
|
|
||||||
if let Some(import_to_add) = import_to_add {
|
|
||||||
item.add_import(import_to_add);
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = &*name;
|
let name = &*name;
|
||||||
|
|
||||||
match ctx.snippet_cap() {
|
match ctx.snippet_cap() {
|
||||||
Some(cap) if needs_bang && !completion.path_is_call() => {
|
Some(cap) if needs_bang && !completion.path_is_call() => {
|
||||||
let snippet = format!("{}!{}$0{}", name, bra, ket);
|
let snippet = format!("{}!{}$0{}", name, bra, ket);
|
||||||
|
@ -73,6 +62,9 @@ fn render(
|
||||||
item.insert_text(name);
|
item.insert_text(name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if let Some(import_to_add) = ctx.import_to_add {
|
||||||
|
item.add_import(import_to_add);
|
||||||
|
}
|
||||||
|
|
||||||
item.build()
|
item.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
//! Renderer for patterns.
|
//! Renderer for patterns.
|
||||||
|
|
||||||
use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
|
use hir::{db::HirDatabase, HasAttrs, Name, StructKind};
|
||||||
use ide_db::SnippetCap;
|
use ide_db::SnippetCap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{ParamKind, PatternContext},
|
context::{ParamKind, PatternContext},
|
||||||
render::RenderContext,
|
render::{variant::visible_fields, RenderContext},
|
||||||
CompletionItem, CompletionItemKind,
|
CompletionItem, CompletionItemKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ pub(crate) fn render_struct_pat(
|
||||||
let _p = profile::span("render_struct_pat");
|
let _p = profile::span("render_struct_pat");
|
||||||
|
|
||||||
let fields = strukt.fields(ctx.db());
|
let fields = strukt.fields(ctx.db());
|
||||||
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
|
let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, strukt)?;
|
||||||
|
|
||||||
if visible_fields.is_empty() {
|
if visible_fields.is_empty() {
|
||||||
// Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
|
// Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
|
||||||
|
@ -36,14 +36,14 @@ pub(crate) fn render_variant_pat(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
local_name: Option<Name>,
|
local_name: Option<Name>,
|
||||||
path: Option<hir::ModPath>,
|
path: Option<&hir::ModPath>,
|
||||||
) -> Option<CompletionItem> {
|
) -> Option<CompletionItem> {
|
||||||
let _p = profile::span("render_variant_pat");
|
let _p = profile::span("render_variant_pat");
|
||||||
|
|
||||||
let fields = variant.fields(ctx.db());
|
let fields = variant.fields(ctx.db());
|
||||||
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
|
let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
|
||||||
|
|
||||||
let name = match &path {
|
let name = match path {
|
||||||
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(),
|
||||||
};
|
};
|
||||||
|
@ -78,9 +78,7 @@ fn render_pat(
|
||||||
fields_omitted: bool,
|
fields_omitted: bool,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let mut pat = match kind {
|
let mut pat = match kind {
|
||||||
StructKind::Tuple if ctx.snippet_cap().is_some() => {
|
StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
|
||||||
render_tuple_as_pat(fields, name, fields_omitted)
|
|
||||||
}
|
|
||||||
StructKind::Record => {
|
StructKind::Record => {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -113,49 +111,53 @@ fn render_record_as_pat(
|
||||||
fields_omitted: bool,
|
fields_omitted: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
let fields = fields.iter();
|
let fields = fields.iter();
|
||||||
if snippet_cap.is_some() {
|
match snippet_cap {
|
||||||
format!(
|
Some(_) => {
|
||||||
"{name} {{ {}{} }}",
|
format!(
|
||||||
fields
|
"{name} {{ {}{} }}",
|
||||||
.enumerate()
|
fields.enumerate().format_with(", ", |(idx, field), f| {
|
||||||
.map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
|
f(&format_args!("{}${}", field.name(db), idx + 1))
|
||||||
.format(", "),
|
}),
|
||||||
if fields_omitted { ", .." } else { "" },
|
if fields_omitted { ", .." } else { "" },
|
||||||
name = name
|
name = name
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
format!(
|
None => {
|
||||||
"{name} {{ {}{} }}",
|
format!(
|
||||||
fields.map(|field| field.name(db)).format(", "),
|
"{name} {{ {}{} }}",
|
||||||
if fields_omitted { ", .." } else { "" },
|
fields.map(|field| field.name(db)).format(", "),
|
||||||
name = name
|
if fields_omitted { ", .." } else { "" },
|
||||||
)
|
name = name
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
|
fn render_tuple_as_pat(
|
||||||
format!(
|
snippet_cap: Option<SnippetCap>,
|
||||||
"{name}({}{})",
|
|
||||||
fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
|
|
||||||
if fields_omitted { ", .." } else { "" },
|
|
||||||
name = name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visible_fields(
|
|
||||||
ctx: &RenderContext<'_>,
|
|
||||||
fields: &[hir::Field],
|
fields: &[hir::Field],
|
||||||
item: impl HasAttrs,
|
name: &str,
|
||||||
) -> Option<(Vec<hir::Field>, bool)> {
|
fields_omitted: bool,
|
||||||
let module = ctx.completion.module?;
|
) -> String {
|
||||||
let n_fields = fields.len();
|
let fields = fields.iter();
|
||||||
let fields = fields
|
match snippet_cap {
|
||||||
.iter()
|
Some(_) => {
|
||||||
.filter(|field| field.is_visible_from(ctx.db(), module))
|
format!(
|
||||||
.copied()
|
"{name}({}{})",
|
||||||
.collect::<Vec<_>>();
|
fields
|
||||||
|
.enumerate()
|
||||||
let fields_omitted =
|
.format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }),
|
||||||
n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
|
if fields_omitted { ", .." } else { "" },
|
||||||
Some((fields, fields_omitted))
|
name = name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
format!(
|
||||||
|
"{name}({}{})",
|
||||||
|
fields.enumerate().map(|(idx, _)| idx).format(", "),
|
||||||
|
if fields_omitted { ", .." } else { "" },
|
||||||
|
name = name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
//! Renderer for `struct` literal.
|
|
||||||
|
|
||||||
use hir::{HasAttrs, Name, StructKind};
|
|
||||||
use syntax::SmolStr;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
render::compound::{
|
|
||||||
format_literal_label, render_record, render_tuple, visible_fields, RenderedCompound,
|
|
||||||
},
|
|
||||||
render::RenderContext,
|
|
||||||
CompletionItem, CompletionItemKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn render_struct_literal(
|
|
||||||
ctx: RenderContext<'_>,
|
|
||||||
strukt: hir::Struct,
|
|
||||||
path: Option<hir::ModPath>,
|
|
||||||
local_name: Option<Name>,
|
|
||||||
) -> Option<CompletionItem> {
|
|
||||||
let _p = profile::span("render_struct_literal");
|
|
||||||
|
|
||||||
let fields = strukt.fields(ctx.db());
|
|
||||||
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
|
|
||||||
|
|
||||||
if fields_omitted {
|
|
||||||
// If some fields are private you can't make `struct` literal.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
|
|
||||||
|
|
||||||
let rendered = render_literal(&ctx, path, &name, strukt.kind(ctx.db()), &visible_fields)?;
|
|
||||||
|
|
||||||
Some(build_completion(&ctx, name, rendered, strukt.kind(ctx.db()), strukt))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_completion(
|
|
||||||
ctx: &RenderContext<'_>,
|
|
||||||
name: SmolStr,
|
|
||||||
rendered: RenderedCompound,
|
|
||||||
kind: StructKind,
|
|
||||||
def: impl HasAttrs + Copy,
|
|
||||||
) -> CompletionItem {
|
|
||||||
let mut item = CompletionItem::new(
|
|
||||||
CompletionItemKind::Snippet,
|
|
||||||
ctx.source_range(),
|
|
||||||
format_literal_label(&name, kind),
|
|
||||||
);
|
|
||||||
|
|
||||||
item.set_documentation(ctx.docs(def))
|
|
||||||
.set_deprecated(ctx.is_deprecated(def))
|
|
||||||
.detail(&rendered.detail)
|
|
||||||
.set_relevance(ctx.completion_relevance());
|
|
||||||
match ctx.snippet_cap() {
|
|
||||||
Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
|
|
||||||
None => item.insert_text(rendered.literal),
|
|
||||||
};
|
|
||||||
item.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_literal(
|
|
||||||
ctx: &RenderContext<'_>,
|
|
||||||
path: Option<hir::ModPath>,
|
|
||||||
name: &str,
|
|
||||||
kind: StructKind,
|
|
||||||
fields: &[hir::Field],
|
|
||||||
) -> Option<RenderedCompound> {
|
|
||||||
let path_string;
|
|
||||||
|
|
||||||
let qualified_name = if let Some(path) = path {
|
|
||||||
path_string = path.to_string();
|
|
||||||
&path_string
|
|
||||||
} else {
|
|
||||||
name
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rendered = match kind {
|
|
||||||
StructKind::Tuple if ctx.snippet_cap().is_some() => {
|
|
||||||
render_tuple(ctx.db(), ctx.snippet_cap(), fields, Some(qualified_name))
|
|
||||||
}
|
|
||||||
StructKind::Record => {
|
|
||||||
render_record(ctx.db(), ctx.snippet_cap(), fields, Some(qualified_name))
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if ctx.snippet_cap().is_some() {
|
|
||||||
rendered.literal.push_str("$0");
|
|
||||||
}
|
|
||||||
Some(rendered)
|
|
||||||
}
|
|
|
@ -1,11 +1,12 @@
|
||||||
//! Renderer for `union` literals.
|
//! Renderer for `union` literals.
|
||||||
|
|
||||||
use hir::{HirDisplay, Name, StructKind};
|
use hir::{HirDisplay, Name, StructKind};
|
||||||
|
use ide_db::SymbolKind;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{
|
render::{
|
||||||
compound::{format_literal_label, visible_fields},
|
variant::{format_literal_label, visible_fields},
|
||||||
RenderContext,
|
RenderContext,
|
||||||
},
|
},
|
||||||
CompletionItem, CompletionItemKind,
|
CompletionItem, CompletionItemKind,
|
||||||
|
@ -25,13 +26,13 @@ pub(crate) fn render_union_literal(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut item = CompletionItem::new(
|
let mut item = CompletionItem::new(
|
||||||
CompletionItemKind::Snippet,
|
CompletionItemKind::SymbolKind(SymbolKind::Union),
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
format_literal_label(&name, StructKind::Record),
|
format_literal_label(&name, StructKind::Record),
|
||||||
);
|
);
|
||||||
|
|
||||||
let fields = un.fields(ctx.db());
|
let fields = un.fields(ctx.db());
|
||||||
let (fields, fields_omitted) = visible_fields(&ctx, &fields, un)?;
|
let (fields, fields_omitted) = visible_fields(ctx.completion, &fields, un)?;
|
||||||
|
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Code common to structs, unions, and enum variants.
|
//! Code common to structs, unions, and enum variants.
|
||||||
|
|
||||||
use crate::render::RenderContext;
|
use crate::context::CompletionContext;
|
||||||
use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay, StructKind};
|
use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay, StructKind};
|
||||||
use ide_db::SnippetCap;
|
use ide_db::SnippetCap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -9,19 +9,19 @@ use syntax::SmolStr;
|
||||||
/// A rendered struct, union, or enum variant, split into fields for actual
|
/// A rendered struct, union, or enum variant, split into fields for actual
|
||||||
/// auto-completion (`literal`, using `field: ()`) and display in the
|
/// auto-completion (`literal`, using `field: ()`) and display in the
|
||||||
/// completions menu (`detail`, using `field: type`).
|
/// completions menu (`detail`, using `field: type`).
|
||||||
pub(crate) struct RenderedCompound {
|
pub(crate) struct RenderedLiteral {
|
||||||
pub(crate) literal: String,
|
pub(crate) literal: String,
|
||||||
pub(crate) detail: String,
|
pub(crate) detail: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
|
/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
|
||||||
/// the `name` argument for an anonymous type.
|
/// the `name` argument for an anonymous type.
|
||||||
pub(crate) fn render_record(
|
pub(crate) fn render_record_lit(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
snippet_cap: Option<SnippetCap>,
|
snippet_cap: Option<SnippetCap>,
|
||||||
fields: &[hir::Field],
|
fields: &[hir::Field],
|
||||||
name: Option<&str>,
|
path: &str,
|
||||||
) -> RenderedCompound {
|
) -> RenderedLiteral {
|
||||||
let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
|
let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
|
||||||
if snippet_cap.is_some() {
|
if snippet_cap.is_some() {
|
||||||
f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
|
f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
|
||||||
|
@ -34,20 +34,20 @@ pub(crate) fn render_record(
|
||||||
f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
|
f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
|
||||||
});
|
});
|
||||||
|
|
||||||
RenderedCompound {
|
RenderedLiteral {
|
||||||
literal: format!("{} {{ {} }}", name.unwrap_or(""), completions),
|
literal: format!("{} {{ {} }}", path, completions),
|
||||||
detail: format!("{} {{ {} }}", name.unwrap_or(""), types),
|
detail: format!("{} {{ {} }}", path, types),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
|
/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
|
||||||
/// the `name` argument for an anonymous type.
|
/// the `name` argument for an anonymous type.
|
||||||
pub(crate) fn render_tuple(
|
pub(crate) fn render_tuple_lit(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
snippet_cap: Option<SnippetCap>,
|
snippet_cap: Option<SnippetCap>,
|
||||||
fields: &[hir::Field],
|
fields: &[hir::Field],
|
||||||
name: Option<&str>,
|
path: &str,
|
||||||
) -> RenderedCompound {
|
) -> RenderedLiteral {
|
||||||
let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
|
let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
|
||||||
if snippet_cap.is_some() {
|
if snippet_cap.is_some() {
|
||||||
f(&format_args!("${{{}:()}}", idx + 1))
|
f(&format_args!("${{{}:()}}", idx + 1))
|
||||||
|
@ -58,9 +58,9 @@ pub(crate) fn render_tuple(
|
||||||
|
|
||||||
let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));
|
let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));
|
||||||
|
|
||||||
RenderedCompound {
|
RenderedLiteral {
|
||||||
literal: format!("{}({})", name.unwrap_or(""), completions),
|
literal: format!("{}({})", path, completions),
|
||||||
detail: format!("{}({})", name.unwrap_or(""), types),
|
detail: format!("{}({})", path, types),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,20 +68,20 @@ pub(crate) fn render_tuple(
|
||||||
/// fields, plus a boolean for whether the list is comprehensive (contains no
|
/// fields, plus a boolean for whether the list is comprehensive (contains no
|
||||||
/// private fields and its item is not marked `#[non_exhaustive]`).
|
/// private fields and its item is not marked `#[non_exhaustive]`).
|
||||||
pub(crate) fn visible_fields(
|
pub(crate) fn visible_fields(
|
||||||
ctx: &RenderContext<'_>,
|
ctx: &CompletionContext,
|
||||||
fields: &[hir::Field],
|
fields: &[hir::Field],
|
||||||
item: impl HasAttrs,
|
item: impl HasAttrs,
|
||||||
) -> Option<(Vec<hir::Field>, bool)> {
|
) -> Option<(Vec<hir::Field>, bool)> {
|
||||||
let module = ctx.completion.module?;
|
let module = ctx.module?;
|
||||||
let n_fields = fields.len();
|
let n_fields = fields.len();
|
||||||
let fields = fields
|
let fields = fields
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|field| field.is_visible_from(ctx.db(), module))
|
.filter(|field| field.is_visible_from(ctx.db, module))
|
||||||
.copied()
|
.copied()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let fields_omitted =
|
let fields_omitted =
|
||||||
n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
|
n_fields - fields.len() > 0 || item.attrs(ctx.db).by_key("non_exhaustive").exists();
|
||||||
Some((fields, fields_omitted))
|
Some((fields, fields_omitted))
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ fn foo() {
|
||||||
bn Tuple Tuple($1)$0
|
bn Tuple Tuple($1)$0
|
||||||
st Tuple
|
st Tuple
|
||||||
ev Variant
|
ev Variant
|
||||||
|
md module
|
||||||
en SingleVariantEnum
|
en SingleVariantEnum
|
||||||
st Unit
|
st Unit
|
||||||
ma makro!(…) macro_rules! makro
|
ma makro!(…) macro_rules! makro
|
||||||
|
@ -171,6 +172,7 @@ fn foo(a$0) {
|
||||||
st Record
|
st Record
|
||||||
bn Tuple Tuple($1): Tuple$0
|
bn Tuple Tuple($1): Tuple$0
|
||||||
st Tuple
|
st Tuple
|
||||||
|
md module
|
||||||
st Unit
|
st Unit
|
||||||
ma makro!(…) macro_rules! makro
|
ma makro!(…) macro_rules! makro
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -187,6 +189,7 @@ fn foo(a$0: Tuple) {
|
||||||
st Record
|
st Record
|
||||||
bn Tuple Tuple($1)$0
|
bn Tuple Tuple($1)$0
|
||||||
st Tuple
|
st Tuple
|
||||||
|
md module
|
||||||
st Unit
|
st Unit
|
||||||
ma makro!(…) macro_rules! makro
|
ma makro!(…) macro_rules! makro
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -228,7 +231,6 @@ fn foo() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
kw ref
|
kw ref
|
||||||
kw mut
|
kw mut
|
||||||
ev E::X E::X
|
|
||||||
en E
|
en E
|
||||||
ma m!(…) macro_rules! m
|
ma m!(…) macro_rules! m
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -378,3 +380,67 @@ fn foo() {
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_no_delims_if_existing() {
|
||||||
|
check_empty(
|
||||||
|
r#"
|
||||||
|
struct Bar(u32);
|
||||||
|
fn foo() {
|
||||||
|
match Bar(0) {
|
||||||
|
B$0(b) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
kw self::
|
||||||
|
kw super::
|
||||||
|
kw crate::
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check_empty(
|
||||||
|
r#"
|
||||||
|
struct Foo { bar: u32 }
|
||||||
|
fn foo() {
|
||||||
|
match Foo { bar: 0 } {
|
||||||
|
F$0 { bar } => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
kw return
|
||||||
|
kw self
|
||||||
|
kw super
|
||||||
|
kw crate
|
||||||
|
st Foo
|
||||||
|
fn foo() fn()
|
||||||
|
bt u32
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check_empty(
|
||||||
|
r#"
|
||||||
|
enum Enum {
|
||||||
|
TupleVariant(u32)
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
match Enum::TupleVariant(0) {
|
||||||
|
Enum::T$0(b) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
check_empty(
|
||||||
|
r#"
|
||||||
|
enum Enum {
|
||||||
|
RecordVariant { field: u32 }
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
match (Enum::RecordVariant { field: 0 }) {
|
||||||
|
Enum::RecordV$0 { field } => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ fn main() {
|
||||||
kw true
|
kw true
|
||||||
kw false
|
kw false
|
||||||
kw return
|
kw return
|
||||||
sn Foo {…} Foo { foo1: u32, foo2: u32 }
|
st Foo {…} Foo { foo1: u32, foo2: u32 }
|
||||||
fd ..Default::default()
|
fd ..Default::default()
|
||||||
fd foo1 u32
|
fd foo1 u32
|
||||||
fd foo2 u32
|
fd foo2 u32
|
||||||
|
|
Loading…
Reference in a new issue