diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/utils/insert_use.rs | 179 |
1 files changed, 107 insertions, 72 deletions
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 8563b16ab..dbe2dfdcb 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -34,14 +34,15 @@ pub(crate) fn insert_use_statement( | |||
34 | insert_use(position.clone(), make::path_from_text(path_to_import), Some(MergeBehaviour::Full)); | 34 | insert_use(position.clone(), make::path_from_text(path_to_import), Some(MergeBehaviour::Full)); |
35 | } | 35 | } |
36 | 36 | ||
37 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | ||
37 | pub fn insert_use( | 38 | pub fn insert_use( |
38 | where_: SyntaxNode, | 39 | where_: SyntaxNode, |
39 | path: ast::Path, | 40 | path: ast::Path, |
40 | merge_behaviour: Option<MergeBehaviour>, | 41 | merge: Option<MergeBehaviour>, |
41 | ) -> SyntaxNode { | 42 | ) -> SyntaxNode { |
42 | let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); | 43 | let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); |
43 | // merge into existing imports if possible | 44 | // merge into existing imports if possible |
44 | if let Some(mb) = merge_behaviour { | 45 | if let Some(mb) = merge { |
45 | for existing_use in where_.children().filter_map(ast::Use::cast) { | 46 | for existing_use in where_.children().filter_map(ast::Use::cast) { |
46 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | 47 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { |
47 | let to_delete: SyntaxElement = existing_use.syntax().clone().into(); | 48 | let to_delete: SyntaxElement = existing_use.syntax().clone().into(); |
@@ -59,17 +60,24 @@ pub fn insert_use( | |||
59 | let to_insert: Vec<SyntaxElement> = { | 60 | let to_insert: Vec<SyntaxElement> = { |
60 | let mut buf = Vec::new(); | 61 | let mut buf = Vec::new(); |
61 | 62 | ||
62 | if add_blank == AddBlankLine::Before { | 63 | match add_blank { |
63 | buf.push(make::tokens::single_newline().into()); | 64 | AddBlankLine::Before => buf.push(make::tokens::single_newline().into()), |
65 | AddBlankLine::BeforeTwice => { | ||
66 | buf.push(make::tokens::single_newline().into()); | ||
67 | buf.push(make::tokens::single_newline().into()); | ||
68 | } | ||
69 | _ => (), | ||
64 | } | 70 | } |
65 | 71 | ||
66 | buf.push(use_item.syntax().clone().into()); | 72 | buf.push(use_item.syntax().clone().into()); |
67 | 73 | ||
68 | if add_blank == AddBlankLine::After { | 74 | match add_blank { |
69 | buf.push(make::tokens::single_newline().into()); | 75 | AddBlankLine::After => buf.push(make::tokens::single_newline().into()), |
70 | } else if add_blank == AddBlankLine::AfterTwice { | 76 | AddBlankLine::AfterTwice => { |
71 | buf.push(make::tokens::single_newline().into()); | 77 | buf.push(make::tokens::single_newline().into()); |
72 | buf.push(make::tokens::single_newline().into()); | 78 | buf.push(make::tokens::single_newline().into()); |
79 | } | ||
80 | _ => (), | ||
73 | } | 81 | } |
74 | 82 | ||
75 | buf | 83 | buf |
@@ -83,8 +91,8 @@ fn try_merge_imports( | |||
83 | new: &ast::Use, | 91 | new: &ast::Use, |
84 | merge_behaviour: MergeBehaviour, | 92 | merge_behaviour: MergeBehaviour, |
85 | ) -> Option<ast::Use> { | 93 | ) -> Option<ast::Use> { |
86 | // dont merge into re-exports | 94 | // don't merge into re-exports |
87 | if old.visibility().map(|vis| vis.pub_token()).is_some() { | 95 | if old.visibility().and_then(|vis| vis.pub_token()).is_some() { |
88 | return None; | 96 | return None; |
89 | } | 97 | } |
90 | let old_tree = old.use_tree()?; | 98 | let old_tree = old.use_tree()?; |
@@ -115,8 +123,8 @@ pub fn try_merge_trees( | |||
115 | let rhs_tl = rhs.use_tree_list()?; | 123 | let rhs_tl = rhs.use_tree_list()?; |
116 | 124 | ||
117 | // if we are only allowed to merge the last level check if the split off paths are only one level deep | 125 | // if we are only allowed to merge the last level check if the split off paths are only one level deep |
118 | if merge_behaviour == MergeBehaviour::Last && use_tree_list_is_nested(&lhs_tl) | 126 | if merge_behaviour == MergeBehaviour::Last |
119 | || use_tree_list_is_nested(&rhs_tl) | 127 | && (use_tree_list_is_nested(&lhs_tl) || use_tree_list_is_nested(&rhs_tl)) |
120 | { | 128 | { |
121 | return None; | 129 | return None; |
122 | } | 130 | } |
@@ -124,18 +132,15 @@ pub fn try_merge_trees( | |||
124 | let should_insert_comma = lhs_tl | 132 | let should_insert_comma = lhs_tl |
125 | .r_curly_token() | 133 | .r_curly_token() |
126 | .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev)) | 134 | .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev)) |
127 | .map(|it| it.kind() != T![,]) | 135 | .map(|it| it.kind()) |
128 | .unwrap_or(true); | 136 | != Some(T![,]); |
129 | let mut to_insert: Vec<SyntaxElement> = Vec::new(); | 137 | let mut to_insert: Vec<SyntaxElement> = Vec::new(); |
130 | if should_insert_comma { | 138 | if should_insert_comma { |
131 | to_insert.push(make::token(T![,]).into()); | 139 | to_insert.push(make::token(T![,]).into()); |
132 | to_insert.push(make::tokens::single_space().into()); | 140 | to_insert.push(make::tokens::single_space().into()); |
133 | } | 141 | } |
134 | to_insert.extend( | 142 | to_insert.extend( |
135 | rhs_tl | 143 | rhs_tl.syntax().children_with_tokens().filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])), |
136 | .syntax() | ||
137 | .children_with_tokens() | ||
138 | .filter(|it| it.kind() != T!['{'] && it.kind() != T!['}']), | ||
139 | ); | 144 | ); |
140 | let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into()); | 145 | let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into()); |
141 | let use_tree_list = lhs_tl.insert_children(pos, to_insert); | 146 | let use_tree_list = lhs_tl.insert_children(pos, to_insert); |
@@ -225,6 +230,7 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl | |||
225 | #[derive(PartialEq, Eq)] | 230 | #[derive(PartialEq, Eq)] |
226 | enum AddBlankLine { | 231 | enum AddBlankLine { |
227 | Before, | 232 | Before, |
233 | BeforeTwice, | ||
228 | After, | 234 | After, |
229 | AfterTwice, | 235 | AfterTwice, |
230 | } | 236 | } |
@@ -278,7 +284,9 @@ fn find_insert_position( | |||
278 | } | 284 | } |
279 | // there is no such group, so append after the last one | 285 | // there is no such group, so append after the last one |
280 | None => match last { | 286 | None => match last { |
281 | Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before), | 287 | Some(node) => { |
288 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) | ||
289 | } | ||
282 | // there are no imports in this file at all | 290 | // there are no imports in this file at all |
283 | None => (InsertPosition::First, AddBlankLine::AfterTwice), | 291 | None => (InsertPosition::First, AddBlankLine::AfterTwice), |
284 | }, | 292 | }, |
@@ -297,12 +305,14 @@ mod tests { | |||
297 | #[test] | 305 | #[test] |
298 | fn insert_start() { | 306 | fn insert_start() { |
299 | check_none( | 307 | check_none( |
300 | "std::bar::A", | 308 | "std::bar::AA", |
301 | r"use std::bar::B; | 309 | r" |
310 | use std::bar::B; | ||
302 | use std::bar::D; | 311 | use std::bar::D; |
303 | use std::bar::F; | 312 | use std::bar::F; |
304 | use std::bar::G;", | 313 | use std::bar::G;", |
305 | r"use std::bar::A; | 314 | r" |
315 | use std::bar::AA; | ||
306 | use std::bar::B; | 316 | use std::bar::B; |
307 | use std::bar::D; | 317 | use std::bar::D; |
308 | use std::bar::F; | 318 | use std::bar::F; |
@@ -313,14 +323,16 @@ use std::bar::G;", | |||
313 | #[test] | 323 | #[test] |
314 | fn insert_middle() { | 324 | fn insert_middle() { |
315 | check_none( | 325 | check_none( |
316 | "std::bar::E", | 326 | "std::bar::EE", |
317 | r"use std::bar::A; | 327 | r" |
328 | use std::bar::A; | ||
318 | use std::bar::D; | 329 | use std::bar::D; |
319 | use std::bar::F; | 330 | use std::bar::F; |
320 | use std::bar::G;", | 331 | use std::bar::G;", |
321 | r"use std::bar::A; | 332 | r" |
333 | use std::bar::A; | ||
322 | use std::bar::D; | 334 | use std::bar::D; |
323 | use std::bar::E; | 335 | use std::bar::EE; |
324 | use std::bar::F; | 336 | use std::bar::F; |
325 | use std::bar::G;", | 337 | use std::bar::G;", |
326 | ) | 338 | ) |
@@ -329,29 +341,33 @@ use std::bar::G;", | |||
329 | #[test] | 341 | #[test] |
330 | fn insert_end() { | 342 | fn insert_end() { |
331 | check_none( | 343 | check_none( |
332 | "std::bar::Z", | 344 | "std::bar::ZZ", |
333 | r"use std::bar::A; | 345 | r" |
346 | use std::bar::A; | ||
334 | use std::bar::D; | 347 | use std::bar::D; |
335 | use std::bar::F; | 348 | use std::bar::F; |
336 | use std::bar::G;", | 349 | use std::bar::G;", |
337 | r"use std::bar::A; | 350 | r" |
351 | use std::bar::A; | ||
338 | use std::bar::D; | 352 | use std::bar::D; |
339 | use std::bar::F; | 353 | use std::bar::F; |
340 | use std::bar::G; | 354 | use std::bar::G; |
341 | use std::bar::Z;", | 355 | use std::bar::ZZ;", |
342 | ) | 356 | ) |
343 | } | 357 | } |
344 | 358 | ||
345 | #[test] | 359 | #[test] |
346 | fn insert_middle_pnested() { | 360 | fn insert_middle_nested() { |
347 | check_none( | 361 | check_none( |
348 | "std::bar::E", | 362 | "std::bar::EE", |
349 | r"use std::bar::A; | 363 | r" |
364 | use std::bar::A; | ||
350 | use std::bar::{D, Z}; // example of weird imports due to user | 365 | use std::bar::{D, Z}; // example of weird imports due to user |
351 | use std::bar::F; | 366 | use std::bar::F; |
352 | use std::bar::G;", | 367 | use std::bar::G;", |
353 | r"use std::bar::A; | 368 | r" |
354 | use std::bar::E; | 369 | use std::bar::A; |
370 | use std::bar::EE; | ||
355 | use std::bar::{D, Z}; // example of weird imports due to user | 371 | use std::bar::{D, Z}; // example of weird imports due to user |
356 | use std::bar::F; | 372 | use std::bar::F; |
357 | use std::bar::G;", | 373 | use std::bar::G;", |
@@ -361,17 +377,19 @@ use std::bar::G;", | |||
361 | #[test] | 377 | #[test] |
362 | fn insert_middle_groups() { | 378 | fn insert_middle_groups() { |
363 | check_none( | 379 | check_none( |
364 | "foo::bar::G", | 380 | "foo::bar::GG", |
365 | r"use std::bar::A; | 381 | r" |
382 | use std::bar::A; | ||
366 | use std::bar::D; | 383 | use std::bar::D; |
367 | 384 | ||
368 | use foo::bar::F; | 385 | use foo::bar::F; |
369 | use foo::bar::H;", | 386 | use foo::bar::H;", |
370 | r"use std::bar::A; | 387 | r" |
388 | use std::bar::A; | ||
371 | use std::bar::D; | 389 | use std::bar::D; |
372 | 390 | ||
373 | use foo::bar::F; | 391 | use foo::bar::F; |
374 | use foo::bar::G; | 392 | use foo::bar::GG; |
375 | use foo::bar::H;", | 393 | use foo::bar::H;", |
376 | ) | 394 | ) |
377 | } | 395 | } |
@@ -379,17 +397,19 @@ use foo::bar::H;", | |||
379 | #[test] | 397 | #[test] |
380 | fn insert_first_matching_group() { | 398 | fn insert_first_matching_group() { |
381 | check_none( | 399 | check_none( |
382 | "foo::bar::G", | 400 | "foo::bar::GG", |
383 | r"use foo::bar::A; | 401 | r" |
402 | use foo::bar::A; | ||
384 | use foo::bar::D; | 403 | use foo::bar::D; |
385 | 404 | ||
386 | use std; | 405 | use std; |
387 | 406 | ||
388 | use foo::bar::F; | 407 | use foo::bar::F; |
389 | use foo::bar::H;", | 408 | use foo::bar::H;", |
390 | r"use foo::bar::A; | 409 | r" |
410 | use foo::bar::A; | ||
391 | use foo::bar::D; | 411 | use foo::bar::D; |
392 | use foo::bar::G; | 412 | use foo::bar::GG; |
393 | 413 | ||
394 | use std; | 414 | use std; |
395 | 415 | ||
@@ -399,12 +419,14 @@ use foo::bar::H;", | |||
399 | } | 419 | } |
400 | 420 | ||
401 | #[test] | 421 | #[test] |
402 | fn insert_missing_group() { | 422 | fn insert_missing_group_std() { |
403 | check_none( | 423 | check_none( |
404 | "std::fmt", | 424 | "std::fmt", |
405 | r"use foo::bar::A; | 425 | r" |
426 | use foo::bar::A; | ||
406 | use foo::bar::D;", | 427 | use foo::bar::D;", |
407 | r"use std::fmt; | 428 | r" |
429 | use std::fmt; | ||
408 | 430 | ||
409 | use foo::bar::A; | 431 | use foo::bar::A; |
410 | use foo::bar::D;", | 432 | use foo::bar::D;", |
@@ -412,6 +434,21 @@ use foo::bar::D;", | |||
412 | } | 434 | } |
413 | 435 | ||
414 | #[test] | 436 | #[test] |
437 | fn insert_missing_group_self() { | ||
438 | check_none( | ||
439 | "self::fmt", | ||
440 | r" | ||
441 | use foo::bar::A; | ||
442 | use foo::bar::D;", | ||
443 | r" | ||
444 | use foo::bar::A; | ||
445 | use foo::bar::D; | ||
446 | |||
447 | use self::fmt;", | ||
448 | ) | ||
449 | } | ||
450 | |||
451 | #[test] | ||
415 | fn insert_no_imports() { | 452 | fn insert_no_imports() { |
416 | check_full( | 453 | check_full( |
417 | "foo::bar", | 454 | "foo::bar", |
@@ -436,23 +473,12 @@ fn main() {}", | |||
436 | } | 473 | } |
437 | 474 | ||
438 | #[test] | 475 | #[test] |
439 | fn adds_std_group() { | 476 | fn merge_groups() { |
440 | check_full( | ||
441 | "std::fmt::Debug", | ||
442 | r"use stdx;", | ||
443 | r"use std::fmt::Debug; | ||
444 | |||
445 | use stdx;", | ||
446 | ) | ||
447 | } | ||
448 | |||
449 | #[test] | ||
450 | fn merges_groups() { | ||
451 | check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};") | 477 | check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};") |
452 | } | 478 | } |
453 | 479 | ||
454 | #[test] | 480 | #[test] |
455 | fn merges_groups_last() { | 481 | fn merge_groups_last() { |
456 | check_last( | 482 | check_last( |
457 | "std::io", | 483 | "std::io", |
458 | r"use std::fmt::{Result, Display};", | 484 | r"use std::fmt::{Result, Display};", |
@@ -462,7 +488,7 @@ use std::io;", | |||
462 | } | 488 | } |
463 | 489 | ||
464 | #[test] | 490 | #[test] |
465 | fn merges_groups_full() { | 491 | fn merge_groups_full() { |
466 | check_full( | 492 | check_full( |
467 | "std::io", | 493 | "std::io", |
468 | r"use std::fmt::{Result, Display};", | 494 | r"use std::fmt::{Result, Display};", |
@@ -471,7 +497,7 @@ use std::io;", | |||
471 | } | 497 | } |
472 | 498 | ||
473 | #[test] | 499 | #[test] |
474 | fn merges_groups_long_full() { | 500 | fn merge_groups_long_full() { |
475 | check_full( | 501 | check_full( |
476 | "std::foo::bar::Baz", | 502 | "std::foo::bar::Baz", |
477 | r"use std::foo::bar::Qux;", | 503 | r"use std::foo::bar::Qux;", |
@@ -480,7 +506,7 @@ use std::io;", | |||
480 | } | 506 | } |
481 | 507 | ||
482 | #[test] | 508 | #[test] |
483 | fn merges_groups_long_last() { | 509 | fn merge_groups_long_last() { |
484 | check_last( | 510 | check_last( |
485 | "std::foo::bar::Baz", | 511 | "std::foo::bar::Baz", |
486 | r"use std::foo::bar::Qux;", | 512 | r"use std::foo::bar::Qux;", |
@@ -489,7 +515,7 @@ use std::io;", | |||
489 | } | 515 | } |
490 | 516 | ||
491 | #[test] | 517 | #[test] |
492 | fn merges_groups_long_full_list() { | 518 | fn merge_groups_long_full_list() { |
493 | check_full( | 519 | check_full( |
494 | "std::foo::bar::Baz", | 520 | "std::foo::bar::Baz", |
495 | r"use std::foo::bar::{Qux, Quux};", | 521 | r"use std::foo::bar::{Qux, Quux};", |
@@ -498,7 +524,7 @@ use std::io;", | |||
498 | } | 524 | } |
499 | 525 | ||
500 | #[test] | 526 | #[test] |
501 | fn merges_groups_long_last_list() { | 527 | fn merge_groups_long_last_list() { |
502 | check_last( | 528 | check_last( |
503 | "std::foo::bar::Baz", | 529 | "std::foo::bar::Baz", |
504 | r"use std::foo::bar::{Qux, Quux};", | 530 | r"use std::foo::bar::{Qux, Quux};", |
@@ -507,7 +533,7 @@ use std::io;", | |||
507 | } | 533 | } |
508 | 534 | ||
509 | #[test] | 535 | #[test] |
510 | fn merges_groups_long_full_nested() { | 536 | fn merge_groups_long_full_nested() { |
511 | check_full( | 537 | check_full( |
512 | "std::foo::bar::Baz", | 538 | "std::foo::bar::Baz", |
513 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 539 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
@@ -516,7 +542,7 @@ use std::io;", | |||
516 | } | 542 | } |
517 | 543 | ||
518 | #[test] | 544 | #[test] |
519 | fn merges_groups_long_last_nested() { | 545 | fn merge_groups_long_last_nested() { |
520 | check_last( | 546 | check_last( |
521 | "std::foo::bar::Baz", | 547 | "std::foo::bar::Baz", |
522 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 548 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
@@ -526,7 +552,7 @@ use std::foo::bar::{quux::{Fez, Fizz}, Qux};", | |||
526 | } | 552 | } |
527 | 553 | ||
528 | #[test] | 554 | #[test] |
529 | fn skip_merges_groups_pub() { | 555 | fn merge_groups_skip_pub() { |
530 | check_full( | 556 | check_full( |
531 | "std::io", | 557 | "std::io", |
532 | r"pub use std::fmt::{Result, Display};", | 558 | r"pub use std::fmt::{Result, Display};", |
@@ -535,22 +561,31 @@ use std::io;", | |||
535 | ) | 561 | ) |
536 | } | 562 | } |
537 | 563 | ||
538 | // should this be a thing? | ||
539 | #[test] | 564 | #[test] |
540 | fn split_merge() { | 565 | #[ignore] // FIXME: Support this |
566 | fn split_out_merge() { | ||
541 | check_last( | 567 | check_last( |
542 | "std::fmt::Result", | 568 | "std::fmt::Result", |
543 | r"use std::{fmt, io};", | 569 | r"use std::{fmt, io};", |
544 | r"use std::fmt::Result; | 570 | r"use std::{self, fmt::Result}; |
545 | use std::io;", | 571 | use std::io;", |
546 | ) | 572 | ) |
547 | } | 573 | } |
548 | 574 | ||
549 | #[test] | 575 | #[test] |
550 | fn merges_groups_self() { | 576 | fn merge_groups_self() { |
551 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") | 577 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") |
552 | } | 578 | } |
553 | 579 | ||
580 | #[test] | ||
581 | fn merge_self_glob() { | ||
582 | check_full( | ||
583 | "token::TokenKind", | ||
584 | r"use token::TokenKind::*;", | ||
585 | r"use token::TokenKind::{self::*, self};", | ||
586 | ) | ||
587 | } | ||
588 | |||
554 | fn check( | 589 | fn check( |
555 | path: &str, | 590 | path: &str, |
556 | ra_fixture_before: &str, | 591 | ra_fixture_before: &str, |