ide_completion/render/
variant.rs1use crate::context::CompletionContext;
4use hir::{HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
5use ide_db::SnippetCap;
6use itertools::Itertools;
7use syntax::SmolStr;
8
9pub(crate) struct RenderedLiteral {
13 pub(crate) literal: String,
14 pub(crate) detail: String,
15}
16
17pub(crate) fn render_record_lit(
20 ctx: &CompletionContext<'_>,
21 snippet_cap: Option<SnippetCap>,
22 fields: &[hir::Field],
23 path: &str,
24) -> RenderedLiteral {
25 if snippet_cap.is_none() {
26 return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
27 }
28 let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
29 let mut fmt_field = |fill, tab| {
30 let field_name = field.name(ctx.db);
31
32 if let Some(local) = ctx.locals.get(&field_name)
33 && local
34 .ty(ctx.db)
35 .could_unify_with_deeply(ctx.db, &field.ty(ctx.db).to_type(ctx.db))
36 {
37 f(&format_args!("{}{tab}", field_name.display(ctx.db, ctx.edition)))
38 } else {
39 f(&format_args!("{}: {fill}", field_name.display(ctx.db, ctx.edition)))
40 }
41 };
42 if snippet_cap.is_some() {
43 fmt_field(format_args!("${{{}:()}}", idx + 1), format_args!("${}", idx + 1))
44 } else {
45 fmt_field(format_args!("()"), format_args!(""))
46 }
47 });
48
49 let types = fields.iter().format_with(", ", |field, f| {
50 f(&format_args!(
51 "{}: {}",
52 field.name(ctx.db).display(ctx.db, ctx.edition),
53 field.ty(ctx.db).display(ctx.db, ctx.display_target)
54 ))
55 });
56
57 RenderedLiteral {
58 literal: format!("{path} {{ {completions} }}"),
59 detail: format!("{path} {{ {types} }}"),
60 }
61}
62
63pub(crate) fn render_tuple_lit(
66 ctx: &CompletionContext<'_>,
67 snippet_cap: Option<SnippetCap>,
68 fields: &[hir::Field],
69 path: &str,
70) -> RenderedLiteral {
71 if snippet_cap.is_none() {
72 return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
73 }
74 let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
75 if snippet_cap.is_some() {
76 f(&format_args!("${{{}:()}}", idx + 1))
77 } else {
78 f(&format_args!("()"))
79 }
80 });
81
82 let types = fields
83 .iter()
84 .format_with(", ", |field, f| f(&field.ty(ctx.db).display(ctx.db, ctx.display_target)));
85
86 RenderedLiteral {
87 literal: format!("{path}({completions})"),
88 detail: format!("{path}({types})"),
89 }
90}
91
92pub(crate) fn visible_fields(
96 ctx: &CompletionContext<'_>,
97 fields: &[hir::Field],
98 item: impl HasAttrs + HasCrate + Copy,
99) -> Option<(Vec<hir::Field>, bool)> {
100 let module = ctx.module;
101 let n_fields = fields.len();
102 let fields = fields
103 .iter()
104 .filter(|field| field.is_visible_from(ctx.db, module))
105 .copied()
106 .collect::<Vec<_>>();
107 let has_invisible_field = n_fields - fields.len() > 0;
108 let is_foreign_non_exhaustive =
109 item.attrs(ctx.db).is_non_exhaustive() && item.krate(ctx.db) != module.krate(ctx.db);
110 let fields_omitted = has_invisible_field || is_foreign_non_exhaustive;
111 Some((fields, fields_omitted))
112}
113
114pub(crate) fn format_literal_label(
116 name: &str,
117 kind: StructKind,
118 snippet_cap: Option<SnippetCap>,
119) -> SmolStr {
120 if snippet_cap.is_none() {
121 return name.into();
122 }
123 match kind {
124 StructKind::Tuple => SmolStr::from_iter([name, "(…)"]),
125 StructKind::Record => SmolStr::from_iter([name, " {…}"]),
126 StructKind::Unit => name.into(),
127 }
128}
129
130pub(crate) fn format_literal_lookup(name: &str, kind: StructKind) -> SmolStr {
132 match kind {
133 StructKind::Tuple => SmolStr::from_iter([name, "()"]),
134 StructKind::Record => SmolStr::from_iter([name, "{}"]),
135 StructKind::Unit => name.into(),
136 }
137}