Move more keyword completions
Change-Id: Ibdd1175731317acc91bff3e08ac57f706775bf56
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/326540
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
index 25870c3..226a4cc 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
@@ -248,12 +248,15 @@
@override
void visitCompilationUnit(CompilationUnit node) {
- var followingMember = node.memberAfter(offset);
- if (_forIncompletePreceedingUnitMember(node, followingMember)) {
- // The preceeding member is incomplete, so assume that the user is
- // completing it rather than starting a new member.
+ // This method is only invoked when the cursor is between two members.
+ var surroundingMembers = node.membersBeforeAndAfterOffset(offset);
+ var before = surroundingMembers.before;
+ if (before != null && _handledIncompletePrecedingUnitMember(node, before)) {
+ // The member is incomplete, so assume that the user is completing it
+ //rather than starting a new member.
return;
}
+ _forCompilationUnitMember(node, surroundingMembers);
}
@override
@@ -415,10 +418,10 @@
@override
void visitExpressionStatement(ExpressionStatement node) {
collector.completionLocation = 'ExpressionStatement_expression';
- if (_forIncompletePreceedingStatement(node)) {
+ if (_forIncompletePrecedingStatement(node)) {
if (node.isSingleIdentifier) {
- var preceedingStatement = node.preceedingStatement;
- if (preceedingStatement is TryStatement) {
+ var precedingStatement = node.precedingStatement;
+ if (precedingStatement is TryStatement) {
return;
}
}
@@ -474,18 +477,29 @@
@override
void visitFieldDeclaration(FieldDeclaration node) {
- _forIncompletePreceedingClassMember(node);
+ _forIncompletePrecedingClassMember(node);
var fields = node.fields;
var type = fields.type;
if (type == null) {
- var firstField = fields.variables.firstOrNull;
- if (firstField != null && offset <= firstField.name.end) {
- keywordHelper.addFieldDeclarationKeywords(node);
+ var variables = fields.variables;
+ var firstField = variables.firstOrNull;
+ if (firstField != null) {
+ var name = firstField.name;
+ if (variables.length == 1 && name.isKeyword && offset > name.end) {
+ // The parser has recovered by using one of the existing keywords as
+ // the name of a field, which means that there is no type.
+ keywordHelper.addFieldDeclarationKeywords(node,
+ keyword: name.keyword);
+ } else if (offset <= name.end) {
+ keywordHelper.addFieldDeclarationKeywords(node);
+ }
}
} else {
if (offset <= type.end) {
keywordHelper.addFieldDeclarationKeywords(node);
keywordHelper.addKeyword(Keyword.DYNAMIC);
+ // TODO(brianwilkerson) `var` should only be suggested if neither
+ // `static` nor `final` are present.
keywordHelper.addKeyword(Keyword.VAR);
keywordHelper.addKeyword(Keyword.VOID);
}
@@ -493,6 +507,21 @@
}
@override
+ void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
+ _visitForEachParts(node);
+ }
+
+ @override
+ void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
+ _visitForEachParts(node);
+ }
+
+ @override
+ void visitForEachPartsWithPattern(ForEachPartsWithPattern node) {
+ _visitForEachParts(node);
+ }
+
+ @override
void visitForElement(ForElement node) {
var literal = node.thisOrAncestorOfType<TypedLiteral>();
if (literal is ListLiteral) {
@@ -513,9 +542,52 @@
}
@override
+ void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
+ if (offset >= node.leftSeparator.end &&
+ offset <= node.rightSeparator.offset) {
+ var condition = node.condition;
+ if (condition is SimpleIdentifier &&
+ node.leftSeparator.isSynthetic &&
+ node.rightSeparator.isSynthetic) {
+ // Handle the degenerate case while typing `for (int x i^)`.
+ // Actual: for (int x i^)
+ // Parsed: for (int x; i^;)
+ keywordHelper.addKeyword(Keyword.IN);
+ return;
+ }
+ }
+ }
+
+ @override
void visitForStatement(ForStatement node) {
if (offset <= node.forKeyword.end) {
_forStatement(node);
+ } else if (offset >= node.leftParenthesis.end &&
+ offset <= node.rightParenthesis.offset) {
+ var parts = node.forLoopParts;
+ if (parts is ForPartsWithDeclarations) {
+ var variables = parts.variables;
+ var keyword = variables.keyword;
+ if (variables.variables.length == 1 &&
+ variables.variables[0].name.isSynthetic &&
+ keyword != null &&
+ parts.leftSeparator.isSynthetic) {
+ var afterKeyword = keyword.next!;
+ if (afterKeyword.type == TokenType.OPEN_PAREN) {
+ var endGroup = afterKeyword.endGroup;
+ if (endGroup != null && offset >= endGroup.end) {
+ // Actual: for (va^)
+ // Parsed: for (va^; ;)
+ keywordHelper.addKeyword(Keyword.IN);
+ }
+ }
+ }
+ } else if (parts is ForPartsWithExpression &&
+ parts.leftSeparator.isSynthetic &&
+ parts.initialization is SimpleIdentifier) {
+ keywordHelper.addKeyword(Keyword.FINAL);
+ keywordHelper.addKeyword(Keyword.VAR);
+ }
}
}
@@ -559,8 +631,17 @@
}
@override
+ void visitFunctionTypeAlias(FunctionTypeAlias node) {
+ if (node.typedefKeyword.coversOffset(offset)) {
+ keywordHelper.addKeyword(Keyword.TYPEDEF);
+ }
+ }
+
+ @override
void visitGenericTypeAlias(GenericTypeAlias node) {
- if (offset >= node.equals.end && offset <= node.semicolon.offset) {
+ if (node.typedefKeyword.coversOffset(offset)) {
+ keywordHelper.addKeyword(Keyword.TYPEDEF);
+ } else if (offset >= node.equals.end && offset <= node.semicolon.offset) {
keywordHelper.addKeyword(Keyword.DYNAMIC);
keywordHelper.addKeyword(Keyword.VOID);
}
@@ -676,6 +757,20 @@
}
@override
+ void visitLibraryDirective(LibraryDirective node) {
+ if (offset >= node.end) {
+ var unit = node.parent;
+ if (unit is CompilationUnit) {
+ _forDirective(unit, node);
+ var (before: _, :after) = unit.membersBeforeAndAfterMember(node);
+ if (after is CompilationUnitMember?) {
+ _forCompilationUnitDeclaration();
+ }
+ }
+ }
+ }
+
+ @override
void visitListLiteral(ListLiteral node) {
final offset = this.offset;
if (offset >= node.leftBracket.end && offset <= node.rightBracket.offset) {
@@ -905,6 +1000,16 @@
}
@override
+ void visitRelationalPattern(RelationalPattern node) {
+ var operand = node.operand;
+ if (operand is SimpleIdentifier &&
+ offset >= node.operator.end &&
+ offset <= operand.end) {
+ keywordHelper.addExpressionKeywords(node);
+ }
+ }
+
+ @override
void visitRestPatternElement(RestPatternElement node) {
collector.completionLocation = 'RestPatternElement_pattern';
_forPattern();
@@ -930,6 +1035,20 @@
}
@override
+ void visitSimpleFormalParameter(SimpleFormalParameter node) {
+ var type = node.type;
+ if (type != null) {
+ if (type.beginToken.coversOffset(offset)) {
+ _forTypeAnnotation();
+ } else if (type is GenericFunctionType &&
+ offset < type.functionKeyword.offset &&
+ type.returnType == null) {
+ _forTypeAnnotation();
+ }
+ }
+ }
+
+ @override
void visitSimpleStringLiteral(SimpleStringLiteral node) {
_visitParentIfAtOrBeforeNode(node);
}
@@ -1052,15 +1171,7 @@
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
- var unit = node.parent;
- if (unit is CompilationUnit &&
- _forIncompletePreceedingUnitMember(unit, node)) {
- return;
- } else if (node.isSingleIdentifier) {
- // The parser recovers from a simple identifier by assuming that it's a
- // variable declaration. But a simple identifier could be the start of
- // any kind of member, so defer to the compilation unit.
- node.parent?.accept(this);
+ if (_handledRecovery(node)) {
return;
}
@@ -1128,9 +1239,9 @@
var grandparent = parent.parent;
if (grandparent is FieldDeclaration) {
// The order of these conditions is critical. We need to check for an
- // incomplete preceeding member even when the grandparent isn't a single
+ // incomplete preceding member even when the grandparent isn't a single
// identifier, but want to return only if both conditions are true.
- if (_forIncompletePreceedingClassMember(grandparent) &&
+ if (_forIncompletePrecedingClassMember(grandparent) &&
grandparent.isSingleIdentifier) {
return;
}
@@ -1141,13 +1252,7 @@
keywordHelper.addKeyword(Keyword.IN);
}
} else if (grandparent is TopLevelVariableDeclaration) {
- // The order of these conditions is critical. We need to check for an
- // incomplete preceeding member even when the grandparent isn't a single
- // identifier, but want to return only if both conditions are true.
- var unit = grandparent.parent;
- if (unit is CompilationUnit &&
- _forIncompletePreceedingUnitMember(unit, grandparent) &&
- grandparent.isSingleIdentifier) {
+ if (_handledRecovery(grandparent)) {
return;
}
}
@@ -1232,7 +1337,7 @@
@override
void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
- _forIncompletePreceedingStatement(node);
+ _forIncompletePrecedingStatement(node);
if (offset <= node.beginToken.end) {
_forStatement(node);
}
@@ -1277,6 +1382,17 @@
keywordHelper.addCompilationUnitDeclarationKeywords();
}
+ void _forCompilationUnitMember(CompilationUnit unit,
+ ({AstNode? before, AstNode? after}) surroundingMembers) {
+ var before = surroundingMembers.before;
+ if (before is Directive?) {
+ _forDirective(unit, before);
+ }
+ if (surroundingMembers.after is CompilationUnitMember?) {
+ _forCompilationUnitDeclaration();
+ }
+ }
+
/// Add the suggestions that are appropriate when the selection is at the
/// beginning of a constant expression. The [node] provides context to
/// determine which keywords to include.
@@ -1290,6 +1406,13 @@
}
/// Add the suggestions that are appropriate when the selection is at the
+ /// beginning of a directive. The [before] directive is the directive before
+ /// the one being added.
+ void _forDirective(CompilationUnit unit, Directive? before) {
+ keywordHelper.addDirectiveKeywords(unit, before);
+ }
+
+ /// Add the suggestions that are appropriate when the selection is at the
/// beginning of an enum member.
void _forEnumMember() {
keywordHelper.addEnumMemberKeywords();
@@ -1311,23 +1434,23 @@
keywordHelper.addExtensionMemberKeywords(isStatic: false);
}
- /// Return `true` if the preceeding member is incomplete.
+ /// Return `true` if the preceding member is incomplete.
///
/// If the completion offset is within the first token of the given [member],
- /// then check to see whether the preceeding member is incomplete. If it is,
- /// then the user might be attempting to complete the preceeding member rather
+ /// then check to see whether the preceding member is incomplete. If it is,
+ /// then the user might be attempting to complete the preceding member rather
/// than attempting to prepend something to the given [member], so add the
/// suggestions appropriate for that situation.
- bool _forIncompletePreceedingClassMember(ClassMember member) {
+ bool _forIncompletePrecedingClassMember(ClassMember member) {
if (offset <= member.beginToken.end) {
- var preceedingMember = member.preceedingMember;
- if (preceedingMember == null) {
+ var precedingMember = member.precedingMember;
+ if (precedingMember == null) {
return false;
}
- // Ideally we'd visit the preceeding member in order to avoid
- // duplicating code, but the offset will be past where the parser
- // inserted sythetic tokens, preventing that from working.
- switch (preceedingMember) {
+ // Ideally we'd visit the preceding member in order to avoid duplicating
+ // code, but the offset will be past where the parser inserted sythetic
+ // tokens, preventing that from working.
+ switch (precedingMember) {
// TODO(brianwilkerson) Add support for other kinds of declarations.
case MethodDeclaration declaration:
if (declaration.body.isFullySynthetic) {
@@ -1341,20 +1464,20 @@
}
/// If the completion offset is within the first token of the given
- /// [statement], then check to see whether the preceeding statement is
+ /// [statement], then check to see whether the preceding statement is
/// incomplete. If it is, then the user might be attempting to complete the
- /// preceeding statement rather than attempting to prepend something to the
+ /// preceding statement rather than attempting to prepend something to the
/// given [statement], so add the suggestions appropriate for that situation.
- bool _forIncompletePreceedingStatement(Statement statement) {
+ bool _forIncompletePrecedingStatement(Statement statement) {
if (offset <= statement.beginToken.end) {
- var preceedingStatement = statement.preceedingStatement;
- if (preceedingStatement == null) {
+ var precedingStatement = statement.precedingStatement;
+ if (precedingStatement == null) {
return false;
}
- // Ideally we'd visit the preceeding member in order to avoid
+ // Ideally we'd visit the preceding member in order to avoid
// duplicating code, but the offset will be past where the parser
// inserted sythetic tokens, preventing that from working.
- switch (preceedingStatement) {
+ switch (precedingStatement) {
// TODO(brianwilkerson) Add support for other kinds of declarations.
case IfStatement declaration:
if (declaration.elseKeyword == null) {
@@ -1372,47 +1495,6 @@
return false;
}
- /// Return `true` if the preceeding member is incomplete.
- ///
- /// If the completion offset is within the first token of the given [member],
- /// then check to see whether the preceeding member is incomplete. If it is,
- /// then the user might be attempting to complete the preceeding member rather
- /// than attempting to prepend something to the given [member], so add the
- /// suggestions appropriate for that situation.
- bool _forIncompletePreceedingUnitMember(
- CompilationUnit parent, AstNode? member) {
- if (member == null || offset <= member.beginToken.end) {
- var members = parent.sortedDirectivesAndDeclarations;
- var index = member == null ? members.length : members.indexOf(member);
- if (index <= 0) {
- return false;
- }
- var preceedingMember = members[index - 1];
- // Ideally we'd visit the preceeding member in order to avoid duplicating
- // code, but in some cases the offset will be past where the parser
- // inserted sythetic tokens, preventing that from working.
- switch (preceedingMember) {
- // TODO(brianwilkerson) Add support for other kinds of declarations.
- case ClassDeclaration declaration:
- if (declaration.hasNoBody) {
- keywordHelper.addClassDeclarationKeywords(declaration);
- return true;
- }
- case ExtensionTypeDeclaration declaration:
- if (declaration.hasNoBody) {
- visitExtensionTypeDeclaration(declaration);
- return true;
- }
- case ImportDirective directive:
- if (directive.semicolon.isSynthetic) {
- visitImportDirective(directive);
- return true;
- }
- }
- }
- return false;
- }
-
/// Add the suggestions that are appropriate when the selection is at the
/// beginning of a mixin member.
void _forMixinMember() {
@@ -1449,6 +1531,105 @@
// _addTypesInScope();
}
+ /// Return `true` if the [precedingMember] is incomplete.
+ ///
+ /// If it's incomplete, assume that the user is attempting to complete it and
+ /// offer appropriate suggestions.
+ bool _handledIncompletePrecedingUnitMember(
+ CompilationUnit unit, AstNode precedingMember) {
+ // Ideally we'd visit the preceding member in order to avoid duplicating
+ // code, but in some cases the offset will be past where the parser inserted
+ // sythetic tokens, preventing that from working.
+ switch (precedingMember) {
+ // TODO(brianwilkerson) Add support for other kinds of declarations.
+ case ClassDeclaration declaration:
+ if (declaration.hasNoBody) {
+ keywordHelper.addClassDeclarationKeywords(declaration);
+ return true;
+ }
+ // case ExtensionDeclaration declaration:
+ // if (declaration.leftBracket.isSynthetic) {
+ // // If the prior member is an unfinished extension declaration then the
+ // // user is probably finishing that.
+ // _addExtensionDeclarationKeywords(declaration);
+ // return;
+ // }
+ // }
+ case ExtensionTypeDeclaration declaration:
+ if (declaration.hasNoBody) {
+ visitExtensionTypeDeclaration(declaration);
+ return true;
+ }
+ case FunctionDeclaration declaration:
+ var body = declaration.functionExpression.body;
+ if (body.isEmpty) {
+ keywordHelper.addFunctionBodyModifiers(body);
+ }
+ case ImportDirective directive:
+ if (directive.semicolon.isSynthetic) {
+ visitImportDirective(directive);
+ return true;
+ }
+ // case MixinDeclaration declaration:
+ // if (declaration.leftBracket.isSynthetic) {
+ // // If the prior member is an unfinished mixin declaration
+ // // then the user is probably finishing that.
+ // _addMixinDeclarationKeywords(declaration);
+ // return;
+ // }
+ // }
+ }
+ return false;
+ }
+
+ /// Return `true` if the given [declaration] is the result of recovery and
+ /// suggestions have already been produced.
+ ///
+ /// The parser recovers from a simple identifier by assuming that it's a
+ /// top-level variable declaration. But a simple identifier could be the start
+ /// of any kind of member, so defer to the compilation unit.
+ bool _handledRecovery(TopLevelVariableDeclaration declaration) {
+ var unit = declaration.parent;
+ if (unit is CompilationUnit) {
+ ({AstNode? before, AstNode? after})? surroundingMembers;
+ if (offset <= declaration.beginToken.end) {
+ surroundingMembers = unit.membersBeforeAndAfterMember(declaration);
+ var before = surroundingMembers.before;
+ if (before != null &&
+ _handledIncompletePrecedingUnitMember(unit, before)) {
+ // The preceding member is incomplete, so assume that the user is
+ // completing it rather than starting a new member.
+ return true;
+ }
+ }
+ if (declaration.isSingleIdentifier) {
+ surroundingMembers ??= unit.membersBeforeAndAfterMember(declaration);
+ _forCompilationUnitMember(unit, surroundingMembers);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void _visitForEachParts(ForEachParts node) {
+ if (node.inKeyword.coversOffset(offset)) {
+ var previous = node.findPrevious(node.inKeyword);
+ if (previous is SyntheticStringToken && previous.lexeme == 'in') {
+ previous = node.findPrevious(previous);
+ }
+ if (previous != null && previous.type == TokenType.EQ) {
+ keywordHelper.addKeyword(Keyword.CONST);
+ keywordHelper.addKeyword(Keyword.FALSE);
+ keywordHelper.addKeyword(Keyword.NULL);
+ keywordHelper.addKeyword(Keyword.TRUE);
+ } else {
+ keywordHelper.addKeyword(Keyword.IN);
+ }
+ } else if (!node.inKeyword.isSynthetic && node.iterable.isSynthetic) {
+ keywordHelper.addKeyword(Keyword.AWAIT);
+ }
+ }
+
/// If the completion offset is at or before the offset of the [node], then
/// visit the parent of the node.
void _visitParentIfAtOrBeforeNode(AstNode node) {
@@ -1573,7 +1754,7 @@
extension on ClassMember {
/// Return the member before `this`, or `null` if this is the first member in
/// the body.
- ClassMember? get preceedingMember {
+ ClassMember? get precedingMember {
final parent = this.parent;
var members = switch (parent) {
ClassDeclaration() => parent.members,
@@ -1595,16 +1776,36 @@
}
extension on CompilationUnit {
- /// Return the member that is immediately after the given [offset] or `null`
- /// if the offset isn't before a member.
- AstNode? memberAfter(int offset) {
+ /// Return a record whose fields are the members in this compilation unit
+ /// that are lexically immediately before and after the given [member].
+ ({AstNode? before, AstNode? after}) membersBeforeAndAfterMember(
+ AstNode? member) {
var members = sortedDirectivesAndDeclarations;
- for (var member in members) {
- if (offset < member.offset) {
- return member;
+ AstNode? before, after;
+ if (member != null) {
+ var index = members.indexOf(member);
+ if (index > 0) {
+ before = members[index - 1];
+ }
+ if (index + 1 < members.length) {
+ after = members[index + 1];
}
}
- return null;
+ return (before: before, after: after);
+ }
+
+ /// Return a record whose fields are the members in the compilation [unit]
+ /// that are lexically immediately before and after the given [member].
+ ({AstNode? before, AstNode? after}) membersBeforeAndAfterOffset(int offset) {
+ var members = sortedDirectivesAndDeclarations;
+ AstNode? previous;
+ for (var member in members) {
+ if (offset < member.offset) {
+ return (before: previous, after: member);
+ }
+ previous = member;
+ }
+ return (before: previous, after: null);
}
}
@@ -1659,7 +1860,7 @@
extension on Statement {
/// Return the statement before `this`, or `null` if this is the first statement in
/// the block.
- Statement? get preceedingStatement {
+ Statement? get precedingStatement {
final parent = this.parent;
if (parent is! Block) {
return null;
@@ -1686,10 +1887,11 @@
/// identifier.
bool get isSingleIdentifier {
var first = beginToken;
+ var next = first.next;
var last = endToken;
return first.isKeywordOrIdentifier &&
last.isSynthetic &&
- first.next == last;
+ (next == last || (next?.isSynthetic == true && next?.next == last));
}
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
index 5a853ad..d76f20a 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
@@ -12,7 +12,6 @@
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
-import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
@@ -105,137 +104,6 @@
}
@override
- void visitCompilationUnit(CompilationUnit node) {
- SyntacticEntity? previousMember;
- for (var member in node.childEntities) {
- if (entity == member) {
- break;
- }
- previousMember = member;
- }
- if (previousMember is ClassDeclaration) {
- if (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 ExtensionDeclaration) {
- if (previousMember.leftBracket.isSynthetic) {
- // If the prior member is an unfinished extension declaration then the
- // user is probably finishing that.
- _addExtensionDeclarationKeywords(previousMember);
- return;
- }
- }
- if (previousMember is MixinDeclaration) {
- if (previousMember.leftBracket.isSynthetic) {
- // If the prior member is an unfinished mixin declaration
- // then the user is probably finishing that.
- _addMixinDeclarationKeywords(previousMember);
- return;
- }
- }
- if (previousMember is ImportDirective) {
- if (previousMember.semicolon.isSynthetic) {
- // If the prior member is an unfinished import directive
- // then the user is probably finishing that
- _addImportDirectiveKeywords(previousMember);
- return;
- }
- }
- if (previousMember is ExtensionTypeDeclaration) {
- // Already handled by the in-scope completion pass.
- return;
- }
- if (previousMember == null || previousMember is Directive) {
- if (previousMember == null &&
- !node.directives.any((d) => d is LibraryDirective)) {
- _addSuggestions([Keyword.LIBRARY]);
- }
- _addSuggestion2(IMPORT_STATEMENT, offset: 8);
- _addSuggestion2(EXPORT_STATEMENT, offset: 8);
- _addSuggestion2(PART_STATEMENT, offset: 6);
- }
- if (entity == null || entity is Declaration) {
- if (previousMember is FunctionDeclaration &&
- previousMember.functionExpression.body.isEmpty) {
- _addSuggestion(Keyword.ASYNC);
- _addSuggestion2(ASYNC_STAR);
- _addSuggestion2(SYNC_STAR);
- }
- _addCompilationUnitKeywords();
- }
- }
-
- @override
- void visitFieldDeclaration(FieldDeclaration node) {
- if (request.opType.completionLocation == 'FieldDeclaration_static') {
- _addSuggestion(Keyword.CONST);
- _addSuggestion(Keyword.DYNAMIC);
- _addSuggestion(Keyword.FINAL);
- _addSuggestion(Keyword.LATE);
- return;
- }
-
- if (request.opType.completionLocation == 'FieldDeclaration_static_late') {
- _addSuggestion(Keyword.DYNAMIC);
- _addSuggestion(Keyword.FINAL);
- return;
- }
-
- var fields = node.fields;
- if (entity != fields) {
- return;
- }
- var variables = fields.variables;
- if (variables.isEmpty || request.offset > variables.first.beginToken.end) {
- return;
- }
- if (node.abstractKeyword == null) {
- _addSuggestion(Keyword.ABSTRACT);
- }
- if (node.covariantKeyword == null) {
- _addSuggestion(Keyword.COVARIANT);
- }
- if (node.externalKeyword == null) {
- _addSuggestion(Keyword.EXTERNAL);
- }
- if (node.fields.lateKeyword == null &&
- request.featureSet.isEnabled(Feature.non_nullable)) {
- _addSuggestion(Keyword.LATE);
- }
- if (node.fields.type == null) {
- _addSuggestion(Keyword.DYNAMIC);
- }
- if (!node.isStatic) {
- _addSuggestion(Keyword.STATIC);
- }
- if (!variables.first.isConst) {
- _addSuggestion(Keyword.CONST);
- }
- if (!variables.first.isFinal) {
- _addSuggestion(Keyword.FINAL);
- }
- }
-
- @override
- void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
- _visitForEachParts(node);
- }
-
- @override
- void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
- _visitForEachParts(node);
- }
-
- @override
- void visitForEachPartsWithPattern(ForEachPartsWithPattern node) {
- _visitForEachParts(node);
- }
-
- @override
void visitFormalParameterList(FormalParameterList node) {
var constructorDeclaration =
node.thisOrAncestorOfType<ConstructorDeclaration>();
@@ -316,175 +184,6 @@
}
}
- @override
- void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
- _visitForParts(node);
- }
-
- @override
- void visitForPartsWithExpression(ForPartsWithExpression node) {
- _visitForParts(node);
- }
-
- @override
- void visitForPartsWithPattern(ForPartsWithPattern node) {
- _visitForParts(node);
- }
-
- @override
- void visitForStatement(ForStatement node) {
- // Actual: for (va^)
- // Parsed: for (va^; ;)
- if (node.forLoopParts == entity) {
- _addSuggestions([Keyword.FINAL, Keyword.VAR]);
- } else if (node.rightParenthesis == entity) {
- var parts = node.forLoopParts;
- if (parts is ForPartsWithDeclarations) {
- var variables = parts.variables;
- var keyword = variables.keyword;
- if (variables.variables.length == 1 &&
- variables.variables[0].name.isSynthetic &&
- keyword != null &&
- parts.leftSeparator.isSynthetic) {
- var afterKeyword = keyword.next!;
- if (afterKeyword.type == TokenType.OPEN_PAREN) {
- var endGroup = afterKeyword.endGroup;
- if (endGroup != null && request.offset >= endGroup.end) {
- _addSuggestion(Keyword.IN);
- }
- }
- }
- }
- }
- }
-
- // @override
- // void visitMethodDeclaration(MethodDeclaration node) {
- // if (entity == node.body) {
- // if (node.body.isEmpty) {
- // _addClassBodyKeywords();
- // _addSuggestion(Keyword.ASYNC);
- // _addSuggestion2(ASYNC_STAR);
- // _addSuggestion2(SYNC_STAR);
- // } else {
- // _addSuggestion(Keyword.ASYNC);
- // if (node.body is! ExpressionFunctionBody) {
- // _addSuggestion2(ASYNC_STAR);
- // _addSuggestion2(SYNC_STAR);
- // }
- // }
- // } else if (entity == node.returnType || entity == node.name) {
- // // If the cursor is at the beginning of the declaration, include the class
- // // body keywords. See dartbug.com/41039.
- // _addClassBodyKeywords();
- // }
- // }
-
- @override
- void visitMethodInvocation(MethodInvocation node) {
- if (entity == node.methodName) {
- // no keywords in '.' expressions
- } else if (entity == node.argumentList) {
- // Note that we're checking the argumentList rather than the typeArgumentList
- // as you'd expect. For some reason, when the cursor is in a type argument
- // list (f<^>()), the entity is the invocation's argumentList...
- // See similar logic in `imported_reference_contributor`.
-
- _addSuggestion(Keyword.DYNAMIC);
- _addSuggestion(Keyword.VOID);
- } else {
- _addExpressionKeywords(node);
- }
- }
-
- @override
- void visitRelationalPattern(RelationalPattern node) {
- var operator = node.operator;
- if (request.offset >= operator.end) {
- if (request.opType.completionLocation == 'TypeArgumentList_argument') {
- // This is most likely a type argument list.
- _addSuggestions([Keyword.DYNAMIC, Keyword.VOID]);
- return;
- }
- _addConstantExpressionKeywords(node);
- _addSuggestions([Keyword.DYNAMIC, Keyword.VOID]);
- }
- }
-
- @override
- void visitSimpleFormalParameter(SimpleFormalParameter node) {
- var entity = this.entity;
- if (node.type == entity && entity is GenericFunctionType) {
- var offset = request.offset;
- var returnType = entity.returnType;
- if ((returnType == null && offset < entity.offset) ||
- (returnType != null &&
- offset >= returnType.offset &&
- offset < returnType.end)) {
- _addSuggestion(Keyword.DYNAMIC);
- _addSuggestion(Keyword.VOID);
- }
- }
- }
-
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- _addExpressionKeywords(node);
- }
-
- 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);
- } else if (node.withClause == null) {
- _addSuggestion(Keyword.WITH);
- }
- if (node.implementsClause == null) {
- _addSuggestion(Keyword.IMPLEMENTS);
- }
- }
-
- void _addCompilationUnitKeywords() {
- _addSuggestions([
- Keyword.ABSTRACT,
- Keyword.CLASS,
- Keyword.CONST,
- Keyword.COVARIANT,
- Keyword.DYNAMIC,
- Keyword.FINAL,
- Keyword.MIXIN,
- Keyword.TYPEDEF,
- Keyword.VAR,
- Keyword.VOID
- ]);
- if (request.featureSet.isEnabled(Feature.extension_methods)) {
- _addSuggestion(Keyword.EXTENSION);
- }
- if (request.featureSet.isEnabled(Feature.non_nullable)) {
- _addSuggestion(Keyword.LATE);
- }
- if (request.featureSet.isEnabled(Feature.class_modifiers)) {
- _addSuggestions([Keyword.BASE, Keyword.INTERFACE]);
- }
- if (request.featureSet.isEnabled(Feature.sealed_class)) {
- _addSuggestion(Keyword.SEALED);
- }
- }
-
- void _addConstantExpressionKeywords(AstNode node) {
- // TODO(brianwilkerson) Use this method in place of `_addExpressionKeywords`
- // when in a constant context.
- _addSuggestions([
- Keyword.FALSE,
- Keyword.NULL,
- Keyword.TRUE,
- ]);
- if (!request.inConstantContext) {
- _addSuggestions([Keyword.CONST]);
- }
- }
-
void _addExpressionKeywords(AstNode node) {
_addSuggestions([
Keyword.FALSE,
@@ -505,44 +204,6 @@
}
}
- void _addExtensionDeclarationKeywords(ExtensionDeclaration node) {
- if (node.onKeyword.isSynthetic) {
- _addSuggestion(Keyword.ON);
- }
- }
-
- void _addImportDirectiveKeywords(ImportDirective node) {
- var hasDeferredKeyword = node.deferredKeyword != null;
- var hasAsKeyword = node.asKeyword != null;
- if (!hasAsKeyword) {
- _addSuggestion(Keyword.AS);
- }
- if (!hasDeferredKeyword) {
- if (!hasAsKeyword) {
- _addSuggestion2(DEFERRED_AS);
- } else if (entity == node.asKeyword) {
- _addSuggestion(Keyword.DEFERRED);
- }
- }
- if (!hasDeferredKeyword || hasAsKeyword) {
- if (node.combinators.isEmpty) {
- _addSuggestion(Keyword.SHOW);
- _addSuggestion(Keyword.HIDE);
- }
- }
- }
-
- void _addMixinDeclarationKeywords(MixinDeclaration node) {
- // Very simplistic suggestion because analyzer will warn if
- // the on / implements clauses are out of order
- if (node.onClause == null) {
- _addSuggestion(Keyword.ON);
- }
- if (node.implementsClause == null) {
- _addSuggestion(Keyword.IMPLEMENTS);
- }
- }
-
void _addSuggestion(Keyword keyword) {
_addSuggestion2(keyword.lexeme);
}
@@ -556,45 +217,4 @@
_addSuggestion(keyword);
}
}
-
- void _visitForEachParts(ForEachParts node) {
- if (entity == node.inKeyword) {
- var previous = node.findPrevious(node.inKeyword);
- if (previous is SyntheticStringToken && previous.lexeme == 'in') {
- previous = node.findPrevious(previous);
- }
- if (previous != null && previous.type == TokenType.EQ) {
- _addSuggestions(
- [Keyword.CONST, Keyword.FALSE, Keyword.NULL, Keyword.TRUE]);
- } else {
- _addSuggestion(Keyword.IN);
- }
- } else if (!node.inKeyword.isSynthetic && node.iterable.isSynthetic) {
- _addSuggestion(Keyword.AWAIT);
- }
- }
-
- void _visitForParts(ForParts node) {
- // Actual: for (int x i^)
- // Parsed: for (int x; i^;)
- // Handle the degenerate case while typing - for (int x i^)
- if (node.condition == entity &&
- entity is SimpleIdentifier &&
- node is ForPartsWithDeclarations) {
- if (_isPreviousTokenSynthetic(entity, TokenType.SEMICOLON)) {
- _addSuggestion(Keyword.IN);
- }
- }
- }
-
- static bool _isPreviousTokenSynthetic(Object? entity, TokenType type) {
- if (entity is AstNode) {
- var token = entity.beginToken;
- var previousToken = entity.findPrevious(token);
- return previousToken != null &&
- previousToken.isSynthetic &&
- previousToken.type == type;
- }
- return false;
- }
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/keyword_helper.dart b/pkg/analysis_server/lib/src/services/completion/dart/keyword_helper.dart
index 3a43f4a..eb311fb 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/keyword_helper.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/keyword_helper.dart
@@ -35,13 +35,13 @@
// in order to help users discover what keywords are available. If the
// keywords are in the wrong order a diagnostic (and fix) will help them get
// the keywords in the correct location.
- if (node.extendsClause == null) {
+ if (_isAbsentOrIn(node.extendsClause?.extendsKeyword)) {
addKeyword(Keyword.EXTENDS);
}
- if (node.withClause == null) {
+ if (_isAbsentOrIn(node.withClause?.withKeyword)) {
addKeyword(Keyword.WITH);
}
- if (node.implementsClause == null) {
+ if (_isAbsentOrIn(node.implementsClause?.implementsKeyword)) {
addKeyword(Keyword.IMPLEMENTS);
}
}
@@ -69,6 +69,9 @@
/// declaration before the `class` keyword. The [node] is the class
/// declaration containing the selection point.
void addClassModifiers(ClassDeclaration node) {
+ if (_isAbsentOrIn(node.abstractKeyword) && node.sealedKeyword == null) {
+ addKeyword(Keyword.ABSTRACT);
+ }
if (featureSet.isEnabled(Feature.class_modifiers) &&
featureSet.isEnabled(Feature.sealed_class)) {
if (node.baseKeyword == null &&
@@ -86,12 +89,12 @@
addKeyword(Keyword.MIXIN);
}
}
- if (node.baseKeyword != null && node.mixinKeyword == null) {
+ if (node.baseKeyword != null && _isAbsentOrIn(node.mixinKeyword)) {
// base ^ class A {}
// abstract base ^ class A {}
addKeyword(Keyword.MIXIN);
}
- if (node.mixinKeyword != null && node.baseKeyword == null) {
+ if (node.mixinKeyword != null && _isAbsentOrIn(node.baseKeyword)) {
// abstract ^ mixin class A {}
addKeyword(Keyword.BASE);
}
@@ -130,6 +133,7 @@
addKeyword(Keyword.CONST);
addKeyword(Keyword.COVARIANT);
addKeyword(Keyword.DYNAMIC);
+ addKeyword(Keyword.EXTERNAL);
addKeyword(Keyword.FINAL);
addKeyword(Keyword.MIXIN);
addKeyword(Keyword.TYPEDEF);
@@ -190,6 +194,20 @@
}
}
+ /// Add the keywords that are appropriate when the selection is at the
+ /// beginning of a directive in a compilation unit. The [before] directive is
+ /// the directive before the one being added.
+ void addDirectiveKeywords(CompilationUnit unit, Directive? before) {
+ // TODO(brianwilkerson) If we had both the members before and after the new
+ // directive, we could limit the keywords based on surrounding members.
+ if (before == null && !unit.directives.any((d) => d is LibraryDirective)) {
+ addKeyword(Keyword.LIBRARY);
+ }
+ addKeywordFromText(Keyword.IMPORT, " '^';");
+ addKeywordFromText(Keyword.EXPORT, " '^';");
+ addKeywordFromText(Keyword.PART, " '^';");
+ }
+
/// Add the keywords that are appropriate when the selection is in an enum
/// declaration between the name of the enum and the body. The [node] is the
/// enum declaration containing the selection point.
@@ -198,10 +216,10 @@
// in order to help users discover what keywords are available. If the
// keywords are in the wrong order a diagnostic (and fix) will help them get
// the keywords in the correct location.
- if (node.withClause == null) {
+ if (_isAbsentOrIn(node.withClause?.withKeyword)) {
addKeyword(Keyword.WITH);
}
- if (node.implementsClause == null) {
+ if (_isAbsentOrIn(node.implementsClause?.implementsKeyword)) {
addKeyword(Keyword.IMPLEMENTS);
}
}
@@ -312,37 +330,47 @@
/// Add the keywords that are appropriate when the selection is at the
/// beginning of field declaration.
- void addFieldDeclarationKeywords(FieldDeclaration node) {
- if (node.abstractKeyword == null) {
- addKeyword(Keyword.ABSTRACT);
- }
- if (node.covariantKeyword == null) {
- addKeyword(Keyword.COVARIANT);
- }
- if (node.externalKeyword == null) {
+ ///
+ /// If the declaration consists of a single variable and the name of the
+ /// variable is a keyword, then the parser used a keyword as the name of the
+ /// variable as part of recovery and the [keyword] should be treated like the
+ /// keyword it really is.
+ void addFieldDeclarationKeywords(FieldDeclaration node, {Keyword? keyword}) {
+ if (_isAbsentOrIn(node.externalKeyword) && keyword != Keyword.EXTERNAL) {
addKeyword(Keyword.EXTERNAL);
}
- if (node.fields.lateKeyword == null &&
- featureSet.isEnabled(Feature.non_nullable)) {
- addKeyword(Keyword.LATE);
- }
- if (node.fields.type == null) {
- addKeyword(Keyword.DYNAMIC);
- }
- if (!node.isStatic) {
- addKeyword(Keyword.STATIC);
- }
var fields = node.fields;
if (fields.type == null) {
- addKeyword(Keyword.VAR);
+ addKeyword(Keyword.DYNAMIC);
+ addKeyword(Keyword.VOID);
+ }
+ if (!node.isStatic && keyword != Keyword.STATIC) {
+ if (_isAbsentOrIn(node.abstractKeyword) && keyword != Keyword.ABSTRACT) {
+ addKeyword(Keyword.ABSTRACT);
+ }
+ if (_isAbsentOrIn(node.covariantKeyword) &&
+ keyword != Keyword.COVARIANT) {
+ addKeyword(Keyword.COVARIANT);
+ }
+ if (_isAbsentOrIn(fields.lateKeyword) &&
+ keyword != Keyword.LATE &&
+ featureSet.isEnabled(Feature.non_nullable)) {
+ addKeyword(Keyword.LATE);
+ }
+ addKeyword(Keyword.STATIC);
}
var firstField = fields.variables.firstOrNull;
if (firstField != null) {
- if (!firstField.isConst) {
+ if (!firstField.isConst &&
+ !firstField.isFinal &&
+ keyword != Keyword.CONST &&
+ keyword != Keyword.FINAL &&
+ keyword != Keyword.VAR) {
addKeyword(Keyword.CONST);
- }
- if (!firstField.isFinal) {
addKeyword(Keyword.FINAL);
+ if (fields.type == null) {
+ addKeyword(Keyword.VAR);
+ }
}
}
}
@@ -351,7 +379,7 @@
/// or `=>` in a function body. The [body] is used to determine which keywords
/// are appropriate.
void addFunctionBodyModifiers(FunctionBody? body) {
- if (body?.keyword == null) {
+ if (_isAbsentOrIn(body?.keyword)) {
addKeyword(Keyword.ASYNC);
if (body is! ExpressionFunctionBody) {
addKeywordFromText(Keyword.ASYNC, '*');
@@ -422,10 +450,10 @@
// in order to help users discover what keywords are available. If the
// keywords are in the wrong order a diagnostic (and fix) will help them get
// the keywords in the correct location.
- if (node.onClause == null) {
+ if (_isAbsentOrIn(node.onClause?.onKeyword)) {
addKeyword(Keyword.ON);
}
- if (node.implementsClause == null) {
+ if (_isAbsentOrIn(node.implementsClause?.implementsKeyword)) {
addKeyword(Keyword.IMPLEMENTS);
}
}
@@ -452,7 +480,7 @@
/// declaration before the `mixin` keyword. The [node] is the mixin
/// declaration containing the selection point.
void addMixinModifiers(MixinDeclaration node) {
- if (node.baseKeyword == null) {
+ if (_isAbsentOrIn(node.baseKeyword)) {
addKeyword(Keyword.BASE);
}
}
@@ -529,6 +557,12 @@
addKeyword(Keyword.FINAL);
addKeyword(Keyword.VAR);
}
+
+ /// Return `true` if the [token] is `null` or if the offset is toughing the
+ /// [token].
+ bool _isAbsentOrIn(Token? token) {
+ return token == null || (token.offset <= offset && offset <= token.end);
+ }
}
extension on CollectionElement? {
diff --git a/pkg/analysis_server/test/completion_test.dart b/pkg/analysis_server/test/completion_test.dart
index 7882958..8a993f6 100644
--- a/pkg/analysis_server/test/completion_test.dart
+++ b/pkg/analysis_server/test/completion_test.dart
@@ -54,21 +54,17 @@
class Date{}final num M = Dat!1''', <String>['1+Date']);
// space, char, eol are important
- buildTests(
- 'testCommentSnippets009',
- '''
-class Maps{}class x extends!5 !2M!3 !4implements!6 !1\n{}''',
- <String>[
- '1+Map',
- '2+Maps',
- '3+Maps',
- '4-Maps',
- '4+implements',
- '5-Maps',
- '6-Map',
- '6+implements'
- ],
- failingTests: '46');
+ buildTests('testCommentSnippets009', '''
+class Maps{}class x extends!5 !2M!3 !4implements!6 !1\n{}''', <String>[
+ '1+Map',
+ '2+Maps',
+ '3+Maps',
+ '4-Maps',
+ '4+implements',
+ '5-Maps',
+ '6-Map',
+ '6+implements'
+ ]);
// space, char, eol are important
buildTests('testCommentSnippets010', '''
@@ -1950,7 +1946,7 @@
'7-Dclass',
'7-Ctype',
],
- failingTests: '2346');
+ failingTests: '4');
// keywords
buildTests(
@@ -2135,7 +2131,10 @@
'7+show',
'8-null'
],
- failingTests: '234567'); //TODO(jwren) 234 failing as correct selection
+ // Test 1 fails because we don't suggest other directives when at the
+ // beginning of a directive. Some clients will replace the existing
+ // keyword rather than insert a new one.
+ failingTests: '1234567'); //TODO(jwren) 234 failing as correct selection
// offset assertions can't be passed into buildTests(..)
// keywords
diff --git a/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart b/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart
index e701163..54f1050 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/class_body_test.dart
@@ -374,15 +374,11 @@
${keywords.asKeywordSuggestions}
''', where: context.where);
} else {
- // TODO(scheglov) This is wrong.
final keywords = {
- Keyword.ABSTRACT,
Keyword.CONST,
- Keyword.COVARIANT,
Keyword.DYNAMIC,
Keyword.EXTERNAL,
Keyword.FINAL,
- Keyword.LATE,
Keyword.VAR,
Keyword.VOID,
};
@@ -407,8 +403,12 @@
_printKeywordsOrClass();
final keywords = {
+ Keyword.CONST,
Keyword.DYNAMIC,
+ Keyword.EXTERNAL,
Keyword.FINAL,
+ Keyword.VAR,
+ Keyword.VOID,
};
assertResponse('''
@@ -430,8 +430,10 @@
final keywords = {
Keyword.CONST,
Keyword.DYNAMIC,
+ Keyword.EXTERNAL,
Keyword.FINAL,
- Keyword.LATE,
+ Keyword.VAR,
+ Keyword.VOID,
};
assertResponse('''
@@ -451,17 +453,13 @@
_printKeywordsOrClass();
final keywords = {
- // TODO(scheglov) This does not look right.
- Keyword.ABSTRACT,
Keyword.CONST,
- // TODO(scheglov) This does not look right.
- Keyword.COVARIANT,
Keyword.DYNAMIC,
// TODO(scheglov) This does not look right.
Keyword.EXTERNAL,
Keyword.FINAL,
- Keyword.LATE,
Keyword.VAR,
+ Keyword.VOID,
};
assertResponse('''
diff --git a/pkg/analysis_server/test/services/completion/dart/location/compilation_unit_member_test.dart b/pkg/analysis_server/test/services/completion/dart/location/compilation_unit_member_test.dart
index e2e354d..598dc3b 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/compilation_unit_member_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/compilation_unit_member_test.dart
@@ -113,6 +113,8 @@
''');
assertResponse(r'''
suggestions
+ abstract
+ kind: keyword
mixin
kind: keyword
''');
@@ -144,6 +146,8 @@
''');
assertResponse('''
suggestions
+ abstract
+ kind: keyword
''');
}
@@ -170,6 +174,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -224,6 +230,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -291,49 +299,17 @@
replacement
left: 3
suggestions
- abstract
- kind: keyword
- base
- kind: keyword
- class
- kind: keyword
- const
- kind: keyword
- covariant
- kind: keyword
- dynamic
- kind: keyword
export '';
kind: keyword
selection: 8
- extension
- kind: keyword
- external
- kind: keyword
- final
- kind: keyword
import '';
kind: keyword
selection: 8
- interface
- kind: keyword
- late
- kind: keyword
library
kind: keyword
- mixin
- kind: keyword
part '';
kind: keyword
selection: 6
- sealed
- kind: keyword
- typedef
- kind: keyword
- var
- kind: keyword
- void
- kind: keyword
''');
}
}
@@ -407,54 +383,21 @@
^imp
import "package:foo/foo.dart";
''');
- // TODO(danrubel) should not suggest declaration keywords
assertResponse(r'''
replacement
right: 3
suggestions
- abstract
- kind: keyword
- base
- kind: keyword
- class
- kind: keyword
- const
- kind: keyword
- covariant
- kind: keyword
- dynamic
- kind: keyword
export '';
kind: keyword
selection: 8
- extension
- kind: keyword
- external
- kind: keyword
- final
- kind: keyword
import '';
kind: keyword
selection: 8
- interface
- kind: keyword
- late
- kind: keyword
library
kind: keyword
- mixin
- kind: keyword
part '';
kind: keyword
selection: 6
- sealed
- kind: keyword
- typedef
- kind: keyword
- var
- kind: keyword
- void
- kind: keyword
''');
}
@@ -479,6 +422,8 @@
kind: keyword
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
interface
@@ -580,6 +525,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -678,6 +625,8 @@
''');
assertResponse(r'''
suggestions
+ abstract
+ kind: keyword
''');
}
@@ -696,6 +645,8 @@
''');
assertResponse('''
suggestions
+ abstract
+ kind: keyword
''');
}
@@ -731,6 +682,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -825,6 +778,8 @@
''');
assertResponse(r'''
suggestions
+ abstract
+ kind: keyword
base
kind: keyword
''');
@@ -855,6 +810,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -894,54 +851,21 @@
^imp
import "package:foo/foo.dart";
''');
- // TODO(danrubel) should not suggest declaration keywords
assertResponse(r'''
replacement
right: 3
suggestions
- abstract
- kind: keyword
- base
- kind: keyword
- class
- kind: keyword
- const
- kind: keyword
- covariant
- kind: keyword
- dynamic
- kind: keyword
export '';
kind: keyword
selection: 8
- extension
- kind: keyword
- external
- kind: keyword
- final
- kind: keyword
import '';
kind: keyword
selection: 8
- interface
- kind: keyword
- late
- kind: keyword
library
kind: keyword
- mixin
- kind: keyword
part '';
kind: keyword
selection: 6
- sealed
- kind: keyword
- typedef
- kind: keyword
- var
- kind: keyword
- void
- kind: keyword
''');
}
@@ -985,6 +909,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -1035,6 +961,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
@@ -1083,6 +1011,8 @@
kind: keyword
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
interface
@@ -1180,7 +1110,6 @@
imp^
import "package:foo/foo.dart";
''');
- // TODO(danrubel) should not suggest declaration keywords
if (isProtocolVersion2) {
assertResponse(r'''
replacement
@@ -1195,47 +1124,15 @@
replacement
left: 3
suggestions
- abstract
- kind: keyword
- base
- kind: keyword
- class
- kind: keyword
- const
- kind: keyword
- covariant
- kind: keyword
- dynamic
- kind: keyword
export '';
kind: keyword
selection: 8
- extension
- kind: keyword
- external
- kind: keyword
- final
- kind: keyword
import '';
kind: keyword
selection: 8
- interface
- kind: keyword
- late
- kind: keyword
- mixin
- kind: keyword
part '';
kind: keyword
selection: 6
- sealed
- kind: keyword
- typedef
- kind: keyword
- var
- kind: keyword
- void
- kind: keyword
''');
}
}
@@ -1246,8 +1143,7 @@
imp^
import "package:foo/foo.dart";
''');
- // TODO(danrubel) should not suggest declaration keywords
- // TODO(brianwilkerson) Should not suggest library, export or part directives.
+ // TODO(brianwilkerson) Should not suggest `export` or `part` directives.
if (isProtocolVersion2) {
assertResponse(r'''
replacement
@@ -1262,47 +1158,15 @@
replacement
left: 3
suggestions
- abstract
- kind: keyword
- base
- kind: keyword
- class
- kind: keyword
- const
- kind: keyword
- covariant
- kind: keyword
- dynamic
- kind: keyword
export '';
kind: keyword
selection: 8
- extension
- kind: keyword
- external
- kind: keyword
- final
- kind: keyword
import '';
kind: keyword
selection: 8
- interface
- kind: keyword
- late
- kind: keyword
- mixin
- kind: keyword
part '';
kind: keyword
selection: 6
- sealed
- kind: keyword
- typedef
- kind: keyword
- var
- kind: keyword
- void
- kind: keyword
''');
}
}
@@ -1330,6 +1194,8 @@
selection: 8
extension
kind: keyword
+ external
+ kind: keyword
final
kind: keyword
import '';
diff --git a/pkg/analysis_server/test/services/completion/dart/location/field_declaration_test.dart b/pkg/analysis_server/test/services/completion/dart/location/field_declaration_test.dart
index 6ae6911..6736e4b 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/field_declaration_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/field_declaration_test.dart
@@ -27,8 +27,6 @@
suggestions
const
kind: keyword
- covariant
- kind: keyword
''');
} else {
assertResponse(r'''
@@ -66,20 +64,14 @@
}
kind: override
selection: 90 38
- abstract
- kind: keyword
const
kind: keyword
- covariant
- kind: keyword
dynamic
kind: keyword
external
kind: keyword
final
kind: keyword
- late
- kind: keyword
var
kind: keyword
void
@@ -138,20 +130,14 @@
}
kind: override
selection: 90 38
- abstract
- kind: keyword
const
kind: keyword
- covariant
- kind: keyword
dynamic
kind: keyword
external
kind: keyword
final
kind: keyword
- late
- kind: keyword
var
kind: keyword
void
@@ -225,28 +211,20 @@
suggestions
const
kind: keyword
- covariant
- kind: keyword
''');
} else {
assertResponse(r'''
replacement
left: 1
suggestions
- abstract
- kind: keyword
const
kind: keyword
- covariant
- kind: keyword
dynamic
kind: keyword
external
kind: keyword
final
kind: keyword
- late
- kind: keyword
var
kind: keyword
void
@@ -274,20 +252,14 @@
replacement
left: 1
suggestions
- abstract
- kind: keyword
const
kind: keyword
- covariant
- kind: keyword
dynamic
kind: keyword
external
kind: keyword
final
kind: keyword
- late
- kind: keyword
var
kind: keyword
void
diff --git a/pkg/analysis_server/test/services/completion/dart/location/function_declaration_test.dart b/pkg/analysis_server/test/services/completion/dart/location/function_declaration_test.dart
index dc58b73..c6a098c 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/function_declaration_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/function_declaration_test.dart
@@ -152,7 +152,7 @@
''');
}
- Future<void> test_afterRightParent_beforeVariable_partial() async {
+ Future<void> test_afterRightParen_beforeVariable_partial() async {
await computeSuggestions('''
void f()a^ Foo foo;
''');
@@ -161,8 +161,6 @@
replacement
left: 1
suggestions
- abstract
- kind: keyword
async
kind: keyword
async*
@@ -173,44 +171,20 @@
replacement
left: 1
suggestions
- abstract
- kind: keyword
async
kind: keyword
async*
kind: keyword
- base
- kind: keyword
- class
- kind: keyword
const
kind: keyword
- covariant
- kind: keyword
- dynamic
- kind: keyword
- extension
- kind: keyword
external
kind: keyword
final
kind: keyword
- interface
- kind: keyword
late
kind: keyword
- mixin
- kind: keyword
- sealed
- kind: keyword
sync*
kind: keyword
- typedef
- kind: keyword
- var
- kind: keyword
- void
- kind: keyword
''');
}
}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/relational_pattern_test.dart b/pkg/analysis_server/test/services/completion/dart/location/relational_pattern_test.dart
index aae9928..d51afeb 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/relational_pattern_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/relational_pattern_test.dart
@@ -71,17 +71,13 @@
kind: class
B01
kind: constructorInvocation
- const
- kind: keyword
- dynamic
- kind: keyword
false
kind: keyword
null
kind: keyword
- true
+ switch
kind: keyword
- void
+ true
kind: keyword
''');
}
@@ -99,6 +95,7 @@
class A02 {}
class B01 {}
''');
+ // TODO(brianwilkerson) We lost `const`.
assertResponse(r'''
suggestions
A01
@@ -113,17 +110,13 @@
kind: class
B01
kind: constructorInvocation
- const
- kind: keyword
- dynamic
- kind: keyword
false
kind: keyword
null
kind: keyword
- true
+ switch
kind: keyword
- void
+ true
kind: keyword
''');
}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/type_argument_list_test.dart b/pkg/analysis_server/test/services/completion/dart/location/type_argument_list_test.dart
index c2fe3b3..35d86c8 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/type_argument_list_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/type_argument_list_test.dart
@@ -160,9 +160,13 @@
kind: class
B01
kind: class
- dynamic
+ false
kind: keyword
- void
+ null
+ kind: keyword
+ switch
+ kind: keyword
+ true
kind: keyword
''');
}