aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/references/rename.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/references/rename.rs')
-rw-r--r--crates/ide/src/references/rename.rs165
1 files changed, 158 insertions, 7 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 26ac2371a..b8725693a 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Module, ModuleDef, ModuleSource, Semantics}; 3use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ide_db::base_db::SourceDatabaseExt; 4use ide_db::base_db::{FileRange, SourceDatabaseExt};
5use ide_db::{ 5use ide_db::{
6 defs::{Definition, NameClass, NameRefClass}, 6 defs::{Definition, NameClass, NameRefClass},
7 RootDatabase, 7 RootDatabase,
@@ -106,9 +106,12 @@ fn find_module_at_offset(
106 Some(module) 106 Some(module)
107} 107}
108 108
109fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { 109fn source_edit_from_reference(
110 sema: &Semantics<RootDatabase>,
111 reference: Reference,
112 new_name: &str,
113) -> SourceFileEdit {
110 let mut replacement_text = String::new(); 114 let mut replacement_text = String::new();
111 let file_id = reference.file_range.file_id;
112 let range = match reference.kind { 115 let range = match reference.kind {
113 ReferenceKind::FieldShorthandForField => { 116 ReferenceKind::FieldShorthandForField => {
114 mark::hit!(test_rename_struct_field_for_shorthand); 117 mark::hit!(test_rename_struct_field_for_shorthand);
@@ -122,12 +125,48 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
122 replacement_text.push_str(new_name); 125 replacement_text.push_str(new_name);
123 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 126 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
124 } 127 }
128 ReferenceKind::RecordFieldExprOrPat => {
129 mark::hit!(test_rename_field_expr_pat);
130 replacement_text.push_str(new_name);
131 edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name)
132 }
125 _ => { 133 _ => {
126 replacement_text.push_str(new_name); 134 replacement_text.push_str(new_name);
127 reference.file_range.range 135 reference.file_range.range
128 } 136 }
129 }; 137 };
130 SourceFileEdit { file_id, edit: TextEdit::replace(range, replacement_text) } 138 SourceFileEdit {
139 file_id: reference.file_range.file_id,
140 edit: TextEdit::replace(range, replacement_text),
141 }
142}
143
144fn edit_text_range_for_record_field_expr_or_pat(
145 sema: &Semantics<RootDatabase>,
146 file_range: FileRange,
147 new_name: &str,
148) -> TextRange {
149 let source_file = sema.parse(file_range.file_id);
150 let file_syntax = source_file.syntax();
151 let original_range = file_range.range;
152
153 syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range)
154 .and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) {
155 Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()),
156 _ => None,
157 })
158 .or_else(|| {
159 syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range)
160 .and_then(|field_pat| match field_pat.pat() {
161 Some(ast::Pat::IdentPat(pat))
162 if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) =>
163 {
164 Some(field_pat.syntax().text_range())
165 }
166 _ => None,
167 })
168 })
169 .unwrap_or(original_range)
131} 170}
132 171
133fn rename_mod( 172fn rename_mod(
@@ -170,7 +209,7 @@ fn rename_mod(
170 let ref_edits = refs 209 let ref_edits = refs
171 .references 210 .references
172 .into_iter() 211 .into_iter()
173 .map(|reference| source_edit_from_reference(reference, new_name)); 212 .map(|reference| source_edit_from_reference(sema, reference, new_name));
174 source_file_edits.extend(ref_edits); 213 source_file_edits.extend(ref_edits);
175 214
176 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 215 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -211,7 +250,7 @@ fn rename_to_self(
211 250
212 let mut edits = usages 251 let mut edits = usages
213 .into_iter() 252 .into_iter()
214 .map(|reference| source_edit_from_reference(reference, "self")) 253 .map(|reference| source_edit_from_reference(sema, reference, "self"))
215 .collect::<Vec<_>>(); 254 .collect::<Vec<_>>();
216 255
217 edits.push(SourceFileEdit { 256 edits.push(SourceFileEdit {
@@ -300,7 +339,7 @@ fn rename_reference(
300 339
301 let edit = refs 340 let edit = refs
302 .into_iter() 341 .into_iter()
303 .map(|reference| source_edit_from_reference(reference, new_name)) 342 .map(|reference| source_edit_from_reference(sema, reference, new_name))
304 .collect::<Vec<_>>(); 343 .collect::<Vec<_>>();
305 344
306 if edit.is_empty() { 345 if edit.is_empty() {
@@ -1097,4 +1136,116 @@ impl Foo {
1097"#, 1136"#,
1098 ); 1137 );
1099 } 1138 }
1139
1140 #[test]
1141 fn test_initializer_use_field_init_shorthand() {
1142 mark::check!(test_rename_field_expr_pat);
1143 check(
1144 "bar",
1145 r#"
1146struct Foo { i<|>: i32 }
1147
1148fn foo(bar: i32) -> Foo {
1149 Foo { i: bar }
1150}
1151"#,
1152 r#"
1153struct Foo { bar: i32 }
1154
1155fn foo(bar: i32) -> Foo {
1156 Foo { bar }
1157}
1158"#,
1159 );
1160 }
1161
1162 #[test]
1163 fn test_struct_field_destructure_into_shorthand() {
1164 check(
1165 "baz",
1166 r#"
1167struct Foo { i<|>: i32 }
1168
1169fn foo(foo: Foo) {
1170 let Foo { i: baz } = foo;
1171 let _ = baz;
1172}
1173"#,
1174 r#"
1175struct Foo { baz: i32 }
1176
1177fn foo(foo: Foo) {
1178 let Foo { baz } = foo;
1179 let _ = baz;
1180}
1181"#,
1182 );
1183 }
1184
1185 #[test]
1186 fn test_rename_binding_in_destructure_pat() {
1187 let expected_fixture = r#"
1188struct Foo {
1189 i: i32,
1190}
1191
1192fn foo(foo: Foo) {
1193 let Foo { i: bar } = foo;
1194 let _ = bar;
1195}
1196"#;
1197 check(
1198 "bar",
1199 r#"
1200struct Foo {
1201 i: i32,
1202}
1203
1204fn foo(foo: Foo) {
1205 let Foo { i: b } = foo;
1206 let _ = b<|>;
1207}
1208"#,
1209 expected_fixture,
1210 );
1211 check(
1212 "bar",
1213 r#"
1214struct Foo {
1215 i: i32,
1216}
1217
1218fn foo(foo: Foo) {
1219 let Foo { i } = foo;
1220 let _ = i<|>;
1221}
1222"#,
1223 expected_fixture,
1224 );
1225 }
1226
1227 #[test]
1228 fn test_rename_binding_in_destructure_param_pat() {
1229 check(
1230 "bar",
1231 r#"
1232struct Foo {
1233 i: i32
1234}
1235
1236fn foo(Foo { i }: foo) -> i32 {
1237 i<|>
1238}
1239"#,
1240 r#"
1241struct Foo {
1242 i: i32
1243}
1244
1245fn foo(Foo { i: bar }: foo) -> i32 {
1246 bar
1247}
1248"#,
1249 )
1250 }
1100} 1251}