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.rs130
-rw-r--r--crates/completion/src/render/function.rs341
-rw-r--r--crates/completion/src/render/macro_.rs213
-rw-r--r--crates/completion/src/render/pattern.rs146
-rw-r--r--crates/completion/src/render/type_alias.rs55
7 files changed, 0 insertions, 1034 deletions
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs
deleted file mode 100644
index d053a988b..000000000
--- a/crates/completion/src/render/builder_ext.rs
+++ /dev/null
@@ -1,94 +0,0 @@
1//! Extensions for `Builder` structure required for item rendering.
2
3use itertools::Itertools;
4use test_utils::mark;
5
6use crate::{item::Builder, CompletionContext};
7
8#[derive(Debug)]
9pub(super) enum Params {
10 Named(Vec<String>),
11 Anonymous(usize),
12}
13
14impl Params {
15 pub(super) fn len(&self) -> usize {
16 match self {
17 Params::Named(xs) => xs.len(),
18 Params::Anonymous(len) => *len,
19 }
20 }
21
22 pub(super) fn is_empty(&self) -> bool {
23 self.len() == 0
24 }
25}
26
27impl Builder {
28 fn should_add_parens(&self, ctx: &CompletionContext) -> bool {
29 if !ctx.config.add_call_parenthesis {
30 return false;
31 }
32 if ctx.use_item_syntax.is_some() {
33 mark::hit!(no_parens_in_use_item);
34 return false;
35 }
36 if ctx.is_pattern_call {
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_parens(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
deleted file mode 100644
index ce924f309..000000000
--- a/crates/completion/src/render/const_.rs
+++ /dev/null
@@ -1,55 +0,0 @@
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) -> Option<ConstRender<'a>> {
30 let ast_node = const_.source(ctx.db())?.value;
31 Some(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
deleted file mode 100644
index 89fb49773..000000000
--- a/crates/completion/src/render/enum_variant.rs
+++ /dev/null
@@ -1,130 +0,0 @@
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, ImportEdit},
9 render::{builder_ext::Params, RenderContext},
10};
11
12pub(crate) fn render_variant<'a>(
13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>,
16 variant: hir::Variant,
17 path: Option<ModPath>,
18) -> CompletionItem {
19 let _p = profile::span("render_enum_variant");
20 EnumRender::new(ctx, local_name, variant, path).render(import_to_add)
21}
22
23#[derive(Debug)]
24struct EnumRender<'a> {
25 ctx: RenderContext<'a>,
26 name: String,
27 variant: hir::Variant,
28 path: Option<ModPath>,
29 qualified_name: String,
30 short_qualified_name: String,
31 variant_kind: StructKind,
32}
33
34impl<'a> EnumRender<'a> {
35 fn new(
36 ctx: RenderContext<'a>,
37 local_name: Option<String>,
38 variant: hir::Variant,
39 path: Option<ModPath>,
40 ) -> EnumRender<'a> {
41 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()).to_string());
42 let variant_kind = variant.kind(ctx.db());
43
44 let (qualified_name, short_qualified_name) = match &path {
45 Some(path) => {
46 let full = path.to_string();
47 let short =
48 path.segments[path.segments.len().saturating_sub(2)..].iter().join("::");
49 (full, short)
50 }
51 None => (name.to_string(), name.to_string()),
52 };
53
54 EnumRender { ctx, name, variant, path, qualified_name, short_qualified_name, variant_kind }
55 }
56
57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
58 let mut builder = CompletionItem::new(
59 CompletionKind::Reference,
60 self.ctx.source_range(),
61 self.qualified_name.clone(),
62 )
63 .kind(CompletionItemKind::EnumVariant)
64 .set_documentation(self.variant.docs(self.ctx.db()))
65 .set_deprecated(self.ctx.is_deprecated(self.variant))
66 .add_import(import_to_add)
67 .detail(self.detail());
68
69 if self.variant_kind == StructKind::Tuple {
70 mark::hit!(inserts_parens_for_tuple_enums);
71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
72 builder =
73 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
74 } else if self.path.is_some() {
75 builder = builder.lookup_by(self.short_qualified_name);
76 }
77
78 builder.build()
79 }
80
81 fn detail(&self) -> String {
82 let detail_types = self
83 .variant
84 .fields(self.ctx.db())
85 .into_iter()
86 .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db())));
87
88 match self.variant_kind {
89 StructKind::Tuple | StructKind::Unit => format!(
90 "({})",
91 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
92 ),
93 StructKind::Record => format!(
94 "{{ {} }}",
95 detail_types
96 .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
97 .format(", ")
98 ),
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use test_utils::mark;
106
107 use crate::test_utils::check_edit;
108
109 #[test]
110 fn inserts_parens_for_tuple_enums() {
111 mark::check!(inserts_parens_for_tuple_enums);
112 check_edit(
113 "Some",
114 r#"
115enum Option<T> { Some(T), None }
116use Option::*;
117fn main() -> Option<i32> {
118 Som$0
119}
120"#,
121 r#"
122enum Option<T> { Some(T), None }
123use Option::*;
124fn main() -> Option<i32> {
125 Some($0)
126}
127"#,
128 );
129 }
130}
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
deleted file mode 100644
index f5b0ce3e3..000000000
--- a/crates/completion/src/render/function.rs
+++ /dev/null
@@ -1,341 +0,0 @@
1//! Renderer for function calls.
2
3use hir::{HasSource, Type};
4use syntax::{ast::Fn, display::function_declaration};
5use test_utils::mark;
6
7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext},
10};
11
12pub(crate) fn render_fn<'a>(
13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>,
16 fn_: hir::Function,
17) -> Option<CompletionItem> {
18 let _p = profile::span("render_fn");
19 Some(FunctionRender::new(ctx, local_name, fn_)?.render(import_to_add))
20}
21
22#[derive(Debug)]
23struct FunctionRender<'a> {
24 ctx: RenderContext<'a>,
25 name: String,
26 func: hir::Function,
27 ast_node: Fn,
28}
29
30impl<'a> FunctionRender<'a> {
31 fn new(
32 ctx: RenderContext<'a>,
33 local_name: Option<String>,
34 fn_: hir::Function,
35 ) -> Option<FunctionRender<'a>> {
36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
37 let ast_node = fn_.source(ctx.db())?.value;
38
39 Some(FunctionRender { ctx, name, func: fn_, ast_node })
40 }
41
42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
43 let params = self.params();
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45 .kind(self.kind())
46 .set_documentation(self.ctx.docs(self.func))
47 .set_deprecated(self.ctx.is_deprecated(self.func))
48 .detail(self.detail())
49 .add_call_parens(self.ctx.completion, self.name, params)
50 .add_import(import_to_add)
51 .build()
52 }
53
54 fn detail(&self) -> String {
55 function_declaration(&self.ast_node)
56 }
57
58 fn add_arg(&self, arg: &str, ty: &Type) -> String {
59 if let Some(derefed_ty) = ty.remove_ref() {
60 for (name, local) in self.ctx.completion.locals.iter() {
61 if name == arg && local.ty(self.ctx.db()) == derefed_ty {
62 let mutability = if ty.is_mutable_reference() { "&mut " } else { "&" };
63 return format!("{}{}", mutability, arg);
64 }
65 }
66 }
67 arg.to_string()
68 }
69
70 fn params(&self) -> Params {
71 let ast_params = match self.ast_node.param_list() {
72 Some(it) => it,
73 None => return Params::Named(Vec::new()),
74 };
75
76 let mut params_pats = Vec::new();
77 let params_ty = if self.ctx.completion.dot_receiver.is_some() {
78 self.func.method_params(self.ctx.db()).unwrap_or_default()
79 } else {
80 if let Some(s) = ast_params.self_param() {
81 mark::hit!(parens_for_method_call_as_assoc_fn);
82 params_pats.push(Some(s.to_string()));
83 }
84 self.func.assoc_fn_params(self.ctx.db())
85 };
86 params_pats
87 .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string())));
88
89 let params = params_pats
90 .into_iter()
91 .zip(params_ty)
92 .flat_map(|(pat, param_ty)| {
93 let pat = pat?;
94 let name = pat;
95 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
96 Some(self.add_arg(arg, param_ty.ty()))
97 })
98 .collect();
99 Params::Named(params)
100 }
101
102 fn kind(&self) -> CompletionItemKind {
103 if self.func.self_param(self.ctx.db()).is_some() {
104 CompletionItemKind::Method
105 } else {
106 CompletionItemKind::Function
107 }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use test_utils::mark;
114
115 use crate::{
116 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
117 CompletionConfig,
118 };
119
120 #[test]
121 fn inserts_parens_for_function_calls() {
122 mark::check!(inserts_parens_for_function_calls);
123 check_edit(
124 "no_args",
125 r#"
126fn no_args() {}
127fn main() { no_$0 }
128"#,
129 r#"
130fn no_args() {}
131fn main() { no_args()$0 }
132"#,
133 );
134
135 check_edit(
136 "with_args",
137 r#"
138fn with_args(x: i32, y: String) {}
139fn main() { with_$0 }
140"#,
141 r#"
142fn with_args(x: i32, y: String) {}
143fn main() { with_args(${1:x}, ${2:y})$0 }
144"#,
145 );
146
147 check_edit(
148 "foo",
149 r#"
150struct S;
151impl S {
152 fn foo(&self) {}
153}
154fn bar(s: &S) { s.f$0 }
155"#,
156 r#"
157struct S;
158impl S {
159 fn foo(&self) {}
160}
161fn bar(s: &S) { s.foo()$0 }
162"#,
163 );
164
165 check_edit(
166 "foo",
167 r#"
168struct S {}
169impl S {
170 fn foo(&self, x: i32) {}
171}
172fn bar(s: &S) {
173 s.f$0
174}
175"#,
176 r#"
177struct S {}
178impl S {
179 fn foo(&self, x: i32) {}
180}
181fn bar(s: &S) {
182 s.foo(${1:x})$0
183}
184"#,
185 );
186 }
187
188 #[test]
189 fn parens_for_method_call_as_assoc_fn() {
190 mark::check!(parens_for_method_call_as_assoc_fn);
191 check_edit(
192 "foo",
193 r#"
194struct S;
195impl S {
196 fn foo(&self) {}
197}
198fn main() { S::f$0 }
199"#,
200 r#"
201struct S;
202impl S {
203 fn foo(&self) {}
204}
205fn main() { S::foo(${1:&self})$0 }
206"#,
207 );
208 }
209
210 #[test]
211 fn suppress_arg_snippets() {
212 mark::check!(suppress_arg_snippets);
213 check_edit_with_config(
214 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
215 "with_args",
216 r#"
217fn with_args(x: i32, y: String) {}
218fn main() { with_$0 }
219"#,
220 r#"
221fn with_args(x: i32, y: String) {}
222fn main() { with_args($0) }
223"#,
224 );
225 }
226
227 #[test]
228 fn strips_underscores_from_args() {
229 check_edit(
230 "foo",
231 r#"
232fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
233fn main() { f$0 }
234"#,
235 r#"
236fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
237fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
238"#,
239 );
240 }
241
242 #[test]
243 fn insert_ref_when_matching_local_in_scope() {
244 check_edit(
245 "ref_arg",
246 r#"
247struct Foo {}
248fn ref_arg(x: &Foo) {}
249fn main() {
250 let x = Foo {};
251 ref_ar$0
252}
253"#,
254 r#"
255struct Foo {}
256fn ref_arg(x: &Foo) {}
257fn main() {
258 let x = Foo {};
259 ref_arg(${1:&x})$0
260}
261"#,
262 );
263 }
264
265 #[test]
266 fn insert_mut_ref_when_matching_local_in_scope() {
267 check_edit(
268 "ref_arg",
269 r#"
270struct Foo {}
271fn ref_arg(x: &mut Foo) {}
272fn main() {
273 let x = Foo {};
274 ref_ar$0
275}
276"#,
277 r#"
278struct Foo {}
279fn ref_arg(x: &mut Foo) {}
280fn main() {
281 let x = Foo {};
282 ref_arg(${1:&mut x})$0
283}
284"#,
285 );
286 }
287
288 #[test]
289 fn insert_ref_when_matching_local_in_scope_for_method() {
290 check_edit(
291 "apply_foo",
292 r#"
293struct Foo {}
294struct Bar {}
295impl Bar {
296 fn apply_foo(&self, x: &Foo) {}
297}
298
299fn main() {
300 let x = Foo {};
301 let y = Bar {};
302 y.$0
303}
304"#,
305 r#"
306struct Foo {}
307struct Bar {}
308impl Bar {
309 fn apply_foo(&self, x: &Foo) {}
310}
311
312fn main() {
313 let x = Foo {};
314 let y = Bar {};
315 y.apply_foo(${1:&x})$0
316}
317"#,
318 );
319 }
320
321 #[test]
322 fn trim_mut_keyword_in_func_completion() {
323 check_edit(
324 "take_mutably",
325 r#"
326fn take_mutably(mut x: &i32) {}
327
328fn main() {
329 take_m$0
330}
331"#,
332 r#"
333fn take_mutably(mut x: &i32) {}
334
335fn main() {
336 take_mutably(${1:x})$0
337}
338"#,
339 );
340 }
341}
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
deleted file mode 100644
index f893e420a..000000000
--- a/crates/completion/src/render/macro_.rs
+++ /dev/null
@@ -1,213 +0,0 @@
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, ImportEdit},
9 render::RenderContext,
10};
11
12pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportEdit>,
15 name: String,
16 macro_: hir::MacroDef,
17) -> Option<CompletionItem> {
18 let _p = profile::span("render_macro");
19 MacroRender::new(ctx, name, macro_).render(import_to_add)
20}
21
22#[derive(Debug)]
23struct MacroRender<'a> {
24 ctx: RenderContext<'a>,
25 name: String,
26 macro_: hir::MacroDef,
27 docs: Option<Documentation>,
28 bra: &'static str,
29 ket: &'static str,
30}
31
32impl<'a> MacroRender<'a> {
33 fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> {
34 let docs = ctx.docs(macro_);
35 let docs_str = docs.as_ref().map_or("", |s| s.as_str());
36 let (bra, ket) = guess_macro_braces(&name, docs_str);
37
38 MacroRender { ctx, name, macro_, docs, bra, ket }
39 }
40
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 let mut builder =
43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
44 .kind(CompletionItemKind::Macro)
45 .set_documentation(self.docs.clone())
46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
47 .add_import(import_to_add)
48 .set_detail(self.detail());
49
50 let needs_bang = self.needs_bang();
51 builder = match self.ctx.snippet_cap() {
52 Some(cap) if needs_bang => {
53 let snippet = self.snippet();
54 let lookup = self.lookup();
55 builder.insert_snippet(cap, snippet).lookup_by(lookup)
56 }
57 None if needs_bang => builder.insert_text(self.banged_name()),
58 _ => {
59 mark::hit!(dont_insert_macro_call_parens_unncessary);
60 builder.insert_text(&self.name)
61 }
62 };
63
64 Some(builder.build())
65 }
66
67 fn needs_bang(&self) -> bool {
68 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call
69 }
70
71 fn label(&self) -> String {
72 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
73 format!("{}!{}…{}", self.name, self.bra, self.ket)
74 } else {
75 self.banged_name()
76 }
77 }
78
79 fn snippet(&self) -> String {
80 format!("{}!{}$0{}", self.name, self.bra, self.ket)
81 }
82
83 fn lookup(&self) -> String {
84 self.banged_name()
85 }
86
87 fn banged_name(&self) -> String {
88 format!("{}!", self.name)
89 }
90
91 fn detail(&self) -> Option<String> {
92 let ast_node = self.macro_.source(self.ctx.db())?.value;
93 Some(macro_label(&ast_node))
94 }
95}
96
97fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
98 let mut votes = [0, 0, 0];
99 for (idx, s) in docs.match_indices(&macro_name) {
100 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
101 // Ensure to match the full word
102 if after.starts_with('!')
103 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
104 {
105 // It may have spaces before the braces like `foo! {}`
106 match after[1..].chars().find(|&c| !c.is_whitespace()) {
107 Some('{') => votes[0] += 1,
108 Some('[') => votes[1] += 1,
109 Some('(') => votes[2] += 1,
110 _ => {}
111 }
112 }
113 }
114
115 // Insert a space before `{}`.
116 // We prefer the last one when some votes equal.
117 let (_vote, (bra, ket)) = votes
118 .iter()
119 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
120 .max_by_key(|&(&vote, _)| vote)
121 .unwrap();
122 (*bra, *ket)
123}
124
125#[cfg(test)]
126mod tests {
127 use test_utils::mark;
128
129 use crate::test_utils::check_edit;
130
131 #[test]
132 fn dont_insert_macro_call_parens_unncessary() {
133 mark::check!(dont_insert_macro_call_parens_unncessary);
134 check_edit(
135 "frobnicate!",
136 r#"
137//- /main.rs crate:main deps:foo
138use foo::$0;
139//- /foo/lib.rs crate:foo
140#[macro_export]
141macro_rules! frobnicate { () => () }
142"#,
143 r#"
144use foo::frobnicate;
145"#,
146 );
147
148 check_edit(
149 "frobnicate!",
150 r#"
151macro_rules! frobnicate { () => () }
152fn main() { frob$0!(); }
153"#,
154 r#"
155macro_rules! frobnicate { () => () }
156fn main() { frobnicate!(); }
157"#,
158 );
159 }
160
161 #[test]
162 fn guesses_macro_braces() {
163 check_edit(
164 "vec!",
165 r#"
166/// Creates a [`Vec`] containing the arguments.
167///
168/// ```
169/// let v = vec![1, 2, 3];
170/// assert_eq!(v[0], 1);
171/// assert_eq!(v[1], 2);
172/// assert_eq!(v[2], 3);
173/// ```
174macro_rules! vec { () => {} }
175
176fn fn main() { v$0 }
177"#,
178 r#"
179/// Creates a [`Vec`] containing the arguments.
180///
181/// ```
182/// let v = vec![1, 2, 3];
183/// assert_eq!(v[0], 1);
184/// assert_eq!(v[1], 2);
185/// assert_eq!(v[2], 3);
186/// ```
187macro_rules! vec { () => {} }
188
189fn fn main() { vec![$0] }
190"#,
191 );
192
193 check_edit(
194 "foo!",
195 r#"
196/// Foo
197///
198/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
199/// call as `let _=foo! { hello world };`
200macro_rules! foo { () => {} }
201fn main() { $0 }
202"#,
203 r#"
204/// Foo
205///
206/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
207/// call as `let _=foo! { hello world };`
208macro_rules! foo { () => {} }
209fn main() { foo! {$0} }
210"#,
211 )
212 }
213}
diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs
deleted file mode 100644
index 61d8a17e5..000000000
--- a/crates/completion/src/render/pattern.rs
+++ /dev/null
@@ -1,146 +0,0 @@
1//! Renderer for patterns.
2
3use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
4use ide_db::helpers::SnippetCap;
5use itertools::Itertools;
6
7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind};
8
9fn visible_fields(
10 ctx: &RenderContext<'_>,
11 fields: &[hir::Field],
12 item: impl HasAttrs,
13) -> Option<(Vec<hir::Field>, bool)> {
14 let module = ctx.completion.scope.module()?;
15 let n_fields = fields.len();
16 let fields = fields
17 .into_iter()
18 .filter(|field| field.is_visible_from(ctx.db(), module))
19 .copied()
20 .collect::<Vec<_>>();
21
22 let fields_omitted =
23 n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
24 Some((fields, fields_omitted))
25}
26
27pub(crate) fn render_struct_pat(
28 ctx: RenderContext<'_>,
29 strukt: hir::Struct,
30 local_name: Option<Name>,
31) -> Option<CompletionItem> {
32 let _p = profile::span("render_struct_pat");
33
34 let fields = strukt.fields(ctx.db());
35 let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
36
37 if visible_fields.is_empty() {
38 // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
39 return None;
40 }
41
42 let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
43 let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?;
44
45 Some(build_completion(ctx, name, pat, strukt))
46}
47
48pub(crate) fn render_variant_pat(
49 ctx: RenderContext<'_>,
50 variant: hir::Variant,
51 local_name: Option<Name>,
52) -> Option<CompletionItem> {
53 let _p = profile::span("render_variant_pat");
54
55 let fields = variant.fields(ctx.db());
56 let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
57
58 let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string();
59 let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
60
61 Some(build_completion(ctx, name, pat, variant))
62}
63
64fn build_completion(
65 ctx: RenderContext<'_>,
66 name: String,
67 pat: String,
68 item: impl HasAttrs + Copy,
69) -> CompletionItem {
70 let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name)
71 .kind(CompletionItemKind::Binding)
72 .set_documentation(ctx.docs(item))
73 .set_deprecated(ctx.is_deprecated(item))
74 .detail(&pat);
75 let completion = if let Some(snippet_cap) = ctx.snippet_cap() {
76 completion.insert_snippet(snippet_cap, pat)
77 } else {
78 completion.insert_text(pat)
79 };
80 completion.build()
81}
82
83fn render_pat(
84 ctx: &RenderContext<'_>,
85 name: &str,
86 kind: StructKind,
87 fields: &[hir::Field],
88 fields_omitted: bool,
89) -> Option<String> {
90 let mut pat = match kind {
91 StructKind::Tuple if ctx.snippet_cap().is_some() => {
92 render_tuple_as_pat(&fields, &name, fields_omitted)
93 }
94 StructKind::Record => {
95 render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted)
96 }
97 _ => return None,
98 };
99
100 if ctx.completion.is_param {
101 pat.push(':');
102 pat.push(' ');
103 pat.push_str(&name);
104 }
105 if ctx.snippet_cap().is_some() {
106 pat.push_str("$0");
107 }
108 Some(pat)
109}
110
111fn render_record_as_pat(
112 db: &dyn HirDatabase,
113 snippet_cap: Option<SnippetCap>,
114 fields: &[hir::Field],
115 name: &str,
116 fields_omitted: bool,
117) -> String {
118 let fields = fields.iter();
119 if snippet_cap.is_some() {
120 format!(
121 "{name} {{ {}{} }}",
122 fields
123 .enumerate()
124 .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
125 .format(", "),
126 if fields_omitted { ", .." } else { "" },
127 name = name
128 )
129 } else {
130 format!(
131 "{name} {{ {}{} }}",
132 fields.map(|field| field.name(db)).format(", "),
133 if fields_omitted { ", .." } else { "" },
134 name = name
135 )
136 }
137}
138
139fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
140 format!(
141 "{name}({}{})",
142 fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
143 if fields_omitted { ", .." } else { "" },
144 name = name
145 )
146}
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
deleted file mode 100644
index 69b445b9c..000000000
--- a/crates/completion/src/render/type_alias.rs
+++ /dev/null
@@ -1,55 +0,0 @@
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) -> Option<TypeAliasRender<'a>> {
30 let ast_node = type_alias.source(ctx.db())?.value;
31 Some(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}