diff options
-rw-r--r-- | Cargo.lock | 31 | ||||
-rw-r--r-- | crates/ide/src/completion.rs | 49 | ||||
-rw-r--r-- | crates/ide/src/completion/completion_context.rs | 27 | ||||
-rw-r--r-- | crates/ide/src/completion/patterns.rs | 48 | ||||
-rw-r--r-- | crates/ide/src/completion/test_utils.rs | 12 | ||||
-rw-r--r-- | crates/vfs-notify/src/lib.rs | 17 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 3 | ||||
-rw-r--r-- | docs/dev/style.md | 61 | ||||
-rw-r--r-- | docs/user/manual.adoc | 11 | ||||
-rw-r--r-- | xtask/Cargo.toml | 2 | ||||
-rw-r--r-- | xtask/src/codegen.rs | 21 | ||||
-rw-r--r-- | xtask/src/codegen/gen_assists_docs.rs | 2 | ||||
-rw-r--r-- | xtask/src/codegen/gen_features.rs | 12 | ||||
-rw-r--r-- | xtask/src/codegen/gen_syntax.rs | 15 | ||||
-rw-r--r-- | xtask/src/dist.rs | 33 | ||||
-rw-r--r-- | xtask/src/install.rs | 46 | ||||
-rw-r--r-- | xtask/src/lib.rs | 52 | ||||
-rw-r--r-- | xtask/src/main.rs | 4 | ||||
-rw-r--r-- | xtask/src/metrics.rs | 39 | ||||
-rw-r--r-- | xtask/src/not_bash.rs | 169 | ||||
-rw-r--r-- | xtask/src/pre_cache.rs | 5 | ||||
-rw-r--r-- | xtask/src/pre_commit.rs | 8 | ||||
-rw-r--r-- | xtask/src/release.rs | 60 | ||||
-rw-r--r-- | xtask/tests/tidy.rs | 36 |
24 files changed, 391 insertions, 372 deletions
diff --git a/Cargo.lock b/Cargo.lock index d470d84f2..fa56ea3be 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -422,12 +422,6 @@ dependencies = [ | |||
422 | ] | 422 | ] |
423 | 423 | ||
424 | [[package]] | 424 | [[package]] |
425 | name = "fs-err" | ||
426 | version = "2.5.0" | ||
427 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
428 | checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431" | ||
429 | |||
430 | [[package]] | ||
431 | name = "fsevent" | 425 | name = "fsevent" |
432 | version = "2.0.2" | 426 | version = "2.0.2" |
433 | source = "registry+https://github.com/rust-lang/crates.io-index" | 427 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1447,18 +1441,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | |||
1447 | 1441 | ||
1448 | [[package]] | 1442 | [[package]] |
1449 | name = "serde" | 1443 | name = "serde" |
1450 | version = "1.0.116" | 1444 | version = "1.0.117" |
1451 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1452 | checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" | 1446 | checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" |
1453 | dependencies = [ | 1447 | dependencies = [ |
1454 | "serde_derive", | 1448 | "serde_derive", |
1455 | ] | 1449 | ] |
1456 | 1450 | ||
1457 | [[package]] | 1451 | [[package]] |
1458 | name = "serde_derive" | 1452 | name = "serde_derive" |
1459 | version = "1.0.116" | 1453 | version = "1.0.117" |
1460 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1461 | checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" | 1455 | checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" |
1462 | dependencies = [ | 1456 | dependencies = [ |
1463 | "proc-macro2", | 1457 | "proc-macro2", |
1464 | "quote", | 1458 | "quote", |
@@ -1921,16 +1915,31 @@ dependencies = [ | |||
1921 | ] | 1915 | ] |
1922 | 1916 | ||
1923 | [[package]] | 1917 | [[package]] |
1918 | name = "xshell" | ||
1919 | version = "0.1.2" | ||
1920 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1921 | checksum = "3e52c34730b631e3aea95f5f650e7457a9219f4c62df9ed2d1106ddacb0d54e2" | ||
1922 | dependencies = [ | ||
1923 | "xshell-macros", | ||
1924 | ] | ||
1925 | |||
1926 | [[package]] | ||
1927 | name = "xshell-macros" | ||
1928 | version = "0.1.2" | ||
1929 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1930 | checksum = "495f6e94cc76a22553d2a49188d7d49155c87d4b82414b749b121d69aa9c0240" | ||
1931 | |||
1932 | [[package]] | ||
1924 | name = "xtask" | 1933 | name = "xtask" |
1925 | version = "0.1.0" | 1934 | version = "0.1.0" |
1926 | dependencies = [ | 1935 | dependencies = [ |
1927 | "anyhow", | 1936 | "anyhow", |
1928 | "flate2", | 1937 | "flate2", |
1929 | "fs-err", | ||
1930 | "pico-args", | 1938 | "pico-args", |
1931 | "proc-macro2", | 1939 | "proc-macro2", |
1932 | "quote", | 1940 | "quote", |
1933 | "ungrammar", | 1941 | "ungrammar", |
1934 | "walkdir", | 1942 | "walkdir", |
1935 | "write-json", | 1943 | "write-json", |
1944 | "xshell", | ||
1936 | ] | 1945 | ] |
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs index b0e35b2bd..69e875014 100644 --- a/crates/ide/src/completion.rs +++ b/crates/ide/src/completion.rs | |||
@@ -112,6 +112,11 @@ pub(crate) fn completions( | |||
112 | ) -> Option<Completions> { | 112 | ) -> Option<Completions> { |
113 | let ctx = CompletionContext::new(db, position, config)?; | 113 | let ctx = CompletionContext::new(db, position, config)?; |
114 | 114 | ||
115 | if ctx.no_completion_required() { | ||
116 | // No work required here. | ||
117 | return None; | ||
118 | } | ||
119 | |||
115 | let mut acc = Completions::default(); | 120 | let mut acc = Completions::default(); |
116 | complete_attribute::complete_attribute(&mut acc, &ctx); | 121 | complete_attribute::complete_attribute(&mut acc, &ctx); |
117 | complete_fn_param::complete_fn_param(&mut acc, &ctx); | 122 | complete_fn_param::complete_fn_param(&mut acc, &ctx); |
@@ -157,6 +162,23 @@ mod tests { | |||
157 | panic!("completion detail not found: {}", expected.detail) | 162 | panic!("completion detail not found: {}", expected.detail) |
158 | } | 163 | } |
159 | 164 | ||
165 | fn check_no_completion(ra_fixture: &str) { | ||
166 | let (analysis, position) = fixture::position(ra_fixture); | ||
167 | let config = CompletionConfig::default(); | ||
168 | analysis.completions(&config, position).unwrap(); | ||
169 | |||
170 | let completions: Option<Vec<String>> = analysis | ||
171 | .completions(&config, position) | ||
172 | .unwrap() | ||
173 | .and_then(|completions| if completions.is_empty() { None } else { Some(completions) }) | ||
174 | .map(|completions| { | ||
175 | completions.into_iter().map(|completion| format!("{:?}", completion)).collect() | ||
176 | }); | ||
177 | |||
178 | // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic. | ||
179 | assert_eq!(completions, None, "Completions were generated, but weren't expected"); | ||
180 | } | ||
181 | |||
160 | #[test] | 182 | #[test] |
161 | fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { | 183 | fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { |
162 | check_detail_and_documentation( | 184 | check_detail_and_documentation( |
@@ -208,4 +230,31 @@ mod tests { | |||
208 | DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, | 230 | DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, |
209 | ); | 231 | ); |
210 | } | 232 | } |
233 | |||
234 | #[test] | ||
235 | fn test_no_completions_required() { | ||
236 | // There must be no hint for 'in' keyword. | ||
237 | check_no_completion( | ||
238 | r#" | ||
239 | fn foo() { | ||
240 | for i i<|> | ||
241 | } | ||
242 | "#, | ||
243 | ); | ||
244 | // After 'in' keyword hints may be spawned. | ||
245 | check_detail_and_documentation( | ||
246 | r#" | ||
247 | /// Do the foo | ||
248 | fn foo() -> &'static str { "foo" } | ||
249 | |||
250 | fn bar() { | ||
251 | for c in fo<|> | ||
252 | } | ||
253 | "#, | ||
254 | DetailAndDocumentation { | ||
255 | detail: "fn foo() -> &'static str", | ||
256 | documentation: "Do the foo", | ||
257 | }, | ||
258 | ); | ||
259 | } | ||
211 | } | 260 | } |
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 8dea8a4bf..d9f90477c 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -16,10 +16,11 @@ use crate::{ | |||
16 | call_info::ActiveParameter, | 16 | call_info::ActiveParameter, |
17 | completion::{ | 17 | completion::{ |
18 | patterns::{ | 18 | patterns::{ |
19 | has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, | 19 | fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, |
20 | has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent, | 20 | has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, |
21 | has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, | 21 | has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, |
22 | is_in_loop_body, is_match_arm, unsafe_is_prev, | 22 | has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm, |
23 | unsafe_is_prev, | ||
23 | }, | 24 | }, |
24 | CompletionConfig, | 25 | CompletionConfig, |
25 | }, | 26 | }, |
@@ -86,11 +87,14 @@ pub(crate) struct CompletionContext<'a> { | |||
86 | pub(super) in_loop_body: bool, | 87 | pub(super) in_loop_body: bool, |
87 | pub(super) has_trait_parent: bool, | 88 | pub(super) has_trait_parent: bool, |
88 | pub(super) has_impl_parent: bool, | 89 | pub(super) has_impl_parent: bool, |
90 | pub(super) inside_impl_trait_block: bool, | ||
89 | pub(super) has_field_list_parent: bool, | 91 | pub(super) has_field_list_parent: bool, |
90 | pub(super) trait_as_prev_sibling: bool, | 92 | pub(super) trait_as_prev_sibling: bool, |
91 | pub(super) impl_as_prev_sibling: bool, | 93 | pub(super) impl_as_prev_sibling: bool, |
92 | pub(super) is_match_arm: bool, | 94 | pub(super) is_match_arm: bool, |
93 | pub(super) has_item_list_or_source_file_parent: bool, | 95 | pub(super) has_item_list_or_source_file_parent: bool, |
96 | pub(super) for_is_prev2: bool, | ||
97 | pub(super) fn_is_prev: bool, | ||
94 | pub(super) locals: Vec<(String, Local)>, | 98 | pub(super) locals: Vec<(String, Local)>, |
95 | } | 99 | } |
96 | 100 | ||
@@ -168,12 +172,15 @@ impl<'a> CompletionContext<'a> { | |||
168 | block_expr_parent: false, | 172 | block_expr_parent: false, |
169 | has_trait_parent: false, | 173 | has_trait_parent: false, |
170 | has_impl_parent: false, | 174 | has_impl_parent: false, |
175 | inside_impl_trait_block: false, | ||
171 | has_field_list_parent: false, | 176 | has_field_list_parent: false, |
172 | trait_as_prev_sibling: false, | 177 | trait_as_prev_sibling: false, |
173 | impl_as_prev_sibling: false, | 178 | impl_as_prev_sibling: false, |
174 | if_is_prev: false, | 179 | if_is_prev: false, |
175 | is_match_arm: false, | 180 | is_match_arm: false, |
176 | has_item_list_or_source_file_parent: false, | 181 | has_item_list_or_source_file_parent: false, |
182 | for_is_prev2: false, | ||
183 | fn_is_prev: false, | ||
177 | locals, | 184 | locals, |
178 | }; | 185 | }; |
179 | 186 | ||
@@ -221,6 +228,15 @@ impl<'a> CompletionContext<'a> { | |||
221 | Some(ctx) | 228 | Some(ctx) |
222 | } | 229 | } |
223 | 230 | ||
231 | /// Checks whether completions in that particular case don't make much sense. | ||
232 | /// Examples: | ||
233 | /// - `fn <|>` -- we expect function name, it's unlikely that "hint" will be helpful. | ||
234 | /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. | ||
235 | /// - `for _ i<|>` -- obviously, it'll be "in" keyword. | ||
236 | pub(crate) fn no_completion_required(&self) -> bool { | ||
237 | (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 | ||
238 | } | ||
239 | |||
224 | /// The range of the identifier that is being completed. | 240 | /// The range of the identifier that is being completed. |
225 | pub(crate) fn source_range(&self) -> TextRange { | 241 | pub(crate) fn source_range(&self) -> TextRange { |
226 | // check kind of macro-expanded token, but use range of original token | 242 | // check kind of macro-expanded token, but use range of original token |
@@ -244,6 +260,7 @@ impl<'a> CompletionContext<'a> { | |||
244 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 260 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
245 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); | 261 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); |
246 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); | 262 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); |
263 | self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
247 | self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); | 264 | self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); |
248 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); | 265 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); |
249 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); | 266 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); |
@@ -253,6 +270,8 @@ impl<'a> CompletionContext<'a> { | |||
253 | self.mod_declaration_under_caret = | 270 | self.mod_declaration_under_caret = |
254 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 271 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
255 | .filter(|module| module.item_list().is_none()); | 272 | .filter(|module| module.item_list().is_none()); |
273 | self.for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
274 | self.fn_is_prev = fn_is_prev(syntax_element.clone()); | ||
256 | } | 275 | } |
257 | 276 | ||
258 | fn fill( | 277 | fn fill( |
diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index b17ddf133..cf6d5947d 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs | |||
@@ -9,7 +9,7 @@ use syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | #[cfg(test)] | 11 | #[cfg(test)] |
12 | use crate::completion::test_utils::check_pattern_is_applicable; | 12 | use crate::completion::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; |
13 | 13 | ||
14 | pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { | 14 | pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { |
15 | not_same_range_ancestor(element) | 15 | not_same_range_ancestor(element) |
@@ -34,6 +34,25 @@ pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { | |||
34 | fn test_has_impl_parent() { | 34 | fn test_has_impl_parent() { |
35 | check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); | 35 | check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); |
36 | } | 36 | } |
37 | |||
38 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { | ||
39 | // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`, | ||
40 | // where we only check the first parent with different text range. | ||
41 | element | ||
42 | .ancestors() | ||
43 | .find(|it| it.kind() == IMPL) | ||
44 | .map(|it| ast::Impl::cast(it).unwrap()) | ||
45 | .map(|it| it.trait_().is_some()) | ||
46 | .unwrap_or(false) | ||
47 | } | ||
48 | #[test] | ||
49 | fn test_inside_impl_trait_block() { | ||
50 | check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", inside_impl_trait_block); | ||
51 | check_pattern_is_applicable(r"impl Foo for Bar { fn f<|> }", inside_impl_trait_block); | ||
52 | check_pattern_is_not_applicable(r"impl A { f<|> }", inside_impl_trait_block); | ||
53 | check_pattern_is_not_applicable(r"impl A { fn f<|> }", inside_impl_trait_block); | ||
54 | } | ||
55 | |||
37 | pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { | 56 | pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { |
38 | not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() | 57 | not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() |
39 | } | 58 | } |
@@ -116,6 +135,33 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { | |||
116 | .is_some() | 135 | .is_some() |
117 | } | 136 | } |
118 | 137 | ||
138 | pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool { | ||
139 | element | ||
140 | .into_token() | ||
141 | .and_then(|it| previous_non_trivia_token(it)) | ||
142 | .filter(|it| it.kind() == FN_KW) | ||
143 | .is_some() | ||
144 | } | ||
145 | #[test] | ||
146 | fn test_fn_is_prev() { | ||
147 | check_pattern_is_applicable(r"fn l<|>", fn_is_prev); | ||
148 | } | ||
149 | |||
150 | /// Check if the token previous to the previous one is `for`. | ||
151 | /// For example, `for _ i<|>` => true. | ||
152 | pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { | ||
153 | element | ||
154 | .into_token() | ||
155 | .and_then(|it| previous_non_trivia_token(it)) | ||
156 | .and_then(|it| previous_non_trivia_token(it)) | ||
157 | .filter(|it| it.kind() == FOR_KW) | ||
158 | .is_some() | ||
159 | } | ||
160 | #[test] | ||
161 | fn test_for_is_prev2() { | ||
162 | check_pattern_is_applicable(r"for i i<|>", for_is_prev2); | ||
163 | } | ||
164 | |||
119 | #[test] | 165 | #[test] |
120 | fn test_if_is_prev() { | 166 | fn test_if_is_prev() { |
121 | check_pattern_is_applicable(r"if l<|>", if_is_prev); | 167 | check_pattern_is_applicable(r"if l<|>", if_is_prev); |
diff --git a/crates/ide/src/completion/test_utils.rs b/crates/ide/src/completion/test_utils.rs index feb8cd2a6..dabbef888 100644 --- a/crates/ide/src/completion/test_utils.rs +++ b/crates/ide/src/completion/test_utils.rs | |||
@@ -104,6 +104,18 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) - | |||
104 | .unwrap(); | 104 | .unwrap(); |
105 | } | 105 | } |
106 | 106 | ||
107 | pub(crate) fn check_pattern_is_not_applicable(code: &str, check: fn(SyntaxElement) -> bool) { | ||
108 | let (analysis, pos) = fixture::position(code); | ||
109 | analysis | ||
110 | .with_db(|db| { | ||
111 | let sema = Semantics::new(db); | ||
112 | let original_file = sema.parse(pos.file_id); | ||
113 | let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap(); | ||
114 | assert!(!check(NodeOrToken::Token(token))); | ||
115 | }) | ||
116 | .unwrap(); | ||
117 | } | ||
118 | |||
107 | pub(crate) fn get_all_completion_items( | 119 | pub(crate) fn get_all_completion_items( |
108 | config: CompletionConfig, | 120 | config: CompletionConfig, |
109 | code: &str, | 121 | code: &str, |
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index e1e36612a..c605bcf3c 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs | |||
@@ -165,14 +165,15 @@ impl NotifyActor { | |||
165 | let mut res = Vec::new(); | 165 | let mut res = Vec::new(); |
166 | 166 | ||
167 | for root in dirs.include.iter() { | 167 | for root in dirs.include.iter() { |
168 | let walkdir = WalkDir::new(root).into_iter().filter_entry(|entry| { | 168 | let walkdir = |
169 | if !entry.file_type().is_dir() { | 169 | WalkDir::new(root).follow_links(true).into_iter().filter_entry(|entry| { |
170 | return true; | 170 | if !entry.file_type().is_dir() { |
171 | } | 171 | return true; |
172 | let path = AbsPath::assert(entry.path()); | 172 | } |
173 | root == path | 173 | let path = AbsPath::assert(entry.path()); |
174 | || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path) | 174 | root == path |
175 | }); | 175 | || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path) |
176 | }); | ||
176 | 177 | ||
177 | let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| { | 178 | let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| { |
178 | let is_dir = entry.file_type().is_dir(); | 179 | let is_dir = entry.file_type().is_dir(); |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 43a69d6ce..780f5cb91 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -392,7 +392,10 @@ rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look | |||
392 | { | 392 | { |
393 | workspaceRoot?: string; | 393 | workspaceRoot?: string; |
394 | cargoArgs: string[]; | 394 | cargoArgs: string[]; |
395 | cargoExtraArgs: string[]; | ||
395 | executableArgs: string[]; | 396 | executableArgs: string[]; |
397 | expectTest?: boolean; | ||
398 | overrideCargo?: string; | ||
396 | } | 399 | } |
397 | ``` | 400 | ``` |
398 | 401 | ||
diff --git a/docs/dev/style.md b/docs/dev/style.md index 883a6845d..7a64a0d22 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -366,27 +366,66 @@ People read things from top to bottom, so place most important things first. | |||
366 | 366 | ||
367 | Specifically, if all items except one are private, always put the non-private item on top. | 367 | Specifically, if all items except one are private, always put the non-private item on top. |
368 | 368 | ||
369 | Put `struct`s and `enum`s first, functions and impls last. | ||
370 | |||
371 | Do | ||
372 | |||
373 | ```rust | 369 | ```rust |
374 | // Good | 370 | // Good |
375 | struct Foo { | 371 | pub(crate) fn frobnicate() { |
376 | bars: Vec<Bar> | 372 | Helper::act() |
373 | } | ||
374 | |||
375 | #[derive(Default)] | ||
376 | struct Helper { stuff: i32 } | ||
377 | |||
378 | impl Helper { | ||
379 | fn act(&self) { | ||
380 | |||
381 | } | ||
382 | } | ||
383 | |||
384 | // Not as good | ||
385 | #[derive(Default)] | ||
386 | struct Helper { stuff: i32 } | ||
387 | |||
388 | pub(crate) fn frobnicate() { | ||
389 | Helper::act() | ||
377 | } | 390 | } |
378 | 391 | ||
379 | struct Bar; | 392 | impl Helper { |
393 | fn act(&self) { | ||
394 | |||
395 | } | ||
396 | } | ||
380 | ``` | 397 | ``` |
381 | 398 | ||
382 | rather than | 399 | If there's a mixture of private and public items, put public items first. |
400 | If function bodies are folded in the editor, the source code should read as documentation for the public API. | ||
401 | |||
402 | Put `struct`s and `enum`s first, functions and impls last. Order types declarations in top-down manner. | ||
383 | 403 | ||
384 | ```rust | 404 | ```rust |
405 | // Good | ||
406 | struct Parent { | ||
407 | children: Vec<Child> | ||
408 | } | ||
409 | |||
410 | struct Child; | ||
411 | |||
412 | impl Parent { | ||
413 | } | ||
414 | |||
415 | impl Child { | ||
416 | } | ||
417 | |||
385 | // Not as good | 418 | // Not as good |
386 | struct Bar; | 419 | struct Child; |
387 | 420 | ||
388 | struct Foo { | 421 | impl Child { |
389 | bars: Vec<Bar> | 422 | } |
423 | |||
424 | struct Parent { | ||
425 | children: Vec<Child> | ||
426 | } | ||
427 | |||
428 | impl Parent { | ||
390 | } | 429 | } |
391 | ``` | 430 | ``` |
392 | 431 | ||
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 46e7bd091..8a3cc00df 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -260,16 +260,7 @@ If you get an error saying `No such file or directory: 'rust-analyzer'`, see the | |||
260 | 260 | ||
261 | === GNOME Builder | 261 | === GNOME Builder |
262 | 262 | ||
263 | Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. | 263 | GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If the LSP binary is not available, GNOME Builder can install it when opening a Rust file. |
264 | |||
265 | Gnome Builder currently has support for RLS, and there's no way to configure the language server executable. A future version might support `rust-analyzer` out of the box. | ||
266 | |||
267 | 1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). | ||
268 | 2. Enable the Rust Builder plugin. | ||
269 | |||
270 | ==== GNOME Builder (Nightly) | ||
271 | |||
272 | https://nightly.gnome.org/repo/appstream/org.gnome.Builder.flatpakref[GNOME Builder (Nightly)] has now native support for `rust-analyzer` out of the box. If the `rust-analyzer` binary is not available, GNOME Builder can install it when opening a Rust source file. | ||
273 | 264 | ||
274 | == Non-Cargo Based Projects | 265 | == Non-Cargo Based Projects |
275 | 266 | ||
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 01a838825..2ef956485 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml | |||
@@ -18,5 +18,5 @@ quote = "1.0.2" | |||
18 | ungrammar = "1.1.3" | 18 | ungrammar = "1.1.3" |
19 | walkdir = "2.3.1" | 19 | walkdir = "2.3.1" |
20 | write-json = "0.1.0" | 20 | write-json = "0.1.0" |
21 | fs-err = "2.3" | 21 | xshell = "0.1" |
22 | # Avoid adding more dependencies to this crate | 22 | # Avoid adding more dependencies to this crate |
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 45b17bb48..3ee4c1adf 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -15,12 +15,9 @@ use std::{ | |||
15 | fmt, mem, | 15 | fmt, mem, |
16 | path::{Path, PathBuf}, | 16 | path::{Path, PathBuf}, |
17 | }; | 17 | }; |
18 | use xshell::{cmd, pushenv, read_file, write_file}; | ||
18 | 19 | ||
19 | use crate::{ | 20 | use crate::{ensure_rustfmt, project_root, Result}; |
20 | ensure_rustfmt, | ||
21 | not_bash::{fs2, pushenv, run}, | ||
22 | project_root, Result, | ||
23 | }; | ||
24 | 21 | ||
25 | pub use self::{ | 22 | pub use self::{ |
26 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, | 23 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, |
@@ -57,7 +54,7 @@ impl CodegenCmd { | |||
57 | /// A helper to update file on disk if it has changed. | 54 | /// A helper to update file on disk if it has changed. |
58 | /// With verify = false, | 55 | /// With verify = false, |
59 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | 56 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { |
60 | match fs2::read_to_string(path) { | 57 | match read_file(path) { |
61 | Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { | 58 | Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { |
62 | return Ok(()); | 59 | return Ok(()); |
63 | } | 60 | } |
@@ -67,7 +64,7 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | |||
67 | anyhow::bail!("`{}` is not up-to-date", path.display()); | 64 | anyhow::bail!("`{}` is not up-to-date", path.display()); |
68 | } | 65 | } |
69 | eprintln!("updating {}", path.display()); | 66 | eprintln!("updating {}", path.display()); |
70 | fs2::write(path, contents)?; | 67 | write_file(path, contents)?; |
71 | return Ok(()); | 68 | return Ok(()); |
72 | 69 | ||
73 | fn normalize(s: &str) -> String { | 70 | fn normalize(s: &str) -> String { |
@@ -77,13 +74,13 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | |||
77 | 74 | ||
78 | const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; | 75 | const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; |
79 | 76 | ||
80 | fn reformat(text: impl std::fmt::Display) -> Result<String> { | 77 | fn reformat(text: &str) -> Result<String> { |
81 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); | 78 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); |
82 | ensure_rustfmt()?; | 79 | ensure_rustfmt()?; |
83 | let stdout = run!( | 80 | let rustfmt_toml = project_root().join("rustfmt.toml"); |
84 | "rustfmt --config-path {} --config fn_single_line=true", project_root().join("rustfmt.toml").display(); | 81 | let stdout = cmd!("rustfmt --config-path {rustfmt_toml} --config fn_single_line=true") |
85 | <text.to_string().as_bytes() | 82 | .stdin(text) |
86 | )?; | 83 | .read()?; |
87 | Ok(format!("//! {}\n\n{}\n", PREAMBLE, stdout)) | 84 | Ok(format!("//! {}\n\n{}\n", PREAMBLE, stdout)) |
88 | } | 85 | } |
89 | 86 | ||
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index f0ded8b87..ff307e2aa 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs | |||
@@ -134,7 +134,7 @@ r#####" | |||
134 | 134 | ||
135 | buf.push_str(&test) | 135 | buf.push_str(&test) |
136 | } | 136 | } |
137 | let buf = reformat(buf)?; | 137 | let buf = reformat(&buf.to_string())?; |
138 | codegen::update(&project_root().join("crates/assists/src/tests/generated.rs"), &buf, mode) | 138 | codegen::update(&project_root().join("crates/assists/src/tests/generated.rs"), &buf, mode) |
139 | } | 139 | } |
140 | 140 | ||
diff --git a/xtask/src/codegen/gen_features.rs b/xtask/src/codegen/gen_features.rs index 78268308b..3cf15ce02 100644 --- a/xtask/src/codegen/gen_features.rs +++ b/xtask/src/codegen/gen_features.rs | |||
@@ -3,15 +3,13 @@ use std::path::{Path, PathBuf}; | |||
3 | 3 | ||
4 | use quote::quote; | 4 | use quote::quote; |
5 | use walkdir::WalkDir; | 5 | use walkdir::WalkDir; |
6 | use xshell::{cmd, read_file}; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::codegen::{project_root, reformat, update, Mode, Result}; |
8 | codegen::{project_root, reformat, update, Mode, Result}, | ||
9 | not_bash::{fs2, run}, | ||
10 | }; | ||
11 | 9 | ||
12 | pub fn generate_features(mode: Mode) -> Result<()> { | 10 | pub fn generate_features(mode: Mode) -> Result<()> { |
13 | if !Path::new("./target/rust").exists() { | 11 | if !Path::new("./target/rust").exists() { |
14 | run!("git clone https://github.com/rust-lang/rust ./target/rust")?; | 12 | cmd!("git clone https://github.com/rust-lang/rust ./target/rust").run()?; |
15 | } | 13 | } |
16 | 14 | ||
17 | let contents = generate_descriptor("./target/rust/src/doc/unstable-book/src".into())?; | 15 | let contents = generate_descriptor("./target/rust/src/doc/unstable-book/src".into())?; |
@@ -34,7 +32,7 @@ fn generate_descriptor(src_dir: PathBuf) -> Result<String> { | |||
34 | .map(|entry| { | 32 | .map(|entry| { |
35 | let path = entry.path(); | 33 | let path = entry.path(); |
36 | let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); | 34 | let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); |
37 | let doc = fs2::read_to_string(path).unwrap(); | 35 | let doc = read_file(path).unwrap(); |
38 | 36 | ||
39 | quote! { LintCompletion { label: #feature_ident, description: #doc } } | 37 | quote! { LintCompletion { label: #feature_ident, description: #doc } } |
40 | }); | 38 | }); |
@@ -46,5 +44,5 @@ fn generate_descriptor(src_dir: PathBuf) -> Result<String> { | |||
46 | #(#definitions),* | 44 | #(#definitions),* |
47 | ]; | 45 | ]; |
48 | }; | 46 | }; |
49 | reformat(ts) | 47 | reformat(&ts.to_string()) |
50 | } | 48 | } |
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 733493fef..02f4095ce 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -61,10 +61,13 @@ fn generate_tokens(grammar: &AstSrc) -> Result<String> { | |||
61 | } | 61 | } |
62 | }); | 62 | }); |
63 | 63 | ||
64 | let pretty = reformat(quote! { | 64 | let pretty = reformat( |
65 | use crate::{SyntaxKind::{self, *}, SyntaxToken, ast::AstToken}; | 65 | "e! { |
66 | #(#tokens)* | 66 | use crate::{SyntaxKind::{self, *}, SyntaxToken, ast::AstToken}; |
67 | })? | 67 | #(#tokens)* |
68 | } | ||
69 | .to_string(), | ||
70 | )? | ||
68 | .replace("#[derive", "\n#[derive"); | 71 | .replace("#[derive", "\n#[derive"); |
69 | Ok(pretty) | 72 | Ok(pretty) |
70 | } | 73 | } |
@@ -261,7 +264,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> { | |||
261 | } | 264 | } |
262 | } | 265 | } |
263 | 266 | ||
264 | let pretty = reformat(res)?; | 267 | let pretty = reformat(&res)?; |
265 | Ok(pretty) | 268 | Ok(pretty) |
266 | } | 269 | } |
267 | 270 | ||
@@ -383,7 +386,7 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> { | |||
383 | } | 386 | } |
384 | }; | 387 | }; |
385 | 388 | ||
386 | reformat(ast) | 389 | reformat(&ast.to_string()) |
387 | } | 390 | } |
388 | 391 | ||
389 | fn to_upper_snake_case(s: &str) -> String { | 392 | fn to_upper_snake_case(s: &str) -> String { |
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index aa7d94967..9e15a5a4c 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use flate2::{write::GzEncoder, Compression}; | ||
2 | use std::{ | 1 | use std::{ |
3 | env, | 2 | env, |
4 | fs::File, | 3 | fs::File, |
@@ -7,11 +6,10 @@ use std::{ | |||
7 | }; | 6 | }; |
8 | 7 | ||
9 | use anyhow::Result; | 8 | use anyhow::Result; |
9 | use flate2::{write::GzEncoder, Compression}; | ||
10 | use xshell::{cmd, cp, mkdir_p, pushd, read_file, rm_rf, write_file}; | ||
10 | 11 | ||
11 | use crate::{ | 12 | use crate::{date_iso, project_root}; |
12 | not_bash::{date_iso, fs2, pushd, rm_rf, run}, | ||
13 | project_root, | ||
14 | }; | ||
15 | 13 | ||
16 | pub struct DistCmd { | 14 | pub struct DistCmd { |
17 | pub nightly: bool, | 15 | pub nightly: bool, |
@@ -22,7 +20,7 @@ impl DistCmd { | |||
22 | pub fn run(self) -> Result<()> { | 20 | pub fn run(self) -> Result<()> { |
23 | let dist = project_root().join("dist"); | 21 | let dist = project_root().join("dist"); |
24 | rm_rf(&dist)?; | 22 | rm_rf(&dist)?; |
25 | fs2::create_dir_all(&dist)?; | 23 | mkdir_p(&dist)?; |
26 | 24 | ||
27 | if let Some(version) = self.client_version { | 25 | if let Some(version) = self.client_version { |
28 | let release_tag = if self.nightly { "nightly".to_string() } else { date_iso()? }; | 26 | let release_tag = if self.nightly { "nightly".to_string() } else { date_iso()? }; |
@@ -34,7 +32,7 @@ impl DistCmd { | |||
34 | } | 32 | } |
35 | 33 | ||
36 | fn dist_client(version: &str, release_tag: &str) -> Result<()> { | 34 | fn dist_client(version: &str, release_tag: &str) -> Result<()> { |
37 | let _d = pushd("./editors/code"); | 35 | let _d = pushd("./editors/code")?; |
38 | let nightly = release_tag == "nightly"; | 36 | let nightly = release_tag == "nightly"; |
39 | 37 | ||
40 | let mut patch = Patch::new("./package.json")?; | 38 | let mut patch = Patch::new("./package.json")?; |
@@ -54,20 +52,16 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> { | |||
54 | } | 52 | } |
55 | patch.commit()?; | 53 | patch.commit()?; |
56 | 54 | ||
57 | run!("npm ci")?; | 55 | cmd!("npm ci").run()?; |
58 | run!("npx vsce package -o ../../dist/rust-analyzer.vsix")?; | 56 | cmd!("npx vsce package -o ../../dist/rust-analyzer.vsix").run()?; |
59 | Ok(()) | 57 | Ok(()) |
60 | } | 58 | } |
61 | 59 | ||
62 | fn dist_server() -> Result<()> { | 60 | fn dist_server() -> Result<()> { |
63 | if cfg!(target_os = "linux") { | 61 | if cfg!(target_os = "linux") { |
64 | env::set_var("CC", "clang"); | 62 | env::set_var("CC", "clang"); |
65 | run!( | ||
66 | "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release" | ||
67 | )?; | ||
68 | } else { | ||
69 | run!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release")?; | ||
70 | } | 63 | } |
64 | cmd!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release").run()?; | ||
71 | 65 | ||
72 | let (src, dst) = if cfg!(target_os = "linux") { | 66 | let (src, dst) = if cfg!(target_os = "linux") { |
73 | ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux") | 67 | ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux") |
@@ -82,7 +76,7 @@ fn dist_server() -> Result<()> { | |||
82 | let src = Path::new(src); | 76 | let src = Path::new(src); |
83 | let dst = Path::new(dst); | 77 | let dst = Path::new(dst); |
84 | 78 | ||
85 | fs2::copy(&src, &dst)?; | 79 | cp(&src, &dst)?; |
86 | gzip(&src, &dst.with_extension("gz"))?; | 80 | gzip(&src, &dst.with_extension("gz"))?; |
87 | 81 | ||
88 | Ok(()) | 82 | Ok(()) |
@@ -105,7 +99,7 @@ struct Patch { | |||
105 | impl Patch { | 99 | impl Patch { |
106 | fn new(path: impl Into<PathBuf>) -> Result<Patch> { | 100 | fn new(path: impl Into<PathBuf>) -> Result<Patch> { |
107 | let path = path.into(); | 101 | let path = path.into(); |
108 | let contents = fs2::read_to_string(&path)?; | 102 | let contents = read_file(&path)?; |
109 | Ok(Patch { path, original_contents: contents.clone(), contents }) | 103 | Ok(Patch { path, original_contents: contents.clone(), contents }) |
110 | } | 104 | } |
111 | 105 | ||
@@ -115,13 +109,14 @@ impl Patch { | |||
115 | self | 109 | self |
116 | } | 110 | } |
117 | 111 | ||
118 | fn commit(&self) -> io::Result<()> { | 112 | fn commit(&self) -> Result<()> { |
119 | fs2::write(&self.path, &self.contents) | 113 | write_file(&self.path, &self.contents)?; |
114 | Ok(()) | ||
120 | } | 115 | } |
121 | } | 116 | } |
122 | 117 | ||
123 | impl Drop for Patch { | 118 | impl Drop for Patch { |
124 | fn drop(&mut self) { | 119 | fn drop(&mut self) { |
125 | fs2::write(&self.path, &self.original_contents).unwrap(); | 120 | write_file(&self.path, &self.original_contents).unwrap(); |
126 | } | 121 | } |
127 | } | 122 | } |
diff --git a/xtask/src/install.rs b/xtask/src/install.rs index fcc4f05e4..789e9f27b 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs | |||
@@ -3,8 +3,7 @@ | |||
3 | use std::{env, path::PathBuf, str}; | 3 | use std::{env, path::PathBuf, str}; |
4 | 4 | ||
5 | use anyhow::{bail, format_err, Context, Result}; | 5 | use anyhow::{bail, format_err, Context, Result}; |
6 | 6 | use xshell::{cmd, pushd}; | |
7 | use crate::not_bash::{pushd, run}; | ||
8 | 7 | ||
9 | // Latest stable, feel free to send a PR if this lags behind. | 8 | // Latest stable, feel free to send a PR if this lags behind. |
10 | const REQUIRED_RUST_VERSION: u32 = 47; | 9 | const REQUIRED_RUST_VERSION: u32 = 47; |
@@ -76,7 +75,7 @@ fn fix_path_for_mac() -> Result<()> { | |||
76 | } | 75 | } |
77 | 76 | ||
78 | fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { | 77 | fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { |
79 | let _dir = pushd("./editors/code"); | 78 | let _dir = pushd("./editors/code")?; |
80 | 79 | ||
81 | let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { | 80 | let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { |
82 | ["code", "code-insiders", "codium", "code-oss"] | 81 | ["code", "code-insiders", "codium", "code-oss"] |
@@ -89,24 +88,25 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { | |||
89 | }; | 88 | }; |
90 | 89 | ||
91 | let installed_extensions = if cfg!(unix) { | 90 | let installed_extensions = if cfg!(unix) { |
92 | run!("npm --version").context("`npm` is required to build the VS Code plugin")?; | 91 | cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?; |
93 | run!("npm install")?; | 92 | cmd!("npm install").run()?; |
94 | 93 | ||
95 | run!("npm run package --scripts-prepend-node-path")?; | 94 | cmd!("npm run package --scripts-prepend-node-path").run()?; |
96 | 95 | ||
97 | let code = find_code(|bin| run!("{} --version", bin).is_ok())?; | 96 | let code = find_code(|bin| cmd!("{bin} --version").read().is_ok())?; |
98 | run!("{} --install-extension rust-analyzer.vsix --force", code)?; | 97 | cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?; |
99 | run!("{} --list-extensions", code; echo = false)? | 98 | cmd!("{code} --list-extensions").read()? |
100 | } else { | 99 | } else { |
101 | run!("cmd.exe /c npm --version") | 100 | cmd!("cmd.exe /c npm --version") |
101 | .run() | ||
102 | .context("`npm` is required to build the VS Code plugin")?; | 102 | .context("`npm` is required to build the VS Code plugin")?; |
103 | run!("cmd.exe /c npm install")?; | 103 | cmd!("cmd.exe /c npm install").run()?; |
104 | 104 | ||
105 | run!("cmd.exe /c npm run package")?; | 105 | cmd!("cmd.exe /c npm run package").run()?; |
106 | 106 | ||
107 | let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?; | 107 | let code = find_code(|bin| cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok())?; |
108 | run!(r"cmd.exe /c {}.cmd --install-extension rust-analyzer.vsix --force", code)?; | 108 | cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?; |
109 | run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)? | 109 | cmd!("cmd.exe /c {code}.cmd --list-extensions").read()? |
110 | }; | 110 | }; |
111 | 111 | ||
112 | if !installed_extensions.contains("rust-analyzer") { | 112 | if !installed_extensions.contains("rust-analyzer") { |
@@ -122,7 +122,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { | |||
122 | 122 | ||
123 | fn install_server(opts: ServerOpt) -> Result<()> { | 123 | fn install_server(opts: ServerOpt) -> Result<()> { |
124 | let mut old_rust = false; | 124 | let mut old_rust = false; |
125 | if let Ok(stdout) = run!("cargo --version") { | 125 | if let Ok(stdout) = cmd!("cargo --version").read() { |
126 | if !check_version(&stdout, REQUIRED_RUST_VERSION) { | 126 | if !check_version(&stdout, REQUIRED_RUST_VERSION) { |
127 | old_rust = true; | 127 | old_rust = true; |
128 | } | 128 | } |
@@ -134,12 +134,13 @@ fn install_server(opts: ServerOpt) -> Result<()> { | |||
134 | REQUIRED_RUST_VERSION, | 134 | REQUIRED_RUST_VERSION, |
135 | ) | 135 | ) |
136 | } | 136 | } |
137 | 137 | let features = match opts.malloc { | |
138 | let malloc_feature = match opts.malloc { | 138 | Malloc::System => &[][..], |
139 | Malloc::System => "", | 139 | Malloc::Mimalloc => &["--features", "mimalloc"], |
140 | Malloc::Mimalloc => "--features mimalloc", | ||
141 | }; | 140 | }; |
142 | let res = run!("cargo install --path crates/rust-analyzer --locked --force {}", malloc_feature); | 141 | |
142 | let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}"); | ||
143 | let res = cmd.run(); | ||
143 | 144 | ||
144 | if res.is_err() && old_rust { | 145 | if res.is_err() && old_rust { |
145 | eprintln!( | 146 | eprintln!( |
@@ -148,7 +149,8 @@ fn install_server(opts: ServerOpt) -> Result<()> { | |||
148 | ); | 149 | ); |
149 | } | 150 | } |
150 | 151 | ||
151 | res.map(drop) | 152 | res?; |
153 | Ok(()) | ||
152 | } | 154 | } |
153 | 155 | ||
154 | fn check_version(version_output: &str, min_minor_version: u32) -> bool { | 156 | fn check_version(version_output: &str, min_minor_version: u32) -> bool { |
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index e790d995f..babec2dbd 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | //! | 2 | //! |
3 | //! See https://github.com/matklad/cargo-xtask/ | 3 | //! See https://github.com/matklad/cargo-xtask/ |
4 | 4 | ||
5 | pub mod not_bash; | ||
6 | pub mod codegen; | 5 | pub mod codegen; |
7 | mod ast_src; | 6 | mod ast_src; |
8 | 7 | ||
@@ -19,11 +18,9 @@ use std::{ | |||
19 | }; | 18 | }; |
20 | 19 | ||
21 | use walkdir::{DirEntry, WalkDir}; | 20 | use walkdir::{DirEntry, WalkDir}; |
21 | use xshell::{cmd, pushd, pushenv}; | ||
22 | 22 | ||
23 | use crate::{ | 23 | use crate::codegen::Mode; |
24 | codegen::Mode, | ||
25 | not_bash::{pushd, pushenv}, | ||
26 | }; | ||
27 | 24 | ||
28 | pub use anyhow::{bail, Context as _, Result}; | 25 | pub use anyhow::{bail, Context as _, Result}; |
29 | 26 | ||
@@ -53,18 +50,19 @@ pub fn rust_files(path: &Path) -> impl Iterator<Item = PathBuf> { | |||
53 | } | 50 | } |
54 | 51 | ||
55 | pub fn run_rustfmt(mode: Mode) -> Result<()> { | 52 | pub fn run_rustfmt(mode: Mode) -> Result<()> { |
56 | let _dir = pushd(project_root()); | 53 | let _dir = pushd(project_root())?; |
57 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); | 54 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); |
58 | ensure_rustfmt()?; | 55 | ensure_rustfmt()?; |
59 | match mode { | 56 | let check = match mode { |
60 | Mode::Overwrite => run!("cargo fmt"), | 57 | Mode::Overwrite => &[][..], |
61 | Mode::Verify => run!("cargo fmt -- --check"), | 58 | Mode::Verify => &["--", "--check"], |
62 | }?; | 59 | }; |
60 | cmd!("cargo fmt {check...}").run()?; | ||
63 | Ok(()) | 61 | Ok(()) |
64 | } | 62 | } |
65 | 63 | ||
66 | fn ensure_rustfmt() -> Result<()> { | 64 | fn ensure_rustfmt() -> Result<()> { |
67 | let out = run!("rustfmt --version")?; | 65 | let out = cmd!("rustfmt --version").read()?; |
68 | if !out.contains("stable") { | 66 | if !out.contains("stable") { |
69 | bail!( | 67 | bail!( |
70 | "Failed to run rustfmt from toolchain 'stable'. \ | 68 | "Failed to run rustfmt from toolchain 'stable'. \ |
@@ -75,40 +73,46 @@ fn ensure_rustfmt() -> Result<()> { | |||
75 | } | 73 | } |
76 | 74 | ||
77 | pub fn run_clippy() -> Result<()> { | 75 | pub fn run_clippy() -> Result<()> { |
78 | if run!("cargo clippy --version").is_err() { | 76 | if cmd!("cargo clippy --version").read().is_err() { |
79 | bail!( | 77 | bail!( |
80 | "Failed run cargo clippy. \ | 78 | "Failed run cargo clippy. \ |
81 | Please run `rustup component add clippy` to install it.", | 79 | Please run `rustup component add clippy` to install it.", |
82 | ) | 80 | ) |
83 | } | 81 | } |
84 | 82 | ||
85 | let allowed_lints = [ | 83 | let allowed_lints = " |
86 | "clippy::collapsible_if", | 84 | -A clippy::collapsible_if |
87 | "clippy::needless_pass_by_value", | 85 | -A clippy::needless_pass_by_value |
88 | "clippy::nonminimal_bool", | 86 | -A clippy::nonminimal_bool |
89 | "clippy::redundant_pattern_matching", | 87 | -A clippy::redundant_pattern_matching |
90 | ]; | 88 | " |
91 | run!("cargo clippy --all-features --all-targets -- -A {}", allowed_lints.join(" -A "))?; | 89 | .split_ascii_whitespace(); |
90 | cmd!("cargo clippy --all-features --all-targets -- {allowed_lints...}").run()?; | ||
92 | Ok(()) | 91 | Ok(()) |
93 | } | 92 | } |
94 | 93 | ||
95 | pub fn run_fuzzer() -> Result<()> { | 94 | pub fn run_fuzzer() -> Result<()> { |
96 | let _d = pushd("./crates/syntax"); | 95 | let _d = pushd("./crates/syntax")?; |
97 | let _e = pushenv("RUSTUP_TOOLCHAIN", "nightly"); | 96 | let _e = pushenv("RUSTUP_TOOLCHAIN", "nightly"); |
98 | if run!("cargo fuzz --help").is_err() { | 97 | if cmd!("cargo fuzz --help").read().is_err() { |
99 | run!("cargo install cargo-fuzz")?; | 98 | cmd!("cargo install cargo-fuzz").run()?; |
100 | }; | 99 | }; |
101 | 100 | ||
102 | // Expecting nightly rustc | 101 | // Expecting nightly rustc |
103 | let out = run!("rustc --version")?; | 102 | let out = cmd!("rustc --version").read()?; |
104 | if !out.contains("nightly") { | 103 | if !out.contains("nightly") { |
105 | bail!("fuzz tests require nightly rustc") | 104 | bail!("fuzz tests require nightly rustc") |
106 | } | 105 | } |
107 | 106 | ||
108 | run!("cargo fuzz run parser")?; | 107 | cmd!("cargo fuzz run parser").run()?; |
109 | Ok(()) | 108 | Ok(()) |
110 | } | 109 | } |
111 | 110 | ||
111 | fn date_iso() -> Result<String> { | ||
112 | let res = cmd!("date --iso --utc").read()?; | ||
113 | Ok(res) | ||
114 | } | ||
115 | |||
112 | fn is_release_tag(tag: &str) -> bool { | 116 | fn is_release_tag(tag: &str) -> bool { |
113 | tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit()) | 117 | tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit()) |
114 | } | 118 | } |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3f4aa5497..97e5dcd4e 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -12,12 +12,12 @@ use std::env; | |||
12 | 12 | ||
13 | use codegen::CodegenCmd; | 13 | use codegen::CodegenCmd; |
14 | use pico_args::Arguments; | 14 | use pico_args::Arguments; |
15 | use xshell::pushd; | ||
15 | use xtask::{ | 16 | use xtask::{ |
16 | codegen::{self, Mode}, | 17 | codegen::{self, Mode}, |
17 | dist::DistCmd, | 18 | dist::DistCmd, |
18 | install::{ClientOpt, InstallCmd, Malloc, ServerOpt}, | 19 | install::{ClientOpt, InstallCmd, Malloc, ServerOpt}, |
19 | metrics::MetricsCmd, | 20 | metrics::MetricsCmd, |
20 | not_bash::pushd, | ||
21 | pre_cache::PreCacheCmd, | 21 | pre_cache::PreCacheCmd, |
22 | pre_commit, project_root, | 22 | pre_commit, project_root, |
23 | release::{PromoteCmd, ReleaseCmd}, | 23 | release::{PromoteCmd, ReleaseCmd}, |
@@ -29,7 +29,7 @@ fn main() -> Result<()> { | |||
29 | return pre_commit::run_hook(); | 29 | return pre_commit::run_hook(); |
30 | } | 30 | } |
31 | 31 | ||
32 | let _d = pushd(project_root()); | 32 | let _d = pushd(project_root())?; |
33 | 33 | ||
34 | let mut args = Arguments::from_env(); | 34 | let mut args = Arguments::from_env(); |
35 | let subcommand = args.subcommand()?.unwrap_or_default(); | 35 | let subcommand = args.subcommand()?.unwrap_or_default(); |
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index 4bade2c7e..e0d1aaf97 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs | |||
@@ -7,8 +7,7 @@ use std::{ | |||
7 | }; | 7 | }; |
8 | 8 | ||
9 | use anyhow::{bail, format_err, Result}; | 9 | use anyhow::{bail, format_err, Result}; |
10 | 10 | use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf}; | |
11 | use crate::not_bash::{fs2, pushd, pushenv, rm_rf, run}; | ||
12 | 11 | ||
13 | type Unit = String; | 12 | type Unit = String; |
14 | 13 | ||
@@ -23,12 +22,13 @@ impl MetricsCmd { | |||
23 | rm_rf("./target/release")?; | 22 | rm_rf("./target/release")?; |
24 | } | 23 | } |
25 | if !Path::new("./target/rustc-perf").exists() { | 24 | if !Path::new("./target/rustc-perf").exists() { |
26 | fs2::create_dir_all("./target/rustc-perf")?; | 25 | mkdir_p("./target/rustc-perf")?; |
27 | run!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")?; | 26 | cmd!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf") |
27 | .run()?; | ||
28 | } | 28 | } |
29 | { | 29 | { |
30 | let _d = pushd("./target/rustc-perf"); | 30 | let _d = pushd("./target/rustc-perf")?; |
31 | run!("git reset --hard 1d9288b0da7febf2599917da1b57dc241a1af033")?; | 31 | cmd!("git reset --hard 1d9288b0da7febf2599917da1b57dc241a1af033").run()?; |
32 | } | 32 | } |
33 | 33 | ||
34 | let _env = pushenv("RA_METRICS", "1"); | 34 | let _env = pushenv("RA_METRICS", "1"); |
@@ -39,17 +39,20 @@ impl MetricsCmd { | |||
39 | metrics.measure_analysis_stats("webrender")?; | 39 | metrics.measure_analysis_stats("webrender")?; |
40 | 40 | ||
41 | if !self.dry_run { | 41 | if !self.dry_run { |
42 | let _d = pushd("target"); | 42 | let _d = pushd("target")?; |
43 | let metrics_token = env::var("METRICS_TOKEN").unwrap(); | 43 | let metrics_token = env::var("METRICS_TOKEN").unwrap(); |
44 | let repo = format!("https://{}@github.com/rust-analyzer/metrics.git", metrics_token); | 44 | cmd!( |
45 | run!("git clone --depth 1 {}", repo)?; | 45 | "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git" |
46 | let _d = pushd("metrics"); | 46 | ) |
47 | .run()?; | ||
48 | let _d = pushd("metrics")?; | ||
47 | 49 | ||
48 | let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?; | 50 | let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?; |
49 | writeln!(file, "{}", metrics.json())?; | 51 | writeln!(file, "{}", metrics.json())?; |
50 | run!("git add .")?; | 52 | cmd!("git add .").run()?; |
51 | run!("git -c user.name=Bot -c [email protected] commit --message 📈")?; | 53 | cmd!("git -c user.name=Bot -c [email protected] commit --message 📈") |
52 | run!("git push origin master")?; | 54 | .run()?; |
55 | cmd!("git push origin master").run()?; | ||
53 | } | 56 | } |
54 | eprintln!("{:#?}", metrics); | 57 | eprintln!("{:#?}", metrics); |
55 | Ok(()) | 58 | Ok(()) |
@@ -59,10 +62,10 @@ impl MetricsCmd { | |||
59 | impl Metrics { | 62 | impl Metrics { |
60 | fn measure_build(&mut self) -> Result<()> { | 63 | fn measure_build(&mut self) -> Result<()> { |
61 | eprintln!("\nMeasuring build"); | 64 | eprintln!("\nMeasuring build"); |
62 | run!("cargo fetch")?; | 65 | cmd!("cargo fetch").run()?; |
63 | 66 | ||
64 | let time = Instant::now(); | 67 | let time = Instant::now(); |
65 | run!("cargo build --release --package rust-analyzer --bin rust-analyzer")?; | 68 | cmd!("cargo build --release --package rust-analyzer --bin rust-analyzer").run()?; |
66 | let time = time.elapsed(); | 69 | let time = time.elapsed(); |
67 | self.report("build", time.as_millis() as u64, "ms".into()); | 70 | self.report("build", time.as_millis() as u64, "ms".into()); |
68 | Ok(()) | 71 | Ok(()) |
@@ -78,7 +81,7 @@ impl Metrics { | |||
78 | } | 81 | } |
79 | fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> { | 82 | fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> { |
80 | eprintln!("\nMeasuring analysis-stats/{}", name); | 83 | eprintln!("\nMeasuring analysis-stats/{}", name); |
81 | let output = run!("./target/release/rust-analyzer analysis-stats --quiet {}", path)?; | 84 | let output = cmd!("./target/release/rust-analyzer analysis-stats --quiet {path}").read()?; |
82 | for (metric, value, unit) in parse_metrics(&output) { | 85 | for (metric, value, unit) in parse_metrics(&output) { |
83 | self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into()); | 86 | self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into()); |
84 | } | 87 | } |
@@ -118,7 +121,7 @@ impl Metrics { | |||
118 | fn new() -> Result<Metrics> { | 121 | fn new() -> Result<Metrics> { |
119 | let host = Host::new()?; | 122 | let host = Host::new()?; |
120 | let timestamp = SystemTime::now(); | 123 | let timestamp = SystemTime::now(); |
121 | let revision = run!("git rev-parse HEAD")?; | 124 | let revision = cmd!("git rev-parse HEAD").read()?; |
122 | Ok(Metrics { host, timestamp, revision, metrics: BTreeMap::new() }) | 125 | Ok(Metrics { host, timestamp, revision, metrics: BTreeMap::new() }) |
123 | } | 126 | } |
124 | 127 | ||
@@ -160,7 +163,7 @@ impl Host { | |||
160 | return Ok(Host { os, cpu, mem }); | 163 | return Ok(Host { os, cpu, mem }); |
161 | 164 | ||
162 | fn read_field<'a>(path: &str, field: &str) -> Result<String> { | 165 | fn read_field<'a>(path: &str, field: &str) -> Result<String> { |
163 | let text = fs2::read_to_string(path)?; | 166 | let text = read_file(path)?; |
164 | 167 | ||
165 | let line = text | 168 | let line = text |
166 | .lines() | 169 | .lines() |
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs deleted file mode 100644 index 038898993..000000000 --- a/xtask/src/not_bash.rs +++ /dev/null | |||
@@ -1,169 +0,0 @@ | |||
1 | //! A bad shell -- small cross platform module for writing glue code | ||
2 | |||
3 | use std::{ | ||
4 | cell::RefCell, | ||
5 | env, | ||
6 | ffi::OsString, | ||
7 | io::{self, Write}, | ||
8 | path::{Path, PathBuf}, | ||
9 | process::{Command, Stdio}, | ||
10 | }; | ||
11 | |||
12 | use anyhow::{bail, Context, Result}; | ||
13 | |||
14 | pub use fs_err as fs2; | ||
15 | |||
16 | #[macro_export] | ||
17 | macro_rules! run { | ||
18 | ($($expr:expr),*) => { | ||
19 | run!($($expr),*; echo = true) | ||
20 | }; | ||
21 | ($($expr:expr),* ; echo = $echo:expr) => { | ||
22 | $crate::not_bash::run_process(format!($($expr),*), $echo, None) | ||
23 | }; | ||
24 | ($($expr:expr),* ; <$stdin:expr) => { | ||
25 | $crate::not_bash::run_process(format!($($expr),*), false, Some($stdin)) | ||
26 | }; | ||
27 | } | ||
28 | pub use crate::run; | ||
29 | |||
30 | pub struct Pushd { | ||
31 | _p: (), | ||
32 | } | ||
33 | |||
34 | pub fn pushd(path: impl Into<PathBuf>) -> Pushd { | ||
35 | Env::with(|env| env.pushd(path.into())); | ||
36 | Pushd { _p: () } | ||
37 | } | ||
38 | |||
39 | impl Drop for Pushd { | ||
40 | fn drop(&mut self) { | ||
41 | Env::with(|env| env.popd()) | ||
42 | } | ||
43 | } | ||
44 | |||
45 | pub struct Pushenv { | ||
46 | _p: (), | ||
47 | } | ||
48 | |||
49 | pub fn pushenv(var: &str, value: &str) -> Pushenv { | ||
50 | Env::with(|env| env.pushenv(var.into(), value.into())); | ||
51 | Pushenv { _p: () } | ||
52 | } | ||
53 | |||
54 | impl Drop for Pushenv { | ||
55 | fn drop(&mut self) { | ||
56 | Env::with(|env| env.popenv()) | ||
57 | } | ||
58 | } | ||
59 | |||
60 | pub fn rm_rf(path: impl AsRef<Path>) -> io::Result<()> { | ||
61 | let path = path.as_ref(); | ||
62 | if !path.exists() { | ||
63 | return Ok(()); | ||
64 | } | ||
65 | if path.is_file() { | ||
66 | fs2::remove_file(path) | ||
67 | } else { | ||
68 | fs2::remove_dir_all(path) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[doc(hidden)] | ||
73 | pub fn run_process(cmd: String, echo: bool, stdin: Option<&[u8]>) -> Result<String> { | ||
74 | run_process_inner(&cmd, echo, stdin).with_context(|| format!("process `{}` failed", cmd)) | ||
75 | } | ||
76 | |||
77 | pub fn date_iso() -> Result<String> { | ||
78 | run!("date --iso --utc") | ||
79 | } | ||
80 | |||
81 | fn run_process_inner(cmd: &str, echo: bool, stdin: Option<&[u8]>) -> Result<String> { | ||
82 | let mut args = shelx(cmd); | ||
83 | let binary = args.remove(0); | ||
84 | let current_dir = Env::with(|it| it.cwd().to_path_buf()); | ||
85 | |||
86 | if echo { | ||
87 | println!("> {}", cmd) | ||
88 | } | ||
89 | |||
90 | let mut command = Command::new(binary); | ||
91 | command.args(args).current_dir(current_dir).stderr(Stdio::inherit()); | ||
92 | let output = match stdin { | ||
93 | None => command.stdin(Stdio::null()).output(), | ||
94 | Some(stdin) => { | ||
95 | command.stdin(Stdio::piped()).stdout(Stdio::piped()); | ||
96 | let mut process = command.spawn()?; | ||
97 | process.stdin.take().unwrap().write_all(stdin)?; | ||
98 | process.wait_with_output() | ||
99 | } | ||
100 | }?; | ||
101 | let stdout = String::from_utf8(output.stdout)?; | ||
102 | |||
103 | if echo { | ||
104 | print!("{}", stdout) | ||
105 | } | ||
106 | |||
107 | if !output.status.success() { | ||
108 | bail!("{}", output.status) | ||
109 | } | ||
110 | |||
111 | Ok(stdout.trim().to_string()) | ||
112 | } | ||
113 | |||
114 | // FIXME: some real shell lexing here | ||
115 | fn shelx(cmd: &str) -> Vec<String> { | ||
116 | let mut res = Vec::new(); | ||
117 | for (string_piece, in_quotes) in cmd.split('\'').zip([false, true].iter().copied().cycle()) { | ||
118 | if in_quotes { | ||
119 | res.push(string_piece.to_string()) | ||
120 | } else { | ||
121 | if !string_piece.is_empty() { | ||
122 | res.extend(string_piece.split_ascii_whitespace().map(|it| it.to_string())) | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | res | ||
127 | } | ||
128 | |||
129 | struct Env { | ||
130 | pushd_stack: Vec<PathBuf>, | ||
131 | pushenv_stack: Vec<(OsString, Option<OsString>)>, | ||
132 | } | ||
133 | |||
134 | impl Env { | ||
135 | fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T { | ||
136 | thread_local! { | ||
137 | static ENV: RefCell<Env> = RefCell::new(Env { | ||
138 | pushd_stack: vec![env::current_dir().unwrap()], | ||
139 | pushenv_stack: vec![], | ||
140 | }); | ||
141 | } | ||
142 | ENV.with(|it| f(&mut *it.borrow_mut())) | ||
143 | } | ||
144 | |||
145 | fn pushd(&mut self, dir: PathBuf) { | ||
146 | let dir = self.cwd().join(dir); | ||
147 | self.pushd_stack.push(dir); | ||
148 | env::set_current_dir(self.cwd()) | ||
149 | .unwrap_or_else(|err| panic!("Failed to set cwd to {}: {}", self.cwd().display(), err)); | ||
150 | } | ||
151 | fn popd(&mut self) { | ||
152 | self.pushd_stack.pop().unwrap(); | ||
153 | env::set_current_dir(self.cwd()).unwrap(); | ||
154 | } | ||
155 | fn pushenv(&mut self, var: OsString, value: OsString) { | ||
156 | self.pushenv_stack.push((var.clone(), env::var_os(&var))); | ||
157 | env::set_var(var, value) | ||
158 | } | ||
159 | fn popenv(&mut self) { | ||
160 | let (var, value) = self.pushenv_stack.pop().unwrap(); | ||
161 | match value { | ||
162 | None => env::remove_var(var), | ||
163 | Some(value) => env::set_var(var, value), | ||
164 | } | ||
165 | } | ||
166 | fn cwd(&self) -> &Path { | ||
167 | self.pushd_stack.last().unwrap() | ||
168 | } | ||
169 | } | ||
diff --git a/xtask/src/pre_cache.rs b/xtask/src/pre_cache.rs index 47ba6ba24..569f88f68 100644 --- a/xtask/src/pre_cache.rs +++ b/xtask/src/pre_cache.rs | |||
@@ -4,8 +4,7 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use anyhow::Result; | 6 | use anyhow::Result; |
7 | 7 | use xshell::rm_rf; | |
8 | use crate::not_bash::{fs2, rm_rf}; | ||
9 | 8 | ||
10 | pub struct PreCacheCmd; | 9 | pub struct PreCacheCmd; |
11 | 10 | ||
@@ -26,7 +25,7 @@ impl PreCacheCmd { | |||
26 | } | 25 | } |
27 | } | 26 | } |
28 | 27 | ||
29 | fs2::remove_file("./target/.rustc_info.json")?; | 28 | rm_rf("./target/.rustc_info.json")?; |
30 | 29 | ||
31 | let to_delete = read_dir("./crates", FileType::is_dir)? | 30 | let to_delete = read_dir("./crates", FileType::is_dir)? |
32 | .into_iter() | 31 | .into_iter() |
diff --git a/xtask/src/pre_commit.rs b/xtask/src/pre_commit.rs index 056f34acf..8f2dbea19 100644 --- a/xtask/src/pre_commit.rs +++ b/xtask/src/pre_commit.rs | |||
@@ -3,19 +3,21 @@ | |||
3 | use std::{fs, path::PathBuf}; | 3 | use std::{fs, path::PathBuf}; |
4 | 4 | ||
5 | use anyhow::{bail, Result}; | 5 | use anyhow::{bail, Result}; |
6 | use xshell::cmd; | ||
6 | 7 | ||
7 | use crate::{not_bash::run, project_root, run_rustfmt, Mode}; | 8 | use crate::{project_root, run_rustfmt, Mode}; |
8 | 9 | ||
9 | // FIXME: if there are changed `.ts` files, also reformat TypeScript (by | 10 | // FIXME: if there are changed `.ts` files, also reformat TypeScript (by |
10 | // shelling out to `npm fmt`). | 11 | // shelling out to `npm fmt`). |
11 | pub fn run_hook() -> Result<()> { | 12 | pub fn run_hook() -> Result<()> { |
12 | run_rustfmt(Mode::Overwrite)?; | 13 | run_rustfmt(Mode::Overwrite)?; |
13 | 14 | ||
14 | let diff = run!("git diff --diff-filter=MAR --name-only --cached")?; | 15 | let diff = cmd!("git diff --diff-filter=MAR --name-only --cached").read()?; |
15 | 16 | ||
16 | let root = project_root(); | 17 | let root = project_root(); |
17 | for line in diff.lines() { | 18 | for line in diff.lines() { |
18 | run!("git update-index --add {}", root.join(line).display())?; | 19 | let file = root.join(line); |
20 | cmd!("git update-index --add {file}").run()?; | ||
19 | } | 21 | } |
20 | 22 | ||
21 | Ok(()) | 23 | Ok(()) |
diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 3aab29801..14fc1f0dd 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | use crate::{ | 1 | use xshell::{cmd, cp, pushd, read_dir, write_file}; |
2 | codegen, is_release_tag, | 2 | |
3 | not_bash::{date_iso, fs2, pushd, run}, | 3 | use crate::{codegen, date_iso, is_release_tag, project_root, Mode, Result}; |
4 | project_root, Mode, Result, | ||
5 | }; | ||
6 | 4 | ||
7 | pub struct ReleaseCmd { | 5 | pub struct ReleaseCmd { |
8 | pub dry_run: bool, | 6 | pub dry_run: bool, |
@@ -11,10 +9,10 @@ pub struct ReleaseCmd { | |||
11 | impl ReleaseCmd { | 9 | impl ReleaseCmd { |
12 | pub fn run(self) -> Result<()> { | 10 | pub fn run(self) -> Result<()> { |
13 | if !self.dry_run { | 11 | if !self.dry_run { |
14 | run!("git switch release")?; | 12 | cmd!("git switch release").run()?; |
15 | run!("git fetch upstream --tags --force")?; | 13 | cmd!("git fetch upstream --tags --force").run()?; |
16 | run!("git reset --hard tags/nightly")?; | 14 | cmd!("git reset --hard tags/nightly").run()?; |
17 | run!("git push")?; | 15 | cmd!("git push").run()?; |
18 | } | 16 | } |
19 | codegen::generate_assists_docs(Mode::Overwrite)?; | 17 | codegen::generate_assists_docs(Mode::Overwrite)?; |
20 | codegen::generate_feature_docs(Mode::Overwrite)?; | 18 | codegen::generate_feature_docs(Mode::Overwrite)?; |
@@ -23,8 +21,8 @@ impl ReleaseCmd { | |||
23 | let changelog_dir = website_root.join("./thisweek/_posts"); | 21 | let changelog_dir = website_root.join("./thisweek/_posts"); |
24 | 22 | ||
25 | let today = date_iso()?; | 23 | let today = date_iso()?; |
26 | let commit = run!("git rev-parse HEAD")?; | 24 | let commit = cmd!("git rev-parse HEAD").read()?; |
27 | let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); | 25 | let changelog_n = read_dir(changelog_dir.as_path())?.len(); |
28 | 26 | ||
29 | let contents = format!( | 27 | let contents = format!( |
30 | "\ | 28 | "\ |
@@ -52,20 +50,20 @@ https://github.com/sponsors/rust-analyzer[GitHub Sponsors]. | |||
52 | ); | 50 | ); |
53 | 51 | ||
54 | let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); | 52 | let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); |
55 | fs2::write(&path, &contents)?; | 53 | write_file(&path, &contents)?; |
56 | 54 | ||
57 | for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { | 55 | for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { |
58 | let src = project_root().join("./docs/user/").join(adoc); | 56 | let src = project_root().join("./docs/user/").join(adoc); |
59 | let dst = website_root.join(adoc); | 57 | let dst = website_root.join(adoc); |
60 | fs2::copy(src, dst)?; | 58 | cp(src, dst)?; |
61 | } | 59 | } |
62 | 60 | ||
63 | let tags = run!("git tag --list"; echo = false)?; | 61 | let tags = cmd!("git tag --list").read()?; |
64 | let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); | 62 | let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); |
65 | 63 | ||
66 | let git_log = run!("git log {}..HEAD --merges --reverse", prev_tag; echo = false)?; | 64 | let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?; |
67 | let git_log_dst = website_root.join("git.log"); | 65 | let git_log_dst = website_root.join("git.log"); |
68 | fs2::write(git_log_dst, &git_log)?; | 66 | write_file(git_log_dst, &git_log)?; |
69 | 67 | ||
70 | Ok(()) | 68 | Ok(()) |
71 | } | 69 | } |
@@ -77,27 +75,25 @@ pub struct PromoteCmd { | |||
77 | 75 | ||
78 | impl PromoteCmd { | 76 | impl PromoteCmd { |
79 | pub fn run(self) -> Result<()> { | 77 | pub fn run(self) -> Result<()> { |
80 | let _dir = pushd("../rust-rust-analyzer"); | 78 | let _dir = pushd("../rust-rust-analyzer")?; |
81 | run!("git switch master")?; | 79 | cmd!("git switch master").run()?; |
82 | run!("git fetch upstream")?; | 80 | cmd!("git fetch upstream").run()?; |
83 | run!("git reset --hard upstream/master")?; | 81 | cmd!("git reset --hard upstream/master").run()?; |
84 | run!("git submodule update --recursive")?; | 82 | cmd!("git submodule update --recursive").run()?; |
85 | 83 | ||
86 | let branch = format!("rust-analyzer-{}", date_iso()?); | 84 | let branch = format!("rust-analyzer-{}", date_iso()?); |
87 | run!("git switch -c {}", branch)?; | 85 | cmd!("git switch -c {branch}").run()?; |
88 | { | 86 | { |
89 | let _dir = pushd("src/tools/rust-analyzer"); | 87 | let _dir = pushd("src/tools/rust-analyzer")?; |
90 | run!("git fetch origin")?; | 88 | cmd!("git fetch origin").run()?; |
91 | run!("git reset --hard origin/release")?; | 89 | cmd!("git reset --hard origin/release").run()?; |
92 | } | 90 | } |
93 | run!("git add src/tools/rust-analyzer")?; | 91 | cmd!("git add src/tools/rust-analyzer").run()?; |
94 | run!("git commit -m':arrow_up: rust-analyzer'")?; | 92 | cmd!("git commit -m':arrow_up: rust-analyzer'").run()?; |
95 | if !self.dry_run { | 93 | if !self.dry_run { |
96 | run!("git push")?; | 94 | cmd!("git push").run()?; |
97 | run!( | 95 | cmd!("xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost") |
98 | "xdg-open https://github.com/matklad/rust/pull/new/{}?body=r%3F%20%40ghost", | 96 | .run()?; |
99 | branch | ||
100 | )?; | ||
101 | } | 97 | } |
102 | Ok(()) | 98 | Ok(()) |
103 | } | 99 | } |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index b3bb9d543..d335adb72 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -3,9 +3,9 @@ use std::{ | |||
3 | path::{Path, PathBuf}, | 3 | path::{Path, PathBuf}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use xshell::{cmd, read_file}; | ||
6 | use xtask::{ | 7 | use xtask::{ |
7 | codegen::{self, Mode}, | 8 | codegen::{self, Mode}, |
8 | not_bash::{fs2, run}, | ||
9 | project_root, run_rustfmt, rust_files, | 9 | project_root, run_rustfmt, rust_files, |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -48,14 +48,13 @@ fn smoke_test_docs_generation() { | |||
48 | fn check_lsp_extensions_docs() { | 48 | fn check_lsp_extensions_docs() { |
49 | let expected_hash = { | 49 | let expected_hash = { |
50 | let lsp_ext_rs = | 50 | let lsp_ext_rs = |
51 | fs2::read_to_string(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")) | 51 | read_file(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")).unwrap(); |
52 | .unwrap(); | ||
53 | stable_hash(lsp_ext_rs.as_str()) | 52 | stable_hash(lsp_ext_rs.as_str()) |
54 | }; | 53 | }; |
55 | 54 | ||
56 | let actual_hash = { | 55 | let actual_hash = { |
57 | let lsp_extensions_md = | 56 | let lsp_extensions_md = |
58 | fs2::read_to_string(project_root().join("docs/dev/lsp-extensions.md")).unwrap(); | 57 | read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap(); |
59 | let text = lsp_extensions_md | 58 | let text = lsp_extensions_md |
60 | .lines() | 59 | .lines() |
61 | .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) | 60 | .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) |
@@ -83,7 +82,7 @@ Please adjust docs/dev/lsp-extensions.md. | |||
83 | fn rust_files_are_tidy() { | 82 | fn rust_files_are_tidy() { |
84 | let mut tidy_docs = TidyDocs::default(); | 83 | let mut tidy_docs = TidyDocs::default(); |
85 | for path in rust_files(&project_root().join("crates")) { | 84 | for path in rust_files(&project_root().join("crates")) { |
86 | let text = fs2::read_to_string(&path).unwrap(); | 85 | let text = read_file(&path).unwrap(); |
87 | check_todo(&path, &text); | 86 | check_todo(&path, &text); |
88 | check_trailing_ws(&path, &text); | 87 | check_trailing_ws(&path, &text); |
89 | deny_clippy(&path, &text); | 88 | deny_clippy(&path, &text); |
@@ -94,8 +93,10 @@ fn rust_files_are_tidy() { | |||
94 | 93 | ||
95 | #[test] | 94 | #[test] |
96 | fn check_merge_commits() { | 95 | fn check_merge_commits() { |
97 | let stdout = run!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19.."; echo = false) | 96 | let stdout = |
98 | .unwrap(); | 97 | dbg!(cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..")) |
98 | .read() | ||
99 | .unwrap(); | ||
99 | if !stdout.is_empty() { | 100 | if !stdout.is_empty() { |
100 | panic!( | 101 | panic!( |
101 | " | 102 | " |
@@ -168,7 +169,7 @@ Zlib OR Apache-2.0 OR MIT | |||
168 | .filter(|it| !it.is_empty()) | 169 | .filter(|it| !it.is_empty()) |
169 | .collect::<Vec<_>>(); | 170 | .collect::<Vec<_>>(); |
170 | 171 | ||
171 | let meta = run!("cargo metadata --format-version 1"; echo = false).unwrap(); | 172 | let meta = cmd!("cargo metadata --format-version 1").read().unwrap(); |
172 | let mut licenses = meta | 173 | let mut licenses = meta |
173 | .split(|c| c == ',' || c == '{' || c == '}') | 174 | .split(|c| c == ',' || c == '{' || c == '}') |
174 | .filter(|it| it.contains(r#""license""#)) | 175 | .filter(|it| it.contains(r#""license""#)) |
@@ -177,6 +178,25 @@ Zlib OR Apache-2.0 OR MIT | |||
177 | .collect::<Vec<_>>(); | 178 | .collect::<Vec<_>>(); |
178 | licenses.sort(); | 179 | licenses.sort(); |
179 | licenses.dedup(); | 180 | licenses.dedup(); |
181 | if licenses != expected { | ||
182 | let mut diff = String::new(); | ||
183 | |||
184 | diff += &format!("New Licenses:\n"); | ||
185 | for &l in licenses.iter() { | ||
186 | if !expected.contains(&l) { | ||
187 | diff += &format!(" {}\n", l) | ||
188 | } | ||
189 | } | ||
190 | |||
191 | diff += &format!("\nMissing Licenses:\n"); | ||
192 | for &l in expected.iter() { | ||
193 | if !licenses.contains(&l) { | ||
194 | diff += &format!(" {}\n", l) | ||
195 | } | ||
196 | } | ||
197 | |||
198 | panic!("different set of licenses!\n{}", diff); | ||
199 | } | ||
180 | assert_eq!(licenses, expected); | 200 | assert_eq!(licenses, expected); |
181 | } | 201 | } |
182 | 202 | ||