aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs62
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs36
-rw-r--r--xtask/src/main.rs42
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(&macro_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 &macro_declaration, 179 &macro_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;
8use std::{env, path::PathBuf}; 8use std::{env, path::PathBuf};
9use xtask::{ 9use 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.
15const REQUIRED_RUST_VERSION: u32 = 38;
16
14struct InstallOpt { 17struct 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
212fn install_server(opts: ServerOpt) -> Result<()> { 215fn 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
248fn 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}