• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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