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.contributor.dart.local_ref; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'package:analysis_server/plugin/protocol/protocol.dart' as protocol |
| 10 show Element, ElementKind; |
| 11 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.
dart'; |
| 12 import 'package:analysis_server/src/services/completion/dart/completion_manager.
dart' |
| 13 show DartCompletionRequestImpl; |
| 14 import 'package:analysis_server/src/services/completion/local_declaration_visito
r.dart' |
| 15 show LocalDeclarationVisitor; |
| 16 import 'package:analysis_server/src/services/completion/optype.dart'; |
| 17 import 'package:analyzer/src/generated/ast.dart'; |
| 18 import 'package:analyzer/src/generated/scanner.dart'; |
| 19 import 'package:analyzer/src/generated/source.dart'; |
| 20 |
| 21 import '../../../protocol_server.dart' |
| 22 show CompletionSuggestion, CompletionSuggestionKind, Location; |
| 23 |
| 24 const DYNAMIC = 'dynamic'; |
| 25 |
| 26 final TypeName NO_RETURN_TYPE = new TypeName( |
| 27 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '', 0)), null); |
| 28 |
| 29 /** |
| 30 * A contributor for calculating label suggestions. |
| 31 */ |
| 32 class LabelContributor extends DartCompletionContributor { |
| 33 @override |
| 34 Future<List<CompletionSuggestion>> computeSuggestions( |
| 35 DartCompletionRequest request) async { |
| 36 OpType optype = (request as DartCompletionRequestImpl).opType; |
| 37 |
| 38 // Collect suggestions from the specific child [AstNode] that contains |
| 39 // the completion offset and all of its parents recursively. |
| 40 List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; |
| 41 if (!optype.isPrefixed) { |
| 42 if (optype.includeStatementLabelSuggestions || |
| 43 optype.includeCaseLabelSuggestions) { |
| 44 new _LabelVisitor(request, optype.includeStatementLabelSuggestions, |
| 45 optype.includeCaseLabelSuggestions, suggestions) |
| 46 .visit(request.target.containingNode); |
| 47 } |
| 48 } |
| 49 return suggestions; |
| 50 } |
| 51 } |
| 52 |
| 53 /** |
| 54 * A visitor for collecting suggestions for break and continue labels. |
| 55 */ |
| 56 class _LabelVisitor extends LocalDeclarationVisitor { |
| 57 final DartCompletionRequest request; |
| 58 final List<CompletionSuggestion> suggestions; |
| 59 |
| 60 /** |
| 61 * True if statement labels should be included as suggestions. |
| 62 */ |
| 63 final bool includeStatementLabels; |
| 64 |
| 65 /** |
| 66 * True if case labels should be included as suggestions. |
| 67 */ |
| 68 final bool includeCaseLabels; |
| 69 |
| 70 _LabelVisitor(DartCompletionRequest request, this.includeStatementLabels, |
| 71 this.includeCaseLabels, this.suggestions) |
| 72 : super(request.offset), |
| 73 request = request; |
| 74 |
| 75 @override |
| 76 void declaredClass(ClassDeclaration declaration) { |
| 77 // ignored |
| 78 } |
| 79 |
| 80 @override |
| 81 void declaredClassTypeAlias(ClassTypeAlias declaration) { |
| 82 // ignored |
| 83 } |
| 84 |
| 85 @override |
| 86 void declaredField(FieldDeclaration fieldDecl, VariableDeclaration varDecl) { |
| 87 // ignored |
| 88 } |
| 89 |
| 90 @override |
| 91 void declaredFunction(FunctionDeclaration declaration) { |
| 92 // ignored |
| 93 } |
| 94 |
| 95 @override |
| 96 void declaredFunctionTypeAlias(FunctionTypeAlias declaration) { |
| 97 // ignored |
| 98 } |
| 99 |
| 100 @override |
| 101 void declaredLabel(Label label, bool isCaseLabel) { |
| 102 if (isCaseLabel ? includeCaseLabels : includeStatementLabels) { |
| 103 CompletionSuggestion suggestion = _addSuggestion(label.label); |
| 104 if (suggestion != null) { |
| 105 suggestion.element = createLocalElement( |
| 106 request.source, protocol.ElementKind.LABEL, label.label, |
| 107 returnType: NO_RETURN_TYPE); |
| 108 } |
| 109 } |
| 110 } |
| 111 |
| 112 @override |
| 113 void declaredLocalVar(SimpleIdentifier name, TypeName type) { |
| 114 // ignored |
| 115 } |
| 116 |
| 117 @override |
| 118 void declaredMethod(MethodDeclaration declaration) { |
| 119 // ignored |
| 120 } |
| 121 |
| 122 @override |
| 123 void declaredParam(SimpleIdentifier name, TypeName type) { |
| 124 // ignored |
| 125 } |
| 126 |
| 127 @override |
| 128 void declaredTopLevelVar( |
| 129 VariableDeclarationList varList, VariableDeclaration varDecl) { |
| 130 // ignored |
| 131 } |
| 132 |
| 133 @override |
| 134 void visitFunctionExpression(FunctionExpression node) { |
| 135 // Labels are only accessible within the local function, so stop visiting |
| 136 // once we reach a function boundary. |
| 137 finished(); |
| 138 } |
| 139 |
| 140 @override |
| 141 void visitMethodDeclaration(MethodDeclaration node) { |
| 142 // Labels are only accessible within the local function, so stop visiting |
| 143 // once we reach a function boundary. |
| 144 finished(); |
| 145 } |
| 146 |
| 147 CompletionSuggestion _addSuggestion(SimpleIdentifier id) { |
| 148 if (id != null) { |
| 149 String completion = id.name; |
| 150 if (completion != null && completion.length > 0 && completion != '_') { |
| 151 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 152 CompletionSuggestionKind.IDENTIFIER, |
| 153 DART_RELEVANCE_DEFAULT, |
| 154 completion, |
| 155 completion.length, |
| 156 0, |
| 157 false, |
| 158 false); |
| 159 suggestions.add(suggestion); |
| 160 return suggestion; |
| 161 } |
| 162 } |
| 163 return null; |
| 164 } |
| 165 } |
| 166 |
| 167 /** |
| 168 * Create a new protocol Element for inclusion in a completion suggestion. |
| 169 */ |
| 170 protocol.Element createLocalElement( |
| 171 Source source, protocol.ElementKind kind, SimpleIdentifier id, |
| 172 {String parameters, |
| 173 TypeName returnType, |
| 174 bool isAbstract: false, |
| 175 bool isDeprecated: false}) { |
| 176 String name; |
| 177 Location location; |
| 178 if (id != null) { |
| 179 name = id.name; |
| 180 // TODO(danrubel) use lineInfo to determine startLine and startColumn |
| 181 location = new Location(source.fullName, id.offset, id.length, 0, 0); |
| 182 } else { |
| 183 name = ''; |
| 184 location = new Location(source.fullName, -1, 0, 1, 0); |
| 185 } |
| 186 int flags = protocol.Element.makeFlags( |
| 187 isAbstract: isAbstract, |
| 188 isDeprecated: isDeprecated, |
| 189 isPrivate: Identifier.isPrivateName(name)); |
| 190 return new protocol.Element(kind, name, flags, |
| 191 location: location, |
| 192 parameters: parameters, |
| 193 returnType: nameForType(returnType)); |
| 194 } |
| 195 |
| 196 /** |
| 197 * Create a new suggestion for the given field. |
| 198 * Return the new suggestion or `null` if it could not be created. |
| 199 */ |
| 200 CompletionSuggestion createLocalFieldSuggestion( |
| 201 Source source, FieldDeclaration fieldDecl, VariableDeclaration varDecl) { |
| 202 bool deprecated = isDeprecated(fieldDecl) || isDeprecated(varDecl); |
| 203 TypeName type = fieldDecl.fields.type; |
| 204 return createLocalSuggestion( |
| 205 varDecl.name, deprecated, DART_RELEVANCE_LOCAL_FIELD, type, |
| 206 classDecl: fieldDecl.parent, |
| 207 element: createLocalElement( |
| 208 source, protocol.ElementKind.FIELD, varDecl.name, |
| 209 returnType: type, isDeprecated: deprecated)); |
| 210 } |
| 211 |
| 212 /** |
| 213 * Create a new suggestion based upon the given information. |
| 214 * Return the new suggestion or `null` if it could not be created. |
| 215 */ |
| 216 CompletionSuggestion createLocalSuggestion(SimpleIdentifier id, |
| 217 bool isDeprecated, int defaultRelevance, TypeName returnType, |
| 218 {ClassDeclaration classDecl, protocol.Element element}) { |
| 219 if (id == null) { |
| 220 return null; |
| 221 } |
| 222 String completion = id.name; |
| 223 if (completion == null || completion.length <= 0 || completion == '_') { |
| 224 return null; |
| 225 } |
| 226 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 227 CompletionSuggestionKind.INVOCATION, |
| 228 isDeprecated ? DART_RELEVANCE_LOW : defaultRelevance, |
| 229 completion, |
| 230 completion.length, |
| 231 0, |
| 232 isDeprecated, |
| 233 false, |
| 234 returnType: nameForType(returnType), |
| 235 element: element); |
| 236 if (classDecl != null) { |
| 237 SimpleIdentifier classId = classDecl.name; |
| 238 if (classId != null) { |
| 239 String className = classId.name; |
| 240 if (className != null && className.length > 0) { |
| 241 suggestion.declaringType = className; |
| 242 } |
| 243 } |
| 244 } |
| 245 return suggestion; |
| 246 } |
| 247 |
| 248 /** |
| 249 * Return `true` if the @deprecated annotation is present |
| 250 */ |
| 251 bool isDeprecated(AnnotatedNode node) { |
| 252 if (node != null) { |
| 253 NodeList<Annotation> metadata = node.metadata; |
| 254 if (metadata != null) { |
| 255 return metadata.any((Annotation a) { |
| 256 return a.name is SimpleIdentifier && a.name.name == 'deprecated'; |
| 257 }); |
| 258 } |
| 259 } |
| 260 return false; |
| 261 } |
| 262 |
| 263 /** |
| 264 * Return the name for the given type. |
| 265 */ |
| 266 String nameForType(TypeName type) { |
| 267 if (type == NO_RETURN_TYPE) { |
| 268 return null; |
| 269 } |
| 270 if (type == null) { |
| 271 return DYNAMIC; |
| 272 } |
| 273 Identifier id = type.name; |
| 274 if (id == null) { |
| 275 return DYNAMIC; |
| 276 } |
| 277 String name = id.name; |
| 278 if (name == null || name.length <= 0) { |
| 279 return DYNAMIC; |
| 280 } |
| 281 TypeArgumentList typeArgs = type.typeArguments; |
| 282 if (typeArgs != null) { |
| 283 //TODO (danrubel) include type arguments |
| 284 } |
| 285 return name; |
| 286 } |
OLD | NEW |