1 //! Renderer for `enum` variants.
2
3 use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
4 use ide_db::SymbolKind;
5
6 use crate::{
7 context::{CompletionContext, PathCompletionCtx, PathKind},
8 item::{Builder, CompletionItem},
9 render::{
10 compute_type_match,
11 variant::{
12 format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit,
13 visible_fields, RenderedLiteral,
14 },
15 RenderContext,
16 },
17 CompletionItemKind, CompletionRelevance,
18 };
19
render_variant_lit( ctx: RenderContext<'_>, path_ctx: &PathCompletionCtx, local_name: Option<hir::Name>, variant: hir::Variant, path: Option<hir::ModPath>, ) -> Option<Builder>20 pub(crate) fn render_variant_lit(
21 ctx: RenderContext<'_>,
22 path_ctx: &PathCompletionCtx,
23 local_name: Option<hir::Name>,
24 variant: hir::Variant,
25 path: Option<hir::ModPath>,
26 ) -> Option<Builder> {
27 let _p = profile::span("render_enum_variant");
28 let db = ctx.db();
29
30 let name = local_name.unwrap_or_else(|| variant.name(db));
31 render(ctx, path_ctx, Variant::EnumVariant(variant), name, path)
32 }
33
render_struct_literal( ctx: RenderContext<'_>, path_ctx: &PathCompletionCtx, strukt: hir::Struct, path: Option<hir::ModPath>, local_name: Option<hir::Name>, ) -> Option<Builder>34 pub(crate) fn render_struct_literal(
35 ctx: RenderContext<'_>,
36 path_ctx: &PathCompletionCtx,
37 strukt: hir::Struct,
38 path: Option<hir::ModPath>,
39 local_name: Option<hir::Name>,
40 ) -> Option<Builder> {
41 let _p = profile::span("render_struct_literal");
42 let db = ctx.db();
43
44 let name = local_name.unwrap_or_else(|| strukt.name(db));
45 render(ctx, path_ctx, Variant::Struct(strukt), name, path)
46 }
47
48 fn render(
49 ctx @ RenderContext { completion, .. }: RenderContext<'_>,
50 path_ctx: &PathCompletionCtx,
51 thing: Variant,
52 name: hir::Name,
53 path: Option<hir::ModPath>,
54 ) -> Option<Builder> {
55 let db = completion.db;
56 let mut kind = thing.kind(db);
57 let should_add_parens = match &path_ctx {
58 PathCompletionCtx { has_call_parens: true, .. } => false,
59 PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. } => false,
60 _ => true,
61 };
62
63 let fields = thing.fields(completion)?;
64 let (qualified_name, short_qualified_name, qualified) = match path {
65 Some(path) => {
66 let short = hir::ModPath::from_segments(
67 hir::PathKind::Plain,
68 path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
69 );
70 (path, short, true)
71 }
72 None => (name.clone().into(), name.into(), false),
73 };
74 let (qualified_name, escaped_qualified_name) = (
75 qualified_name.unescaped().display(ctx.db()).to_string(),
76 qualified_name.display(ctx.db()).to_string(),
77 );
78 let snippet_cap = ctx.snippet_cap();
79
80 let mut rendered = match kind {
81 StructKind::Tuple if should_add_parens => {
82 render_tuple_lit(db, snippet_cap, &fields, &escaped_qualified_name)
83 }
84 StructKind::Record if should_add_parens => {
85 render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name)
86 }
87 _ => RenderedLiteral {
88 literal: escaped_qualified_name.clone(),
89 detail: escaped_qualified_name,
90 },
91 };
92
93 if snippet_cap.is_some() {
94 rendered.literal.push_str("$0");
95 }
96
97 // only show name in label if not adding parens
98 if !should_add_parens {
99 kind = StructKind::Unit;
100 }
101 let label = format_literal_label(&qualified_name, kind, snippet_cap);
102 let lookup = if qualified {
103 format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind)
104 } else {
105 format_literal_lookup(&qualified_name, kind)
106 };
107
108 let mut item = CompletionItem::new(
109 CompletionItemKind::SymbolKind(thing.symbol_kind()),
110 ctx.source_range(),
111 label,
112 );
113
114 item.lookup_by(lookup);
115 item.detail(rendered.detail);
116
117 match snippet_cap {
118 Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal).trigger_call_info(),
119 None => item.insert_text(rendered.literal),
120 };
121
122 item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx));
123
124 let ty = thing.ty(db);
125 item.set_relevance(CompletionRelevance {
126 type_match: compute_type_match(ctx.completion, &ty),
127 ..ctx.completion_relevance()
128 });
129
130 super::path_ref_match(completion, path_ctx, &ty, &mut item);
131
132 if let Some(import_to_add) = ctx.import_to_add {
133 item.add_import(import_to_add);
134 }
135 Some(item)
136 }
137
138 #[derive(Clone, Copy)]
139 enum Variant {
140 Struct(hir::Struct),
141 EnumVariant(hir::Variant),
142 }
143
144 impl Variant {
fields(self, ctx: &CompletionContext<'_>) -> Option<Vec<hir::Field>>145 fn fields(self, ctx: &CompletionContext<'_>) -> Option<Vec<hir::Field>> {
146 let fields = match self {
147 Variant::Struct(it) => it.fields(ctx.db),
148 Variant::EnumVariant(it) => it.fields(ctx.db),
149 };
150 let (visible_fields, fields_omitted) = match self {
151 Variant::Struct(it) => visible_fields(ctx, &fields, it)?,
152 Variant::EnumVariant(it) => visible_fields(ctx, &fields, it)?,
153 };
154 if !fields_omitted {
155 Some(visible_fields)
156 } else {
157 None
158 }
159 }
160
kind(self, db: &dyn HirDatabase) -> StructKind161 fn kind(self, db: &dyn HirDatabase) -> StructKind {
162 match self {
163 Variant::Struct(it) => it.kind(db),
164 Variant::EnumVariant(it) => it.kind(db),
165 }
166 }
167
symbol_kind(self) -> SymbolKind168 fn symbol_kind(self) -> SymbolKind {
169 match self {
170 Variant::Struct(_) => SymbolKind::Struct,
171 Variant::EnumVariant(_) => SymbolKind::Variant,
172 }
173 }
174
docs(self, db: &dyn HirDatabase) -> Option<Documentation>175 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
176 match self {
177 Variant::Struct(it) => it.docs(db),
178 Variant::EnumVariant(it) => it.docs(db),
179 }
180 }
181
is_deprecated(self, ctx: &RenderContext<'_>) -> bool182 fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
183 match self {
184 Variant::Struct(it) => ctx.is_deprecated(it),
185 Variant::EnumVariant(it) => ctx.is_deprecated(it),
186 }
187 }
188
ty(self, db: &dyn HirDatabase) -> hir::Type189 fn ty(self, db: &dyn HirDatabase) -> hir::Type {
190 match self {
191 Variant::Struct(it) => it.ty(db),
192 Variant::EnumVariant(it) => it.parent_enum(db).ty(db),
193 }
194 }
195 }
196