Use shorthand record syntax when renaming struct initializer field

This commit is contained in:
Lukas Wirth 2020-11-14 17:49:36 +01:00
parent 99fa139bea
commit e55a44a831
4 changed files with 68 additions and 19 deletions

View file

@ -106,7 +106,11 @@ fn find_module_at_offset(
Some(module) Some(module)
} }
fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { fn source_edit_from_reference(
sema: &Semantics<RootDatabase>,
reference: Reference,
new_name: &str,
) -> SourceFileEdit {
let mut replacement_text = String::new(); let mut replacement_text = String::new();
let file_id = reference.file_range.file_id; let file_id = reference.file_range.file_id;
let range = match reference.kind { let range = match reference.kind {
@ -122,6 +126,22 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
replacement_text.push_str(new_name); replacement_text.push_str(new_name);
TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
} }
ReferenceKind::RecordExprField => {
replacement_text.push_str(new_name);
let mut range = reference.file_range.range;
if let Some(field_expr) = syntax::algo::find_node_at_range::<ast::RecordExprField>(
sema.parse(file_id).syntax(),
reference.file_range.range,
) {
// use shorthand initializer if we were to write foo: foo
if let Some(name) = field_expr.expr().and_then(|e| e.name_ref()) {
if &name.to_string() == new_name {
range = field_expr.syntax().text_range();
}
}
}
range
}
_ => { _ => {
replacement_text.push_str(new_name); replacement_text.push_str(new_name);
reference.file_range.range reference.file_range.range
@ -170,7 +190,7 @@ fn rename_mod(
let ref_edits = refs let ref_edits = refs
.references .references
.into_iter() .into_iter()
.map(|reference| source_edit_from_reference(reference, new_name)); .map(|reference| source_edit_from_reference(sema, reference, new_name));
source_file_edits.extend(ref_edits); source_file_edits.extend(ref_edits);
Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@ -211,7 +231,7 @@ fn rename_to_self(
let mut edits = usages let mut edits = usages
.into_iter() .into_iter()
.map(|reference| source_edit_from_reference(reference, "self")) .map(|reference| source_edit_from_reference(sema, reference, "self"))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
edits.push(SourceFileEdit { edits.push(SourceFileEdit {
@ -300,7 +320,7 @@ fn rename_reference(
let edit = refs let edit = refs
.into_iter() .into_iter()
.map(|reference| source_edit_from_reference(reference, new_name)) .map(|reference| source_edit_from_reference(sema, reference, new_name))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if edit.is_empty() { if edit.is_empty() {
@ -1094,6 +1114,27 @@ impl Foo {
foo.i foo.i
} }
} }
"#,
);
}
#[test]
fn test_initializer_use_field_init_shorthand() {
check(
"bar",
r#"
struct Foo { i<|>: i32 }
fn foo(bar: i32) -> Foo {
Foo { i: bar }
}
"#,
r#"
struct Foo { bar: i32 }
fn foo(bar: i32) -> Foo {
Foo { bar }
}
"#, "#,
); );
} }

View file

@ -30,6 +30,7 @@ pub enum ReferenceKind {
FieldShorthandForField, FieldShorthandForField,
FieldShorthandForLocal, FieldShorthandForLocal,
StructLiteral, StructLiteral,
RecordExprField,
Other, Other,
} }
@ -278,12 +279,15 @@ impl<'a> FindUsages<'a> {
) -> bool { ) -> bool {
match NameRefClass::classify(self.sema, &name_ref) { match NameRefClass::classify(self.sema, &name_ref) {
Some(NameRefClass::Definition(def)) if &def == self.def => { Some(NameRefClass::Definition(def)) if &def == self.def => {
let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) let kind =
{ if name_ref.syntax().parent().and_then(ast::RecordExprField::cast).is_some() {
ReferenceKind::StructLiteral ReferenceKind::RecordExprField
} else { } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref)
ReferenceKind::Other {
}; ReferenceKind::StructLiteral
} else {
ReferenceKind::Other
};
let reference = Reference { let reference = Reference {
file_range: self.sema.original_range(name_ref.syntax()), file_range: self.sema.original_range(name_ref.syntax()),

View file

@ -22,6 +22,18 @@ impl ast::Expr {
_ => false, _ => false,
} }
} }
pub fn name_ref(&self) -> Option<ast::NameRef> {
if let ast::Expr::PathExpr(expr) = self {
let path = expr.path()?;
let segment = path.segment()?;
let name_ref = segment.name_ref()?;
if path.qualifier().is_none() {
return Some(name_ref);
}
}
None
}
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -203,15 +203,7 @@ impl ast::RecordExprField {
if let Some(name_ref) = self.name_ref() { if let Some(name_ref) = self.name_ref() {
return Some(name_ref); return Some(name_ref);
} }
if let Some(ast::Expr::PathExpr(expr)) = self.expr() { self.expr()?.name_ref()
let path = expr.path()?;
let segment = path.segment()?;
let name_ref = segment.name_ref()?;
if path.qualifier().is_none() {
return Some(name_ref);
}
}
None
} }
} }