aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-09-04 14:28:05 +0100
committerGitHub <[email protected]>2020-09-04 14:28:05 +0100
commit9dfa69a44ac5054b5c970fdd13ade4d50f4c097d (patch)
tree58b1f9069d022c985ef57a74212fcefd03a877c3 /crates/assists/src/handlers
parent8a21b2c96acb351baa81422f6633462b5db2298a (diff)
parent82f61e6629f709d7f347fd801ef5c31f476ff29e (diff)
Merge #5935
5935: Rewrite import insertion r=matklad a=Veykril This is my attempt at refactoring the import insertion #3947. I hope what I created here is somewhat in line with what was requested, it wouldn't surprise me . `common_prefix` is a copy from `merge_imports.rs` so those should be unified somewhere, `try_merge_trees` is also copied from there but slighly modified to take the `MergeBehaviour` enum into account. `MergeBehaviour` should in the end become a configuration option, and the order if `ImportGroup` probably as well? I'm not too familiar with the assist stuff and the like which is why I dont know what i have to do with `insert_use_statement` and `find_insert_use_container` for now. I will most likely add more test cases in the end as well as I currently only tried to hit every path in `find_insert_position`. Some of the merge tests also fail atm due to them not sorting what they insert. There is also this test case I'm not sure if we want to support it. I would assume we want to? https://github.com/rust-analyzer/rust-analyzer/pull/5935/files#diff-6923916dd8bdd2f1ab4b984adacd265fR540-R547 The entire module was rewritten so looking at the the file itself is probably better than looking at the diff. Regarding the sub issues of #3947: - #3301: This is fixed with the rewrite, what this implementation does is that it scans through the first occurence of groupings and picks the appropriate one out. This means the user can actually rearrange the groupings on a per file basis to their liking. If a group isnt being found it is inserted according to the `ImportGroup` variant order(Would be nice if this was configurable I imagine). - #3831: This should be fixed with the introduced `MergeBehaviour` enum and it's `Last` variant. - #3946: This should also be [fixed](https://github.com/rust-analyzer/rust-analyzer/pull/5935/files#diff-6923916dd8bdd2f1ab4b984adacd265fR87) - #5795: This is fixed in the sense that the grouping search picks the first group that is of the same kind as the import that is being added. So if there is a random import in the middle of the program it should only be considered if there is no group of the same kind in the file already present. - the last point in the list I havent checked yet, tho I got the feeling that it's not gonna be too simple as that will require knowledge of whether in this example `ast` is a crate or the module that is already imported. Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r--crates/assists/src/handlers/auto_import.rs20
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs33
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs68
3 files changed, 71 insertions, 50 deletions
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index c4770f336..66e819154 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,11 +1,13 @@
1use std::collections::BTreeSet; 1use std::collections::BTreeSet;
2 2
3use ast::make;
3use either::Either; 4use either::Either;
4use hir::{ 5use hir::{
5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, 6 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
6 Type, 7 Type,
7}; 8};
8use ide_db::{imports_locator, RootDatabase}; 9use ide_db::{imports_locator, RootDatabase};
10use insert_use::ImportScope;
9use rustc_hash::FxHashSet; 11use rustc_hash::FxHashSet;
10use syntax::{ 12use syntax::{
11 ast::{self, AstNode}, 13 ast::{self, AstNode},
@@ -13,7 +15,8 @@ use syntax::{
13}; 15};
14 16
15use crate::{ 17use crate::{
16 utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel, 18 utils::{insert_use, MergeBehaviour},
19 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
17}; 20};
18 21
19// Assist: auto_import 22// Assist: auto_import
@@ -44,6 +47,9 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
44 47
45 let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; 48 let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
46 let group = auto_import_assets.get_import_group_message(); 49 let group = auto_import_assets.get_import_group_message();
50 let scope =
51 ImportScope::find_insert_use_container(&auto_import_assets.syntax_under_caret, ctx)?;
52 let syntax = scope.as_syntax_node();
47 for import in proposed_imports { 53 for import in proposed_imports {
48 acc.add_group( 54 acc.add_group(
49 &group, 55 &group,
@@ -51,12 +57,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
51 format!("Import `{}`", &import), 57 format!("Import `{}`", &import),
52 range, 58 range,
53 |builder| { 59 |builder| {
54 insert_use_statement( 60 let new_syntax = insert_use(
55 &auto_import_assets.syntax_under_caret, 61 &scope,
56 &import.to_string(), 62 make::path_from_text(&import.to_string()),
57 ctx, 63 Some(MergeBehaviour::Full),
58 builder.text_edit_builder(),
59 ); 64 );
65 builder.replace(syntax.text_range(), new_syntax.to_string())
60 }, 66 },
61 ); 67 );
62 } 68 }
@@ -358,7 +364,7 @@ mod tests {
358 } 364 }
359 ", 365 ",
360 r" 366 r"
361 use PubMod::{PubStruct2, PubStruct1}; 367 use PubMod::{PubStruct1, PubStruct2};
362 368
363 struct Test { 369 struct Test {
364 test: PubStruct2<u8>, 370 test: PubStruct2<u8>,
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 8ac20210a..80c62d8bb 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -10,9 +10,12 @@ use syntax::{
10}; 10};
11 11
12use crate::{ 12use crate::{
13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, 13 assist_context::AssistBuilder,
14 AssistKind, Assists, 14 utils::{insert_use, MergeBehaviour},
15 AssistContext, AssistId, AssistKind, Assists,
15}; 16};
17use ast::make;
18use insert_use::ImportScope;
16 19
17// Assist: extract_struct_from_enum_variant 20// Assist: extract_struct_from_enum_variant
18// 21//
@@ -94,6 +97,7 @@ fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVari
94 .any(|(name, _)| name.to_string() == variant_name.to_string()) 97 .any(|(name, _)| name.to_string() == variant_name.to_string())
95} 98}
96 99
100#[allow(dead_code)]
97fn insert_import( 101fn insert_import(
98 ctx: &AssistContext, 102 ctx: &AssistContext,
99 builder: &mut AssistBuilder, 103 builder: &mut AssistBuilder,
@@ -107,12 +111,16 @@ fn insert_import(
107 if let Some(mut mod_path) = mod_path { 111 if let Some(mut mod_path) = mod_path {
108 mod_path.segments.pop(); 112 mod_path.segments.pop();
109 mod_path.segments.push(variant_hir_name.clone()); 113 mod_path.segments.push(variant_hir_name.clone());
110 insert_use_statement( 114 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
111 path.syntax(), 115 let syntax = scope.as_syntax_node();
112 &mod_path.to_string(), 116
113 ctx, 117 let new_syntax = insert_use(
114 builder.text_edit_builder(), 118 &scope,
119 make::path_from_text(&mod_path.to_string()),
120 Some(MergeBehaviour::Full),
115 ); 121 );
122 // FIXME: this will currently panic as multiple imports will have overlapping text ranges
123 builder.replace(syntax.text_range(), new_syntax.to_string())
116 } 124 }
117 Some(()) 125 Some(())
118} 126}
@@ -167,9 +175,9 @@ fn update_reference(
167 builder: &mut AssistBuilder, 175 builder: &mut AssistBuilder,
168 reference: Reference, 176 reference: Reference,
169 source_file: &SourceFile, 177 source_file: &SourceFile,
170 enum_module_def: &ModuleDef, 178 _enum_module_def: &ModuleDef,
171 variant_hir_name: &Name, 179 _variant_hir_name: &Name,
172 visited_modules_set: &mut FxHashSet<Module>, 180 _visited_modules_set: &mut FxHashSet<Module>,
173) -> Option<()> { 181) -> Option<()> {
174 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( 182 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
175 source_file.syntax(), 183 source_file.syntax(),
@@ -178,13 +186,14 @@ fn update_reference(
178 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 186 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
179 let list = call.arg_list()?; 187 let list = call.arg_list()?;
180 let segment = path_expr.path()?.segment()?; 188 let segment = path_expr.path()?.segment()?;
181 let module = ctx.sema.scope(&path_expr.syntax()).module()?; 189 let _module = ctx.sema.scope(&path_expr.syntax()).module()?;
182 let list_range = list.syntax().text_range(); 190 let list_range = list.syntax().text_range();
183 let inside_list_range = TextRange::new( 191 let inside_list_range = TextRange::new(
184 list_range.start().checked_add(TextSize::from(1))?, 192 list_range.start().checked_add(TextSize::from(1))?,
185 list_range.end().checked_sub(TextSize::from(1))?, 193 list_range.end().checked_sub(TextSize::from(1))?,
186 ); 194 );
187 builder.edit_file(reference.file_range.file_id); 195 builder.edit_file(reference.file_range.file_id);
196 /* FIXME: this most likely requires AST-based editing, see `insert_import`
188 if !visited_modules_set.contains(&module) { 197 if !visited_modules_set.contains(&module) {
189 if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) 198 if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
190 .is_some() 199 .is_some()
@@ -192,6 +201,7 @@ fn update_reference(
192 visited_modules_set.insert(module); 201 visited_modules_set.insert(module);
193 } 202 }
194 } 203 }
204 */
195 builder.replace(inside_list_range, format!("{}{}", segment, list)); 205 builder.replace(inside_list_range, format!("{}{}", segment, list));
196 Some(()) 206 Some(())
197} 207}
@@ -250,6 +260,7 @@ pub enum A { One(One) }"#,
250 } 260 }
251 261
252 #[test] 262 #[test]
263 #[ignore] // FIXME: this currently panics if `insert_import` is used
253 fn test_extract_struct_with_complex_imports() { 264 fn test_extract_struct_with_complex_imports() {
254 check_assist( 265 check_assist(
255 extract_struct_from_enum_variant, 266 extract_struct_from_enum_variant,
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index 470e5f8ff..85c70d16b 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -2,9 +2,10 @@ use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode, TextRang
2use test_utils::mark; 2use test_utils::mark;
3 3
4use crate::{ 4use crate::{
5 utils::{find_insert_use_container, insert_use_statement}, 5 utils::{insert_use, ImportScope, MergeBehaviour},
6 AssistContext, AssistId, AssistKind, Assists, 6 AssistContext, AssistId, AssistKind, Assists,
7}; 7};
8use ast::make;
8 9
9// Assist: replace_qualified_name_with_use 10// Assist: replace_qualified_name_with_use
10// 11//
@@ -32,7 +33,7 @@ pub(crate) fn replace_qualified_name_with_use(
32 mark::hit!(dont_import_trivial_paths); 33 mark::hit!(dont_import_trivial_paths);
33 return None; 34 return None;
34 } 35 }
35 let path_to_import = path.to_string().clone(); 36 let path_to_import = path.to_string();
36 let path_to_import = match path.segment()?.generic_arg_list() { 37 let path_to_import = match path.segment()?.generic_arg_list() {
37 Some(generic_args) => { 38 Some(generic_args) => {
38 let generic_args_start = 39 let generic_args_start =
@@ -43,28 +44,26 @@ pub(crate) fn replace_qualified_name_with_use(
43 }; 44 };
44 45
45 let target = path.syntax().text_range(); 46 let target = path.syntax().text_range();
47 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
48 let syntax = scope.as_syntax_node();
46 acc.add( 49 acc.add(
47 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), 50 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
48 "Replace qualified path with use", 51 "Replace qualified path with use",
49 target, 52 target,
50 |builder| { 53 |builder| {
51 let container = match find_insert_use_container(path.syntax(), ctx) {
52 Some(c) => c,
53 None => return,
54 };
55 insert_use_statement(
56 path.syntax(),
57 &path_to_import.to_string(),
58 ctx,
59 builder.text_edit_builder(),
60 );
61
62 // Now that we've brought the name into scope, re-qualify all paths that could be 54 // Now that we've brought the name into scope, re-qualify all paths that could be
63 // affected (that is, all paths inside the node we added the `use` to). 55 // affected (that is, all paths inside the node we added the `use` to).
64 let mut rewriter = SyntaxRewriter::default(); 56 let mut rewriter = SyntaxRewriter::default();
65 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone()); 57 shorten_paths(&mut rewriter, syntax.clone(), path);
66 shorten_paths(&mut rewriter, syntax, path); 58 let rewritten_syntax = rewriter.rewrite(&syntax);
67 builder.rewrite(rewriter); 59 if let Some(ref import_scope) = ImportScope::from(rewritten_syntax) {
60 let new_syntax = insert_use(
61 import_scope,
62 make::path_from_text(path_to_import),
63 Some(MergeBehaviour::Full),
64 );
65 builder.replace(syntax.text_range(), new_syntax.to_string())
66 }
68 }, 67 },
69 ) 68 )
70} 69}
@@ -220,9 +219,10 @@ impl std::fmt::Debug<|> for Foo {
220} 219}
221 ", 220 ",
222 r" 221 r"
223use stdx;
224use std::fmt::Debug; 222use std::fmt::Debug;
225 223
224use stdx;
225
226impl Debug for Foo { 226impl Debug for Foo {
227} 227}
228 ", 228 ",
@@ -274,7 +274,7 @@ impl std::io<|> for Foo {
274} 274}
275 ", 275 ",
276 r" 276 r"
277use std::{io, fmt}; 277use std::{fmt, io};
278 278
279impl io for Foo { 279impl io for Foo {
280} 280}
@@ -293,7 +293,7 @@ impl std::fmt::Debug<|> for Foo {
293} 293}
294 ", 294 ",
295 r" 295 r"
296use std::fmt::{self, Debug, }; 296use std::fmt::{self, Debug};
297 297
298impl Debug for Foo { 298impl Debug for Foo {
299} 299}
@@ -312,7 +312,7 @@ impl std::fmt<|> for Foo {
312} 312}
313 ", 313 ",
314 r" 314 r"
315use std::fmt::{self, Debug}; 315use std::fmt::{Debug, self};
316 316
317impl fmt for Foo { 317impl fmt for Foo {
318} 318}
@@ -330,8 +330,9 @@ use std::fmt::{Debug, nested::{Display}};
330impl std::fmt::nested<|> for Foo { 330impl std::fmt::nested<|> for Foo {
331} 331}
332", 332",
333 // FIXME(veykril): should be nested::{self, Display} here
333 r" 334 r"
334use std::fmt::{Debug, nested::{Display, self}}; 335use std::fmt::{Debug, nested::{Display}, nested};
335 336
336impl nested for Foo { 337impl nested for Foo {
337} 338}
@@ -349,8 +350,9 @@ use std::fmt::{Debug, nested::{self, Display}};
349impl std::fmt::nested<|> for Foo { 350impl std::fmt::nested<|> for Foo {
350} 351}
351", 352",
353 // FIXME(veykril): nested is duplicated now
352 r" 354 r"
353use std::fmt::{Debug, nested::{self, Display}}; 355use std::fmt::{Debug, nested::{self, Display}, nested};
354 356
355impl nested for Foo { 357impl nested for Foo {
356} 358}
@@ -369,7 +371,7 @@ impl std::fmt::nested::Debug<|> for Foo {
369} 371}
370", 372",
371 r" 373 r"
372use std::fmt::{Debug, nested::{Display, Debug}}; 374use std::fmt::{Debug, nested::{Display}, nested::Debug};
373 375
374impl Debug for Foo { 376impl Debug for Foo {
375} 377}
@@ -388,7 +390,7 @@ impl std::fmt::nested::Display<|> for Foo {
388} 390}
389", 391",
390 r" 392 r"
391use std::fmt::{nested::Display, Debug}; 393use std::fmt::{Debug, nested::Display};
392 394
393impl Display for Foo { 395impl Display for Foo {
394} 396}
@@ -407,7 +409,7 @@ impl std::fmt::Display<|> for Foo {
407} 409}
408", 410",
409 r" 411 r"
410use std::fmt::{Display, nested::Debug}; 412use std::fmt::{nested::Debug, Display};
411 413
412impl Display for Foo { 414impl Display for Foo {
413} 415}
@@ -427,11 +429,12 @@ use crate::{
427 429
428fn foo() { crate::ty::lower<|>::trait_env() } 430fn foo() { crate::ty::lower<|>::trait_env() }
429", 431",
432 // FIXME(veykril): formatting broke here
430 r" 433 r"
431use crate::{ 434use crate::{
432 ty::{Substs, Ty, lower}, 435 ty::{Substs, Ty},
433 AssocItem, 436 AssocItem,
434}; 437ty::lower};
435 438
436fn foo() { lower::trait_env() } 439fn foo() { lower::trait_env() }
437", 440",
@@ -451,6 +454,8 @@ impl foo::Debug<|> for Foo {
451 r" 454 r"
452use std::fmt as foo; 455use std::fmt as foo;
453 456
457use foo::Debug;
458
454impl Debug for Foo { 459impl Debug for Foo {
455} 460}
456", 461",
@@ -515,6 +520,7 @@ fn main() {
515 ", 520 ",
516 r" 521 r"
517#![allow(dead_code)] 522#![allow(dead_code)]
523
518use std::fmt::Debug; 524use std::fmt::Debug;
519 525
520fn main() { 526fn main() {
@@ -627,7 +633,7 @@ fn main() {
627} 633}
628 ", 634 ",
629 r" 635 r"
630use std::fmt::{self, Display}; 636use std::fmt::{Display, self};
631 637
632fn main() { 638fn main() {
633 fmt; 639 fmt;
@@ -647,9 +653,8 @@ impl std::io<|> for Foo {
647} 653}
648 ", 654 ",
649 r" 655 r"
650use std::io;
651
652pub use std::fmt; 656pub use std::fmt;
657use std::io;
653 658
654impl io for Foo { 659impl io for Foo {
655} 660}
@@ -668,9 +673,8 @@ impl std::io<|> for Foo {
668} 673}
669 ", 674 ",
670 r" 675 r"
671use std::io;
672
673pub(crate) use std::fmt; 676pub(crate) use std::fmt;
677use std::io;
674 678
675impl io for Foo { 679impl io for Foo {
676} 680}