diff options
Diffstat (limited to 'crates/completion/src/render/pattern.rs')
-rw-r--r-- | crates/completion/src/render/pattern.rs | 128 |
1 files changed, 128 insertions, 0 deletions
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 @@ | |||
1 | //! Renderer for patterns. | ||
2 | |||
3 | use hir::{db::HirDatabase, HasVisibility, Name, StructKind}; | ||
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; | ||
7 | |||
8 | pub(crate) fn render_struct_pat<'a>( | ||
9 | ctx: RenderContext<'a>, | ||
10 | strukt: hir::Struct, | ||
11 | local_name: Option<Name>, | ||
12 | ) -> Option<CompletionItem> { | ||
13 | let _p = profile::span("render_struct_pat"); | ||
14 | |||
15 | let module = ctx.completion.scope.module()?; | ||
16 | let fields = strukt.fields(ctx.db()); | ||
17 | let n_fields = fields.len(); | ||
18 | let fields = fields | ||
19 | .into_iter() | ||
20 | .filter(|field| field.is_visible_from(ctx.db(), module)) | ||
21 | .collect::<Vec<_>>(); | ||
22 | |||
23 | if fields.is_empty() { | ||
24 | // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields | ||
25 | return None; | ||
26 | } | ||
27 | let fields_omitted = n_fields - fields.len() > 0; | ||
28 | |||
29 | let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); | ||
30 | let mut pat = match strukt.kind(ctx.db()) { | ||
31 | StructKind::Tuple if ctx.snippet_cap().is_some() => { | ||
32 | render_tuple_as_pat(&fields, &name, fields_omitted) | ||
33 | } | ||
34 | StructKind::Record => render_record_as_pat(ctx.db(), &fields, &name, fields_omitted), | ||
35 | _ => return None, | ||
36 | }; | ||
37 | |||
38 | if ctx.completion.is_param { | ||
39 | pat.push(':'); | ||
40 | pat.push(' '); | ||
41 | pat.push_str(&name); | ||
42 | } | ||
43 | if ctx.snippet_cap().is_some() { | ||
44 | pat.push_str("$0"); | ||
45 | } | ||
46 | |||
47 | let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) | ||
48 | .kind(CompletionItemKind::Binding) | ||
49 | .set_documentation(ctx.docs(strukt)) | ||
50 | .set_deprecated(ctx.is_deprecated(strukt)) | ||
51 | .detail(&pat); | ||
52 | if let Some(snippet_cap) = ctx.snippet_cap() { | ||
53 | completion = completion.insert_snippet(snippet_cap, pat); | ||
54 | } else { | ||
55 | completion = completion.insert_text(pat); | ||
56 | } | ||
57 | Some(completion.build()) | ||
58 | } | ||
59 | |||
60 | pub(crate) fn render_variant_pat<'a>( | ||
61 | ctx: RenderContext<'a>, | ||
62 | variant: hir::Variant, | ||
63 | local_name: Option<Name>, | ||
64 | ) -> Option<CompletionItem> { | ||
65 | let _p = profile::span("render_variant_pat"); | ||
66 | |||
67 | let module = ctx.completion.scope.module()?; | ||
68 | let fields = variant.fields(ctx.db()); | ||
69 | let n_fields = fields.len(); | ||
70 | let fields = fields | ||
71 | .into_iter() | ||
72 | .filter(|field| field.is_visible_from(ctx.db(), module)) | ||
73 | .collect::<Vec<_>>(); | ||
74 | |||
75 | let fields_omitted = n_fields - fields.len() > 0; | ||
76 | |||
77 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); | ||
78 | let mut pat = match variant.kind(ctx.db()) { | ||
79 | StructKind::Tuple if ctx.snippet_cap().is_some() => { | ||
80 | render_tuple_as_pat(&fields, &name, fields_omitted) | ||
81 | } | ||
82 | StructKind::Record => render_record_as_pat(ctx.db(), &fields, &name, fields_omitted), | ||
83 | _ => return None, | ||
84 | }; | ||
85 | |||
86 | if ctx.completion.is_param { | ||
87 | pat.push(':'); | ||
88 | pat.push(' '); | ||
89 | pat.push_str(&name); | ||
90 | } | ||
91 | if ctx.snippet_cap().is_some() { | ||
92 | pat.push_str("$0"); | ||
93 | } | ||
94 | let mut completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) | ||
95 | .kind(CompletionItemKind::Binding) | ||
96 | .set_documentation(ctx.docs(variant)) | ||
97 | .set_deprecated(ctx.is_deprecated(variant)) | ||
98 | .detail(&pat); | ||
99 | if let Some(snippet_cap) = ctx.snippet_cap() { | ||
100 | completion = completion.insert_snippet(snippet_cap, pat); | ||
101 | } else { | ||
102 | completion = completion.insert_text(pat); | ||
103 | } | ||
104 | Some(completion.build()) | ||
105 | } | ||
106 | |||
107 | fn render_record_as_pat( | ||
108 | db: &dyn HirDatabase, | ||
109 | fields: &[hir::Field], | ||
110 | name: &str, | ||
111 | fields_omitted: bool, | ||
112 | ) -> String { | ||
113 | format!( | ||
114 | "{name} {{ {}{} }}", | ||
115 | fields.into_iter().map(|field| field.name(db)).format(", "), | ||
116 | if fields_omitted { ", .." } else { "" }, | ||
117 | name = name | ||
118 | ) | ||
119 | } | ||
120 | |||
121 | fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String { | ||
122 | format!( | ||
123 | "{name}({}{})", | ||
124 | fields.into_iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), | ||
125 | if fields_omitted { ", .." } else { "" }, | ||
126 | name = name | ||
127 | ) | ||
128 | } | ||