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