aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/render/macro_.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/render/macro_.rs')
-rw-r--r--crates/completion/src/render/macro_.rs213
1 files changed, 0 insertions, 213 deletions
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}