aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/render
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/render')
-rw-r--r--crates/completion/src/render/builder_ext.rs94
-rw-r--r--crates/completion/src/render/const_.rs55
-rw-r--r--crates/completion/src/render/enum_variant.rs180
-rw-r--r--crates/completion/src/render/function.rs303
-rw-r--r--crates/completion/src/render/macro_.rs216
-rw-r--r--crates/completion/src/render/type_alias.rs55
6 files changed, 903 insertions, 0 deletions
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs
new file mode 100644
index 000000000..37b0d0459
--- /dev/null
+++ b/crates/completion/src/render/builder_ext.rs
@@ -0,0 +1,94 @@
1//! Extensions for `Builder` structure required for item rendering.
2
3use itertools::Itertools;
4use test_utils::mark;
5
6use crate::{item::Builder, CompletionContext};
7
8pub(super) enum Params {
9 Named(Vec<String>),
10 Anonymous(usize),
11}
12
13impl Params {
14 pub(super) fn len(&self) -> usize {
15 match self {
16 Params::Named(xs) => xs.len(),
17 Params::Anonymous(len) => *len,
18 }
19 }
20
21 pub(super) fn is_empty(&self) -> bool {
22 self.len() == 0
23 }
24}
25
26impl Builder {
27 pub(super) fn should_add_parems(&self, ctx: &CompletionContext) -> bool {
28 if !ctx.config.add_call_parenthesis {
29 return false;
30 }
31 if ctx.use_item_syntax.is_some() {
32 mark::hit!(no_parens_in_use_item);
33 return false;
34 }
35 if ctx.is_pattern_call {
36 mark::hit!(dont_duplicate_pattern_parens);
37 return false;
38 }
39 if ctx.is_call {
40 return false;
41 }
42
43 // Don't add parentheses if the expected type is some function reference.
44 if let Some(ty) = &ctx.expected_type {
45 if ty.is_fn() {
46 mark::hit!(no_call_parens_if_fn_ptr_needed);
47 return false;
48 }
49 }
50
51 // Nothing prevents us from adding parentheses
52 true
53 }
54
55 pub(super) fn add_call_parens(
56 mut self,
57 ctx: &CompletionContext,
58 name: String,
59 params: Params,
60 ) -> Builder {
61 if !self.should_add_parems(ctx) {
62 return self;
63 }
64
65 let cap = match ctx.config.snippet_cap {
66 Some(it) => it,
67 None => return self,
68 };
69 // If not an import, add parenthesis automatically.
70 mark::hit!(inserts_parens_for_function_calls);
71
72 let (snippet, label) = if params.is_empty() {
73 (format!("{}()$0", name), format!("{}()", name))
74 } else {
75 self = self.trigger_call_info();
76 let snippet = match (ctx.config.add_call_argument_snippets, params) {
77 (true, Params::Named(params)) => {
78 let function_params_snippet =
79 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
80 f(&format_args!("${{{}:{}}}", index + 1, param_name))
81 });
82 format!("{}({})$0", name, function_params_snippet)
83 }
84 _ => {
85 mark::hit!(suppress_arg_snippets);
86 format!("{}($0)", name)
87 }
88 };
89
90 (snippet, format!("{}(…)", name))
91 };
92 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
93 }
94}
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
new file mode 100644
index 000000000..039bdabc0
--- /dev/null
+++ b/crates/completion/src/render/const_.rs
@@ -0,0 +1,55 @@
1//! Renderer for `const` fields.
2
3use hir::HasSource;
4use syntax::{
5 ast::{Const, NameOwner},
6 display::const_label,
7};
8
9use crate::{
10 item::{CompletionItem, CompletionItemKind, CompletionKind},
11 render::RenderContext,
12};
13
14pub(crate) fn render_const<'a>(
15 ctx: RenderContext<'a>,
16 const_: hir::Const,
17) -> Option<CompletionItem> {
18 ConstRender::new(ctx, const_).render()
19}
20
21#[derive(Debug)]
22struct ConstRender<'a> {
23 ctx: RenderContext<'a>,
24 const_: hir::Const,
25 ast_node: Const,
26}
27
28impl<'a> ConstRender<'a> {
29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> ConstRender<'a> {
30 let ast_node = const_.source(ctx.db()).value;
31 ConstRender { ctx, const_, ast_node }
32 }
33
34 fn render(self) -> Option<CompletionItem> {
35 let name = self.name()?;
36 let detail = self.detail();
37
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::Const)
40 .set_documentation(self.ctx.docs(self.const_))
41 .set_deprecated(self.ctx.is_deprecated(self.const_))
42 .detail(detail)
43 .build();
44
45 Some(item)
46 }
47
48 fn name(&self) -> Option<String> {
49 self.ast_node.name().map(|name| name.text().to_string())
50 }
51
52 fn detail(&self) -> String {
53 const_label(&self.ast_node)
54 }
55}
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
new file mode 100644
index 000000000..fd412ed0e
--- /dev/null
+++ b/crates/completion/src/render/enum_variant.rs
@@ -0,0 +1,180 @@
1//! Renderer for `enum` variants.
2
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use itertools::Itertools;
5use test_utils::mark;
6
7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 render::{builder_ext::Params, RenderContext},
10};
11
12pub(crate) fn render_enum_variant<'a>(
13 ctx: RenderContext<'a>,
14 local_name: Option<String>,
15 variant: hir::EnumVariant,
16 path: Option<ModPath>,
17) -> CompletionItem {
18 EnumVariantRender::new(ctx, local_name, variant, path).render()
19}
20
21#[derive(Debug)]
22struct EnumVariantRender<'a> {
23 ctx: RenderContext<'a>,
24 name: String,
25 variant: hir::EnumVariant,
26 path: Option<ModPath>,
27 qualified_name: String,
28 short_qualified_name: String,
29 variant_kind: StructKind,
30}
31
32impl<'a> EnumVariantRender<'a> {
33 fn new(
34 ctx: RenderContext<'a>,
35 local_name: Option<String>,
36 variant: hir::EnumVariant,
37 path: Option<ModPath>,
38 ) -> EnumVariantRender<'a> {
39 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()).to_string());
40 let variant_kind = variant.kind(ctx.db());
41
42 let (qualified_name, short_qualified_name) = match &path {
43 Some(path) => {
44 let full = path.to_string();
45 let short =
46 path.segments[path.segments.len().saturating_sub(2)..].iter().join("::");
47 (full, short)
48 }
49 None => (name.to_string(), name.to_string()),
50 };
51
52 EnumVariantRender {
53 ctx,
54 name,
55 variant,
56 path,
57 qualified_name,
58 short_qualified_name,
59 variant_kind,
60 }
61 }
62
63 fn render(self) -> CompletionItem {
64 let mut builder = CompletionItem::new(
65 CompletionKind::Reference,
66 self.ctx.source_range(),
67 self.qualified_name.clone(),
68 )
69 .kind(CompletionItemKind::EnumVariant)
70 .set_documentation(self.variant.docs(self.ctx.db()))
71 .set_deprecated(self.ctx.is_deprecated(self.variant))
72 .detail(self.detail());
73
74 if self.variant_kind == StructKind::Tuple {
75 mark::hit!(inserts_parens_for_tuple_enums);
76 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
77 builder =
78 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
79 } else if self.path.is_some() {
80 builder = builder.lookup_by(self.short_qualified_name);
81 }
82
83 builder.build()
84 }
85
86 fn detail(&self) -> String {
87 let detail_types = self
88 .variant
89 .fields(self.ctx.db())
90 .into_iter()
91 .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db())));
92
93 match self.variant_kind {
94 StructKind::Tuple | StructKind::Unit => format!(
95 "({})",
96 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
97 ),
98 StructKind::Record => format!(
99 "{{ {} }}",
100 detail_types
101 .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
102 .format(", ")
103 ),
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use test_utils::mark;
111
112 use crate::test_utils::check_edit;
113
114 #[test]
115 fn inserts_parens_for_tuple_enums() {
116 mark::check!(inserts_parens_for_tuple_enums);
117 check_edit(
118 "Some",
119 r#"
120enum Option<T> { Some(T), None }
121use Option::*;
122fn main() -> Option<i32> {
123 Som<|>
124}
125"#,
126 r#"
127enum Option<T> { Some(T), None }
128use Option::*;
129fn main() -> Option<i32> {
130 Some($0)
131}
132"#,
133 );
134 check_edit(
135 "Some",
136 r#"
137enum Option<T> { Some(T), None }
138use Option::*;
139fn main(value: Option<i32>) {
140 match value {
141 Som<|>
142 }
143}
144"#,
145 r#"
146enum Option<T> { Some(T), None }
147use Option::*;
148fn main(value: Option<i32>) {
149 match value {
150 Some($0)
151 }
152}
153"#,
154 );
155 }
156
157 #[test]
158 fn dont_duplicate_pattern_parens() {
159 mark::check!(dont_duplicate_pattern_parens);
160 check_edit(
161 "Var",
162 r#"
163enum E { Var(i32) }
164fn main() {
165 match E::Var(92) {
166 E::<|>(92) => (),
167 }
168}
169"#,
170 r#"
171enum E { Var(i32) }
172fn main() {
173 match E::Var(92) {
174 E::Var(92) => (),
175 }
176}
177"#,
178 );
179 }
180}
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
new file mode 100644
index 000000000..4fa6eafd7
--- /dev/null
+++ b/crates/completion/src/render/function.rs
@@ -0,0 +1,303 @@
1//! Renderer for function calls.
2
3use hir::{HasSource, Type};
4use syntax::{ast::Fn, display::function_declaration};
5
6use crate::{
7 item::{CompletionItem, CompletionItemKind, CompletionKind},
8 render::{builder_ext::Params, RenderContext},
9};
10
11pub(crate) fn render_fn<'a>(
12 ctx: RenderContext<'a>,
13 local_name: Option<String>,
14 fn_: hir::Function,
15) -> CompletionItem {
16 FunctionRender::new(ctx, local_name, fn_).render()
17}
18
19#[derive(Debug)]
20struct FunctionRender<'a> {
21 ctx: RenderContext<'a>,
22 name: String,
23 fn_: hir::Function,
24 ast_node: Fn,
25}
26
27impl<'a> FunctionRender<'a> {
28 fn new(
29 ctx: RenderContext<'a>,
30 local_name: Option<String>,
31 fn_: hir::Function,
32 ) -> FunctionRender<'a> {
33 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
34 let ast_node = fn_.source(ctx.db()).value;
35
36 FunctionRender { ctx, name, fn_, ast_node }
37 }
38
39 fn render(self) -> CompletionItem {
40 let params = self.params();
41 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
42 .kind(self.kind())
43 .set_documentation(self.ctx.docs(self.fn_))
44 .set_deprecated(self.ctx.is_deprecated(self.fn_))
45 .detail(self.detail())
46 .add_call_parens(self.ctx.completion, self.name, params)
47 .build()
48 }
49
50 fn detail(&self) -> String {
51 function_declaration(&self.ast_node)
52 }
53
54 fn add_arg(&self, arg: &str, ty: &Type) -> String {
55 if let Some(derefed_ty) = ty.remove_ref() {
56 for (name, local) in self.ctx.completion.locals.iter() {
57 if name == arg && local.ty(self.ctx.db()) == derefed_ty {
58 let mutability = if ty.is_mutable_reference() { "&mut " } else { "&" };
59 return format!("{}{}", mutability, arg);
60 }
61 }
62 }
63 arg.to_string()
64 }
65
66 fn params(&self) -> Params {
67 let params_ty = self.fn_.params(self.ctx.db());
68 let params = self
69 .ast_node
70 .param_list()
71 .into_iter()
72 .flat_map(|it| it.params())
73 .zip(params_ty)
74 .flat_map(|(it, param_ty)| {
75 if let Some(pat) = it.pat() {
76 let name = pat.to_string();
77 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
78 return Some(self.add_arg(arg, param_ty.ty()));
79 }
80 None
81 })
82 .collect();
83 Params::Named(params)
84 }
85
86 fn kind(&self) -> CompletionItemKind {
87 if self.fn_.self_param(self.ctx.db()).is_some() {
88 CompletionItemKind::Method
89 } else {
90 CompletionItemKind::Function
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use test_utils::mark;
98
99 use crate::{
100 test_utils::{check_edit, check_edit_with_config},
101 CompletionConfig,
102 };
103
104 #[test]
105 fn inserts_parens_for_function_calls() {
106 mark::check!(inserts_parens_for_function_calls);
107 check_edit(
108 "no_args",
109 r#"
110fn no_args() {}
111fn main() { no_<|> }
112"#,
113 r#"
114fn no_args() {}
115fn main() { no_args()$0 }
116"#,
117 );
118
119 check_edit(
120 "with_args",
121 r#"
122fn with_args(x: i32, y: String) {}
123fn main() { with_<|> }
124"#,
125 r#"
126fn with_args(x: i32, y: String) {}
127fn main() { with_args(${1:x}, ${2:y})$0 }
128"#,
129 );
130
131 check_edit(
132 "foo",
133 r#"
134struct S;
135impl S {
136 fn foo(&self) {}
137}
138fn bar(s: &S) { s.f<|> }
139"#,
140 r#"
141struct S;
142impl S {
143 fn foo(&self) {}
144}
145fn bar(s: &S) { s.foo()$0 }
146"#,
147 );
148
149 check_edit(
150 "foo",
151 r#"
152struct S {}
153impl S {
154 fn foo(&self, x: i32) {}
155}
156fn bar(s: &S) {
157 s.f<|>
158}
159"#,
160 r#"
161struct S {}
162impl S {
163 fn foo(&self, x: i32) {}
164}
165fn bar(s: &S) {
166 s.foo(${1:x})$0
167}
168"#,
169 );
170 }
171
172 #[test]
173 fn suppress_arg_snippets() {
174 mark::check!(suppress_arg_snippets);
175 check_edit_with_config(
176 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
177 "with_args",
178 r#"
179fn with_args(x: i32, y: String) {}
180fn main() { with_<|> }
181"#,
182 r#"
183fn with_args(x: i32, y: String) {}
184fn main() { with_args($0) }
185"#,
186 );
187 }
188
189 #[test]
190 fn strips_underscores_from_args() {
191 check_edit(
192 "foo",
193 r#"
194fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
195fn main() { f<|> }
196"#,
197 r#"
198fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
199fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
200"#,
201 );
202 }
203
204 #[test]
205 fn insert_ref_when_matching_local_in_scope() {
206 check_edit(
207 "ref_arg",
208 r#"
209struct Foo {}
210fn ref_arg(x: &Foo) {}
211fn main() {
212 let x = Foo {};
213 ref_ar<|>
214}
215"#,
216 r#"
217struct Foo {}
218fn ref_arg(x: &Foo) {}
219fn main() {
220 let x = Foo {};
221 ref_arg(${1:&x})$0
222}
223"#,
224 );
225 }
226
227 #[test]
228 fn insert_mut_ref_when_matching_local_in_scope() {
229 check_edit(
230 "ref_arg",
231 r#"
232struct Foo {}
233fn ref_arg(x: &mut Foo) {}
234fn main() {
235 let x = Foo {};
236 ref_ar<|>
237}
238"#,
239 r#"
240struct Foo {}
241fn ref_arg(x: &mut Foo) {}
242fn main() {
243 let x = Foo {};
244 ref_arg(${1:&mut x})$0
245}
246"#,
247 );
248 }
249
250 #[test]
251 fn insert_ref_when_matching_local_in_scope_for_method() {
252 check_edit(
253 "apply_foo",
254 r#"
255struct Foo {}
256struct Bar {}
257impl Bar {
258 fn apply_foo(&self, x: &Foo) {}
259}
260
261fn main() {
262 let x = Foo {};
263 let y = Bar {};
264 y.<|>
265}
266"#,
267 r#"
268struct Foo {}
269struct Bar {}
270impl Bar {
271 fn apply_foo(&self, x: &Foo) {}
272}
273
274fn main() {
275 let x = Foo {};
276 let y = Bar {};
277 y.apply_foo(${1:&x})$0
278}
279"#,
280 );
281 }
282
283 #[test]
284 fn trim_mut_keyword_in_func_completion() {
285 check_edit(
286 "take_mutably",
287 r#"
288fn take_mutably(mut x: &i32) {}
289
290fn main() {
291 take_m<|>
292}
293"#,
294 r#"
295fn take_mutably(mut x: &i32) {}
296
297fn main() {
298 take_mutably(${1:x})$0
299}
300"#,
301 );
302 }
303}
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
new file mode 100644
index 000000000..96be59cc3
--- /dev/null
+++ b/crates/completion/src/render/macro_.rs
@@ -0,0 +1,216 @@
1//! Renderer for macro invocations.
2
3use hir::{Documentation, HasSource};
4use syntax::display::macro_label;
5use test_utils::mark;
6
7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 render::RenderContext,
10};
11
12pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>,
14 name: String,
15 macro_: hir::MacroDef,
16) -> Option<CompletionItem> {
17 MacroRender::new(ctx, name, macro_).render()
18}
19
20#[derive(Debug)]
21struct MacroRender<'a> {
22 ctx: RenderContext<'a>,
23 name: String,
24 macro_: hir::MacroDef,
25 docs: Option<Documentation>,
26 bra: &'static str,
27 ket: &'static str,
28}
29
30impl<'a> MacroRender<'a> {
31 fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> {
32 let docs = ctx.docs(macro_);
33 let docs_str = docs.as_ref().map_or("", |s| s.as_str());
34 let (bra, ket) = guess_macro_braces(&name, docs_str);
35
36 MacroRender { ctx, name, macro_, docs, bra, ket }
37 }
38
39 fn render(&self) -> Option<CompletionItem> {
40 // FIXME: Currently proc-macro do not have ast-node,
41 // such that it does not have source
42 if self.macro_.is_proc_macro() {
43 return None;
44 }
45
46 let mut builder =
47 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
48 .kind(CompletionItemKind::Macro)
49 .set_documentation(self.docs.clone())
50 .set_deprecated(self.ctx.is_deprecated(self.macro_))
51 .detail(self.detail());
52
53 let needs_bang = self.needs_bang();
54 builder = match self.ctx.snippet_cap() {
55 Some(cap) if needs_bang => {
56 let snippet = self.snippet();
57 let lookup = self.lookup();
58 builder.insert_snippet(cap, snippet).lookup_by(lookup)
59 }
60 None if needs_bang => builder.insert_text(self.banged_name()),
61 _ => {
62 mark::hit!(dont_insert_macro_call_parens_unncessary);
63 builder.insert_text(&self.name)
64 }
65 };
66
67 Some(builder.build())
68 }
69
70 fn needs_bang(&self) -> bool {
71 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call
72 }
73
74 fn label(&self) -> String {
75 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
76 format!("{}!{}…{}", self.name, self.bra, self.ket)
77 } else {
78 self.banged_name()
79 }
80 }
81
82 fn snippet(&self) -> String {
83 format!("{}!{}$0{}", self.name, self.bra, self.ket)
84 }
85
86 fn lookup(&self) -> String {
87 self.banged_name()
88 }
89
90 fn banged_name(&self) -> String {
91 format!("{}!", self.name)
92 }
93
94 fn detail(&self) -> String {
95 let ast_node = self.macro_.source(self.ctx.db()).value;
96 macro_label(&ast_node)
97 }
98}
99
100fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
101 let mut votes = [0, 0, 0];
102 for (idx, s) in docs.match_indices(&macro_name) {
103 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
104 // Ensure to match the full word
105 if after.starts_with('!')
106 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
107 {
108 // It may have spaces before the braces like `foo! {}`
109 match after[1..].chars().find(|&c| !c.is_whitespace()) {
110 Some('{') => votes[0] += 1,
111 Some('[') => votes[1] += 1,
112 Some('(') => votes[2] += 1,
113 _ => {}
114 }
115 }
116 }
117
118 // Insert a space before `{}`.
119 // We prefer the last one when some votes equal.
120 let (_vote, (bra, ket)) = votes
121 .iter()
122 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
123 .max_by_key(|&(&vote, _)| vote)
124 .unwrap();
125 (*bra, *ket)
126}
127
128#[cfg(test)]
129mod tests {
130 use test_utils::mark;
131
132 use crate::test_utils::check_edit;
133
134 #[test]
135 fn dont_insert_macro_call_parens_unncessary() {
136 mark::check!(dont_insert_macro_call_parens_unncessary);
137 check_edit(
138 "frobnicate!",
139 r#"
140//- /main.rs crate:main deps:foo
141use foo::<|>;
142//- /foo/lib.rs crate:foo
143#[macro_export]
144macro_rules frobnicate { () => () }
145"#,
146 r#"
147use foo::frobnicate;
148"#,
149 );
150
151 check_edit(
152 "frobnicate!",
153 r#"
154macro_rules frobnicate { () => () }
155fn main() { frob<|>!(); }
156"#,
157 r#"
158macro_rules frobnicate { () => () }
159fn main() { frobnicate!(); }
160"#,
161 );
162 }
163
164 #[test]
165 fn guesses_macro_braces() {
166 check_edit(
167 "vec!",
168 r#"
169/// Creates a [`Vec`] containing the arguments.
170///
171/// ```
172/// let v = vec![1, 2, 3];
173/// assert_eq!(v[0], 1);
174/// assert_eq!(v[1], 2);
175/// assert_eq!(v[2], 3);
176/// ```
177macro_rules! vec { () => {} }
178
179fn fn main() { v<|> }
180"#,
181 r#"
182/// Creates a [`Vec`] containing the arguments.
183///
184/// ```
185/// let v = vec![1, 2, 3];
186/// assert_eq!(v[0], 1);
187/// assert_eq!(v[1], 2);
188/// assert_eq!(v[2], 3);
189/// ```
190macro_rules! vec { () => {} }
191
192fn fn main() { vec![$0] }
193"#,
194 );
195
196 check_edit(
197 "foo!",
198 r#"
199/// Foo
200///
201/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
202/// call as `let _=foo! { hello world };`
203macro_rules! foo { () => {} }
204fn main() { <|> }
205"#,
206 r#"
207/// Foo
208///
209/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
210/// call as `let _=foo! { hello world };`
211macro_rules! foo { () => {} }
212fn main() { foo! {$0} }
213"#,
214 )
215 }
216}
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
new file mode 100644
index 000000000..9605c7fa9
--- /dev/null
+++ b/crates/completion/src/render/type_alias.rs
@@ -0,0 +1,55 @@
1//! Renderer for type aliases.
2
3use hir::HasSource;
4use syntax::{
5 ast::{NameOwner, TypeAlias},
6 display::type_label,
7};
8
9use crate::{
10 item::{CompletionItem, CompletionItemKind, CompletionKind},
11 render::RenderContext,
12};
13
14pub(crate) fn render_type_alias<'a>(
15 ctx: RenderContext<'a>,
16 type_alias: hir::TypeAlias,
17) -> Option<CompletionItem> {
18 TypeAliasRender::new(ctx, type_alias).render()
19}
20
21#[derive(Debug)]
22struct TypeAliasRender<'a> {
23 ctx: RenderContext<'a>,
24 type_alias: hir::TypeAlias,
25 ast_node: TypeAlias,
26}
27
28impl<'a> TypeAliasRender<'a> {
29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> TypeAliasRender<'a> {
30 let ast_node = type_alias.source(ctx.db()).value;
31 TypeAliasRender { ctx, type_alias, ast_node }
32 }
33
34 fn render(self) -> Option<CompletionItem> {
35 let name = self.name()?;
36 let detail = self.detail();
37
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::TypeAlias)
40 .set_documentation(self.ctx.docs(self.type_alias))
41 .set_deprecated(self.ctx.is_deprecated(self.type_alias))
42 .detail(detail)
43 .build();
44
45 Some(item)
46 }
47
48 fn name(&self) -> Option<String> {
49 self.ast_node.name().map(|name| name.text().to_string())
50 }
51
52 fn detail(&self) -> String {
53 type_label(&self.ast_node)
54 }
55}