| Index: pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart | 
| diff --git a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..b4a6aed91064c16b618fa547f893038d3426ee02 | 
| --- /dev/null | 
| +++ b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart | 
| @@ -0,0 +1,244 @@ | 
| +// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file | 
| +// for details. All rights reserved. Use of this source code is governed by a | 
| +// BSD-style license that can be found in the LICENSE file. | 
| + | 
| +library services.completion.contributor.dart.arglist; | 
| + | 
| +import 'dart:async'; | 
| + | 
| +import 'package:analysis_server/src/protocol_server.dart' | 
| +    hide Element, ElementKind; | 
| +import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart'; | 
| +import 'package:analyzer/src/generated/ast.dart'; | 
| +import 'package:analyzer/src/generated/element.dart'; | 
| +import 'package:analyzer/src/generated/utilities_dart.dart'; | 
| + | 
| +/** | 
| + * Determine the number of arguments. | 
| + */ | 
| +int _argCount(DartCompletionRequest request) { | 
| +  AstNode node = request.target.containingNode; | 
| +  if (node is ArgumentList) { | 
| +    return node.arguments.length; | 
| +  } | 
| +  return 0; | 
| +} | 
| + | 
| +String _getParamType(ParameterElement param) { | 
| +  DartType type = param.type; | 
| +  if (type != null) { | 
| +    return type.displayName; | 
| +  } | 
| +  return 'dynamic'; | 
| +} | 
| + | 
| +/** | 
| + * If the containing [node] is an argument list | 
| + * or named expression in an argument list | 
| + * then return the simple identifier for the method, constructor, or annotation | 
| + * to which the argument list is associated | 
| + */ | 
| +SimpleIdentifier _getTargetId(AstNode node) { | 
| +  if (node is NamedExpression) { | 
| +    return _getTargetId(node.parent); | 
| +  } | 
| +  if (node is ArgumentList) { | 
| +    AstNode parent = node.parent; | 
| +    if (parent is MethodInvocation) { | 
| +      return parent.methodName; | 
| +    } | 
| +    if (parent is InstanceCreationExpression) { | 
| +      ConstructorName constructorName = parent.constructorName; | 
| +      if (constructorName != null) { | 
| +        if (constructorName.name != null) { | 
| +          return constructorName.name; | 
| +        } | 
| +        Identifier typeName = constructorName.type.name; | 
| +        if (typeName is SimpleIdentifier) { | 
| +          return typeName; | 
| +        } | 
| +        if (typeName is PrefixedIdentifier) { | 
| +          return typeName.identifier; | 
| +        } | 
| +      } | 
| +    } | 
| +    if (parent is Annotation) { | 
| +      return parent.constructorName ?? parent.name; | 
| +    } | 
| +  } | 
| +  return null; | 
| +} | 
| + | 
| +/** | 
| + * Determine if the completion target is at the end of the list of arguments. | 
| + */ | 
| +bool _isAppendingToArgList(DartCompletionRequest request) { | 
| +  AstNode node = request.target.containingNode; | 
| +  if (node is ArgumentList) { | 
| +    var entity = request.target.entity; | 
| +    if (entity == node.rightParenthesis) { | 
| +      return true; | 
| +    } | 
| +    if (node.arguments.length > 0 && node.arguments.last == entity) { | 
| +      return entity is SimpleIdentifier; | 
| +    } | 
| +  } | 
| +  return false; | 
| +} | 
| + | 
| +/** | 
| + * Determine if the completion target is an emtpy argument list. | 
| + */ | 
| +bool _isEmptyArgList(DartCompletionRequest request) { | 
| +  AstNode node = request.target.containingNode; | 
| +  return node is ArgumentList && | 
| +      node.leftParenthesis.next == node.rightParenthesis; | 
| +} | 
| + | 
| +/** | 
| + * Return a collection of currently specified named arguments | 
| + */ | 
| +Iterable<String> _namedArgs(DartCompletionRequest request) { | 
| +  AstNode node = request.target.containingNode; | 
| +  List<String> namedArgs = new List<String>(); | 
| +  if (node is ArgumentList) { | 
| +    for (Expression arg in node.arguments) { | 
| +      if (arg is NamedExpression) { | 
| +        namedArgs.add(arg.name.label.name); | 
| +      } | 
| +    } | 
| +  } | 
| +  return namedArgs; | 
| +} | 
| + | 
| +/** | 
| + * A contributor for calculating `completion.getSuggestions` request results | 
| + * when the cursor position is inside the arguments to a method call. | 
| + */ | 
| +class ArgListContributor extends DartCompletionContributor { | 
| +  DartCompletionRequest request; | 
| +  List<CompletionSuggestion> suggestions; | 
| + | 
| +  @override | 
| +  Future<List<CompletionSuggestion>> computeSuggestions( | 
| +      DartCompletionRequest request) async { | 
| +    this.request = request; | 
| +    this.suggestions = <CompletionSuggestion>[]; | 
| + | 
| +    // Determine if the target is in an argument list | 
| +    // for a method or a constructor or an annotation | 
| +    // and resolve the identifier | 
| +    SimpleIdentifier targetId = _getTargetId(request.target.containingNode); | 
| +    if (targetId == null) { | 
| +      return EMPTY_LIST; | 
| +    } | 
| + | 
| +    // Resolve the target expression to determine the arguments | 
| +    await request.resolveIdentifier(targetId); | 
| +    // Gracefully degrade if the element could not be resolved | 
| +    // e.g. target changed, completion aborted | 
| +    targetId = _getTargetId(request.target.containingNode); | 
| +    if (targetId == null) { | 
| +      return EMPTY_LIST; | 
| +    } | 
| +    Element elem = targetId.bestElement; | 
| +    if (elem == null) { | 
| +      return EMPTY_LIST; | 
| +    } | 
| + | 
| +    // Generate argument list suggestion based upon the type of element | 
| +    if (elem is ClassElement) { | 
| +      for (ConstructorElement constructor in elem.constructors) { | 
| +        if (!constructor.isFactory) { | 
| +          _addSuggestions(constructor.parameters); | 
| +          return suggestions; | 
| +        } | 
| +      } | 
| +    } | 
| +    if (elem is ConstructorElement) { | 
| +      _addSuggestions(elem.parameters); | 
| +      return suggestions; | 
| +    } | 
| +    if (elem is FunctionElement) { | 
| +      _addSuggestions(elem.parameters); | 
| +      return suggestions; | 
| +    } | 
| +    if (elem is MethodElement) { | 
| +      _addSuggestions(elem.parameters); | 
| +      return suggestions; | 
| +    } | 
| + | 
| +    print('${elem.runtimeType} :: $elem'); | 
| +    return EMPTY_LIST; | 
| +  } | 
| + | 
| +  void _addArgListSuggestion(Iterable<ParameterElement> requiredParam) { | 
| +    StringBuffer completion = new StringBuffer('('); | 
| +    List<String> paramNames = new List<String>(); | 
| +    List<String> paramTypes = new List<String>(); | 
| +    for (ParameterElement param in requiredParam) { | 
| +      String name = param.name; | 
| +      if (name != null && name.length > 0) { | 
| +        if (completion.length > 1) { | 
| +          completion.write(', '); | 
| +        } | 
| +        completion.write(name); | 
| +        paramNames.add(name); | 
| +        paramTypes.add(_getParamType(param)); | 
| +      } | 
| +    } | 
| +    completion.write(')'); | 
| +    CompletionSuggestion suggestion = new CompletionSuggestion( | 
| +        CompletionSuggestionKind.ARGUMENT_LIST, | 
| +        DART_RELEVANCE_HIGH, | 
| +        completion.toString(), | 
| +        completion.length, | 
| +        0, | 
| +        false, | 
| +        false); | 
| +    suggestion.parameterNames = paramNames; | 
| +    suggestion.parameterTypes = paramTypes; | 
| +    suggestions.add(suggestion); | 
| +  } | 
| + | 
| +  void _addDefaultParamSuggestions(Iterable<ParameterElement> parameters) { | 
| +    Iterable<String> namedArgs = _namedArgs(request); | 
| +    for (ParameterElement param in parameters) { | 
| +      if (param.parameterKind == ParameterKind.NAMED) { | 
| +        _addNamedParameterSuggestion(request, namedArgs, param.name); | 
| +      } | 
| +    } | 
| +  } | 
| + | 
| +  void _addNamedParameterSuggestion( | 
| +      DartCompletionRequest request, List<String> namedArgs, String name) { | 
| +    if (name != null && name.length > 0 && !namedArgs.contains(name)) { | 
| +      suggestions.add(new CompletionSuggestion( | 
| +          CompletionSuggestionKind.NAMED_ARGUMENT, | 
| +          DART_RELEVANCE_NAMED_PARAMETER, | 
| +          '$name: ', | 
| +          name.length + 2, | 
| +          0, | 
| +          false, | 
| +          false)); | 
| +    } | 
| +  } | 
| + | 
| +  void _addSuggestions(Iterable<ParameterElement> parameters) { | 
| +    if (parameters == null || parameters.length == 0) { | 
| +      return; | 
| +    } | 
| +    Iterable<ParameterElement> requiredParam = parameters.where( | 
| +        (ParameterElement p) => p.parameterKind == ParameterKind.REQUIRED); | 
| +    int requiredCount = requiredParam.length; | 
| +    if (requiredCount > 0 && _isEmptyArgList(request)) { | 
| +      _addArgListSuggestion(requiredParam); | 
| +      return; | 
| +    } | 
| +    if (_isAppendingToArgList(request)) { | 
| +      if (requiredCount == 0 || requiredCount < _argCount(request)) { | 
| +        _addDefaultParamSuggestions(parameters); | 
| +      } | 
| +    } | 
| +  } | 
| +} | 
|  |