| Index: pkg/analysis_server/lib/src/services/completion/keyword_contributor.dart
|
| diff --git a/pkg/analysis_server/lib/src/services/completion/keyword_contributor.dart b/pkg/analysis_server/lib/src/services/completion/keyword_contributor.dart
|
| index c38a01f647546cd978026b9d8b93bdad0fd28591..ec230071b8b7cfe092a6fca0799fdfc9a3c60db5 100644
|
| --- a/pkg/analysis_server/lib/src/services/completion/keyword_contributor.dart
|
| +++ b/pkg/analysis_server/lib/src/services/completion/keyword_contributor.dart
|
| @@ -11,6 +11,9 @@ import 'package:analysis_server/src/services/completion/dart_completion_manager.
|
| import 'package:analyzer/src/generated/ast.dart';
|
| import 'package:analyzer/src/generated/scanner.dart';
|
|
|
| +const ASYNC = 'async';
|
| +const AWAIT = 'await';
|
| +
|
| /**
|
| * A contributor for calculating `completion.getSuggestions` request results
|
| * for the local library in which the completion is requested.
|
| @@ -50,22 +53,18 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| if (entity == node.name) {
|
| return;
|
| }
|
| - if (entity == node.rightBracket || entity is ClassMember) {
|
| - _addSuggestions([
|
| - Keyword.CONST,
|
| - Keyword.DYNAMIC,
|
| - Keyword.FACTORY,
|
| - Keyword.FINAL,
|
| - Keyword.GET,
|
| - Keyword.OPERATOR,
|
| - Keyword.SET,
|
| - Keyword.STATIC,
|
| - Keyword.VAR,
|
| - Keyword.VOID
|
| - ]);
|
| - return;
|
| + if (entity == node.rightBracket) {
|
| + _addClassBodyKeywords();
|
| + } else if (entity is ClassMember) {
|
| + _addClassBodyKeywords();
|
| + int index = node.members.indexOf(entity);
|
| + ClassMember previous = index > 0 ? node.members[index - 1] : null;
|
| + if (previous is MethodDeclaration && previous.body is EmptyFunctionBody) {
|
| + _addSuggestion2(ASYNC);
|
| + }
|
| + } else {
|
| + _addClassDeclarationKeywords(node);
|
| }
|
| - _addClassDeclarationKeywords(node);
|
| }
|
|
|
| @override
|
| @@ -104,16 +103,19 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| [Keyword.EXPORT, Keyword.IMPORT, Keyword.PART], DART_RELEVANCE_HIGH);
|
| }
|
| if (entity == null || entity is Declaration) {
|
| - _addSuggestions([
|
| - Keyword.ABSTRACT,
|
| - Keyword.CLASS,
|
| - Keyword.CONST,
|
| - Keyword.DYNAMIC,
|
| - Keyword.FINAL,
|
| - Keyword.TYPEDEF,
|
| - Keyword.VAR,
|
| - Keyword.VOID
|
| - ], DART_RELEVANCE_HIGH);
|
| + if (previousMember is FunctionDeclaration &&
|
| + previousMember.functionExpression is FunctionExpression &&
|
| + previousMember.functionExpression.body is EmptyFunctionBody) {
|
| + _addSuggestion2(ASYNC, DART_RELEVANCE_HIGH);
|
| + }
|
| + _addCompilationUnitKeywords();
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitExpressionFunctionBody(ExpressionFunctionBody node) {
|
| + if (entity == node.expression) {
|
| + _addStatementKeywords(node);
|
| }
|
| }
|
|
|
| @@ -127,6 +129,18 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| }
|
|
|
| @override
|
| + visitFunctionExpression(FunctionExpression node) {
|
| + if (entity == node.body) {
|
| + _addSuggestion2(ASYNC, DART_RELEVANCE_HIGH);
|
| + if (node.body is EmptyFunctionBody &&
|
| + node.parent is FunctionDeclaration &&
|
| + node.parent.parent is CompilationUnit) {
|
| + _addCompilationUnitKeywords();
|
| + }
|
| + }
|
| + }
|
| +
|
| + @override
|
| visitIfStatement(IfStatement node) {
|
| if (entity == node.thenStatement) {
|
| _addStatementKeywords(node);
|
| @@ -145,6 +159,33 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| }
|
| }
|
|
|
| + @override
|
| + visitMethodDeclaration(MethodDeclaration node) {
|
| + if (entity == node.body) {
|
| + if (node.body is EmptyFunctionBody) {
|
| + _addClassBodyKeywords();
|
| + _addSuggestion2(ASYNC);
|
| + } else {
|
| + _addSuggestion2(ASYNC, DART_RELEVANCE_HIGH);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _addClassBodyKeywords() {
|
| + _addSuggestions([
|
| + Keyword.CONST,
|
| + Keyword.DYNAMIC,
|
| + Keyword.FACTORY,
|
| + Keyword.FINAL,
|
| + Keyword.GET,
|
| + Keyword.OPERATOR,
|
| + Keyword.SET,
|
| + Keyword.STATIC,
|
| + Keyword.VAR,
|
| + Keyword.VOID
|
| + ]);
|
| + }
|
| +
|
| void _addClassDeclarationKeywords(ClassDeclaration node) {
|
| // Very simplistic suggestion because analyzer will warn if
|
| // the extends / with / implements keywords are out of order
|
| @@ -158,6 +199,19 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| }
|
| }
|
|
|
| + void _addCompilationUnitKeywords() {
|
| + _addSuggestions([
|
| + Keyword.ABSTRACT,
|
| + Keyword.CLASS,
|
| + Keyword.CONST,
|
| + Keyword.DYNAMIC,
|
| + Keyword.FINAL,
|
| + Keyword.TYPEDEF,
|
| + Keyword.VAR,
|
| + Keyword.VOID
|
| + ], DART_RELEVANCE_HIGH);
|
| + }
|
| +
|
| void _addImportDirectiveKeywords(ImportDirective node) {
|
| if (node.asKeyword == null) {
|
| _addSuggestion(Keyword.AS, DART_RELEVANCE_HIGH);
|
| @@ -168,9 +222,12 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| }
|
|
|
| void _addStatementKeywords(AstNode node) {
|
| - if (_isInClassMemberBody(node)) {
|
| + if (_inClassMemberBody(node)) {
|
| _addSuggestions([Keyword.SUPER, Keyword.THIS,]);
|
| }
|
| + if (_inAsyncMethodOrFunction(node)) {
|
| + _addSuggestion2(AWAIT);
|
| + }
|
| _addSuggestions([
|
| Keyword.ASSERT,
|
| Keyword.CASE,
|
| @@ -192,8 +249,12 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| }
|
|
|
| void _addSuggestion(Keyword keyword,
|
| - [int relevance = DART_RELEVANCE_DEFAULT]) {
|
| - String completion = keyword.syntax;
|
| + [int relevance = DART_RELEVANCE_KEYWORD]) {
|
| + _addSuggestion2(keyword.syntax, relevance);
|
| + }
|
| +
|
| + void _addSuggestion2(String completion,
|
| + [int relevance = DART_RELEVANCE_KEYWORD]) {
|
| request.addSuggestion(new CompletionSuggestion(
|
| CompletionSuggestionKind.KEYWORD, relevance, completion,
|
| completion.length, 0, false, false));
|
| @@ -206,7 +267,12 @@ class _KeywordVisitor extends GeneralizingAstVisitor {
|
| });
|
| }
|
|
|
| - bool _isInClassMemberBody(AstNode node) {
|
| + bool _inAsyncMethodOrFunction(AstNode node) {
|
| + FunctionBody body = node.getAncestor((n) => n is FunctionBody);
|
| + return body != null && body.isAsynchronous;
|
| + }
|
| +
|
| + bool _inClassMemberBody(AstNode node) {
|
| while (true) {
|
| AstNode body = node.getAncestor((n) => n is FunctionBody);
|
| if (body == null) {
|
|
|