| 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 | 8 |
| 9 import 'package:analysis_server/src/protocol_server.dart' hide Element, | 9 import 'package:analysis_server/src/protocol_server.dart' hide Element, |
| 10 ElementKind; | 10 ElementKind; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 * A visitor for determining which imported class and top level variable | 42 * A visitor for determining which imported class and top level variable |
| 43 * should be suggested and building those suggestions. | 43 * should be suggested and building those suggestions. |
| 44 */ | 44 */ |
| 45 class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> { | 45 class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> { |
| 46 final DartCompletionRequest request; | 46 final DartCompletionRequest request; |
| 47 | 47 |
| 48 _ImportedVisitor(this.request); | 48 _ImportedVisitor(this.request); |
| 49 | 49 |
| 50 @override | 50 @override |
| 51 Future<bool> visitBlock(Block node) { | 51 Future<bool> visitBlock(Block node) { |
| 52 return _addImportedElementSuggestions(); | 52 return _addImportedElementSuggestions(node); |
| 53 } | 53 } |
| 54 | 54 |
| 55 @override | 55 @override |
| 56 Future<bool> visitCascadeExpression(CascadeExpression node) { | 56 Future<bool> visitCascadeExpression(CascadeExpression node) { |
| 57 // Make suggestions for the target, but not for the selector | 57 // Make suggestions for the target, but not for the selector |
| 58 // InvocationComputer makes selector suggestions | 58 // InvocationComputer makes selector suggestions |
| 59 Expression target = node.target; | 59 Expression target = node.target; |
| 60 if (target != null && request.offset <= target.end) { | 60 if (target != null && request.offset <= target.end) { |
| 61 return _addImportedElementSuggestions(); | 61 return _addImportedElementSuggestions(node); |
| 62 } | 62 } |
| 63 return new Future.value(false); | 63 return new Future.value(false); |
| 64 } | 64 } |
| 65 | 65 |
| 66 @override | 66 @override |
| 67 Future<bool> visitClassDeclaration(ClassDeclaration node) { | 67 Future<bool> visitClassDeclaration(ClassDeclaration node) { |
| 68 // Make suggestions in the body of the class declaration | 68 // Make suggestions in the body of the class declaration |
| 69 Token leftBracket = node.leftBracket; | 69 Token leftBracket = node.leftBracket; |
| 70 if (leftBracket != null && request.offset >= leftBracket.end) { | 70 if (leftBracket != null && request.offset >= leftBracket.end) { |
| 71 return _addImportedElementSuggestions(); | 71 return _addImportedElementSuggestions(node); |
| 72 } | 72 } |
| 73 return new Future.value(false); | 73 return new Future.value(false); |
| 74 } | 74 } |
| 75 | 75 |
| 76 @override | 76 @override |
| 77 Future<bool> visitCombinator(Combinator node) { | 77 Future<bool> visitCombinator(Combinator node) { |
| 78 return _addCombinatorSuggestions(node); | 78 return _addCombinatorSuggestions(node); |
| 79 } | 79 } |
| 80 | 80 |
| 81 @override | 81 @override |
| 82 Future<bool> visitExpression(Expression node) { | 82 Future<bool> visitExpression(Expression node) { |
| 83 return _addImportedElementSuggestions(); | 83 return _addImportedElementSuggestions(node); |
| 84 } | 84 } |
| 85 | 85 |
| 86 @override | 86 @override |
| 87 Future<bool> visitExpressionStatement(ExpressionStatement node) { | 87 Future<bool> visitExpressionStatement(ExpressionStatement node) { |
| 88 Expression expression = node.expression; | 88 Expression expression = node.expression; |
| 89 // A pre-variable declaration (e.g. C ^) is parsed as an expression | 89 // A pre-variable declaration (e.g. C ^) is parsed as an expression |
| 90 // statement. Do not make suggestions for the variable name. | 90 // statement. Do not make suggestions for the variable name. |
| 91 if (expression is SimpleIdentifier && request.offset <= expression.end) { | 91 if (expression is SimpleIdentifier && request.offset <= expression.end) { |
| 92 return _addImportedElementSuggestions(); | 92 return _addImportedElementSuggestions(node); |
| 93 } | 93 } |
| 94 return new Future.value(false); | 94 return new Future.value(false); |
| 95 } | 95 } |
| 96 | 96 |
| 97 @override | 97 @override |
| 98 Future<bool> visitForStatement(ForStatement node) { | 98 Future<bool> visitForStatement(ForStatement node) { |
| 99 Token leftParenthesis = node.leftParenthesis; | 99 Token leftParenthesis = node.leftParenthesis; |
| 100 if (leftParenthesis != null && request.offset >= leftParenthesis.end) { | 100 if (leftParenthesis != null && request.offset >= leftParenthesis.end) { |
| 101 return _addImportedElementSuggestions(); | 101 return _addImportedElementSuggestions(node); |
| 102 } | 102 } |
| 103 return new Future.value(false); | 103 return new Future.value(false); |
| 104 } | 104 } |
| 105 | 105 |
| 106 @override | 106 @override |
| 107 Future<bool> visitIfStatement(IfStatement node) { | 107 Future<bool> visitIfStatement(IfStatement node) { |
| 108 Token leftParen = node.leftParenthesis; | 108 Token leftParen = node.leftParenthesis; |
| 109 if (leftParen != null && request.offset >= leftParen.end) { | 109 if (leftParen != null && request.offset >= leftParen.end) { |
| 110 Token rightParen = node.rightParenthesis; | 110 Token rightParen = node.rightParenthesis; |
| 111 if (rightParen == null || request.offset <= rightParen.offset) { | 111 if (rightParen == null || request.offset <= rightParen.offset) { |
| 112 return _addImportedElementSuggestions(); | 112 return _addImportedElementSuggestions(node); |
| 113 } | 113 } |
| 114 } | 114 } |
| 115 return new Future.value(false); | 115 return new Future.value(false); |
| 116 } | 116 } |
| 117 | 117 |
| 118 @override | 118 @override |
| 119 Future<bool> visitInterpolationExpression(InterpolationExpression node) { | 119 Future<bool> visitInterpolationExpression(InterpolationExpression node) { |
| 120 Expression expression = node.expression; | 120 Expression expression = node.expression; |
| 121 if (expression is SimpleIdentifier) { | 121 if (expression is SimpleIdentifier) { |
| 122 return _addImportedElementSuggestions(); | 122 return _addImportedElementSuggestions(node); |
| 123 } | 123 } |
| 124 return new Future.value(false); | 124 return new Future.value(false); |
| 125 } | 125 } |
| 126 | 126 |
| 127 @override | 127 @override |
| 128 Future<bool> visitMethodInvocation(MethodInvocation node) { | 128 Future<bool> visitMethodInvocation(MethodInvocation node) { |
| 129 Token period = node.period; | 129 Token period = node.period; |
| 130 if (period == null || request.offset <= period.offset) { | 130 if (period == null || request.offset <= period.offset) { |
| 131 return _addImportedElementSuggestions(); | 131 return _addImportedElementSuggestions(node); |
| 132 } | 132 } |
| 133 return new Future.value(false); | 133 return new Future.value(false); |
| 134 } | 134 } |
| 135 | 135 |
| 136 @override | 136 @override |
| 137 Future<bool> visitNode(AstNode node) { | 137 Future<bool> visitNode(AstNode node) { |
| 138 return new Future.value(false); | 138 return new Future.value(false); |
| 139 } | 139 } |
| 140 | 140 |
| 141 @override | 141 @override |
| 142 Future<bool> visitPrefixedIdentifier(PrefixedIdentifier node) { | 142 Future<bool> visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 143 // Make suggestions for the prefix, but not for the selector | 143 // Make suggestions for the prefix, but not for the selector |
| 144 // InvocationComputer makes selector suggestions | 144 // InvocationComputer makes selector suggestions |
| 145 Token period = node.period; | 145 Token period = node.period; |
| 146 if (period != null && request.offset <= period.offset) { | 146 if (period != null && request.offset <= period.offset) { |
| 147 return _addImportedElementSuggestions(); | 147 return _addImportedElementSuggestions(node); |
| 148 } | 148 } |
| 149 return new Future.value(false); | 149 return new Future.value(false); |
| 150 } | 150 } |
| 151 | 151 |
| 152 @override | 152 @override |
| 153 Future<bool> visitPropertyAccess(PropertyAccess node) { | 153 Future<bool> visitPropertyAccess(PropertyAccess node) { |
| 154 // Make suggestions for the target, but not for the property name | 154 // Make suggestions for the target, but not for the property name |
| 155 // InvocationComputer makes property name suggestions | 155 // InvocationComputer makes property name suggestions |
| 156 var operator = node.operator; | 156 var operator = node.operator; |
| 157 if (operator != null && request.offset < operator.offset) { | 157 if (operator != null && request.offset < operator.offset) { |
| 158 return _addImportedElementSuggestions(); | 158 return _addImportedElementSuggestions(node); |
| 159 } | 159 } |
| 160 return new Future.value(false); | 160 return new Future.value(false); |
| 161 } | 161 } |
| 162 | 162 |
| 163 @override | 163 @override |
| 164 Future<bool> visitSimpleIdentifier(SimpleIdentifier node) { | 164 Future<bool> visitSimpleIdentifier(SimpleIdentifier node) { |
| 165 return node.parent.accept(this); | 165 return node.parent.accept(this); |
| 166 } | 166 } |
| 167 | 167 |
| 168 @override | 168 @override |
| 169 Future<bool> visitStringLiteral(StringLiteral node) { | 169 Future<bool> visitStringLiteral(StringLiteral node) { |
| 170 return new Future.value(false); | 170 return new Future.value(false); |
| 171 } | 171 } |
| 172 | 172 |
| 173 @override | 173 @override |
| 174 Future<bool> visitTypeName(TypeName node) { | 174 Future<bool> visitTypeName(TypeName node) { |
| 175 return _addImportedElementSuggestions(typesOnly: true); | 175 return _addImportedElementSuggestions(node, typesOnly: true); |
| 176 } | 176 } |
| 177 | 177 |
| 178 @override | 178 @override |
| 179 visitVariableDeclaration(VariableDeclaration node) { | 179 visitVariableDeclaration(VariableDeclaration node) { |
| 180 Token equals = node.equals; | 180 Token equals = node.equals; |
| 181 // Make suggestions for the RHS of a variable declaration | 181 // Make suggestions for the RHS of a variable declaration |
| 182 if (equals != null && request.offset >= equals.end) { | 182 if (equals != null && request.offset >= equals.end) { |
| 183 return _addImportedElementSuggestions(); | 183 return _addImportedElementSuggestions(node); |
| 184 } | 184 } |
| 185 return new Future.value(false); | 185 return new Future.value(false); |
| 186 } | 186 } |
| 187 | 187 |
| 188 Future _addCombinatorSuggestions(Combinator node) { | 188 Future _addCombinatorSuggestions(Combinator node) { |
| 189 var directive = node.getAncestor((parent) => parent is NamespaceDirective); | 189 var directive = node.getAncestor((parent) => parent is NamespaceDirective); |
| 190 if (directive is NamespaceDirective) { | 190 if (directive is NamespaceDirective) { |
| 191 LibraryElement library = directive.uriElement; | 191 LibraryElement library = directive.uriElement; |
| 192 LibraryElementSuggestionBuilder.suggestionsFor(request, library); | 192 LibraryElementSuggestionBuilder.suggestionsFor(request, library); |
| 193 return new Future.value(true); | 193 return new Future.value(true); |
| 194 } | 194 } |
| 195 | 195 |
| 196 return new Future.value(false); | 196 return new Future.value(false); |
| 197 } | 197 } |
| 198 | 198 |
| 199 void _addElementSuggestion(Element element, CompletionRelevance relevance) { | 199 void _addElementSuggestion(Element element, CompletionRelevance relevance) { |
| 200 |
| 201 if (element is ExecutableElement) { |
| 202 if (element.isOperator) { |
| 203 return; |
| 204 } |
| 205 } |
| 206 |
| 200 CompletionSuggestionKind kind = | 207 CompletionSuggestionKind kind = |
| 201 newCompletionSuggestionKind_fromElementKind(element.kind); | 208 newCompletionSuggestionKind_fromElementKind(element.kind); |
| 202 | 209 |
| 203 String completion = element.displayName; | 210 String completion = element.displayName; |
| 204 CompletionSuggestion suggestion = new CompletionSuggestion( | 211 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 205 kind, | 212 kind, |
| 206 relevance, | 213 relevance, |
| 207 completion, | 214 completion, |
| 208 completion.length, | 215 completion.length, |
| 209 0, | 216 0, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 223 if (type != null) { | 230 if (type != null) { |
| 224 String name = type.displayName; | 231 String name = type.displayName; |
| 225 if (name != null && name.length > 0 && name != 'dynamic') { | 232 if (name != null && name.length > 0 && name != 'dynamic') { |
| 226 suggestion.returnType = name; | 233 suggestion.returnType = name; |
| 227 } | 234 } |
| 228 } | 235 } |
| 229 | 236 |
| 230 request.suggestions.add(suggestion); | 237 request.suggestions.add(suggestion); |
| 231 } | 238 } |
| 232 | 239 |
| 233 Future<bool> _addImportedElementSuggestions({bool typesOnly: false}) { | 240 void _addElementSuggestions(List<Element> elements) { |
| 241 elements.forEach((Element elem) { |
| 242 _addElementSuggestion(elem, CompletionRelevance.DEFAULT); |
| 243 }); |
| 244 } |
| 245 |
| 246 Future<bool> _addImportedElementSuggestions(AstNode node, {bool typesOnly: |
| 247 false}) { |
| 234 | 248 |
| 235 // Exclude elements from local library | 249 // Exclude elements from local library |
| 236 // because they are provided by LocalComputer | 250 // because they are provided by LocalComputer |
| 237 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); | 251 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); |
| 238 excludedLibs.add(request.unit.element.enclosingElement); | 252 excludedLibs.add(request.unit.element.enclosingElement); |
| 239 | 253 |
| 240 // Include explicitly imported elements | 254 // Include explicitly imported elements |
| 255 Map<String, ClassElement> classMap = new Map<String, ClassElement>(); |
| 241 request.unit.directives.forEach((Directive directive) { | 256 request.unit.directives.forEach((Directive directive) { |
| 242 if (directive is ImportDirective) { | 257 if (directive is ImportDirective) { |
| 243 ImportElement importElem = directive.element; | 258 ImportElement importElem = directive.element; |
| 244 if (importElem != null && importElem.importedLibrary != null) { | 259 if (importElem != null && importElem.importedLibrary != null) { |
| 245 if (directive.prefix == null) { | 260 if (directive.prefix == null) { |
| 246 Namespace importNamespace = | 261 Namespace importNamespace = |
| 247 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); | 262 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); |
| 248 importNamespace.definedNames.forEach((_, Element element) { | 263 // Include top level elements |
| 249 if (!typesOnly || element is ClassElement) { | 264 importNamespace.definedNames.forEach((String name, Element elem) { |
| 250 _addElementSuggestion(element, CompletionRelevance.DEFAULT); | 265 if (elem is ClassElement) { |
| 266 classMap[name] = elem; |
| 267 _addElementSuggestion(elem, CompletionRelevance.DEFAULT); |
| 268 } else if (!typesOnly) { |
| 269 _addElementSuggestion(elem, CompletionRelevance.DEFAULT); |
| 251 } | 270 } |
| 252 }); | 271 }); |
| 253 } else { | 272 } else { |
| 254 // Exclude elements from prefixed imports | 273 // Exclude elements from prefixed imports |
| 255 // because they are provided by InvocationComputer | 274 // because they are provided by InvocationComputer |
| 256 excludedLibs.add(importElem.importedLibrary); | 275 excludedLibs.add(importElem.importedLibrary); |
| 257 _addLibraryPrefixSuggestion(importElem); | 276 _addLibraryPrefixSuggestion(importElem); |
| 258 } | 277 } |
| 259 } | 278 } |
| 260 } | 279 } |
| 261 }); | 280 }); |
| 262 | 281 |
| 263 // Include implicitly imported dart:core elements | 282 // Include implicitly imported dart:core elements |
| 264 Source coreUri = request.context.sourceFactory.forUri('dart:core'); | 283 Source coreUri = request.context.sourceFactory.forUri('dart:core'); |
| 265 LibraryElement coreLib = request.context.getLibraryElement(coreUri); | 284 LibraryElement coreLib = request.context.getLibraryElement(coreUri); |
| 266 Namespace coreNamespace = | 285 Namespace coreNamespace = |
| 267 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | 286 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); |
| 268 coreNamespace.definedNames.forEach((_, Element element) { | 287 coreNamespace.definedNames.forEach((String name, Element elem) { |
| 269 if (!typesOnly || element is ClassElement) { | 288 if (elem is ClassElement) { |
| 270 _addElementSuggestion(element, CompletionRelevance.DEFAULT); | 289 classMap[name] = elem; |
| 290 _addElementSuggestion(elem, CompletionRelevance.DEFAULT); |
| 291 } else if (!typesOnly) { |
| 292 _addElementSuggestion(elem, CompletionRelevance.DEFAULT); |
| 271 } | 293 } |
| 272 }); | 294 }); |
| 273 | 295 |
| 296 // Build a list of inherited types that are imported |
| 297 // and include any inherited imported members |
| 298 var classDecl = node.getAncestor((p) => p is ClassDeclaration); |
| 299 if (classDecl is ClassDeclaration) { |
| 300 List<String> inheritedTypes = new List<String>(); |
| 301 visitInheritedTypes(classDecl, (ClassDeclaration classDecl) { |
| 302 // ignored |
| 303 }, (String typeName) { |
| 304 inheritedTypes.add(typeName); |
| 305 }); |
| 306 Set<String> visited = new Set<String>(); |
| 307 while (inheritedTypes.length > 0) { |
| 308 String name = inheritedTypes.removeLast(); |
| 309 ClassElement elem = classMap[name]; |
| 310 if (visited.add(name) && elem != null) { |
| 311 _addElementSuggestions(elem.accessors); |
| 312 _addElementSuggestions(elem.methods); |
| 313 elem.allSupertypes.forEach((InterfaceType type) { |
| 314 if (visited.add(type.name)) { |
| 315 _addElementSuggestions(type.accessors); |
| 316 _addElementSuggestions(type.methods); |
| 317 } |
| 318 }); |
| 319 } |
| 320 } |
| 321 } |
| 322 |
| 274 // Add non-imported elements as low relevance | 323 // Add non-imported elements as low relevance |
| 275 var future = request.searchEngine.searchTopLevelDeclarations(''); | 324 var future = request.searchEngine.searchTopLevelDeclarations(''); |
| 276 return future.then((List<SearchMatch> matches) { | 325 return future.then((List<SearchMatch> matches) { |
| 277 Set<String> completionSet = new Set<String>(); | 326 Set<String> completionSet = new Set<String>(); |
| 278 request.suggestions.forEach((CompletionSuggestion suggestion) { | 327 request.suggestions.forEach((CompletionSuggestion suggestion) { |
| 279 completionSet.add(suggestion.completion); | 328 completionSet.add(suggestion.completion); |
| 280 }); | 329 }); |
| 281 matches.forEach((SearchMatch match) { | 330 matches.forEach((SearchMatch match) { |
| 282 if (match.kind == MatchKind.DECLARATION) { | 331 if (match.kind == MatchKind.DECLARATION) { |
| 283 Element element = match.element; | 332 Element element = match.element; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 306 importElem.isDeprecated, | 355 importElem.isDeprecated, |
| 307 false); | 356 false); |
| 308 LibraryElement lib = importElem.importedLibrary; | 357 LibraryElement lib = importElem.importedLibrary; |
| 309 if (lib != null) { | 358 if (lib != null) { |
| 310 suggestion.element = newElement_fromEngine(lib); | 359 suggestion.element = newElement_fromEngine(lib); |
| 311 } | 360 } |
| 312 request.suggestions.add(suggestion); | 361 request.suggestions.add(suggestion); |
| 313 } | 362 } |
| 314 } | 363 } |
| 315 } | 364 } |
| OLD | NEW |