Skip to content

Commit 64326b2

Browse files
authored
Merge pull request rust-lang#20589 from A4-Tacks/extract-mod-not-in-impl
Fix extract multiple item in impl for extract_module
2 parents dbfbe8f + 2969b0e commit 64326b2

File tree

1 file changed

+92
-60
lines changed

1 file changed

+92
-60
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs

Lines changed: 92 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::ops::RangeInclusive;
1+
use std::{iter::once, ops::RangeInclusive};
22

33
use hir::{HasSource, ModuleSource};
44
use ide_db::{
@@ -63,19 +63,6 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
6363
syntax::NodeOrToken::Token(t) => t.parent()?,
6464
};
6565

66-
//If the selection is inside impl block, we need to place new module outside impl block,
67-
//as impl blocks cannot contain modules
68-
69-
let mut impl_parent: Option<ast::Impl> = None;
70-
let mut impl_child_count: usize = 0;
71-
if let Some(parent_assoc_list) = node.parent()
72-
&& let Some(parent_impl) = parent_assoc_list.parent()
73-
&& let Some(impl_) = ast::Impl::cast(parent_impl)
74-
{
75-
impl_child_count = parent_assoc_list.children().count();
76-
impl_parent = Some(impl_);
77-
}
78-
7966
let mut curr_parent_module: Option<ast::Module> = None;
8067
if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) {
8168
curr_parent_module = ast::Module::cast(mod_syn_opt);
@@ -94,7 +81,22 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
9481
return None;
9582
}
9683

97-
let old_item_indent = module.body_items[0].indent_level();
84+
let mut old_item_indent = module.body_items[0].indent_level();
85+
let old_items: Vec<_> = module.use_items.iter().chain(&module.body_items).cloned().collect();
86+
87+
// If the selection is inside impl block, we need to place new module outside impl block,
88+
// as impl blocks cannot contain modules
89+
90+
let mut impl_parent: Option<ast::Impl> = None;
91+
let mut impl_child_count: usize = 0;
92+
if let Some(parent_assoc_list) = module.body_items[0].syntax().parent()
93+
&& let Some(parent_impl) = parent_assoc_list.parent()
94+
&& let Some(impl_) = ast::Impl::cast(parent_impl)
95+
{
96+
impl_child_count = parent_assoc_list.children().count();
97+
old_item_indent = impl_.indent_level();
98+
impl_parent = Some(impl_);
99+
}
98100

99101
acc.add(
100102
AssistId::refactor_extract("extract_module"),
@@ -127,7 +129,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
127129
let import_items = module.resolve_imports(curr_parent_module, ctx);
128130
module.change_visibility(record_fields);
129131

130-
let module_def = generate_module_def(&impl_parent, module, old_item_indent).to_string();
132+
let module_def = generate_module_def(&impl_parent, &module).indent(old_item_indent);
131133

132134
let mut usages_to_be_processed_for_cur_file = vec![];
133135
for (file_id, usages) in usages_to_be_processed {
@@ -149,62 +151,68 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
149151
if let Some(impl_) = impl_parent {
150152
// Remove complete impl block if it has only one child (as such it will be empty
151153
// after deleting that child)
152-
let node_to_be_removed = if impl_child_count == 1 {
153-
impl_.syntax()
154+
let nodes_to_be_removed = if impl_child_count == old_items.len() {
155+
vec![impl_.syntax()]
154156
} else {
155157
//Remove selected node
156-
&node
158+
old_items.iter().map(|it| it.syntax()).collect()
157159
};
158160

159-
builder.delete(node_to_be_removed.text_range());
160-
// Remove preceding indentation from node
161-
if let Some(range) = indent_range_before_given_node(node_to_be_removed) {
162-
builder.delete(range);
161+
for node_to_be_removed in nodes_to_be_removed {
162+
builder.delete(node_to_be_removed.text_range());
163+
// Remove preceding indentation from node
164+
if let Some(range) = indent_range_before_given_node(node_to_be_removed) {
165+
builder.delete(range);
166+
}
163167
}
164168

165-
builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}"));
169+
builder.insert(
170+
impl_.syntax().text_range().end(),
171+
format!("\n\n{old_item_indent}{module_def}"),
172+
);
166173
} else {
167174
for import_item in import_items {
168175
if !module_text_range.contains_range(import_item) {
169176
builder.delete(import_item);
170177
}
171178
}
172-
builder.replace(module_text_range, module_def)
179+
builder.replace(module_text_range, module_def.to_string())
173180
}
174181
},
175182
)
176183
}
177184

