[DAS] Fixing swap with single Widget on children

R=pquitslund@google.com

Fixes https://github.com/dart-lang/sdk/issues/55940

Change-Id: I5cad91a97dcd9fe7b550b3661e8a46a301cbb558
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387862
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Auto-Submit: Felipe Morschel <fmorschel.dev@gmail.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart
index 8be89ba..dcf824e 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart
@@ -21,10 +21,15 @@
   Future<void> swapParentAndChild(
       ChangeBuilder builder,
       InstanceCreationExpression parent,
-      InstanceCreationExpression child) async {
-    // The child must have its own child.
-    var stableChild = child.childArgument;
-    if (stableChild == null) {
+      InstanceCreationExpression child,
+      bool parentHadSingleChild) async {
+    // The child must have its own single child.
+    AstNode stableChild;
+    if (_singleChildInChildren(child) case var first?) {
+      stableChild = first;
+    } else if (child.childArgument case var childArgument?) {
+      stableChild = childArgument;
+    } else {
       return;
     }
 
@@ -68,9 +73,9 @@
         builder.writeln('(');
 
         // Write all arguments of the parent.
-        // Don't write its child.
+        // Don't write its child/children.
         for (var argument in parentArgs.arguments) {
-          if (!argument.isChildArgument) {
+          if (!argument.isChildArgument && !argument.isChildrenArgument) {
             var text = utils.getNodeText(argument);
             text = utils.replaceSourceIndent(
               text,
@@ -84,17 +89,36 @@
           }
         }
 
-        // Write the child of the "child" now, as the child of the "parent".
+        // Write the child(ren) of the "child" now, as the child(ren) of the "parent".
         {
           var text = utils.getNodeText(stableChild);
+          if (text.trim().startsWith('child: ')) {
+            text = text.substring('child: '.length);
+          } else if (text.trim().startsWith('children: ')) {
+            text = text.substring('children: '.length);
+          }
           builder.write(childIndent);
           builder.write('  ');
+          if (parentHadSingleChild) {
+            builder.write('child: ');
+          } else {
+            builder.write('children: [');
+            builder.writeln();
+            builder.write(childIndent);
+            builder.write('    ');
+          }
           builder.write(text);
           builder.writeln(',');
         }
 
         // Close the parent expression.
         builder.write(childIndent);
+        if (!parentHadSingleChild) {
+          builder.write('  ');
+          builder.write(']');
+          builder.writeln(',');
+          builder.write(childIndent);
+        }
         builder.writeln('),');
 
         // Close the child expression.
@@ -103,6 +127,20 @@
       });
     });
   }
+
+  InstanceCreationExpression? _singleChildInChildren(
+      InstanceCreationExpression parent) {
+    if (parent.childrenArgument case var childrenArgument?) {
+      if (childrenArgument.expression case ListLiteral list) {
+        if (list.elements case NodeList(length: 1, first: var first)) {
+          if (first is InstanceCreationExpression) {
+            return first;
+          }
+        }
+      }
+    }
+    return null;
+  }
 }
 
 class FlutterSwapWithChild extends FlutterParentAndChild {
@@ -117,13 +155,18 @@
     if (parent == null || !parent.isWidgetCreation) {
       return;
     }
+    var parentHasSingleChild = true;
 
-    var childArgument = parent.childArgument;
-    var child = childArgument?.expression;
+    Expression? child;
+    if (_singleChildInChildren(parent) case var first?) {
+      child = first;
+      parentHasSingleChild = false;
+    }
+    child ??= parent.childArgument?.expression;
     if (child is! InstanceCreationExpression || !child.isWidgetCreation) {
       return;
     }
 
-    await swapParentAndChild(builder, parent, child);
+    await swapParentAndChild(builder, parent, child, parentHasSingleChild);
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart
index a7b09fc..e6d1303 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart
@@ -21,13 +21,25 @@
     if (child == null || !child.isWidgetCreation) {
       return;
     }
+    var parentHadSingleChild = true;
 
+    NamedExpression? namedExpression;
+    if (child.parent case ListLiteral listLiteral) {
+      if (listLiteral.elements case NodeList(length: var length)
+          when length != 1) {
+        return;
+      }
+      if (listLiteral.parent case NamedExpression parent) {
+        namedExpression = parent;
+        parentHadSingleChild = false;
+      }
+    }
     // NamedExpression (child:), ArgumentList, InstanceCreationExpression
-    var expr = child.parent?.parent?.parent;
+    var expr = (namedExpression ?? child.parent)?.parent?.parent;
     if (expr is! InstanceCreationExpression) {
       return;
     }
 
-    await swapParentAndChild(builder, expr, child);
+    await swapParentAndChild(builder, expr, child, parentHadSingleChild);
   }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart
index f06745d..b999280 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart
@@ -115,4 +115,63 @@
   }
 }''');
   }
+
+  Future<void> test_single_child_multi() async {
+    await resolveTestCode('''
+import 'package:flutter/material.dart';
+build() {
+  return Scaffold(
+    body: /*caret*/Column(
+      children: [
+        Padding(
+          padding: const EdgeInsets.all(8),
+          child: Text('Hello'),
+        ),
+      ],
+    ),
+  );
+}
+startResize() {}
+''');
+    await assertHasAssist('''
+import 'package:flutter/material.dart';
+build() {
+  return Scaffold(
+    body: Padding(
+      padding: const EdgeInsets.all(8),
+      child: Column(
+        children: [
+          Text('Hello'),
+        ],
+      ),
+    ),
+  );
+}
+startResize() {}
+''');
+  }
+
+  Future<void> test_two_children_multi() async {
+    await resolveTestCode('''
+import 'package:flutter/material.dart';
+build() {
+  return Scaffold(
+    body: /*caret*/Column(
+      children: [
+        Padding(
+          padding: const EdgeInsets.all(8),
+          child: Text('Hello'),
+        ),
+        Padding(
+          padding: const EdgeInsets.all(8),
+          child: Text('Hello'),
+        ),
+      ],
+    ),
+  );
+}
+startResize() {}
+''');
+    await assertNoAssist();
+  }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart
index 4980dea..b19bef1 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart
@@ -162,4 +162,63 @@
 }
 ''');
   }
+
+  Future<void> test_single_child_multi() async {
+    await resolveTestCode('''
+import 'package:flutter/material.dart';
+build() {
+  return Scaffold(
+    body: Column(
+      children: [
+        /*caret*/Padding(
+          padding: const EdgeInsets.all(8),
+          child: Text('Hello'),
+        ),
+      ],
+    ),
+  );
+}
+startResize() {}
+''');
+    await assertHasAssist('''
+import 'package:flutter/material.dart';
+build() {
+  return Scaffold(
+    body: Padding(
+      padding: const EdgeInsets.all(8),
+      child: Column(
+        children: [
+          Text('Hello'),
+        ],
+      ),
+    ),
+  );
+}
+startResize() {}
+''');
+  }
+
+  Future<void> test_two_children_multi() async {
+    await resolveTestCode('''
+import 'package:flutter/material.dart';
+build() {
+  return Scaffold(
+    body: Column(
+      children: [
+        /*caret*/Padding(
+          padding: const EdgeInsets.all(8),
+          child: Text('Hello'),
+        ),
+        Padding(
+          padding: const EdgeInsets.all(8),
+          child: Text('Hello'),
+        ),
+      ],
+    ),
+  );
+}
+startResize() {}
+''');
+    await assertNoAssist();
+  }
 }