// 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. /** * Utilities for converting Dart entities into analysis server's protocol * entities. */ import 'package:analysis_server/src/protocol_server.dart'; import 'package:analyzer/dart/element/element.dart' as engine; import 'package:analyzer/src/generated/utilities_dart.dart' as engine; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:path/path.dart' as pathos; /** * Return a protocol [Element] corresponding to the given [engine.Element]. */ Element convertElement(engine.Element element) { String name = getElementDisplayName(element); String elementTypeParameters = _getTypeParametersString(element); String elementParameters = _getParametersString(element); String elementReturnType = getReturnTypeString(element); ElementKind kind = convertElementToElementKind(element); return new Element( kind, name, Element.makeFlags( isPrivate: element.isPrivate, isDeprecated: element.hasDeprecated, isAbstract: _isAbstract(element), isConst: _isConst(element), isFinal: _isFinal(element), isStatic: _isStatic(element)), location: newLocation_fromElement(element), typeParameters: elementTypeParameters, parameters: elementParameters, returnType: elementReturnType); } /** * Return a protocol [ElementKind] corresponding to the given * [engine.ElementKind]. * * This does not take into account that an instance of [ClassElement] can be an * enum and an instance of [FieldElement] can be an enum constant. * Use [convertElementToElementKind] where possible. */ ElementKind convertElementKind(engine.ElementKind kind) { if (kind == engine.ElementKind.CLASS) { return ElementKind.CLASS; } if (kind == engine.ElementKind.COMPILATION_UNIT) { return ElementKind.COMPILATION_UNIT; } if (kind == engine.ElementKind.CONSTRUCTOR) { return ElementKind.CONSTRUCTOR; } if (kind == engine.ElementKind.FIELD) { return ElementKind.FIELD; } if (kind == engine.ElementKind.FUNCTION) { return ElementKind.FUNCTION; } if (kind == engine.ElementKind.FUNCTION_TYPE_ALIAS) { return ElementKind.FUNCTION_TYPE_ALIAS; } if (kind == engine.ElementKind.GENERIC_FUNCTION_TYPE) { return ElementKind.FUNCTION_TYPE_ALIAS; } if (kind == engine.ElementKind.GETTER) { return ElementKind.GETTER; } if (kind == engine.ElementKind.LABEL) { return ElementKind.LABEL; } if (kind == engine.ElementKind.LIBRARY) { return ElementKind.LIBRARY; } if (kind == engine.ElementKind.LOCAL_VARIABLE) { return ElementKind.LOCAL_VARIABLE; } if (kind == engine.ElementKind.METHOD) { return ElementKind.METHOD; } if (kind == engine.ElementKind.PARAMETER) { return ElementKind.PARAMETER; } if (kind == engine.ElementKind.PREFIX) { return ElementKind.PREFIX; } if (kind == engine.ElementKind.SETTER) { return ElementKind.SETTER; } if (kind == engine.ElementKind.TOP_LEVEL_VARIABLE) { return ElementKind.TOP_LEVEL_VARIABLE; } if (kind == engine.ElementKind.TYPE_PARAMETER) { return ElementKind.TYPE_PARAMETER; } return ElementKind.UNKNOWN; } /** * Return an [ElementKind] corresponding to the given [engine.Element]. */ ElementKind convertElementToElementKind(engine.Element element) { if (element is engine.ClassElement) { if (element.isEnum) { return ElementKind.ENUM; } if (element.isMixin) { return ElementKind.MIXIN; } } if (element is engine.FieldElement && element.isEnumConstant && // MyEnum.values and MyEnum.one.index return isEnumConstant = true // so these additional checks are necessary. // TODO(danrubel) MyEnum.values is constant, but is a list // so should it return isEnumConstant = true? // MyEnum.one.index is final but *not* constant // so should it return isEnumConstant = true? // Or should we return ElementKind.ENUM_CONSTANT here // in either or both of these cases? element.type != null && element.type.element == element.enclosingElement) { return ElementKind.ENUM_CONSTANT; } return convertElementKind(element.kind); } String getElementDisplayName(engine.Element element) { if (element is engine.CompilationUnitElement) { return pathos.basename(element.source.fullName); } else { return element.displayName; } } String _getParametersString(engine.Element element) { // TODO(scheglov) expose the corresponding feature from ExecutableElement List parameters; if (element is engine.ExecutableElement) { // valid getters don't have parameters if (element.kind == engine.ElementKind.GETTER && element.parameters.isEmpty) { return null; } parameters = element.parameters.toList(); } else if (element is engine.FunctionTypeAliasElement) { parameters = element.parameters.toList(); } else { return null; } parameters.sort(_preferRequiredParams); StringBuffer sb = new StringBuffer(); String closeOptionalString = ''; for (engine.ParameterElement parameter in parameters) { if (sb.isNotEmpty) { sb.write(', '); } if (closeOptionalString.isEmpty) { if (parameter.isNamed) { sb.write('{'); closeOptionalString = '}'; } else if (parameter.isOptionalPositional) { sb.write('['); closeOptionalString = ']'; } } if (parameter.hasRequired) { sb.write('@required '); } parameter.appendToWithoutDelimiters(sb); } sb.write(closeOptionalString); return '(' + sb.toString() + ')'; } String _getTypeParametersString(engine.Element element) { List typeParameters; if (element is engine.ClassElement) { typeParameters = element.typeParameters; } else if (element is engine.FunctionTypeAliasElement) { typeParameters = element.typeParameters; } if (typeParameters == null || typeParameters.isEmpty) { return null; } return '<${typeParameters.join(', ')}>'; } bool _isAbstract(engine.Element element) { // TODO(scheglov) add isAbstract to Element API if (element is engine.ClassElement) { return element.isAbstract; } if (element is engine.MethodElement) { return element.isAbstract; } if (element is engine.PropertyAccessorElement) { return element.isAbstract; } return false; } bool _isConst(engine.Element element) { // TODO(scheglov) add isConst to Element API if (element is engine.ConstructorElement) { return element.isConst; } if (element is engine.VariableElement) { return element.isConst; } return false; } bool _isFinal(engine.Element element) { // TODO(scheglov) add isFinal to Element API if (element is engine.VariableElement) { return element.isFinal; } return false; } bool _isStatic(engine.Element element) { // TODO(scheglov) add isStatic to Element API if (element is engine.ExecutableElement) { return element.isStatic; } if (element is engine.PropertyInducingElement) { return element.isStatic; } return false; } // Sort @required named parameters before optional ones. int _preferRequiredParams( engine.ParameterElement e1, engine.ParameterElement e2) { int rank1 = e1.hasRequired ? 0 : !e1.isNamed ? -1 : 1; int rank2 = e2.hasRequired ? 0 : !e2.isNamed ? -1 : 1; return rank1 - rank2; }