]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide-completion / src / render / pattern.rs
CommitLineData
064997fb
FG
1//! Renderer for patterns.
2
3use hir::{db::HirDatabase, HasAttrs, Name, StructKind};
4use ide_db::SnippetCap;
5use itertools::Itertools;
6use syntax::SmolStr;
7
8use crate::{
9 context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext},
10 render::{
f2b60f7d 11 variant::{format_literal_label, format_literal_lookup, visible_fields},
064997fb
FG
12 RenderContext,
13 },
14 CompletionItem, CompletionItemKind,
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 = profile::span("render_struct_pat");
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 // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
30 return None;
31 }
32
33 let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
f2b60f7d 34 let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
064997fb 35 let kind = strukt.kind(ctx.db());
9c376795 36 let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
f2b60f7d 37 let lookup = format_literal_lookup(name.as_str(), kind);
064997fb
FG
38 let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
39
353b0b11
FG
40 let db = ctx.db();
41
42 Some(build_completion(ctx, label, lookup, pat, strukt, strukt.ty(db), false))
064997fb
FG
43}
44
45pub(crate) fn render_variant_pat(
46 ctx: RenderContext<'_>,
47 pattern_ctx: &PatternContext,
48 path_ctx: Option<&PathCompletionCtx>,
49 variant: hir::Variant,
50 local_name: Option<Name>,
51 path: Option<&hir::ModPath>,
52) -> Option<CompletionItem> {
53 let _p = profile::span("render_variant_pat");
54
55 let fields = variant.fields(ctx.db());
56 let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
353b0b11 57 let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db());
064997fb
FG
58
59 let (name, escaped_name) = match path {
f2b60f7d 60 Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
064997fb
FG
61 None => {
62 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
f2b60f7d 63 (name.unescaped().to_smol_str(), name.to_smol_str())
064997fb
FG
64 }
65 };
66
f2b60f7d
FG
67 let (label, lookup, pat) = match path_ctx {
68 Some(PathCompletionCtx { has_call_parens: true, .. }) => {
69 (name.clone(), name, escaped_name.to_string())
70 }
064997fb
FG
71 _ => {
72 let kind = variant.kind(ctx.db());
9c376795 73 let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
f2b60f7d 74 let lookup = format_literal_lookup(name.as_str(), kind);
064997fb
FG
75 let pat = render_pat(
76 &ctx,
77 pattern_ctx,
78 &escaped_name,
79 kind,
80 &visible_fields,
81 fields_omitted,
82 )?;
f2b60f7d 83 (label, lookup, pat)
064997fb
FG
84 }
85 };
86
353b0b11
FG
87 Some(build_completion(
88 ctx,
89 label,
90 lookup,
91 pat,
92 variant,
93 enum_ty,
94 pattern_ctx.missing_variants.contains(&variant),
95 ))
064997fb
FG
96}
97
98fn build_completion(
99 ctx: RenderContext<'_>,
100 label: SmolStr,
f2b60f7d 101 lookup: SmolStr,
064997fb
FG
102 pat: String,
103 def: impl HasAttrs + Copy,
353b0b11
FG
104 adt_ty: hir::Type,
105 // Missing in context of match statement completions
106 is_variant_missing: bool,
064997fb 107) -> CompletionItem {
353b0b11
FG
108 let mut relevance = ctx.completion_relevance();
109
110 if is_variant_missing {
111 relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty);
112 }
113
064997fb
FG
114 let mut item = CompletionItem::new(CompletionItemKind::Binding, ctx.source_range(), label);
115 item.set_documentation(ctx.docs(def))
116 .set_deprecated(ctx.is_deprecated(def))
117 .detail(&pat)
f2b60f7d 118 .lookup_by(lookup)
353b0b11 119 .set_relevance(relevance);
064997fb
FG
120 match ctx.snippet_cap() {
121 Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
122 None => item.insert_text(pat),
123 };
124 item.build()
125}
126
127fn render_pat(
128 ctx: &RenderContext<'_>,
129 pattern_ctx: &PatternContext,
130 name: &str,
131 kind: StructKind,
132 fields: &[hir::Field],
133 fields_omitted: bool,
134) -> Option<String> {
135 let mut pat = match kind {
136 StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
137 StructKind::Record => {
138 render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
139 }
140 StructKind::Unit => name.to_string(),
141 };
142
143 let needs_ascription = matches!(
144 pattern_ctx,
145 PatternContext {
146 param_ctx: Some(ParamContext { kind: ParamKind::Function(_), .. }),
147 has_type_ascription: false,
148 ..
149 }
150 );
151 if needs_ascription {
152 pat.push(':');
153 pat.push(' ');
154 pat.push_str(name);
155 }
156 if ctx.snippet_cap().is_some() {
157 pat.push_str("$0");
158 }
159 Some(pat)
160}
161
162fn render_record_as_pat(
163 db: &dyn HirDatabase,
164 snippet_cap: Option<SnippetCap>,
165 fields: &[hir::Field],
166 name: &str,
167 fields_omitted: bool,
168) -> String {
169 let fields = fields.iter();
170 match snippet_cap {
171 Some(_) => {
172 format!(
173 "{name} {{ {}{} }}",
174 fields.enumerate().format_with(", ", |(idx, field), f| {
f2b60f7d 175 f(&format_args!("{}${}", field.name(db), idx + 1))
064997fb
FG
176 }),
177 if fields_omitted { ", .." } else { "" },
178 name = name
179 )
180 }
181 None => {
182 format!(
183 "{name} {{ {}{} }}",
f2b60f7d 184 fields.map(|field| field.name(db).to_smol_str()).format(", "),
064997fb
FG
185 if fields_omitted { ", .." } else { "" },
186 name = name
187 )
188 }
189 }
190}
191
192fn render_tuple_as_pat(
193 snippet_cap: Option<SnippetCap>,
194 fields: &[hir::Field],
195 name: &str,
196 fields_omitted: bool,
197) -> String {
198 let fields = fields.iter();
199 match snippet_cap {
200 Some(_) => {
201 format!(
202 "{name}({}{})",
203 fields
204 .enumerate()
205 .format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }),
206 if fields_omitted { ", .." } else { "" },
207 name = name
208 )
209 }
210 None => {
211 format!(
212 "{name}({}{})",
213 fields.enumerate().map(|(idx, _)| idx).format(", "),
214 if fields_omitted { ", .." } else { "" },
215 name = name
216 )
217 }
218 }
219}