Chromium Code Reviews| Index: pkg/analysis_server/lib/src/services/completion/keyword_computer.dart |
| diff --git a/pkg/analysis_server/lib/src/services/completion/keyword_computer.dart b/pkg/analysis_server/lib/src/services/completion/keyword_computer.dart |
| index 36d403b4ca6886c3eb967345e1d6b71c1f8f5760..f688f2fa0100ec573e5bcbd7e328ea2e8abf86e3 100644 |
| --- a/pkg/analysis_server/lib/src/services/completion/keyword_computer.dart |
| +++ b/pkg/analysis_server/lib/src/services/completion/keyword_computer.dart |
| @@ -18,7 +18,7 @@ import 'package:analyzer/src/generated/scanner.dart'; |
| class KeywordComputer extends DartCompletionComputer { |
| @override |
| bool computeFast(DartCompletionRequest request) { |
| - request.node.accept(new _KeywordVisitor(request)); |
| + request.target.containingNode.accept(new _KeywordVisitor(request)); |
| return true; |
| } |
| @@ -33,67 +33,44 @@ class KeywordComputer extends DartCompletionComputer { |
| */ |
| class _KeywordVisitor extends GeneralizingAstVisitor { |
| final DartCompletionRequest request; |
| + final Object entity; |
| - /** |
| - * The identifier visited or `null` if not visited. |
| - */ |
| - SimpleIdentifier identifier; |
| - |
| - _KeywordVisitor(this.request); |
| + _KeywordVisitor(DartCompletionRequest request) |
| + : this.request = request, |
| + this.entity = request.target.entity; |
| @override |
| visitBlock(Block node) { |
| if (_isInClassMemberBody(node)) { |
| - _addSuggestions([ |
| - Keyword.ASSERT, |
| - Keyword.CASE, |
| - Keyword.CONTINUE, |
| - Keyword.DO, |
| - Keyword.FINAL, |
| - Keyword.FOR, |
| - Keyword.IF, |
| - Keyword.NEW, |
| - Keyword.RETHROW, |
| - Keyword.RETURN, |
| - Keyword.SUPER, |
| - Keyword.SWITCH, |
| - Keyword.THIS, |
| - Keyword.THROW, |
| - Keyword.TRY, |
| - Keyword.VAR, |
| - Keyword.VOID, |
| - Keyword.WHILE |
| - ]); |
| - } else { |
| - _addSuggestions([ |
| - Keyword.ASSERT, |
| - Keyword.CASE, |
| - Keyword.CONTINUE, |
| - Keyword.DO, |
| - Keyword.FINAL, |
| - Keyword.FOR, |
| - Keyword.IF, |
| - Keyword.NEW, |
| - Keyword.RETHROW, |
| - Keyword.RETURN, |
| - Keyword.SWITCH, |
| - Keyword.THROW, |
| - Keyword.TRY, |
| - Keyword.VAR, |
| - Keyword.VOID, |
| - Keyword.WHILE |
| - ]); |
| + _addSuggestions([Keyword.SUPER, Keyword.THIS,]); |
| } |
| + _addSuggestions([ |
| + Keyword.ASSERT, |
| + Keyword.CASE, |
| + Keyword.CONTINUE, |
| + Keyword.DO, |
| + Keyword.FINAL, |
| + Keyword.FOR, |
| + Keyword.IF, |
| + Keyword.NEW, |
| + Keyword.RETHROW, |
| + Keyword.RETURN, |
| + Keyword.SWITCH, |
| + Keyword.THROW, |
| + Keyword.TRY, |
| + Keyword.VAR, |
| + Keyword.VOID, |
| + Keyword.WHILE |
| + ]); |
| } |
| @override |
| visitClassDeclaration(ClassDeclaration node) { |
| // Don't suggest class name |
| - if (node.name == identifier) { |
| + if (entity == node.name) { |
| return; |
| } |
| - // Inside the class declaration { } |
| - if (request.offset > node.leftBracket.offset) { |
| + if (entity == node.rightBracket) { |
|
Paul Berry
2015/04/03 19:04:29
Shouldn't these suggestions also be made if entity
danrubel
2015/04/03 20:54:01
Good catch! https://codereview.chromium.org/105740
|
| _addSuggestions([ |
| Keyword.CONST, |
| Keyword.DYNAMIC, |
| @@ -113,41 +90,31 @@ class _KeywordVisitor extends GeneralizingAstVisitor { |
| @override |
| visitCompilationUnit(CompilationUnit node) { |
| - Directive firstDirective; |
| - int endOfDirectives = 0; |
| - if (node.directives.length > 0) { |
| - firstDirective = node.directives[0]; |
| - endOfDirectives = node.directives.last.end - 1; |
| - } |
| - int startOfDeclarations = node.end; |
| - if (node.declarations.length > 0) { |
| - startOfDeclarations = node.declarations[0].offset; |
| - // If the first token is a simple identifier |
| - // and cursor position in within that first token |
| - // then consider cursor to be before the first declaration |
| - Token token = node.declarations[0].firstTokenAfterCommentAndMetadata; |
| - if (token.offset <= request.offset && request.offset <= token.end) { |
| - startOfDeclarations = token.end; |
| + var previousMember = null; |
| + for (var member in node.childEntities) { |
| + if (entity == member) { |
| + break; |
| } |
| + previousMember = member; |
| } |
| - |
| - // Simplistic check for library as first directive |
| - if (firstDirective is! LibraryDirective) { |
| - if (firstDirective != null) { |
| - if (request.offset <= firstDirective.offset) { |
| - _addSuggestions([Keyword.LIBRARY], DART_RELEVANCE_HIGH); |
| - } |
| - } else { |
| - if (request.offset <= startOfDeclarations) { |
| - _addSuggestions([Keyword.LIBRARY], DART_RELEVANCE_HIGH); |
| - } |
| + 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 (request.offset <= startOfDeclarations) { |
| + if (previousMember == null || previousMember is Directive) { |
| + if (previousMember == null && |
| + !node.directives.any((d) => d is LibraryDirective)) { |
| + _addSuggestions([Keyword.LIBRARY], DART_RELEVANCE_HIGH); |
| + } |
| _addSuggestions( |
| [Keyword.EXPORT, Keyword.IMPORT, Keyword.PART], DART_RELEVANCE_HIGH); |
| } |
| - if (request.offset >= endOfDirectives) { |
| + if (entity == null || entity is Declaration) { |
| _addSuggestions([ |
| Keyword.ABSTRACT, |
| Keyword.CLASS, |
| @@ -159,50 +126,6 @@ class _KeywordVisitor extends GeneralizingAstVisitor { |
| } |
| } |
| - @override |
| - visitNode(AstNode node) { |
| - if (_isOffsetAfterNode(node)) { |
| - node.parent.accept(this); |
| - } |
| - } |
| - |
| - visitSimpleIdentifier(SimpleIdentifier node) { |
| - identifier = node; |
| - node.parent.accept(this); |
| - } |
| - |
| - void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| - if (identifier != null && node.beginToken == identifier.beginToken) { |
| - AstNode unit = node.parent; |
| - if (unit is CompilationUnit) { |
| - CompilationUnitMember previous; |
| - for (CompilationUnitMember member in unit.declarations) { |
| - if (member == node && previous is ClassDeclaration) { |
| - if (previous.endToken.isSynthetic) { |
| - // Partial keywords (simple identifirs) that are part of a |
| - // class declaration can be parsed |
| - // as a TypeName in a TopLevelVariableDeclaration |
| - _addClassDeclarationKeywords(previous); |
| - return; |
| - } |
| - } |
| - previous = member; |
| - } |
| - // Partial keywords (simple identifiers) can be parsed |
| - // as a TypeName in a TopLevelVariableDeclaration |
| - unit.accept(this); |
| - } |
| - } |
| - } |
| - |
| - void visitTypeName(TypeName node) { |
| - node.parent.accept(this); |
| - } |
| - |
| - void visitVariableDeclarationList(VariableDeclarationList node) { |
| - node.parent.accept(this); |
| - } |
| - |
| void _addClassDeclarationKeywords(ClassDeclaration node) { |
| // Very simplistic suggestion because analyzer will warn if |
| // the extends / with / implements keywords are out of order |
| @@ -220,8 +143,8 @@ class _KeywordVisitor extends GeneralizingAstVisitor { |
| [int relevance = DART_RELEVANCE_DEFAULT]) { |
| String completion = keyword.syntax; |
| request.addSuggestion(new CompletionSuggestion( |
| - CompletionSuggestionKind.KEYWORD, |
| - relevance, completion, completion.length, 0, false, false)); |
| + CompletionSuggestionKind.KEYWORD, relevance, completion, |
| + completion.length, 0, false, false)); |
| } |
| void _addSuggestions(List<Keyword> keywords, |
| @@ -231,19 +154,7 @@ class _KeywordVisitor extends GeneralizingAstVisitor { |
| }); |
| } |
| - bool _isOffsetAfterNode(AstNode node) { |
| - if (request.offset == node.end) { |
| - Token token = node.endToken; |
| - if (token != null && !token.isSynthetic) { |
| - if (token.lexeme == ';' || token.lexeme == '}') { |
| - return true; |
| - } |
| - } |
| - } |
| - return false; |
| - } |
| - |
| - static bool _isInClassMemberBody(AstNode node) { |
|
Paul Berry
2015/04/03 19:04:29
Any particular reason for dropping the "static" ke
|
| + bool _isInClassMemberBody(AstNode node) { |
| while (true) { |
| AstNode body = node.getAncestor((n) => n is FunctionBody); |
| if (body == null) { |