]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
New upstream version 1.67.1+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
FG
35 let kind = strukt.kind(ctx.db());
36 let label = format_literal_label(name.as_str(), kind);
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
f2b60f7d 40 Some(build_completion(ctx, label, lookup, pat, strukt))
064997fb
FG
41}
42
43pub(crate) fn render_variant_pat(
44 ctx: RenderContext<'_>,
45 pattern_ctx: &PatternContext,
46 path_ctx: Option<&PathCompletionCtx>,
47 variant: hir::Variant,
48 local_name: Option<Name>,
49 path: Option<&hir::ModPath>,
50) -> Option<CompletionItem> {
51 let _p = profile::span("render_variant_pat");
52
53 let fields = variant.fields(ctx.db());
54 let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
55
56 let (name, escaped_name) = match path {
f2b60f7d 57 Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
064997fb
FG
58 None => {
59 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
f2b60f7d 60 (name.unescaped().to_smol_str(), name.to_smol_str())
064997fb
FG
61 }
62 };
63
f2b60f7d
FG
64 let (label, lookup, pat) = match path_ctx {
65 Some(PathCompletionCtx { has_call_parens: true, .. }) => {
66 (name.clone(), name, escaped_name.to_string())
67 }
064997fb
FG
68 _ => {
69 let kind = variant.kind(ctx.db());
70 let label = format_literal_label(name.as_str(), kind);
f2b60f7d 71 let lookup = format_literal_lookup(name.as_str(), kind);
064997fb
FG
72 let pat = render_pat(
73 &ctx,
74 pattern_ctx,
75 &escaped_name,
76 kind,
77 &visible_fields,
78 fields_omitted,
79 )?;
f2b60f7d 80 (label, lookup, pat)
064997fb
FG
81 }
82 };
83
f2b60f7d 84 Some(build_completion(ctx, label, lookup, pat, variant))
064997fb
FG
85}
86
87fn build_completion(
88 ctx: RenderContext<'_>,
89 label: SmolStr,
f2b60f7d 90 lookup: SmolStr,
064997fb
FG
91 pat: String,
92 def: impl HasAttrs + Copy,
93) -> CompletionItem {
94 let mut item = CompletionItem::new(CompletionItemKind::Binding, ctx.source_range(), label);
95 item.set_documentation(ctx.docs(def))
96 .set_deprecated(ctx.is_deprecated(def))
97 .detail(&pat)
f2b60f7d 98 .lookup_by(lookup)
064997fb
FG
99 .set_relevance(ctx.completion_relevance());
100 match ctx.snippet_cap() {
101 Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
102 None => item.insert_text(pat),
103 };
104 item.build()
105}
106
107fn render_pat(
108 ctx: &RenderContext<'_>,
109 pattern_ctx: &PatternContext,
110 name: &str,
111 kind: StructKind,
112 fields: &[hir::Field],
113 fields_omitted: bool,
114) -> Option<String> {
115 let mut pat = match kind {
116 StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
117 StructKind::Record => {
118 render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
119 }
120 StructKind::Unit => name.to_string(),
121 };
122
123 let needs_ascription = matches!(
124 pattern_ctx,
125 PatternContext {
126 param_ctx: Some(ParamContext { kind: ParamKind::Function(_), .. }),
127 has_type_ascription: false,
128 ..
129 }
130 );
131 if needs_ascription {
132 pat.push(':');
133 pat.push(' ');
134 pat.push_str(name);
135 }
136 if ctx.snippet_cap().is_some() {
137 pat.push_str("$0");
138 }
139 Some(pat)
140}
141
142fn render_record_as_pat(
143 db: &dyn HirDatabase,
144 snippet_cap: Option<SnippetCap>,
145 fields: &[hir::Field],
146 name: &str,
147 fields_omitted: bool,
148) -> String {
149 let fields = fields.iter();
150 match snippet_cap {
151 Some(_) => {
152 format!(
153 "{name} {{ {}{} }}",
154 fields.enumerate().format_with(", ", |(idx, field), f| {
f2b60f7d 155 f(&format_args!("{}${}", field.name(db), idx + 1))
064997fb
FG
156 }),
157 if fields_omitted { ", .." } else { "" },
158 name = name
159 )
160 }
161 None => {
162 format!(
163 "{name} {{ {}{} }}",
f2b60f7d 164 fields.map(|field| field.name(db).to_smol_str()).format(", "),
064997fb
FG
165 if fields_omitted { ", .." } else { "" },
166 name = name
167 )
168 }
169 }
170}
171
172fn render_tuple_as_pat(
173 snippet_cap: Option<SnippetCap>,
174 fields: &[hir::Field],
175 name: &str,
176 fields_omitted: bool,
177) -> String {
178 let fields = fields.iter();
179 match snippet_cap {
180 Some(_) => {
181 format!(
182 "{name}({}{})",
183 fields
184 .enumerate()
185 .format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }),
186 if fields_omitted { ", .." } else { "" },
187 name = name
188 )
189 }
190 None => {
191 format!(
192 "{name}({}{})",
193 fields.enumerate().map(|(idx, _)| idx).format(", "),
194 if fields_omitted { ", .." } else { "" },
195 name = name
196 )
197 }
198 }
199}