From b184bfad7a2dc6a9bf6654a7eec6c68a27c49f70 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 20 Dec 2020 18:19:23 +0100 Subject: Add completions for patterns --- crates/completion/src/render/pattern.rs | 128 ++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 crates/completion/src/render/pattern.rs (limited to 'crates/completion/src/render') diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs new file mode 100644 index 000000000..e20b0027b --- /dev/null +++ b/crates/completion/src/render/pattern.rs @@ -0,0 +1,128 @@ +//! Renderer for patterns. + +use hir::{db::HirDatabase, HasVisibility, Name, StructKind}; +use itertools::Itertools; + +use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; + +pub(crate) fn render_struct_pat<'a>( + ctx: RenderContext<'a>, + strukt: hir::Struct, + local_name: Option, +) -> Option { + let _p = profile::span("render_struct_pat"); + + let module = ctx.completion.scope.module()?; + let fields = strukt.fields(ctx.db()); + let n_fields = fields.len(); + let fields = fields + .into_iter() + .filter(|field| field.is_visible_from(ctx.db(), module)) + .collect::>(); + + if fields.is_empty() { + // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields + return None; + } + let fields_omitted = n_fields - fields.len() > 0; + + let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); + let mut pat = match strukt.kind(ctx.db()) { + StructKind::Tuple if ctx.snippet_cap().is_some() => { + render_tuple_as_pat(&fields, &name, fields_omitted) + } + StructKind::Record => render_record_as_pat(ctx.db(), &fields, &name, fields_omitted), + _ => return None, + }; + + if ctx.completion.is_param { + pat.push(':'); + pat.push(' '); + pat.push_str(&name); + } + if ctx.snippet_cap().is_some() { + pat.push_str("$0"); + } + + let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) + .kind(CompletionItemKind::Binding) + .set_documentation(ctx.docs(strukt)) + .set_deprecated(ctx.is_deprecated(strukt)) + .detail(&pat); + if let Some(snippet_cap) = ctx.snippet_cap() { + completion = completion.insert_snippet(snippet_cap, pat); + } else { + completion = completion.insert_text(pat); + } + Some(completion.build()) +} + +pub(crate) fn render_variant_pat<'a>( + ctx: RenderContext<'a>, + variant: hir::Variant, + local_name: Option, +) -> Option { + let _p = profile::span("render_variant_pat"); + + let module = ctx.completion.scope.module()?; + let fields = variant.fields(ctx.db()); + let n_fields = fields.len(); + let fields = fields + .into_iter() + .filter(|field| field.is_visible_from(ctx.db(), module)) + .collect::>(); + + let fields_omitted = n_fields - fields.len() > 0; + + let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); + let mut pat = match variant.kind(ctx.db()) { + StructKind::Tuple if ctx.snippet_cap().is_some() => { + render_tuple_as_pat(&fields, &name, fields_omitted) + } + StructKind::Record => render_record_as_pat(ctx.db(), &fields, &name, fields_omitted), + _ => return None, + }; + + if ctx.completion.is_param { + pat.push(':'); + pat.push(' '); + pat.push_str(&name); + } + if ctx.snippet_cap().is_some() { + pat.push_str("$0"); + } + let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) + .kind(CompletionItemKind::Binding) + .set_documentation(ctx.docs(variant)) + .set_deprecated(ctx.is_deprecated(variant)) + .detail(&pat); + if let Some(snippet_cap) = ctx.snippet_cap() { + completion = completion.insert_snippet(snippet_cap, pat); + } else { + completion = completion.insert_text(pat); + } + Some(completion.build()) +} + +fn render_record_as_pat( + db: &dyn HirDatabase, + fields: &[hir::Field], + name: &str, + fields_omitted: bool, +) -> String { + format!( + "{name} {{ {}{} }}", + fields.into_iter().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.into_iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), + if fields_omitted { ", .." } else { "" }, + name = name + ) +} -- cgit v1.2.3 From 2f6a24950a555bdfecbda7a50735d643f9d0e7f9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 20 Dec 2020 19:16:28 +0100 Subject: Emit snippets for struct pattern completion if enabled --- crates/completion/src/render/pattern.rs | 41 +++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) (limited to 'crates/completion/src/render') diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index e20b0027b..2327bf363 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs @@ -3,7 +3,10 @@ use hir::{db::HirDatabase, HasVisibility, Name, StructKind}; use itertools::Itertools; -use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; +use crate::{ + config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem, + CompletionItemKind, +}; pub(crate) fn render_struct_pat<'a>( ctx: RenderContext<'a>, @@ -31,7 +34,9 @@ pub(crate) fn render_struct_pat<'a>( StructKind::Tuple if ctx.snippet_cap().is_some() => { render_tuple_as_pat(&fields, &name, fields_omitted) } - StructKind::Record => render_record_as_pat(ctx.db(), &fields, &name, fields_omitted), + StructKind::Record => { + render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) + } _ => return None, }; @@ -79,7 +84,9 @@ pub(crate) fn render_variant_pat<'a>( StructKind::Tuple if ctx.snippet_cap().is_some() => { render_tuple_as_pat(&fields, &name, fields_omitted) } - StructKind::Record => render_record_as_pat(ctx.db(), &fields, &name, fields_omitted), + StructKind::Record => { + render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) + } _ => return None, }; @@ -106,22 +113,36 @@ pub(crate) fn render_variant_pat<'a>( fn render_record_as_pat( db: &dyn HirDatabase, + snippet_cap: Option, fields: &[hir::Field], name: &str, fields_omitted: bool, ) -> String { - format!( - "{name} {{ {}{} }}", - fields.into_iter().map(|field| field.name(db)).format(", "), - if fields_omitted { ", .." } else { "" }, - name = name - ) + let fields = fields.iter(); + if snippet_cap.is_some() { + format!( + "{name} {{ {}{} }}", + fields + .enumerate() + .map(|(idx, field)| format!("${{{}:{}}}", idx + 1, field.name(db))) + .format(", "), + if fields_omitted { ", .." } else { "" }, + name = name + ) + } else { + 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.into_iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), + fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), if fields_omitted { ", .." } else { "" }, name = name ) -- cgit v1.2.3 From a5874a38cb1bbaee685ff92a40b66f9d5bcf34f6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 20 Dec 2020 19:20:55 +0100 Subject: Reduce code duplication in pattern completion --- crates/completion/src/render/pattern.rs | 63 +++++++++++++++------------------ 1 file changed, 29 insertions(+), 34 deletions(-) (limited to 'crates/completion/src/render') diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index 2327bf363..cfe2116bd 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs @@ -8,8 +8,8 @@ use crate::{ CompletionItemKind, }; -pub(crate) fn render_struct_pat<'a>( - ctx: RenderContext<'a>, +pub(crate) fn render_struct_pat( + ctx: RenderContext<'_>, strukt: hir::Struct, local_name: Option, ) -> Option { @@ -30,24 +30,7 @@ pub(crate) fn render_struct_pat<'a>( let fields_omitted = n_fields - fields.len() > 0; let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); - let mut pat = match strukt.kind(ctx.db()) { - StructKind::Tuple if ctx.snippet_cap().is_some() => { - render_tuple_as_pat(&fields, &name, fields_omitted) - } - StructKind::Record => { - render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) - } - _ => return None, - }; - - if ctx.completion.is_param { - pat.push(':'); - pat.push(' '); - pat.push_str(&name); - } - if ctx.snippet_cap().is_some() { - pat.push_str("$0"); - } + let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &fields, fields_omitted)?; let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) .kind(CompletionItemKind::Binding) @@ -62,8 +45,8 @@ pub(crate) fn render_struct_pat<'a>( Some(completion.build()) } -pub(crate) fn render_variant_pat<'a>( - ctx: RenderContext<'a>, +pub(crate) fn render_variant_pat( + ctx: RenderContext<'_>, variant: hir::Variant, local_name: Option, ) -> Option { @@ -80,7 +63,29 @@ pub(crate) fn render_variant_pat<'a>( let fields_omitted = n_fields - fields.len() > 0; let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); - let mut pat = match variant.kind(ctx.db()) { + let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &fields, fields_omitted)?; + + let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) + .kind(CompletionItemKind::Binding) + .set_documentation(ctx.docs(variant)) + .set_deprecated(ctx.is_deprecated(variant)) + .detail(&pat); + if let Some(snippet_cap) = ctx.snippet_cap() { + completion = completion.insert_snippet(snippet_cap, pat); + } else { + completion = completion.insert_text(pat); + } + Some(completion.build()) +} + +fn render_pat( + ctx: &RenderContext<'_>, + name: &str, + kind: StructKind, + fields: &[hir::Field], + 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) } @@ -98,17 +103,7 @@ pub(crate) fn render_variant_pat<'a>( if ctx.snippet_cap().is_some() { pat.push_str("$0"); } - let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) - .kind(CompletionItemKind::Binding) - .set_documentation(ctx.docs(variant)) - .set_deprecated(ctx.is_deprecated(variant)) - .detail(&pat); - if let Some(snippet_cap) = ctx.snippet_cap() { - completion = completion.insert_snippet(snippet_cap, pat); - } else { - completion = completion.insert_text(pat); - } - Some(completion.build()) + Some(pat) } fn render_record_as_pat( -- cgit v1.2.3 From 33ecad4407e1345cd662f535c2ae7a04e4bee52b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 20 Dec 2020 20:13:06 +0100 Subject: Create non-exhaustive patterns for non_exhaustive attributed items --- crates/completion/src/render/pattern.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'crates/completion/src/render') diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index cfe2116bd..2d1600105 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs @@ -1,6 +1,6 @@ //! Renderer for patterns. -use hir::{db::HirDatabase, HasVisibility, Name, StructKind}; +use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind}; use itertools::Itertools; use crate::{ @@ -27,7 +27,8 @@ pub(crate) fn render_struct_pat( // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields return None; } - let fields_omitted = n_fields - fields.len() > 0; + let fields_omitted = + n_fields - fields.len() > 0 || strukt.attrs(ctx.db()).by_key("non_exhaustive").exists(); let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &fields, fields_omitted)?; @@ -60,7 +61,8 @@ pub(crate) fn render_variant_pat( .filter(|field| field.is_visible_from(ctx.db(), module)) .collect::>(); - let fields_omitted = n_fields - fields.len() > 0; + let fields_omitted = + n_fields - fields.len() > 0 || variant.attrs(ctx.db()).by_key("non_exhaustive").exists(); let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &fields, fields_omitted)?; -- cgit v1.2.3 From 2cd2947bf8cb7abfb06ceb00804447def899d37d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 21 Dec 2020 15:10:27 +0100 Subject: Insert snippet positions after fields names in record patterns --- crates/completion/src/render/pattern.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/completion/src/render') diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index 2d1600105..073d50a11 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs @@ -121,7 +121,7 @@ fn render_record_as_pat( "{name} {{ {}{} }}", fields .enumerate() - .map(|(idx, field)| format!("${{{}:{}}}", idx + 1, field.name(db))) + .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1)) .format(", "), if fields_omitted { ", .." } else { "" }, name = name -- cgit v1.2.3 From 83121efcd577124a992dc8bd304690b36bda2931 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 22 Dec 2020 19:00:38 +0100 Subject: Reduce some more code duplication --- crates/completion/src/render/builder_ext.rs | 1 - crates/completion/src/render/enum_variant.rs | 45 ---------------- crates/completion/src/render/pattern.rs | 80 ++++++++++++++-------------- 3 files changed, 41 insertions(+), 85 deletions(-) (limited to 'crates/completion/src/render') diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs index ce8718bd5..d053a988b 100644 --- a/crates/completion/src/render/builder_ext.rs +++ b/crates/completion/src/render/builder_ext.rs @@ -34,7 +34,6 @@ impl Builder { return false; } if ctx.is_pattern_call { - mark::hit!(dont_duplicate_pattern_parens); return false; } if ctx.is_call { diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 7176fd9b3..732e139ec 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -124,51 +124,6 @@ use Option::*; fn main() -> Option { Some($0) } -"#, - ); - check_edit( - "Some", - r#" -enum Option { Some(T), None } -use Option::*; -fn main(value: Option) { - match value { - Som<|> - } -} -"#, - r#" -enum Option { Some(T), None } -use Option::*; -fn main(value: Option) { - match value { - Some($0) - } -} -"#, - ); - } - - #[test] - fn dont_duplicate_pattern_parens() { - mark::check!(dont_duplicate_pattern_parens); - check_edit( - "Var", - r#" -enum E { Var(i32) } -fn main() { - match E::Var(92) { - E::<|>(92) => (), - } -} -"#, - r#" -enum E { Var(i32) } -fn main() { - match E::Var(92) { - E::Var(92) => (), - } -} "#, ); } diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index 073d50a11..a3b6a3cac 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs @@ -8,6 +8,24 @@ use crate::{ CompletionItemKind, }; +fn visible_fields( + ctx: &RenderContext<'_>, + fields: &[hir::Field], + item: impl HasAttrs, +) -> Option<(Vec, bool)> { + let module = ctx.completion.scope.module()?; + let n_fields = fields.len(); + let fields = fields + .into_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)) +} + pub(crate) fn render_struct_pat( ctx: RenderContext<'_>, strukt: hir::Struct, @@ -15,35 +33,18 @@ pub(crate) fn render_struct_pat( ) -> Option { let _p = profile::span("render_struct_pat"); - let module = ctx.completion.scope.module()?; let fields = strukt.fields(ctx.db()); - let n_fields = fields.len(); - let fields = fields - .into_iter() - .filter(|field| field.is_visible_from(ctx.db(), module)) - .collect::>(); + let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?; - if fields.is_empty() { + if visible_fields.is_empty() { // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields return None; } - let fields_omitted = - n_fields - fields.len() > 0 || strukt.attrs(ctx.db()).by_key("non_exhaustive").exists(); let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); - let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &fields, fields_omitted)?; + let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?; - let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) - .kind(CompletionItemKind::Binding) - .set_documentation(ctx.docs(strukt)) - .set_deprecated(ctx.is_deprecated(strukt)) - .detail(&pat); - if let Some(snippet_cap) = ctx.snippet_cap() { - completion = completion.insert_snippet(snippet_cap, pat); - } else { - completion = completion.insert_text(pat); - } - Some(completion.build()) + Some(build_completion(ctx, name, pat, strukt)) } pub(crate) fn render_variant_pat( @@ -53,31 +54,32 @@ pub(crate) fn render_variant_pat( ) -> Option { let _p = profile::span("render_variant_pat"); - let module = ctx.completion.scope.module()?; let fields = variant.fields(ctx.db()); - let n_fields = fields.len(); - let fields = fields - .into_iter() - .filter(|field| field.is_visible_from(ctx.db(), module)) - .collect::>(); - - let fields_omitted = - n_fields - fields.len() > 0 || variant.attrs(ctx.db()).by_key("non_exhaustive").exists(); + let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); - let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &fields, fields_omitted)?; + let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; - let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) + Some(build_completion(ctx, name, pat, variant)) +} + +fn build_completion( + ctx: RenderContext<'_>, + name: String, + pat: String, + item: impl HasAttrs + Copy, +) -> CompletionItem { + let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) .kind(CompletionItemKind::Binding) - .set_documentation(ctx.docs(variant)) - .set_deprecated(ctx.is_deprecated(variant)) + .set_documentation(ctx.docs(item)) + .set_deprecated(ctx.is_deprecated(item)) .detail(&pat); - if let Some(snippet_cap) = ctx.snippet_cap() { - completion = completion.insert_snippet(snippet_cap, pat); + let completion = if let Some(snippet_cap) = ctx.snippet_cap() { + completion.insert_snippet(snippet_cap, pat) } else { - completion = completion.insert_text(pat); - } - Some(completion.build()) + completion.insert_text(pat) + }; + completion.build() } fn render_pat( -- cgit v1.2.3