aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
diff options
context:
space:
mode:
authorChetan Khilosiya <[email protected]>2021-02-22 18:47:48 +0000
committerChetan Khilosiya <[email protected]>2021-02-22 19:29:16 +0000
commite4756cb4f6e66097638b9d101589358976be2ba8 (patch)
treeb6ca0ae6b45b57834476ae0f9985cec3a6bd9090 /crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
parent8687053b118f47ce1a4962d0baa19b22d40d2758 (diff)
7526: Rename crate assists to ide_assists.
Diffstat (limited to 'crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs')
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs678
1 files changed, 678 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
new file mode 100644
index 000000000..f3bc6cf39
--- /dev/null
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -0,0 +1,678 @@
1use ide_db::helpers::insert_use::{insert_use, ImportScope};
2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode};
3use test_utils::mark;
4
5use crate::{AssistContext, AssistId, AssistKind, Assists};
6
7// Assist: replace_qualified_name_with_use
8//
9// Adds a use statement for a given fully-qualified name.
10//
11// ```
12// fn process(map: std::collections::$0HashMap<String, String>) {}
13// ```
14// ->
15// ```
16// use std::collections::HashMap;
17//
18// fn process(map: HashMap<String, String>) {}
19// ```
20pub(crate) fn replace_qualified_name_with_use(
21 acc: &mut Assists,
22 ctx: &AssistContext,
23) -> Option<()> {
24 let path: ast::Path = ctx.find_node_at_offset()?;
25 // We don't want to mess with use statements
26 if path.syntax().ancestors().find_map(ast::Use::cast).is_some() {
27 return None;
28 }
29 if path.qualifier().is_none() {
30 mark::hit!(dont_import_trivial_paths);
31 return None;
32 }
33
34 let target = path.syntax().text_range();
35 let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
36 let syntax = scope.as_syntax_node();
37 acc.add(
38 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
39 "Replace qualified path with use",
40 target,
41 |builder| {
42 // Now that we've brought the name into scope, re-qualify all paths that could be
43 // affected (that is, all paths inside the node we added the `use` to).
44 let mut rewriter = SyntaxRewriter::default();
45 shorten_paths(&mut rewriter, syntax.clone(), &path);
46 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
47 rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge);
48 builder.rewrite(rewriter);
49 }
50 },
51 )
52}
53
54/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
55fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: &ast::Path) {
56 for child in node.children() {
57 match_ast! {
58 match child {
59 // Don't modify `use` items, as this can break the `use` item when injecting a new
60 // import into the use tree.
61 ast::Use(_it) => continue,
62 // Don't descend into submodules, they don't have the same `use` items in scope.
63 ast::Module(_it) => continue,
64
65 ast::Path(p) => {
66 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
67 Some(()) => {},
68 None => shorten_paths(rewriter, p.syntax().clone(), path),
69 }
70 },
71 _ => shorten_paths(rewriter, child, path),
72 }
73 }
74 }
75}
76
77fn maybe_replace_path(
78 rewriter: &mut SyntaxRewriter<'static>,
79 path: ast::Path,
80 target: ast::Path,
81) -> Option<()> {
82 if !path_eq(path.clone(), target) {
83 return None;
84 }
85
86 // Shorten `path`, leaving only its last segment.
87 if let Some(parent) = path.qualifier() {
88 rewriter.delete(parent.syntax());
89 }
90 if let Some(double_colon) = path.coloncolon_token() {
91 rewriter.delete(&double_colon);
92 }
93
94 Some(())
95}
96
97fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
98 let mut lhs_curr = lhs;
99 let mut rhs_curr = rhs;
100 loop {
101 match (lhs_curr.segment(), rhs_curr.segment()) {
102 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
103 _ => return false,
104 }
105
106 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
107 (Some(lhs), Some(rhs)) => {
108 lhs_curr = lhs;
109 rhs_curr = rhs;
110 }
111 (None, None) => return true,
112 _ => return false,
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use crate::tests::{check_assist, check_assist_not_applicable};
120
121 use super::*;
122
123 #[test]
124 fn test_replace_already_imported() {
125 check_assist(
126 replace_qualified_name_with_use,
127 r"use std::fs;
128
129fn main() {
130 std::f$0s::Path
131}",
132 r"use std::fs;
133
134fn main() {
135 fs::Path
136}",
137 )
138 }
139
140 #[test]
141 fn test_replace_add_use_no_anchor() {
142 check_assist(
143 replace_qualified_name_with_use,
144 r"
145std::fmt::Debug$0
146 ",
147 r"
148use std::fmt::Debug;
149
150Debug
151 ",
152 );
153 }
154 #[test]
155 fn test_replace_add_use_no_anchor_with_item_below() {
156 check_assist(
157 replace_qualified_name_with_use,
158 r"
159std::fmt::Debug$0
160
161fn main() {
162}
163 ",
164 r"
165use std::fmt::Debug;
166
167Debug
168
169fn main() {
170}
171 ",
172 );
173 }
174
175 #[test]
176 fn test_replace_add_use_no_anchor_with_item_above() {
177 check_assist(
178 replace_qualified_name_with_use,
179 r"
180fn main() {
181}
182
183std::fmt::Debug$0
184 ",
185 r"
186use std::fmt::Debug;
187
188fn main() {
189}
190
191Debug
192 ",
193 );
194 }
195
196 #[test]
197 fn test_replace_add_use_no_anchor_2seg() {
198 check_assist(
199 replace_qualified_name_with_use,
200 r"
201std::fmt$0::Debug
202 ",
203 r"
204use std::fmt;
205
206fmt::Debug
207 ",
208 );
209 }
210
211 #[test]
212 fn test_replace_add_use() {
213 check_assist(
214 replace_qualified_name_with_use,
215 r"
216use stdx;
217
218impl std::fmt::Debug$0 for Foo {
219}
220 ",
221 r"
222use std::fmt::Debug;
223
224use stdx;
225
226impl Debug for Foo {
227}
228 ",
229 );
230 }
231
232 #[test]
233 fn test_replace_file_use_other_anchor() {
234 check_assist(
235 replace_qualified_name_with_use,
236 r"
237impl std::fmt::Debug$0 for Foo {
238}
239 ",
240 r"
241use std::fmt::Debug;
242
243impl Debug for Foo {
244}
245 ",
246 );
247 }
248
249 #[test]
250 fn test_replace_add_use_other_anchor_indent() {
251 check_assist(
252 replace_qualified_name_with_use,
253 r"
254 impl std::fmt::Debug$0 for Foo {
255 }
256 ",
257 r"
258 use std::fmt::Debug;
259
260 impl Debug for Foo {
261 }
262 ",
263 );
264 }
265
266 #[test]
267 fn test_replace_split_different() {
268 check_assist(
269 replace_qualified_name_with_use,
270 r"
271use std::fmt;
272
273impl std::io$0 for Foo {
274}
275 ",
276 r"
277use std::{fmt, io};
278
279impl io for Foo {
280}
281 ",
282 );
283 }
284
285 #[test]
286 fn test_replace_split_self_for_use() {
287 check_assist(
288 replace_qualified_name_with_use,
289 r"
290use std::fmt;
291
292impl std::fmt::Debug$0 for Foo {
293}
294 ",
295 r"
296use std::fmt::{self, Debug};
297
298impl Debug for Foo {
299}
300 ",
301 );
302 }
303
304 #[test]
305 fn test_replace_split_self_for_target() {
306 check_assist(
307 replace_qualified_name_with_use,
308 r"
309use std::fmt::Debug;
310
311impl std::fmt$0 for Foo {
312}
313 ",
314 r"
315use std::fmt::{self, Debug};
316
317impl fmt for Foo {
318}
319 ",
320 );
321 }
322
323 #[test]
324 fn test_replace_add_to_nested_self_nested() {
325 check_assist(
326 replace_qualified_name_with_use,
327 r"
328use std::fmt::{Debug, nested::{Display}};
329
330impl std::fmt::nested$0 for Foo {
331}
332",
333 r"
334use std::fmt::{Debug, nested::{self, Display}};
335
336impl nested for Foo {
337}
338",
339 );
340 }
341
342 #[test]
343 fn test_replace_add_to_nested_self_already_included() {
344 check_assist(
345 replace_qualified_name_with_use,
346 r"
347use std::fmt::{Debug, nested::{self, Display}};
348
349impl std::fmt::nested$0 for Foo {
350}
351",
352 r"
353use std::fmt::{Debug, nested::{self, Display}};
354
355impl nested for Foo {
356}
357",
358 );
359 }
360
361 #[test]
362 fn test_replace_add_to_nested_nested() {
363 check_assist(
364 replace_qualified_name_with_use,
365 r"
366use std::fmt::{Debug, nested::{Display}};
367
368impl std::fmt::nested::Debug$0 for Foo {
369}
370",
371 r"
372use std::fmt::{Debug, nested::{Debug, Display}};
373
374impl Debug for Foo {
375}
376",
377 );
378 }
379
380 #[test]
381 fn test_replace_split_common_target_longer() {
382 check_assist(
383 replace_qualified_name_with_use,
384 r"
385use std::fmt::Debug;
386
387impl std::fmt::nested::Display$0 for Foo {
388}
389",
390 r"
391use std::fmt::{Debug, nested::Display};
392
393impl Display for Foo {
394}
395",
396 );
397 }
398
399 #[test]
400 fn test_replace_split_common_use_longer() {
401 check_assist(
402 replace_qualified_name_with_use,
403 r"
404use std::fmt::nested::Debug;
405
406impl std::fmt::Display$0 for Foo {
407}
408",
409 r"
410use std::fmt::{Display, nested::Debug};
411
412impl Display for Foo {
413}
414",
415 );
416 }
417
418 #[test]
419 fn test_replace_use_nested_import() {
420 check_assist(
421 replace_qualified_name_with_use,
422 r"
423use crate::{
424 ty::{Substs, Ty},
425 AssocItem,
426};
427
428fn foo() { crate::ty::lower$0::trait_env() }
429",
430 r"
431use crate::{AssocItem, ty::{Substs, Ty, lower}};
432
433fn foo() { lower::trait_env() }
434",
435 );
436 }
437
438 #[test]
439 fn test_replace_alias() {
440 check_assist(
441 replace_qualified_name_with_use,
442 r"
443use std::fmt as foo;
444
445impl foo::Debug$0 for Foo {
446}
447",
448 r"
449use std::fmt as foo;
450
451use foo::Debug;
452
453impl Debug for Foo {
454}
455",
456 );
457 }
458
459 #[test]
460 fn dont_import_trivial_paths() {
461 mark::check!(dont_import_trivial_paths);
462 check_assist_not_applicable(
463 replace_qualified_name_with_use,
464 r"
465impl foo$0 for Foo {
466}
467",
468 );
469 }
470
471 #[test]
472 fn test_replace_not_applicable_in_use() {
473 check_assist_not_applicable(
474 replace_qualified_name_with_use,
475 r"
476use std::fmt$0;
477",
478 );
479 }
480
481 #[test]
482 fn test_replace_add_use_no_anchor_in_mod_mod() {
483 check_assist(
484 replace_qualified_name_with_use,
485 r"
486mod foo {
487 mod bar {
488 std::fmt::Debug$0
489 }
490}
491 ",
492 r"
493mod foo {
494 mod bar {
495 use std::fmt::Debug;
496
497 Debug
498 }
499}
500 ",
501 );
502 }
503
504 #[test]
505 fn inserts_imports_after_inner_attributes() {
506 check_assist(
507 replace_qualified_name_with_use,
508 r"
509#![allow(dead_code)]
510
511fn main() {
512 std::fmt::Debug$0
513}
514 ",
515 r"
516#![allow(dead_code)]
517
518use std::fmt::Debug;
519
520fn main() {
521 Debug
522}
523 ",
524 );
525 }
526
527 #[test]
528 fn replaces_all_affected_paths() {
529 check_assist(
530 replace_qualified_name_with_use,
531 r"
532fn main() {
533 std::fmt::Debug$0;
534 let x: std::fmt::Debug = std::fmt::Debug;
535}
536 ",
537 r"
538use std::fmt::Debug;
539
540fn main() {
541 Debug;
542 let x: Debug = Debug;
543}
544 ",
545 );
546 }
547
548 #[test]
549 fn replaces_all_affected_paths_mod() {
550 check_assist(
551 replace_qualified_name_with_use,
552 r"
553mod m {
554 fn f() {
555 std::fmt::Debug$0;
556 let x: std::fmt::Debug = std::fmt::Debug;
557 }
558 fn g() {
559 std::fmt::Debug;
560 }
561}
562
563fn f() {
564 std::fmt::Debug;
565}
566 ",
567 r"
568mod m {
569 use std::fmt::Debug;
570
571 fn f() {
572 Debug;
573 let x: Debug = Debug;
574 }
575 fn g() {
576 Debug;
577 }
578}
579
580fn f() {
581 std::fmt::Debug;
582}
583 ",
584 );
585 }
586
587 #[test]
588 fn does_not_replace_in_submodules() {
589 check_assist(
590 replace_qualified_name_with_use,
591 r"
592fn main() {
593 std::fmt::Debug$0;
594}
595
596mod sub {
597 fn f() {
598 std::fmt::Debug;
599 }
600}
601 ",
602 r"
603use std::fmt::Debug;
604
605fn main() {
606 Debug;
607}
608
609mod sub {
610 fn f() {
611 std::fmt::Debug;
612 }
613}
614 ",
615 );
616 }
617
618 #[test]
619 fn does_not_replace_in_use() {
620 check_assist(
621 replace_qualified_name_with_use,
622 r"
623use std::fmt::Display;
624
625fn main() {
626 std::fmt$0;
627}
628 ",
629 r"
630use std::fmt::{self, Display};
631
632fn main() {
633 fmt;
634}
635 ",
636 );
637 }
638
639 #[test]
640 fn does_not_replace_pub_use() {
641 check_assist(
642 replace_qualified_name_with_use,
643 r"
644pub use std::fmt;
645
646impl std::io$0 for Foo {
647}
648 ",
649 r"
650pub use std::fmt;
651use std::io;
652
653impl io for Foo {
654}
655 ",
656 );
657 }
658
659 #[test]
660 fn does_not_replace_pub_crate_use() {
661 check_assist(
662 replace_qualified_name_with_use,
663 r"
664pub(crate) use std::fmt;
665
666impl std::io$0 for Foo {
667}
668 ",
669 r"
670pub(crate) use std::fmt;
671use std::io;
672
673impl io for Foo {
674}
675 ",
676 );
677 }
678}