diff options
Diffstat (limited to 'crates')
38 files changed, 694 insertions, 114 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 43f0d664b..2ab65ab99 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -101,7 +101,6 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
101 | Some(assist) | 101 | Some(assist) |
102 | } | 102 | } |
103 | 103 | ||
104 | #[allow(dead_code)] // will be used for auto import assist with multiple actions | ||
105 | pub(crate) fn add_assist_group( | 104 | pub(crate) fn add_assist_group( |
106 | self, | 105 | self, |
107 | id: AssistId, | 106 | id: AssistId, |
@@ -168,7 +167,6 @@ pub(crate) struct ActionBuilder { | |||
168 | } | 167 | } |
169 | 168 | ||
170 | impl ActionBuilder { | 169 | impl ActionBuilder { |
171 | #[allow(dead_code)] | ||
172 | /// Adds a custom label to the action, if it needs to be different from the assist label | 170 | /// Adds a custom label to the action, if it needs to be different from the assist label |
173 | pub(crate) fn label(&mut self, label: impl Into<String>) { | 171 | pub(crate) fn label(&mut self, label: impl Into<String>) { |
174 | self.label = Some(label.into()) | 172 | self.label = Some(label.into()) |
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs index f9f826b88..38a351a54 100644 --- a/crates/ra_assists/src/assists/add_explicit_type.rs +++ b/crates/ra_assists/src/assists/add_explicit_type.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use hir::{db::HirDatabase, HirDisplay}; | 1 | use hir::{db::HirDatabase, HirDisplay}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, AstNode, LetStmt, NameOwner}, | 3 | ast::{self, AstNode, LetStmt, NameOwner}, |
4 | T, | 4 | TextRange, T, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{Assist, AssistCtx, AssistId}; |
@@ -34,6 +34,14 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi | |||
34 | // The binding must have a name | 34 | // The binding must have a name |
35 | let name = pat.name()?; | 35 | let name = pat.name()?; |
36 | let name_range = name.syntax().text_range(); | 36 | let name_range = name.syntax().text_range(); |
37 | // Assist should only be applicable if cursor is between 'let' and '=' | ||
38 | let stmt_range = stmt.syntax().text_range(); | ||
39 | let eq_range = stmt.eq_token()?.text_range(); | ||
40 | let let_range = TextRange::from_to(stmt_range.start(), eq_range.start()); | ||
41 | let cursor_in_range = ctx.frange.range.is_subrange(&let_range); | ||
42 | if !cursor_in_range { | ||
43 | return None; | ||
44 | } | ||
37 | // Assist not applicable if the type has already been specified | 45 | // Assist not applicable if the type has already been specified |
38 | if stmt.syntax().children_with_tokens().any(|child| child.kind() == T![:]) { | 46 | if stmt.syntax().children_with_tokens().any(|child| child.kind() == T![:]) { |
39 | return None; | 47 | return None; |
@@ -109,4 +117,20 @@ mod tests { | |||
109 | fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() { | 117 | fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() { |
110 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }"); | 118 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }"); |
111 | } | 119 | } |
120 | |||
121 | #[test] | ||
122 | fn add_explicit_type_not_applicable_if_cursor_after_equals() { | ||
123 | check_assist_not_applicable( | ||
124 | add_explicit_type, | ||
125 | "fn f() {let a =<|> match 1 {2 => 3, 3 => 5};}", | ||
126 | ) | ||
127 | } | ||
128 | |||
129 | #[test] | ||
130 | fn add_explicit_type_not_applicable_if_cursor_before_let() { | ||
131 | check_assist_not_applicable( | ||
132 | add_explicit_type, | ||
133 | "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}", | ||
134 | ) | ||
135 | } | ||
112 | } | 136 | } |
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs new file mode 100644 index 000000000..9163cc662 --- /dev/null +++ b/crates/ra_assists/src/assists/auto_import.rs | |||
@@ -0,0 +1,222 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use ra_syntax::{ | ||
3 | ast::{self, AstNode}, | ||
4 | SmolStr, SyntaxElement, | ||
5 | SyntaxKind::{NAME_REF, USE_ITEM}, | ||
6 | SyntaxNode, | ||
7 | }; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_ctx::{ActionBuilder, Assist, AssistCtx}, | ||
11 | auto_import_text_edit, AssistId, ImportsLocator, | ||
12 | }; | ||
13 | |||
14 | // Assist: auto_import | ||
15 | // | ||
16 | // If the name is unresolved, provides all possible imports for it. | ||
17 | // | ||
18 | // ``` | ||
19 | // fn main() { | ||
20 | // let map = HashMap<|>::new(); | ||
21 | // } | ||
22 | // ``` | ||
23 | // -> | ||
24 | // ``` | ||
25 | // use std::collections::HashMap; | ||
26 | // | ||
27 | // fn main() { | ||
28 | // let map = HashMap<|>::new(); | ||
29 | // } | ||
30 | // ``` | ||
31 | pub(crate) fn auto_import<F: ImportsLocator>( | ||
32 | ctx: AssistCtx<impl HirDatabase>, | ||
33 | imports_locator: &mut F, | ||
34 | ) -> Option<Assist> { | ||
35 | let path: ast::Path = ctx.find_node_at_offset()?; | ||
36 | let module = path.syntax().ancestors().find_map(ast::Module::cast); | ||
37 | let position = match module.and_then(|it| it.item_list()) { | ||
38 | Some(item_list) => item_list.syntax().clone(), | ||
39 | None => { | ||
40 | let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?; | ||
41 | current_file.syntax().clone() | ||
42 | } | ||
43 | }; | ||
44 | let source_analyzer = ctx.source_analyzer(&position, None); | ||
45 | let module_with_name_to_import = source_analyzer.module()?; | ||
46 | let path_to_import = ctx.covering_element().ancestors().find_map(ast::Path::cast)?; | ||
47 | if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() { | ||
48 | return None; | ||
49 | } | ||
50 | |||
51 | let name_to_import = &find_applicable_name_ref(ctx.covering_element())?.syntax().to_string(); | ||
52 | let proposed_imports = imports_locator | ||
53 | .find_imports(&name_to_import.to_string()) | ||
54 | .into_iter() | ||
55 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | ||
56 | .filter(|use_path| !use_path.segments.is_empty()) | ||
57 | .take(20) | ||
58 | .map(|import| import.to_string()) | ||
59 | .collect::<std::collections::BTreeSet<_>>(); | ||
60 | if proposed_imports.is_empty() { | ||
61 | return None; | ||
62 | } | ||
63 | |||
64 | ctx.add_assist_group(AssistId("auto_import"), "auto import", || { | ||
65 | proposed_imports | ||
66 | .into_iter() | ||
67 | .map(|import| import_to_action(import, &position, &path_to_import.syntax())) | ||
68 | .collect() | ||
69 | }) | ||
70 | } | ||
71 | |||
72 | fn find_applicable_name_ref(element: SyntaxElement) -> Option<ast::NameRef> { | ||
73 | if element.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() { | ||
74 | None | ||
75 | } else if element.kind() == NAME_REF { | ||
76 | Some(element.as_node().cloned().and_then(ast::NameRef::cast)?) | ||
77 | } else { | ||
78 | let parent = element.parent()?; | ||
79 | if parent.kind() == NAME_REF { | ||
80 | Some(ast::NameRef::cast(parent)?) | ||
81 | } else { | ||
82 | None | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { | ||
88 | let mut action_builder = ActionBuilder::default(); | ||
89 | action_builder.label(format!("Import `{}`", &import)); | ||
90 | auto_import_text_edit( | ||
91 | position, | ||
92 | anchor, | ||
93 | &[SmolStr::new(import)], | ||
94 | action_builder.text_edit_builder(), | ||
95 | ); | ||
96 | action_builder | ||
97 | } | ||
98 | |||
99 | #[cfg(test)] | ||
100 | mod tests { | ||
101 | use super::*; | ||
102 | use crate::helpers::{ | ||
103 | check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable, | ||
104 | TestImportsLocator, | ||
105 | }; | ||
106 | |||
107 | #[test] | ||
108 | fn applicable_when_found_an_import() { | ||
109 | check_assist_with_imports_locator( | ||
110 | auto_import, | ||
111 | TestImportsLocator::new, | ||
112 | r" | ||
113 | PubStruct<|> | ||
114 | |||
115 | pub mod PubMod { | ||
116 | pub struct PubStruct; | ||
117 | } | ||
118 | ", | ||
119 | r" | ||
120 | use PubMod::PubStruct; | ||
121 | |||
122 | PubStruct<|> | ||
123 | |||
124 | pub mod PubMod { | ||
125 | pub struct PubStruct; | ||
126 | } | ||
127 | ", | ||
128 | ); | ||
129 | } | ||
130 | |||
131 | #[test] | ||
132 | fn applicable_when_found_multiple_imports() { | ||
133 | check_assist_with_imports_locator( | ||
134 | auto_import, | ||
135 | TestImportsLocator::new, | ||
136 | r" | ||
137 | PubStruct<|> | ||
138 | |||
139 | pub mod PubMod1 { | ||
140 | pub struct PubStruct; | ||
141 | } | ||
142 | pub mod PubMod2 { | ||
143 | pub struct PubStruct; | ||
144 | } | ||
145 | pub mod PubMod3 { | ||
146 | pub struct PubStruct; | ||
147 | } | ||
148 | ", | ||
149 | r" | ||
150 | use PubMod1::PubStruct; | ||
151 | |||
152 | PubStruct<|> | ||
153 | |||
154 | pub mod PubMod1 { | ||
155 | pub struct PubStruct; | ||
156 | } | ||
157 | pub mod PubMod2 { | ||
158 | pub struct PubStruct; | ||
159 | } | ||
160 | pub mod PubMod3 { | ||
161 | pub struct PubStruct; | ||
162 | } | ||
163 | ", | ||
164 | ); | ||
165 | } | ||
166 | |||
167 | #[test] | ||
168 | fn not_applicable_for_already_imported_types() { | ||
169 | check_assist_with_imports_locator_not_applicable( | ||
170 | auto_import, | ||
171 | TestImportsLocator::new, | ||
172 | r" | ||
173 | use PubMod::PubStruct; | ||
174 | |||
175 | PubStruct<|> | ||
176 | |||
177 | pub mod PubMod { | ||
178 | pub struct PubStruct; | ||
179 | } | ||
180 | ", | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn not_applicable_for_types_with_private_paths() { | ||
186 | check_assist_with_imports_locator_not_applicable( | ||
187 | auto_import, | ||
188 | TestImportsLocator::new, | ||
189 | r" | ||
190 | PrivateStruct<|> | ||
191 | |||
192 | pub mod PubMod { | ||
193 | struct PrivateStruct; | ||
194 | } | ||
195 | ", | ||
196 | ); | ||
197 | } | ||
198 | |||
199 | #[test] | ||
200 | fn not_applicable_when_no_imports_found() { | ||
201 | check_assist_with_imports_locator_not_applicable( | ||
202 | auto_import, | ||
203 | TestImportsLocator::new, | ||
204 | " | ||
205 | PubStruct<|>", | ||
206 | ); | ||
207 | } | ||
208 | |||
209 | #[test] | ||
210 | fn not_applicable_in_import_statements() { | ||
211 | check_assist_with_imports_locator_not_applicable( | ||
212 | auto_import, | ||
213 | TestImportsLocator::new, | ||
214 | r" | ||
215 | use PubStruct<|>; | ||
216 | |||
217 | pub mod PubMod { | ||
218 | pub struct PubStruct; | ||
219 | }", | ||
220 | ); | ||
221 | } | ||
222 | } | ||
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs index 45e0f983f..83527d904 100644 --- a/crates/ra_assists/src/assists/inline_local_variable.rs +++ b/crates/ra_assists/src/assists/inline_local_variable.rs | |||
@@ -23,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
23 | // (1 + 2) * 4; | 23 | // (1 + 2) * 4; |
24 | // } | 24 | // } |
25 | // ``` | 25 | // ``` |
26 | pub(crate) fn inline_local_varialbe(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 26 | pub(crate) fn inline_local_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
27 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; | 27 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; |
28 | let bind_pat = match let_stmt.pat()? { | 28 | let bind_pat = match let_stmt.pat()? { |
29 | ast::Pat::BindPat(pat) => pat, | 29 | ast::Pat::BindPat(pat) => pat, |
@@ -47,6 +47,9 @@ pub(crate) fn inline_local_varialbe(ctx: AssistCtx<impl HirDatabase>) -> Option< | |||
47 | }; | 47 | }; |
48 | let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); | 48 | let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); |
49 | let refs = analyzer.find_all_refs(&bind_pat); | 49 | let refs = analyzer.find_all_refs(&bind_pat); |
50 | if refs.is_empty() { | ||
51 | return None; | ||
52 | }; | ||
50 | 53 | ||
51 | let mut wrap_in_parens = vec![true; refs.len()]; | 54 | let mut wrap_in_parens = vec![true; refs.len()]; |
52 | 55 | ||
@@ -117,7 +120,7 @@ mod tests { | |||
117 | #[test] | 120 | #[test] |
118 | fn test_inline_let_bind_literal_expr() { | 121 | fn test_inline_let_bind_literal_expr() { |
119 | check_assist( | 122 | check_assist( |
120 | inline_local_varialbe, | 123 | inline_local_variable, |
121 | " | 124 | " |
122 | fn bar(a: usize) {} | 125 | fn bar(a: usize) {} |
123 | fn foo() { | 126 | fn foo() { |
@@ -151,7 +154,7 @@ fn foo() { | |||
151 | #[test] | 154 | #[test] |
152 | fn test_inline_let_bind_bin_expr() { | 155 | fn test_inline_let_bind_bin_expr() { |
153 | check_assist( | 156 | check_assist( |
154 | inline_local_varialbe, | 157 | inline_local_variable, |
155 | " | 158 | " |
156 | fn bar(a: usize) {} | 159 | fn bar(a: usize) {} |
157 | fn foo() { | 160 | fn foo() { |
@@ -185,7 +188,7 @@ fn foo() { | |||
185 | #[test] | 188 | #[test] |
186 | fn test_inline_let_bind_function_call_expr() { | 189 | fn test_inline_let_bind_function_call_expr() { |
187 | check_assist( | 190 | check_assist( |
188 | inline_local_varialbe, | 191 | inline_local_variable, |
189 | " | 192 | " |
190 | fn bar(a: usize) {} | 193 | fn bar(a: usize) {} |
191 | fn foo() { | 194 | fn foo() { |
@@ -219,7 +222,7 @@ fn foo() { | |||
219 | #[test] | 222 | #[test] |
220 | fn test_inline_let_bind_cast_expr() { | 223 | fn test_inline_let_bind_cast_expr() { |
221 | check_assist( | 224 | check_assist( |
222 | inline_local_varialbe, | 225 | inline_local_variable, |
223 | " | 226 | " |
224 | fn bar(a: usize): usize { a } | 227 | fn bar(a: usize): usize { a } |
225 | fn foo() { | 228 | fn foo() { |
@@ -253,7 +256,7 @@ fn foo() { | |||
253 | #[test] | 256 | #[test] |
254 | fn test_inline_let_bind_block_expr() { | 257 | fn test_inline_let_bind_block_expr() { |
255 | check_assist( | 258 | check_assist( |
256 | inline_local_varialbe, | 259 | inline_local_variable, |
257 | " | 260 | " |
258 | fn foo() { | 261 | fn foo() { |
259 | let a<|> = { 10 + 1 }; | 262 | let a<|> = { 10 + 1 }; |
@@ -285,7 +288,7 @@ fn foo() { | |||
285 | #[test] | 288 | #[test] |
286 | fn test_inline_let_bind_paren_expr() { | 289 | fn test_inline_let_bind_paren_expr() { |
287 | check_assist( | 290 | check_assist( |
288 | inline_local_varialbe, | 291 | inline_local_variable, |
289 | " | 292 | " |
290 | fn foo() { | 293 | fn foo() { |
291 | let a<|> = ( 10 + 1 ); | 294 | let a<|> = ( 10 + 1 ); |
@@ -317,7 +320,7 @@ fn foo() { | |||
317 | #[test] | 320 | #[test] |
318 | fn test_not_inline_mut_variable() { | 321 | fn test_not_inline_mut_variable() { |
319 | check_assist_not_applicable( | 322 | check_assist_not_applicable( |
320 | inline_local_varialbe, | 323 | inline_local_variable, |
321 | " | 324 | " |
322 | fn foo() { | 325 | fn foo() { |
323 | let mut a<|> = 1 + 1; | 326 | let mut a<|> = 1 + 1; |
@@ -329,7 +332,7 @@ fn foo() { | |||
329 | #[test] | 332 | #[test] |
330 | fn test_call_expr() { | 333 | fn test_call_expr() { |
331 | check_assist( | 334 | check_assist( |
332 | inline_local_varialbe, | 335 | inline_local_variable, |
333 | " | 336 | " |
334 | fn foo() { | 337 | fn foo() { |
335 | let a<|> = bar(10 + 1); | 338 | let a<|> = bar(10 + 1); |
@@ -347,7 +350,7 @@ fn foo() { | |||
347 | #[test] | 350 | #[test] |
348 | fn test_index_expr() { | 351 | fn test_index_expr() { |
349 | check_assist( | 352 | check_assist( |
350 | inline_local_varialbe, | 353 | inline_local_variable, |
351 | " | 354 | " |
352 | fn foo() { | 355 | fn foo() { |
353 | let x = vec![1, 2, 3]; | 356 | let x = vec![1, 2, 3]; |
@@ -367,7 +370,7 @@ fn foo() { | |||
367 | #[test] | 370 | #[test] |
368 | fn test_method_call_expr() { | 371 | fn test_method_call_expr() { |
369 | check_assist( | 372 | check_assist( |
370 | inline_local_varialbe, | 373 | inline_local_variable, |
371 | " | 374 | " |
372 | fn foo() { | 375 | fn foo() { |
373 | let bar = vec![1]; | 376 | let bar = vec![1]; |
@@ -387,7 +390,7 @@ fn foo() { | |||
387 | #[test] | 390 | #[test] |
388 | fn test_field_expr() { | 391 | fn test_field_expr() { |
389 | check_assist( | 392 | check_assist( |
390 | inline_local_varialbe, | 393 | inline_local_variable, |
391 | " | 394 | " |
392 | struct Bar { | 395 | struct Bar { |
393 | foo: usize | 396 | foo: usize |
@@ -415,7 +418,7 @@ fn foo() { | |||
415 | #[test] | 418 | #[test] |
416 | fn test_try_expr() { | 419 | fn test_try_expr() { |
417 | check_assist( | 420 | check_assist( |
418 | inline_local_varialbe, | 421 | inline_local_variable, |
419 | " | 422 | " |
420 | fn foo() -> Option<usize> { | 423 | fn foo() -> Option<usize> { |
421 | let bar = Some(1); | 424 | let bar = Some(1); |
@@ -437,7 +440,7 @@ fn foo() -> Option<usize> { | |||
437 | #[test] | 440 | #[test] |
438 | fn test_ref_expr() { | 441 | fn test_ref_expr() { |
439 | check_assist( | 442 | check_assist( |
440 | inline_local_varialbe, | 443 | inline_local_variable, |
441 | " | 444 | " |
442 | fn foo() { | 445 | fn foo() { |
443 | let bar = 10; | 446 | let bar = 10; |
@@ -455,7 +458,7 @@ fn foo() { | |||
455 | #[test] | 458 | #[test] |
456 | fn test_tuple_expr() { | 459 | fn test_tuple_expr() { |
457 | check_assist( | 460 | check_assist( |
458 | inline_local_varialbe, | 461 | inline_local_variable, |
459 | " | 462 | " |
460 | fn foo() { | 463 | fn foo() { |
461 | let a<|> = (10, 20); | 464 | let a<|> = (10, 20); |
@@ -471,7 +474,7 @@ fn foo() { | |||
471 | #[test] | 474 | #[test] |
472 | fn test_array_expr() { | 475 | fn test_array_expr() { |
473 | check_assist( | 476 | check_assist( |
474 | inline_local_varialbe, | 477 | inline_local_variable, |
475 | " | 478 | " |
476 | fn foo() { | 479 | fn foo() { |
477 | let a<|> = [1, 2, 3]; | 480 | let a<|> = [1, 2, 3]; |
@@ -487,7 +490,7 @@ fn foo() { | |||
487 | #[test] | 490 | #[test] |
488 | fn test_paren() { | 491 | fn test_paren() { |
489 | check_assist( | 492 | check_assist( |
490 | inline_local_varialbe, | 493 | inline_local_variable, |
491 | " | 494 | " |
492 | fn foo() { | 495 | fn foo() { |
493 | let a<|> = (10 + 20); | 496 | let a<|> = (10 + 20); |
@@ -505,7 +508,7 @@ fn foo() { | |||
505 | #[test] | 508 | #[test] |
506 | fn test_path_expr() { | 509 | fn test_path_expr() { |
507 | check_assist( | 510 | check_assist( |
508 | inline_local_varialbe, | 511 | inline_local_variable, |
509 | " | 512 | " |
510 | fn foo() { | 513 | fn foo() { |
511 | let d = 10; | 514 | let d = 10; |
@@ -525,7 +528,7 @@ fn foo() { | |||
525 | #[test] | 528 | #[test] |
526 | fn test_block_expr() { | 529 | fn test_block_expr() { |
527 | check_assist( | 530 | check_assist( |
528 | inline_local_varialbe, | 531 | inline_local_variable, |
529 | " | 532 | " |
530 | fn foo() { | 533 | fn foo() { |
531 | let a<|> = { 10 }; | 534 | let a<|> = { 10 }; |
@@ -543,7 +546,7 @@ fn foo() { | |||
543 | #[test] | 546 | #[test] |
544 | fn test_used_in_different_expr1() { | 547 | fn test_used_in_different_expr1() { |
545 | check_assist( | 548 | check_assist( |
546 | inline_local_varialbe, | 549 | inline_local_variable, |
547 | " | 550 | " |
548 | fn foo() { | 551 | fn foo() { |
549 | let a<|> = 10 + 20; | 552 | let a<|> = 10 + 20; |
@@ -565,7 +568,7 @@ fn foo() { | |||
565 | #[test] | 568 | #[test] |
566 | fn test_used_in_for_expr() { | 569 | fn test_used_in_for_expr() { |
567 | check_assist( | 570 | check_assist( |
568 | inline_local_varialbe, | 571 | inline_local_variable, |
569 | " | 572 | " |
570 | fn foo() { | 573 | fn foo() { |
571 | let a<|> = vec![10, 20]; | 574 | let a<|> = vec![10, 20]; |
@@ -581,7 +584,7 @@ fn foo() { | |||
581 | #[test] | 584 | #[test] |
582 | fn test_used_in_while_expr() { | 585 | fn test_used_in_while_expr() { |
583 | check_assist( | 586 | check_assist( |
584 | inline_local_varialbe, | 587 | inline_local_variable, |
585 | " | 588 | " |
586 | fn foo() { | 589 | fn foo() { |
587 | let a<|> = 1 > 0; | 590 | let a<|> = 1 > 0; |
@@ -597,7 +600,7 @@ fn foo() { | |||
597 | #[test] | 600 | #[test] |
598 | fn test_used_in_break_expr() { | 601 | fn test_used_in_break_expr() { |
599 | check_assist( | 602 | check_assist( |
600 | inline_local_varialbe, | 603 | inline_local_variable, |
601 | " | 604 | " |
602 | fn foo() { | 605 | fn foo() { |
603 | let a<|> = 1 + 1; | 606 | let a<|> = 1 + 1; |
@@ -617,7 +620,7 @@ fn foo() { | |||
617 | #[test] | 620 | #[test] |
618 | fn test_used_in_return_expr() { | 621 | fn test_used_in_return_expr() { |
619 | check_assist( | 622 | check_assist( |
620 | inline_local_varialbe, | 623 | inline_local_variable, |
621 | " | 624 | " |
622 | fn foo() { | 625 | fn foo() { |
623 | let a<|> = 1 > 0; | 626 | let a<|> = 1 > 0; |
@@ -633,7 +636,7 @@ fn foo() { | |||
633 | #[test] | 636 | #[test] |
634 | fn test_used_in_match_expr() { | 637 | fn test_used_in_match_expr() { |
635 | check_assist( | 638 | check_assist( |
636 | inline_local_varialbe, | 639 | inline_local_variable, |
637 | " | 640 | " |
638 | fn foo() { | 641 | fn foo() { |
639 | let a<|> = 1 > 0; | 642 | let a<|> = 1 > 0; |
@@ -645,4 +648,16 @@ fn foo() { | |||
645 | }", | 648 | }", |
646 | ); | 649 | ); |
647 | } | 650 | } |
651 | |||
652 | #[test] | ||
653 | fn test_not_applicable_if_variable_unused() { | ||
654 | check_assist_not_applicable( | ||
655 | inline_local_variable, | ||
656 | " | ||
657 | fn foo() { | ||
658 | let <|>a = 0; | ||
659 | } | ||
660 | ", | ||
661 | ) | ||
662 | } | ||
648 | } | 663 | } |
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index 5dc1ee233..65d51428b 100644 --- a/crates/ra_assists/src/doc_tests.rs +++ b/crates/ra_assists/src/doc_tests.rs | |||
@@ -11,6 +11,10 @@ use test_utils::{assert_eq_text, extract_range_or_offset}; | |||
11 | use crate::test_db::TestDB; | 11 | use crate::test_db::TestDB; |
12 | 12 | ||
13 | fn check(assist_id: &str, before: &str, after: &str) { | 13 | fn check(assist_id: &str, before: &str, after: &str) { |
14 | // FIXME we cannot get the imports search functionality here yet, but still need to generate a test and a doc for an assist | ||
15 | if assist_id == "auto_import" { | ||
16 | return; | ||
17 | } | ||
14 | let (selection, before) = extract_range_or_offset(before); | 18 | let (selection, before) = extract_range_or_offset(before); |
15 | let (db, file_id) = TestDB::with_single_file(&before); | 19 | let (db, file_id) = TestDB::with_single_file(&before); |
16 | let frange = FileRange { file_id, range: selection.into() }; | 20 | let frange = FileRange { file_id, range: selection.into() }; |
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 7d84dc8fb..ec4587ce7 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -215,6 +215,25 @@ fn main() { | |||
215 | } | 215 | } |
216 | 216 | ||
217 | #[test] | 217 | #[test] |
218 | fn doctest_auto_import() { | ||
219 | check( | ||
220 | "auto_import", | ||
221 | r#####" | ||
222 | fn main() { | ||
223 | let map = HashMap<|>::new(); | ||
224 | } | ||
225 | "#####, | ||
226 | r#####" | ||
227 | use std::collections::HashMap; | ||
228 | |||
229 | fn main() { | ||
230 | let map = HashMap<|>::new(); | ||
231 | } | ||
232 | "#####, | ||
233 | ) | ||
234 | } | ||
235 | |||
236 | #[test] | ||
218 | fn doctest_change_visibility() { | 237 | fn doctest_change_visibility() { |
219 | check( | 238 | check( |
220 | "change_visibility", | 239 | "change_visibility", |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index d45b58966..625ebc4a2 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -14,7 +14,7 @@ mod test_db; | |||
14 | pub mod ast_transform; | 14 | pub mod ast_transform; |
15 | 15 | ||
16 | use either::Either; | 16 | use either::Either; |
17 | use hir::db::HirDatabase; | 17 | use hir::{db::HirDatabase, ModuleDef}; |
18 | use ra_db::FileRange; | 18 | use ra_db::FileRange; |
19 | use ra_syntax::{TextRange, TextUnit}; | 19 | use ra_syntax::{TextRange, TextUnit}; |
20 | use ra_text_edit::TextEdit; | 20 | use ra_text_edit::TextEdit; |
@@ -77,6 +77,51 @@ where | |||
77 | }) | 77 | }) |
78 | } | 78 | } |
79 | 79 | ||
80 | /// A functionality for locating imports for the given name. | ||
81 | /// | ||
82 | /// Currently has to be a trait with the real implementation provided by the ra_ide_api crate, | ||
83 | /// due to the search functionality located there. | ||
84 | /// Later, this trait should be removed completely and the search functionality moved to a separate crate, | ||
85 | /// accessible from the ra_assists crate. | ||
86 | pub trait ImportsLocator { | ||
87 | /// Finds all imports for the given name and the module that contains this name. | ||
88 | fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef>; | ||
89 | } | ||
90 | |||
91 | /// Return all the assists applicable at the given position | ||
92 | /// and additional assists that need the imports locator functionality to work. | ||
93 | /// | ||
94 | /// Assists are returned in the "resolved" state, that is with edit fully | ||
95 | /// computed. | ||
96 | pub fn assists_with_imports_locator<H, F>( | ||
97 | db: &H, | ||
98 | range: FileRange, | ||
99 | mut imports_locator: F, | ||
100 | ) -> Vec<ResolvedAssist> | ||
101 | where | ||
102 | H: HirDatabase + 'static, | ||
103 | F: ImportsLocator, | ||
104 | { | ||
105 | AssistCtx::with_ctx(db, range, true, |ctx| { | ||
106 | let mut assists = assists::all() | ||
107 | .iter() | ||
108 | .map(|f| f(ctx.clone())) | ||
109 | .chain( | ||
110 | assists::all_with_imports_locator() | ||
111 | .iter() | ||
112 | .map(|f| f(ctx.clone(), &mut imports_locator)), | ||
113 | ) | ||
114 | .filter_map(std::convert::identity) | ||
115 | .map(|a| match a { | ||
116 | Assist::Resolved { assist } => assist, | ||
117 | Assist::Unresolved { .. } => unreachable!(), | ||
118 | }) | ||
119 | .collect(); | ||
120 | sort_assists(&mut assists); | ||
121 | assists | ||
122 | }) | ||
123 | } | ||
124 | |||
80 | /// Return all the assists applicable at the given position. | 125 | /// Return all the assists applicable at the given position. |
81 | /// | 126 | /// |
82 | /// Assists are returned in the "resolved" state, that is with edit fully | 127 | /// Assists are returned in the "resolved" state, that is with edit fully |
@@ -85,8 +130,6 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist> | |||
85 | where | 130 | where |
86 | H: HirDatabase + 'static, | 131 | H: HirDatabase + 'static, |
87 | { | 132 | { |
88 | use std::cmp::Ordering; | ||
89 | |||
90 | AssistCtx::with_ctx(db, range, true, |ctx| { | 133 | AssistCtx::with_ctx(db, range, true, |ctx| { |
91 | let mut a = assists::all() | 134 | let mut a = assists::all() |
92 | .iter() | 135 | .iter() |
@@ -95,19 +138,24 @@ where | |||
95 | Assist::Resolved { assist } => assist, | 138 | Assist::Resolved { assist } => assist, |
96 | Assist::Unresolved { .. } => unreachable!(), | 139 | Assist::Unresolved { .. } => unreachable!(), |
97 | }) | 140 | }) |
98 | .collect::<Vec<_>>(); | 141 | .collect(); |
99 | a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { | 142 | sort_assists(&mut a); |
100 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | ||
101 | (Some(_), None) => Ordering::Less, | ||
102 | (None, Some(_)) => Ordering::Greater, | ||
103 | (None, None) => Ordering::Equal, | ||
104 | }); | ||
105 | a | 143 | a |
106 | }) | 144 | }) |
107 | } | 145 | } |
108 | 146 | ||
147 | fn sort_assists(assists: &mut Vec<ResolvedAssist>) { | ||
148 | use std::cmp::Ordering; | ||
149 | assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { | ||
150 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | ||
151 | (Some(_), None) => Ordering::Less, | ||
152 | (None, Some(_)) => Ordering::Greater, | ||
153 | (None, None) => Ordering::Equal, | ||
154 | }); | ||
155 | } | ||
156 | |||
109 | mod assists { | 157 | mod assists { |
110 | use crate::{Assist, AssistCtx}; | 158 | use crate::{Assist, AssistCtx, ImportsLocator}; |
111 | use hir::db::HirDatabase; | 159 | use hir::db::HirDatabase; |
112 | 160 | ||
113 | mod add_derive; | 161 | mod add_derive; |
@@ -116,6 +164,7 @@ mod assists { | |||
116 | mod add_custom_impl; | 164 | mod add_custom_impl; |
117 | mod add_new; | 165 | mod add_new; |
118 | mod apply_demorgan; | 166 | mod apply_demorgan; |
167 | mod auto_import; | ||
119 | mod invert_if; | 168 | mod invert_if; |
120 | mod flip_comma; | 169 | mod flip_comma; |
121 | mod flip_binexpr; | 170 | mod flip_binexpr; |
@@ -157,7 +206,7 @@ mod assists { | |||
157 | add_import::add_import, | 206 | add_import::add_import, |
158 | add_missing_impl_members::add_missing_impl_members, | 207 | add_missing_impl_members::add_missing_impl_members, |
159 | add_missing_impl_members::add_missing_default_members, | 208 | add_missing_impl_members::add_missing_default_members, |
160 | inline_local_variable::inline_local_varialbe, | 209 | inline_local_variable::inline_local_variable, |
161 | move_guard::move_guard_to_arm_body, | 210 | move_guard::move_guard_to_arm_body, |
162 | move_guard::move_arm_cond_to_match_guard, | 211 | move_guard::move_arm_cond_to_match_guard, |
163 | move_bounds::move_bounds_to_where_clause, | 212 | move_bounds::move_bounds_to_where_clause, |
@@ -168,15 +217,69 @@ mod assists { | |||
168 | early_return::convert_to_guarded_return, | 217 | early_return::convert_to_guarded_return, |
169 | ] | 218 | ] |
170 | } | 219 | } |
220 | |||
221 | pub(crate) fn all_with_imports_locator<'a, DB: HirDatabase, F: ImportsLocator>( | ||
222 | ) -> &'a [fn(AssistCtx<DB>, &mut F) -> Option<Assist>] { | ||
223 | &[auto_import::auto_import] | ||
224 | } | ||
171 | } | 225 | } |
172 | 226 | ||
173 | #[cfg(test)] | 227 | #[cfg(test)] |
174 | mod helpers { | 228 | mod helpers { |
175 | use ra_db::{fixture::WithFixture, FileRange}; | 229 | use hir::db::DefDatabase; |
230 | use ra_db::{fixture::WithFixture, FileId, FileRange}; | ||
176 | use ra_syntax::TextRange; | 231 | use ra_syntax::TextRange; |
177 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; | 232 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; |
178 | 233 | ||
179 | use crate::{test_db::TestDB, Assist, AssistCtx}; | 234 | use crate::{test_db::TestDB, Assist, AssistCtx, ImportsLocator}; |
235 | use std::sync::Arc; | ||
236 | |||
237 | // FIXME remove the `ModuleDefId` reexport from `ra_hir` when this gets removed. | ||
238 | pub(crate) struct TestImportsLocator { | ||
239 | db: Arc<TestDB>, | ||
240 | test_file_id: FileId, | ||
241 | } | ||
242 | |||
243 | impl TestImportsLocator { | ||
244 | pub(crate) fn new(db: Arc<TestDB>, test_file_id: FileId) -> Self { | ||
245 | TestImportsLocator { db, test_file_id } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | impl ImportsLocator for TestImportsLocator { | ||
250 | fn find_imports(&mut self, name_to_import: &str) -> Vec<hir::ModuleDef> { | ||
251 | let crate_def_map = self.db.crate_def_map(self.db.test_crate()); | ||
252 | let mut findings = Vec::new(); | ||
253 | |||
254 | let mut module_ids_to_process = | ||
255 | crate_def_map.modules_for_file(self.test_file_id).collect::<Vec<_>>(); | ||
256 | |||
257 | while !module_ids_to_process.is_empty() { | ||
258 | let mut more_ids_to_process = Vec::new(); | ||
259 | for local_module_id in module_ids_to_process.drain(..) { | ||
260 | for (name, namespace_data) in | ||
261 | crate_def_map[local_module_id].scope.entries_without_primitives() | ||
262 | { | ||
263 | let found_a_match = &name.to_string() == name_to_import; | ||
264 | vec![namespace_data.types, namespace_data.values] | ||
265 | .into_iter() | ||
266 | .filter_map(std::convert::identity) | ||
267 | .for_each(|(module_def_id, _)| { | ||
268 | if found_a_match { | ||
269 | findings.push(module_def_id.into()); | ||
270 | } | ||
271 | if let hir::ModuleDefId::ModuleId(module_id) = module_def_id { | ||
272 | more_ids_to_process.push(module_id.local_id); | ||
273 | } | ||
274 | }); | ||
275 | } | ||
276 | } | ||
277 | module_ids_to_process = more_ids_to_process; | ||
278 | } | ||
279 | |||
280 | findings | ||
281 | } | ||
282 | } | ||
180 | 283 | ||
181 | pub(crate) fn check_assist( | 284 | pub(crate) fn check_assist( |
182 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 285 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, |
@@ -206,6 +309,38 @@ mod helpers { | |||
206 | assert_eq_text!(after, &actual); | 309 | assert_eq_text!(after, &actual); |
207 | } | 310 | } |
208 | 311 | ||
312 | pub(crate) fn check_assist_with_imports_locator<F: ImportsLocator>( | ||
313 | assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>, | ||
314 | imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F, | ||
315 | before: &str, | ||
316 | after: &str, | ||
317 | ) { | ||
318 | let (before_cursor_pos, before) = extract_offset(before); | ||
319 | let (db, file_id) = TestDB::with_single_file(&before); | ||
320 | let db = Arc::new(db); | ||
321 | let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id); | ||
322 | let frange = | ||
323 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
324 | let assist = | ||
325 | AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator)) | ||
326 | .expect("code action is not applicable"); | ||
327 | let action = match assist { | ||
328 | Assist::Unresolved { .. } => unreachable!(), | ||
329 | Assist::Resolved { assist } => assist.get_first_action(), | ||
330 | }; | ||
331 | |||
332 | let actual = action.edit.apply(&before); | ||
333 | let actual_cursor_pos = match action.cursor_position { | ||
334 | None => action | ||
335 | .edit | ||
336 | .apply_to_offset(before_cursor_pos) | ||
337 | .expect("cursor position is affected by the edit"), | ||
338 | Some(off) => off, | ||
339 | }; | ||
340 | let actual = add_cursor(&actual, actual_cursor_pos); | ||
341 | assert_eq_text!(after, &actual); | ||
342 | } | ||
343 | |||
209 | pub(crate) fn check_assist_range( | 344 | pub(crate) fn check_assist_range( |
210 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 345 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, |
211 | before: &str, | 346 | before: &str, |
@@ -279,6 +414,22 @@ mod helpers { | |||
279 | assert!(assist.is_none()); | 414 | assert!(assist.is_none()); |
280 | } | 415 | } |
281 | 416 | ||
417 | pub(crate) fn check_assist_with_imports_locator_not_applicable<F: ImportsLocator>( | ||
418 | assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>, | ||
419 | imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F, | ||
420 | before: &str, | ||
421 | ) { | ||
422 | let (before_cursor_pos, before) = extract_offset(before); | ||
423 | let (db, file_id) = TestDB::with_single_file(&before); | ||
424 | let db = Arc::new(db); | ||
425 | let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id); | ||
426 | let frange = | ||
427 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
428 | let assist = | ||
429 | AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator)); | ||
430 | assert!(assist.is_none()); | ||
431 | } | ||
432 | |||
282 | pub(crate) fn check_assist_range_not_applicable( | 433 | pub(crate) fn check_assist_range_not_applicable( |
283 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 434 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, |
284 | before: &str, | 435 | before: &str, |
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml index 9ead48abf..49e06e0d3 100644 --- a/crates/ra_cargo_watch/Cargo.toml +++ b/crates/ra_cargo_watch/Cargo.toml | |||
@@ -6,12 +6,12 @@ authors = ["rust-analyzer developers"] | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | crossbeam-channel = "0.4" | 8 | crossbeam-channel = "0.4" |
9 | lsp-types = { version = "0.69.0", features = ["proposed"] } | 9 | lsp-types = { version = "0.70.0", features = ["proposed"] } |
10 | log = "0.4.3" | 10 | log = "0.4.3" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | jod-thread = "0.1.0" | 12 | jod-thread = "0.1.0" |
13 | parking_lot = "0.10.0" | 13 | parking_lot = "0.10.0" |
14 | 14 | ||
15 | [dev-dependencies] | 15 | [dev-dependencies] |
16 | insta = "0.12.0" | 16 | insta = "0.13.0" |
17 | serde_json = "1.0" \ No newline at end of file | 17 | serde_json = "1.0" \ No newline at end of file |
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs index ac0f1d28a..8fba400ae 100644 --- a/crates/ra_cargo_watch/src/conv.rs +++ b/crates/ra_cargo_watch/src/conv.rs | |||
@@ -117,7 +117,7 @@ fn is_deprecated(rd: &RustDiagnostic) -> bool { | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | #[derive(Debug)] | 120 | #[derive(Clone, Debug)] |
121 | pub struct SuggestedFix { | 121 | pub struct SuggestedFix { |
122 | pub title: String, | 122 | pub title: String, |
123 | pub location: Location, | 123 | pub location: Location, |
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap index cb0920914..cb0920914 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap | |||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap index 19510ecc1..19510ecc1 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap | |||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap index 92f7eec05..92f7eec05 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap | |||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap index cf683e4b6..cf683e4b6 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap | |||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap index 8c1483c74..8c1483c74 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap | |||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap index eb5a2247b..eb5a2247b 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap | |||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap index 2f4518931..2f4518931 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap | |||
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index 7f4c9280c..bbe634603 100644 --- a/crates/ra_cargo_watch/src/lib.rs +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -7,7 +7,6 @@ use lsp_types::{ | |||
7 | Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, | 7 | Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, |
8 | WorkDoneProgressReport, | 8 | WorkDoneProgressReport, |
9 | }; | 9 | }; |
10 | use parking_lot::RwLock; | ||
11 | use std::{ | 10 | use std::{ |
12 | collections::HashMap, | 11 | collections::HashMap, |
13 | path::PathBuf, | 12 | path::PathBuf, |
@@ -38,7 +37,7 @@ pub struct CheckOptions { | |||
38 | #[derive(Debug)] | 37 | #[derive(Debug)] |
39 | pub struct CheckWatcher { | 38 | pub struct CheckWatcher { |
40 | pub task_recv: Receiver<CheckTask>, | 39 | pub task_recv: Receiver<CheckTask>, |
41 | pub state: Arc<RwLock<CheckState>>, | 40 | pub state: Arc<CheckState>, |
42 | cmd_send: Option<Sender<CheckCommand>>, | 41 | cmd_send: Option<Sender<CheckCommand>>, |
43 | handle: Option<JoinHandle<()>>, | 42 | handle: Option<JoinHandle<()>>, |
44 | } | 43 | } |
@@ -46,7 +45,7 @@ pub struct CheckWatcher { | |||
46 | impl CheckWatcher { | 45 | impl CheckWatcher { |
47 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { | 46 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { |
48 | let options = options.clone(); | 47 | let options = options.clone(); |
49 | let state = Arc::new(RwLock::new(CheckState::new())); | 48 | let state = Arc::new(CheckState::new()); |
50 | 49 | ||
51 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 50 | let (task_send, task_recv) = unbounded::<CheckTask>(); |
52 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 51 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); |
@@ -59,7 +58,7 @@ impl CheckWatcher { | |||
59 | 58 | ||
60 | /// Returns a CheckWatcher that doesn't actually do anything | 59 | /// Returns a CheckWatcher that doesn't actually do anything |
61 | pub fn dummy() -> CheckWatcher { | 60 | pub fn dummy() -> CheckWatcher { |
62 | let state = Arc::new(RwLock::new(CheckState::new())); | 61 | let state = Arc::new(CheckState::new()); |
63 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state } | 62 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state } |
64 | } | 63 | } |
65 | 64 | ||
@@ -87,7 +86,7 @@ impl std::ops::Drop for CheckWatcher { | |||
87 | } | 86 | } |
88 | } | 87 | } |
89 | 88 | ||
90 | #[derive(Debug)] | 89 | #[derive(Clone, Debug)] |
91 | pub struct CheckState { | 90 | pub struct CheckState { |
92 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, | 91 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, |
93 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, | 92 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, |
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 12af075f7..bcd408421 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -7,7 +7,7 @@ publish = false | |||
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | pico-args = "0.3.0" | 9 | pico-args = "0.3.0" |
10 | env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } | 10 | env_logger = { version = "0.7.1", default-features = false } |
11 | 11 | ||
12 | ra_syntax = { path = "../ra_syntax" } | 12 | ra_syntax = { path = "../ra_syntax" } |
13 | ra_ide = { path = "../ra_ide" } | 13 | ra_ide = { path = "../ra_ide" } |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index e1c7b7a20..9e2673d13 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -56,6 +56,7 @@ pub use hir_def::{ | |||
56 | nameres::ModuleSource, | 56 | nameres::ModuleSource, |
57 | path::{ModPath, Path, PathKind}, | 57 | path::{ModPath, Path, PathKind}, |
58 | type_ref::Mutability, | 58 | type_ref::Mutability, |
59 | ModuleDefId, // FIXME this is exposed and should be used for implementing the `TestImportsLocator` in `ra_assists` only, should be removed later along with the trait and the implementation. | ||
59 | }; | 60 | }; |
60 | pub use hir_expand::{ | 61 | pub use hir_expand::{ |
61 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, | 62 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 2c368f690..1efa00fe0 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -26,4 +26,4 @@ ra_cfg = { path = "../ra_cfg" } | |||
26 | tt = { path = "../ra_tt", package = "ra_tt" } | 26 | tt = { path = "../ra_tt", package = "ra_tt" } |
27 | 27 | ||
28 | [dev-dependencies] | 28 | [dev-dependencies] |
29 | insta = "0.12.0" | 29 | insta = "0.13.0" |
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 60793db44..d229639d9 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -28,4 +28,4 @@ chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "ff65b5a | |||
28 | lalrpop-intern = "0.15.1" | 28 | lalrpop-intern = "0.15.1" |
29 | 29 | ||
30 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | insta = "0.12.0" | 31 | insta = "0.13.0" |
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index 37def7c03..d1ff85f0f 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -9,7 +9,7 @@ pub struct HirFormatter<'a, 'b, DB> { | |||
9 | fmt: &'a mut fmt::Formatter<'b>, | 9 | fmt: &'a mut fmt::Formatter<'b>, |
10 | buf: String, | 10 | buf: String, |
11 | curr_size: usize, | 11 | curr_size: usize, |
12 | max_size: Option<usize>, | 12 | pub(crate) max_size: Option<usize>, |
13 | omit_verbose_types: bool, | 13 | omit_verbose_types: bool, |
14 | } | 14 | } |
15 | 15 | ||
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index d63f862dc..908e4862d 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -855,7 +855,12 @@ impl HirDisplay for ApplicationTy { | |||
855 | } | 855 | } |
856 | TypeCtor::Ref(m) => { | 856 | TypeCtor::Ref(m) => { |
857 | let t = self.parameters.as_single(); | 857 | let t = self.parameters.as_single(); |
858 | write!(f, "&{}{}", m.as_keyword_for_ref(), t.display(f.db))?; | 858 | let ty_display = if f.omit_verbose_types() { |
859 | t.display_truncated(f.db, f.max_size) | ||
860 | } else { | ||
861 | t.display(f.db) | ||
862 | }; | ||
863 | write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; | ||
859 | } | 864 | } |
860 | TypeCtor::Never => write!(f, "!")?, | 865 | TypeCtor::Never => write!(f, "!")?, |
861 | TypeCtor::Tuple { .. } => { | 866 | TypeCtor::Tuple { .. } => { |
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 2c9f9dce0..53817d1f7 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -39,7 +39,7 @@ ra_assists = { path = "../ra_assists" } | |||
39 | hir = { path = "../ra_hir", package = "ra_hir" } | 39 | hir = { path = "../ra_hir", package = "ra_hir" } |
40 | 40 | ||
41 | [dev-dependencies] | 41 | [dev-dependencies] |
42 | insta = "0.12.0" | 42 | insta = "0.13.0" |
43 | 43 | ||
44 | [dev-dependencies.proptest] | 44 | [dev-dependencies.proptest] |
45 | version = "0.9.0" | 45 | version = "0.9.0" |
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index a936900da..c43c45c65 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | 2 | ||
3 | use ra_db::{FilePosition, FileRange}; | 3 | use ra_db::{FilePosition, FileRange}; |
4 | 4 | ||
5 | use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; | 5 | use crate::{ |
6 | 6 | db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit, | |
7 | }; | ||
7 | use either::Either; | 8 | use either::Either; |
8 | pub use ra_assists::AssistId; | 9 | pub use ra_assists::AssistId; |
9 | use ra_assists::{AssistAction, AssistLabel}; | 10 | use ra_assists::{AssistAction, AssistLabel}; |
@@ -16,7 +17,7 @@ pub struct Assist { | |||
16 | } | 17 | } |
17 | 18 | ||
18 | pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { | 19 | pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { |
19 | ra_assists::assists(db, frange) | 20 | ra_assists::assists_with_imports_locator(db, frange, ImportsLocatorIde::new(db)) |
20 | .into_iter() | 21 | .into_iter() |
21 | .map(|assist| { | 22 | .map(|assist| { |
22 | let file_id = frange.file_id; | 23 | let file_id = frange.file_id; |
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index b0aa2c8e0..ce617840c 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs | |||
@@ -166,13 +166,15 @@ impl LibraryData { | |||
166 | const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); | 166 | const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); |
167 | 167 | ||
168 | impl RootDatabase { | 168 | impl RootDatabase { |
169 | pub(crate) fn request_cancellation(&mut self) { | ||
170 | let _p = profile("RootDatabase::request_cancellation"); | ||
171 | self.salsa_runtime_mut().synthetic_write(Durability::LOW); | ||
172 | } | ||
173 | |||
169 | pub(crate) fn apply_change(&mut self, change: AnalysisChange) { | 174 | pub(crate) fn apply_change(&mut self, change: AnalysisChange) { |
170 | let _p = profile("RootDatabase::apply_change"); | 175 | let _p = profile("RootDatabase::apply_change"); |
176 | self.request_cancellation(); | ||
171 | log::info!("apply_change {:?}", change); | 177 | log::info!("apply_change {:?}", change); |
172 | { | ||
173 | let _p = profile("RootDatabase::apply_change/cancellation"); | ||
174 | self.salsa_runtime_mut().synthetic_write(Durability::LOW); | ||
175 | } | ||
176 | if !change.new_roots.is_empty() { | 178 | if !change.new_roots.is_empty() { |
177 | let mut local_roots = Vec::clone(&self.local_roots()); | 179 | let mut local_roots = Vec::clone(&self.local_roots()); |
178 | for (root_id, is_local) in change.new_roots { | 180 | for (root_id, is_local) in change.new_roots { |
diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs new file mode 100644 index 000000000..48b014c7d --- /dev/null +++ b/crates/ra_ide/src/imports_locator.rs | |||
@@ -0,0 +1,76 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the ra_assists module. | ||
2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. | ||
3 | |||
4 | use crate::{ | ||
5 | db::RootDatabase, | ||
6 | references::{classify_name, NameDefinition, NameKind}, | ||
7 | symbol_index::{self, FileSymbol}, | ||
8 | Query, | ||
9 | }; | ||
10 | use hir::{db::HirDatabase, ModuleDef, SourceBinder}; | ||
11 | use ra_assists::ImportsLocator; | ||
12 | use ra_prof::profile; | ||
13 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; | ||
14 | |||
15 | pub(crate) struct ImportsLocatorIde<'a> { | ||
16 | source_binder: SourceBinder<'a, RootDatabase>, | ||
17 | } | ||
18 | |||
19 | impl<'a> ImportsLocatorIde<'a> { | ||
20 | pub(crate) fn new(db: &'a RootDatabase) -> Self { | ||
21 | Self { source_binder: SourceBinder::new(db) } | ||
22 | } | ||
23 | |||
24 | fn get_name_definition( | ||
25 | &mut self, | ||
26 | db: &impl HirDatabase, | ||
27 | import_candidate: &FileSymbol, | ||
28 | ) -> Option<NameDefinition> { | ||
29 | let _p = profile("get_name_definition"); | ||
30 | let file_id = import_candidate.file_id.into(); | ||
31 | let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); | ||
32 | let candidate_name_node = if candidate_node.kind() != NAME { | ||
33 | candidate_node.children().find(|it| it.kind() == NAME)? | ||
34 | } else { | ||
35 | candidate_node | ||
36 | }; | ||
37 | classify_name( | ||
38 | &mut self.source_binder, | ||
39 | hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, | ||
40 | ) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | impl ImportsLocator for ImportsLocatorIde<'_> { | ||
45 | fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { | ||
46 | let _p = profile("search_for_imports"); | ||
47 | let db = self.source_binder.db; | ||
48 | |||
49 | let project_results = { | ||
50 | let mut query = Query::new(name_to_import.to_string()); | ||
51 | query.exact(); | ||
52 | query.limit(40); | ||
53 | symbol_index::world_symbols(db, query) | ||
54 | }; | ||
55 | let lib_results = { | ||
56 | let mut query = Query::new(name_to_import.to_string()); | ||
57 | query.libs(); | ||
58 | query.exact(); | ||
59 | query.limit(40); | ||
60 | symbol_index::world_symbols(db, query) | ||
61 | }; | ||
62 | |||
63 | project_results | ||
64 | .into_iter() | ||
65 | .chain(lib_results.into_iter()) | ||
66 | .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) | ||
67 | .filter_map(|name_definition_to_import| { | ||
68 | if let NameKind::Def(module_def) = name_definition_to_import.kind { | ||
69 | Some(module_def) | ||
70 | } else { | ||
71 | None | ||
72 | } | ||
73 | }) | ||
74 | .collect() | ||
75 | } | ||
76 | } | ||
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 236557541..393ca9447 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -245,6 +245,7 @@ struct Test<K, T = u8> { | |||
245 | 245 | ||
246 | fn main() { | 246 | fn main() { |
247 | let zz = Test { t: 23, k: 33 }; | 247 | let zz = Test { t: 23, k: 33 }; |
248 | let zz_ref = &zz; | ||
248 | }"#, | 249 | }"#, |
249 | ); | 250 | ); |
250 | 251 | ||
@@ -255,6 +256,11 @@ fn main() { | |||
255 | kind: TypeHint, | 256 | kind: TypeHint, |
256 | label: "Test<i32>", | 257 | label: "Test<i32>", |
257 | }, | 258 | }, |
259 | InlayHint { | ||
260 | range: [105; 111), | ||
261 | kind: TypeHint, | ||
262 | label: "&Test<i32>", | ||
263 | }, | ||
258 | ] | 264 | ] |
259 | "### | 265 | "### |
260 | ); | 266 | ); |
@@ -374,6 +380,7 @@ fn main() { | |||
374 | 380 | ||
375 | let multiply = |a, b, c, d| a * b * c * d; | 381 | let multiply = |a, b, c, d| a * b * c * d; |
376 | let _: i32 = multiply(1, 2, 3, 4); | 382 | let _: i32 = multiply(1, 2, 3, 4); |
383 | let multiply_ref = &multiply; | ||
377 | 384 | ||
378 | let return_42 = || 42; | 385 | let return_42 = || 42; |
379 | }"#, | 386 | }"#, |
@@ -417,7 +424,12 @@ fn main() { | |||
417 | label: "i32", | 424 | label: "i32", |
418 | }, | 425 | }, |
419 | InlayHint { | 426 | InlayHint { |
420 | range: [201; 210), | 427 | range: [200; 212), |
428 | kind: TypeHint, | ||
429 | label: "&|…| -> i32", | ||
430 | }, | ||
431 | InlayHint { | ||
432 | range: [235; 244), | ||
421 | kind: TypeHint, | 433 | kind: TypeHint, |
422 | label: "|| -> i32", | 434 | label: "|| -> i32", |
423 | }, | 435 | }, |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 4d8deb21c..03ad6b2c1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -30,6 +30,7 @@ mod syntax_highlighting; | |||
30 | mod parent_module; | 30 | mod parent_module; |
31 | mod references; | 31 | mod references; |
32 | mod impls; | 32 | mod impls; |
33 | mod imports_locator; | ||
33 | mod assists; | 34 | mod assists; |
34 | mod diagnostics; | 35 | mod diagnostics; |
35 | mod syntax_tree; | 36 | mod syntax_tree; |
@@ -202,6 +203,9 @@ impl AnalysisHost { | |||
202 | pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { | 203 | pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { |
203 | self.db.per_query_memory_usage() | 204 | self.db.per_query_memory_usage() |
204 | } | 205 | } |
206 | pub fn request_cancellation(&mut self) { | ||
207 | self.db.request_cancellation(); | ||
208 | } | ||
205 | pub fn raw_database( | 209 | pub fn raw_database( |
206 | &self, | 210 | &self, |
207 | ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { | 211 | ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 7533692f6..8622dd956 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -43,7 +43,7 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | |||
43 | let name = fn_def.name()?.text().clone(); | 43 | let name = fn_def.name()?.text().clone(); |
44 | let kind = if name == "main" { | 44 | let kind = if name == "main" { |
45 | RunnableKind::Bin | 45 | RunnableKind::Bin |
46 | } else if fn_def.has_atom_attr("test") { | 46 | } else if has_test_related_attribute(&fn_def) { |
47 | RunnableKind::Test { name: name.to_string() } | 47 | RunnableKind::Test { name: name.to_string() } |
48 | } else if fn_def.has_atom_attr("bench") { | 48 | } else if fn_def.has_atom_attr("bench") { |
49 | RunnableKind::Bench { name: name.to_string() } | 49 | RunnableKind::Bench { name: name.to_string() } |
@@ -53,6 +53,20 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | |||
53 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 53 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) |
54 | } | 54 | } |
55 | 55 | ||
56 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
57 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
58 | /// Also a regular `#[test]` annotation is supported. | ||
59 | /// | ||
60 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
61 | /// but it's better than not to have the runnables for the tests at all. | ||
62 | fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | ||
63 | fn_def | ||
64 | .attrs() | ||
65 | .filter_map(|attr| attr.path()) | ||
66 | .map(|path| path.syntax().to_string().to_lowercase()) | ||
67 | .any(|attribute_text| attribute_text.contains("test")) | ||
68 | } | ||
69 | |||
56 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { | 70 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { |
57 | let has_test_function = module | 71 | let has_test_function = module |
58 | .item_list()? | 72 | .item_list()? |
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 579158780..fdf81ed87 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -14,7 +14,7 @@ serde_json = "1.0.34" | |||
14 | serde = { version = "1.0.83", features = ["derive"] } | 14 | serde = { version = "1.0.83", features = ["derive"] } |
15 | crossbeam-channel = "0.4" | 15 | crossbeam-channel = "0.4" |
16 | log = "0.4.3" | 16 | log = "0.4.3" |
17 | lsp-types = { version = "0.69.0", features = ["proposed"] } | 17 | lsp-types = { version = "0.70.0", features = ["proposed"] } |
18 | rustc-hash = "1.0" | 18 | rustc-hash = "1.0" |
19 | parking_lot = "0.10.0" | 19 | parking_lot = "0.10.0" |
20 | jod-thread = "0.1.0" | 20 | jod-thread = "0.1.0" |
@@ -26,10 +26,13 @@ lsp-server = "0.3.0" | |||
26 | ra_project_model = { path = "../ra_project_model" } | 26 | ra_project_model = { path = "../ra_project_model" } |
27 | ra_prof = { path = "../ra_prof" } | 27 | ra_prof = { path = "../ra_prof" } |
28 | ra_vfs_glob = { path = "../ra_vfs_glob" } | 28 | ra_vfs_glob = { path = "../ra_vfs_glob" } |
29 | env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } | 29 | env_logger = { version = "0.7.1", default-features = false } |
30 | ra_cargo_watch = { path = "../ra_cargo_watch" } | 30 | ra_cargo_watch = { path = "../ra_cargo_watch" } |
31 | either = "1.5" | 31 | either = "1.5" |
32 | 32 | ||
33 | [target.'cfg(windows)'.dependencies] | ||
34 | winapi = "0.3" | ||
35 | |||
33 | [dev-dependencies] | 36 | [dev-dependencies] |
34 | tempfile = "3" | 37 | tempfile = "3" |
35 | test_utils = { path = "../test_utils" } | 38 | test_utils = { path = "../test_utils" } |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 7822be2e2..15bf519c9 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -29,9 +29,6 @@ use crate::{ | |||
29 | Result, ServerConfig, | 29 | Result, ServerConfig, |
30 | }; | 30 | }; |
31 | 31 | ||
32 | const THREADPOOL_SIZE: usize = 8; | ||
33 | const MAX_IN_FLIGHT_LIBS: usize = THREADPOOL_SIZE - 3; | ||
34 | |||
35 | #[derive(Debug)] | 32 | #[derive(Debug)] |
36 | pub struct LspError { | 33 | pub struct LspError { |
37 | pub code: i32, | 34 | pub code: i32, |
@@ -60,6 +57,25 @@ pub fn main_loop( | |||
60 | ) -> Result<()> { | 57 | ) -> Result<()> { |
61 | log::info!("server_config: {:#?}", config); | 58 | log::info!("server_config: {:#?}", config); |
62 | 59 | ||
60 | // Windows scheduler implements priority boosts: if thread waits for an | ||
61 | // event (like a condvar), and event fires, priority of the thread is | ||
62 | // temporary bumped. This optimization backfires in our case: each time the | ||
63 | // `main_loop` schedules a task to run on a threadpool, the worker threads | ||
64 | // gets a higher priority, and (on a machine with fewer cores) displaces the | ||
65 | // main loop! We work-around this by marking the main loop as a | ||
66 | // higher-priority thread. | ||
67 | // | ||
68 | // https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities | ||
69 | // https://docs.microsoft.com/en-us/windows/win32/procthread/priority-boosts | ||
70 | // https://github.com/rust-analyzer/rust-analyzer/issues/2835 | ||
71 | #[cfg(windows)] | ||
72 | unsafe { | ||
73 | use winapi::um::processthreadsapi::*; | ||
74 | let thread = GetCurrentThread(); | ||
75 | let thread_priority_above_normal = 1; | ||
76 | SetThreadPriority(thread, thread_priority_above_normal); | ||
77 | } | ||
78 | |||
63 | let mut loop_state = LoopState::default(); | 79 | let mut loop_state = LoopState::default(); |
64 | let mut world_state = { | 80 | let mut world_state = { |
65 | let feature_flags = { | 81 | let feature_flags = { |
@@ -168,7 +184,7 @@ pub fn main_loop( | |||
168 | ) | 184 | ) |
169 | }; | 185 | }; |
170 | 186 | ||
171 | let pool = ThreadPool::new(THREADPOOL_SIZE); | 187 | let pool = ThreadPool::default(); |
172 | let (task_sender, task_receiver) = unbounded::<Task>(); | 188 | let (task_sender, task_receiver) = unbounded::<Task>(); |
173 | let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>(); | 189 | let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>(); |
174 | 190 | ||
@@ -210,7 +226,7 @@ pub fn main_loop( | |||
210 | )?; | 226 | )?; |
211 | } | 227 | } |
212 | } | 228 | } |
213 | 229 | world_state.analysis_host.request_cancellation(); | |
214 | log::info!("waiting for tasks to finish..."); | 230 | log::info!("waiting for tasks to finish..."); |
215 | task_receiver.into_iter().for_each(|task| { | 231 | task_receiver.into_iter().for_each(|task| { |
216 | on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) | 232 | on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) |
@@ -371,7 +387,8 @@ fn loop_turn( | |||
371 | loop_state.pending_libraries.extend(changes); | 387 | loop_state.pending_libraries.extend(changes); |
372 | } | 388 | } |
373 | 389 | ||
374 | while loop_state.in_flight_libraries < MAX_IN_FLIGHT_LIBS | 390 | let max_in_flight_libs = pool.max_count().saturating_sub(2).max(1); |
391 | while loop_state.in_flight_libraries < max_in_flight_libs | ||
375 | && !loop_state.pending_libraries.is_empty() | 392 | && !loop_state.pending_libraries.is_empty() |
376 | { | 393 | { |
377 | let (root, files) = loop_state.pending_libraries.pop().unwrap(); | 394 | let (root, files) = loop_state.pending_libraries.pop().unwrap(); |
@@ -586,12 +603,14 @@ fn on_notification( | |||
586 | 603 | ||
587 | fn on_check_task( | 604 | fn on_check_task( |
588 | task: CheckTask, | 605 | task: CheckTask, |
589 | world_state: &WorldState, | 606 | world_state: &mut WorldState, |
590 | task_sender: &Sender<Task>, | 607 | task_sender: &Sender<Task>, |
591 | ) -> Result<()> { | 608 | ) -> Result<()> { |
592 | match task { | 609 | match task { |
593 | CheckTask::ClearDiagnostics => { | 610 | CheckTask::ClearDiagnostics => { |
594 | let cleared_files = world_state.check_watcher.state.write().clear(); | 611 | let state = Arc::get_mut(&mut world_state.check_watcher.state) |
612 | .expect("couldn't get check watcher state as mutable"); | ||
613 | let cleared_files = state.clear(); | ||
595 | 614 | ||
596 | // Send updated diagnostics for each cleared file | 615 | // Send updated diagnostics for each cleared file |
597 | for url in cleared_files { | 616 | for url in cleared_files { |
@@ -600,11 +619,9 @@ fn on_check_task( | |||
600 | } | 619 | } |
601 | 620 | ||
602 | CheckTask::AddDiagnostic(url, diagnostic) => { | 621 | CheckTask::AddDiagnostic(url, diagnostic) => { |
603 | world_state | 622 | let state = Arc::get_mut(&mut world_state.check_watcher.state) |
604 | .check_watcher | 623 | .expect("couldn't get check watcher state as mutable"); |
605 | .state | 624 | state.add_diagnostic_with_fixes(url.clone(), diagnostic); |
606 | .write() | ||
607 | .add_diagnostic_with_fixes(url.clone(), diagnostic); | ||
608 | 625 | ||
609 | // We manually send a diagnostic update when the watcher asks | 626 | // We manually send a diagnostic update when the watcher asks |
610 | // us to, to avoid the issue of having to change the file to | 627 | // us to, to avoid the issue of having to change the file to |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 8e43f0575..666f2ee29 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -674,8 +674,7 @@ pub fn handle_code_action( | |||
674 | res.push(action.into()); | 674 | res.push(action.into()); |
675 | } | 675 | } |
676 | 676 | ||
677 | for fix in world.check_watcher.read().fixes_for(¶ms.text_document.uri).into_iter().flatten() | 677 | for fix in world.check_watcher.fixes_for(¶ms.text_document.uri).into_iter().flatten() { |
678 | { | ||
679 | let fix_range = fix.location.range.conv_with(&line_index); | 678 | let fix_range = fix.location.range.conv_with(&line_index); |
680 | if fix_range.intersection(&range).is_none() { | 679 | if fix_range.intersection(&range).is_none() { |
681 | continue; | 680 | continue; |
@@ -895,7 +894,7 @@ pub fn publish_diagnostics( | |||
895 | tags: None, | 894 | tags: None, |
896 | }) | 895 | }) |
897 | .collect(); | 896 | .collect(); |
898 | if let Some(check_diags) = world.check_watcher.read().diagnostics_for(&uri) { | 897 | if let Some(check_diags) = world.check_watcher.diagnostics_for(&uri) { |
899 | diagnostics.extend(check_diags.iter().cloned()); | 898 | diagnostics.extend(check_diags.iter().cloned()); |
900 | } | 899 | } |
901 | Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) | 900 | Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index e7a0acfc7..3059ef9ec 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -63,7 +63,7 @@ pub struct WorldSnapshot { | |||
63 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 63 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
64 | pub analysis: Analysis, | 64 | pub analysis: Analysis, |
65 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 65 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
66 | pub check_watcher: Arc<RwLock<CheckState>>, | 66 | pub check_watcher: CheckState, |
67 | vfs: Arc<RwLock<Vfs>>, | 67 | vfs: Arc<RwLock<Vfs>>, |
68 | } | 68 | } |
69 | 69 | ||
@@ -220,7 +220,7 @@ impl WorldState { | |||
220 | analysis: self.analysis_host.analysis(), | 220 | analysis: self.analysis_host.analysis(), |
221 | vfs: Arc::clone(&self.vfs), | 221 | vfs: Arc::clone(&self.vfs), |
222 | latest_requests: Arc::clone(&self.latest_requests), | 222 | latest_requests: Arc::clone(&self.latest_requests), |
223 | check_watcher: self.check_watcher.state.clone(), | 223 | check_watcher: (*self.check_watcher.state).clone(), |
224 | } | 224 | } |
225 | } | 225 | } |
226 | 226 | ||
diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index 65134277e..81055746b 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs | |||
@@ -27,7 +27,7 @@ pub use syntax_kind::SyntaxKind; | |||
27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
28 | pub struct ParseError(pub String); | 28 | pub struct ParseError(pub String); |
29 | 29 | ||
30 | /// `TokenSource` abstracts the source of the tokens parser operates one. | 30 | /// `TokenSource` abstracts the source of the tokens parser operates on. |
31 | /// | 31 | /// |
32 | /// Hopefully this will allow us to treat text and token trees in the same way! | 32 | /// Hopefully this will allow us to treat text and token trees in the same way! |
33 | pub trait TokenSource { | 33 | pub trait TokenSource { |
@@ -43,7 +43,7 @@ pub trait TokenSource { | |||
43 | fn is_keyword(&self, kw: &str) -> bool; | 43 | fn is_keyword(&self, kw: &str) -> bool; |
44 | } | 44 | } |
45 | 45 | ||
46 | /// `TokenCursor` abstracts the cursor of `TokenSource` operates one. | 46 | /// `Token` abstracts the cursor of `TokenSource` operates on. |
47 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 47 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
48 | pub struct Token { | 48 | pub struct Token { |
49 | /// What is the current token? | 49 | /// What is the current token? |
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index da541005a..c7973ddf4 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs | |||
@@ -106,6 +106,21 @@ pub fn profile(desc: &str) -> Profiler { | |||
106 | }) | 106 | }) |
107 | } | 107 | } |
108 | 108 | ||
109 | pub fn print_time(desc: &str) -> impl Drop + '_ { | ||
110 | struct Guard<'a> { | ||
111 | desc: &'a str, | ||
112 | start: Instant, | ||
113 | } | ||
114 | |||
115 | impl Drop for Guard<'_> { | ||
116 | fn drop(&mut self) { | ||
117 | eprintln!("{}: {:?}", self.desc, self.start.elapsed()) | ||
118 | } | ||
119 | } | ||
120 | |||
121 | Guard { desc, start: Instant::now() } | ||
122 | } | ||
123 | |||
109 | pub struct Profiler { | 124 | pub struct Profiler { |
110 | desc: Option<String>, | 125 | desc: Option<String>, |
111 | } | 126 | } |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 6da4b1309..539759450 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -305,44 +305,39 @@ impl ast::Literal { | |||
305 | .unwrap() | 305 | .unwrap() |
306 | } | 306 | } |
307 | 307 | ||
308 | fn find_suffix(text: &str, possible_suffixes: &[&str]) -> Option<SmolStr> { | ||
309 | possible_suffixes | ||
310 | .iter() | ||
311 | .find(|&suffix| text.ends_with(suffix)) | ||
312 | .map(|&suffix| SmolStr::new(suffix)) | ||
313 | } | ||
314 | |||
308 | pub fn kind(&self) -> LiteralKind { | 315 | pub fn kind(&self) -> LiteralKind { |
309 | match self.token().kind() { | 316 | const INT_SUFFIXES: [&'static str; 12] = [ |
310 | INT_NUMBER => { | 317 | "u64", "u32", "u16", "u8", "usize", "isize", "i64", "i32", "i16", "i8", "u128", "i128", |
311 | let int_suffix_list = [ | 318 | ]; |
312 | "isize", "i128", "i64", "i32", "i16", "i8", "usize", "u128", "u64", "u32", | 319 | const FLOAT_SUFFIXES: [&'static str; 2] = ["f32", "f64"]; |
313 | "u16", "u8", | ||
314 | ]; | ||
315 | 320 | ||
321 | let token = self.token(); | ||
322 | |||
323 | match token.kind() { | ||
324 | INT_NUMBER => { | ||
325 | // FYI: there was a bug here previously, thus an if statement bellow is necessary. | ||
316 | // The lexer treats e.g. `1f64` as an integer literal. See | 326 | // The lexer treats e.g. `1f64` as an integer literal. See |
317 | // https://github.com/rust-analyzer/rust-analyzer/issues/1592 | 327 | // https://github.com/rust-analyzer/rust-analyzer/issues/1592 |
318 | // and the comments on the linked PR. | 328 | // and the comments on the linked PR. |
319 | let float_suffix_list = ["f32", "f64"]; | ||
320 | |||
321 | let text = self.token().text().to_string(); | ||
322 | 329 | ||
323 | let float_suffix = float_suffix_list | 330 | let text = token.text(); |
324 | .iter() | ||
325 | .find(|&s| text.ends_with(s)) | ||
326 | .map(|&suf| SmolStr::new(suf)); | ||
327 | 331 | ||
328 | if float_suffix.is_some() { | 332 | if let suffix @ Some(_) = Self::find_suffix(&text, &FLOAT_SUFFIXES) { |
329 | LiteralKind::FloatNumber { suffix: float_suffix } | 333 | LiteralKind::FloatNumber { suffix } |
330 | } else { | 334 | } else { |
331 | let suffix = int_suffix_list | 335 | LiteralKind::IntNumber { suffix: Self::find_suffix(&text, &INT_SUFFIXES) } |
332 | .iter() | ||
333 | .find(|&s| text.ends_with(s)) | ||
334 | .map(|&suf| SmolStr::new(suf)); | ||
335 | LiteralKind::IntNumber { suffix } | ||
336 | } | 336 | } |
337 | } | 337 | } |
338 | FLOAT_NUMBER => { | 338 | FLOAT_NUMBER => { |
339 | let allowed_suffix_list = ["f64", "f32"]; | 339 | let text = token.text(); |
340 | let text = self.token().text().to_string(); | 340 | LiteralKind::FloatNumber { suffix: Self::find_suffix(&text, &FLOAT_SUFFIXES) } |
341 | let suffix = allowed_suffix_list | ||
342 | .iter() | ||
343 | .find(|&s| text.ends_with(s)) | ||
344 | .map(|&suf| SmolStr::new(suf)); | ||
345 | LiteralKind::FloatNumber { suffix } | ||
346 | } | 341 | } |
347 | STRING | RAW_STRING => LiteralKind::String, | 342 | STRING | RAW_STRING => LiteralKind::String, |
348 | T![true] | T![false] => LiteralKind::Bool, | 343 | T![true] | T![false] => LiteralKind::Bool, |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index d9666cdca..cb0aee422 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -234,6 +234,10 @@ impl ast::LetStmt { | |||
234 | Some(node) => node.kind() == T![;], | 234 | Some(node) => node.kind() == T![;], |
235 | } | 235 | } |
236 | } | 236 | } |
237 | |||
238 | pub fn eq_token(&self) -> Option<SyntaxToken> { | ||
239 | self.syntax().children_with_tokens().find(|t| t.kind() == EQ).and_then(|it| it.into_token()) | ||
240 | } | ||
237 | } | 241 | } |
238 | 242 | ||
239 | impl ast::ExprStmt { | 243 | impl ast::ExprStmt { |