Skip to content

Commit 513c6d3

Browse files
committed
fix: re-insert use stmts that is extracted
1 parent de71605 commit 513c6d3

File tree

1 file changed

+48
-13
lines changed

1 file changed

+48
-13
lines changed

crates/ide-assists/src/handlers/extract_module.rs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use syntax::{
2020
},
2121
match_ast, ted, AstNode,
2222
SyntaxKind::{self, WHITESPACE},
23-
SyntaxNode, TextRange,
23+
SyntaxNode, TextRange, TextSize,
2424
};
2525

2626
use crate::{AssistContext, Assists};
@@ -109,7 +109,14 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
109109

110110
//We are getting item usages and record_fields together, record_fields
111111
//for change_visibility and usages for first point mentioned above in the process
112-
let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
112+
113+
let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) =
114+
module.get_usages_and_record_fields(ctx);
115+
116+
builder.edit_file(ctx.file_id());
117+
use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| {
118+
builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}"));
119+
});
113120

114121
let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
115122
module.change_visibility(record_fields);
@@ -224,9 +231,12 @@ impl Module {
224231
fn get_usages_and_record_fields(
225232
&self,
226233
ctx: &AssistContext<'_>,
227-
) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
234+
) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>, FxHashMap<TextSize, ast::Use>)
235+
{
228236
let mut adt_fields = Vec::new();
229237
let mut refs: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
238+
// use `TextSize` as key to avoid repeated use stmts
239+
let mut use_stmts_to_be_inserted = FxHashMap::default();
230240

231241
//Here impl is not included as each item inside impl will be tied to the parent of
232242
//implementing block(a struct, enum, etc), if the parent is in selected module, it will
@@ -238,7 +248,7 @@ impl Module {
238248
ast::Adt(it) => {
239249
if let Some( nod ) = ctx.sema.to_def(&it) {
240250
let node_def = Definition::Adt(nod);
241-
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
251+
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted);
242252

243253
//Enum Fields are not allowed to explicitly specify pub, it is implied
244254
match it {
@@ -272,59 +282,84 @@ impl Module {
272282
ast::TypeAlias(it) => {
273283
if let Some( nod ) = ctx.sema.to_def(&it) {
274284
let node_def = Definition::TypeAlias(nod);
275-
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
285+
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted);
276286
}
277287
},
278288
ast::Const(it) => {
279289
if let Some( nod ) = ctx.sema.to_def(&it) {
280290
let node_def = Definition::Const(nod);
281-
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
291+
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted);
282292
}
283293
},
284294
ast::Static(it) => {
285295
if let Some( nod ) = ctx.sema.to_def(&it) {
286296
let node_def = Definition::Static(nod);
287-
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
297+
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted);
288298
}
289299
},
290300
ast::Fn(it) => {
291301
if let Some( nod ) = ctx.sema.to_def(&it) {
292302
let node_def = Definition::Function(nod);
293-
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
303+
self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted);
294304
}
295305
},
296306
ast::Macro(it) => {
297307
if let Some(nod) = ctx.sema.to_def(&it) {
298-
self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs);
308+
self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted);
299309
}
300310
},
301311
_ => (),
302312
}
303313
}
304314
}
305315

306-
(refs, adt_fields)
316+
(refs, adt_fields, use_stmts_to_be_inserted)
307317
}
308318

309319
fn expand_and_group_usages_file_wise(
310320
&self,
311321
ctx: &AssistContext<'_>,
312322
node_def: Definition,
313323
refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>,
324+
use_stmts_to_be_inserted: &mut FxHashMap<TextSize, ast::Use>,
314325
) {
315326
let mod_name = self.name;
327+
let covering_node = match ctx.covering_element() {
328+
syntax::NodeOrToken::Node(node) => node,
329+
syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic
330+
};
316331
let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range());
332+
let mut use_stmts_set = FxHashSet::default();
317333

318334
for (file_id, refs) in node_def.usages(&ctx.sema).all() {
319335
let source_file = ctx.sema.parse(file_id);
320-
let usages = refs.into_iter().filter_map(|FileReference { range, name, .. }| {
336+
let usages = refs.into_iter().filter_map(|FileReference { range, .. }| {
321337
// handle normal usages
322338
let name_ref = find_node_at_range::<ast::NameRef>(source_file.syntax(), range)?;
323-
let name = name.syntax().to_string();
324339

325340
if out_of_sel(name_ref.syntax()) {
326341
let new_ref = format!("{mod_name}::{name_ref}");
327-
return Some((name_ref.syntax().text_range(), new_ref));
342+
return Some((range, new_ref));
343+
} else if let Some(use_) = name_ref.syntax().ancestors().find_map(ast::Use::cast) {
344+
// handle usages in use_stmts which is in_sel
345+
// check if `use` is top stmt in selection
346+
if use_.syntax().parent().is_some_and(|parent| parent == covering_node)
347+
&& use_stmts_set.insert(use_.syntax().text_range().start())
348+
{
349+
let use_ = use_stmts_to_be_inserted
350+
.entry(use_.syntax().text_range().start())
351+
.or_insert_with(|| use_.clone_subtree().clone_for_update());
352+
for seg in use_
353+
.syntax()
354+
.descendants()
355+
.filter_map(ast::NameRef::cast)
356+
.filter(|seg| seg.syntax().to_string() == name_ref.to_string())
357+
{
358+
let new_ref = make::path_from_text(&format!("{mod_name}::{seg}"))
359+
.clone_for_update();
360+
ted::replace(seg.syntax().parent()?, new_ref.syntax());
361+
}
362+
}
328363
}
329364

330365
None

0 commit comments

Comments
 (0)