• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Code common to structs, unions, and enum variants.
2 
3 use crate::context::CompletionContext;
4 use hir::{db::HirDatabase, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
5 use ide_db::SnippetCap;
6 use itertools::Itertools;
7 use syntax::SmolStr;
8 
9 /// A rendered struct, union, or enum variant, split into fields for actual
10 /// auto-completion (`literal`, using `field: ()`) and display in the
11 /// completions menu (`detail`, using `field: type`).
12 pub(crate) struct RenderedLiteral {
13     pub(crate) literal: String,
14     pub(crate) detail: String,
15 }
16 
17 /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
18 /// the `name` argument for an anonymous type.
render_record_lit( db: &dyn HirDatabase, snippet_cap: Option<SnippetCap>, fields: &[hir::Field], path: &str, ) -> RenderedLiteral19 pub(crate) fn render_record_lit(
20     db: &dyn HirDatabase,
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_string(), detail: path.to_string() };
27     }
28     let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
29         if snippet_cap.is_some() {
30             f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1))
31         } else {
32             f(&format_args!("{}: ()", field.name(db).display(db.upcast())))
33         }
34     });
35 
36     let types = fields.iter().format_with(", ", |field, f| {
37         f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db)))
38     });
39 
40     RenderedLiteral {
41         literal: format!("{path} {{ {completions} }}"),
42         detail: format!("{path} {{ {types} }}"),
43     }
44 }
45 
46 /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
47 /// the `name` argument for an anonymous type.
render_tuple_lit( db: &dyn HirDatabase, snippet_cap: Option<SnippetCap>, fields: &[hir::Field], path: &str, ) -> RenderedLiteral48 pub(crate) fn render_tuple_lit(
49     db: &dyn HirDatabase,
50     snippet_cap: Option<SnippetCap>,
51     fields: &[hir::Field],
52     path: &str,
53 ) -> RenderedLiteral {
54     if snippet_cap.is_none() {
55         return RenderedLiteral { literal: path.to_string(), detail: path.to_string() };
56     }
57     let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
58         if snippet_cap.is_some() {
59             f(&format_args!("${{{}:()}}", idx + 1))
60         } else {
61             f(&format_args!("()"))
62         }
63     });
64 
65     let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));
66 
67     RenderedLiteral {
68         literal: format!("{path}({completions})"),
69         detail: format!("{path}({types})"),
70     }
71 }
72 
73 /// Find all the visible fields in a given list. Returns the list of visible
74 /// fields, plus a boolean for whether the list is comprehensive (contains no
75 /// private fields and its item is not marked `#[non_exhaustive]`).
visible_fields( ctx: &CompletionContext<'_>, fields: &[hir::Field], item: impl HasAttrs + HasCrate + Copy, ) -> Option<(Vec<hir::Field>, bool)>76 pub(crate) fn visible_fields(
77     ctx: &CompletionContext<'_>,
78     fields: &[hir::Field],
79     item: impl HasAttrs + HasCrate + Copy,
80 ) -> Option<(Vec<hir::Field>, bool)> {
81     let module = ctx.module;
82     let n_fields = fields.len();
83     let fields = fields
84         .iter()
85         .filter(|field| field.is_visible_from(ctx.db, module))
86         .copied()
87         .collect::<Vec<_>>();
88     let has_invisible_field = n_fields - fields.len() > 0;
89     let is_foreign_non_exhaustive = item.attrs(ctx.db).by_key("non_exhaustive").exists()
90         && item.krate(ctx.db) != module.krate();
91     let fields_omitted = has_invisible_field || is_foreign_non_exhaustive;
92     Some((fields, fields_omitted))
93 }
94 
95 /// Format a struct, etc. literal option for display in the completions menu.
format_literal_label( name: &str, kind: StructKind, snippet_cap: Option<SnippetCap>, ) -> SmolStr96 pub(crate) fn format_literal_label(
97     name: &str,
98     kind: StructKind,
99     snippet_cap: Option<SnippetCap>,
100 ) -> SmolStr {
101     if snippet_cap.is_none() {
102         return name.into();
103     }
104     match kind {
105         StructKind::Tuple => SmolStr::from_iter([name, "(…)"]),
106         StructKind::Record => SmolStr::from_iter([name, " {…}"]),
107         StructKind::Unit => name.into(),
108     }
109 }
110 
111 /// Format a struct, etc. literal option for lookup used in completions filtering.
format_literal_lookup(name: &str, kind: StructKind) -> SmolStr112 pub(crate) fn format_literal_lookup(name: &str, kind: StructKind) -> SmolStr {
113     match kind {
114         StructKind::Tuple => SmolStr::from_iter([name, "()"]),
115         StructKind::Record => SmolStr::from_iter([name, "{}"]),
116         StructKind::Unit => name.into(),
117     }
118 }
119