aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2020-06-13 18:05:31 +0100
committerJonas Schievink <[email protected]>2020-06-13 18:12:05 +0100
commit5d66bfe16343141132c8a7aefa727b209ec3e66a (patch)
tree24cf6c4c08f8bc009edc4e3f0ce665509b4e86c6 /crates/ra_assists
parentb65c0a5893e0ed88119a00062ae26e86375e4883 (diff)
Shorten *all* qualified paths when adding use
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assist_context.rs2
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs201
2 files changed, 192 insertions, 11 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index edd8255f4..ee614de72 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -252,7 +252,7 @@ impl AssistBuilder {
252 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { 252 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
253 let node = rewriter.rewrite_root().unwrap(); 253 let node = rewriter.rewrite_root().unwrap();
254 let new = rewriter.rewrite(&node); 254 let new = rewriter.rewrite(&node);
255 algo::diff(&node, &new).into_text_edit(&mut self.edit) 255 algo::diff(&node, &new).into_text_edit(&mut self.edit);
256 } 256 }
257 257
258 // FIXME: kill this API 258 // FIXME: kill this API
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index 0197a8cf0..6cbf8309b 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,7 +1,11 @@
1use hir; 1use hir::{self, ModPath};
2use ra_syntax::{ast, AstNode, SmolStr, TextRange}; 2use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
3 3
4use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; 4use crate::{
5 utils::{find_insert_use_container, insert_use_statement},
6 AssistContext, AssistId, Assists,
7};
8use either::Either;
5 9
6// Assist: replace_qualified_name_with_use 10// Assist: replace_qualified_name_with_use
7// 11//
@@ -39,16 +43,28 @@ pub(crate) fn replace_qualified_name_with_use(
39 target, 43 target,
40 |builder| { 44 |builder| {
41 let path_to_import = hir_path.mod_path().clone(); 45 let path_to_import = hir_path.mod_path().clone();
46 let container = match find_insert_use_container(path.syntax(), ctx) {
47 Some(c) => c,
48 None => return,
49 };
42 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder()); 50 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
43 51
44 if let Some(last) = path.segment() { 52 // Now that we've brought the name into scope, re-qualify all paths that could be
45 // Here we are assuming the assist will provide a correct use statement 53 // affected (that is, all paths inside the node we added the `use` to).
46 // so we can delete the path qualifier 54 let hir_path = match hir::Path::from_ast(path.clone()) {
47 builder.delete(TextRange::new( 55 Some(p) => p,
48 path.syntax().text_range().start(), 56 None => return,
49 last.syntax().text_range().start(), 57 };
50 )); 58 let mut rewriter = SyntaxRewriter::default();
59 match container {
60 Either::Left(l) => {
61 shorten_paths(&mut rewriter, l.syntax().clone(), hir_path.mod_path());
62 }
63 Either::Right(r) => {
64 shorten_paths(&mut rewriter, r.syntax().clone(), hir_path.mod_path());
65 }
51 } 66 }
67 builder.rewrite(rewriter);
52 }, 68 },
53 ) 69 )
54} 70}
@@ -73,6 +89,59 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
73 Some(ps) 89 Some(ps)
74} 90}
75 91
92/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
93fn shorten_paths(re: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: &ModPath) {
94 for child in node.children() {
95 match_ast! {
96 match child {
97 // Don't modify `use` items, as this can break the `use` item when injecting a new
98 // import into the use tree.
99 ast::UseItem(_it) => continue,
100 // Don't descend into submodules, they don't have the same `use` items in scope.
101 ast::Module(_it) => continue,
102
103 ast::Path(p) => {
104 match maybe_replace_path(re, &p, path) {
105 Some(()) => {},
106 None => shorten_paths(re, p.syntax().clone(), path),
107 }
108 },
109 _ => shorten_paths(re, child, path),
110 }
111 }
112 }
113}
114
115fn maybe_replace_path(
116 re: &mut SyntaxRewriter<'static>,
117 p: &ast::Path,
118 path: &ModPath,
119) -> Option<()> {
120 let hir_path = hir::Path::from_ast(p.clone())?;
121
122 if hir_path.mod_path() != path {
123 return None;
124 }
125
126 // Replace path with its last "plain" segment.
127 let mut mod_path = hir_path.mod_path().clone();
128 let last = mod_path.segments.len() - 1;
129 mod_path.segments.swap(0, last);
130 mod_path.segments.truncate(1);
131 mod_path.kind = hir::PathKind::Plain;
132
133 let mut new_path = crate::ast_transform::path_to_ast(mod_path);
134
135 let type_args = p.segment().and_then(|s| s.type_arg_list());
136 if let Some(type_args) = type_args {
137 let last_segment = new_path.segment().unwrap();
138 new_path = new_path.with_segment(last_segment.with_type_args(type_args));
139 }
140
141 re.replace(p.syntax(), new_path.syntax());
142 Some(())
143}
144
76#[cfg(test)] 145#[cfg(test)]
77mod tests { 146mod tests {
78 use crate::tests::{check_assist, check_assist_not_applicable}; 147 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -463,4 +532,116 @@ fn main() {
463 ", 532 ",
464 ); 533 );
465 } 534 }
535
536 #[test]
537 fn replaces_all_affected_paths() {
538 check_assist(
539 replace_qualified_name_with_use,
540 "
541fn main() {
542 std::fmt::Debug<|>;
543 let x: std::fmt::Debug = std::fmt::Debug;
544}
545 ",
546 "
547use std::fmt::Debug;
548
549fn main() {
550 Debug;
551 let x: Debug = Debug;
552}
553 ",
554 );
555 }
556
557 #[test]
558 fn replaces_all_affected_paths_mod() {
559 check_assist(
560 replace_qualified_name_with_use,
561 "
562mod m {
563 fn f() {
564 std::fmt::Debug<|>;
565 let x: std::fmt::Debug = std::fmt::Debug;
566 }
567 fn g() {
568 std::fmt::Debug;
569 }
570}
571
572fn f() {
573 std::fmt::Debug;
574}
575 ",
576 "
577mod m {
578 use std::fmt::Debug;
579
580 fn f() {
581 Debug;
582 let x: Debug = Debug;
583 }
584 fn g() {
585 Debug;
586 }
587}
588
589fn f() {
590 std::fmt::Debug;
591}
592 ",
593 );
594 }
595
596 #[test]
597 fn does_not_replace_in_submodules() {
598 check_assist(
599 replace_qualified_name_with_use,
600 "
601fn main() {
602 std::fmt::Debug<|>;
603}
604
605mod sub {
606 fn f() {
607 std::fmt::Debug;
608 }
609}
610 ",
611 "
612use std::fmt::Debug;
613
614fn main() {
615 Debug;
616}
617
618mod sub {
619 fn f() {
620 std::fmt::Debug;
621 }
622}
623 ",
624 );
625 }
626
627 #[test]
628 fn does_not_replace_in_use() {
629 check_assist(
630 replace_qualified_name_with_use,
631 "
632use std::fmt::Display;
633
634fn main() {
635 std::fmt<|>;
636}
637 ",
638 "
639use std::fmt::{self, Display};
640
641fn main() {
642 fmt;
643}
644 ",
645 );
646 }
466} 647}