1use hir::{Name, StructKind, db::HirDatabase};
4use ide_db::{SnippetCap, documentation::HasDocs};
5use itertools::Itertools;
6use syntax::{Edition, SmolStr, ToSmolStr};
7
8use crate::{
9 CompletionItem, CompletionItemKind,
10 context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext},
11 render::{
12 RenderContext,
13 variant::{format_literal_label, format_literal_lookup, visible_fields},
14 },
15};
16
17pub(crate) fn render_struct_pat(
18 ctx: RenderContext<'_>,
19 pattern_ctx: &PatternContext,
20 strukt: hir::Struct,
21 local_name: Option<Name>,
22) -> Option<CompletionItem> {
23 let _p = tracing::info_span!("render_struct_pat").entered();
24
25 let fields = strukt.fields(ctx.db());
26 let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, strukt)?;
27
28 if visible_fields.is_empty() {
29 return None;
31 }
32
33 let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
34 let (name, escaped_name) =
35 (name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr());
36 let kind = strukt.kind(ctx.db());
37 let label = format_literal_label(name, kind, ctx.snippet_cap());
38 let lookup = format_literal_lookup(name, kind);
39 let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
40
41 let db = ctx.db();
42
43 Some(build_completion(ctx, label, lookup, pat, strukt, strukt.ty(db), false))
44}
45
46pub(crate) fn render_variant_pat(
47 ctx: RenderContext<'_>,
48 pattern_ctx: &PatternContext,
49 path_ctx: Option<&PathCompletionCtx<'_>>,
50 variant: hir::Variant,
51 local_name: Option<Name>,
52 path: Option<&hir::ModPath>,
53) -> Option<CompletionItem> {
54 let _p = tracing::info_span!("render_variant_pat").entered();
55
56 let fields = variant.fields(ctx.db());
57 let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
58 let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db());
59
60 let (name, escaped_name) = match path {
61 Some(path) => (
62 path.display_verbatim(ctx.db()).to_smolstr(),
63 path.display(ctx.db(), ctx.completion.edition).to_smolstr(),
64 ),
65 None => {
66 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
67
68 (
69 name.as_str().to_smolstr(),
70 name.display(ctx.db(), ctx.completion.edition).to_smolstr(),
71 )
72 }
73 };
74
75 let (label, lookup, pat) = match path_ctx {
76 Some(PathCompletionCtx { has_call_parens: true, .. }) => {
77 (name.clone(), name, escaped_name.to_string())
78 }
79 _ => {
80 let kind = variant.kind(ctx.db());
81 let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
82 let lookup = format_literal_lookup(name.as_str(), kind);
83 let pat = render_pat(
84 &ctx,
85 pattern_ctx,
86 &escaped_name,
87 kind,
88 &visible_fields,
89 fields_omitted,
90 )?;
91 (label, lookup, pat)
92 }
93 };
94
95 Some(build_completion(
96 ctx,
97 label,
98 lookup,
99 pat,
100 variant,
101 enum_ty,
102 pattern_ctx.missing_variants.contains(&variant),
103 ))
104}
105
106fn build_completion(
107 ctx: RenderContext<'_>,
108 label: SmolStr,
109 lookup: SmolStr,
110 pat: String,
111 def: impl HasDocs,
112 adt_ty: hir::Type<'_>,
113 is_variant_missing: bool,
115) -> CompletionItem {
116 let mut relevance = ctx.completion_relevance();
117
118 if is_variant_missing {
119 relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty);
120 }
121
122 let mut item = CompletionItem::new(
123 CompletionItemKind::Binding,
124 ctx.source_range(),
125 label,
126 ctx.completion.edition,
127 );
128 item.set_documentation(ctx.docs(def))
129 .set_deprecated(ctx.is_deprecated(def))
130 .detail(&pat)
131 .lookup_by(lookup)
132 .set_relevance(relevance);
133 match ctx.snippet_cap() {
134 Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
135 None => item.insert_text(pat),
136 };
137 item.build(ctx.db())
138}
139
140fn render_pat(
141 ctx: &RenderContext<'_>,
142 pattern_ctx: &PatternContext,
143 name: &str,
144 kind: StructKind,
145 fields: &[hir::Field],
146 fields_omitted: bool,
147) -> Option<String> {
148 let mut pat = match kind {
149 StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
150 StructKind::Record => render_record_as_pat(
151 ctx.db(),
152 ctx.snippet_cap(),
153 fields,
154 name,
155 fields_omitted,
156 ctx.completion.edition,
157 ),
158 StructKind::Unit => name.to_owned(),
159 };
160
161 let needs_ascription = matches!(
162 pattern_ctx,
163 PatternContext {
164 param_ctx: Some(ParamContext { kind: ParamKind::Function(_), .. }),
165 has_type_ascription: false,
166 parent_pat: None,
167 ..
168 }
169 );
170 if needs_ascription {
171 pat.push(':');
172 pat.push(' ');
173 pat.push_str(name);
174 }
175 if ctx.snippet_cap().is_some() {
176 pat.push_str("$0");
177 }
178 Some(pat)
179}
180
181fn render_record_as_pat(
182 db: &dyn HirDatabase,
183 snippet_cap: Option<SnippetCap>,
184 fields: &[hir::Field],
185 name: &str,
186 fields_omitted: bool,
187 edition: Edition,
188) -> String {
189 let fields = fields.iter();
190 match snippet_cap {
191 Some(_) => {
192 format!(
193 "{name} {{ {}{} }}",
194 fields.enumerate().format_with(", ", |(idx, field), f| {
195 f(&format_args!("{}${}", field.name(db).display(db, edition), idx + 1))
196 }),
197 if fields_omitted { ", .." } else { "" },
198 name = name
199 )
200 }
201 None => {
202 format!(
203 "{name} {{ {}{} }}",
204 fields.map(|field| field.name(db).display_no_db(edition).to_smolstr()).format(", "),
205 if fields_omitted { ", .." } else { "" },
206 name = name
207 )
208 }
209 }
210}
211
212fn render_tuple_as_pat(
213 snippet_cap: Option<SnippetCap>,
214 fields: &[hir::Field],
215 name: &str,
216 fields_omitted: bool,
217) -> String {
218 let fields = fields.iter();
219 match snippet_cap {
220 Some(_) => {
221 format!(
222 "{name}({}{})",
223 fields
224 .enumerate()
225 .format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }),
226 if fields_omitted { ", .." } else { "" },
227 name = name
228 )
229 }
230 None => {
231 format!(
232 "{name}({}{})",
233 fields.enumerate().map(|(idx, _)| idx).format(", "),
234 if fields_omitted { ", .." } else { "" },
235 name = name
236 )
237 }
238 }
239}