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 |
deleted file mode 100644 |
index 0da2c8c1652cc8e69e924c34257f9c77ab7840cd..0000000000000000000000000000000000000000 |
--- a/pkg/analysis_server/lib/src/services/completion/keyword_contributor.dart |
+++ /dev/null |
@@ -1,525 +0,0 @@ |
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
- |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library services.completion.contributor.dart.keyword; |
- |
-import 'dart:async'; |
- |
-import 'package:analysis_server/plugin/protocol/protocol.dart'; |
-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'; |
- |
-const ASYNC = 'async'; |
-const AWAIT = 'await'; |
- |
-/** |
- * A contributor for calculating `completion.getSuggestions` request results |
- * for the local library in which the completion is requested. |
- */ |
-class KeywordContributor extends DartCompletionContributor { |
- @override |
- bool computeFast(DartCompletionRequest request) { |
- if (!request.target.isCommentText) { |
- request.target.containingNode.accept(new _KeywordVisitor(request)); |
- } |
- return true; |
- } |
- |
- @override |
- Future<bool> computeFull(DartCompletionRequest request) { |
- return new Future.value(false); |
- } |
-} |
- |
-/** |
- * A visitor for generating keyword suggestions. |
- */ |
-class _KeywordVisitor extends GeneralizingAstVisitor { |
- final DartCompletionRequest request; |
- final Object entity; |
- |
- _KeywordVisitor(DartCompletionRequest request) |
- : this.request = request, |
- this.entity = request.target.entity; |
- |
- @override |
- visitArgumentList(ArgumentList node) { |
- if (entity == node.rightParenthesis || |
- (entity is SimpleIdentifier && node.arguments.contains(entity))) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitBlock(Block node) { |
- if (entity is ExpressionStatement) { |
- Expression expression = (entity as ExpressionStatement).expression; |
- if (expression is SimpleIdentifier) { |
- Token token = expression.token; |
- Token previous = token.previous; |
- if (previous.isSynthetic) { |
- previous = previous.previous; |
- } |
- Token next = token.next; |
- if (next.isSynthetic) { |
- next = next.next; |
- } |
- if (previous.lexeme == ')' && next.lexeme == '{') { |
- _addSuggestion2(ASYNC); |
- } |
- } |
- } |
- _addStatementKeywords(node); |
- if (_inCatchClause(node)) { |
- _addSuggestion(Keyword.RETHROW, DART_RELEVANCE_KEYWORD - 1); |
- } |
- } |
- |
- @override |
- visitClassDeclaration(ClassDeclaration node) { |
- // Don't suggest class name |
- if (entity == node.name) { |
- 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); |
- } |
- } |
- |
- @override |
- visitCompilationUnit(CompilationUnit node) { |
- var previousMember = null; |
- for (var member in node.childEntities) { |
- if (entity == member) { |
- break; |
- } |
- previousMember = member; |
- } |
- if (previousMember is ClassDeclaration) { |
- if (previousMember.leftBracket == null || |
- previousMember.leftBracket.isSynthetic) { |
- // If the prior member is an unfinished class declaration |
- // then the user is probably finishing that |
- _addClassDeclarationKeywords(previousMember); |
- return; |
- } |
- } |
- if (previousMember is ImportDirective) { |
- if (previousMember.semicolon == null || |
- previousMember.semicolon.isSynthetic) { |
- // If the prior member is an unfinished import directive |
- // then the user is probably finishing that |
- _addImportDirectiveKeywords(previousMember); |
- return; |
- } |
- } |
- if (previousMember == null || previousMember is Directive) { |
- if (previousMember == null && |
- !node.directives.any((d) => d is LibraryDirective)) { |
- _addSuggestions([Keyword.LIBRARY], DART_RELEVANCE_HIGH); |
- } |
- _addSuggestions( |
- [Keyword.IMPORT, Keyword.EXPORT, Keyword.PART], DART_RELEVANCE_HIGH); |
- } |
- if (entity == null || entity is Declaration) { |
- if (previousMember is FunctionDeclaration && |
- previousMember.functionExpression is FunctionExpression && |
- previousMember.functionExpression.body is EmptyFunctionBody) { |
- _addSuggestion2(ASYNC, relevance: DART_RELEVANCE_HIGH); |
- } |
- _addCompilationUnitKeywords(); |
- } |
- } |
- |
- @override |
- visitExpression(Expression node) { |
- _addExpressionKeywords(node); |
- } |
- |
- @override |
- visitExpressionFunctionBody(ExpressionFunctionBody node) { |
- if (entity == node.expression) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitForEachStatement(ForEachStatement node) { |
- if (entity == node.inKeyword) { |
- Token previous = node.inKeyword.previous; |
- if (previous is SyntheticStringToken && previous.lexeme == 'in') { |
- previous = previous.previous; |
- } |
- if (previous != null && previous.type == TokenType.EQ) { |
- _addSuggestions([ |
- Keyword.CONST, |
- Keyword.FALSE, |
- Keyword.NEW, |
- Keyword.NULL, |
- Keyword.TRUE |
- ]); |
- } else { |
- _addSuggestion(Keyword.IN, DART_RELEVANCE_HIGH); |
- } |
- } |
- } |
- |
- @override |
- visitFormalParameterList(FormalParameterList node) { |
- AstNode constructorDeclaration = |
- node.getAncestor((p) => p is ConstructorDeclaration); |
- if (constructorDeclaration != null) { |
- _addSuggestions([Keyword.THIS]); |
- } |
- } |
- |
- @override |
- visitForStatement(ForStatement node) { |
- // Handle the degenerate case while typing - for (int x i^) |
- if (node.condition == entity && entity is SimpleIdentifier) { |
- Token entityToken = (entity as SimpleIdentifier).beginToken; |
- if (entityToken.previous.isSynthetic && |
- entityToken.previous.type == TokenType.SEMICOLON) { |
- _addSuggestion(Keyword.IN, DART_RELEVANCE_HIGH); |
- } |
- } |
- } |
- |
- @override |
- visitFunctionExpression(FunctionExpression node) { |
- if (entity == node.body) { |
- if (!node.body.isAsynchronous) { |
- _addSuggestion2(ASYNC, relevance: 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); |
- } else if (entity == node.condition) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitImportDirective(ImportDirective node) { |
- if (entity == node.asKeyword) { |
- if (node.deferredKeyword == null) { |
- _addSuggestion(Keyword.DEFERRED, DART_RELEVANCE_HIGH); |
- } |
- } |
- // Handle degenerate case where import statement does not have a semicolon |
- // and the cursor is in the uri string |
- if ((entity == node.semicolon && |
- node.uri != null && |
- node.uri.offset + 1 != request.offset) || |
- node.combinators.contains(entity)) { |
- _addImportDirectiveKeywords(node); |
- } |
- } |
- |
- @override |
- visitInstanceCreationExpression(InstanceCreationExpression node) { |
- if (entity == node.constructorName) { |
- // no keywords in 'new ^' expression |
- } else { |
- super.visitInstanceCreationExpression(node); |
- } |
- } |
- |
- @override |
- visitIsExpression(IsExpression node) { |
- if (entity == node.isOperator) { |
- _addSuggestion(Keyword.IS, DART_RELEVANCE_HIGH); |
- } else { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitLibraryIdentifier(LibraryIdentifier node) { |
- // no suggestions |
- } |
- |
- @override |
- visitMethodDeclaration(MethodDeclaration node) { |
- if (entity == node.body) { |
- if (node.body is EmptyFunctionBody) { |
- _addClassBodyKeywords(); |
- _addSuggestion2(ASYNC); |
- } else { |
- _addSuggestion2(ASYNC, relevance: DART_RELEVANCE_HIGH); |
- } |
- } |
- } |
- |
- @override |
- visitMethodInvocation(MethodInvocation node) { |
- if (entity == node.methodName) { |
- // no keywords in '.' expression |
- } else { |
- super.visitMethodInvocation(node); |
- } |
- } |
- |
- @override |
- visitNamedExpression(NamedExpression node) { |
- if (entity is SimpleIdentifier && entity == node.expression) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitNode(AstNode node) { |
- // ignored |
- } |
- |
- @override |
- visitPrefixedIdentifier(PrefixedIdentifier node) { |
- if (entity != node.identifier) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitPropertyAccess(PropertyAccess node) { |
- // suggestions before '.' but not after |
- if (entity != node.propertyName) { |
- super.visitPropertyAccess(node); |
- } |
- } |
- |
- @override |
- visitReturnStatement(ReturnStatement node) { |
- if (entity == node.expression) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- @override |
- visitStringLiteral(StringLiteral node) { |
- // ignored |
- } |
- |
- @override |
- visitSwitchStatement(SwitchStatement node) { |
- if (entity == node.expression) { |
- _addExpressionKeywords(node); |
- } else if (entity == node.rightBracket) { |
- if (node.members.isEmpty) { |
- _addSuggestions([Keyword.CASE, Keyword.DEFAULT], DART_RELEVANCE_HIGH); |
- } else { |
- _addSuggestions([Keyword.CASE, Keyword.DEFAULT]); |
- _addStatementKeywords(node); |
- } |
- } |
- if (node.members.contains(entity)) { |
- if (entity == node.members.first) { |
- _addSuggestions([Keyword.CASE, Keyword.DEFAULT], DART_RELEVANCE_HIGH); |
- } else { |
- _addSuggestions([Keyword.CASE, Keyword.DEFAULT]); |
- _addStatementKeywords(node); |
- } |
- } |
- } |
- |
- @override |
- visitVariableDeclaration(VariableDeclaration node) { |
- if (entity == node.initializer) { |
- _addExpressionKeywords(node); |
- } |
- } |
- |
- 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 |
- if (node.extendsClause == null) { |
- _addSuggestion(Keyword.EXTENDS, DART_RELEVANCE_HIGH); |
- } else if (node.withClause == null) { |
- _addSuggestion(Keyword.WITH, DART_RELEVANCE_HIGH); |
- } |
- if (node.implementsClause == null) { |
- _addSuggestion(Keyword.IMPLEMENTS, DART_RELEVANCE_HIGH); |
- } |
- } |
- |
- void _addCompilationUnitKeywords() { |
- _addSuggestions([ |
- Keyword.ABSTRACT, |
- Keyword.CLASS, |
- Keyword.CONST, |
- Keyword.DYNAMIC, |
- Keyword.FINAL, |
- Keyword.TYPEDEF, |
- Keyword.VAR, |
- Keyword.VOID |
- ], DART_RELEVANCE_HIGH); |
- } |
- |
- void _addExpressionKeywords(AstNode node) { |
- _addSuggestions([ |
- Keyword.CONST, |
- Keyword.FALSE, |
- Keyword.NEW, |
- Keyword.NULL, |
- Keyword.TRUE, |
- ]); |
- if (_inClassMemberBody(node)) { |
- _addSuggestions([Keyword.SUPER, Keyword.THIS,]); |
- } |
- if (_inAsyncMethodOrFunction(node)) { |
- _addSuggestion2(AWAIT); |
- } |
- } |
- |
- void _addImportDirectiveKeywords(ImportDirective node) { |
- bool hasDeferredKeyword = node.deferredKeyword != null; |
- bool hasAsKeyword = node.asKeyword != null; |
- if (!hasAsKeyword) { |
- _addSuggestion(Keyword.AS, DART_RELEVANCE_HIGH); |
- } |
- if (!hasDeferredKeyword) { |
- if (!hasAsKeyword) { |
- _addSuggestion2('deferred as', relevance: DART_RELEVANCE_HIGH); |
- } else if (entity == node.asKeyword) { |
- _addSuggestion(Keyword.DEFERRED, DART_RELEVANCE_HIGH); |
- } |
- } |
- if (!hasDeferredKeyword || hasAsKeyword) { |
- if (node.combinators.isEmpty) { |
- _addSuggestion2('show', relevance: DART_RELEVANCE_HIGH); |
- _addSuggestion2('hide', relevance: DART_RELEVANCE_HIGH); |
- } |
- } |
- } |
- |
- void _addStatementKeywords(AstNode node) { |
- if (_inClassMemberBody(node)) { |
- _addSuggestions([Keyword.SUPER, Keyword.THIS,]); |
- } |
- if (_inAsyncMethodOrFunction(node)) { |
- _addSuggestion2(AWAIT); |
- } |
- if (_inLoop(node)) { |
- _addSuggestions([Keyword.BREAK, Keyword.CONTINUE]); |
- } |
- if (_inSwitch(node)) { |
- _addSuggestions([Keyword.BREAK]); |
- } |
- _addSuggestions([ |
- Keyword.ASSERT, |
- Keyword.CONST, |
- Keyword.DO, |
- Keyword.FINAL, |
- Keyword.FOR, |
- Keyword.IF, |
- Keyword.NEW, |
- Keyword.RETURN, |
- Keyword.SWITCH, |
- Keyword.THROW, |
- Keyword.TRY, |
- Keyword.VAR, |
- Keyword.VOID, |
- Keyword.WHILE |
- ]); |
- } |
- |
- void _addSuggestion(Keyword keyword, |
- [int relevance = DART_RELEVANCE_KEYWORD]) { |
- _addSuggestion2(keyword.syntax, relevance: relevance); |
- } |
- |
- void _addSuggestion2(String completion, |
- {int offset, int relevance: DART_RELEVANCE_KEYWORD}) { |
- if (offset == null) { |
- offset = completion.length; |
- } |
- request.addSuggestion(new CompletionSuggestion( |
- CompletionSuggestionKind.KEYWORD, |
- relevance, |
- completion, |
- offset, |
- 0, |
- false, |
- false)); |
- } |
- |
- void _addSuggestions(List<Keyword> keywords, |
- [int relevance = DART_RELEVANCE_KEYWORD]) { |
- keywords.forEach((Keyword keyword) { |
- _addSuggestion(keyword, relevance); |
- }); |
- } |
- |
- bool _inAsyncMethodOrFunction(AstNode node) { |
- FunctionBody body = node.getAncestor((n) => n is FunctionBody); |
- return body != null && body.isAsynchronous; |
- } |
- |
- bool _inCatchClause(Block node) => |
- node.getAncestor((p) => p is CatchClause) != null; |
- |
- bool _inClassMemberBody(AstNode node) { |
- while (true) { |
- AstNode body = node.getAncestor((n) => n is FunctionBody); |
- if (body == null) { |
- return false; |
- } |
- AstNode parent = body.parent; |
- if (parent is ConstructorDeclaration || parent is MethodDeclaration) { |
- return true; |
- } |
- node = parent; |
- } |
- } |
- |
- bool _inDoLoop(AstNode node) => |
- node.getAncestor((p) => p is DoStatement) != null; |
- |
- bool _inForLoop(AstNode node) => |
- node.getAncestor((p) => p is ForStatement || p is ForEachStatement) != |
- null; |
- |
- bool _inLoop(AstNode node) => |
- _inDoLoop(node) || _inForLoop(node) || _inWhileLoop(node); |
- |
- bool _inSwitch(AstNode node) => |
- node.getAncestor((p) => p is SwitchStatement) != null; |
- |
- bool _inWhileLoop(AstNode node) => |
- node.getAncestor((p) => p is WhileStatement) != null; |
-} |