[analysis_server] Add "defaultValue" to EditableArguments API
This adds a new field "defaultValue" to the API response that contains the default value for a parameter.
Change-Id: Ief11da553d57871ad1e3fab54c731cbc98d674a4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406000
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/editable_arguments_mixin.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart
index a753a9b..29000e3 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart
@@ -152,17 +152,17 @@
return null;
}
- /// Returns the name of an enum constant prefixed with the enum name.
- String? getQualifiedEnumConstantName(FieldElement2 enumConstant) {
- var enumName = enumConstant.enclosingElement2.name3;
- var name = enumConstant.name3;
- return enumName != null && name != null ? '$enumName.$name' : null;
- }
-
/// Returns a list of the constants of an enum constant prefixed with the enum
/// name.
List<String> getQualifiedEnumConstantNames(EnumElement2 element3) =>
element3.constants2.map(getQualifiedEnumConstantName).nonNulls.toList();
+
+ /// Returns the name of an enum constant prefixed with the enum name.
+ static String? getQualifiedEnumConstantName(FieldElement2 enumConstant) {
+ var enumName = enumConstant.enclosingElement2.name3;
+ var name = enumConstant.name3;
+ return enumName != null && name != null ? '$enumName.$name' : null;
+ }
}
extension on InvocationExpressionImpl {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_editable_arguments.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_editable_arguments.dart
index e331b6c..5145f1b 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_editable_arguments.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_editable_arguments.dart
@@ -167,12 +167,14 @@
}) {
var valueExpression =
argument is NamedExpression ? argument.expression : argument;
+ var hasArgument = valueExpression != null;
// Lazily compute the values if we will use this parameter/argument.
late var values = _getValues(parameter, valueExpression);
String? type;
Object? value;
+ Object? defaultValue;
List<String>? options;
// Determine whether a value for this parameter is editable.
@@ -186,40 +188,39 @@
if (parameter.type.isDartCoreDouble) {
type = 'double';
value =
- (values.argumentValue ?? values.parameterValue)?.toDoubleValue() ??
- (values.argumentValue ?? values.parameterValue)?.toIntValue();
+ values.argumentValue?.toDoubleValue() ??
+ values.argumentValue?.toIntValue();
+ defaultValue =
+ values.parameterValue?.toDoubleValue() ??
+ values.parameterValue?.toIntValue();
} else if (parameter.type.isDartCoreInt) {
type = 'int';
- value = (values.argumentValue ?? values.parameterValue)?.toIntValue();
+ value = values.argumentValue?.toIntValue();
+ defaultValue = values.parameterValue?.toIntValue();
} else if (parameter.type.isDartCoreBool) {
type = 'bool';
- value = (values.argumentValue ?? values.parameterValue)?.toBoolValue();
+ value = values.argumentValue?.toBoolValue();
+ defaultValue = values.parameterValue?.toBoolValue();
} else if (parameter.type.isDartCoreString) {
type = 'string';
- value = (values.argumentValue ?? values.parameterValue)?.toStringValue();
+ value = values.argumentValue?.toStringValue();
+ defaultValue = values.parameterValue?.toStringValue();
} else if (parameter.type case InterfaceType(:EnumElement2 element3)) {
type = 'enum';
options = getQualifiedEnumConstantNames(element3);
-
- // Try to match the argument value up with the enum.
- var valueObject = values.argumentValue ?? values.parameterValue;
- if (valueObject?.type case InterfaceType(
- element3: EnumElement2 valueElement,
- ) when element3 == valueElement) {
- var index = valueObject?.getField('index')?.toIntValue();
- if (index != null) {
- var enumConstant = element3.constants2.elementAtOrNull(index);
- if (enumConstant != null) {
- value = getQualifiedEnumConstantName(enumConstant);
- }
- }
- }
+ value = values.argumentValue?.toEnumStringValue(element3);
+ defaultValue = values.parameterValue?.toEnumStringValue(element3);
} else {
// TODO(dantup): Determine which parameters we don't include (such as
// Widgets) and which we include just without values.
return null;
}
+ // If no argument is present, we always populate "value" with the default.
+ if (!hasArgument) {
+ value = defaultValue;
+ }
+
var isEditable = notEditableReason == null;
// Compute a displayValue.
@@ -246,6 +247,7 @@
displayValue: displayValue,
options: options,
isDefault: values.isDefault,
+ defaultValue: defaultValue,
hasArgument: valueExpression != null,
isRequired: parameter.isRequired,
isNullable:
@@ -255,3 +257,23 @@
);
}
}
+
+extension on DartObject? {
+ Object? toEnumStringValue(EnumElement2 element3) {
+ var valueObject = this;
+ if (valueObject?.type case InterfaceType(
+ element3: EnumElement2 valueElement,
+ ) when element3 == valueElement) {
+ var index = valueObject?.getField('index')?.toIntValue();
+ if (index != null) {
+ var enumConstant = element3.constants2.elementAtOrNull(index);
+ if (enumConstant != null) {
+ return EditableArgumentsMixin.getQualifiedEnumConstantName(
+ enumConstant,
+ );
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart b/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
index e98d02b..44fa05c 100644
--- a/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
+++ b/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
@@ -75,6 +75,7 @@
Object? displayValue = anything,
Object? hasArgument = anything,
Object? isDefault = anything,
+ Object? defaultValue = anything,
Object? isRequired = anything,
Object? isNullable = anything,
Object? isEditable = anything,
@@ -88,6 +89,7 @@
.having((arg) => arg.displayValue, 'displayValue', displayValue)
.having((arg) => arg.hasArgument, 'hasArgument', hasArgument)
.having((arg) => arg.isDefault, 'isDefault', isDefault)
+ .having((arg) => arg.defaultValue, 'defaultValue', defaultValue)
.having((arg) => arg.isRequired, 'isRequired', isRequired)
.having((arg) => arg.isNullable, 'isNullable', isNullable)
.having((arg) => arg.isEditable, 'isEditable', isEditable)
@@ -125,6 +127,104 @@
);
}
+ test_defaultValue_named_default() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget({int? a = 1});
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(a: 1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: 1)));
+ }
+
+ test_defaultValue_named_default_constantVariable() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ static const constantOne = 1;
+
+ const MyWidget({int? a = constantOne});
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(a: 1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: 1)));
+ }
+
+ test_defaultValue_named_default_null() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget({int? a = null});
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(a: 1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: null)));
+ }
+
+ test_defaultValue_named_noDefault() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget({int? a});
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(a: 1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: null)));
+ }
+
+ test_defaultValue_named_required_noDefault() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget({required int? a});
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(a: 1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: null)));
+ }
+
+ test_defaultValue_positional() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(int a);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: null)));
+ }
+
+ test_defaultValue_positional_optional_default() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget([int? a = 1]);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: 1)));
+ }
+
+ test_defaultValue_positional_optional_noDefault() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget([int? a]);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1);
+}
+''');
+ expect(result, hasArg(isArg('a', defaultValue: null)));
+ }
+
test_documentation_literal() async {
var result = await getEditableArgumentsFor('''
class MyWidget extends StatelessWidget {
@@ -977,14 +1077,27 @@
result,
hasArgs(
orderedEquals([
- isArg('supplied', type: 'bool', value: false, isDefault: false),
+ isArg(
+ 'supplied',
+ type: 'bool',
+ value: false,
+ isDefault: false,
+ defaultValue: true,
+ ),
isArg(
'suppliedAsDefault',
type: 'bool',
value: true,
isDefault: true,
+ defaultValue: true,
),
- isArg('notSupplied', type: 'bool', value: true, isDefault: true),
+ isArg(
+ 'notSupplied',
+ type: 'bool',
+ value: true,
+ isDefault: true,
+ defaultValue: true,
+ ),
]),
),
);
@@ -1054,14 +1167,27 @@
result,
hasArgs(
orderedEquals([
- isArg('supplied', type: 'double', value: 2.0, isDefault: false),
+ isArg(
+ 'supplied',
+ type: 'double',
+ value: 2.0,
+ isDefault: false,
+ defaultValue: 1.0,
+ ),
isArg(
'suppliedAsDefault',
type: 'double',
value: 1.0,
isDefault: true,
+ defaultValue: 1.0,
),
- isArg('notSupplied', type: 'double', value: 1.0, isDefault: true),
+ isArg(
+ 'notSupplied',
+ type: 'double',
+ value: 1.0,
+ isDefault: true,
+ defaultValue: 1.0,
+ ),
]),
),
);
@@ -1167,6 +1293,7 @@
type: 'enum',
value: 'E.two',
isDefault: false,
+ defaultValue: 'E.one',
options: optionsMatcher,
),
isArg(
@@ -1174,6 +1301,7 @@
type: 'enum',
value: 'E.one',
isDefault: true,
+ defaultValue: 'E.one',
options: optionsMatcher,
),
isArg(
@@ -1181,6 +1309,7 @@
type: 'enum',
value: 'E.one',
isDefault: true,
+ defaultValue: 'E.one',
options: optionsMatcher,
),
]),
@@ -1260,9 +1389,27 @@
result,
hasArgs(
orderedEquals([
- isArg('supplied', type: 'int', value: 2, isDefault: false),
- isArg('suppliedAsDefault', type: 'int', value: 1, isDefault: true),
- isArg('notSupplied', type: 'int', value: 1, isDefault: true),
+ isArg(
+ 'supplied',
+ type: 'int',
+ value: 2,
+ isDefault: false,
+ defaultValue: 1,
+ ),
+ isArg(
+ 'suppliedAsDefault',
+ type: 'int',
+ value: 1,
+ isDefault: true,
+ defaultValue: 1,
+ ),
+ isArg(
+ 'notSupplied',
+ type: 'int',
+ value: 1,
+ isDefault: true,
+ defaultValue: 1,
+ ),
]),
),
);
@@ -1332,14 +1479,27 @@
result,
hasArgs(
orderedEquals([
- isArg('supplied', type: 'string', value: 'b', isDefault: false),
+ isArg(
+ 'supplied',
+ type: 'string',
+ value: 'b',
+ isDefault: false,
+ defaultValue: 'a',
+ ),
isArg(
'suppliedAsDefault',
type: 'string',
value: 'a',
isDefault: true,
+ defaultValue: 'a',
),
- isArg('notSupplied', type: 'string', value: 'a', isDefault: true),
+ isArg(
+ 'notSupplied',
+ type: 'string',
+ value: 'a',
+ isDefault: true,
+ defaultValue: 'a',
+ ),
]),
),
);
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index 6134d6a..efc6401 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -468,6 +468,16 @@
'because there is no argument or because it is explicitly provided '
'as the same value.',
),
+ Field(
+ name: 'defaultValue',
+ type: TypeReference.LspAny,
+ allowsNull: false,
+ allowsUndefined: true,
+ comment:
+ 'The default value for this parameter if no argument is supplied. '
+ 'Setting the argument to this value does not remove it from the '
+ 'argument list.',
+ ),
field(
'displayValue',
type: 'string',
diff --git a/third_party/pkg/language_server_protocol/lib/protocol_custom_generated.dart b/third_party/pkg/language_server_protocol/lib/protocol_custom_generated.dart
index 39db11a..78a8b9f 100644
--- a/third_party/pkg/language_server_protocol/lib/protocol_custom_generated.dart
+++ b/third_party/pkg/language_server_protocol/lib/protocol_custom_generated.dart
@@ -1437,6 +1437,10 @@
EditableArgument.fromJson,
);
+ /// The default value for this parameter if no argument is supplied. Setting
+ /// the argument to this value does not remove it from the argument list.
+ final Object? defaultValue;
+
/// A string that can be displayed to indicate the value for this argument.
/// This will be populated in cases where the source code is not literally the
/// same as the value field, for example an expression or named constant.
@@ -1482,6 +1486,7 @@
/// and displayValue can be shown as the current value instead.
final Object? value;
EditableArgument({
+ this.defaultValue,
this.displayValue,
required this.hasArgument,
required this.isDefault,
@@ -1496,6 +1501,7 @@
});
@override
int get hashCode => Object.hash(
+ defaultValue,
displayValue,
hasArgument,
isDefault,
@@ -1513,6 +1519,7 @@
bool operator ==(Object other) {
return other is EditableArgument &&
other.runtimeType == EditableArgument &&
+ defaultValue == other.defaultValue &&
displayValue == other.displayValue &&
hasArgument == other.hasArgument &&
isDefault == other.isDefault &&
@@ -1529,6 +1536,9 @@
@override
Map<String, Object?> toJson() {
var result = <String, Object?>{};
+ if (defaultValue != null) {
+ result['defaultValue'] = defaultValue;
+ }
if (displayValue != null) {
result['displayValue'] = displayValue;
}
@@ -1601,6 +1611,8 @@
}
static EditableArgument fromJson(Map<String, Object?> json) {
+ final defaultValueJson = json['defaultValue'];
+ final defaultValue = defaultValueJson;
final displayValueJson = json['displayValue'];
final displayValue = displayValueJson as String?;
final hasArgumentJson = json['hasArgument'];
@@ -1625,6 +1637,7 @@
final valueJson = json['value'];
final value = valueJson;
return EditableArgument(
+ defaultValue: defaultValue,
displayValue: displayValue,
hasArgument: hasArgument,
isDefault: isDefault,