| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library services.completion.computer.dart.toplevel; | 5 library services.completion.computer.dart.toplevel; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; |
| 8 | 9 |
| 9 import 'package:analysis_server/src/protocol_server.dart' hide Element, | 10 import 'package:analysis_server/src/protocol_server.dart' hide Element, |
| 10 ElementKind; | 11 ElementKind; |
| 11 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; | 12 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; |
| 12 import 'package:analysis_server/src/services/completion/suggestion_builder.dart'
; | 13 import 'package:analysis_server/src/services/completion/suggestion_builder.dart'
; |
| 13 import 'package:analysis_server/src/services/search/search_engine.dart'; | 14 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 14 import 'package:analyzer/src/generated/ast.dart'; | 15 import 'package:analyzer/src/generated/ast.dart'; |
| 15 import 'package:analyzer/src/generated/element.dart'; | 16 import 'package:analyzer/src/generated/element.dart'; |
| 16 import 'package:analyzer/src/generated/resolver.dart'; | 17 import 'package:analyzer/src/generated/resolver.dart'; |
| 17 import 'package:analyzer/src/generated/scanner.dart'; | 18 import 'package:analyzer/src/generated/scanner.dart'; |
| 18 import 'package:analyzer/src/generated/source.dart'; | 19 import 'package:analyzer/src/generated/source.dart'; |
| 19 | 20 |
| 20 /** | 21 /** |
| 21 * A computer for calculating imported class and top level variable | 22 * A computer for calculating imported class and top level variable |
| 22 * `completion.getSuggestions` request results. | 23 * `completion.getSuggestions` request results. |
| 23 */ | 24 */ |
| 24 class ImportedComputer extends DartCompletionComputer { | 25 class ImportedComputer extends DartCompletionComputer { |
| 26 _ImportedSuggestionBuilder builder; |
| 25 | 27 |
| 26 @override | 28 @override |
| 27 bool computeFast(DartCompletionRequest request) { | 29 bool computeFast(DartCompletionRequest request) { |
| 28 // TODO: implement computeFast | 30 builder = request.node.accept(new _ImportedAstVisitor(request)); |
| 29 // - compute results based upon current search, then replace those results | 31 if (builder != null) { |
| 30 // during the full compute phase | 32 return builder.computeFast(); |
| 31 // - filter results based upon completion offset | 33 } |
| 32 return false; | 34 return true; |
| 33 } | 35 } |
| 34 | 36 |
| 35 @override | 37 @override |
| 36 Future<bool> computeFull(DartCompletionRequest request) { | 38 Future<bool> computeFull(DartCompletionRequest request) { |
| 37 return request.node.accept(new _ImportedVisitor(request)); | 39 if (builder != null) { |
| 40 return builder.computeFull(request.node); |
| 41 } |
| 42 return new Future.value(false); |
| 38 } | 43 } |
| 39 } | 44 } |
| 40 | 45 |
| 41 /** | 46 /** |
| 42 * A visitor for determining which imported classes and top level variables | 47 * [_ImportedAstVisitor] determines whether an import suggestions are needed |
| 43 * should be suggested and building those suggestions. | 48 * and instantiates the builder to create those suggestions. |
| 44 */ | 49 */ |
| 45 class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> { | 50 class _ImportedAstVisitor extends |
| 51 GeneralizingAstVisitor<_ImportedSuggestionBuilder> { |
| 46 final DartCompletionRequest request; | 52 final DartCompletionRequest request; |
| 47 | 53 |
| 48 _ImportedVisitor(this.request); | 54 _ImportedAstVisitor(this.request); |
| 49 | 55 |
| 50 @override | 56 @override |
| 51 Future<bool> visitArgumentList(ArgumentList node) { | 57 _ImportedSuggestionBuilder visitArgumentList(ArgumentList node) { |
| 52 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 58 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 53 } | 59 } |
| 54 | 60 |
| 55 @override | 61 @override |
| 56 Future<bool> visitBlock(Block node) { | 62 _ImportedSuggestionBuilder visitBlock(Block node) { |
| 57 return _addImportedElementSuggestions(node); | 63 return new _ImportedSuggestionBuilder(request); |
| 58 } | 64 } |
| 59 | 65 |
| 60 @override | 66 @override |
| 61 Future<bool> visitCascadeExpression(CascadeExpression node) { | 67 _ImportedSuggestionBuilder visitCascadeExpression(CascadeExpression node) { |
| 62 // Make suggestions for the target, but not for the selector | 68 // Make suggestions for the target, but not for the selector |
| 63 // InvocationComputer makes selector suggestions | 69 // InvocationComputer makes selector suggestions |
| 64 Expression target = node.target; | 70 Expression target = node.target; |
| 65 if (target != null && request.offset <= target.end) { | 71 if (target != null && request.offset <= target.end) { |
| 66 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 72 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 67 } | 73 } |
| 68 return new Future.value(false); | 74 return null; |
| 69 } | 75 } |
| 70 | 76 |
| 71 @override | 77 @override |
| 72 Future<bool> visitClassDeclaration(ClassDeclaration node) { | 78 _ImportedSuggestionBuilder visitClassDeclaration(ClassDeclaration node) { |
| 73 // Make suggestions in the body of the class declaration | 79 // Make suggestions in the body of the class declaration |
| 74 Token leftBracket = node.leftBracket; | 80 Token leftBracket = node.leftBracket; |
| 75 if (leftBracket != null && request.offset >= leftBracket.end) { | 81 if (leftBracket != null && request.offset >= leftBracket.end) { |
| 76 return _addImportedElementSuggestions(node); | 82 return new _ImportedSuggestionBuilder(request); |
| 77 } | 83 } |
| 78 return new Future.value(false); | 84 return null; |
| 79 } | 85 } |
| 80 | 86 |
| 81 @override | 87 @override |
| 82 Future<bool> visitExpression(Expression node) { | 88 _ImportedSuggestionBuilder visitExpression(Expression node) { |
| 83 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 89 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 84 } | 90 } |
| 85 | 91 |
| 86 @override | 92 @override |
| 87 Future<bool> visitExpressionStatement(ExpressionStatement node) { | 93 _ImportedSuggestionBuilder |
| 94 visitExpressionStatement(ExpressionStatement node) { |
| 88 Expression expression = node.expression; | 95 Expression expression = node.expression; |
| 89 // A pre-variable declaration (e.g. C ^) is parsed as an expression | 96 // A pre-variable declaration (e.g. C ^) is parsed as an expression |
| 90 // statement. Do not make suggestions for the variable name. | 97 // statement. Do not make suggestions for the variable name. |
| 91 if (expression is SimpleIdentifier && request.offset <= expression.end) { | 98 if (expression is SimpleIdentifier && request.offset <= expression.end) { |
| 92 return _addImportedElementSuggestions(node); | 99 return new _ImportedSuggestionBuilder(request); |
| 93 } | 100 } |
| 94 return new Future.value(false); | 101 return null; |
| 95 } | 102 } |
| 96 | 103 |
| 97 @override | 104 @override |
| 98 Future<bool> visitFormalParameterList(FormalParameterList node) { | 105 _ImportedSuggestionBuilder |
| 106 visitFormalParameterList(FormalParameterList node) { |
| 99 Token leftParen = node.leftParenthesis; | 107 Token leftParen = node.leftParenthesis; |
| 100 if (leftParen != null && request.offset > leftParen.offset) { | 108 if (leftParen != null && request.offset > leftParen.offset) { |
| 101 Token rightParen = node.rightParenthesis; | 109 Token rightParen = node.rightParenthesis; |
| 102 if (rightParen == null || request.offset <= rightParen.offset) { | 110 if (rightParen == null || request.offset <= rightParen.offset) { |
| 103 return _addImportedElementSuggestions(node); | 111 return new _ImportedSuggestionBuilder(request); |
| 104 } | 112 } |
| 105 } | 113 } |
| 106 return new Future.value(false); | 114 return null; |
| 107 } | 115 } |
| 108 | 116 |
| 109 @override | 117 @override |
| 110 Future<bool> visitForStatement(ForStatement node) { | 118 _ImportedSuggestionBuilder visitForStatement(ForStatement node) { |
| 111 Token leftParen = node.leftParenthesis; | 119 Token leftParen = node.leftParenthesis; |
| 112 if (leftParen != null && request.offset >= leftParen.end) { | 120 if (leftParen != null && request.offset >= leftParen.end) { |
| 113 return _addImportedElementSuggestions(node); | 121 return new _ImportedSuggestionBuilder(request); |
| 114 } | 122 } |
| 115 return new Future.value(false); | 123 return null; |
| 116 } | 124 } |
| 117 | 125 |
| 118 @override | 126 @override |
| 119 Future<bool> visitIfStatement(IfStatement node) { | 127 _ImportedSuggestionBuilder visitIfStatement(IfStatement node) { |
| 120 Token leftParen = node.leftParenthesis; | 128 Token leftParen = node.leftParenthesis; |
| 121 if (leftParen != null && request.offset >= leftParen.end) { | 129 if (leftParen != null && request.offset >= leftParen.end) { |
| 122 Token rightParen = node.rightParenthesis; | 130 Token rightParen = node.rightParenthesis; |
| 123 if (rightParen == null || request.offset <= rightParen.offset) { | 131 if (rightParen == null || request.offset <= rightParen.offset) { |
| 124 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 132 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 125 } | 133 } |
| 126 } | 134 } |
| 127 return new Future.value(false); | 135 return null; |
| 128 } | 136 } |
| 129 | 137 |
| 130 @override | 138 @override |
| 131 Future<bool> visitInterpolationExpression(InterpolationExpression node) { | 139 _ImportedSuggestionBuilder |
| 140 visitInterpolationExpression(InterpolationExpression node) { |
| 132 Expression expression = node.expression; | 141 Expression expression = node.expression; |
| 133 if (expression is SimpleIdentifier) { | 142 if (expression is SimpleIdentifier) { |
| 134 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 143 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 135 } | 144 } |
| 136 return new Future.value(false); | 145 return null; |
| 137 } | 146 } |
| 138 | 147 |
| 139 @override | 148 @override |
| 140 Future<bool> visitMethodInvocation(MethodInvocation node) { | 149 _ImportedSuggestionBuilder visitMethodInvocation(MethodInvocation node) { |
| 141 Token period = node.period; | 150 Token period = node.period; |
| 142 if (period == null || request.offset <= period.offset) { | 151 if (period == null || request.offset <= period.offset) { |
| 143 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 152 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 144 } | 153 } |
| 145 return new Future.value(false); | 154 return null; |
| 146 } | 155 } |
| 147 | 156 |
| 148 @override | 157 @override |
| 149 Future<bool> visitNode(AstNode node) { | 158 _ImportedSuggestionBuilder visitNode(AstNode node) { |
| 150 return new Future.value(false); | 159 return null; |
| 151 } | 160 } |
| 152 | 161 |
| 153 @override | 162 @override |
| 154 Future<bool> visitPrefixedIdentifier(PrefixedIdentifier node) { | 163 _ImportedSuggestionBuilder visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 155 // Make suggestions for the prefix, but not for the selector | 164 // Make suggestions for the prefix, but not for the selector |
| 156 // InvocationComputer makes selector suggestions | 165 // InvocationComputer makes selector suggestions |
| 157 Token period = node.period; | 166 Token period = node.period; |
| 158 if (period != null && request.offset <= period.offset) { | 167 if (period != null && request.offset <= period.offset) { |
| 159 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 168 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 160 } | 169 } |
| 161 return new Future.value(false); | 170 return null; |
| 162 } | 171 } |
| 163 | 172 |
| 164 @override | 173 @override |
| 165 Future<bool> visitPropertyAccess(PropertyAccess node) { | 174 _ImportedSuggestionBuilder visitPropertyAccess(PropertyAccess node) { |
| 166 // Make suggestions for the target, but not for the property name | 175 // Make suggestions for the target, but not for the property name |
| 167 // InvocationComputer makes property name suggestions | 176 // InvocationComputer makes property name suggestions |
| 168 var operator = node.operator; | 177 var operator = node.operator; |
| 169 if (operator != null && request.offset < operator.offset) { | 178 if (operator != null && request.offset < operator.offset) { |
| 170 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 179 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 171 } | 180 } |
| 172 return new Future.value(false); | 181 return null; |
| 173 } | 182 } |
| 174 | 183 |
| 175 @override | 184 @override |
| 176 Future<bool> visitSimpleIdentifier(SimpleIdentifier node) { | 185 _ImportedSuggestionBuilder visitSimpleIdentifier(SimpleIdentifier node) { |
| 177 return node.parent.accept(this); | 186 return node.parent.accept(this); |
| 178 } | 187 } |
| 179 | 188 |
| 180 @override | 189 @override |
| 181 Future<bool> visitStringLiteral(StringLiteral node) { | 190 _ImportedSuggestionBuilder visitStringLiteral(StringLiteral node) { |
| 182 return new Future.value(false); | 191 return null; |
| 183 } | 192 } |
| 184 | 193 |
| 185 @override | 194 @override |
| 186 Future<bool> visitTypeName(TypeName node) { | 195 _ImportedSuggestionBuilder visitTypeName(TypeName node) { |
| 187 return _addImportedElementSuggestions(node, typesOnly: true); | 196 return new _ImportedSuggestionBuilder(request, typesOnly: true); |
| 188 } | 197 } |
| 189 | 198 |
| 190 @override | 199 @override |
| 191 visitVariableDeclaration(VariableDeclaration node) { | 200 _ImportedSuggestionBuilder |
| 201 visitVariableDeclaration(VariableDeclaration node) { |
| 192 Token equals = node.equals; | 202 Token equals = node.equals; |
| 193 // Make suggestions for the RHS of a variable declaration | 203 // Make suggestions for the RHS of a variable declaration |
| 194 if (equals != null && request.offset >= equals.end) { | 204 if (equals != null && request.offset >= equals.end) { |
| 195 return _addImportedElementSuggestions(node, excludeVoidReturn: true); | 205 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 196 } | 206 } |
| 197 return new Future.value(false); | 207 return null; |
| 198 } | 208 } |
| 199 | 209 } |
| 200 void _addElementSuggestion(Element element, bool typesOnly, | 210 |
| 201 bool excludeVoidReturn, CompletionRelevance relevance) { | 211 /** |
| 212 * [_ImportedSuggestionBuilder] traverses the imports and builds suggestions |
| 213 * based upon imported elements. |
| 214 */ |
| 215 class _ImportedSuggestionBuilder { |
| 216 final DartCompletionRequest request; |
| 217 final bool typesOnly; |
| 218 final bool excludeVoidReturn; |
| 219 final HashSet<String> completions = new HashSet(); |
| 220 DartCompletionCache cache; |
| 221 String importKey; |
| 222 |
| 223 _ImportedSuggestionBuilder(this.request, {this.typesOnly: false, |
| 224 this.excludeVoidReturn: false}) { |
| 225 cache = request.cache; |
| 226 } |
| 227 |
| 228 /** |
| 229 * Compute a hash of the import directives. |
| 230 */ |
| 231 String get computeImportKey { |
| 232 if (importKey == null) { |
| 233 StringBuffer sb = new StringBuffer(); |
| 234 request.unit.directives.forEach((Directive directive) { |
| 235 if (directive is ImportDirective) { |
| 236 sb.write(directive.toSource()); |
| 237 } |
| 238 }); |
| 239 importKey = sb.toString(); |
| 240 } |
| 241 return importKey; |
| 242 } |
| 243 |
| 244 void addCachedSuggestions() { |
| 245 DartCompletionCache cache = request.cache; |
| 246 request.suggestions |
| 247 ..addAll(cache.importedTypeSuggestions) |
| 248 ..addAll(cache.libraryPrefixSuggestions); |
| 249 if (!typesOnly) { |
| 250 request.suggestions.addAll(cache.otherImportedSuggestions); |
| 251 if (!excludeVoidReturn) { |
| 252 request.suggestions.addAll(cache.importedVoidReturnSuggestions); |
| 253 } |
| 254 } |
| 255 } |
| 256 |
| 257 void addLibraryPrefixSuggestion(ImportElement importElem) { |
| 258 CompletionSuggestion suggestion = null; |
| 259 String completion = importElem.prefix.displayName; |
| 260 if (completion != null && completion.length > 0) { |
| 261 suggestion = new CompletionSuggestion( |
| 262 CompletionSuggestionKind.INVOCATION, |
| 263 CompletionRelevance.DEFAULT, |
| 264 completion, |
| 265 completion.length, |
| 266 0, |
| 267 importElem.isDeprecated, |
| 268 false); |
| 269 LibraryElement lib = importElem.importedLibrary; |
| 270 if (lib != null) { |
| 271 suggestion.element = newElement_fromEngine(lib); |
| 272 } |
| 273 cache.libraryPrefixSuggestions.add(suggestion); |
| 274 completions.add(suggestion.completion); |
| 275 } |
| 276 } |
| 277 |
| 278 void addSuggestion(Element element, CompletionRelevance relevance) { |
| 202 | 279 |
| 203 if (element is ExecutableElement) { | 280 if (element is ExecutableElement) { |
| 204 if (element.isOperator) { | 281 if (element.isOperator) { |
| 205 return; | 282 return; |
| 206 } | 283 } |
| 207 if (excludeVoidReturn) { | |
| 208 DartType returnType = element.returnType; | |
| 209 if (returnType != null && returnType.isVoid) { | |
| 210 return; | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 if (typesOnly && element is! ClassElement) { | |
| 215 return; | |
| 216 } | 284 } |
| 217 | 285 |
| 218 String completion = element.displayName; | 286 String completion = element.displayName; |
| 219 CompletionSuggestion suggestion = new CompletionSuggestion( | 287 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 220 CompletionSuggestionKind.INVOCATION, | 288 CompletionSuggestionKind.INVOCATION, |
| 221 element.isDeprecated ? CompletionRelevance.LOW : relevance, | 289 element.isDeprecated ? CompletionRelevance.LOW : relevance, |
| 222 completion, | 290 completion, |
| 223 completion.length, | 291 completion.length, |
| 224 0, | 292 0, |
| 225 element.isDeprecated, | 293 element.isDeprecated, |
| 226 false); | 294 false); |
| 227 | 295 |
| 228 suggestion.element = newElement_fromEngine(element); | 296 suggestion.element = newElement_fromEngine(element); |
| 229 | 297 |
| 230 DartType type; | 298 DartType type; |
| 231 if (element is FunctionElement) { | 299 if (element is FunctionElement) { |
| 232 type = element.returnType; | 300 type = element.returnType; |
| 233 } else if (element is PropertyAccessorElement && element.isGetter) { | 301 } else if (element is PropertyAccessorElement && element.isGetter) { |
| 234 type = element.returnType; | 302 type = element.returnType; |
| 235 } else if (element is TopLevelVariableElement) { | 303 } else if (element is TopLevelVariableElement) { |
| 236 type = element.type; | 304 type = element.type; |
| 237 } | 305 } |
| 238 if (type != null) { | 306 if (type != null) { |
| 239 String name = type.displayName; | 307 String name = type.displayName; |
| 240 if (name != null && name.length > 0 && name != 'dynamic') { | 308 if (name != null && name.length > 0 && name != 'dynamic') { |
| 241 suggestion.returnType = name; | 309 suggestion.returnType = name; |
| 242 } | 310 } |
| 243 } | 311 } |
| 244 | 312 |
| 245 request.suggestions.add(suggestion); | 313 if (element is ExecutableElement) { |
| 314 DartType returnType = element.returnType; |
| 315 if (returnType != null && returnType.isVoid) { |
| 316 cache.importedVoidReturnSuggestions.add(suggestion); |
| 317 } else { |
| 318 cache.otherImportedSuggestions.add(suggestion); |
| 319 } |
| 320 } else if (element is ClassElement) { |
| 321 cache.importedTypeSuggestions.add(suggestion); |
| 322 } else { |
| 323 cache.otherImportedSuggestions.add(suggestion); |
| 324 } |
| 325 completions.add(suggestion.completion); |
| 246 } | 326 } |
| 247 | 327 |
| 248 void _addElementSuggestions(List<Element> elements, bool typesOnly, | 328 void addSuggestions(List<Element> elements) { |
| 249 bool excludeVoidReturn) { | |
| 250 elements.forEach((Element elem) { | 329 elements.forEach((Element elem) { |
| 251 _addElementSuggestion( | 330 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 252 elem, | |
| 253 typesOnly, | |
| 254 excludeVoidReturn, | |
| 255 CompletionRelevance.DEFAULT); | |
| 256 }); | 331 }); |
| 257 } | 332 } |
| 258 | 333 |
| 259 Future<bool> _addImportedElementSuggestions(AstNode node, {bool typesOnly: | 334 /** |
| 260 false, bool excludeVoidReturn: false}) { | 335 * If the needed information is cached, then add suggestions and return `true` |
| 336 * else return `false` indicating that additional work is necessary. |
| 337 */ |
| 338 bool computeFast() { |
| 339 if (cache.importKey == computeImportKey) { |
| 340 addCachedSuggestions(); |
| 341 return true; |
| 342 } |
| 343 return false; |
| 344 } |
| 345 |
| 346 /** |
| 347 * Compute suggested based upon imported elements. |
| 348 */ |
| 349 computeFull(AstNode node) { |
| 350 CompilationUnit unit = node.getAncestor((p) => p is CompilationUnit); |
| 351 cache.importedTypeSuggestions = <CompletionSuggestion>[]; |
| 352 cache.libraryPrefixSuggestions = <CompletionSuggestion>[]; |
| 353 cache.otherImportedSuggestions = <CompletionSuggestion>[]; |
| 354 cache.importedVoidReturnSuggestions = <CompletionSuggestion>[]; |
| 261 | 355 |
| 262 // Exclude elements from local library | 356 // Exclude elements from local library |
| 263 // because they are provided by LocalComputer | 357 // because they are provided by LocalComputer |
| 264 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); | 358 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); |
| 265 excludedLibs.add(request.unit.element.enclosingElement); | 359 excludedLibs.add(unit.element.enclosingElement); |
| 266 | 360 |
| 267 // Include explicitly imported elements | 361 // Include explicitly imported elements |
| 268 Map<String, ClassElement> classMap = new Map<String, ClassElement>(); | 362 Map<String, ClassElement> classMap = new Map<String, ClassElement>(); |
| 269 request.unit.directives.forEach((Directive directive) { | 363 unit.directives.forEach((Directive directive) { |
| 270 if (directive is ImportDirective) { | 364 if (directive is ImportDirective) { |
| 271 ImportElement importElem = directive.element; | 365 ImportElement importElem = directive.element; |
| 272 if (importElem != null && importElem.importedLibrary != null) { | 366 if (importElem != null && importElem.importedLibrary != null) { |
| 273 if (directive.prefix == null) { | 367 if (directive.prefix == null) { |
| 274 Namespace importNamespace = | 368 Namespace importNamespace = |
| 275 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); | 369 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); |
| 276 // Include top level elements | 370 // Include top level elements |
| 277 importNamespace.definedNames.forEach((String name, Element elem) { | 371 importNamespace.definedNames.forEach((String name, Element elem) { |
| 278 if (elem is ClassElement) { | 372 if (elem is ClassElement) { |
| 279 classMap[name] = elem; | 373 classMap[name] = elem; |
| 280 } | 374 } |
| 281 _addElementSuggestion( | 375 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 282 elem, | |
| 283 typesOnly, | |
| 284 excludeVoidReturn, | |
| 285 CompletionRelevance.DEFAULT); | |
| 286 }); | 376 }); |
| 287 } else { | 377 } else { |
| 288 // Exclude elements from prefixed imports | 378 // Exclude elements from prefixed imports |
| 289 // because they are provided by InvocationComputer | 379 // because they are provided by InvocationComputer |
| 290 excludedLibs.add(importElem.importedLibrary); | 380 excludedLibs.add(importElem.importedLibrary); |
| 291 _addLibraryPrefixSuggestion(importElem); | 381 addLibraryPrefixSuggestion(importElem); |
| 292 } | 382 } |
| 293 } | 383 } |
| 294 } | 384 } |
| 295 }); | 385 }); |
| 296 | 386 |
| 297 // Include implicitly imported dart:core elements | 387 // Include implicitly imported dart:core elements |
| 298 Source coreUri = request.context.sourceFactory.forUri('dart:core'); | 388 Source coreUri = request.context.sourceFactory.forUri('dart:core'); |
| 299 LibraryElement coreLib = request.context.getLibraryElement(coreUri); | 389 LibraryElement coreLib = request.context.getLibraryElement(coreUri); |
| 300 Namespace coreNamespace = | 390 Namespace coreNamespace = |
| 301 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | 391 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); |
| 302 coreNamespace.definedNames.forEach((String name, Element elem) { | 392 coreNamespace.definedNames.forEach((String name, Element elem) { |
| 303 if (elem is ClassElement) { | 393 if (elem is ClassElement) { |
| 304 classMap[name] = elem; | 394 classMap[name] = elem; |
| 305 } | 395 } |
| 306 _addElementSuggestion( | 396 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 307 elem, | |
| 308 typesOnly, | |
| 309 excludeVoidReturn, | |
| 310 CompletionRelevance.DEFAULT); | |
| 311 }); | 397 }); |
| 312 | 398 |
| 313 // Build a list of inherited types that are imported | 399 // Build a list of inherited types that are imported |
| 314 // and include any inherited imported members | 400 // and include any inherited imported members |
| 315 var classDecl = node.getAncestor((p) => p is ClassDeclaration); | 401 var classDecl = node.getAncestor((p) => p is ClassDeclaration); |
| 316 if (classDecl is ClassDeclaration) { | 402 if (classDecl is ClassDeclaration) { |
| 317 List<String> inheritedTypes = new List<String>(); | 403 List<String> inheritedTypes = new List<String>(); |
| 318 visitInheritedTypes(classDecl, (ClassDeclaration classDecl) { | 404 visitInheritedTypes(classDecl, (ClassDeclaration classDecl) { |
| 319 // ignored | 405 // ignored |
| 320 }, (String typeName) { | 406 }, (String typeName) { |
| 321 inheritedTypes.add(typeName); | 407 inheritedTypes.add(typeName); |
| 322 }); | 408 }); |
| 323 Set<String> visited = new Set<String>(); | 409 Set<String> visited = new Set<String>(); |
| 324 while (inheritedTypes.length > 0) { | 410 while (inheritedTypes.length > 0) { |
| 325 String name = inheritedTypes.removeLast(); | 411 String name = inheritedTypes.removeLast(); |
| 326 ClassElement elem = classMap[name]; | 412 ClassElement elem = classMap[name]; |
| 327 if (visited.add(name) && elem != null) { | 413 if (visited.add(name) && elem != null) { |
| 328 _addElementSuggestions(elem.accessors, typesOnly, excludeVoidReturn); | 414 addSuggestions(elem.accessors); |
| 329 _addElementSuggestions(elem.methods, typesOnly, excludeVoidReturn); | 415 addSuggestions(elem.methods); |
| 330 elem.allSupertypes.forEach((InterfaceType type) { | 416 elem.allSupertypes.forEach((InterfaceType type) { |
| 331 if (visited.add(type.name)) { | 417 if (visited.add(type.name)) { |
| 332 _addElementSuggestions( | 418 addSuggestions(type.accessors); |
| 333 type.accessors, | 419 addSuggestions(type.methods); |
| 334 typesOnly, | |
| 335 excludeVoidReturn); | |
| 336 _addElementSuggestions( | |
| 337 type.methods, | |
| 338 typesOnly, | |
| 339 excludeVoidReturn); | |
| 340 } | 420 } |
| 341 }); | 421 }); |
| 342 } | 422 } |
| 343 } | 423 } |
| 344 } | 424 } |
| 345 | 425 |
| 346 // Add non-imported elements as low relevance | 426 // Add non-imported elements as low relevance |
| 347 var future = request.searchEngine.searchTopLevelDeclarations(''); | 427 var future = request.searchEngine.searchTopLevelDeclarations(''); |
| 348 return future.then((List<SearchMatch> matches) { | 428 return future.then((List<SearchMatch> matches) { |
| 349 Set<String> completionSet = new Set<String>(); | |
| 350 request.suggestions.forEach((CompletionSuggestion suggestion) { | |
| 351 completionSet.add(suggestion.completion); | |
| 352 }); | |
| 353 matches.forEach((SearchMatch match) { | 429 matches.forEach((SearchMatch match) { |
| 354 if (match.kind == MatchKind.DECLARATION) { | 430 if (match.kind == MatchKind.DECLARATION) { |
| 355 Element element = match.element; | 431 Element element = match.element; |
| 356 if (element.isPublic && | 432 if (element.isPublic && |
| 357 !excludedLibs.contains(element.library) && | 433 !excludedLibs.contains(element.library) && |
| 358 !completionSet.contains(element.displayName)) { | 434 !completions.contains(element.displayName)) { |
| 359 if (!typesOnly || element is ClassElement) { | 435 addSuggestion(element, CompletionRelevance.LOW); |
| 360 _addElementSuggestion( | |
| 361 element, | |
| 362 typesOnly, | |
| 363 excludeVoidReturn, | |
| 364 CompletionRelevance.LOW); | |
| 365 } | |
| 366 } | 436 } |
| 367 } | 437 } |
| 368 }); | 438 }); |
| 439 cache.importKey = computeImportKey; |
| 440 addCachedSuggestions(); |
| 369 return true; | 441 return true; |
| 370 }); | 442 }); |
| 371 } | 443 } |
| 372 | |
| 373 void _addLibraryPrefixSuggestion(ImportElement importElem) { | |
| 374 String completion = importElem.prefix.displayName; | |
| 375 if (completion != null && completion.length > 0) { | |
| 376 CompletionSuggestion suggestion = new CompletionSuggestion( | |
| 377 CompletionSuggestionKind.INVOCATION, | |
| 378 CompletionRelevance.DEFAULT, | |
| 379 completion, | |
| 380 completion.length, | |
| 381 0, | |
| 382 importElem.isDeprecated, | |
| 383 false); | |
| 384 LibraryElement lib = importElem.importedLibrary; | |
| 385 if (lib != null) { | |
| 386 suggestion.element = newElement_fromEngine(lib); | |
| 387 } | |
| 388 request.suggestions.add(suggestion); | |
| 389 } | |
| 390 } | |
| 391 } | 444 } |
| OLD | NEW |