Index: pkg/analysis_server/lib/src/services/completion/local_computer.dart |
diff --git a/pkg/analysis_server/lib/src/services/completion/local_computer.dart b/pkg/analysis_server/lib/src/services/completion/local_computer.dart |
index 06f299c8df2bf018d386e4b5ce4f992c0ffca60c..84edaef9862aab3891d3a38aaaa02ba2f4193bc7 100644 |
--- a/pkg/analysis_server/lib/src/services/completion/local_computer.dart |
+++ b/pkg/analysis_server/lib/src/services/completion/local_computer.dart |
@@ -6,9 +6,11 @@ library services.completion.computer.dart.local; |
import 'dart:async'; |
-import 'package:analysis_server/src/protocol.dart'; |
+import 'package:analysis_server/src/protocol.dart' as protocol show Element, ElementKind; |
+import 'package:analysis_server/src/protocol.dart' hide Element, ElementKind; |
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart'; |
import 'package:analyzer/src/generated/ast.dart'; |
+import 'package:analyzer/src/generated/scanner.dart'; |
/** |
* A computer for calculating `completion.getSuggestions` request results |
@@ -43,6 +45,16 @@ class LocalComputer extends DartCompletionComputer { |
* that contains the completion offset to the [CompilationUnit]. |
*/ |
class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
+ static const DYNAMIC = 'dynamic'; |
+ |
+ static final TypeName NO_RETURN_TYPE = new TypeName( |
+ new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '', 0)), |
+ null); |
+ |
+ static final TypeName STACKTRACE_TYPE = new TypeName( |
+ new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, 'StackTrace', 0)), |
+ null); |
+ |
final DartCompletionRequest request; |
_LocalVisitor(this.request); |
@@ -56,15 +68,11 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
// _addSuggestion(label.label, CompletionSuggestionKind.LABEL); |
}); |
} else if (stmt is VariableDeclarationStatement) { |
- VariableDeclarationList varList = stmt.variables; |
+ var varList = stmt.variables; |
if (varList != null) { |
varList.variables.forEach((VariableDeclaration varDecl) { |
if (varDecl.end < request.offset) { |
- _addSuggestion( |
- varDecl.name, |
- CompletionSuggestionKind.LOCAL_VARIABLE, |
- varList.type, |
- null); |
+ _addLocalVarSuggestion(varDecl.name, varList.type); |
} |
}); |
} |
@@ -76,17 +84,9 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
@override |
visitCatchClause(CatchClause node) { |
- _addSuggestion( |
- node.exceptionParameter, |
- CompletionSuggestionKind.PARAMETER, |
- node.exceptionType, |
- null); |
- // TODO (danrubel) add stack trace types |
- _addSuggestion( |
- node.stackTraceParameter, |
- CompletionSuggestionKind.PARAMETER, |
- null, |
- null); |
+ _addParamSuggestion(node.exceptionParameter, node.exceptionType); |
+ CompletionSuggestion suggestion = |
+ _addParamSuggestion(node.stackTraceParameter, STACKTRACE_TYPE); |
visitNode(node); |
} |
@@ -94,16 +94,9 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
visitClassDeclaration(ClassDeclaration node) { |
node.members.forEach((ClassMember classMbr) { |
if (classMbr is FieldDeclaration) { |
- _addVarListSuggestions( |
- classMbr.fields, |
- CompletionSuggestionKind.FIELD, |
- node); |
+ _addFieldSuggestions(node, classMbr); |
} else if (classMbr is MethodDeclaration) { |
- _addSuggestion( |
- classMbr.name, |
- CompletionSuggestionKind.METHOD_NAME, |
- classMbr.returnType, |
- node); |
+ _addMethodSuggestion(node, classMbr); |
} |
}); |
visitNode(node); |
@@ -113,33 +106,18 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
visitCompilationUnit(CompilationUnit node) { |
node.directives.forEach((Directive directive) { |
if (directive is ImportDirective) { |
- _addSuggestion( |
- directive.prefix, |
- CompletionSuggestionKind.LIBRARY_PREFIX, |
- null, |
- null); |
+ _addLibraryPrefixSuggestion(directive); |
} |
}); |
node.declarations.forEach((Declaration declaration) { |
if (declaration is ClassDeclaration) { |
- _addSuggestion( |
- declaration.name, |
- CompletionSuggestionKind.CLASS, |
- null, |
- null); |
+ _addClassSuggestion(declaration); |
} else if (declaration is EnumDeclaration) { |
// _addSuggestion(d.name, CompletionSuggestionKind.ENUM); |
} else if (declaration is FunctionDeclaration) { |
- _addSuggestion( |
- declaration.name, |
- CompletionSuggestionKind.FUNCTION, |
- declaration.returnType, |
- null); |
+ _addFunctionSuggestion(declaration); |
} else if (declaration is TopLevelVariableDeclaration) { |
- _addVarListSuggestions( |
- declaration.variables, |
- CompletionSuggestionKind.TOP_LEVEL_VARIABLE, |
- null); |
+ _addTopLevelVarSuggestions(declaration.variables); |
} else if (declaration is ClassTypeAlias) { |
_addSuggestion( |
declaration.name, |
@@ -171,21 +149,28 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
@override |
visitForEachStatement(ForEachStatement node) { |
- // TODO (danrubel) supply type of local variable |
- _addSuggestion( |
- node.identifier, |
- CompletionSuggestionKind.LOCAL_VARIABLE, |
- null, |
- null); |
+ SimpleIdentifier id; |
+ TypeName type; |
+ DeclaredIdentifier loopVar = node.loopVariable; |
+ if (loopVar != null) { |
+ id = loopVar.identifier; |
+ type = loopVar.type; |
+ } else { |
+ id = node.identifier; |
+ type = null; |
+ } |
+ _addLocalVarSuggestion(id, type); |
visitNode(node); |
} |
@override |
visitForStatement(ForStatement node) { |
- _addVarListSuggestions( |
- node.variables, |
- CompletionSuggestionKind.LOCAL_VARIABLE, |
- null); |
+ var varList = node.variables; |
+ if (varList != null) { |
+ varList.variables.forEach((VariableDeclaration varDecl) { |
+ _addLocalVarSuggestion(varDecl.name, varList.type); |
+ }); |
+ } |
visitNode(node); |
} |
@@ -224,6 +209,106 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
} |
} |
+ void _addClassSuggestion(ClassDeclaration declaration) { |
+ CompletionSuggestion suggestion = |
+ _addSuggestion(declaration.name, CompletionSuggestionKind.CLASS, null, null); |
+ if (suggestion != null) { |
+ suggestion.element = _createElement( |
+ protocol.ElementKind.CLASS, |
+ declaration.name, |
+ NO_RETURN_TYPE, |
+ declaration.isAbstract, |
+ _isDeprecated(declaration.metadata)); |
+ } |
+ } |
+ |
+ void _addFieldSuggestions(ClassDeclaration node, FieldDeclaration fieldDecl) { |
+ bool isDeprecated = _isDeprecated(fieldDecl.metadata); |
+ fieldDecl.fields.variables.forEach((VariableDeclaration varDecl) { |
+ CompletionSuggestion suggestion = _addSuggestion( |
+ varDecl.name, |
+ CompletionSuggestionKind.GETTER, |
+ fieldDecl.fields.type, |
+ node); |
+ if (suggestion != null) { |
+ suggestion.element = _createElement( |
+ protocol.ElementKind.GETTER, |
+ varDecl.name, |
+ fieldDecl.fields.type, |
+ false, |
+ isDeprecated || _isDeprecated(varDecl.metadata)); |
+ } |
+ }); |
+ } |
+ |
+ void _addFunctionSuggestion(FunctionDeclaration declaration) { |
+ CompletionSuggestion suggestion = _addSuggestion( |
+ declaration.name, |
+ CompletionSuggestionKind.FUNCTION, |
+ declaration.returnType, |
+ null); |
+ if (suggestion != null) { |
+ suggestion.element = _createElement( |
+ protocol.ElementKind.FUNCTION, |
+ declaration.name, |
+ declaration.returnType, |
+ false, |
+ _isDeprecated(declaration.metadata)); |
+ } |
+ } |
+ |
+ void _addLibraryPrefixSuggestion(ImportDirective directive) { |
+ CompletionSuggestion suggestion = _addSuggestion( |
+ directive.prefix, |
+ CompletionSuggestionKind.LIBRARY_PREFIX, |
+ null, |
+ null); |
+ if (suggestion != null) { |
+ suggestion.element = _createElement( |
+ protocol.ElementKind.LIBRARY, |
+ directive.prefix, |
+ NO_RETURN_TYPE, |
+ false, |
+ false); |
+ } |
+ } |
+ |
+ void _addLocalVarSuggestion(SimpleIdentifier id, TypeName returnType) { |
+ CompletionSuggestion suggestion = |
+ _addSuggestion(id, CompletionSuggestionKind.LOCAL_VARIABLE, returnType, null); |
+ if (suggestion != null) { |
+ suggestion.element = _createElement( |
+ protocol.ElementKind.LOCAL_VARIABLE, |
+ id, |
+ returnType, |
+ false, |
+ false); |
+ } |
+ } |
+ |
+ void _addMethodSuggestion(ClassDeclaration node, MethodDeclaration classMbr) { |
+ protocol.ElementKind kind; |
+ CompletionSuggestionKind csKind; |
+ if (classMbr.isGetter) { |
+ kind = protocol.ElementKind.GETTER; |
+ csKind = CompletionSuggestionKind.GETTER; |
+ } else if (classMbr.isSetter) { |
+ kind = protocol.ElementKind.SETTER; |
+ csKind = CompletionSuggestionKind.SETTER; |
+ } else { |
+ kind = protocol.ElementKind.METHOD; |
+ csKind = CompletionSuggestionKind.METHOD; |
+ } |
+ CompletionSuggestion suggestion = |
+ _addSuggestion(classMbr.name, csKind, classMbr.returnType, node); |
+ suggestion.element = _createElement( |
+ kind, |
+ classMbr.name, |
+ classMbr.returnType, |
+ classMbr.isAbstract, |
+ _isDeprecated(classMbr.metadata)); |
+ } |
+ |
void _addParamListSuggestions(FormalParameterList paramList) { |
if (paramList != null) { |
paramList.parameters.forEach((FormalParameter param) { |
@@ -241,17 +326,24 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
} else if (normalParam is SimpleFormalParameter) { |
type = normalParam.type; |
} |
- _addSuggestion( |
- param.identifier, |
- CompletionSuggestionKind.PARAMETER, |
- type, |
- null); |
+ _addParamSuggestion(param.identifier, type); |
}); |
} |
} |
- void _addSuggestion(SimpleIdentifier id, CompletionSuggestionKind kind, |
- TypeName typeName, ClassDeclaration classDecl) { |
+ CompletionSuggestion _addParamSuggestion(SimpleIdentifier identifier, |
+ TypeName type) { |
+ CompletionSuggestion suggestion = |
+ _addSuggestion(identifier, CompletionSuggestionKind.PARAMETER, type, null); |
+ if (suggestion != null) { |
+ suggestion.element = |
+ _createElement(protocol.ElementKind.PARAMETER, identifier, type, false, false); |
+ } |
+ return suggestion; |
+ } |
+ |
+ CompletionSuggestion _addSuggestion(SimpleIdentifier id, |
+ CompletionSuggestionKind kind, TypeName typeName, ClassDeclaration classDecl) { |
if (id != null) { |
String completion = id.name; |
if (completion != null && completion.length > 0) { |
@@ -282,14 +374,80 @@ class _LocalVisitor extends GeneralizingAstVisitor<dynamic> { |
} |
} |
request.suggestions.add(suggestion); |
+ return suggestion; |
} |
} |
+ return null; |
} |
- void _addVarListSuggestions(VariableDeclarationList variables, |
- CompletionSuggestionKind kind, ClassDeclaration classDecl) { |
- variables.variables.forEach((VariableDeclaration varDecl) { |
- _addSuggestion(varDecl.name, kind, variables.type, classDecl); |
- }); |
+ void _addTopLevelVarSuggestions(VariableDeclarationList varList) { |
+ if (varList != null) { |
+ bool isDeprecated = _isDeprecated(varList.metadata); |
+ varList.variables.forEach((VariableDeclaration varDecl) { |
+ CompletionSuggestion suggestion = _addSuggestion( |
+ varDecl.name, |
+ CompletionSuggestionKind.TOP_LEVEL_VARIABLE, |
+ varList.type, |
+ null); |
+ if (suggestion != null) { |
+ suggestion.element = _createElement( |
+ protocol.ElementKind.TOP_LEVEL_VARIABLE, |
+ varDecl.name, |
+ varList.type, |
+ false, |
+ isDeprecated || _isDeprecated(varDecl.metadata)); |
+ } |
+ }); |
+ } |
+ } |
+ |
+ /** |
+ * Create a new protocol Element for inclusion in a completion suggestion. |
+ */ |
+ protocol.Element _createElement(protocol.ElementKind kind, |
+ SimpleIdentifier id, TypeName returnType, bool isAbstract, bool isDeprecated) { |
+ String name = id.name; |
+ int flags = protocol.Element.makeFlags( |
+ isAbstract: isAbstract, |
+ isDeprecated: isDeprecated, |
+ isPrivate: Identifier.isPrivateName(name)); |
+ return new protocol.Element( |
+ kind, |
+ name, |
+ flags, |
+ returnType: _nameForType(returnType)); |
+ } |
+ |
+ /** |
+ * Return `true` if the @deprecated annotation is present |
+ */ |
+ bool _isDeprecated(NodeList<Annotation> metadata) => |
+ metadata != null && |
+ metadata.any( |
+ (Annotation a) => a.name is SimpleIdentifier && a.name.name == 'deprecated'); |
+ |
+ /** |
+ * Return the name for the given type. |
+ */ |
+ String _nameForType(TypeName type) { |
+ if (type == NO_RETURN_TYPE) { |
+ return null; |
+ } |
+ if (type == null) { |
+ return DYNAMIC; |
+ } |
+ Identifier id = type.name; |
+ if (id == null) { |
+ return DYNAMIC; |
+ } |
+ String name = id.name; |
+ if (name == null || name.length <= 0) { |
+ return DYNAMIC; |
+ } |
+ TypeArgumentList typeArgs = type.typeArguments; |
+ if (typeArgs != null) { |
+ //TODO (danrubel) include type arguments |
+ } |
+ return name; |
} |
} |