about summary refs log tree commit diff
diff options
context:
space:
mode:
authordfireBird <me@dfirebird.dev>2024-02-24 00:49:52 +0530
committerdfireBird <me@dfirebird.dev>2024-02-24 11:59:48 +0530
commit6f4354f6adc37ed889491cbfe6ed3e730e55fa91 (patch)
tree1528355227778d38251117d3e907a482be13506e
parent03b3cb6be9f21c082f4206b35c7fe7f291c94eaa (diff)
downloadrust-6f4354f6adc37ed889491cbfe6ed3e730e55fa91.tar.gz
rust-6f4354f6adc37ed889491cbfe6ed3e730e55fa91.zip
add assist for filling fields by replacing ellipsis in record syntax
-rw-r--r--crates/ide-assists/src/handlers/fill_record_pattern_fields.rs236
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs21
3 files changed, 259 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs b/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
new file mode 100644
index 00000000000..e7b27c98eb2
--- /dev/null
+++ b/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
@@ -0,0 +1,236 @@
+use syntax::{
+    ast::{self, make},
+    AstNode,
+};
+
+use crate::{AssistContext, AssistId, Assists};
+
+// Assist: fill_record_pattern_fields
+//
+// Fills fields by replacing rest pattern in record patterns.
+//
+// ```
+// struct Bar { y: Y, z: Z }
+//
+// fn foo(bar: Bar) {
+//     let Bar { ..$0 } = bar;
+// }
+// ```
+// ->
+// ```
+// struct Bar { y: Y, z: Z }
+//
+// fn foo(bar: Bar) {
+//     let Bar { y, z  } = bar;
+// }
+// ```
+pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let record_pat = ctx.find_node_at_offset::<ast::RecordPat>()?;
+
+    let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
+    if !ellipsis.syntax().text_range().contains_inclusive(ctx.offset()) {
+        return None;
+    }
+
+    let target_range = ellipsis.syntax().text_range();
+
+    let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
+
+    let old_field_list = record_pat.record_pat_field_list()?;
+    let new_field_list = make::record_pat_field_list(old_field_list.fields()).clone_for_update();
+    for (f, _) in missing_fields.iter() {
+        let field =
+            make::record_pat_field_shorthand(make::name_ref(&f.name(ctx.sema.db).to_smol_str()));
+        new_field_list.add_field(field.clone_for_update());
+    }
+
+    let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
+    if old_range.file_id != ctx.file_id() {
+        return None;
+    }
+
+    acc.add(
+        AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite),
+        "Fill structure fields",
+        target_range,
+        move |builder| builder.replace_ast(old_field_list, new_field_list),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    #[test]
+    fn fill_fields_enum_with_only_ellipsis() {
+        check_assist(
+            fill_record_pattern_fields,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::B{ ..$0 } => true,
+    };
+}
+"#,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::B{ y, z  } => true,
+    };
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn fill_fields_enum_with_fields() {
+        check_assist(
+            fill_record_pattern_fields,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::B{ y, ..$0 } => true,
+    };
+}
+"#,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::B{ y, z } => true,
+    };
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn fill_fields_struct_with_only_ellipsis() {
+        check_assist(
+            fill_record_pattern_fields,
+            r#"
+struct Bar {
+    y: Y,
+    z: Z,
+}
+
+fn foo(bar: Bar) {
+    let Bar { ..$0 } = bar;
+}
+"#,
+            r#"
+struct Bar {
+    y: Y,
+    z: Z,
+}
+
+fn foo(bar: Bar) {
+    let Bar { y, z  } = bar;
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn fill_fields_struct_with_fields() {
+        check_assist(
+            fill_record_pattern_fields,
+            r#"
+struct Bar {
+    y: Y,
+    z: Z,
+}
+
+fn foo(bar: Bar) {
+    let Bar { y, ..$0 } = bar;
+}
+"#,
+            r#"
+struct Bar {
+    y: Y,
+    z: Z,
+}
+
+fn foo(bar: Bar) {
+    let Bar { y, z } = bar;
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn not_applicable_when_not_in_ellipsis() {
+        check_assist_not_applicable(
+            fill_record_pattern_fields,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::B{..}$0 => true,
+    };
+}
+"#,
+        );
+        check_assist_not_applicable(
+            fill_record_pattern_fields,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::B$0{..} => true,
+    };
+}
+"#,
+        );
+        check_assist_not_applicable(
+            fill_record_pattern_fields,
+            r#"
+enum Foo {
+    A(X),
+    B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+    match foo {
+        Foo::A(_) => false,
+        Foo::$0B{..} => true,
+    };
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index dcc89014b95..5814c3b81e4 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -137,6 +137,7 @@ mod handlers {
     mod extract_struct_from_enum_variant;
     mod extract_type_alias;
     mod extract_variable;
+    mod fill_record_pattern_fields;
     mod fix_visibility;
     mod flip_binexpr;
     mod flip_comma;
@@ -254,6 +255,7 @@ mod handlers {
             extract_expressions_from_format_string::extract_expressions_from_format_string,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_type_alias::extract_type_alias,
+            fill_record_pattern_fields::fill_record_pattern_fields,
             fix_visibility::fix_visibility,
             flip_binexpr::flip_binexpr,
             flip_comma::flip_comma,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 268ba3225b6..82d05f39202 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -910,6 +910,27 @@ fn main() {
 }
 
 #[test]
+fn doctest_fill_record_pattern_fields() {
+    check_doc_test(
+        "fill_record_pattern_fields",
+        r#####"
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { ..$0 } = bar;
+}
+"#####,
+        r#####"
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+    let Bar { y, z  } = bar;
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_fix_visibility() {
     check_doc_test(
         "fix_visibility",