aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/utils/insert_use.rs179
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.
37pub fn insert_use( 38pub 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)]
226enum AddBlankLine { 231enum 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"
310use std::bar::B;
302use std::bar::D; 311use std::bar::D;
303use std::bar::F; 312use std::bar::F;
304use std::bar::G;", 313use std::bar::G;",
305 r"use std::bar::A; 314 r"
315use std::bar::AA;
306use std::bar::B; 316use std::bar::B;
307use std::bar::D; 317use std::bar::D;
308use std::bar::F; 318use 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"
328use std::bar::A;
318use std::bar::D; 329use std::bar::D;
319use std::bar::F; 330use std::bar::F;
320use std::bar::G;", 331use std::bar::G;",
321 r"use std::bar::A; 332 r"
333use std::bar::A;
322use std::bar::D; 334use std::bar::D;
323use std::bar::E; 335use std::bar::EE;
324use std::bar::F; 336use std::bar::F;
325use std::bar::G;", 337use 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"
346use std::bar::A;
334use std::bar::D; 347use std::bar::D;
335use std::bar::F; 348use std::bar::F;
336use std::bar::G;", 349use std::bar::G;",
337 r"use std::bar::A; 350 r"
351use std::bar::A;
338use std::bar::D; 352use std::bar::D;
339use std::bar::F; 353use std::bar::F;
340use std::bar::G; 354use std::bar::G;
341use std::bar::Z;", 355use 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"
364use std::bar::A;
350use std::bar::{D, Z}; // example of weird imports due to user 365use std::bar::{D, Z}; // example of weird imports due to user
351use std::bar::F; 366use std::bar::F;
352use std::bar::G;", 367use std::bar::G;",
353 r"use std::bar::A; 368 r"
354use std::bar::E; 369use std::bar::A;
370use std::bar::EE;
355use std::bar::{D, Z}; // example of weird imports due to user 371use std::bar::{D, Z}; // example of weird imports due to user
356use std::bar::F; 372use std::bar::F;
357use std::bar::G;", 373use 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"
382use std::bar::A;
366use std::bar::D; 383use std::bar::D;
367 384
368use foo::bar::F; 385use foo::bar::F;
369use foo::bar::H;", 386use foo::bar::H;",
370 r"use std::bar::A; 387 r"
388use std::bar::A;
371use std::bar::D; 389use std::bar::D;
372 390
373use foo::bar::F; 391use foo::bar::F;
374use foo::bar::G; 392use foo::bar::GG;
375use foo::bar::H;", 393use 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"
402use foo::bar::A;
384use foo::bar::D; 403use foo::bar::D;
385 404
386use std; 405use std;
387 406
388use foo::bar::F; 407use foo::bar::F;
389use foo::bar::H;", 408use foo::bar::H;",
390 r"use foo::bar::A; 409 r"
410use foo::bar::A;
391use foo::bar::D; 411use foo::bar::D;
392use foo::bar::G; 412use foo::bar::GG;
393 413
394use std; 414use 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"
426use foo::bar::A;
406use foo::bar::D;", 427use foo::bar::D;",
407 r"use std::fmt; 428 r"
429use std::fmt;
408 430
409use foo::bar::A; 431use foo::bar::A;
410use foo::bar::D;", 432use 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"
441use foo::bar::A;
442use foo::bar::D;",
443 r"
444use foo::bar::A;
445use foo::bar::D;
446
447use 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
445use 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};
545use std::io;", 571use 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,