[analysis_server] Insert new arguments before last child/children arguments
Usually when adding arguments, we'd always put them at the end. However there is convention (and a lint) for putting child/children last - so if the arguments in the last position are child/children, we should insert before them.
Change-Id: Ia0e0cd6b10f16cef30aa0cfe4fdedecc223f08c4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403584
Reviewed-by: Elliott Brooks <elliottbrooks@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_edit_argument.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_edit_argument.dart
index 70160c0..3c55664 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_edit_argument.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_edit_argument.dart
@@ -267,6 +267,22 @@
);
}
+ /// Returns whether [argument] is a [NamedExpression] with a name of
+ /// 'child' or 'children'.
+ bool _isNamedChildOrChildren(Expression argument) {
+ if (argument is! NamedExpression) {
+ return false;
+ }
+
+ return argument.name.label.name == 'child' ||
+ argument.name.label.name == 'children';
+ }
+
+ /// Returns whether [argument] is _not_ a [NamedExpression] with a name of
+ /// 'child' or 'children'.
+ bool _isNotNamedChildOrChildren(Expression argument) =>
+ !_isNamedChildOrChildren(argument);
+
/// Sends [workspaceEdit] to the client and returns `null` if applied
/// successfully or an error otherwise.
Future<ErrorOr<Null>> _sendEditToClient(WorkspaceEdit workspaceEdit) async {
@@ -367,22 +383,27 @@
parameter.isNamed && parameterName != null ? '$parameterName: ' : '';
var argumentCodeToInsert = '$prefix$newValueCode';
+ // Usually we insert at the end (after the last argument), but if the last
+ // argument is child/children we should go before it.
+ var argumentToInsertAfter = argumentList.arguments.lastWhereOrNull(
+ _isNotNamedChildOrChildren,
+ );
+
// Build the final code to insert.
var newCode = StringBuffer();
- var hasTrailingComma =
- argumentList.arguments.lastOrNull?.endToken.next?.lexeme == ',';
- if (!hasTrailingComma && argumentList.arguments.isNotEmpty) {
+ if (argumentToInsertAfter != null) {
+ // If we're being inserted after an argument, put a new comma between us.
newCode.write(', ');
- } else if (hasTrailingComma) {
- newCode.write(' ');
}
newCode.write(argumentCodeToInsert);
- if (hasTrailingComma) {
- newCode.write(',');
+ if (argumentToInsertAfter == null && argumentList.arguments.isNotEmpty) {
+ // If we're not inserted after an existing argument but there are future
+ // arguments, add a comma in between us.
+ newCode.write(', ');
}
builder.addSimpleInsertion(
- argumentList.rightParenthesis.offset,
+ argumentToInsertAfter?.end ?? argumentList.leftParenthesis.end,
newCode.toString(),
);
}
diff --git a/pkg/analysis_server/test/lsp/edit_argument_test.dart b/pkg/analysis_server/test/lsp/edit_argument_test.dart
index beef9af..5e2a997 100644
--- a/pkg/analysis_server/test/lsp/edit_argument_test.dart
+++ b/pkg/analysis_server/test/lsp/edit_argument_test.dart
@@ -273,6 +273,33 @@
);
}
+ test_named_addAfterNamed_afterChildNotAtEnd() async {
+ await _expectSimpleArgumentEdit(
+ params: '({ int? x, int? y, Widget? child })',
+ originalArgs: '(child: null, x: 1)',
+ edit: ArgumentEdit(name: 'y', newValue: 2),
+ expectedArgs: '(child: null, x: 1, y: 2)',
+ );
+ }
+
+ test_named_addAfterNamed_beforeChildAtEnd() async {
+ await _expectSimpleArgumentEdit(
+ params: '({ int? x, int? y, Widget? child })',
+ originalArgs: '(x: 1, child: null)',
+ edit: ArgumentEdit(name: 'y', newValue: 2),
+ expectedArgs: '(x: 1, y: 2, child: null)',
+ );
+ }
+
+ test_named_addAfterNamed_beforeChildrenAtEnd() async {
+ await _expectSimpleArgumentEdit(
+ params: '({ int? x, int? y, List<Widget>? children })',
+ originalArgs: '(x: 1, children: [])',
+ edit: ArgumentEdit(name: 'y', newValue: 2),
+ expectedArgs: '(x: 1, y: 2, children: [])',
+ );
+ }
+
test_named_addAfterPositional() async {
await _expectSimpleArgumentEdit(
params: '(int? x, { int? y })',
@@ -282,6 +309,33 @@
);
}
+ test_named_addAfterPositional_afterChildNotAtEnd() async {
+ await _expectSimpleArgumentEdit(
+ params: '(int? x, { int? y, Widget? child })',
+ originalArgs: '(child: null, 1)',
+ edit: ArgumentEdit(name: 'y', newValue: 2),
+ expectedArgs: '(child: null, 1, y: 2)',
+ );
+ }
+
+ test_named_addAfterPositional_beforeChildAtEnd() async {
+ await _expectSimpleArgumentEdit(
+ params: '(int? x, { int? y, Widget? child })',
+ originalArgs: '(1, child: null)',
+ edit: ArgumentEdit(name: 'y', newValue: 2),
+ expectedArgs: '(1, y: 2, child: null)',
+ );
+ }
+
+ test_named_addAfterPositional_beforeChildrenAtEnd() async {
+ await _expectSimpleArgumentEdit(
+ params: '(int? x, { int? y, List<Widget>? children })',
+ originalArgs: '(1, children: [])',
+ edit: ArgumentEdit(name: 'y', newValue: 2),
+ expectedArgs: '(1, y: 2, children: [])',
+ );
+ }
+
test_optionalPositional_addAfterPositional() async {
await _expectSimpleArgumentEdit(
params: '([int? x, int? y])',