| 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);
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|