Skip to content

Commit 3ee3221

Browse files
authored
Merge pull request #21023 from A4-Tacks/syntax-editor-duplicate-changed
Fix syntax_editor duplicated changed element
2 parents 9c3a46a + d48361b commit 3ee3221

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,4 +1197,57 @@ fn foo() {
11971197
"#,
11981198
);
11991199
}
1200+
1201+
#[test]
1202+
fn regression_issue_21020() {
1203+
check_assist(
1204+
convert_tuple_struct_to_named_struct,
1205+
r#"
1206+
pub struct S$0(pub ());
1207+
1208+
trait T {
1209+
fn id(&self) -> usize;
1210+
}
1211+
1212+
trait T2 {
1213+
fn foo(&self) -> usize;
1214+
}
1215+
1216+
impl T for S {
1217+
fn id(&self) -> usize {
1218+
self.0.len()
1219+
}
1220+
}
1221+
1222+
impl T2 for S {
1223+
fn foo(&self) -> usize {
1224+
self.0.len()
1225+
}
1226+
}
1227+
"#,
1228+
r#"
1229+
pub struct S { pub field1: () }
1230+
1231+
trait T {
1232+
fn id(&self) -> usize;
1233+
}
1234+
1235+
trait T2 {
1236+
fn foo(&self) -> usize;
1237+
}
1238+
1239+
impl T for S {
1240+
fn id(&self) -> usize {
1241+
self.field1.len()
1242+
}
1243+
}
1244+
1245+
impl T2 for S {
1246+
fn foo(&self) -> usize {
1247+
self.field1.len()
1248+
}
1249+
}
1250+
"#,
1251+
);
1252+
}
12001253
}

crates/syntax/src/syntax_editor.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,4 +653,58 @@ mod tests {
653653
let expect = expect![["fn it() {\n \n}"]];
654654
expect.assert_eq(&edit.new_root.to_string());
655655
}
656+
657+
#[test]
658+
fn test_more_times_replace_node_to_mutable_token() {
659+
let arg_list =
660+
make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
661+
662+
let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
663+
let target_expr = make::token(parser::SyntaxKind::UNDERSCORE);
664+
665+
for arg in arg_list.args() {
666+
editor.replace(arg.syntax(), &target_expr);
667+
}
668+
669+
let edit = editor.finish();
670+
671+
let expect = expect![["(_, _)"]];
672+
expect.assert_eq(&edit.new_root.to_string());
673+
}
674+
675+
#[test]
676+
fn test_more_times_replace_node_to_mutable() {
677+
let arg_list =
678+
make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
679+
680+
let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
681+
let target_expr = make::expr_literal("3").clone_for_update();
682+
683+
for arg in arg_list.args() {
684+
editor.replace(arg.syntax(), target_expr.syntax());
685+
}
686+
687+
let edit = editor.finish();
688+
689+
let expect = expect![["(3, 3)"]];
690+
expect.assert_eq(&edit.new_root.to_string());
691+
}
692+
693+
#[test]
694+
fn test_more_times_insert_node_to_mutable() {
695+
let arg_list =
696+
make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
697+
698+
let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
699+
let target_expr = make::ext::expr_unit().clone_for_update();
700+
701+
for arg in arg_list.args() {
702+
editor.insert(Position::before(arg.syntax()), target_expr.syntax());
703+
}
704+
705+
let edit = editor.finish();
706+
707+
let expect = expect![["(()1, ()2)"]];
708+
expect.assert_eq(&edit.new_root.to_string());
709+
}
656710
}

crates/syntax/src/syntax_editor/edit_algo.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,35 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
150150
// Map change targets to the correct syntax nodes
151151
let tree_mutator = TreeMutator::new(&root);
152152
let mut changed_elements = vec![];
153+
let mut changed_elements_set = rustc_hash::FxHashSet::default();
154+
let mut deduplicate_node = |node_or_token: &mut SyntaxElement| {
155+
let node;
156+
let node = match node_or_token {
157+
SyntaxElement::Token(token) => match token.parent() {
158+
None => return,
159+
Some(parent) => {
160+
node = parent;
161+
&node
162+
}
163+
},
164+
SyntaxElement::Node(node) => node,
165+
};
166+
if changed_elements_set.contains(node) {
167+
let new_node = node.clone_subtree().clone_for_update();
168+
match node_or_token {
169+
SyntaxElement::Node(node) => *node = new_node,
170+
SyntaxElement::Token(token) => {
171+
*token = new_node
172+
.children_with_tokens()
173+
.filter_map(SyntaxElement::into_token)
174+
.find(|it| it.kind() == token.kind() && it.text() == token.text())
175+
.unwrap();
176+
}
177+
}
178+
} else {
179+
changed_elements_set.insert(node.clone());
180+
}
181+
};
153182

154183
for index in independent_changes {
155184
match &mut changes[index as usize] {
@@ -180,6 +209,18 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
180209
}
181210
}
182211

212+
match &mut changes[index as usize] {
213+
Change::Insert(_, element) | Change::Replace(_, Some(element)) => {
214+
deduplicate_node(element);
215+
}
216+
Change::InsertAll(_, elements)
217+
| Change::ReplaceWithMany(_, elements)
218+
| Change::ReplaceAll(_, elements) => {
219+
elements.iter_mut().for_each(&mut deduplicate_node);
220+
}
221+
Change::Replace(_, None) => (),
222+
}
223+
183224
// Collect changed elements
184225
match &changes[index as usize] {
185226
Change::Insert(_, element) => changed_elements.push(element.clone()),

0 commit comments

Comments
 (0)