diff options
Diffstat (limited to 'crates/completion/src/render/macro_.rs')
-rw-r--r-- | crates/completion/src/render/macro_.rs | 213 |
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 | |||
3 | use hir::{Documentation, HasSource}; | ||
4 | use syntax::display::macro_label; | ||
5 | use test_utils::mark; | ||
6 | |||
7 | use crate::{ | ||
8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, | ||
9 | render::RenderContext, | ||
10 | }; | ||
11 | |||
12 | pub(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)] | ||
23 | struct 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 | |||
32 | impl<'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 | |||
97 | fn 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(¯o_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)] | ||
126 | mod 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 | ||
138 | use foo::$0; | ||
139 | //- /foo/lib.rs crate:foo | ||
140 | #[macro_export] | ||
141 | macro_rules! frobnicate { () => () } | ||
142 | "#, | ||
143 | r#" | ||
144 | use foo::frobnicate; | ||
145 | "#, | ||
146 | ); | ||
147 | |||
148 | check_edit( | ||
149 | "frobnicate!", | ||
150 | r#" | ||
151 | macro_rules! frobnicate { () => () } | ||
152 | fn main() { frob$0!(); } | ||
153 | "#, | ||
154 | r#" | ||
155 | macro_rules! frobnicate { () => () } | ||
156 | fn 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 | /// ``` | ||
174 | macro_rules! vec { () => {} } | ||
175 | |||
176 | fn 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 | /// ``` | ||
187 | macro_rules! vec { () => {} } | ||
188 | |||
189 | fn 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 };` | ||
200 | macro_rules! foo { () => {} } | ||
201 | fn main() { $0 } | ||
202 | "#, | ||
203 | r#" | ||
204 | /// Foo | ||
205 | /// | ||
206 | /// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`, | ||
207 | /// call as `let _=foo! { hello world };` | ||
208 | macro_rules! foo { () => {} } | ||
209 | fn main() { foo! {$0} } | ||
210 | "#, | ||
211 | ) | ||
212 | } | ||
213 | } | ||