aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/Cargo.toml2
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs49
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs319
-rw-r--r--crates/ra_assists/src/lib.rs29
6 files changed, 340 insertions, 65 deletions
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index d314dc8e6..a87f4052a 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -11,6 +11,8 @@ doctest = false
11format-buf = "1.0.0" 11format-buf = "1.0.0"
12join_to_string = "0.1.3" 12join_to_string = "0.1.3"
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.9.0"
15either = "1.5.3"
14 16
15ra_syntax = { path = "../ra_syntax" } 17ra_syntax = { path = "../ra_syntax" }
16ra_text_edit = { path = "../ra_text_edit" } 18ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index aef6793e8..62dcb3808 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -275,8 +275,8 @@ enum Action { Move { distance: u32 }, Stop }
275 275
276fn handle(action: Action) { 276fn handle(action: Action) {
277 match action { 277 match action {
278 Action::Move { distance } => (), 278 Action::Move { distance } => {}
279 Action::Stop => (), 279 Action::Stop => {}
280 } 280 }
281} 281}
282"#####, 282"#####,
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index e5920b6f6..722f207e2 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -151,7 +151,7 @@ fn add_missing_impl_members_inner(
151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), 151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
152 _ => it, 152 _ => it,
153 }) 153 })
154 .map(|it| edit::strip_attrs_and_docs(&it)); 154 .map(|it| edit::remove_attrs_and_docs(&it));
155 let new_impl_item_list = impl_item_list.append_items(items); 155 let new_impl_item_list = impl_item_list.append_items(items);
156 let cursor_position = { 156 let cursor_position = {
157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); 157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap();
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index bb280f633..99682e023 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -17,6 +17,7 @@ use crate::{
17 utils::insert_use_statement, 17 utils::insert_use_statement,
18 AssistId, 18 AssistId,
19}; 19};
20use either::Either;
20 21
21// Assist: auto_import 22// Assist: auto_import
22// 23//
@@ -58,6 +59,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
58 group.finish() 59 group.finish()
59} 60}
60 61
62#[derive(Debug)]
61struct AutoImportAssets { 63struct AutoImportAssets {
62 import_candidate: ImportCandidate, 64 import_candidate: ImportCandidate,
63 module_with_name_to_import: Module, 65 module_with_name_to_import: Module,
@@ -127,14 +129,14 @@ impl AutoImportAssets {
127 ImportsLocator::new(db) 129 ImportsLocator::new(db)
128 .find_imports(&self.get_search_query()) 130 .find_imports(&self.get_search_query())
129 .into_iter() 131 .into_iter()
130 .filter_map(|module_def| match &self.import_candidate { 132 .filter_map(|candidate| match &self.import_candidate {
131 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 133 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
132 let located_assoc_item = match module_def { 134 let located_assoc_item = match candidate {
133 ModuleDef::Function(located_function) => located_function 135 Either::Left(ModuleDef::Function(located_function)) => located_function
134 .as_assoc_item(db) 136 .as_assoc_item(db)
135 .map(|assoc| assoc.container(db)) 137 .map(|assoc| assoc.container(db))
136 .and_then(Self::assoc_to_trait), 138 .and_then(Self::assoc_to_trait),
137 ModuleDef::Const(located_const) => located_const 139 Either::Left(ModuleDef::Const(located_const)) => located_const
138 .as_assoc_item(db) 140 .as_assoc_item(db)
139 .map(|assoc| assoc.container(db)) 141 .map(|assoc| assoc.container(db))
140 .and_then(Self::assoc_to_trait), 142 .and_then(Self::assoc_to_trait),
@@ -153,10 +155,11 @@ impl AutoImportAssets {
153 |_, assoc| Self::assoc_to_trait(assoc.container(db)), 155 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
154 ) 156 )
155 .map(ModuleDef::from) 157 .map(ModuleDef::from)
158 .map(Either::Left)
156 } 159 }
157 ImportCandidate::TraitMethod(function_callee, _) => { 160 ImportCandidate::TraitMethod(function_callee, _) => {
158 let located_assoc_item = 161 let located_assoc_item =
159 if let ModuleDef::Function(located_function) = module_def { 162 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
160 located_function 163 located_function
161 .as_assoc_item(db) 164 .as_assoc_item(db)
162 .map(|assoc| assoc.container(db)) 165 .map(|assoc| assoc.container(db))
@@ -179,10 +182,18 @@ impl AutoImportAssets {
179 }, 182 },
180 ) 183 )
181 .map(ModuleDef::from) 184 .map(ModuleDef::from)
185 .map(Either::Left)
186 }
187 _ => Some(candidate),
188 })
189 .filter_map(|candidate| match candidate {
190 Either::Left(module_def) => {
191 self.module_with_name_to_import.find_use_path(db, module_def)
192 }
193 Either::Right(macro_def) => {
194 self.module_with_name_to_import.find_use_path(db, macro_def)
182 } 195 }
183 _ => Some(module_def),
184 }) 196 })
185 .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def))
186 .filter(|use_path| !use_path.segments.is_empty()) 197 .filter(|use_path| !use_path.segments.is_empty())
187 .take(20) 198 .take(20)
188 .collect::<BTreeSet<_>>() 199 .collect::<BTreeSet<_>>()
@@ -440,6 +451,30 @@ mod tests {
440 } 451 }
441 452
442 #[test] 453 #[test]
454 fn macro_import() {
455 check_assist(
456 auto_import,
457 r"
458 //- /lib.rs crate:crate_with_macro
459 #[macro_export]
460 macro_rules! foo {
461 () => ()
462 }
463
464 //- /main.rs crate:main deps:crate_with_macro
465 fn main() {
466 foo<|>
467 }",
468 r"use crate_with_macro::foo;
469
470fn main() {
471 foo<|>
472}
473",
474 );
475 }
476
477 #[test]
443 fn auto_import_target() { 478 fn auto_import_target() {
444 check_assist_target( 479 check_assist_target(
445 auto_import, 480 auto_import,
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index fbd6a3ec3..add82e5b1 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,7 +2,8 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools;
6use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
7 8
8use crate::{Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
@@ -29,8 +30,8 @@ use ast::{MatchArm, Pat};
29// 30//
30// fn handle(action: Action) { 31// fn handle(action: Action) {
31// match action { 32// match action {
32// Action::Move { distance } => (), 33// Action::Move { distance } => {}
33// Action::Stop => (), 34// Action::Stop => {}
34// } 35// }
35// } 36// }
36// ``` 37// ```
@@ -39,13 +40,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
39 let match_arm_list = match_expr.match_arm_list()?; 40 let match_arm_list = match_expr.match_arm_list()?;
40 41
41 let expr = match_expr.expr()?; 42 let expr = match_expr.expr()?;
42 let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
43 let module = ctx.sema.scope(expr.syntax()).module()?;
44
45 let variants = enum_def.variants(ctx.db);
46 if variants.is_empty() {
47 return None;
48 }
49 43
50 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect(); 44 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
51 if arms.len() == 1 { 45 if arms.len() == 1 {
@@ -54,13 +48,49 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
54 } 48 }
55 } 49 }
56 50
57 let db = ctx.db; 51 let module = ctx.sema.scope(expr.syntax()).module()?;
58 let missing_arms: Vec<MatchArm> = variants 52
59 .into_iter() 53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
60 .filter_map(|variant| build_pat(db, module, variant)) 54 let variants = enum_def.variants(ctx.db);
61 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 55
62 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 56 variants
63 .collect(); 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect()
62 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
63 // Partial fill not currently supported for tuple of enums.
64 if !arms.is_empty() {
65 return None;
66 }
67
68 // We do not currently support filling match arms for a tuple
69 // containing a single enum.
70 if enum_defs.len() < 2 {
71 return None;
72 }
73
74 // When calculating the match arms for a tuple of enums, we want
75 // to create a match arm for each possible combination of enum
76 // values. The `multi_cartesian_product` method transforms
77 // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
78 // where each tuple represents a proposed match arm.
79 enum_defs
80 .into_iter()
81 .map(|enum_def| enum_def.variants(ctx.db))
82 .multi_cartesian_product()
83 .map(|variants| {
84 let patterns =
85 variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant));
86 ast::Pat::from(make::tuple_pat(patterns))
87 })
88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
89 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
90 .collect()
91 } else {
92 return None;
93 };
64 94
65 if missing_arms.is_empty() { 95 if missing_arms.is_empty() {
66 return None; 96 return None;
@@ -104,8 +134,27 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
104 }) 134 })
105} 135}
106 136
137fn resolve_tuple_of_enum_def(
138 sema: &Semantics<RootDatabase>,
139 expr: &ast::Expr,
140) -> Option<Vec<hir::Enum>> {
141 sema.type_of_expr(&expr)?
142 .tuple_fields(sema.db)
143 .iter()
144 .map(|ty| {
145 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
146 Some(Adt::Enum(e)) => Some(e),
147 // For now we only handle expansion for a tuple of enums. Here
148 // we map non-enum items to None and rely on `collect` to
149 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
150 _ => None,
151 })
152 })
153 .collect()
154}
155
107fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
108 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?);
109 158
110 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
111 let pat: ast::Pat = match var.source(db).value.kind() { 160 let pat: ast::Pat = match var.source(db).value.kind() {
@@ -143,8 +192,23 @@ mod tests {
143 fn main() { 192 fn main() {
144 match A::As<|> { 193 match A::As<|> {
145 A::As, 194 A::As,
146 A::Bs{x,y:Some(_)} => (), 195 A::Bs{x,y:Some(_)} => {}
147 A::Cs(_, Some(_)) => (), 196 A::Cs(_, Some(_)) => {}
197 }
198 }
199 "#,
200 );
201 }
202
203 #[test]
204 fn tuple_of_non_enum() {
205 // for now this case is not handled, although it potentially could be
206 // in the future
207 check_assist_not_applicable(
208 fill_match_arms,
209 r#"
210 fn main() {
211 match (0, false)<|> {
148 } 212 }
149 } 213 }
150 "#, 214 "#,
@@ -163,8 +227,8 @@ mod tests {
163 } 227 }
164 fn main() { 228 fn main() {
165 match A::As<|> { 229 match A::As<|> {
166 A::Bs{x,y:Some(_)} => (), 230 A::Bs{x,y:Some(_)} => {}
167 A::Cs(_, Some(_)) => (), 231 A::Cs(_, Some(_)) => {}
168 } 232 }
169 } 233 }
170 "#, 234 "#,
@@ -176,9 +240,9 @@ mod tests {
176 } 240 }
177 fn main() { 241 fn main() {
178 match <|>A::As { 242 match <|>A::As {
179 A::Bs{x,y:Some(_)} => (), 243 A::Bs{x,y:Some(_)} => {}
180 A::Cs(_, Some(_)) => (), 244 A::Cs(_, Some(_)) => {}
181 A::As => (), 245 A::As => {}
182 } 246 }
183 } 247 }
184 "#, 248 "#,
@@ -197,7 +261,7 @@ mod tests {
197 } 261 }
198 fn main() { 262 fn main() {
199 match A::As<|> { 263 match A::As<|> {
200 A::Cs(_) | A::Bs => (), 264 A::Cs(_) | A::Bs => {}
201 } 265 }
202 } 266 }
203 "#, 267 "#,
@@ -209,8 +273,8 @@ mod tests {
209 } 273 }
210 fn main() { 274 fn main() {
211 match <|>A::As { 275 match <|>A::As {
212 A::Cs(_) | A::Bs => (), 276 A::Cs(_) | A::Bs => {}
213 A::As => (), 277 A::As => {}
214 } 278 }
215 } 279 }
216 "#, 280 "#,
@@ -235,8 +299,8 @@ mod tests {
235 } 299 }
236 fn main() { 300 fn main() {
237 match A::As<|> { 301 match A::As<|> {
238 A::Bs if 0 < 1 => (), 302 A::Bs if 0 < 1 => {}
239 A::Ds(_value) => (), 303 A::Ds(_value) => { let x = 1; }
240 A::Es(B::Xs) => (), 304 A::Es(B::Xs) => (),
241 } 305 }
242 } 306 }
@@ -255,11 +319,11 @@ mod tests {
255 } 319 }
256 fn main() { 320 fn main() {
257 match <|>A::As { 321 match <|>A::As {
258 A::Bs if 0 < 1 => (), 322 A::Bs if 0 < 1 => {}
259 A::Ds(_value) => (), 323 A::Ds(_value) => { let x = 1; }
260 A::Es(B::Xs) => (), 324 A::Es(B::Xs) => (),
261 A::As => (), 325 A::As => {}
262 A::Cs => (), 326 A::Cs => {}
263 } 327 }
264 } 328 }
265 "#, 329 "#,
@@ -296,11 +360,174 @@ mod tests {
296 fn main() { 360 fn main() {
297 let a = A::As; 361 let a = A::As;
298 match <|>a { 362 match <|>a {
299 A::As => (), 363 A::As => {}
300 A::Bs => (), 364 A::Bs => {}
301 A::Cs(_) => (), 365 A::Cs(_) => {}
302 A::Ds(_, _) => (), 366 A::Ds(_, _) => {}
303 A::Es { x, y } => (), 367 A::Es { x, y } => {}
368 }
369 }
370 "#,
371 );
372 }
373
374 #[test]
375 fn fill_match_arms_tuple_of_enum() {
376 check_assist(
377 fill_match_arms,
378 r#"
379 enum A {
380 One,
381 Two,
382 }
383 enum B {
384 One,
385 Two,
386 }
387
388 fn main() {
389 let a = A::One;
390 let b = B::One;
391 match (a<|>, b) {}
392 }
393 "#,
394 r#"
395 enum A {
396 One,
397 Two,
398 }
399 enum B {
400 One,
401 Two,
402 }
403
404 fn main() {
405 let a = A::One;
406 let b = B::One;
407 match <|>(a, b) {
408 (A::One, B::One) => {}
409 (A::One, B::Two) => {}
410 (A::Two, B::One) => {}
411 (A::Two, B::Two) => {}
412 }
413 }
414 "#,
415 );
416 }
417
418 #[test]
419 fn fill_match_arms_tuple_of_enum_ref() {
420 check_assist(
421 fill_match_arms,
422 r#"
423 enum A {
424 One,
425 Two,
426 }
427 enum B {
428 One,
429 Two,
430 }
431
432 fn main() {
433 let a = A::One;
434 let b = B::One;
435 match (&a<|>, &b) {}
436 }
437 "#,
438 r#"
439 enum A {
440 One,
441 Two,
442 }
443 enum B {
444 One,
445 Two,
446 }
447
448 fn main() {
449 let a = A::One;
450 let b = B::One;
451 match <|>(&a, &b) {
452 (A::One, B::One) => {}
453 (A::One, B::Two) => {}
454 (A::Two, B::One) => {}
455 (A::Two, B::Two) => {}
456 }
457 }
458 "#,
459 );
460 }
461
462 #[test]
463 fn fill_match_arms_tuple_of_enum_partial() {
464 check_assist_not_applicable(
465 fill_match_arms,
466 r#"
467 enum A {
468 One,
469 Two,
470 }
471 enum B {
472 One,
473 Two,
474 }
475
476 fn main() {
477 let a = A::One;
478 let b = B::One;
479 match (a<|>, b) {
480 (A::Two, B::One) => {}
481 }
482 }
483 "#,
484 );
485 }
486
487 #[test]
488 fn fill_match_arms_tuple_of_enum_not_applicable() {
489 check_assist_not_applicable(
490 fill_match_arms,
491 r#"
492 enum A {
493 One,
494 Two,
495 }
496 enum B {
497 One,
498 Two,
499 }
500
501 fn main() {
502 let a = A::One;
503 let b = B::One;
504 match (a<|>, b) {
505 (A::Two, B::One) => {}
506 (A::One, B::One) => {}
507 (A::One, B::Two) => {}
508 (A::Two, B::Two) => {}
509 }
510 }
511 "#,
512 );
513 }
514
515 #[test]
516 fn fill_match_arms_single_element_tuple_of_enum() {
517 // For now we don't hande the case of a single element tuple, but
518 // we could handle this in the future if `make::tuple_pat` allowed
519 // creating a tuple with a single pattern.
520 check_assist_not_applicable(
521 fill_match_arms,
522 r#"
523 enum A {
524 One,
525 Two,
526 }
527
528 fn main() {
529 let a = A::One;
530 match (a<|>, ) {
304 } 531 }
305 } 532 }
306 "#, 533 "#,
@@ -328,7 +555,7 @@ mod tests {
328 555
329 fn foo(a: &A) { 556 fn foo(a: &A) {
330 match <|>a { 557 match <|>a {
331 A::As => (), 558 A::As => {}
332 } 559 }
333 } 560 }
334 "#, 561 "#,
@@ -353,7 +580,7 @@ mod tests {
353 580
354 fn foo(a: &mut A) { 581 fn foo(a: &mut A) {
355 match <|>a { 582 match <|>a {
356 A::Es { x, y } => (), 583 A::Es { x, y } => {}
357 } 584 }
358 } 585 }
359 "#, 586 "#,
@@ -384,7 +611,7 @@ mod tests {
384 611
385 fn main() { 612 fn main() {
386 match E::X { 613 match E::X {
387 <|>_ => {}, 614 <|>_ => {}
388 } 615 }
389 } 616 }
390 "#, 617 "#,
@@ -393,8 +620,8 @@ mod tests {
393 620
394 fn main() { 621 fn main() {
395 match <|>E::X { 622 match <|>E::X {
396 E::X => (), 623 E::X => {}
397 E::Y => (), 624 E::Y => {}
398 } 625 }
399 } 626 }
400 "#, 627 "#,
@@ -421,8 +648,8 @@ mod tests {
421 648
422 fn main() { 649 fn main() {
423 match <|>X { 650 match <|>X {
424 X => (), 651 X => {}
425 foo::E::Y => (), 652 foo::E::Y => {}
426 } 653 }
427 } 654 }
428 "#, 655 "#,
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index b8704ea7d..bcc9b3f10 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -165,7 +165,6 @@ mod helpers {
165 165
166 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 166 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
167 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 167 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
168 use ra_syntax::TextRange;
169 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; 168 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
170 169
171 use crate::{AssistCtx, AssistHandler}; 170 use crate::{AssistCtx, AssistHandler};
@@ -175,8 +174,7 @@ mod helpers {
175 let (mut db, file_id) = RootDatabase::with_single_file(text); 174 let (mut db, file_id) = RootDatabase::with_single_file(text);
176 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`, 175 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`,
177 // but it looks like this might need specialization? :( 176 // but it looks like this might need specialization? :(
178 let local_roots = vec![db.file_source_root(file_id)]; 177 db.set_local_roots(Arc::new(vec![db.file_source_root(file_id)]));
179 db.set_local_roots(Arc::new(local_roots));
180 (db, file_id) 178 (db, file_id)
181 } 179 }
182 180
@@ -206,11 +204,24 @@ mod helpers {
206 } 204 }
207 205
208 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { 206 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) {
209 let (range_or_offset, before) = extract_range_or_offset(before); 207 let (text_without_caret, file_with_caret_id, range_or_offset, db) =
210 let range: TextRange = range_or_offset.into(); 208 if before.contains("//-") {
209 let (mut db, position) = RootDatabase::with_position(before);
210 db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
211 (
212 db.file_text(position.file_id).as_ref().to_owned(),
213 position.file_id,
214 RangeOrOffset::Offset(position.offset),
215 db,
216 )
217 } else {
218 let (range_or_offset, text_without_caret) = extract_range_or_offset(before);
219 let (db, file_id) = with_single_file(&text_without_caret);
220 (text_without_caret, file_id, range_or_offset, db)
221 };
222
223 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
211 224
212 let (db, file_id) = with_single_file(&before);
213 let frange = FileRange { file_id, range };
214 let sema = Semantics::new(&db); 225 let sema = Semantics::new(&db);
215 let assist_ctx = AssistCtx::new(&sema, frange, true); 226 let assist_ctx = AssistCtx::new(&sema, frange, true);
216 227
@@ -218,7 +229,7 @@ mod helpers {
218 (Some(assist), ExpectedResult::After(after)) => { 229 (Some(assist), ExpectedResult::After(after)) => {
219 let action = assist.0[0].action.clone().unwrap(); 230 let action = assist.0[0].action.clone().unwrap();
220 231
221 let mut actual = action.edit.apply(&before); 232 let mut actual = action.edit.apply(&text_without_caret);
222 match action.cursor_position { 233 match action.cursor_position {
223 None => { 234 None => {
224 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { 235 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
@@ -237,7 +248,7 @@ mod helpers {
237 (Some(assist), ExpectedResult::Target(target)) => { 248 (Some(assist), ExpectedResult::Target(target)) => {
238 let action = assist.0[0].action.clone().unwrap(); 249 let action = assist.0[0].action.clone().unwrap();
239 let range = action.target.expect("expected target on action"); 250 let range = action.target.expect("expected target on action");
240 assert_eq_text!(&before[range], target); 251 assert_eq_text!(&text_without_caret[range], target);
241 } 252 }
242 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), 253 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
243 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => { 254 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {