Skip to content

Commit 9dfa69a

Browse files
bors[bot]Veykril
andauthored
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]>
2 parents 8a21b2c + 82f61e6 commit 9dfa69a

File tree

6 files changed

+744
-535
lines changed

6 files changed

+744
-535
lines changed

crates/assists/src/handlers/auto_import.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
use std::collections::BTreeSet;
22

3+
use ast::make;
34
use either::Either;
45
use hir::{
56
AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
67
Type,
78
};
89
use ide_db::{imports_locator, RootDatabase};
10+
use insert_use::ImportScope;
911
use rustc_hash::FxHashSet;
1012
use syntax::{
1113
ast::{self, AstNode},
1214
SyntaxNode,
1315
};
1416

1517
use crate::{
16-
utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
18+
utils::{insert_use, MergeBehaviour},
19+
AssistContext, AssistId, AssistKind, Assists, GroupLabel,
1720
};
1821

1922
// Assist: auto_import
@@ -44,19 +47,22 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
4447

4548
let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
4649
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();
4753
for import in proposed_imports {
4854
acc.add_group(
4955
&group,
5056
AssistId("auto_import", AssistKind::QuickFix),
5157
format!("Import `{}`", &import),
5258
range,
5359
|builder| {
54-
insert_use_statement(
55-
&auto_import_assets.syntax_under_caret,
56-
&import.to_string(),
57-
ctx,
58-
builder.text_edit_builder(),
60+
let new_syntax = insert_use(
61+
&scope,
62+
make::path_from_text(&import.to_string()),
63+
Some(MergeBehaviour::Full),
5964
);
65+
builder.replace(syntax.text_range(), new_syntax.to_string())
6066
},
6167
);
6268
}
@@ -358,7 +364,7 @@ mod tests {
358364
}
359365
",
360366
r"
361-
use PubMod::{PubStruct2, PubStruct1};
367+
use PubMod::{PubStruct1, PubStruct2};
362368
363369
struct Test {
364370
test: PubStruct2<u8>,

crates/assists/src/handlers/extract_struct_from_enum_variant.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ use syntax::{
1010
};
1111

1212
use crate::{
13-
assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
14-
AssistKind, Assists,
13+
assist_context::AssistBuilder,
14+
utils::{insert_use, MergeBehaviour},
15+
AssistContext, AssistId, AssistKind, Assists,
1516
};
17+
use ast::make;
18+
use insert_use::ImportScope;
1619

1720
// Assist: extract_struct_from_enum_variant
1821
//
@@ -94,6 +97,7 @@ fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVari
9497
.any(|(name, _)| name.to_string() == variant_name.to_string())
9598
}
9699

100+
#[allow(dead_code)]
97101
fn insert_import(
98102
ctx: &AssistContext,
99103
builder: &mut AssistBuilder,
@@ -107,12 +111,16 @@ fn insert_import(
107111
if let Some(mut mod_path) = mod_path {
108112
mod_path.segments.pop();
109113
mod_path.segments.push(variant_hir_name.clone());
110-
insert_use_statement(
111-
path.syntax(),
112-
&mod_path.to_string(),
113-
ctx,
114-
builder.text_edit_builder(),
114+
let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
115+
let syntax = scope.as_syntax_node();
116+
117+
let new_syntax = insert_use(
118+
&scope,
119+
make::path_from_text(&mod_path.to_string()),
120+
Some(MergeBehaviour::Full),
115121
);
122+
// FIXME: this will currently panic as multiple imports will have overlapping text ranges
123+
builder.replace(syntax.text_range(), new_syntax.to_string())
116124
}
117125
Some(())
118126
}
@@ -167,9 +175,9 @@ fn update_reference(
167175
builder: &mut AssistBuilder,
168176
reference: Reference,
169177
source_file: &SourceFile,
170-
enum_module_def: &ModuleDef,
171-
variant_hir_name: &Name,
172-
visited_modules_set: &mut FxHashSet<Module>,
178+
_enum_module_def: &ModuleDef,
179+
_variant_hir_name: &Name,
180+
_visited_modules_set: &mut FxHashSet<Module>,
173181
) -> Option<()> {
174182
let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
175183
source_file.syntax(),
@@ -178,20 +186,22 @@ fn update_reference(
178186
let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
179187
let list = call.arg_list()?;
180188
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()?;
182190
let list_range = list.syntax().text_range();
183191
let inside_list_range = TextRange::new(
184192
list_range.start().checked_add(TextSize::from(1))?,
185193
list_range.end().checked_sub(TextSize::from(1))?,
186194
);
187195
builder.edit_file(reference.file_range.file_id);
196+
/* FIXME: this most likely requires AST-based editing, see `insert_import`
188197
if !visited_modules_set.contains(&module) {
189198
if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
190199
.is_some()
191200
{
192201
visited_modules_set.insert(module);
193202
}
194203
}
204+
*/
195205
builder.replace(inside_list_range, format!("{}{}", segment, list));
196206
Some(())
197207
}
@@ -250,6 +260,7 @@ pub enum A { One(One) }"#,
250260
}
251261

252262
#[test]
263+
#[ignore] // FIXME: this currently panics if `insert_import` is used
253264
fn test_extract_struct_with_complex_imports() {
254265
check_assist(
255266
extract_struct_from_enum_variant,

crates/assists/src/handlers/replace_qualified_name_with_use.rs

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode, TextRang
22
use test_utils::mark;
33

44
use crate::{
5-
utils::{find_insert_use_container, insert_use_statement},
5+
utils::{insert_use, ImportScope, MergeBehaviour},
66
AssistContext, AssistId, AssistKind, Assists,
77
};
8+
use ast::make;
89

910
// Assist: replace_qualified_name_with_use
1011
//
@@ -32,7 +33,7 @@ pub(crate) fn replace_qualified_name_with_use(
3233
mark::hit!(dont_import_trivial_paths);
3334
return None;
3435
}
35-
let path_to_import = path.to_string().clone();
36+
let path_to_import = path.to_string();
3637
let path_to_import = match path.segment()?.generic_arg_list() {
3738
Some(generic_args) => {
3839
let generic_args_start =
@@ -43,28 +44,26 @@ pub(crate) fn replace_qualified_name_with_use(
4344
};
4445

4546
let target = path.syntax().text_range();
47+
let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
48+
let syntax = scope.as_syntax_node();
4649
acc.add(
4750
AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
4851
"Replace qualified path with use",
4952
target,
5053
|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-
6254
// Now that we've brought the name into scope, re-qualify all paths that could be
6355
// affected (that is, all paths inside the node we added the `use` to).
6456
let mut rewriter = SyntaxRewriter::default();
65-
let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
66-
shorten_paths(&mut rewriter, syntax, path);
67-
builder.rewrite(rewriter);
57+
shorten_paths(&mut rewriter, syntax.clone(), path);
58+
let rewritten_syntax = rewriter.rewrite(&syntax);
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+
}
6867
},
6968
)
7069
}
@@ -220,9 +219,10 @@ impl std::fmt::Debug<|> for Foo {
220219
}
221220
",
222221
r"
223-
use stdx;
224222
use std::fmt::Debug;
225223
224+
use stdx;
225+
226226
impl Debug for Foo {
227227
}
228228
",
@@ -274,7 +274,7 @@ impl std::io<|> for Foo {
274274
}
275275
",
276276
r"
277-
use std::{io, fmt};
277+
use std::{fmt, io};
278278
279279
impl io for Foo {
280280
}
@@ -293,7 +293,7 @@ impl std::fmt::Debug<|> for Foo {
293293
}
294294
",
295295
r"
296-
use std::fmt::{self, Debug, };
296+
use std::fmt::{self, Debug};
297297
298298
impl Debug for Foo {
299299
}
@@ -312,7 +312,7 @@ impl std::fmt<|> for Foo {
312312
}
313313
",
314314
r"
315-
use std::fmt::{self, Debug};
315+
use std::fmt::{Debug, self};
316316
317317
impl fmt for Foo {
318318
}
@@ -330,8 +330,9 @@ use std::fmt::{Debug, nested::{Display}};
330330
impl std::fmt::nested<|> for Foo {
331331
}
332332
",
333+
// FIXME(veykril): should be nested::{self, Display} here
333334
r"
334-
use std::fmt::{Debug, nested::{Display, self}};
335+
use std::fmt::{Debug, nested::{Display}, nested};
335336
336337
impl nested for Foo {
337338
}
@@ -349,8 +350,9 @@ use std::fmt::{Debug, nested::{self, Display}};
349350
impl std::fmt::nested<|> for Foo {
350351
}
351352
",
353+
// FIXME(veykril): nested is duplicated now
352354
r"
353-
use std::fmt::{Debug, nested::{self, Display}};
355+
use std::fmt::{Debug, nested::{self, Display}, nested};
354356
355357
impl nested for Foo {
356358
}
@@ -369,7 +371,7 @@ impl std::fmt::nested::Debug<|> for Foo {
369371
}
370372
",
371373
r"
372-
use std::fmt::{Debug, nested::{Display, Debug}};
374+
use std::fmt::{Debug, nested::{Display}, nested::Debug};
373375
374376
impl Debug for Foo {
375377
}
@@ -388,7 +390,7 @@ impl std::fmt::nested::Display<|> for Foo {
388390
}
389391
",
390392
r"
391-
use std::fmt::{nested::Display, Debug};
393+
use std::fmt::{Debug, nested::Display};
392394
393395
impl Display for Foo {
394396
}
@@ -407,7 +409,7 @@ impl std::fmt::Display<|> for Foo {
407409
}
408410
",
409411
r"
410-
use std::fmt::{Display, nested::Debug};
412+
use std::fmt::{nested::Debug, Display};
411413
412414
impl Display for Foo {
413415
}
@@ -427,11 +429,12 @@ use crate::{
427429
428430
fn foo() { crate::ty::lower<|>::trait_env() }
429431
",
432+
// FIXME(veykril): formatting broke here
430433
r"
431434
use crate::{
432-
ty::{Substs, Ty, lower},
435+
ty::{Substs, Ty},
433436
AssocItem,
434-
};
437+
ty::lower};
435438
436439
fn foo() { lower::trait_env() }
437440
",
@@ -451,6 +454,8 @@ impl foo::Debug<|> for Foo {
451454
r"
452455
use std::fmt as foo;
453456
457+
use foo::Debug;
458+
454459
impl Debug for Foo {
455460
}
456461
",
@@ -515,6 +520,7 @@ fn main() {
515520
",
516521
r"
517522
#![allow(dead_code)]
523+
518524
use std::fmt::Debug;
519525
520526
fn main() {
@@ -627,7 +633,7 @@ fn main() {
627633
}
628634
",
629635
r"
630-
use std::fmt::{self, Display};
636+
use std::fmt::{Display, self};
631637
632638
fn main() {
633639
fmt;
@@ -647,9 +653,8 @@ impl std::io<|> for Foo {
647653
}
648654
",
649655
r"
650-
use std::io;
651-
652656
pub use std::fmt;
657+
use std::io;
653658
654659
impl io for Foo {
655660
}
@@ -668,9 +673,8 @@ impl std::io<|> for Foo {
668673
}
669674
",
670675
r"
671-
use std::io;
672-
673676
pub(crate) use std::fmt;
677+
use std::io;
674678
675679
impl io for Foo {
676680
}

crates/assists/src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use syntax::{
1616

1717
use crate::assist_config::SnippetCap;
1818

19-
pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
19+
pub(crate) use insert_use::{insert_use, ImportScope, MergeBehaviour};
2020

2121
pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
2222
extract_trivial_expression(&block)

0 commit comments

Comments
 (0)