diff options
-rw-r--r-- | Cargo.lock | 28 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/convert_comment_block.rs | 419 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/proc_macro_srv/src/rustc_server.rs | 40 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/args.rs | 274 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/flags.rs | 251 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 85 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/analysis_bench.rs | 3 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/analysis_stats.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/diagnostics.rs | 3 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/load_cargo.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/ssr.rs | 4 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 11 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 3 | ||||
-rw-r--r-- | xtask/Cargo.toml | 2 | ||||
-rw-r--r-- | xtask/src/codegen.rs | 8 | ||||
-rw-r--r-- | xtask/src/flags.rs | 139 | ||||
-rw-r--r-- | xtask/src/main.rs | 134 | ||||
-rw-r--r-- | xtask/src/metrics.rs | 8 | ||||
-rw-r--r-- | xtask/src/pre_cache.rs | 4 | ||||
-rw-r--r-- | xtask/src/release.rs | 14 | ||||
-rw-r--r-- | xtask/src/tidy.rs | 43 |
23 files changed, 1020 insertions, 468 deletions
diff --git a/Cargo.lock b/Cargo.lock index 0476a15eb..625c61883 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1115,12 +1115,6 @@ dependencies = [ | |||
1115 | ] | 1115 | ] |
1116 | 1116 | ||
1117 | [[package]] | 1117 | [[package]] |
1118 | name = "pico-args" | ||
1119 | version = "0.4.0" | ||
1120 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1121 | checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6" | ||
1122 | |||
1123 | [[package]] | ||
1124 | name = "pin-project-lite" | 1118 | name = "pin-project-lite" |
1125 | version = "0.2.4" | 1119 | version = "0.2.4" |
1126 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1339,7 +1333,6 @@ dependencies = [ | |||
1339 | "mimalloc", | 1333 | "mimalloc", |
1340 | "oorandom", | 1334 | "oorandom", |
1341 | "parking_lot", | 1335 | "parking_lot", |
1342 | "pico-args", | ||
1343 | "proc_macro_srv", | 1336 | "proc_macro_srv", |
1344 | "profile", | 1337 | "profile", |
1345 | "project_model", | 1338 | "project_model", |
@@ -1361,6 +1354,7 @@ dependencies = [ | |||
1361 | "vfs", | 1354 | "vfs", |
1362 | "vfs-notify", | 1355 | "vfs-notify", |
1363 | "winapi", | 1356 | "winapi", |
1357 | "xflags", | ||
1364 | ] | 1358 | ] |
1365 | 1359 | ||
1366 | [[package]] | 1360 | [[package]] |
@@ -1913,6 +1907,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1913 | checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3" | 1907 | checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3" |
1914 | 1908 | ||
1915 | [[package]] | 1909 | [[package]] |
1910 | name = "xflags" | ||
1911 | version = "0.1.3" | ||
1912 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1913 | checksum = "ddb4b07c0db813f8e2b5e1b2189ef56fcddb27a6f9ef71314dbf8cc50096a5db" | ||
1914 | dependencies = [ | ||
1915 | "xflags-macros", | ||
1916 | ] | ||
1917 | |||
1918 | [[package]] | ||
1919 | name = "xflags-macros" | ||
1920 | version = "0.1.3" | ||
1921 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1922 | checksum = "f8e168a99d6ce9d5dd0d0913f1bded279377843952dd8ff83f81b862a1dad0e1" | ||
1923 | dependencies = [ | ||
1924 | "proc-macro2", | ||
1925 | ] | ||
1926 | |||
1927 | [[package]] | ||
1916 | name = "xshell" | 1928 | name = "xshell" |
1917 | version = "0.1.9" | 1929 | version = "0.1.9" |
1918 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1930 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1933,11 +1945,11 @@ version = "0.1.0" | |||
1933 | dependencies = [ | 1945 | dependencies = [ |
1934 | "anyhow", | 1946 | "anyhow", |
1935 | "flate2", | 1947 | "flate2", |
1936 | "pico-args", | ||
1937 | "proc-macro2", | 1948 | "proc-macro2", |
1938 | "quote", | 1949 | "quote", |
1939 | "ungrammar", | 1950 | "ungrammar", |
1940 | "walkdir", | 1951 | "walkdir", |
1941 | "write-json", | 1952 | "write-json", |
1953 | "xflags", | ||
1942 | "xshell", | 1954 | "xshell", |
1943 | ] | 1955 | ] |
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs new file mode 100644 index 000000000..cdc45fc42 --- /dev/null +++ b/crates/ide_assists/src/handlers/convert_comment_block.rs | |||
@@ -0,0 +1,419 @@ | |||
1 | use itertools::Itertools; | ||
2 | use std::convert::identity; | ||
3 | use syntax::{ | ||
4 | ast::{ | ||
5 | self, | ||
6 | edit::IndentLevel, | ||
7 | Comment, CommentKind, | ||
8 | CommentPlacement::{Inner, Outer}, | ||
9 | CommentShape::{self, Block, Line}, | ||
10 | Whitespace, | ||
11 | }, | ||
12 | AstToken, Direction, SyntaxElement, TextRange, | ||
13 | }; | ||
14 | |||
15 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
16 | |||
17 | /// Assist: line_to_block | ||
18 | /// | ||
19 | /// Converts comments between block and single-line form | ||
20 | /// | ||
21 | /// ``` | ||
22 | /// // Multi-line | ||
23 | /// // comment | ||
24 | /// ``` | ||
25 | /// -> | ||
26 | /// ``` | ||
27 | /// /** | ||
28 | /// Multi-line | ||
29 | /// comment | ||
30 | /// */ | ||
31 | /// ``` | ||
32 | pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
33 | if let Some(comment) = ctx.find_token_at_offset::<ast::Comment>() { | ||
34 | // Only allow comments which are alone on their line | ||
35 | if let Some(prev) = comment.syntax().prev_token() { | ||
36 | if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() { | ||
37 | return None; | ||
38 | } | ||
39 | } | ||
40 | |||
41 | return match comment.kind().shape { | ||
42 | ast::CommentShape::Block => block_to_line(acc, comment), | ||
43 | ast::CommentShape::Line => line_to_block(acc, comment), | ||
44 | }; | ||
45 | } | ||
46 | |||
47 | return None; | ||
48 | } | ||
49 | |||
50 | fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> { | ||
51 | let target = comment.syntax().text_range(); | ||
52 | |||
53 | acc.add( | ||
54 | AssistId("block_to_line", AssistKind::RefactorRewrite), | ||
55 | "Replace block comment with line comments", | ||
56 | target, | ||
57 | |edit| { | ||
58 | let indentation = IndentLevel::from_token(comment.syntax()); | ||
59 | let line_prefix = | ||
60 | comment_kind_prefix(CommentKind { shape: CommentShape::Line, ..comment.kind() }); | ||
61 | |||
62 | let text = comment.text(); | ||
63 | let text = &text[comment.prefix().len()..(text.len() - "*/".len())].trim(); | ||
64 | |||
65 | let lines = text.lines().peekable(); | ||
66 | |||
67 | let indent_spaces = indentation.to_string(); | ||
68 | let output = lines | ||
69 | .map(|l| l.trim_start_matches(&indent_spaces)) | ||
70 | .map(|l| { | ||
71 | // Don't introduce trailing whitespace | ||
72 | if l.is_empty() { | ||
73 | line_prefix.to_string() | ||
74 | } else { | ||
75 | format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces)) | ||
76 | } | ||
77 | }) | ||
78 | .join(&format!("\n{}", indent_spaces)); | ||
79 | |||
80 | edit.replace(target, output) | ||
81 | }, | ||
82 | ) | ||
83 | } | ||
84 | |||
85 | fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { | ||
86 | // Find all the comments we'll be collapsing into a block | ||
87 | let comments = relevant_line_comments(&comment); | ||
88 | |||
89 | // Establish the target of our edit based on the comments we found | ||
90 | let target = TextRange::new( | ||
91 | comments[0].syntax().text_range().start(), | ||
92 | comments.last().unwrap().syntax().text_range().end(), | ||
93 | ); | ||
94 | |||
95 | acc.add( | ||
96 | AssistId("line_to_block", AssistKind::RefactorRewrite), | ||
97 | "Replace line comments with a single block comment", | ||
98 | target, | ||
99 | |edit| { | ||
100 | // We pick a single indentation level for the whole block comment based on the | ||
101 | // comment where the assist was invoked. This will be prepended to the | ||
102 | // contents of each line comment when they're put into the block comment. | ||
103 | let indentation = IndentLevel::from_token(&comment.syntax()); | ||
104 | |||
105 | let block_comment_body = | ||
106 | comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); | ||
107 | |||
108 | let block_prefix = | ||
109 | comment_kind_prefix(CommentKind { shape: CommentShape::Block, ..comment.kind() }); | ||
110 | |||
111 | let output = | ||
112 | format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation.to_string()); | ||
113 | |||
114 | edit.replace(target, output) | ||
115 | }, | ||
116 | ) | ||
117 | } | ||
118 | |||
119 | /// The line -> block assist can be invoked from anywhere within a sequence of line comments. | ||
120 | /// relevant_line_comments crawls backwards and forwards finding the complete sequence of comments that will | ||
121 | /// be joined. | ||
122 | fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> { | ||
123 | // The prefix identifies the kind of comment we're dealing with | ||
124 | let prefix = comment.prefix(); | ||
125 | let same_prefix = |c: &ast::Comment| c.prefix() == prefix; | ||
126 | |||
127 | // These tokens are allowed to exist between comments | ||
128 | let skippable = |not: &SyntaxElement| { | ||
129 | not.clone() | ||
130 | .into_token() | ||
131 | .and_then(Whitespace::cast) | ||
132 | .map(|w| !w.spans_multiple_lines()) | ||
133 | .unwrap_or(false) | ||
134 | }; | ||
135 | |||
136 | // Find all preceding comments (in reverse order) that have the same prefix | ||
137 | let prev_comments = comment | ||
138 | .syntax() | ||
139 | .siblings_with_tokens(Direction::Prev) | ||
140 | .filter(|s| !skippable(s)) | ||
141 | .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) | ||
142 | .take_while(|opt_com| opt_com.is_some()) | ||
143 | .filter_map(identity) | ||
144 | .skip(1); // skip the first element so we don't duplicate it in next_comments | ||
145 | |||
146 | let next_comments = comment | ||
147 | .syntax() | ||
148 | .siblings_with_tokens(Direction::Next) | ||
149 | .filter(|s| !skippable(s)) | ||
150 | .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) | ||
151 | .take_while(|opt_com| opt_com.is_some()) | ||
152 | .filter_map(identity); | ||
153 | |||
154 | let mut comments: Vec<_> = prev_comments.collect(); | ||
155 | comments.reverse(); | ||
156 | comments.extend(next_comments); | ||
157 | comments | ||
158 | } | ||
159 | |||
160 | // Line comments usually begin with a single space character following the prefix as seen here: | ||
161 | //^ | ||
162 | // But comments can also include indented text: | ||
163 | // > Hello there | ||
164 | // | ||
165 | // We handle this by stripping *AT MOST* one space character from the start of the line | ||
166 | // This has its own problems because it can cause alignment issues: | ||
167 | // | ||
168 | // /* | ||
169 | // a ----> a | ||
170 | //b ----> b | ||
171 | // */ | ||
172 | // | ||
173 | // But since such comments aren't idiomatic we're okay with this. | ||
174 | fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String { | ||
175 | let contents_without_prefix = comm.text().strip_prefix(comm.prefix()).unwrap(); | ||
176 | let contents = contents_without_prefix.strip_prefix(' ').unwrap_or(contents_without_prefix); | ||
177 | |||
178 | // Don't add the indentation if the line is empty | ||
179 | if contents.is_empty() { | ||
180 | contents.to_owned() | ||
181 | } else { | ||
182 | indentation.to_string() + &contents | ||
183 | } | ||
184 | } | ||
185 | |||
186 | fn comment_kind_prefix(ck: ast::CommentKind) -> &'static str { | ||
187 | match (ck.shape, ck.doc) { | ||
188 | (Line, Some(Inner)) => "//!", | ||
189 | (Line, Some(Outer)) => "///", | ||
190 | (Line, None) => "//", | ||
191 | (Block, Some(Inner)) => "/*!", | ||
192 | (Block, Some(Outer)) => "/**", | ||
193 | (Block, None) => "/*", | ||
194 | } | ||
195 | } | ||
196 | |||
197 | #[cfg(test)] | ||
198 | mod tests { | ||
199 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
200 | |||
201 | use super::*; | ||
202 | |||
203 | #[test] | ||
204 | fn single_line_to_block() { | ||
205 | check_assist( | ||
206 | convert_comment_block, | ||
207 | r#" | ||
208 | // line$0 comment | ||
209 | fn main() { | ||
210 | foo(); | ||
211 | } | ||
212 | "#, | ||
213 | r#" | ||
214 | /* | ||
215 | line comment | ||
216 | */ | ||
217 | fn main() { | ||
218 | foo(); | ||
219 | } | ||
220 | "#, | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn single_line_to_block_indented() { | ||
226 | check_assist( | ||
227 | convert_comment_block, | ||
228 | r#" | ||
229 | fn main() { | ||
230 | // line$0 comment | ||
231 | foo(); | ||
232 | } | ||
233 | "#, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | /* | ||
237 | line comment | ||
238 | */ | ||
239 | foo(); | ||
240 | } | ||
241 | "#, | ||
242 | ); | ||
243 | } | ||
244 | |||
245 | #[test] | ||
246 | fn multiline_to_block() { | ||
247 | check_assist( | ||
248 | convert_comment_block, | ||
249 | r#" | ||
250 | fn main() { | ||
251 | // above | ||
252 | // line$0 comment | ||
253 | // | ||
254 | // below | ||
255 | foo(); | ||
256 | } | ||
257 | "#, | ||
258 | r#" | ||
259 | fn main() { | ||
260 | /* | ||
261 | above | ||
262 | line comment | ||
263 | |||
264 | below | ||
265 | */ | ||
266 | foo(); | ||
267 | } | ||
268 | "#, | ||
269 | ); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
273 | fn end_of_line_to_block() { | ||
274 | check_assist_not_applicable( | ||
275 | convert_comment_block, | ||
276 | r#" | ||
277 | fn main() { | ||
278 | foo(); // end-of-line$0 comment | ||
279 | } | ||
280 | "#, | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | #[test] | ||
285 | fn single_line_different_kinds() { | ||
286 | check_assist( | ||
287 | convert_comment_block, | ||
288 | r#" | ||
289 | fn main() { | ||
290 | /// different prefix | ||
291 | // line$0 comment | ||
292 | // below | ||
293 | foo(); | ||
294 | } | ||
295 | "#, | ||
296 | r#" | ||
297 | fn main() { | ||
298 | /// different prefix | ||
299 | /* | ||
300 | line comment | ||
301 | below | ||
302 | */ | ||
303 | foo(); | ||
304 | } | ||
305 | "#, | ||
306 | ); | ||
307 | } | ||
308 | |||
309 | #[test] | ||
310 | fn single_line_separate_chunks() { | ||
311 | check_assist( | ||
312 | convert_comment_block, | ||
313 | r#" | ||
314 | fn main() { | ||
315 | // different chunk | ||
316 | |||
317 | // line$0 comment | ||
318 | // below | ||
319 | foo(); | ||
320 | } | ||
321 | "#, | ||
322 | r#" | ||
323 | fn main() { | ||
324 | // different chunk | ||
325 | |||
326 | /* | ||
327 | line comment | ||
328 | below | ||
329 | */ | ||
330 | foo(); | ||
331 | } | ||
332 | "#, | ||
333 | ); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
337 | fn doc_block_comment_to_lines() { | ||
338 | check_assist( | ||
339 | convert_comment_block, | ||
340 | r#" | ||
341 | /** | ||
342 | hi$0 there | ||
343 | */ | ||
344 | "#, | ||
345 | r#" | ||
346 | /// hi there | ||
347 | "#, | ||
348 | ); | ||
349 | } | ||
350 | |||
351 | #[test] | ||
352 | fn block_comment_to_lines() { | ||
353 | check_assist( | ||
354 | convert_comment_block, | ||
355 | r#" | ||
356 | /* | ||
357 | hi$0 there | ||
358 | */ | ||
359 | "#, | ||
360 | r#" | ||
361 | // hi there | ||
362 | "#, | ||
363 | ); | ||
364 | } | ||
365 | |||
366 | #[test] | ||
367 | fn inner_doc_block_to_lines() { | ||
368 | check_assist( | ||
369 | convert_comment_block, | ||
370 | r#" | ||
371 | /*! | ||
372 | hi$0 there | ||
373 | */ | ||
374 | "#, | ||
375 | r#" | ||
376 | //! hi there | ||
377 | "#, | ||
378 | ); | ||
379 | } | ||
380 | |||
381 | #[test] | ||
382 | fn block_to_lines_indent() { | ||
383 | check_assist( | ||
384 | convert_comment_block, | ||
385 | r#" | ||
386 | fn main() { | ||
387 | /*! | ||
388 | hi$0 there | ||
389 | |||
390 | ``` | ||
391 | code_sample | ||
392 | ``` | ||
393 | */ | ||
394 | } | ||
395 | "#, | ||
396 | r#" | ||
397 | fn main() { | ||
398 | //! hi there | ||
399 | //! | ||
400 | //! ``` | ||
401 | //! code_sample | ||
402 | //! ``` | ||
403 | } | ||
404 | "#, | ||
405 | ); | ||
406 | } | ||
407 | |||
408 | #[test] | ||
409 | fn end_of_line_block_to_line() { | ||
410 | check_assist_not_applicable( | ||
411 | convert_comment_block, | ||
412 | r#" | ||
413 | fn main() { | ||
414 | foo(); /* end-of-line$0 comment */ | ||
415 | } | ||
416 | "#, | ||
417 | ); | ||
418 | } | ||
419 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 53542d433..9c8148462 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -115,6 +115,7 @@ mod handlers { | |||
115 | mod auto_import; | 115 | mod auto_import; |
116 | mod change_visibility; | 116 | mod change_visibility; |
117 | mod convert_integer_literal; | 117 | mod convert_integer_literal; |
118 | mod convert_comment_block; | ||
118 | mod early_return; | 119 | mod early_return; |
119 | mod expand_glob_import; | 120 | mod expand_glob_import; |
120 | mod extract_function; | 121 | mod extract_function; |
@@ -178,6 +179,7 @@ mod handlers { | |||
178 | auto_import::auto_import, | 179 | auto_import::auto_import, |
179 | change_visibility::change_visibility, | 180 | change_visibility::change_visibility, |
180 | convert_integer_literal::convert_integer_literal, | 181 | convert_integer_literal::convert_integer_literal, |
182 | convert_comment_block::convert_comment_block, | ||
181 | early_return::convert_to_guarded_return, | 183 | early_return::convert_to_guarded_return, |
182 | expand_glob_import::expand_glob_import, | 184 | expand_glob_import::expand_glob_import, |
183 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 185 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs index a8504f762..952b4a97f 100644 --- a/crates/proc_macro_srv/src/rustc_server.rs +++ b/crates/proc_macro_srv/src/rustc_server.rs | |||
@@ -14,7 +14,6 @@ use std::collections::HashMap; | |||
14 | use std::hash::Hash; | 14 | use std::hash::Hash; |
15 | use std::iter::FromIterator; | 15 | use std::iter::FromIterator; |
16 | use std::ops::Bound; | 16 | use std::ops::Bound; |
17 | use std::str::FromStr; | ||
18 | use std::{ascii, vec::IntoIter}; | 17 | use std::{ascii, vec::IntoIter}; |
19 | 18 | ||
20 | type Group = tt::Subtree; | 19 | type Group = tt::Subtree; |
@@ -278,6 +277,42 @@ impl server::FreeFunctions for Rustc { | |||
278 | } | 277 | } |
279 | } | 278 | } |
280 | 279 | ||
280 | fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree { | ||
281 | tt::Subtree { | ||
282 | delimiter: subtree.delimiter.map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }), | ||
283 | token_trees: subtree | ||
284 | .token_trees | ||
285 | .into_iter() | ||
286 | .map(|t| token_tree_replace_token_ids_with_unspecified(t)) | ||
287 | .collect(), | ||
288 | } | ||
289 | } | ||
290 | |||
291 | fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree { | ||
292 | match tt { | ||
293 | tt::TokenTree::Leaf(leaf) => { | ||
294 | tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf)) | ||
295 | } | ||
296 | tt::TokenTree::Subtree(subtree) => { | ||
297 | tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree)) | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | |||
302 | fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf { | ||
303 | match leaf { | ||
304 | tt::Leaf::Literal(lit) => { | ||
305 | tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit }) | ||
306 | } | ||
307 | tt::Leaf::Punct(punct) => { | ||
308 | tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct }) | ||
309 | } | ||
310 | tt::Leaf::Ident(ident) => { | ||
311 | tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident }) | ||
312 | } | ||
313 | } | ||
314 | } | ||
315 | |||
281 | impl server::TokenStream for Rustc { | 316 | impl server::TokenStream for Rustc { |
282 | fn new(&mut self) -> Self::TokenStream { | 317 | fn new(&mut self) -> Self::TokenStream { |
283 | Self::TokenStream::new() | 318 | Self::TokenStream::new() |
@@ -287,7 +322,8 @@ impl server::TokenStream for Rustc { | |||
287 | stream.is_empty() | 322 | stream.is_empty() |
288 | } | 323 | } |
289 | fn from_str(&mut self, src: &str) -> Self::TokenStream { | 324 | fn from_str(&mut self, src: &str) -> Self::TokenStream { |
290 | Self::TokenStream::from_str(src).expect("cannot parse string") | 325 | let (subtree, _) = mbe::parse_to_token_tree(src).expect("cannot parse string"); |
326 | TokenStream::with_subtree(subtree_replace_token_ids_with_unspecified(subtree)) | ||
291 | } | 327 | } |
292 | fn to_string(&mut self, stream: &Self::TokenStream) -> String { | 328 | fn to_string(&mut self, stream: &Self::TokenStream) -> String { |
293 | stream.to_string() | 329 | stream.to_string() |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index b881cc229..8789f0852 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -24,7 +24,7 @@ jod-thread = "0.1.0" | |||
24 | log = "0.4.8" | 24 | log = "0.4.8" |
25 | lsp-types = { version = "0.88.0", features = ["proposed"] } | 25 | lsp-types = { version = "0.88.0", features = ["proposed"] } |
26 | parking_lot = "0.11.0" | 26 | parking_lot = "0.11.0" |
27 | pico-args = "0.4.0" | 27 | xflags = "0.1.2" |
28 | oorandom = "11.1.2" | 28 | oorandom = "11.1.2" |
29 | rustc-hash = "1.1.0" | 29 | rustc-hash = "1.1.0" |
30 | serde = { version = "1.0.106", features = ["derive"] } | 30 | serde = { version = "1.0.106", features = ["derive"] } |
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs deleted file mode 100644 index 164d94a30..000000000 --- a/crates/rust-analyzer/src/bin/args.rs +++ /dev/null | |||
@@ -1,274 +0,0 @@ | |||
1 | //! Command like parsing for rust-analyzer. | ||
2 | //! | ||
3 | //! If run started args, we run the LSP server loop. With a subcommand, we do a | ||
4 | //! one-time batch processing. | ||
5 | |||
6 | use std::{env, path::PathBuf}; | ||
7 | |||
8 | use anyhow::{bail, format_err, Result}; | ||
9 | use ide_ssr::{SsrPattern, SsrRule}; | ||
10 | use pico_args::Arguments; | ||
11 | use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; | ||
12 | use vfs::AbsPathBuf; | ||
13 | |||
14 | pub(crate) struct Args { | ||
15 | pub(crate) verbosity: Verbosity, | ||
16 | pub(crate) log_file: Option<PathBuf>, | ||
17 | pub(crate) no_buffering: bool, | ||
18 | pub(crate) command: Command, | ||
19 | #[allow(unused)] | ||
20 | pub(crate) wait_dbg: bool, | ||
21 | } | ||
22 | |||
23 | pub(crate) enum Command { | ||
24 | Parse { no_dump: bool }, | ||
25 | Symbols, | ||
26 | Highlight { rainbow: bool }, | ||
27 | AnalysisStats(AnalysisStatsCmd), | ||
28 | Bench(BenchCmd), | ||
29 | Diagnostics { path: PathBuf, load_output_dirs: bool, with_proc_macro: bool }, | ||
30 | Ssr { rules: Vec<SsrRule> }, | ||
31 | StructuredSearch { debug_snippet: Option<String>, patterns: Vec<SsrPattern> }, | ||
32 | ProcMacro, | ||
33 | RunServer, | ||
34 | PrintConfigSchema, | ||
35 | Version, | ||
36 | Help, | ||
37 | } | ||
38 | |||
39 | const HELP: &str = "\ | ||
40 | rust-analyzer | ||
41 | |||
42 | USAGE: | ||
43 | rust-analyzer [FLAGS] [COMMAND] [COMMAND_OPTIONS] | ||
44 | |||
45 | FLAGS: | ||
46 | --version Print version | ||
47 | -h, --help Print this help | ||
48 | |||
49 | -v, --verbose | ||
50 | -vv, --spammy | ||
51 | -q, --quiet Set verbosity | ||
52 | |||
53 | --print-config-schema | ||
54 | Dump a LSP config JSON schema | ||
55 | --log-file <PATH> Log to the specified file instead of stderr | ||
56 | --no-log-buffering | ||
57 | Flush log records to the file immediately | ||
58 | |||
59 | --wait-dbg Wait until a debugger is attached to. | ||
60 | The flag is valid for debug builds only | ||
61 | |||
62 | ENVIRONMENTAL VARIABLES: | ||
63 | RA_LOG Set log filter in env_logger format | ||
64 | RA_PROFILE Enable hierarchical profiler | ||
65 | RA_WAIT_DBG If set acts like a --wait-dbg flag | ||
66 | |||
67 | COMMANDS: | ||
68 | |||
69 | not specified Launch LSP server | ||
70 | |||
71 | parse < main.rs Parse tree | ||
72 | --no-dump Suppress printing | ||
73 | |||
74 | symbols < main.rs Parse input an print the list of symbols | ||
75 | |||
76 | highlight < main.rs Highlight input as html | ||
77 | --rainbow Enable rainbow highlighting of identifiers | ||
78 | |||
79 | analysis-stats <PATH> Batch typecheck project and print summary statistics | ||
80 | <PATH> Directory with Cargo.toml | ||
81 | --randomize Randomize order in which crates, modules, and items are processed | ||
82 | --parallel Run type inference in parallel | ||
83 | --memory-usage Collect memory usage statistics | ||
84 | -o, --only <PATH> Only analyze items matching this path | ||
85 | --with-deps Also analyze all dependencies | ||
86 | --load-output-dirs | ||
87 | Load OUT_DIR values by running `cargo check` before analysis | ||
88 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
89 | |||
90 | analysis-bench <PATH> Benchmark specific analysis operation | ||
91 | <PATH> Directory with Cargo.toml | ||
92 | --highlight <PATH> | ||
93 | Compute syntax highlighting for this file | ||
94 | --complete <PATH:LINE:COLUMN> | ||
95 | Compute completions at this location | ||
96 | --goto-def <PATH:LINE:COLUMN> | ||
97 | Compute goto definition at this location | ||
98 | --memory-usage Collect memory usage statistics | ||
99 | --load-output-dirs | ||
100 | Load OUT_DIR values by running `cargo check` before analysis | ||
101 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
102 | |||
103 | diagnostics <PATH> | ||
104 | <PATH> Directory with Cargo.toml | ||
105 | --load-output-dirs | ||
106 | Load OUT_DIR values by running `cargo check` before analysis | ||
107 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
108 | |||
109 | ssr [RULE...] | ||
110 | <RULE> A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) | ||
111 | |||
112 | search [PATTERN..] | ||
113 | <PATTERN> A structured search replace pattern (`$a.foo($b)`) | ||
114 | --debug <snippet> Prints debug information for any nodes with source exactly | ||
115 | equal to <snippet> | ||
116 | "; | ||
117 | |||
118 | impl Args { | ||
119 | pub(crate) fn parse() -> Result<Args> { | ||
120 | let mut matches = Arguments::from_env(); | ||
121 | |||
122 | if matches.contains("--version") { | ||
123 | finish_args(matches)?; | ||
124 | return Ok(Args { | ||
125 | verbosity: Verbosity::Normal, | ||
126 | log_file: None, | ||
127 | command: Command::Version, | ||
128 | no_buffering: false, | ||
129 | wait_dbg: false, | ||
130 | }); | ||
131 | } | ||
132 | |||
133 | let verbosity = match ( | ||
134 | matches.contains(["-vv", "--spammy"]), | ||
135 | matches.contains(["-v", "--verbose"]), | ||
136 | matches.contains(["-q", "--quiet"]), | ||
137 | ) { | ||
138 | (true, _, true) => bail!("Invalid flags: -q conflicts with -vv"), | ||
139 | (true, _, false) => Verbosity::Spammy, | ||
140 | (false, false, false) => Verbosity::Normal, | ||
141 | (false, false, true) => Verbosity::Quiet, | ||
142 | (false, true, false) => Verbosity::Verbose, | ||
143 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), | ||
144 | }; | ||
145 | let log_file = matches.opt_value_from_str("--log-file")?; | ||
146 | let no_buffering = matches.contains("--no-log-buffering"); | ||
147 | let wait_dbg = matches.contains("--wait-dbg"); | ||
148 | |||
149 | if matches.contains(["-h", "--help"]) { | ||
150 | eprintln!("{}", HELP); | ||
151 | return Ok(Args { | ||
152 | verbosity, | ||
153 | log_file: None, | ||
154 | command: Command::Help, | ||
155 | no_buffering, | ||
156 | wait_dbg, | ||
157 | }); | ||
158 | } | ||
159 | |||
160 | if matches.contains("--print-config-schema") { | ||
161 | return Ok(Args { | ||
162 | verbosity, | ||
163 | log_file, | ||
164 | command: Command::PrintConfigSchema, | ||
165 | no_buffering, | ||
166 | wait_dbg, | ||
167 | }); | ||
168 | } | ||
169 | |||
170 | let subcommand = match matches.subcommand()? { | ||
171 | Some(it) => it, | ||
172 | None => { | ||
173 | finish_args(matches)?; | ||
174 | return Ok(Args { | ||
175 | verbosity, | ||
176 | log_file, | ||
177 | command: Command::RunServer, | ||
178 | no_buffering, | ||
179 | wait_dbg, | ||
180 | }); | ||
181 | } | ||
182 | }; | ||
183 | let command = match subcommand.as_str() { | ||
184 | "parse" => Command::Parse { no_dump: matches.contains("--no-dump") }, | ||
185 | "symbols" => Command::Symbols, | ||
186 | "highlight" => Command::Highlight { rainbow: matches.contains("--rainbow") }, | ||
187 | "analysis-stats" => Command::AnalysisStats(AnalysisStatsCmd { | ||
188 | randomize: matches.contains("--randomize"), | ||
189 | parallel: matches.contains("--parallel"), | ||
190 | memory_usage: matches.contains("--memory-usage"), | ||
191 | only: matches.opt_value_from_str(["-o", "--only"])?, | ||
192 | with_deps: matches.contains("--with-deps"), | ||
193 | load_output_dirs: matches.contains("--load-output-dirs"), | ||
194 | with_proc_macro: matches.contains("--with-proc-macro"), | ||
195 | path: matches | ||
196 | .opt_free_from_str()? | ||
197 | .ok_or_else(|| format_err!("expected positional argument"))?, | ||
198 | }), | ||
199 | "analysis-bench" => Command::Bench(BenchCmd { | ||
200 | what: { | ||
201 | let highlight_path: Option<String> = | ||
202 | matches.opt_value_from_str("--highlight")?; | ||
203 | let complete_path: Option<Position> = | ||
204 | matches.opt_value_from_str("--complete")?; | ||
205 | let goto_def_path: Option<Position> = | ||
206 | matches.opt_value_from_str("--goto-def")?; | ||
207 | match (highlight_path, complete_path, goto_def_path) { | ||
208 | (Some(path), None, None) => { | ||
209 | let path = env::current_dir().unwrap().join(path); | ||
210 | BenchWhat::Highlight { path: AbsPathBuf::assert(path) } | ||
211 | } | ||
212 | (None, Some(position), None) => BenchWhat::Complete(position), | ||
213 | (None, None, Some(position)) => BenchWhat::GotoDef(position), | ||
214 | _ => panic!( | ||
215 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" | ||
216 | ), | ||
217 | } | ||
218 | }, | ||
219 | memory_usage: matches.contains("--memory-usage"), | ||
220 | load_output_dirs: matches.contains("--load-output-dirs"), | ||
221 | with_proc_macro: matches.contains("--with-proc-macro"), | ||
222 | path: matches | ||
223 | .opt_free_from_str()? | ||
224 | .ok_or_else(|| format_err!("expected positional argument"))?, | ||
225 | }), | ||
226 | "diagnostics" => Command::Diagnostics { | ||
227 | load_output_dirs: matches.contains("--load-output-dirs"), | ||
228 | with_proc_macro: matches.contains("--with-proc-macro"), | ||
229 | path: matches | ||
230 | .opt_free_from_str()? | ||
231 | .ok_or_else(|| format_err!("expected positional argument"))?, | ||
232 | }, | ||
233 | "proc-macro" => Command::ProcMacro, | ||
234 | "ssr" => Command::Ssr { | ||
235 | rules: { | ||
236 | let mut acc = Vec::new(); | ||
237 | while let Some(rule) = matches.opt_free_from_str()? { | ||
238 | acc.push(rule); | ||
239 | } | ||
240 | acc | ||
241 | }, | ||
242 | }, | ||
243 | "search" => Command::StructuredSearch { | ||
244 | debug_snippet: matches.opt_value_from_str("--debug")?, | ||
245 | patterns: { | ||
246 | let mut acc = Vec::new(); | ||
247 | while let Some(rule) = matches.opt_free_from_str()? { | ||
248 | acc.push(rule); | ||
249 | } | ||
250 | acc | ||
251 | }, | ||
252 | }, | ||
253 | _ => { | ||
254 | eprintln!("{}", HELP); | ||
255 | return Ok(Args { | ||
256 | verbosity, | ||
257 | log_file: None, | ||
258 | command: Command::Help, | ||
259 | no_buffering, | ||
260 | wait_dbg, | ||
261 | }); | ||
262 | } | ||
263 | }; | ||
264 | finish_args(matches)?; | ||
265 | Ok(Args { verbosity, log_file, command, no_buffering, wait_dbg }) | ||
266 | } | ||
267 | } | ||
268 | |||
269 | fn finish_args(args: Arguments) -> Result<()> { | ||
270 | if !args.finish().is_empty() { | ||
271 | bail!("Unused arguments."); | ||
272 | } | ||
273 | Ok(()) | ||
274 | } | ||
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs new file mode 100644 index 000000000..244912d26 --- /dev/null +++ b/crates/rust-analyzer/src/bin/flags.rs | |||
@@ -0,0 +1,251 @@ | |||
1 | //! Grammar for the command-line arguments. | ||
2 | #![allow(unreachable_pub)] | ||
3 | use std::{env, path::PathBuf}; | ||
4 | |||
5 | use ide_ssr::{SsrPattern, SsrRule}; | ||
6 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; | ||
7 | use vfs::AbsPathBuf; | ||
8 | |||
9 | xflags::args_parser! { | ||
10 | /// LSP server for the Rust programming language. | ||
11 | cmd rust-analyzer { | ||
12 | /// Verbosity level, can be repeated multiple times. | ||
13 | repeated -v, --verbose | ||
14 | /// Verbosity level. | ||
15 | optional -q, --quiet | ||
16 | |||
17 | /// Log to the specified file instead of stderr. | ||
18 | optional --log-file path: PathBuf | ||
19 | /// Flush log records to the file immediately. | ||
20 | optional --no-log-buffering | ||
21 | |||
22 | /// Wait until a debugger is attached to (requires debug build). | ||
23 | optional --wait-dbg | ||
24 | |||
25 | default cmd lsp-server { | ||
26 | /// Print version. | ||
27 | optional --version | ||
28 | /// Print help. | ||
29 | optional -h, --help | ||
30 | |||
31 | /// Dump a LSP config JSON schema. | ||
32 | optional --print-config-schema | ||
33 | } | ||
34 | |||
35 | /// Parse stdin. | ||
36 | cmd parse { | ||
37 | /// Suppress printing. | ||
38 | optional --no-dump | ||
39 | } | ||
40 | |||
41 | /// Parse stdin and print the list of symbols. | ||
42 | cmd symbols {} | ||
43 | |||
44 | /// Highlight stdin as html. | ||
45 | cmd highlight { | ||
46 | /// Enable rainbow highlighting of identifiers. | ||
47 | optional --rainbow | ||
48 | } | ||
49 | |||
50 | /// Batch typecheck project and print summary statistics | ||
51 | cmd analysis-stats | ||
52 | /// Directory with Cargo.toml. | ||
53 | required path: PathBuf | ||
54 | { | ||
55 | /// Randomize order in which crates, modules, and items are processed. | ||
56 | optional --randomize | ||
57 | /// Run type inference in parallel. | ||
58 | optional --parallel | ||
59 | /// Collect memory usage statistics. | ||
60 | optional --memory-usage | ||
61 | |||
62 | /// Only analyze items matching this path. | ||
63 | optional -o, --only path: String | ||
64 | /// Also analyze all dependencies. | ||
65 | optional --with-deps | ||
66 | |||
67 | /// Load OUT_DIR values by running `cargo check` before analysis. | ||
68 | optional --load-output-dirs | ||
69 | /// Use proc-macro-srv for proc-macro expanding. | ||
70 | optional --with-proc-macro | ||
71 | } | ||
72 | |||
73 | /// Benchmark specific analysis operation | ||
74 | cmd analysis-bench | ||
75 | /// Directory with Cargo.toml. | ||
76 | required path: PathBuf | ||
77 | { | ||
78 | /// Collect memory usage statistics. | ||
79 | optional --memory-usage | ||
80 | |||
81 | /// Compute syntax highlighting for this file | ||
82 | optional --highlight path: PathBuf | ||
83 | /// Compute completions at file:line:column location. | ||
84 | optional --complete location: Position | ||
85 | /// Compute goto definition at file:line:column location. | ||
86 | optional --goto-def location: Position | ||
87 | |||
88 | /// Load OUT_DIR values by running `cargo check` before analysis. | ||
89 | optional --load-output-dirs | ||
90 | /// Use proc-macro-srv for proc-macro expanding. | ||
91 | optional --with-proc-macro | ||
92 | } | ||
93 | |||
94 | cmd diagnostics | ||
95 | /// Directory with Cargo.toml. | ||
96 | required path: PathBuf | ||
97 | { | ||
98 | /// Load OUT_DIR values by running `cargo check` before analysis. | ||
99 | optional --load-output-dirs | ||
100 | /// Use proc-macro-srv for proc-macro expanding. | ||
101 | optional --with-proc-macro | ||
102 | } | ||
103 | |||
104 | cmd ssr | ||
105 | /// A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) | ||
106 | repeated rule: SsrRule | ||
107 | {} | ||
108 | |||
109 | cmd search | ||
110 | /// A structured search replace pattern (`$a.foo($b)`) | ||
111 | repeated pattern: SsrPattern | ||
112 | { | ||
113 | /// Prints debug information for any nodes with source exactly equal to snippet. | ||
114 | optional --debug snippet: String | ||
115 | } | ||
116 | |||
117 | cmd proc-macro {} | ||
118 | } | ||
119 | } | ||
120 | |||
121 | // generated start | ||
122 | // The following code is generated by `xflags` macro. | ||
123 | // Run `env XFLAGS_DUMP= cargo build` to regenerate. | ||
124 | #[derive(Debug)] | ||
125 | pub struct RustAnalyzer { | ||
126 | pub verbose: u32, | ||
127 | pub quiet: bool, | ||
128 | pub log_file: Option<PathBuf>, | ||
129 | pub no_log_buffering: bool, | ||
130 | pub wait_dbg: bool, | ||
131 | pub subcommand: RustAnalyzerCmd, | ||
132 | } | ||
133 | |||
134 | #[derive(Debug)] | ||
135 | pub enum RustAnalyzerCmd { | ||
136 | LspServer(LspServer), | ||
137 | Parse(Parse), | ||
138 | Symbols(Symbols), | ||
139 | Highlight(Highlight), | ||
140 | AnalysisStats(AnalysisStats), | ||
141 | AnalysisBench(AnalysisBench), | ||
142 | Diagnostics(Diagnostics), | ||
143 | Ssr(Ssr), | ||
144 | Search(Search), | ||
145 | ProcMacro(ProcMacro), | ||
146 | } | ||
147 | |||
148 | #[derive(Debug)] | ||
149 | pub struct LspServer { | ||
150 | pub version: bool, | ||
151 | pub help: bool, | ||
152 | pub print_config_schema: bool, | ||
153 | } | ||
154 | |||
155 | #[derive(Debug)] | ||
156 | pub struct Parse { | ||
157 | pub no_dump: bool, | ||
158 | } | ||
159 | |||
160 | #[derive(Debug)] | ||
161 | pub struct Symbols {} | ||
162 | |||
163 | #[derive(Debug)] | ||
164 | pub struct Highlight { | ||
165 | pub rainbow: bool, | ||
166 | } | ||
167 | |||
168 | #[derive(Debug)] | ||
169 | pub struct AnalysisStats { | ||
170 | pub path: PathBuf, | ||
171 | |||
172 | pub randomize: bool, | ||
173 | pub parallel: bool, | ||
174 | pub memory_usage: bool, | ||
175 | pub only: Option<String>, | ||
176 | pub with_deps: bool, | ||
177 | pub load_output_dirs: bool, | ||
178 | pub with_proc_macro: bool, | ||
179 | } | ||
180 | |||
181 | #[derive(Debug)] | ||
182 | pub struct AnalysisBench { | ||
183 | pub path: PathBuf, | ||
184 | |||
185 | pub memory_usage: bool, | ||
186 | pub highlight: Option<PathBuf>, | ||
187 | pub complete: Option<Position>, | ||
188 | pub goto_def: Option<Position>, | ||
189 | pub load_output_dirs: bool, | ||
190 | pub with_proc_macro: bool, | ||
191 | } | ||
192 | |||
193 | #[derive(Debug)] | ||
194 | pub struct Diagnostics { | ||
195 | pub path: PathBuf, | ||
196 | |||
197 | pub load_output_dirs: bool, | ||
198 | pub with_proc_macro: bool, | ||
199 | } | ||
200 | |||
201 | #[derive(Debug)] | ||
202 | pub struct Ssr { | ||
203 | pub rule: Vec<SsrRule>, | ||
204 | } | ||
205 | |||
206 | #[derive(Debug)] | ||
207 | pub struct Search { | ||
208 | pub pattern: Vec<SsrPattern>, | ||
209 | |||
210 | pub debug: Option<String>, | ||
211 | } | ||
212 | |||
213 | #[derive(Debug)] | ||
214 | pub struct ProcMacro {} | ||
215 | |||
216 | impl RustAnalyzer { | ||
217 | pub const HELP: &'static str = Self::_HELP; | ||
218 | |||
219 | pub fn from_env() -> xflags::Result<Self> { | ||
220 | let mut p = xflags::rt::Parser::new_from_env(); | ||
221 | Self::_parse(&mut p) | ||
222 | } | ||
223 | } | ||
224 | // generated end | ||
225 | |||
226 | impl RustAnalyzer { | ||
227 | pub(crate) fn verbosity(&self) -> Verbosity { | ||
228 | if self.quiet { | ||
229 | return Verbosity::Quiet; | ||
230 | } | ||
231 | match self.verbose { | ||
232 | 0 => Verbosity::Normal, | ||
233 | 1 => Verbosity::Verbose, | ||
234 | _ => Verbosity::Spammy, | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | impl AnalysisBench { | ||
240 | pub(crate) fn what(&self) -> BenchWhat { | ||
241 | match (&self.highlight, &self.complete, &self.goto_def) { | ||
242 | (Some(path), None, None) => { | ||
243 | let path = env::current_dir().unwrap().join(path); | ||
244 | BenchWhat::Highlight { path: AbsPathBuf::assert(path) } | ||
245 | } | ||
246 | (None, Some(position), None) => BenchWhat::Complete(position.clone()), | ||
247 | (None, None, Some(position)) => BenchWhat::GotoDef(position.clone()), | ||
248 | _ => panic!("exactly one of `--highlight`, `--complete` or `--goto-def` must be set"), | ||
249 | } | ||
250 | } | ||
251 | } | ||
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 89482b952..288847980 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -1,14 +1,20 @@ | |||
1 | //! Driver for rust-analyzer. | 1 | //! Driver for rust-analyzer. |
2 | //! | 2 | //! |
3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis | 3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis |
4 | mod args; | 4 | mod flags; |
5 | mod logger; | 5 | mod logger; |
6 | 6 | ||
7 | use std::{convert::TryFrom, env, fs, path::PathBuf, process}; | 7 | use std::{convert::TryFrom, env, fs, path::Path, process}; |
8 | 8 | ||
9 | use lsp_server::Connection; | 9 | use lsp_server::Connection; |
10 | use project_model::ProjectManifest; | 10 | use project_model::ProjectManifest; |
11 | use rust_analyzer::{cli, config::Config, from_json, lsp_ext::supports_utf8, Result}; | 11 | use rust_analyzer::{ |
12 | cli::{self, AnalysisStatsCmd, BenchCmd}, | ||
13 | config::Config, | ||
14 | from_json, | ||
15 | lsp_ext::supports_utf8, | ||
16 | Result, | ||
17 | }; | ||
12 | use vfs::AbsPathBuf; | 18 | use vfs::AbsPathBuf; |
13 | 19 | ||
14 | #[cfg(all(feature = "mimalloc"))] | 20 | #[cfg(all(feature = "mimalloc"))] |
@@ -28,10 +34,10 @@ fn main() { | |||
28 | } | 34 | } |
29 | 35 | ||
30 | fn try_main() -> Result<()> { | 36 | fn try_main() -> Result<()> { |
31 | let args = args::Args::parse()?; | 37 | let flags = flags::RustAnalyzer::from_env()?; |
32 | 38 | ||
33 | #[cfg(debug_assertions)] | 39 | #[cfg(debug_assertions)] |
34 | if args.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { | 40 | if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { |
35 | #[allow(unused_mut)] | 41 | #[allow(unused_mut)] |
36 | let mut d = 4; | 42 | let mut d = 4; |
37 | while d == 4 { | 43 | while d == 4 { |
@@ -39,35 +45,62 @@ fn try_main() -> Result<()> { | |||
39 | } | 45 | } |
40 | } | 46 | } |
41 | 47 | ||
42 | setup_logging(args.log_file, args.no_buffering)?; | 48 | setup_logging(flags.log_file.as_deref(), flags.no_log_buffering)?; |
43 | match args.command { | 49 | let verbosity = flags.verbosity(); |
44 | args::Command::RunServer => run_server()?, | 50 | |
45 | args::Command::PrintConfigSchema => { | 51 | match flags.subcommand { |
46 | println!("{:#}", Config::json_schema()); | 52 | flags::RustAnalyzerCmd::LspServer(cmd) => { |
53 | if cmd.print_config_schema { | ||
54 | println!("{:#}", Config::json_schema()); | ||
55 | return Ok(()); | ||
56 | } | ||
57 | if cmd.version { | ||
58 | println!("rust-analyzer {}", env!("REV")); | ||
59 | return Ok(()); | ||
60 | } | ||
61 | if cmd.help { | ||
62 | println!("{}", flags::RustAnalyzer::HELP); | ||
63 | return Ok(()); | ||
64 | } | ||
65 | run_server()? | ||
47 | } | 66 | } |
48 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, | 67 | flags::RustAnalyzerCmd::ProcMacro(_) => proc_macro_srv::cli::run()?, |
49 | 68 | flags::RustAnalyzerCmd::Parse(cmd) => cli::parse(cmd.no_dump)?, | |
50 | args::Command::Parse { no_dump } => cli::parse(no_dump)?, | 69 | flags::RustAnalyzerCmd::Symbols(_) => cli::symbols()?, |
51 | args::Command::Symbols => cli::symbols()?, | 70 | flags::RustAnalyzerCmd::Highlight(cmd) => cli::highlight(cmd.rainbow)?, |
52 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, | 71 | flags::RustAnalyzerCmd::AnalysisStats(cmd) => AnalysisStatsCmd { |
53 | args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, | 72 | randomize: cmd.randomize, |
54 | args::Command::Bench(cmd) => cmd.run(args.verbosity)?, | 73 | parallel: cmd.parallel, |
55 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => { | 74 | memory_usage: cmd.memory_usage, |
56 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)? | 75 | only: cmd.only, |
76 | with_deps: cmd.with_deps, | ||
77 | path: cmd.path, | ||
78 | load_output_dirs: cmd.load_output_dirs, | ||
79 | with_proc_macro: cmd.with_proc_macro, | ||
57 | } | 80 | } |
58 | args::Command::Ssr { rules } => { | 81 | .run(verbosity)?, |
59 | cli::apply_ssr_rules(rules)?; | 82 | flags::RustAnalyzerCmd::AnalysisBench(cmd) => { |
83 | let what = cmd.what(); | ||
84 | BenchCmd { | ||
85 | memory_usage: cmd.memory_usage, | ||
86 | path: cmd.path, | ||
87 | load_output_dirs: cmd.load_output_dirs, | ||
88 | with_proc_macro: cmd.with_proc_macro, | ||
89 | what, | ||
90 | } | ||
91 | .run(verbosity)? | ||
60 | } | 92 | } |
61 | args::Command::StructuredSearch { patterns, debug_snippet } => { | 93 | |
62 | cli::search_for_patterns(patterns, debug_snippet)?; | 94 | flags::RustAnalyzerCmd::Diagnostics(cmd) => { |
95 | cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)? | ||
63 | } | 96 | } |
64 | args::Command::Version => println!("rust-analyzer {}", env!("REV")), | 97 | flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?, |
65 | args::Command::Help => {} | 98 | flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?, |
66 | } | 99 | } |
67 | Ok(()) | 100 | Ok(()) |
68 | } | 101 | } |
69 | 102 | ||
70 | fn setup_logging(log_file: Option<PathBuf>, no_buffering: bool) -> Result<()> { | 103 | fn setup_logging(log_file: Option<&Path>, no_buffering: bool) -> Result<()> { |
71 | env::set_var("RUST_BACKTRACE", "short"); | 104 | env::set_var("RUST_BACKTRACE", "short"); |
72 | 105 | ||
73 | let log_file = match log_file { | 106 | let log_file = match log_file { |
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index 8991f3bdb..3bd7e678d 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -35,6 +35,7 @@ pub enum BenchWhat { | |||
35 | GotoDef(Position), | 35 | GotoDef(Position), |
36 | } | 36 | } |
37 | 37 | ||
38 | #[derive(Debug, Clone)] | ||
38 | pub struct Position { | 39 | pub struct Position { |
39 | pub path: AbsPathBuf, | 40 | pub path: AbsPathBuf, |
40 | pub line: u32, | 41 | pub line: u32, |
@@ -68,7 +69,7 @@ impl BenchCmd { | |||
68 | load_out_dirs_from_check: self.load_output_dirs, | 69 | load_out_dirs_from_check: self.load_output_dirs, |
69 | with_proc_macro: self.with_proc_macro, | 70 | with_proc_macro: self.with_proc_macro, |
70 | }; | 71 | }; |
71 | let (mut host, vfs) = | 72 | let (mut host, vfs, _proc_macro) = |
72 | load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; | 73 | load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; |
73 | eprintln!("{:?}\n", start.elapsed()); | 74 | eprintln!("{:?}\n", start.elapsed()); |
74 | 75 | ||
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 9072d8944..ad0759bda 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs | |||
@@ -64,7 +64,7 @@ impl AnalysisStatsCmd { | |||
64 | load_out_dirs_from_check: self.load_output_dirs, | 64 | load_out_dirs_from_check: self.load_output_dirs, |
65 | with_proc_macro: self.with_proc_macro, | 65 | with_proc_macro: self.with_proc_macro, |
66 | }; | 66 | }; |
67 | let (host, vfs) = | 67 | let (host, vfs, _proc_macro) = |
68 | load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; | 68 | load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; |
69 | let db = host.raw_database(); | 69 | let db = host.raw_database(); |
70 | eprintln!("{:<20} {}", "Database loaded:", db_load_sw.elapsed()); | 70 | eprintln!("{:<20} {}", "Database loaded:", db_load_sw.elapsed()); |
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 876f6c44f..8b985716b 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs | |||
@@ -35,7 +35,8 @@ pub fn diagnostics( | |||
35 | ) -> Result<()> { | 35 | ) -> Result<()> { |
36 | let cargo_config = Default::default(); | 36 | let cargo_config = Default::default(); |
37 | let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check, with_proc_macro }; | 37 | let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check, with_proc_macro }; |
38 | let (host, _vfs) = load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | 38 | let (host, _vfs, _proc_macro) = |
39 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | ||
39 | let db = host.raw_database(); | 40 | let db = host.raw_database(); |
40 | let analysis = host.analysis(); | 41 | let analysis = host.analysis(); |
41 | 42 | ||
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 23442afac..310c36904 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -23,7 +23,7 @@ pub fn load_workspace_at( | |||
23 | cargo_config: &CargoConfig, | 23 | cargo_config: &CargoConfig, |
24 | load_config: &LoadCargoConfig, | 24 | load_config: &LoadCargoConfig, |
25 | progress: &dyn Fn(String), | 25 | progress: &dyn Fn(String), |
26 | ) -> Result<(AnalysisHost, vfs::Vfs)> { | 26 | ) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroClient>)> { |
27 | let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); | 27 | let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); |
28 | let root = ProjectManifest::discover_single(&root)?; | 28 | let root = ProjectManifest::discover_single(&root)?; |
29 | let workspace = ProjectWorkspace::load(root, cargo_config, progress)?; | 29 | let workspace = ProjectWorkspace::load(root, cargo_config, progress)?; |
@@ -35,7 +35,7 @@ pub fn load_workspace( | |||
35 | ws: ProjectWorkspace, | 35 | ws: ProjectWorkspace, |
36 | config: &LoadCargoConfig, | 36 | config: &LoadCargoConfig, |
37 | progress: &dyn Fn(String), | 37 | progress: &dyn Fn(String), |
38 | ) -> Result<(AnalysisHost, vfs::Vfs)> { | 38 | ) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroClient>)> { |
39 | let (sender, receiver) = unbounded(); | 39 | let (sender, receiver) = unbounded(); |
40 | let mut vfs = vfs::Vfs::default(); | 40 | let mut vfs = vfs::Vfs::default(); |
41 | let mut loader = { | 41 | let mut loader = { |
@@ -80,7 +80,7 @@ pub fn load_workspace( | |||
80 | log::debug!("crate graph: {:?}", crate_graph); | 80 | log::debug!("crate graph: {:?}", crate_graph); |
81 | let host = | 81 | let host = |
82 | load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); | 82 | load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); |
83 | Ok((host, vfs)) | 83 | Ok((host, vfs, proc_macro_client)) |
84 | } | 84 | } |
85 | 85 | ||
86 | fn load_crate_graph( | 86 | fn load_crate_graph( |
@@ -138,7 +138,8 @@ mod tests { | |||
138 | let cargo_config = Default::default(); | 138 | let cargo_config = Default::default(); |
139 | let load_cargo_config = | 139 | let load_cargo_config = |
140 | LoadCargoConfig { load_out_dirs_from_check: false, with_proc_macro: false }; | 140 | LoadCargoConfig { load_out_dirs_from_check: false, with_proc_macro: false }; |
141 | let (host, _vfs) = load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | 141 | let (host, _vfs, _proc_macro) = |
142 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | ||
142 | 143 | ||
143 | let n_crates = Crate::all(host.raw_database()).len(); | 144 | let n_crates = Crate::all(host.raw_database()).len(); |
144 | // RA has quite a few crates, but the exact count doesn't matter | 145 | // RA has quite a few crates, but the exact count doesn't matter |
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 71a8f8fb9..79f426fff 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/crates/rust-analyzer/src/cli/ssr.rs | |||
@@ -11,7 +11,7 @@ pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { | |||
11 | let cargo_config = Default::default(); | 11 | let cargo_config = Default::default(); |
12 | let load_cargo_config = | 12 | let load_cargo_config = |
13 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; | 13 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; |
14 | let (host, vfs) = | 14 | let (host, vfs, _proc_macro) = |
15 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; | 15 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; |
16 | let db = host.raw_database(); | 16 | let db = host.raw_database(); |
17 | let mut match_finder = MatchFinder::at_first_file(db)?; | 17 | let mut match_finder = MatchFinder::at_first_file(db)?; |
@@ -38,7 +38,7 @@ pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<Stri | |||
38 | let cargo_config = Default::default(); | 38 | let cargo_config = Default::default(); |
39 | let load_cargo_config = | 39 | let load_cargo_config = |
40 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; | 40 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; |
41 | let (host, _vfs) = | 41 | let (host, _vfs, _proc_macro) = |
42 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; | 42 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; |
43 | let db = host.raw_database(); | 43 | let db = host.raw_database(); |
44 | let mut match_finder = MatchFinder::at_first_file(db)?; | 44 | let mut match_finder = MatchFinder::at_first_file(db)?; |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 824ebf41c..0b3b76d4a 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -595,11 +595,14 @@ impl ops::Add<u8> for IndentLevel { | |||
595 | 595 | ||
596 | impl IndentLevel { | 596 | impl IndentLevel { |
597 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { | 597 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { |
598 | let first_token = match node.first_token() { | 598 | match node.first_token() { |
599 | Some(it) => it, | 599 | Some(it) => Self::from_token(&it), |
600 | None => return IndentLevel(0), | 600 | None => return IndentLevel(0), |
601 | }; | 601 | } |
602 | for ws in prev_tokens(first_token).filter_map(ast::Whitespace::cast) { | 602 | } |
603 | |||
604 | pub fn from_token(token: &SyntaxToken) -> IndentLevel { | ||
605 | for ws in prev_tokens(token.clone()).filter_map(ast::Whitespace::cast) { | ||
603 | let text = ws.syntax().text(); | 606 | let text = ws.syntax().text(); |
604 | if let Some(pos) = text.rfind('\n') { | 607 | if let Some(pos) = text.rfind('\n') { |
605 | let level = text[pos + 1..].chars().count() / 4; | 608 | let level = text[pos + 1..].chars().count() / 4; |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 044e3e5e8..977eb8181 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -85,8 +85,9 @@ pub enum CommentPlacement { | |||
85 | } | 85 | } |
86 | 86 | ||
87 | impl CommentKind { | 87 | impl CommentKind { |
88 | const BY_PREFIX: [(&'static str, CommentKind); 8] = [ | 88 | const BY_PREFIX: [(&'static str, CommentKind); 9] = [ |
89 | ("/**/", CommentKind { shape: CommentShape::Block, doc: None }), | 89 | ("/**/", CommentKind { shape: CommentShape::Block, doc: None }), |
90 | ("/***", CommentKind { shape: CommentShape::Block, doc: None }), | ||
90 | ("////", CommentKind { shape: CommentShape::Line, doc: None }), | 91 | ("////", CommentKind { shape: CommentShape::Line, doc: None }), |
91 | ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }), | 92 | ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }), |
92 | ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }), | 93 | ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }), |
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 0455dd2eb..b17dde598 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml | |||
@@ -9,11 +9,11 @@ license = "MIT OR Apache-2.0" | |||
9 | [dependencies] | 9 | [dependencies] |
10 | anyhow = "1.0.26" | 10 | anyhow = "1.0.26" |
11 | flate2 = "1.0" | 11 | flate2 = "1.0" |
12 | pico-args = "0.4.0" | ||
13 | proc-macro2 = "1.0.8" | 12 | proc-macro2 = "1.0.8" |
14 | quote = "1.0.2" | 13 | quote = "1.0.2" |
15 | ungrammar = "=1.11" | 14 | ungrammar = "=1.11" |
16 | walkdir = "2.3.1" | 15 | walkdir = "2.3.1" |
17 | write-json = "0.1.0" | 16 | write-json = "0.1.0" |
18 | xshell = "0.1" | 17 | xshell = "0.1" |
18 | xflags = "0.1.2" | ||
19 | # Avoid adding more dependencies to this crate | 19 | # Avoid adding more dependencies to this crate |
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 743e83e76..2f56c5ad0 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -18,7 +18,7 @@ use std::{ | |||
18 | }; | 18 | }; |
19 | use xshell::{cmd, pushenv, read_file, write_file}; | 19 | use xshell::{cmd, pushenv, read_file, write_file}; |
20 | 20 | ||
21 | use crate::{ensure_rustfmt, project_root, Result}; | 21 | use crate::{ensure_rustfmt, flags, project_root, Result}; |
22 | 22 | ||
23 | pub(crate) use self::{ | 23 | pub(crate) use self::{ |
24 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, | 24 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, |
@@ -35,11 +35,7 @@ pub(crate) enum Mode { | |||
35 | Verify, | 35 | Verify, |
36 | } | 36 | } |
37 | 37 | ||
38 | pub(crate) struct CodegenCmd { | 38 | impl flags::Codegen { |
39 | pub(crate) features: bool, | ||
40 | } | ||
41 | |||
42 | impl CodegenCmd { | ||
43 | pub(crate) fn run(self) -> Result<()> { | 39 | pub(crate) fn run(self) -> Result<()> { |
44 | if self.features { | 40 | if self.features { |
45 | generate_lint_completions(Mode::Overwrite)?; | 41 | generate_lint_completions(Mode::Overwrite)?; |
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs new file mode 100644 index 000000000..5710fbdb5 --- /dev/null +++ b/xtask/src/flags.rs | |||
@@ -0,0 +1,139 @@ | |||
1 | #![allow(unreachable_pub)] | ||
2 | |||
3 | xflags::args_parser! { | ||
4 | /// Run custom build command. | ||
5 | cmd xtask { | ||
6 | default cmd help { | ||
7 | /// Print help information. | ||
8 | optional -h, --help | ||
9 | } | ||
10 | |||
11 | /// Install rust-analyzer server or editor plugin. | ||
12 | cmd install { | ||
13 | /// Install only VS Code plugin. | ||
14 | optional --client | ||
15 | /// One of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss'. | ||
16 | optional --code-bin name: String | ||
17 | |||
18 | /// Install only the language server. | ||
19 | optional --server | ||
20 | /// Use mimalloc allocator for server | ||
21 | optional --mimalloc | ||
22 | /// Use jemalloc allocator for server | ||
23 | optional --jemalloc | ||
24 | } | ||
25 | |||
26 | cmd codegen { | ||
27 | optional --features | ||
28 | } | ||
29 | |||
30 | cmd lint {} | ||
31 | cmd fuzz-tests {} | ||
32 | cmd pre-cache {} | ||
33 | |||
34 | cmd release { | ||
35 | optional --dry-run | ||
36 | } | ||
37 | cmd promote { | ||
38 | optional --dry-run | ||
39 | } | ||
40 | cmd dist { | ||
41 | optional --nightly | ||
42 | optional --client version: String | ||
43 | } | ||
44 | cmd metrics { | ||
45 | optional --dry-run | ||
46 | } | ||
47 | /// Builds a benchmark version of rust-analyzer and puts it into `./target`. | ||
48 | cmd bb | ||
49 | required suffix: String | ||
50 | {} | ||
51 | } | ||
52 | } | ||
53 | |||
54 | // generated start | ||
55 | // The following code is generated by `xflags` macro. | ||
56 | // Run `env XFLAGS_DUMP= cargo build` to regenerate. | ||
57 | #[derive(Debug)] | ||
58 | pub struct Xtask { | ||
59 | pub subcommand: XtaskCmd, | ||
60 | } | ||
61 | |||
62 | #[derive(Debug)] | ||
63 | pub enum XtaskCmd { | ||
64 | Help(Help), | ||
65 | Install(Install), | ||
66 | Codegen(Codegen), | ||
67 | Lint(Lint), | ||
68 | FuzzTests(FuzzTests), | ||
69 | PreCache(PreCache), | ||
70 | Release(Release), | ||
71 | Promote(Promote), | ||
72 | Dist(Dist), | ||
73 | Metrics(Metrics), | ||
74 | Bb(Bb), | ||
75 | } | ||
76 | |||
77 | #[derive(Debug)] | ||
78 | pub struct Help { | ||
79 | pub help: bool, | ||
80 | } | ||
81 | |||
82 | #[derive(Debug)] | ||
83 | pub struct Install { | ||
84 | pub client: bool, | ||
85 | pub code_bin: Option<String>, | ||
86 | pub server: bool, | ||
87 | pub mimalloc: bool, | ||
88 | pub jemalloc: bool, | ||
89 | } | ||
90 | |||
91 | #[derive(Debug)] | ||
92 | pub struct Codegen { | ||
93 | pub features: bool, | ||
94 | } | ||
95 | |||
96 | #[derive(Debug)] | ||
97 | pub struct Lint {} | ||
98 | |||
99 | #[derive(Debug)] | ||
100 | pub struct FuzzTests {} | ||
101 | |||
102 | #[derive(Debug)] | ||
103 | pub struct PreCache {} | ||
104 | |||
105 | #[derive(Debug)] | ||
106 | pub struct Release { | ||
107 | pub dry_run: bool, | ||
108 | } | ||
109 | |||
110 | #[derive(Debug)] | ||
111 | pub struct Promote { | ||
112 | pub dry_run: bool, | ||
113 | } | ||
114 | |||
115 | #[derive(Debug)] | ||
116 | pub struct Dist { | ||
117 | pub nightly: bool, | ||
118 | pub client: Option<String>, | ||
119 | } | ||
120 | |||
121 | #[derive(Debug)] | ||
122 | pub struct Metrics { | ||
123 | pub dry_run: bool, | ||
124 | } | ||
125 | |||
126 | #[derive(Debug)] | ||
127 | pub struct Bb { | ||
128 | pub suffix: String, | ||
129 | } | ||
130 | |||
131 | impl Xtask { | ||
132 | pub const HELP: &'static str = Self::_HELP; | ||
133 | |||
134 | pub fn from_env() -> xflags::Result<Self> { | ||
135 | let mut p = xflags::rt::Parser::new_from_env(); | ||
136 | Self::_parse(&mut p) | ||
137 | } | ||
138 | } | ||
139 | // generated end | ||
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 84b17ce23..e419db7a7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -7,6 +7,8 @@ | |||
7 | //! | 7 | //! |
8 | //! This binary is integrated into the `cargo` command line by using an alias in | 8 | //! This binary is integrated into the `cargo` command line by using an alias in |
9 | //! `.cargo/config`. | 9 | //! `.cargo/config`. |
10 | mod flags; | ||
11 | |||
10 | mod codegen; | 12 | mod codegen; |
11 | mod ast_src; | 13 | mod ast_src; |
12 | #[cfg(test)] | 14 | #[cfg(test)] |
@@ -19,8 +21,6 @@ mod metrics; | |||
19 | mod pre_cache; | 21 | mod pre_cache; |
20 | 22 | ||
21 | use anyhow::{bail, Result}; | 23 | use anyhow::{bail, Result}; |
22 | use codegen::CodegenCmd; | ||
23 | use pico_args::Arguments; | ||
24 | use std::{ | 24 | use std::{ |
25 | env, | 25 | env, |
26 | path::{Path, PathBuf}, | 26 | path::{Path, PathBuf}, |
@@ -32,42 +32,19 @@ use crate::{ | |||
32 | codegen::Mode, | 32 | codegen::Mode, |
33 | dist::DistCmd, | 33 | dist::DistCmd, |
34 | install::{InstallCmd, Malloc, ServerOpt}, | 34 | install::{InstallCmd, Malloc, ServerOpt}, |
35 | metrics::MetricsCmd, | ||
36 | pre_cache::PreCacheCmd, | ||
37 | release::{PromoteCmd, ReleaseCmd}, | ||
38 | }; | 35 | }; |
39 | 36 | ||
40 | fn main() -> Result<()> { | 37 | fn main() -> Result<()> { |
41 | let _d = pushd(project_root())?; | 38 | let _d = pushd(project_root())?; |
42 | 39 | ||
43 | let mut args = Arguments::from_env(); | 40 | let flags = flags::Xtask::from_env()?; |
44 | let subcommand = args.subcommand()?.unwrap_or_default(); | 41 | match flags.subcommand { |
45 | 42 | flags::XtaskCmd::Help(_) => { | |
46 | match subcommand.as_str() { | 43 | println!("{}", flags::Xtask::HELP); |
47 | "install" => { | 44 | return Ok(()); |
48 | if args.contains(["-h", "--help"]) { | 45 | } |
49 | eprintln!( | 46 | flags::XtaskCmd::Install(flags) => { |
50 | "\ | 47 | if flags.server && flags.client { |
51 | cargo xtask install | ||
52 | Install rust-analyzer server or editor plugin. | ||
53 | |||
54 | USAGE: | ||
55 | cargo xtask install [FLAGS] | ||
56 | |||
57 | FLAGS: | ||
58 | --client[=CLIENT] Install only VS Code plugin. | ||
59 | CLIENT is one of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss' | ||
60 | --server Install only the language server | ||
61 | --mimalloc Use mimalloc allocator for server | ||
62 | --jemalloc Use jemalloc allocator for server | ||
63 | -h, --help Prints help information | ||
64 | " | ||
65 | ); | ||
66 | return Ok(()); | ||
67 | } | ||
68 | let server = args.contains("--server"); | ||
69 | let client_code = args.contains("--client"); | ||
70 | if server && client_code { | ||
71 | eprintln!( | 48 | eprintln!( |
72 | "error: The argument `--server` cannot be used with `--client`\n\n\ | 49 | "error: The argument `--server` cannot be used with `--client`\n\n\ |
73 | For more information try --help" | 50 | For more information try --help" |
@@ -75,102 +52,43 @@ FLAGS: | |||
75 | return Ok(()); | 52 | return Ok(()); |
76 | } | 53 | } |
77 | 54 | ||
78 | let malloc = if args.contains("--mimalloc") { | 55 | let malloc = if flags.mimalloc { |
79 | Malloc::Mimalloc | 56 | Malloc::Mimalloc |
80 | } else if args.contains("--jemalloc") { | 57 | } else if flags.jemalloc { |
81 | Malloc::Jemalloc | 58 | Malloc::Jemalloc |
82 | } else { | 59 | } else { |
83 | Malloc::System | 60 | Malloc::System |
84 | }; | 61 | }; |
85 | 62 | ||
86 | let client_opt = args.opt_value_from_str("--client")?; | 63 | let client_bin = flags.code_bin.map(|it| it.parse()).transpose()?; |
87 | |||
88 | finish_args(args)?; | ||
89 | 64 | ||
90 | InstallCmd { | 65 | InstallCmd { |
91 | client: if server { None } else { Some(client_opt.unwrap_or_default()) }, | 66 | client: if flags.server { None } else { client_bin }, |
92 | server: if client_code { None } else { Some(ServerOpt { malloc }) }, | 67 | server: if flags.client { None } else { Some(ServerOpt { malloc }) }, |
93 | } | 68 | } |
94 | .run() | 69 | .run() |
95 | } | 70 | } |
96 | "codegen" => { | 71 | flags::XtaskCmd::Codegen(cmd) => cmd.run(), |
97 | let features = args.contains("--features"); | 72 | flags::XtaskCmd::Lint(_) => run_clippy(), |
98 | finish_args(args)?; | 73 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), |
99 | CodegenCmd { features }.run() | 74 | flags::XtaskCmd::PreCache(cmd) => cmd.run(), |
100 | } | 75 | flags::XtaskCmd::Release(cmd) => cmd.run(), |
101 | "lint" => { | 76 | flags::XtaskCmd::Promote(cmd) => cmd.run(), |
102 | finish_args(args)?; | 77 | flags::XtaskCmd::Dist(flags) => { |
103 | run_clippy() | 78 | DistCmd { nightly: flags.nightly, client_version: flags.client }.run() |
104 | } | ||
105 | "fuzz-tests" => { | ||
106 | finish_args(args)?; | ||
107 | run_fuzzer() | ||
108 | } | ||
109 | "pre-cache" => { | ||
110 | finish_args(args)?; | ||
111 | PreCacheCmd.run() | ||
112 | } | 79 | } |
113 | "release" => { | 80 | flags::XtaskCmd::Metrics(cmd) => cmd.run(), |
114 | let dry_run = args.contains("--dry-run"); | 81 | flags::XtaskCmd::Bb(cmd) => { |
115 | finish_args(args)?; | ||
116 | ReleaseCmd { dry_run }.run() | ||
117 | } | ||
118 | "promote" => { | ||
119 | let dry_run = args.contains("--dry-run"); | ||
120 | finish_args(args)?; | ||
121 | PromoteCmd { dry_run }.run() | ||
122 | } | ||
123 | "dist" => { | ||
124 | let nightly = args.contains("--nightly"); | ||
125 | let client_version: Option<String> = args.opt_value_from_str("--client")?; | ||
126 | finish_args(args)?; | ||
127 | DistCmd { nightly, client_version }.run() | ||
128 | } | ||
129 | "metrics" => { | ||
130 | let dry_run = args.contains("--dry-run"); | ||
131 | finish_args(args)?; | ||
132 | MetricsCmd { dry_run }.run() | ||
133 | } | ||
134 | "bb" => { | ||
135 | let suffix: String = args.free_from_str()?; | ||
136 | finish_args(args)?; | ||
137 | { | 82 | { |
138 | let _d = pushd("./crates/rust-analyzer")?; | 83 | let _d = pushd("./crates/rust-analyzer")?; |
139 | cmd!("cargo build --release --features jemalloc").run()?; | 84 | cmd!("cargo build --release --features jemalloc").run()?; |
140 | } | 85 | } |
141 | cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?; | 86 | cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", cmd.suffix))?; |
142 | Ok(()) | ||
143 | } | ||
144 | _ => { | ||
145 | eprintln!( | ||
146 | "\ | ||
147 | cargo xtask | ||
148 | Run custom build command. | ||
149 | |||
150 | USAGE: | ||
151 | cargo xtask <SUBCOMMAND> | ||
152 | |||
153 | SUBCOMMANDS: | ||
154 | fuzz-tests | ||
155 | codegen | ||
156 | install | ||
157 | lint | ||
158 | dist | ||
159 | promote | ||
160 | bb" | ||
161 | ); | ||
162 | Ok(()) | 87 | Ok(()) |
163 | } | 88 | } |
164 | } | 89 | } |
165 | } | 90 | } |
166 | 91 | ||
167 | fn finish_args(args: Arguments) -> Result<()> { | ||
168 | if !args.finish().is_empty() { | ||
169 | bail!("Unused arguments."); | ||
170 | } | ||
171 | Ok(()) | ||
172 | } | ||
173 | |||
174 | fn project_root() -> PathBuf { | 92 | fn project_root() -> PathBuf { |
175 | Path::new( | 93 | Path::new( |
176 | &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()), | 94 | &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()), |
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index babc2a6d4..eb58b3274 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs | |||
@@ -9,13 +9,11 @@ use std::{ | |||
9 | use anyhow::{bail, format_err, Result}; | 9 | use anyhow::{bail, format_err, Result}; |
10 | use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf}; | 10 | use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf}; |
11 | 11 | ||
12 | type Unit = String; | 12 | use crate::flags; |
13 | 13 | ||
14 | pub(crate) struct MetricsCmd { | 14 | type Unit = String; |
15 | pub(crate) dry_run: bool, | ||
16 | } | ||
17 | 15 | ||
18 | impl MetricsCmd { | 16 | impl flags::Metrics { |
19 | pub(crate) fn run(self) -> Result<()> { | 17 | pub(crate) fn run(self) -> Result<()> { |
20 | let mut metrics = Metrics::new()?; | 18 | let mut metrics = Metrics::new()?; |
21 | if !self.dry_run { | 19 | if !self.dry_run { |
diff --git a/xtask/src/pre_cache.rs b/xtask/src/pre_cache.rs index 54f4a95a9..b456224fd 100644 --- a/xtask/src/pre_cache.rs +++ b/xtask/src/pre_cache.rs | |||
@@ -6,9 +6,9 @@ use std::{ | |||
6 | use anyhow::Result; | 6 | use anyhow::Result; |
7 | use xshell::rm_rf; | 7 | use xshell::rm_rf; |
8 | 8 | ||
9 | pub(crate) struct PreCacheCmd; | 9 | use crate::flags; |
10 | 10 | ||
11 | impl PreCacheCmd { | 11 | impl flags::PreCache { |
12 | /// Cleans the `./target` dir after the build such that only | 12 | /// Cleans the `./target` dir after the build such that only |
13 | /// dependencies are cached on CI. | 13 | /// dependencies are cached on CI. |
14 | pub(crate) fn run(self) -> Result<()> { | 14 | pub(crate) fn run(self) -> Result<()> { |
diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 5008881e4..d8d86fd63 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs | |||
@@ -2,13 +2,9 @@ use std::fmt::Write; | |||
2 | 2 | ||
3 | use xshell::{cmd, cp, pushd, read_dir, write_file}; | 3 | use xshell::{cmd, cp, pushd, read_dir, write_file}; |
4 | 4 | ||
5 | use crate::{codegen, date_iso, is_release_tag, project_root, Mode, Result}; | 5 | use crate::{codegen, date_iso, flags, is_release_tag, project_root, Mode, Result}; |
6 | 6 | ||
7 | pub(crate) struct ReleaseCmd { | 7 | impl flags::Release { |
8 | pub(crate) dry_run: bool, | ||
9 | } | ||
10 | |||
11 | impl ReleaseCmd { | ||
12 | pub(crate) fn run(self) -> Result<()> { | 8 | pub(crate) fn run(self) -> Result<()> { |
13 | if !self.dry_run { | 9 | if !self.dry_run { |
14 | cmd!("git switch release").run()?; | 10 | cmd!("git switch release").run()?; |
@@ -86,11 +82,7 @@ https://github.com/sponsors/rust-analyzer[GitHub Sponsors]. | |||
86 | } | 82 | } |
87 | } | 83 | } |
88 | 84 | ||
89 | pub(crate) struct PromoteCmd { | 85 | impl flags::Promote { |
90 | pub(crate) dry_run: bool, | ||
91 | } | ||
92 | |||
93 | impl PromoteCmd { | ||
94 | pub(crate) fn run(self) -> Result<()> { | 86 | pub(crate) fn run(self) -> Result<()> { |
95 | let _dir = pushd("../rust-rust-analyzer")?; | 87 | let _dir = pushd("../rust-rust-analyzer")?; |
96 | cmd!("git switch master").run()?; | 88 | cmd!("git switch master").run()?; |
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs index 63116ec6b..349ca14d0 100644 --- a/xtask/src/tidy.rs +++ b/xtask/src/tidy.rs | |||
@@ -101,21 +101,44 @@ fn cargo_files_are_tidy() { | |||
101 | let mut section = None; | 101 | let mut section = None; |
102 | for (line_no, text) in read_file(&cargo).unwrap().lines().enumerate() { | 102 | for (line_no, text) in read_file(&cargo).unwrap().lines().enumerate() { |
103 | let text = text.trim(); | 103 | let text = text.trim(); |
104 | if text.starts_with("[") { | 104 | if text.starts_with('[') { |
105 | if !text.ends_with(']') { | ||
106 | panic!( | ||
107 | "\nplease don't add comments or trailing whitespace in section lines.\n\ | ||
108 | {}:{}\n", | ||
109 | cargo.display(), | ||
110 | line_no + 1 | ||
111 | ) | ||
112 | } | ||
105 | section = Some(text); | 113 | section = Some(text); |
106 | continue; | 114 | continue; |
107 | } | 115 | } |
108 | if !section.map(|it| it.starts_with("[dependencies")).unwrap_or(false) { | 116 | let text: String = text.split_whitespace().collect(); |
117 | if !text.contains("path=") { | ||
109 | continue; | 118 | continue; |
110 | } | 119 | } |
111 | let text: String = text.split_whitespace().collect(); | 120 | match section { |
112 | if text.contains("path=") && !text.contains("version") { | 121 | Some(s) if s.contains("dev-dependencies") => { |
113 | panic!( | 122 | if text.contains("version") { |
114 | "\ncargo internal dependencies should have version.\n\ | 123 | panic!( |
115 | {}:{}\n", | 124 | "\ncargo internal dev-dependencies should not have a version.\n\ |
116 | cargo.display(), | 125 | {}:{}\n", |
117 | line_no + 1 | 126 | cargo.display(), |
118 | ) | 127 | line_no + 1 |
128 | ); | ||
129 | } | ||
130 | } | ||
131 | Some(s) if s.contains("dependencies") => { | ||
132 | if !text.contains("version") { | ||
133 | panic!( | ||
134 | "\ncargo internal dependencies should have a version.\n\ | ||
135 | {}:{}\n", | ||
136 | cargo.display(), | ||
137 | line_no + 1 | ||
138 | ); | ||
139 | } | ||
140 | } | ||
141 | _ => {} | ||
119 | } | 142 | } |
120 | } | 143 | } |
121 | } | 144 | } |