diff options
-rw-r--r-- | crates/ide/src/references/rename.rs | 107 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 5 |
2 files changed, 73 insertions, 39 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index bbd9426b6..08f16b54d 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -183,25 +183,41 @@ fn source_edit_from_references( | |||
183 | ) -> (FileId, TextEdit) { | 183 | ) -> (FileId, TextEdit) { |
184 | let mut edit = TextEdit::builder(); | 184 | let mut edit = TextEdit::builder(); |
185 | for reference in references { | 185 | for reference in references { |
186 | match reference.name.as_name_ref() { | 186 | let (range, replacement) = match &reference.name { |
187 | // if the ranges differ then the node is inside a macro call, we can't really attempt | 187 | // if the ranges differ then the node is inside a macro call, we can't really attempt |
188 | // to make special rewrites like shorthand syntax and such, so just rename the node in | 188 | // to make special rewrites like shorthand syntax and such, so just rename the node in |
189 | // the macro input | 189 | // the macro input |
190 | Some(name_ref) if name_ref.syntax().text_range() == reference.range => { | 190 | NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == reference.range => { |
191 | let (range, replacement) = source_edit_from_name_ref(name_ref, new_name, def); | 191 | source_edit_from_name_ref(name_ref, new_name, def) |
192 | edit.replace(range, replacement); | ||
193 | } | 192 | } |
194 | _ => edit.replace(reference.range, new_name.to_owned()), | 193 | NameLike::Name(name) if name.syntax().text_range() == reference.range => { |
195 | }; | 194 | source_edit_from_name(name, new_name) |
195 | } | ||
196 | _ => None, | ||
197 | } | ||
198 | .unwrap_or_else(|| (reference.range, new_name.to_string())); | ||
199 | edit.replace(range, replacement); | ||
196 | } | 200 | } |
197 | (file_id, edit.finish()) | 201 | (file_id, edit.finish()) |
198 | } | 202 | } |
199 | 203 | ||
204 | fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { | ||
205 | if let Some(_) = ast::RecordPatField::for_field_name(name) { | ||
206 | if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | ||
207 | return Some(( | ||
208 | TextRange::empty(ident_pat.syntax().text_range().start()), | ||
209 | format!("{}: ", new_name), | ||
210 | )); | ||
211 | } | ||
212 | } | ||
213 | None | ||
214 | } | ||
215 | |||
200 | fn source_edit_from_name_ref( | 216 | fn source_edit_from_name_ref( |
201 | name_ref: &ast::NameRef, | 217 | name_ref: &ast::NameRef, |
202 | new_name: &str, | 218 | new_name: &str, |
203 | def: Definition, | 219 | def: Definition, |
204 | ) -> (TextRange, String) { | 220 | ) -> Option<(TextRange, String)> { |
205 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { | 221 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { |
206 | let rcf_name_ref = record_field.name_ref(); | 222 | let rcf_name_ref = record_field.name_ref(); |
207 | let rcf_expr = record_field.expr(); | 223 | let rcf_expr = record_field.expr(); |
@@ -215,7 +231,7 @@ fn source_edit_from_name_ref( | |||
215 | // we do not want to erase attributes hence this range start | 231 | // we do not want to erase attributes hence this range start |
216 | let s = field_name.syntax().text_range().start(); | 232 | let s = field_name.syntax().text_range().start(); |
217 | let e = record_field.syntax().text_range().end(); | 233 | let e = record_field.syntax().text_range().end(); |
218 | return (TextRange::new(s, e), new_name.to_owned()); | 234 | return Some((TextRange::new(s, e), new_name.to_owned())); |
219 | } | 235 | } |
220 | } else if init == *name_ref { | 236 | } else if init == *name_ref { |
221 | if field_name.text() == new_name { | 237 | if field_name.text() == new_name { |
@@ -224,32 +240,27 @@ fn source_edit_from_name_ref( | |||
224 | // we do not want to erase attributes hence this range start | 240 | // we do not want to erase attributes hence this range start |
225 | let s = field_name.syntax().text_range().start(); | 241 | let s = field_name.syntax().text_range().start(); |
226 | let e = record_field.syntax().text_range().end(); | 242 | let e = record_field.syntax().text_range().end(); |
227 | return (TextRange::new(s, e), new_name.to_owned()); | 243 | return Some((TextRange::new(s, e), new_name.to_owned())); |
228 | } | 244 | } |
229 | } | 245 | } |
246 | None | ||
230 | } | 247 | } |
231 | // init shorthand | 248 | // init shorthand |
232 | (None, Some(_)) => { | 249 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the |
233 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | 250 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 |
234 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | 251 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { |
235 | match def { | 252 | mark::hit!(test_rename_field_in_field_shorthand); |
236 | Definition::Field(_) => { | 253 | let s = name_ref.syntax().text_range().start(); |
237 | mark::hit!(test_rename_field_in_field_shorthand); | 254 | Some((TextRange::empty(s), format!("{}: ", new_name))) |
238 | let s = name_ref.syntax().text_range().start(); | 255 | } |
239 | return (TextRange::empty(s), format!("{}: ", new_name)); | 256 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { |
240 | } | 257 | mark::hit!(test_rename_local_in_field_shorthand); |
241 | Definition::Local(_) => { | 258 | let s = name_ref.syntax().text_range().end(); |
242 | mark::hit!(test_rename_local_in_field_shorthand); | 259 | Some((TextRange::empty(s), format!(": {}", new_name))) |
243 | let s = name_ref.syntax().text_range().end(); | ||
244 | return (TextRange::empty(s), format!(": {}", new_name)); | ||
245 | } | ||
246 | _ => {} | ||
247 | } | ||
248 | } | 260 | } |
249 | _ => {} | 261 | _ => None, |
250 | } | 262 | } |
251 | } | 263 | } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { |
252 | if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
253 | let rcf_name_ref = record_field.name_ref(); | 264 | let rcf_name_ref = record_field.name_ref(); |
254 | let rcf_pat = record_field.pat(); | 265 | let rcf_pat = record_field.pat(); |
255 | match (rcf_name_ref, rcf_pat) { | 266 | match (rcf_name_ref, rcf_pat) { |
@@ -262,13 +273,16 @@ fn source_edit_from_name_ref( | |||
262 | // we do not want to erase attributes hence this range start | 273 | // we do not want to erase attributes hence this range start |
263 | let s = field_name.syntax().text_range().start(); | 274 | let s = field_name.syntax().text_range().start(); |
264 | let e = record_field.syntax().text_range().end(); | 275 | let e = record_field.syntax().text_range().end(); |
265 | return (TextRange::new(s, e), new_name.to_owned()); | 276 | Some((TextRange::new(s, e), pat.to_string())) |
277 | } else { | ||
278 | None | ||
266 | } | 279 | } |
267 | } | 280 | } |
268 | _ => {} | 281 | _ => None, |
269 | } | 282 | } |
283 | } else { | ||
284 | None | ||
270 | } | 285 | } |
271 | (name_ref.syntax().text_range(), new_name.to_owned()) | ||
272 | } | 286 | } |
273 | 287 | ||
274 | fn rename_mod( | 288 | fn rename_mod( |
@@ -1491,7 +1505,7 @@ fn foo(i: i32) -> Foo { | |||
1491 | } | 1505 | } |
1492 | 1506 | ||
1493 | #[test] | 1507 | #[test] |
1494 | fn test_struct_field_destructure_into_shorthand() { | 1508 | fn test_struct_field_pat_into_shorthand() { |
1495 | mark::check!(test_rename_field_put_init_shorthand_pat); | 1509 | mark::check!(test_rename_field_put_init_shorthand_pat); |
1496 | check( | 1510 | check( |
1497 | "baz", | 1511 | "baz", |
@@ -1499,16 +1513,16 @@ fn foo(i: i32) -> Foo { | |||
1499 | struct Foo { i$0: i32 } | 1513 | struct Foo { i$0: i32 } |
1500 | 1514 | ||
1501 | fn foo(foo: Foo) { | 1515 | fn foo(foo: Foo) { |
1502 | let Foo { i: baz } = foo; | 1516 | let Foo { i: ref baz @ qux } = foo; |
1503 | let _ = baz; | 1517 | let _ = qux; |
1504 | } | 1518 | } |
1505 | "#, | 1519 | "#, |
1506 | r#" | 1520 | r#" |
1507 | struct Foo { baz: i32 } | 1521 | struct Foo { baz: i32 } |
1508 | 1522 | ||
1509 | fn foo(foo: Foo) { | 1523 | fn foo(foo: Foo) { |
1510 | let Foo { baz } = foo; | 1524 | let Foo { ref baz @ qux } = foo; |
1511 | let _ = baz; | 1525 | let _ = qux; |
1512 | } | 1526 | } |
1513 | "#, | 1527 | "#, |
1514 | ); | 1528 | ); |
@@ -1582,6 +1596,27 @@ fn foo(Foo { i: bar }: foo) -> i32 { | |||
1582 | } | 1596 | } |
1583 | 1597 | ||
1584 | #[test] | 1598 | #[test] |
1599 | fn test_struct_field_complex_ident_pat() { | ||
1600 | check( | ||
1601 | "baz", | ||
1602 | r#" | ||
1603 | struct Foo { i$0: i32 } | ||
1604 | |||
1605 | fn foo(foo: Foo) { | ||
1606 | let Foo { ref i } = foo; | ||
1607 | } | ||
1608 | "#, | ||
1609 | r#" | ||
1610 | struct Foo { baz: i32 } | ||
1611 | |||
1612 | fn foo(foo: Foo) { | ||
1613 | let Foo { baz: ref i } = foo; | ||
1614 | } | ||
1615 | "#, | ||
1616 | ); | ||
1617 | } | ||
1618 | |||
1619 | #[test] | ||
1585 | fn test_rename_lifetimes() { | 1620 | fn test_rename_lifetimes() { |
1586 | mark::check!(rename_lifetime); | 1621 | mark::check!(rename_lifetime); |
1587 | check( | 1622 | check( |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index b105cb0e0..307e150e9 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -3,12 +3,11 @@ | |||
3 | 3 | ||
4 | use std::fmt; | 4 | use std::fmt; |
5 | 5 | ||
6 | use ast::AttrsOwner; | ||
7 | use itertools::Itertools; | 6 | use itertools::Itertools; |
8 | use parser::SyntaxKind; | 7 | use parser::SyntaxKind; |
9 | 8 | ||
10 | use crate::{ | 9 | use crate::{ |
11 | ast::{self, support, AstNode, AstToken, NameOwner, SyntaxNode}, | 10 | ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, |
12 | SmolStr, SyntaxElement, SyntaxToken, T, | 11 | SmolStr, SyntaxElement, SyntaxToken, T, |
13 | }; | 12 | }; |
14 | 13 | ||
@@ -324,7 +323,7 @@ impl ast::RecordPatField { | |||
324 | 323 | ||
325 | pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> { | 324 | pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> { |
326 | let candidate = | 325 | let candidate = |
327 | field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?; | 326 | field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?; |
328 | match candidate.field_name()? { | 327 | match candidate.field_name()? { |
329 | NameOrNameRef::Name(name) if name == *field_name => Some(candidate), | 328 | NameOrNameRef::Name(name) if name == *field_name => Some(candidate), |
330 | _ => None, | 329 | _ => None, |