Index: pkg/analysis_server/lib/src/utilities/change_builder_dart.dart |
diff --git a/pkg/analysis_server/lib/src/utilities/change_builder_dart.dart b/pkg/analysis_server/lib/src/utilities/change_builder_dart.dart |
index 422549f16c0e5295629ca05bedcb78e30a461801..f9dfbef4f734157dfb2d707650a6639cf137a1b0 100644 |
--- a/pkg/analysis_server/lib/src/utilities/change_builder_dart.dart |
+++ b/pkg/analysis_server/lib/src/utilities/change_builder_dart.dart |
@@ -15,6 +15,7 @@ import 'package:analyzer/dart/ast/token.dart'; |
import 'package:analyzer/dart/element/element.dart'; |
import 'package:analyzer/dart/element/type.dart'; |
import 'package:analyzer/src/dart/analysis/driver.dart'; |
+import 'package:analyzer/src/generated/resolver.dart'; |
import 'package:analyzer/src/generated/source.dart'; |
import 'package:analyzer/src/generated/utilities_dart.dart'; |
@@ -61,16 +62,21 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
DartFileEditBuilderImpl get dartFileEditBuilder => fileEditBuilder; |
@override |
+ LinkedEditBuilderImpl createLinkedEditBuilder() { |
+ return new DartLinkedEditBuilderImpl(this); |
+ } |
+ |
+ @override |
void writeClassDeclaration(String name, |
{Iterable<DartType> interfaces, |
bool isAbstract: false, |
void memberWriter(), |
Iterable<DartType> mixins, |
String nameGroupName, |
- DartType superclass}) { |
+ DartType superclass, |
+ String superclassGroupName: DartEditBuilder.SUPERCLASS_GROUP_ID}) { |
// TODO(brianwilkerson) Add support for type parameters, probably as a |
// parameterWriter parameter. |
- // TODO(brianwilkerson) Add a superclassGroupName parameter. |
if (isAbstract) { |
write(Keyword.ABSTRACT.syntax); |
write(' '); |
@@ -85,7 +91,7 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
if (superclass != null) { |
write(' extends '); |
- writeType(superclass, groupName: DartEditBuilder.SUPERCLASS_GROUP_ID); |
+ writeType(superclass, groupName: superclassGroupName); |
} else if (mixins != null && mixins.isNotEmpty) { |
write(' extends Object '); |
} |
@@ -132,13 +138,11 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
} |
} |
+ write('('); |
if (argumentList != null) { |
writeParametersMatchingArguments(argumentList); |
- } else { |
- write('()'); |
} |
- writeln(' {'); |
- write(' }'); |
+ writeln(');'); |
} |
@override |
@@ -183,6 +187,46 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
@override |
+ void writeFunctionDeclaration(String name, |
+ {void bodyWriter(), |
+ bool isStatic: false, |
+ String nameGroupName, |
+ void parameterWriter(), |
+ DartType returnType, |
+ String returnTypeGroupName}) { |
+ if (isStatic) { |
+ write(Keyword.STATIC.syntax); |
+ write(' '); |
+ } |
+ if (returnType != null) { |
+ writeType(returnType, groupName: returnTypeGroupName); |
+ write(' '); |
+ } |
+ if (nameGroupName != null) { |
+ addLinkedEdit(nameGroupName, (LinkedEditBuilder builder) { |
+ write(name); |
+ }); |
+ } else { |
+ write(name); |
+ } |
+ write('('); |
+ if (parameterWriter != null) { |
+ parameterWriter(); |
+ } |
+ write(')'); |
+ if (bodyWriter == null) { |
+ if (returnType != null) { |
+ write(' => null;'); |
+ } else { |
+ write(' {}'); |
+ } |
+ } else { |
+ write(' '); |
+ bodyWriter(); |
+ } |
+ } |
+ |
+ @override |
void writeGetterDeclaration(String name, |
{void bodyWriter(), |
bool isStatic: false, |
@@ -215,6 +259,46 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
@override |
+ void writeLocalVariableDeclaration(String name, |
+ {void initializerWriter(), |
+ bool isConst: false, |
+ bool isFinal: false, |
+ String nameGroupName, |
+ DartType type, |
+ String typeGroupName}) { |
+ bool typeRequired = true; |
+ if (isConst) { |
+ write(Keyword.CONST.syntax); |
+ typeRequired = false; |
+ } else if (isFinal) { |
+ write(Keyword.FINAL.syntax); |
+ typeRequired = false; |
+ } |
+ if (type != null) { |
+ if (!typeRequired) { |
+ // The type is required unless we're written a keyword. |
+ write(' '); |
+ } |
+ writeType(type, groupName: typeGroupName); |
+ } else if (typeRequired) { |
+ write(Keyword.VAR.syntax); |
+ } |
+ write(' '); |
+ if (nameGroupName != null) { |
+ addLinkedEdit(nameGroupName, (LinkedEditBuilder builder) { |
+ write(name); |
+ }); |
+ } else { |
+ write(name); |
+ } |
+ if (initializerWriter != null) { |
+ write(' = '); |
+ initializerWriter(); |
+ } |
+ write(';'); |
+ } |
+ |
+ @override |
void writeOverrideOfInheritedMember(ExecutableElement member) { |
// prepare environment |
String prefix = utils.getIndent(1); |
@@ -272,6 +356,29 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
@override |
+ void writeParameterMatchingArgument( |
+ Expression argument, int index, Set<String> usedNames) { |
+ // append type name |
+ DartType type = argument.bestType; |
+ if (writeType(type, addSupertypeProposals: true, groupName: 'TYPE$index')) { |
+ write(' '); |
+ } |
+ // append parameter name |
+ if (argument is NamedExpression) { |
+ write(argument.name.label.name); |
+ } else { |
+ List<String> suggestions = |
+ _getParameterNameSuggestions(usedNames, type, argument, index); |
+ String favorite = suggestions[0]; |
+ usedNames.add(favorite); |
+ addLinkedEdit('PARAM$index', (LinkedEditBuilder builder) { |
+ write(favorite); |
+ builder.addSuggestions(LinkedEditSuggestionKind.PARAMETER, suggestions); |
+ }); |
+ } |
+ } |
+ |
+ @override |
void writeParameters(Iterable<ParameterElement> parameters) { |
write('('); |
bool sawNamed = false; |
@@ -319,42 +426,26 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
@override |
- void writeParametersMatchingArguments(ArgumentList arguments) { |
- Set<String> excluded = new Set(); |
- bool namedFound = false; |
- write('('); |
- List<Expression> argumentList = arguments.arguments; |
- for (int i = 0; i < argumentList.length; i++) { |
- Expression argument = argumentList[i]; |
- DartType type = argument.bestType; |
- List<String> suggestions = |
- _getParameterNameSuggestions(excluded, type, argument, i); |
- String favorite = suggestions[0]; |
- // append separator |
+ void writeParametersMatchingArguments(ArgumentList argumentList) { |
+ // TODO(brianwilkerson) Handle the case when there are required parameters |
+ // after named parameters. |
+ Set<String> usedNames = new Set<String>(); |
+ List<Expression> arguments = argumentList.arguments; |
+ bool hasNamedParameters = false; |
+ for (int i = 0; i < arguments.length; i++) { |
+ Expression argument = arguments[i]; |
if (i > 0) { |
write(', '); |
} |
- if (argument is NamedExpression) { |
- if (!namedFound) { |
- namedFound = true; |
- write('['); |
- } |
- favorite = argument.name.label.name; |
+ if (argument is NamedExpression && !hasNamedParameters) { |
+ hasNamedParameters = true; |
+ write('{'); |
} |
- // append type name |
- writeType(type, addSupertypeProposals: true, groupName: 'TYPE$i'); |
- write(' '); |
- // append parameter name |
- excluded.add(favorite); |
- addLinkedEdit('ARG$i', (LinkedEditBuilder builder) { |
- builder.write(favorite); |
- builder.addSuggestions(LinkedEditSuggestionKind.PARAMETER, suggestions); |
- }); |
+ writeParameterMatchingArgument(argument, i, usedNames); |
} |
- if (namedFound) { |
- write(']'); |
+ if (hasNamedParameters) { |
+ write('}'); |
} |
- write(')'); |
} |
@override |
@@ -372,19 +463,23 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
if (type != null && !type.isDynamic) { |
String typeSource = |
utils.getTypeSource(type, dartFileEditBuilder.librariesToImport); |
- if (groupName != null) { |
- addLinkedEdit(groupName, (LinkedEditBuilder builder) { |
+ if (typeSource != 'dynamic') { |
+ if (groupName != null) { |
+ addLinkedEdit(groupName, (LinkedEditBuilder builder) { |
+ write(typeSource); |
+ if (addSupertypeProposals) { |
+ _addSuperTypeProposals(builder, type, new Set<DartType>()); |
+ } |
+ }); |
+ } else { |
write(typeSource); |
- if (addSupertypeProposals) { |
- _addSuperTypeProposals(builder, type, new Set<DartType>()); |
- } |
- }); |
- } else { |
- write(typeSource); |
+ } |
+ return true; |
} |
- return true; |
- } else if (required) { |
+ } |
+ if (required) { |
write(Keyword.VAR.syntax); |
+ return true; |
} |
return false; |
} |
@@ -427,20 +522,21 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
} |
/** |
- * Return a list containing the suggested names for a parmeter with the given |
+ * Return a list containing the suggested names for a parameter with the given |
* [type] whose value in one location is computed by the given [expression]. |
* The list will not contain any names in the set of [excluded] names. The |
* [index] is the index of the argument, used to create a name if no better |
* name could be created. The first name in the list will be the best name. |
*/ |
List<String> _getParameterNameSuggestions( |
- Set<String> excluded, DartType type, Expression expression, int index) { |
+ Set<String> usedNames, DartType type, Expression expression, int index) { |
List<String> suggestions = |
- getVariableNameSuggestionsForExpression(type, expression, excluded); |
+ getVariableNameSuggestionsForExpression(type, expression, usedNames); |
if (suggestions.length != 0) { |
return suggestions; |
} |
- return <String>['arg$index']; |
+ // TODO(brianwilkerson) Verify that the name below is not in the set of used names. |
+ return <String>['param$index']; |
} |
} |
@@ -477,7 +573,96 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl |
} |
@override |
+ void convertFunctionFromSyncToAsync( |
+ FunctionBody body, TypeProvider typeProvider) { |
+ if (body == null && body.keyword != null) { |
+ throw new ArgumentError( |
+ 'The function must have a synchronous, non-generator body.'); |
+ } |
+ addInsertion(body.offset, (EditBuilder builder) { |
+ builder.write('async '); |
+ }); |
+ _replaceReturnTypeWithFuture(body, typeProvider); |
+ } |
+ |
+ @override |
DartEditBuilderImpl createEditBuilder(int offset, int length) { |
return new DartEditBuilderImpl(this, offset, length); |
} |
+ |
+ @override |
+ void replaceTypeWithFuture( |
+ TypeAnnotation typeAnnotation, TypeProvider typeProvider) { |
+ InterfaceType futureType = typeProvider.futureType; |
+ // |
+ // Check whether the type needs to be replaced. |
+ // |
+ DartType type = typeAnnotation?.type; |
+ if (type == null || |
+ type.isDynamic || |
+ type is InterfaceType && type.element == futureType.element) { |
+ return; |
+ } |
+ // prepare code for the types |
+ String futureTypeCode = utils.getTypeSource(futureType, librariesToImport); |
+ String nodeCode = utils.getNodeText(typeAnnotation); |
+ // wrap the existing type with Future |
+ String returnTypeCode = |
+ nodeCode == 'void' ? futureTypeCode : '$futureTypeCode<$nodeCode>'; |
+ addReplacement(typeAnnotation.offset, typeAnnotation.length, |
+ (EditBuilder builder) { |
+ builder.write(returnTypeCode); |
+ }); |
+ } |
+ |
+ /** |
+ * Create an edit to replace the return type of the innermost function |
+ * containing the given [node] with the type `Future`. The [typeProvider] is |
+ * used to check the current return type, because if it is already `Future` no |
+ * edit will be added. |
+ */ |
+ void _replaceReturnTypeWithFuture(AstNode node, TypeProvider typeProvider) { |
+ while (node != null) { |
+ node = node.parent; |
+ if (node is FunctionDeclaration) { |
+ replaceTypeWithFuture(node.returnType, typeProvider); |
+ return; |
+ } else if (node is MethodDeclaration) { |
+ replaceTypeWithFuture(node.returnType, typeProvider); |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
+/** |
+ * A [LinkedEditBuilder] used to build linked edits for Dart files. |
+ * |
+ * Clients may not extend, implement or mix-in this class. |
+ */ |
+class DartLinkedEditBuilderImpl extends LinkedEditBuilderImpl |
+ implements DartLinkedEditBuilder { |
+ /** |
+ * Initialize a newly created linked edit builder. |
+ */ |
+ DartLinkedEditBuilderImpl(EditBuilderImpl editBuilder) : super(editBuilder); |
+ |
+ @override |
+ void addSuperTypesAsSuggestions(DartType type) { |
+ _addSuperTypesAsSuggestions(type, new Set<DartType>()); |
+ } |
+ |
+ /** |
+ * Safely implement [addSuperTypesAsSuggestions] by using the set of |
+ * [alreadyAdded] types to prevent infinite loops. |
+ */ |
+ void _addSuperTypesAsSuggestions(DartType type, Set<DartType> alreadyAdded) { |
+ if (type is InterfaceType && alreadyAdded.add(type)) { |
+ addSuggestion(LinkedEditSuggestionKind.TYPE, type.displayName); |
+ _addSuperTypesAsSuggestions(type.superclass, alreadyAdded); |
+ for (InterfaceType interfaceType in type.interfaces) { |
+ _addSuperTypesAsSuggestions(interfaceType, alreadyAdded); |
+ } |
+ } |
+ } |
} |