]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/render/enum_variant.rs
rename completion -> ide_completion
[rust.git] / crates / ide_completion / src / render / enum_variant.rs
1 //! Renderer for `enum` variants.
2
3 use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4 use ide_db::SymbolKind;
5 use itertools::Itertools;
6 use test_utils::mark;
7
8 use crate::{
9     item::{CompletionItem, CompletionKind, ImportEdit},
10     render::{builder_ext::Params, RenderContext},
11 };
12
13 pub(crate) fn render_variant<'a>(
14     ctx: RenderContext<'a>,
15     import_to_add: Option<ImportEdit>,
16     local_name: Option<String>,
17     variant: hir::Variant,
18     path: Option<ModPath>,
19 ) -> CompletionItem {
20     let _p = profile::span("render_enum_variant");
21     EnumRender::new(ctx, local_name, variant, path).render(import_to_add)
22 }
23
24 #[derive(Debug)]
25 struct EnumRender<'a> {
26     ctx: RenderContext<'a>,
27     name: String,
28     variant: hir::Variant,
29     path: Option<ModPath>,
30     qualified_name: String,
31     short_qualified_name: String,
32     variant_kind: StructKind,
33 }
34
35 impl<'a> EnumRender<'a> {
36     fn new(
37         ctx: RenderContext<'a>,
38         local_name: Option<String>,
39         variant: hir::Variant,
40         path: Option<ModPath>,
41     ) -> EnumRender<'a> {
42         let name = local_name.unwrap_or_else(|| variant.name(ctx.db()).to_string());
43         let variant_kind = variant.kind(ctx.db());
44
45         let (qualified_name, short_qualified_name) = match &path {
46             Some(path) => {
47                 let full = path.to_string();
48                 let segments = path.segments();
49                 let short = segments[segments.len().saturating_sub(2)..].iter().join("::");
50                 (full, short)
51             }
52             None => (name.to_string(), name.to_string()),
53         };
54
55         EnumRender { ctx, name, variant, path, qualified_name, short_qualified_name, variant_kind }
56     }
57
58     fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59         let mut builder = CompletionItem::new(
60             CompletionKind::Reference,
61             self.ctx.source_range(),
62             self.qualified_name.clone(),
63         )
64         .kind(SymbolKind::Variant)
65         .set_documentation(self.variant.docs(self.ctx.db()))
66         .set_deprecated(self.ctx.is_deprecated(self.variant))
67         .add_import(import_to_add)
68         .detail(self.detail());
69
70         if self.variant_kind == StructKind::Tuple {
71             mark::hit!(inserts_parens_for_tuple_enums);
72             let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
73             builder =
74                 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
75         } else if self.path.is_some() {
76             builder = builder.lookup_by(self.short_qualified_name);
77         }
78
79         builder.build()
80     }
81
82     fn detail(&self) -> String {
83         let detail_types = self
84             .variant
85             .fields(self.ctx.db())
86             .into_iter()
87             .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db())));
88
89         match self.variant_kind {
90             StructKind::Tuple | StructKind::Unit => format!(
91                 "({})",
92                 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
93             ),
94             StructKind::Record => format!(
95                 "{{ {} }}",
96                 detail_types
97                     .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
98                     .format(", ")
99             ),
100         }
101     }
102 }
103
104 #[cfg(test)]
105 mod tests {
106     use test_utils::mark;
107
108     use crate::test_utils::check_edit;
109
110     #[test]
111     fn inserts_parens_for_tuple_enums() {
112         mark::check!(inserts_parens_for_tuple_enums);
113         check_edit(
114             "Some",
115             r#"
116 enum Option<T> { Some(T), None }
117 use Option::*;
118 fn main() -> Option<i32> {
119     Som$0
120 }
121 "#,
122             r#"
123 enum Option<T> { Some(T), None }
124 use Option::*;
125 fn main() -> Option<i32> {
126     Some($0)
127 }
128 "#,
129         );
130     }
131 }