OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library services.completion.dart.suggestion.builder; |
| 6 |
| 7 import 'package:analysis_server/src/protocol_server.dart' as protocol; |
| 8 import 'package:analysis_server/src/protocol_server.dart' |
| 9 hide Element, ElementKind; |
| 10 import 'package:analyzer/src/generated/element.dart'; |
| 11 import 'package:analyzer/src/generated/source.dart'; |
| 12 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 13 import 'package:path/path.dart' as path; |
| 14 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.
dart'; |
| 15 |
| 16 const String DYNAMIC = 'dynamic'; |
| 17 |
| 18 /** |
| 19 * Return a suggestion based upon the given element |
| 20 * or `null` if a suggestion is not appropriate for the given element. |
| 21 * If the suggestion is not currently in scope, then specify |
| 22 * importForSource as the source to which an import should be added. |
| 23 */ |
| 24 CompletionSuggestion createSuggestion(Element element, |
| 25 {String completion, |
| 26 CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION, |
| 27 int relevance: DART_RELEVANCE_DEFAULT, |
| 28 Source importForSource}) { |
| 29 if (element is ExecutableElement && element.isOperator) { |
| 30 // Do not include operators in suggestions |
| 31 return null; |
| 32 } |
| 33 if (completion == null) { |
| 34 completion = element.displayName; |
| 35 } |
| 36 bool isDeprecated = element.isDeprecated; |
| 37 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 38 kind, |
| 39 isDeprecated ? DART_RELEVANCE_LOW : relevance, |
| 40 completion, |
| 41 completion.length, |
| 42 0, |
| 43 isDeprecated, |
| 44 false); |
| 45 suggestion.element = protocol.convertElement(element); |
| 46 Element enclosingElement = element.enclosingElement; |
| 47 if (enclosingElement is ClassElement) { |
| 48 suggestion.declaringType = enclosingElement.displayName; |
| 49 } |
| 50 suggestion.returnType = getReturnTypeString(element); |
| 51 if (element is ExecutableElement && element is! PropertyAccessorElement) { |
| 52 suggestion.parameterNames = element.parameters |
| 53 .map((ParameterElement parameter) => parameter.name) |
| 54 .toList(); |
| 55 suggestion.parameterTypes = element.parameters |
| 56 .map((ParameterElement parameter) => parameter.type.displayName) |
| 57 .toList(); |
| 58 suggestion.requiredParameterCount = element.parameters |
| 59 .where((ParameterElement parameter) => |
| 60 parameter.parameterKind == ParameterKind.REQUIRED) |
| 61 .length; |
| 62 suggestion.hasNamedParameters = element.parameters.any( |
| 63 (ParameterElement parameter) => |
| 64 parameter.parameterKind == ParameterKind.NAMED); |
| 65 } |
| 66 if (importForSource != null) { |
| 67 String srcPath = path.dirname(importForSource.fullName); |
| 68 LibraryElement libElem = element.library; |
| 69 if (libElem != null) { |
| 70 Source libSource = libElem.source; |
| 71 if (libSource != null) { |
| 72 UriKind uriKind = libSource.uriKind; |
| 73 if (uriKind == UriKind.DART_URI) { |
| 74 suggestion.importUri = libSource.uri.toString(); |
| 75 } else if (uriKind == UriKind.PACKAGE_URI) { |
| 76 suggestion.importUri = libSource.uri.toString(); |
| 77 } else if (uriKind == UriKind.FILE_URI && |
| 78 element.source.uriKind == UriKind.FILE_URI) { |
| 79 try { |
| 80 suggestion.importUri = |
| 81 path.relative(libSource.fullName, from: srcPath); |
| 82 } catch (_) { |
| 83 // ignored |
| 84 } |
| 85 } |
| 86 } |
| 87 } |
| 88 if (suggestion.importUri == null) { |
| 89 // Do not include out of scope suggestions |
| 90 // for which we cannot determine an import |
| 91 return null; |
| 92 } |
| 93 } |
| 94 return suggestion; |
| 95 } |
| 96 |
| 97 /** |
| 98 * Common mixin for sharing behavior |
| 99 */ |
| 100 abstract class ElementSuggestionBuilder { |
| 101 /** |
| 102 * A collection of completion suggestions. |
| 103 */ |
| 104 final List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; |
| 105 |
| 106 /** |
| 107 * Return the kind of suggestions that should be built. |
| 108 */ |
| 109 CompletionSuggestionKind get kind; |
| 110 |
| 111 /** |
| 112 * Return the request on which the builder is operating. |
| 113 */ |
| 114 DartCompletionRequest get request; |
| 115 |
| 116 /** |
| 117 * Add a suggestion based upon the given element. |
| 118 */ |
| 119 void addSuggestion(Element element, |
| 120 {String prefix, int relevance: DART_RELEVANCE_DEFAULT}) { |
| 121 if (element.isPrivate) { |
| 122 LibraryElement elementLibrary = element.library; |
| 123 CompilationUnitElement unitElem = request.target.unit.element; |
| 124 if (unitElem == null) { |
| 125 return; |
| 126 } |
| 127 LibraryElement unitLibrary = unitElem.library; |
| 128 if (elementLibrary != unitLibrary) { |
| 129 return; |
| 130 } |
| 131 } |
| 132 if (prefix == null && element.isSynthetic) { |
| 133 if ((element is PropertyAccessorElement) || |
| 134 element is FieldElement && !_isSpecialEnumField(element)) { |
| 135 return; |
| 136 } |
| 137 } |
| 138 String completion = element.displayName; |
| 139 if (prefix != null && prefix.length > 0) { |
| 140 if (completion == null || completion.length <= 0) { |
| 141 completion = prefix; |
| 142 } else { |
| 143 completion = '$prefix.$completion'; |
| 144 } |
| 145 } |
| 146 if (completion == null || completion.length <= 0) { |
| 147 return; |
| 148 } |
| 149 CompletionSuggestion suggestion = createSuggestion(element, |
| 150 completion: completion, kind: kind, relevance: relevance); |
| 151 if (suggestion != null) { |
| 152 suggestions.add(suggestion); |
| 153 } |
| 154 } |
| 155 |
| 156 /** |
| 157 * Determine if the given element is one of the synthetic enum accessors |
| 158 * for which we should generate a suggestion. |
| 159 */ |
| 160 bool _isSpecialEnumField(FieldElement element) { |
| 161 Element parent = element.enclosingElement; |
| 162 if (parent is ClassElement && parent.isEnum) { |
| 163 if (element.name == 'values') { |
| 164 return true; |
| 165 } |
| 166 } |
| 167 return false; |
| 168 } |
| 169 } |
| 170 |
| 171 /** |
| 172 * This class visits elements in a library and provides suggestions based upon |
| 173 * the visible members in that library. Clients should call |
| 174 * [LibraryElementSuggestionBuilder.suggestionsFor]. |
| 175 */ |
| 176 class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor |
| 177 with ElementSuggestionBuilder { |
| 178 final DartCompletionRequest request; |
| 179 final CompletionSuggestionKind kind; |
| 180 final bool typesOnly; |
| 181 final bool instCreation; |
| 182 |
| 183 LibraryElementSuggestionBuilder( |
| 184 this.request, this.kind, this.typesOnly, this.instCreation); |
| 185 |
| 186 @override |
| 187 visitClassElement(ClassElement element) { |
| 188 if (instCreation) { |
| 189 element.visitChildren(this); |
| 190 } else { |
| 191 addSuggestion(element); |
| 192 } |
| 193 } |
| 194 |
| 195 @override |
| 196 visitCompilationUnitElement(CompilationUnitElement element) { |
| 197 element.visitChildren(this); |
| 198 LibraryElement containingLibrary = element.library; |
| 199 if (containingLibrary != null) { |
| 200 for (var lib in containingLibrary.exportedLibraries) { |
| 201 lib.visitChildren(this); |
| 202 } |
| 203 } |
| 204 } |
| 205 |
| 206 @override |
| 207 visitConstructorElement(ConstructorElement element) { |
| 208 if (instCreation) { |
| 209 ClassElement classElem = element.enclosingElement; |
| 210 if (classElem != null) { |
| 211 String prefix = classElem.name; |
| 212 if (prefix != null && prefix.length > 0) { |
| 213 addSuggestion(element, prefix: prefix); |
| 214 } |
| 215 } |
| 216 } |
| 217 } |
| 218 |
| 219 @override |
| 220 visitElement(Element element) { |
| 221 // ignored |
| 222 } |
| 223 |
| 224 @override |
| 225 visitFunctionElement(FunctionElement element) { |
| 226 if (!typesOnly) { |
| 227 addSuggestion(element); |
| 228 } |
| 229 } |
| 230 |
| 231 @override |
| 232 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 233 if (!instCreation) { |
| 234 addSuggestion(element); |
| 235 } |
| 236 } |
| 237 |
| 238 @override |
| 239 visitTopLevelVariableElement(TopLevelVariableElement element) { |
| 240 if (!typesOnly) { |
| 241 addSuggestion(element); |
| 242 } |
| 243 } |
| 244 |
| 245 /** |
| 246 * Add suggestions for the visible members in the given library |
| 247 */ |
| 248 static void suggestionsFor( |
| 249 DartCompletionRequest request, |
| 250 CompletionSuggestionKind kind, |
| 251 LibraryElement library, |
| 252 bool typesOnly, |
| 253 bool instCreation) { |
| 254 if (library != null) { |
| 255 library.visitChildren(new LibraryElementSuggestionBuilder( |
| 256 request, kind, typesOnly, instCreation)); |
| 257 } |
| 258 } |
| 259 } |
OLD | NEW |