]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/render/struct_literal.rs
Replace some String usages with SmolStr in completions
[rust.git] / crates / ide_completion / src / render / struct_literal.rs
1 //! Renderer for `struct` literal.
2
3 use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
4 use ide_db::helpers::SnippetCap;
5 use itertools::Itertools;
6 use syntax::SmolStr;
7
8 use crate::{render::RenderContext, CompletionItem, CompletionItemKind};
9
10 pub(crate) fn render_struct_literal(
11     ctx: RenderContext<'_>,
12     strukt: hir::Struct,
13     local_name: Option<Name>,
14 ) -> Option<CompletionItem> {
15     let _p = profile::span("render_struct_literal");
16
17     let fields = strukt.fields(ctx.db());
18     let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
19
20     if fields_omitted {
21         // If some fields are private you can't make `struct` literal.
22         return None;
23     }
24
25     let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
26     let literal = render_literal(&ctx, &name, strukt.kind(ctx.db()), &visible_fields)?;
27
28     Some(build_completion(ctx, name, literal, strukt))
29 }
30
31 fn build_completion(
32     ctx: RenderContext<'_>,
33     name: SmolStr,
34     literal: String,
35     def: impl HasAttrs + Copy,
36 ) -> CompletionItem {
37     let mut item = CompletionItem::new(
38         CompletionItemKind::Snippet,
39         ctx.source_range(),
40         SmolStr::from_iter([&name, " {…}"]),
41     );
42     item.set_documentation(ctx.docs(def)).set_deprecated(ctx.is_deprecated(def)).detail(&literal);
43     match ctx.snippet_cap() {
44         Some(snippet_cap) => item.insert_snippet(snippet_cap, literal),
45         None => item.insert_text(literal),
46     };
47     item.build()
48 }
49
50 fn render_literal(
51     ctx: &RenderContext<'_>,
52     name: &str,
53     kind: StructKind,
54     fields: &[hir::Field],
55 ) -> Option<String> {
56     let mut literal = match kind {
57         StructKind::Tuple if ctx.snippet_cap().is_some() => render_tuple_as_literal(fields, name),
58         StructKind::Record => render_record_as_literal(ctx.db(), ctx.snippet_cap(), fields, name),
59         _ => return None,
60     };
61
62     if ctx.snippet_cap().is_some() {
63         literal.push_str("$0");
64     }
65     Some(literal)
66 }
67
68 fn render_record_as_literal(
69     db: &dyn HirDatabase,
70     snippet_cap: Option<SnippetCap>,
71     fields: &[hir::Field],
72     name: &str,
73 ) -> String {
74     let fields = fields.iter();
75     if snippet_cap.is_some() {
76         format!(
77             "{name} {{ {} }}",
78             fields
79                 .enumerate()
80                 .map(|(idx, field)| format!("{}: ${{{}:()}}", field.name(db), idx + 1))
81                 .format(", "),
82             name = name
83         )
84     } else {
85         format!(
86             "{name} {{ {} }}",
87             fields.map(|field| format!("{}: ()", field.name(db))).format(", "),
88             name = name
89         )
90     }
91 }
92
93 fn render_tuple_as_literal(fields: &[hir::Field], name: &str) -> String {
94     format!(
95         "{name}({})",
96         fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
97         name = name
98     )
99 }
100
101 fn visible_fields(
102     ctx: &RenderContext<'_>,
103     fields: &[hir::Field],
104     item: impl HasAttrs,
105 ) -> Option<(Vec<hir::Field>, bool)> {
106     let module = ctx.completion.scope.module()?;
107     let n_fields = fields.len();
108     let fields = fields
109         .iter()
110         .filter(|field| field.is_visible_from(ctx.db(), module))
111         .copied()
112         .collect::<Vec<_>>();
113
114     let fields_omitted =
115         n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
116     Some((fields, fields_omitted))
117 }