178185
fn generate_module_def(
179186
parent_impl: &Option<ast::Impl>,
180-
module: Module,
181-
old_indent: IndentLevel,
187+
Module { name, body_items, use_items }: &Module,
182188
) -> ast::Module {
183-
let Module { name, body_items, use_items } = module;
184-
let items = if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) {
189+
let items: Vec<_> = if let Some(impl_) = parent_impl.as_ref()
190+
&& let Some(self_ty) = impl_.self_ty()
191+
{
185192
let assoc_items = body_items
186-
.into_iter()
193+
.iter()
187194
.map(|item| item.syntax().clone())
188195
.filter_map(ast::AssocItem::cast)
189196
.map(|it| it.indent(IndentLevel(1)))
190197
.collect_vec();
191-
let assoc_item_list = make::assoc_item_list(Some(assoc_items));
192-
let impl_ = make::impl_(None, None, None, self_ty.clone(), None, Some(assoc_item_list));
198+
let assoc_item_list = make::assoc_item_list(Some(assoc_items)).clone_for_update();
199+
let impl_ = impl_.reset_indent();
200+
ted::replace(impl_.get_or_create_assoc_item_list().syntax(), assoc_item_list.syntax());
193201
// Add the import for enum/struct corresponding to given impl block
194202
let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax());
195-
let mut module_body_items = use_items;
196-
module_body_items.insert(0, use_impl);
197-
module_body_items.push(ast::Item::Impl(impl_));
198-
module_body_items
203+
once(use_impl)
204+
.chain(use_items.iter().cloned())
205+
.chain(once(ast::Item::Impl(impl_)))
206+
.collect()
199207
} else {
200-
[use_items, body_items].concat()
208+
use_items.iter().chain(body_items).cloned().collect()
201209
};
202210

203211
let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec();
204212
let module_body = make::item_list(Some(items));
205213

206214
let module_name = make::name(name);
207-
make::mod_(module_name, Some(module_body)).indent(old_indent)
215+
make::mod_(module_name, Some(module_body))
208216
}
209217

210218
fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item {
@@ -1400,28 +1408,54 @@ mod modname {
14001408
fn test_if_inside_impl_block_generate_module_outside() {
14011409
check_assist(
14021410
extract_module,
1403-
r"
1404-
struct A {}
1411+
r"struct A {}
14051412
14061413
impl A {
1407-
$0fn foo() {}$0
1414+
$0fn foo() {}$0
14081415
fn bar() {}
14091416
}
14101417
",
1411-
r"
1412-
struct A {}
1418+
r"struct A {}
14131419
14141420
impl A {
14151421
fn bar() {}
14161422
}
14171423
1418-
mod modname {
1419-
use super::A;
1424+
mod modname {
1425+
use super::A;
14201426
1421-
impl A {
1422-
pub(crate) fn foo() {}
1423-
}
1424-
}
1427+
impl A {
1428+
pub(crate) fn foo() {}
1429+
}
1430+
}
1431+
",
1432+
);
1433+
1434+
check_assist(
1435+
extract_module,
1436+
r"struct A {}
1437+
1438+
impl A {
1439+
$0fn foo() {}
1440+
fn bar() {}$0
1441+
fn baz() {}
1442+
}
1443+
",
1444+
r"struct A {}
1445+
1446+
impl A {
1447+
fn baz() {}
1448+
}
1449+
1450+
mod modname {
1451+
use super::A;
1452+
1453+
impl A {
1454+
pub(crate) fn foo() {}
1455+
1456+
pub(crate) fn bar() {}
1457+
}
1458+
}
14251459
",
14261460
)
14271461
}
@@ -1430,27 +1464,25 @@ mod modname {
14301464
fn test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child() {
14311465
check_assist(
14321466
extract_module,
1433-
r"
1434-
struct A {}
1467+
r"struct A {}
14351468
struct B {}
14361469
14371470
impl A {
14381471
$0fn foo(x: B) {}$0
14391472
}
14401473
",
1441-
r"
1442-
struct A {}
1474+
r"struct A {}
14431475
struct B {}
14441476
1445-
mod modname {
1446-
use super::A;
1477+
mod modname {
1478+
use super::A;
14471479
1448-
use super::B;
1480+
use super::B;
14491481
1450-
impl A {
1451-
pub(crate) fn foo(x: B) {}
1452-
}
1453-
}
1482+
impl A {
1483+
pub(crate) fn foo(x: B) {}
1484+
}
1485+
}
14541486
",
14551487
)
14561488
}

0 commit comments

Comments
 (0)