diff options
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | 62 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/presentation.rs | 36 | ||||
-rw-r--r-- | xtask/src/main.rs | 42 |
3 files changed, 131 insertions, 9 deletions
diff --git a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs index d808b2357..09f743c66 100644 --- a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs +++ b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | |||
@@ -56,6 +56,16 @@ mod tests { | |||
56 | do_reference_completion( | 56 | do_reference_completion( |
57 | " | 57 | " |
58 | //- /main.rs | 58 | //- /main.rs |
59 | /// Creates a [`Vec`] containing the arguments. | ||
60 | /// | ||
61 | /// - Create a [`Vec`] containing a given list of elements: | ||
62 | /// | ||
63 | /// ``` | ||
64 | /// let v = vec![1, 2, 3]; | ||
65 | /// assert_eq!(v[0], 1); | ||
66 | /// assert_eq!(v[1], 2); | ||
67 | /// assert_eq!(v[2], 3); | ||
68 | /// ``` | ||
59 | macro_rules! vec { | 69 | macro_rules! vec { |
60 | () => {} | 70 | () => {} |
61 | } | 71 | } |
@@ -68,13 +78,61 @@ mod tests { | |||
68 | @r##"[ | 78 | @r##"[ |
69 | CompletionItem { | 79 | CompletionItem { |
70 | label: "vec!", | 80 | label: "vec!", |
71 | source_range: [46; 46), | 81 | source_range: [280; 280), |
72 | delete: [46; 46), | 82 | delete: [280; 280), |
73 | insert: "vec![$0]", | 83 | insert: "vec![$0]", |
74 | kind: Macro, | 84 | kind: Macro, |
75 | detail: "macro_rules! vec", | 85 | detail: "macro_rules! vec", |
86 | documentation: Documentation( | ||
87 | "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```", | ||
88 | ), | ||
76 | }, | 89 | }, |
77 | ]"## | 90 | ]"## |
78 | ); | 91 | ); |
79 | } | 92 | } |
93 | |||
94 | #[test] | ||
95 | fn completes_macros_braces_guessing() { | ||
96 | assert_debug_snapshot!( | ||
97 | do_reference_completion( | ||
98 | " | ||
99 | //- /main.rs | ||
100 | /// Foo | ||
101 | /// | ||
102 | /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`. | ||
103 | /// Call as `let _=foo! { hello world };` | ||
104 | macro_rules! foo { | ||
105 | () => {} | ||
106 | } | ||
107 | |||
108 | fn main() { | ||
109 | <|> | ||
110 | } | ||
111 | " | ||
112 | ), | ||
113 | @r###"[ | ||
114 | CompletionItem { | ||
115 | label: "foo!", | ||
116 | source_range: [163; 163), | ||
117 | delete: [163; 163), | ||
118 | insert: "foo! {$0}", | ||
119 | kind: Macro, | ||
120 | detail: "macro_rules! foo", | ||
121 | documentation: Documentation( | ||
122 | "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`", | ||
123 | ), | ||
124 | }, | ||
125 | CompletionItem { | ||
126 | label: "main()", | ||
127 | source_range: [163; 163), | ||
128 | delete: [163; 163), | ||
129 | insert: "main()$0", | ||
130 | kind: Function, | ||
131 | lookup: "main", | ||
132 | detail: "fn main()", | ||
133 | }, | ||
134 | ] | ||
135 | "### | ||
136 | ); | ||
137 | } | ||
80 | } | 138 | } |
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 20242d293..aed4ce6d4 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -131,6 +131,33 @@ impl Completions { | |||
131 | self.add_function_with_name(ctx, None, func) | 131 | self.add_function_with_name(ctx, None, func) |
132 | } | 132 | } |
133 | 133 | ||
134 | fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { | ||
135 | let mut votes = [0, 0, 0]; | ||
136 | for (idx, s) in docs.match_indices(¯o_name) { | ||
137 | let (before, after) = (&docs[..idx], &docs[idx + s.len()..]); | ||
138 | // Ensure to match the full word | ||
139 | if after.starts_with("!") | ||
140 | && before | ||
141 | .chars() | ||
142 | .rev() | ||
143 | .next() | ||
144 | .map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric()) | ||
145 | { | ||
146 | // It may have spaces before the braces like `foo! {}` | ||
147 | match after[1..].chars().find(|&c| !c.is_whitespace()) { | ||
148 | Some('{') => votes[0] += 1, | ||
149 | Some('[') => votes[1] += 1, | ||
150 | Some('(') => votes[2] += 1, | ||
151 | _ => {} | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | // Insert a space before `{}`. | ||
157 | // We prefer the last one when some votes equal. | ||
158 | *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1 | ||
159 | } | ||
160 | |||
134 | pub(crate) fn add_macro( | 161 | pub(crate) fn add_macro( |
135 | &mut self, | 162 | &mut self, |
136 | ctx: &CompletionContext, | 163 | ctx: &CompletionContext, |
@@ -141,10 +168,9 @@ impl Completions { | |||
141 | if let Some(name) = name { | 168 | if let Some(name) = name { |
142 | let detail = macro_label(&ast_node); | 169 | let detail = macro_label(&ast_node); |
143 | 170 | ||
144 | let macro_braces_to_insert = match name.as_str() { | 171 | let docs = macro_.docs(ctx.db); |
145 | "vec" => "[$0]", | 172 | let macro_braces_to_insert = |
146 | _ => "($0)", | 173 | self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); |
147 | }; | ||
148 | let macro_declaration = name + "!"; | 174 | let macro_declaration = name + "!"; |
149 | 175 | ||
150 | let builder = CompletionItem::new( | 176 | let builder = CompletionItem::new( |
@@ -153,7 +179,7 @@ impl Completions { | |||
153 | ¯o_declaration, | 179 | ¯o_declaration, |
154 | ) | 180 | ) |
155 | .kind(CompletionItemKind::Macro) | 181 | .kind(CompletionItemKind::Macro) |
156 | .set_documentation(macro_.docs(ctx.db)) | 182 | .set_documentation(docs) |
157 | .detail(detail) | 183 | .detail(detail) |
158 | .insert_snippet(macro_declaration + macro_braces_to_insert); | 184 | .insert_snippet(macro_declaration + macro_braces_to_insert); |
159 | 185 | ||
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 623058436..c08915aac 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -8,9 +8,12 @@ use pico_args::Arguments; | |||
8 | use std::{env, path::PathBuf}; | 8 | use std::{env, path::PathBuf}; |
9 | use xtask::{ | 9 | use xtask::{ |
10 | gen_tests, generate_boilerplate, install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt, | 10 | gen_tests, generate_boilerplate, install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt, |
11 | Cmd, Overwrite, Result, | 11 | run_with_output, Cmd, Overwrite, Result, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | // Latest stable, feel free to send a PR if this lags behind. | ||
15 | const REQUIRED_RUST_VERSION: u32 = 38; | ||
16 | |||
14 | struct InstallOpt { | 17 | struct InstallOpt { |
15 | client: Option<ClientOpt>, | 18 | client: Option<ClientOpt>, |
16 | server: Option<ServerOpt>, | 19 | server: Option<ServerOpt>, |
@@ -210,9 +213,44 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { | |||
210 | } | 213 | } |
211 | 214 | ||
212 | fn install_server(opts: ServerOpt) -> Result<()> { | 215 | fn install_server(opts: ServerOpt) -> Result<()> { |
213 | if opts.jemalloc { | 216 | let mut old_rust = false; |
217 | if let Ok(output) = run_with_output("cargo --version", ".") { | ||
218 | if let Ok(stdout) = String::from_utf8(output.stdout) { | ||
219 | if !check_version(&stdout, REQUIRED_RUST_VERSION) { | ||
220 | old_rust = true; | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | if old_rust { | ||
226 | eprintln!( | ||
227 | "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", | ||
228 | REQUIRED_RUST_VERSION | ||
229 | ) | ||
230 | } | ||
231 | |||
232 | let res = if opts.jemalloc { | ||
214 | run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") | 233 | run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") |
215 | } else { | 234 | } else { |
216 | run("cargo install --path crates/ra_lsp_server --locked --force", ".") | 235 | run("cargo install --path crates/ra_lsp_server --locked --force", ".") |
236 | }; | ||
237 | |||
238 | if res.is_err() && old_rust { | ||
239 | eprintln!( | ||
240 | "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", | ||
241 | REQUIRED_RUST_VERSION | ||
242 | ) | ||
243 | } | ||
244 | |||
245 | res | ||
246 | } | ||
247 | |||
248 | fn check_version(version_output: &str, min_minor_version: u32) -> bool { | ||
249 | // Parse second the number out of | ||
250 | // cargo 1.39.0-beta (1c6ec66d5 2019-09-30) | ||
251 | let minor: Option<u32> = version_output.split('.').nth(1).and_then(|it| it.parse().ok()); | ||
252 | match minor { | ||
253 | None => true, | ||
254 | Some(minor) => minor >= min_minor_version, | ||
217 | } | 255 | } |
218 | } | 256 | } |