From 4fe5f03c7fee75eccd29623347fa31bf5fa7c482 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 16 Mar 2022 13:41:35 +0100 Subject: [PATCH 1/4] Rename compound things to variant things --- crates/ide/src/inlay_hints.rs | 27 +++----- crates/ide_completion/src/context.rs | 3 +- crates/ide_completion/src/render.rs | 2 +- .../ide_completion/src/render/enum_variant.rs | 7 +- crates/ide_completion/src/render/function.rs | 2 +- .../src/render/struct_literal.rs | 11 +-- .../src/render/union_literal.rs | 5 +- .../src/render/{compound.rs => variant.rs} | 10 +-- crates/ide_completion/src/tests/pattern.rs | 68 +++++++++++++++++++ crates/ide_completion/src/tests/record.rs | 2 +- 10 files changed, 101 insertions(+), 36 deletions(-) rename crates/ide_completion/src/render/{compound.rs => variant.rs} (96%) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 322a9b820a..428b8d1109 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -69,24 +69,17 @@ pub(crate) fn inlay_hints( let mut hints = Vec::new(); - if let Some(range_limit) = range_limit { - let range_limit = range_limit.range; - match file.covering_element(range_limit) { + let get_hints = |node| get_hints(&mut hints, &sema, config, node); + match range_limit { + Some(FileRange { range, .. }) => match file.covering_element(range) { NodeOrToken::Token(_) => return hints, - NodeOrToken::Node(n) => { - for node in n - .descendants() - .filter(|descendant| range_limit.contains_range(descendant.text_range())) - { - get_hints(&mut hints, &sema, config, node); - } - } - } - } else { - for node in file.descendants() { - get_hints(&mut hints, &sema, config, node); - } - } + NodeOrToken::Node(n) => n + .descendants() + .filter(|descendant| range.contains_range(descendant.text_range())) + .for_each(get_hints), + }, + None => file.descendants().for_each(get_hints), + }; hints } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 1ad233494a..ed59eb6bd3 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -58,7 +58,7 @@ pub(super) enum PathKind { #[derive(Debug)] 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, /// Whether this path stars with a `::`. pub(super) is_absolute_path: bool, @@ -890,6 +890,7 @@ impl<'a> CompletionContext<'a> { Some(PathKind::Pat) }, ast::RecordPat(it) => { + path_ctx.has_call_parens = true; pat_ctx = Some(pattern_context_for(original_file, it.into())); Some(PathKind::Pat) }, diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 0ed346c55e..d6fa86b55d 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -8,7 +8,7 @@ pub(crate) mod const_; pub(crate) mod pattern; pub(crate) mod type_alias; pub(crate) mod struct_literal; -pub(crate) mod compound; +pub(crate) mod variant; pub(crate) mod union_literal; use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef}; diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index 5b485005d3..9e27a84e2f 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs @@ -7,8 +7,9 @@ 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, + compute_ref_match, compute_type_match, + variant::{format_literal_label, render_record, render_tuple, RenderedLiteral}, + RenderContext, }, CompletionRelevance, }; @@ -56,7 +57,7 @@ fn render( render_record(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name)) } StructKind::Unit => { - RenderedCompound { literal: qualified_name.clone(), detail: qualified_name.clone() } + RenderedLiteral { literal: qualified_name.clone(), detail: qualified_name.clone() } } }; diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 2b9f82fc54..6a46de4229 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs @@ -192,7 +192,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool { Some(PathCompletionCtx { kind: Some(PathKind::Expr), has_call_parens: true, .. }) => { return false } - Some(PathCompletionCtx { kind: Some(PathKind::Use), .. }) => { + Some(PathCompletionCtx { kind: Some(PathKind::Use | PathKind::Type), .. }) => { cov_mark::hit!(no_parens_in_use_item); return false; } diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs index a686be6691..b4fa26add1 100644 --- a/crates/ide_completion/src/render/struct_literal.rs +++ b/crates/ide_completion/src/render/struct_literal.rs @@ -1,11 +1,12 @@ //! Renderer for `struct` literal. use hir::{HasAttrs, Name, StructKind}; +use ide_db::SymbolKind; use syntax::SmolStr; use crate::{ - render::compound::{ - format_literal_label, render_record, render_tuple, visible_fields, RenderedCompound, + render::variant::{ + format_literal_label, render_record, render_tuple, visible_fields, RenderedLiteral, }, render::RenderContext, CompletionItem, CompletionItemKind, @@ -37,12 +38,12 @@ pub(crate) fn render_struct_literal( fn build_completion( ctx: &RenderContext<'_>, name: SmolStr, - rendered: RenderedCompound, + rendered: RenderedLiteral, kind: StructKind, def: impl HasAttrs + Copy, ) -> CompletionItem { let mut item = CompletionItem::new( - CompletionItemKind::Snippet, + CompletionItemKind::SymbolKind(SymbolKind::Struct), ctx.source_range(), format_literal_label(&name, kind), ); @@ -64,7 +65,7 @@ fn render_literal( name: &str, kind: StructKind, fields: &[hir::Field], -) -> Option { +) -> Option { let path_string; let qualified_name = if let Some(path) = path { diff --git a/crates/ide_completion/src/render/union_literal.rs b/crates/ide_completion/src/render/union_literal.rs index 80499e102b..209f006982 100644 --- a/crates/ide_completion/src/render/union_literal.rs +++ b/crates/ide_completion/src/render/union_literal.rs @@ -1,11 +1,12 @@ //! Renderer for `union` literals. use hir::{HirDisplay, Name, StructKind}; +use ide_db::SymbolKind; use itertools::Itertools; use crate::{ render::{ - compound::{format_literal_label, visible_fields}, + variant::{format_literal_label, visible_fields}, RenderContext, }, CompletionItem, CompletionItemKind, @@ -25,7 +26,7 @@ pub(crate) fn render_union_literal( }; let mut item = CompletionItem::new( - CompletionItemKind::Snippet, + CompletionItemKind::SymbolKind(SymbolKind::Union), ctx.source_range(), format_literal_label(&name, StructKind::Record), ); diff --git a/crates/ide_completion/src/render/compound.rs b/crates/ide_completion/src/render/variant.rs similarity index 96% rename from crates/ide_completion/src/render/compound.rs rename to crates/ide_completion/src/render/variant.rs index c7f3bd1f79..e6a92f387e 100644 --- a/crates/ide_completion/src/render/compound.rs +++ b/crates/ide_completion/src/render/variant.rs @@ -9,7 +9,7 @@ use syntax::SmolStr; /// A rendered struct, union, or enum variant, split into fields for actual /// auto-completion (`literal`, using `field: ()`) and display in the /// completions menu (`detail`, using `field: type`). -pub(crate) struct RenderedCompound { +pub(crate) struct RenderedLiteral { pub(crate) literal: String, pub(crate) detail: String, } @@ -21,7 +21,7 @@ pub(crate) fn render_record( snippet_cap: Option, fields: &[hir::Field], name: Option<&str>, -) -> RenderedCompound { +) -> RenderedLiteral { let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1)) @@ -34,7 +34,7 @@ pub(crate) fn render_record( f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db))) }); - RenderedCompound { + RenderedLiteral { literal: format!("{} {{ {} }}", name.unwrap_or(""), completions), detail: format!("{} {{ {} }}", name.unwrap_or(""), types), } @@ -47,7 +47,7 @@ pub(crate) fn render_tuple( snippet_cap: Option, fields: &[hir::Field], name: Option<&str>, -) -> RenderedCompound { +) -> RenderedLiteral { let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| { if snippet_cap.is_some() { f(&format_args!("${{{}:()}}", idx + 1)) @@ -58,7 +58,7 @@ pub(crate) fn render_tuple( let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db))); - RenderedCompound { + RenderedLiteral { literal: format!("{}({})", name.unwrap_or(""), completions), detail: format!("{}({})", name.unwrap_or(""), types), } diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs index 891c1346df..28b6ba6a37 100644 --- a/crates/ide_completion/src/tests/pattern.rs +++ b/crates/ide_completion/src/tests/pattern.rs @@ -378,3 +378,71 @@ 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#" + ev TupleVariant(…) TupleVariant(u32) + "#]], + ); + check_empty( + r#" +enum Enum { + RecordVariant { field: u32 } +} +fn foo() { + match (Enum::RecordVariant { field: 0 }) { + Enum::RecordV$0 { field } => {} + } +} +"#, + expect![[r#" + ev RecordVariant {…} RecordVariant { field: u32 } + "#]], + ); +} diff --git a/crates/ide_completion/src/tests/record.rs b/crates/ide_completion/src/tests/record.rs index 5e9367960f..0322ecbe39 100644 --- a/crates/ide_completion/src/tests/record.rs +++ b/crates/ide_completion/src/tests/record.rs @@ -166,7 +166,7 @@ fn main() { kw true kw false kw return - sn Foo {…} Foo { foo1: u32, foo2: u32 } + st Foo {…} Foo { foo1: u32, foo2: u32 } fd ..Default::default() fd foo1 u32 fd foo2 u32 From 02b401b130fc048a9f863fe0a58655f540947ff6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 16 Mar 2022 16:27:55 +0100 Subject: [PATCH 2/4] Simplify completion render functionality --- crates/ide_completion/src/completions.rs | 93 +++++++--- .../src/completions/flyimport.rs | 2 +- .../ide_completion/src/completions/pattern.rs | 28 ++- .../ide_completion/src/completions/record.rs | 9 +- crates/ide_completion/src/render.rs | 143 ++++++++++++-- .../ide_completion/src/render/enum_variant.rs | 102 ---------- crates/ide_completion/src/render/function.rs | 26 ++- crates/ide_completion/src/render/literal.rs | 174 ++++++++++++++++++ crates/ide_completion/src/render/macro_.rs | 18 +- crates/ide_completion/src/render/pattern.rs | 107 ++++++----- .../src/render/struct_literal.rs | 92 --------- .../src/render/union_literal.rs | 2 +- crates/ide_completion/src/render/variant.rs | 26 +-- crates/ide_completion/src/tests/pattern.rs | 13 +- 14 files changed, 490 insertions(+), 345 deletions(-) delete mode 100644 crates/ide_completion/src/render/enum_variant.rs create mode 100644 crates/ide_completion/src/render/literal.rs delete mode 100644 crates/ide_completion/src/render/struct_literal.rs diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index 91e6b84294..d187d56cb5 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -29,11 +29,11 @@ use crate::{ item::Builder, render::{ const_::render_const, - enum_variant::render_variant, function::{render_fn, render_method}, + literal::{render_struct_literal, render_variant_lit}, + macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, - render_field, render_resolution, render_tuple_field, - struct_literal::render_struct_literal, + render_field, render_resolution, render_resolution_simple, render_tuple_field, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, RenderContext, @@ -124,7 +124,37 @@ impl Completions { cov_mark::hit!(qualified_path_doc_hidden); 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( @@ -138,7 +168,11 @@ impl Completions { Visible::Editable => true, 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( @@ -154,8 +188,7 @@ impl Completions { Visible::No => return, }; self.add(render_method( - RenderContext::new(ctx, is_private_editable), - None, + RenderContext::new(ctx).private_editable(is_private_editable), receiver, local_name, func, @@ -168,7 +201,10 @@ impl Completions { Visible::Editable => true, 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) { @@ -177,7 +213,10 @@ impl Completions { Visible::Editable => true, 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( @@ -185,7 +224,7 @@ impl Completions { ctx: &CompletionContext, 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( @@ -194,8 +233,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - let item = render_variant(RenderContext::new(ctx, false), None, None, variant, Some(path)); - self.add(item); + self.add_opt(render_variant_lit(RenderContext::new(ctx), None, variant, Some(path))); } pub(crate) fn add_enum_variant( @@ -204,8 +242,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - let item = render_variant(RenderContext::new(ctx, false), None, local_name, variant, None); - self.add(item); + self.add_opt(render_variant_lit(RenderContext::new(ctx), local_name, variant, None)); } pub(crate) fn add_field( @@ -220,7 +257,12 @@ impl Completions { Visible::Editable => true, 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); } @@ -231,7 +273,7 @@ impl Completions { path: Option, local_name: Option, ) { - 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); } @@ -242,7 +284,7 @@ impl Completions { path: Option, local_name: Option, ) { - 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); } @@ -253,7 +295,7 @@ impl Completions { field: usize, 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); } @@ -272,7 +314,14 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - 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, + false, + )); + self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None, true)); } pub(crate) fn add_qualified_variant_pat( @@ -281,7 +330,9 @@ impl Completions { variant: hir::Variant, 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, false)); + self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path, true)); } pub(crate) fn add_struct_pat( @@ -290,7 +341,7 @@ impl Completions { strukt: hir::Struct, local_name: Option, ) { - 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)); } } diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index aee2bbb53c..6c8878a7bb 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -198,7 +198,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) }) .filter_map(|import| { render_resolution_with_import( - RenderContext::new(ctx, false), + RenderContext::new(ctx), ImportEdit { import, scope: import_scope.clone() }, ) }), diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 958c892b8d..6c17da07d6 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -54,8 +54,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { { if refutable || single_variant_enum(e) { super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { - acc.add_qualified_variant_pat(ctx, variant, path.clone()); - acc.add_qualified_enum_variant(ctx, variant, path); + acc.add_qualified_variant_pat(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 // suggest variants + auto-imports 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::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { acc.add_struct_pat(ctx, strukt, Some(name.clone())); @@ -76,8 +75,11 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { true } hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), - hir::ModuleDef::Const(..) | hir::ModuleDef::Module(..) => refutable, - hir::ModuleDef::Macro(mac) => mac.is_fn_like(ctx.db), + 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) + } _ => false, }, 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())); true } - Some(hir::Adt::Enum(_)) => refutable, - _ => true, + Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), + 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 { - acc.add_resolution(ctx, name, res); + if add_simple_path { + acc.add_resolution_simple(ctx, name, res); } }); } diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index 264b3784bf..5509ec922f 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs @@ -84,13 +84,16 @@ pub(crate) fn complete_record_literal( match ctx.expected_type.as_ref()?.as_adt()? { 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 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); } 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 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); } @@ -132,7 +135,7 @@ fn baz() { #[test] fn literal_struct_completion_from_sub_modules() { check_edit( - "Struct {…}", + "submod::Struct {…}", r#" mod submod { pub struct Struct { diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index d6fa86b55d..13de818da1 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -3,13 +3,12 @@ pub(crate) mod macro_; pub(crate) mod function; -pub(crate) mod enum_variant; pub(crate) mod const_; pub(crate) mod pattern; pub(crate) mod type_alias; -pub(crate) mod struct_literal; pub(crate) mod variant; pub(crate) mod union_literal; +pub(crate) mod literal; use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef}; use ide_db::{helpers::item_name, RootDatabase, SnippetCap, SymbolKind}; @@ -18,22 +17,30 @@ use syntax::{SmolStr, SyntaxKind, TextRange}; use crate::{ context::{PathCompletionCtx, PathKind}, 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, }; /// Interface for data and methods required for items rendering. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct RenderContext<'a> { completion: &'a CompletionContext<'a>, is_private_editable: bool, + import_to_add: Option, } impl<'a> RenderContext<'a> { - pub(crate) fn new( - completion: &'a CompletionContext<'a>, - is_private_editable: bool, - ) -> RenderContext<'a> { - RenderContext { completion, is_private_editable } + pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { + RenderContext { completion, is_private_editable: false, import_to_add: None } + } + + 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) -> Self { + self.import_to_add = import_to_add; + self } fn snippet_cap(&self) -> Option { @@ -139,6 +146,14 @@ pub(crate) fn render_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( ctx: RenderContext<'_>, import_edit: ImportEdit, @@ -163,24 +178,26 @@ fn render_resolution_( use hir::ModuleDef::*; let db = ctx.db(); - + let ctx = ctx.import_to_add(import_to_add); let kind = match resolution { ScopeDef::ModuleDef(Function(func)) => { - return render_fn(ctx, import_to_add, Some(local_name), func); + return render_fn(ctx, Some(local_name), func); } ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => { - return render_variant(ctx, import_to_add, Some(local_name), var, None); - } - ScopeDef::ModuleDef(Macro(mac)) => { - return render_macro(ctx, import_to_add, local_name, mac) + if let Some(item) = render_variant_lit(ctx.clone(), Some(local_name.clone()), var, None) + { + return item; + } + CompletionItemKind::SymbolKind(SymbolKind::Variant) } + ScopeDef::ModuleDef(Macro(mac)) => return render_macro(ctx, 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 { + if let Some(import_to_add) = ctx.import_to_add { item.add_import(import_to_add); } return item.build(); @@ -253,7 +270,95 @@ fn render_resolution_( item.set_documentation(scope_def_docs(db, 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.build() +} + +fn render_resolution_simple_( + ctx: RenderContext<'_>, + local_name: hir::Name, + import_to_add: Option, + 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(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro), + ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), + ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { + hir::Adt::Struct(_) => SymbolKind::Struct, + hir::Adt::Union(_) => SymbolKind::Union, + hir::Adt::Enum(_) => SymbolKind::Enum, + }), + ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), + ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), + ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), + ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), + ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, + ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { + hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, + hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, + hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, + }), + ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), + ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), + ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { + CompletionItemKind::SymbolKind(SymbolKind::SelfParam) + } + }; + + let local_name = local_name.to_smol_str(); + let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone()); + if let ScopeDef::Local(local) = resolution { + let ty = local.ty(db); + if !ty.is_unknown() { + item.detail(ty.display(db).to_string()); + } + + item.set_relevance(CompletionRelevance { + type_match: compute_type_match(ctx.completion, &ty), + exact_name_match: compute_exact_name_match(ctx.completion, &local_name), + is_local: true, + ..CompletionRelevance::default() + }); + + if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) { + item.ref_match(ref_match); + } + }; + + // Add `<>` for generic types + let type_path_no_ty_args = matches!( + ctx.completion.path_context, + Some(PathCompletionCtx { kind: Some(PathKind::Type), has_type_args: false, .. }) + ) && ctx.completion.config.add_call_parenthesis; + 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, "<…>"])) + .insert_snippet(cap, format!("{}<$0>", local_name)); + } + } + } + item.set_documentation(scope_def_docs(db, resolution)) + .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); + + if let Some(import_to_add) = ctx.import_to_add { item.add_import(import_to_add); } item.build() @@ -577,7 +682,7 @@ fn main() { let _: m::Spam = S$0 } kind: SymbolKind( Variant, ), - lookup: "Spam::Bar", + lookup: "Spam::Bar(…)", detail: "m::Spam::Bar(i32)", relevance: CompletionRelevance { exact_name_match: false, @@ -1156,6 +1261,7 @@ fn main() { "#, expect![[r#" lc s [type+name+local] + st S [type] st S [] fn main() [] fn foo(…) [] @@ -1172,6 +1278,7 @@ fn main() { "#, expect![[r#" lc ssss [type+local] + st S [type] st S [] fn main() [] fn foo(…) [] diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs deleted file mode 100644 index 9e27a84e2f..0000000000 --- a/crates/ide_completion/src/render/enum_variant.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Renderer for `enum` variants. - -use hir::{HasAttrs, StructKind}; -use ide_db::SymbolKind; -use syntax::SmolStr; - -use crate::{ - item::{CompletionItem, ImportEdit}, - render::{ - compute_ref_match, compute_type_match, - variant::{format_literal_label, render_record, render_tuple, RenderedLiteral}, - RenderContext, - }, - CompletionRelevance, -}; - -pub(crate) fn render_variant( - ctx: RenderContext<'_>, - import_to_add: Option, - local_name: Option, - variant: hir::Variant, - path: Option, -) -> 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, - variant: hir::Variant, - path: Option, - import_to_add: Option, -) -> 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 => { - RenderedLiteral { 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() -} diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 6a46de4229..7df13988ad 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs @@ -8,7 +8,7 @@ use syntax::SmolStr; use crate::{ context::{CompletionContext, PathCompletionCtx, PathKind}, - item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit}, + item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance}, patterns::ImmediateLocation, render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext}, }; @@ -20,23 +20,21 @@ enum FuncKind { pub(crate) fn render_fn( ctx: RenderContext<'_>, - import_to_add: Option, local_name: Option, func: hir::Function, ) -> CompletionItem { 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( ctx: RenderContext<'_>, - import_to_add: Option, receiver: Option, local_name: Option, func: hir::Function, ) -> CompletionItem { 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( @@ -44,7 +42,6 @@ fn render( local_name: Option, func: hir::Function, func_kind: FuncKind, - import_to_add: Option, ) -> CompletionItem { let db = completion.db; @@ -98,17 +95,18 @@ fn render( _ => (), } - if import_to_add.is_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()); + match ctx.import_to_add { + Some(import_to_add) => { + item.add_import(import_to_add); + } + 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() } diff --git a/crates/ide_completion/src/render/literal.rs b/crates/ide_completion/src/render/literal.rs new file mode 100644 index 0000000000..d91e80f90c --- /dev/null +++ b/crates/ide_completion/src/render/literal.rs @@ -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, + variant: hir::Variant, + path: Option, +) -> Option { + 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, + local_name: Option, +) -> Option { + 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, +) -> Option { + 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> { + 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 { + 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), + } + } +} diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index d3b0de429c..9f848febeb 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs @@ -4,27 +4,21 @@ use hir::{Documentation, HirDisplay}; use ide_db::SymbolKind; use syntax::SmolStr; -use crate::{ - context::PathKind, - item::{CompletionItem, ImportEdit}, - render::RenderContext, -}; +use crate::{context::PathKind, item::CompletionItem, render::RenderContext}; pub(crate) fn render_macro( ctx: RenderContext<'_>, - import_to_add: Option, name: hir::Name, macro_: hir::Macro, ) -> CompletionItem { let _p = profile::span("render_macro"); - render(ctx, name, macro_, import_to_add) + render(ctx, name, macro_) } fn render( ctx @ RenderContext { completion, .. }: RenderContext<'_>, name: hir::Name, macro_: hir::Macro, - import_to_add: Option, ) -> CompletionItem { let source_range = if completion.is_immediately_after_macro_bang() { cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token); @@ -52,12 +46,7 @@ fn render( .set_documentation(docs) .set_relevance(ctx.completion_relevance()); - if let Some(import_to_add) = import_to_add { - item.add_import(import_to_add); - } - let name = &*name; - match ctx.snippet_cap() { Some(cap) if needs_bang && !completion.path_is_call() => { let snippet = format!("{}!{}$0{}", name, bra, ket); @@ -73,6 +62,9 @@ fn render( item.insert_text(name); } }; + if let Some(import_to_add) = ctx.import_to_add { + item.add_import(import_to_add); + } item.build() } diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index c2d51b1252..b47be68e07 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs @@ -1,13 +1,13 @@ //! Renderer for patterns. -use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind}; +use hir::{db::HirDatabase, HasAttrs, Name, StructKind}; use ide_db::SnippetCap; use itertools::Itertools; use syntax::SmolStr; use crate::{ context::{ParamKind, PatternContext}, - render::RenderContext, + render::{variant::visible_fields, RenderContext}, CompletionItem, CompletionItemKind, }; @@ -19,7 +19,7 @@ pub(crate) fn render_struct_pat( let _p = profile::span("render_struct_pat"); 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() { // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields @@ -36,14 +36,19 @@ pub(crate) fn render_variant_pat( ctx: RenderContext<'_>, variant: hir::Variant, local_name: Option, - path: Option, + path: Option<&hir::ModPath>, + omit_fields: bool, ) -> Option { let _p = profile::span("render_variant_pat"); let fields = variant.fields(ctx.db()); - let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; + let (visible_fields, fields_omitted) = if omit_fields { + (Vec::new(), false) + } else { + visible_fields(ctx.completion, &fields, variant)? + }; - let name = match &path { + let name = match path { Some(path) => path.to_string().into(), None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(), }; @@ -78,9 +83,7 @@ fn render_pat( fields_omitted: bool, ) -> Option { let mut pat = match kind { - StructKind::Tuple if ctx.snippet_cap().is_some() => { - render_tuple_as_pat(fields, name, fields_omitted) - } + 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) } @@ -113,49 +116,53 @@ fn render_record_as_pat( fields_omitted: bool, ) -> String { let fields = fields.iter(); - if snippet_cap.is_some() { - format!( - "{name} {{ {}{} }}", - fields - .enumerate() - .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1)) - .format(", "), - if fields_omitted { ", .." } else { "" }, - name = name - ) - } else { - format!( - "{name} {{ {}{} }}", - fields.map(|field| field.name(db)).format(", "), - if fields_omitted { ", .." } else { "" }, - name = name - ) + match snippet_cap { + Some(_) => { + format!( + "{name} {{ {}{} }}", + fields.enumerate().format_with(", ", |(idx, field), f| { + f(&format_args!("{}${}", field.name(db), idx + 1)) + }), + if fields_omitted { ", .." } else { "" }, + name = name + ) + } + None => { + format!( + "{name} {{ {}{} }}", + fields.map(|field| field.name(db)).format(", "), + if fields_omitted { ", .." } else { "" }, + name = name + ) + } } } -fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String { - format!( - "{name}({}{})", - fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), - if fields_omitted { ", .." } else { "" }, - name = name - ) -} - -fn visible_fields( - ctx: &RenderContext<'_>, +fn render_tuple_as_pat( + snippet_cap: Option, fields: &[hir::Field], - item: impl HasAttrs, -) -> Option<(Vec, bool)> { - let module = ctx.completion.module?; - let n_fields = fields.len(); - let fields = fields - .iter() - .filter(|field| field.is_visible_from(ctx.db(), module)) - .copied() - .collect::>(); - - let fields_omitted = - n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists(); - Some((fields, fields_omitted)) + name: &str, + fields_omitted: bool, +) -> String { + let fields = fields.iter(); + match snippet_cap { + Some(_) => { + format!( + "{name}({}{})", + fields + .enumerate() + .format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }), + if fields_omitted { ", .." } else { "" }, + name = name + ) + } + None => { + format!( + "{name}({}{})", + fields.enumerate().map(|(idx, _)| idx).format(", "), + if fields_omitted { ", .." } else { "" }, + name = name + ) + } + } } diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs deleted file mode 100644 index b4fa26add1..0000000000 --- a/crates/ide_completion/src/render/struct_literal.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Renderer for `struct` literal. - -use hir::{HasAttrs, Name, StructKind}; -use ide_db::SymbolKind; -use syntax::SmolStr; - -use crate::{ - render::variant::{ - format_literal_label, render_record, render_tuple, visible_fields, RenderedLiteral, - }, - render::RenderContext, - CompletionItem, CompletionItemKind, -}; - -pub(crate) fn render_struct_literal( - ctx: RenderContext<'_>, - strukt: hir::Struct, - path: Option, - local_name: Option, -) -> Option { - 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: RenderedLiteral, - kind: StructKind, - def: impl HasAttrs + Copy, -) -> CompletionItem { - let mut item = CompletionItem::new( - CompletionItemKind::SymbolKind(SymbolKind::Struct), - 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, - name: &str, - kind: StructKind, - fields: &[hir::Field], -) -> Option { - 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) -} diff --git a/crates/ide_completion/src/render/union_literal.rs b/crates/ide_completion/src/render/union_literal.rs index 209f006982..aafedaf5aa 100644 --- a/crates/ide_completion/src/render/union_literal.rs +++ b/crates/ide_completion/src/render/union_literal.rs @@ -32,7 +32,7 @@ pub(crate) fn render_union_literal( ); 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() { return None; diff --git a/crates/ide_completion/src/render/variant.rs b/crates/ide_completion/src/render/variant.rs index e6a92f387e..a37b4237c4 100644 --- a/crates/ide_completion/src/render/variant.rs +++ b/crates/ide_completion/src/render/variant.rs @@ -1,6 +1,6 @@ //! 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 ide_db::SnippetCap; use itertools::Itertools; @@ -16,11 +16,11 @@ pub(crate) struct RenderedLiteral { /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for /// the `name` argument for an anonymous type. -pub(crate) fn render_record( +pub(crate) fn render_record_lit( db: &dyn HirDatabase, snippet_cap: Option, fields: &[hir::Field], - name: Option<&str>, + path: &str, ) -> RenderedLiteral { let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { @@ -35,18 +35,18 @@ pub(crate) fn render_record( }); RenderedLiteral { - literal: format!("{} {{ {} }}", name.unwrap_or(""), completions), - detail: format!("{} {{ {} }}", name.unwrap_or(""), types), + literal: format!("{} {{ {} }}", path, completions), + detail: format!("{} {{ {} }}", path, types), } } /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for /// the `name` argument for an anonymous type. -pub(crate) fn render_tuple( +pub(crate) fn render_tuple_lit( db: &dyn HirDatabase, snippet_cap: Option, fields: &[hir::Field], - name: Option<&str>, + path: &str, ) -> RenderedLiteral { let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| { if snippet_cap.is_some() { @@ -59,8 +59,8 @@ pub(crate) fn render_tuple( let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db))); RenderedLiteral { - literal: format!("{}({})", name.unwrap_or(""), completions), - detail: format!("{}({})", name.unwrap_or(""), types), + literal: format!("{}({})", path, completions), + 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 /// private fields and its item is not marked `#[non_exhaustive]`). pub(crate) fn visible_fields( - ctx: &RenderContext<'_>, + ctx: &CompletionContext, fields: &[hir::Field], item: impl HasAttrs, ) -> Option<(Vec, bool)> { - let module = ctx.completion.module?; + let module = ctx.module?; let n_fields = fields.len(); let fields = fields .iter() - .filter(|field| field.is_visible_from(ctx.db(), module)) + .filter(|field| field.is_visible_from(ctx.db, module)) .copied() .collect::>(); 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)) } diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs index 28b6ba6a37..aa8cb36f04 100644 --- a/crates/ide_completion/src/tests/pattern.rs +++ b/crates/ide_completion/src/tests/pattern.rs @@ -124,6 +124,7 @@ fn foo() { st Unit ma makro!(…) macro_rules! makro bn TupleV TupleV($1)$0 + bn TupleV TupleV()$0 ev TupleV ct CONST "#]], @@ -150,6 +151,7 @@ fn foo() { bn Tuple Tuple($1)$0 st Tuple ev Variant + md module en SingleVariantEnum st Unit ma makro!(…) macro_rules! makro @@ -171,6 +173,7 @@ fn foo(a$0) { st Record bn Tuple Tuple($1): Tuple$0 st Tuple + md module st Unit ma makro!(…) macro_rules! makro "#]], @@ -187,6 +190,7 @@ fn foo(a$0: Tuple) { st Record bn Tuple Tuple($1)$0 st Tuple + md module st Unit ma makro!(…) macro_rules! makro "#]], @@ -228,7 +232,6 @@ fn foo() { expect![[r#" kw ref kw mut - ev E::X E::X en E ma m!(…) macro_rules! m "#]], @@ -426,9 +429,7 @@ fn foo() { } } "#, - expect![[r#" - ev TupleVariant(…) TupleVariant(u32) - "#]], + expect![[r#""#]], ); check_empty( r#" @@ -441,8 +442,6 @@ fn foo() { } } "#, - expect![[r#" - ev RecordVariant {…} RecordVariant { field: u32 } - "#]], + expect![[r#""#]], ); } From c1f6f135e1f9ec056e930394a4838aa63fd17b96 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 16 Mar 2022 16:32:36 +0100 Subject: [PATCH 3/4] Remove code duplication --- crates/ide_completion/src/render.rs | 99 +++-------------------------- 1 file changed, 10 insertions(+), 89 deletions(-) diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 13de818da1..10211cd971 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -177,103 +177,24 @@ fn render_resolution_( 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 { + match resolution { + ScopeDef::ModuleDef(Macro(mac)) => { + let ctx = ctx.import_to_add(import_to_add); + return render_macro(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); } ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => { - if let Some(item) = render_variant_lit(ctx.clone(), Some(local_name.clone()), 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) { return item; } - CompletionItemKind::SymbolKind(SymbolKind::Variant) - } - ScopeDef::ModuleDef(Macro(mac)) => return render_macro(ctx, 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) = ctx.import_to_add { - item.add_import(import_to_add); - } - return item.build(); - } - - ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant), - ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), - ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { - hir::Adt::Struct(_) => SymbolKind::Struct, - hir::Adt::Union(_) => SymbolKind::Union, - hir::Adt::Enum(_) => SymbolKind::Enum, - }), - ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), - ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), - ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), - ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), - ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, - ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { - hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, - hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, - hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, - }), - ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), - ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), - ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { - CompletionItemKind::SymbolKind(SymbolKind::SelfParam) - } - }; - - let local_name = local_name.to_smol_str(); - let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone()); - if let ScopeDef::Local(local) = resolution { - let ty = local.ty(db); - if !ty.is_unknown() { - item.detail(ty.display(db).to_string()); - } - - item.set_relevance(CompletionRelevance { - type_match: compute_type_match(ctx.completion, &ty), - exact_name_match: compute_exact_name_match(ctx.completion, &local_name), - is_local: true, - ..CompletionRelevance::default() - }); - - if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) { - item.ref_match(ref_match); - } - }; - - // Add `<>` for generic types - let type_path_no_ty_args = matches!( - ctx.completion.path_context, - Some(PathCompletionCtx { kind: Some(PathKind::Type), has_type_args: false, .. }) - ) && ctx.completion.config.add_call_parenthesis; - 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, "<…>"])) - .insert_snippet(cap, format!("{}<$0>", local_name)); - } } + _ => (), } - item.set_documentation(scope_def_docs(db, resolution)) - .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); - - if let Some(import_to_add) = ctx.import_to_add { - item.add_import(import_to_add); - } - item.build() + render_resolution_simple_(ctx, local_name, import_to_add, resolution) } fn render_resolution_simple_( From a40a847d77833e01bf8227f06c149f2e22a27935 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 16 Mar 2022 16:41:35 +0100 Subject: [PATCH 4/4] Revert omitting field completions --- crates/ide_completion/src/completions.rs | 5 +---- crates/ide_completion/src/render/pattern.rs | 7 +------ crates/ide_completion/src/tests/pattern.rs | 1 - 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index d187d56cb5..867e6bcf48 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -319,9 +319,7 @@ impl Completions { variant, local_name.clone(), None, - false, )); - self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None, true)); } pub(crate) fn add_qualified_variant_pat( @@ -331,8 +329,7 @@ impl Completions { path: hir::ModPath, ) { let path = Some(&path); - self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path, false)); - self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path, true)); + self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path)); } pub(crate) fn add_struct_pat( diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index b47be68e07..efceb85718 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs @@ -37,16 +37,11 @@ pub(crate) fn render_variant_pat( variant: hir::Variant, local_name: Option, path: Option<&hir::ModPath>, - omit_fields: bool, ) -> Option { let _p = profile::span("render_variant_pat"); let fields = variant.fields(ctx.db()); - let (visible_fields, fields_omitted) = if omit_fields { - (Vec::new(), false) - } else { - visible_fields(ctx.completion, &fields, variant)? - }; + let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?; let name = match path { Some(path) => path.to_string().into(), diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs index aa8cb36f04..50d5e01979 100644 --- a/crates/ide_completion/src/tests/pattern.rs +++ b/crates/ide_completion/src/tests/pattern.rs @@ -124,7 +124,6 @@ fn foo() { st Unit ma makro!(…) macro_rules! makro bn TupleV TupleV($1)$0 - bn TupleV TupleV()$0 ev TupleV ct CONST "#]],