Modify the override_contributor.dart to provide the completions even when no class member characters have been typed.

Change-Id: I03164bcb0761faf7634ec219a1b1cc87dc568fa9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144493
Commit-Queue: Jaime Wren <jwren@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Jaime Wren
2020-04-23 16:19:49 +00:00
committed by commit-bot@chromium.org
parent 68e77baf59
commit f7d14e49c2
2 changed files with 73 additions and 28 deletions
@@ -15,6 +15,7 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
@@ -26,14 +27,19 @@ class OverrideContributor implements DartCompletionContributor {
@override
Future<List<CompletionSuggestion>> computeSuggestions(
DartCompletionRequest request, SuggestionBuilder builder) async {
var targetId = _getTargetId(request.target);
if (targetId == null) {
return const <CompletionSuggestion>[];
}
var classDecl = targetId.thisOrAncestorOfType<ClassOrMixinDeclaration>();
var target = request.target;
var containingNode = target.containingNode;
var classDecl =
containingNode.thisOrAncestorOfType<ClassOrMixinDeclaration>();
if (classDecl == null) {
return const <CompletionSuggestion>[];
}
if (_inClassMemberBody(containingNode)) {
return const <CompletionSuggestion>[];
}
var sourceRange = _getTargetSourceRange(target);
sourceRange ??= range.startOffsetEndOffset(request.offset, 0);
var inheritance = InheritanceManager3();
@@ -53,7 +59,7 @@ class OverrideContributor implements DartCompletionContributor {
if (element.returnType != null) {
var invokeSuper = interface.isSuperImplemented(name);
var suggestion =
await _buildSuggestion(request, targetId, element, invokeSuper);
await _buildSuggestion(request, sourceRange, element, invokeSuper);
if (suggestion != null) {
suggestions.add(suggestion);
}
@@ -62,17 +68,17 @@ class OverrideContributor implements DartCompletionContributor {
return suggestions;
}
/// Build a suggestion to replace [targetId] in the given [request] with an
/// Build a suggestion to replace [sourceRange] in the given [request] with an
/// override of the given [element].
Future<CompletionSuggestion> _buildSuggestion(
DartCompletionRequest request,
SimpleIdentifier targetId,
SourceRange sourceRange,
ExecutableElement element,
bool invokeSuper) async {
var displayTextBuffer = StringBuffer();
var builder = DartChangeBuilder(request.result.session);
await builder.addFileEdit(request.result.path, (builder) {
builder.addReplacement(range.node(targetId), (builder) {
builder.addReplacement(sourceRange, (builder) {
builder.writeOverride(
element,
displayTextBuffer: displayTextBuffer,
@@ -102,7 +108,7 @@ class OverrideContributor implements DartCompletionContributor {
if (selectionRange == null) {
return null;
}
var offsetDelta = targetId.offset + replacement.indexOf(completion);
var offsetDelta = sourceRange.offset + replacement.indexOf(completion);
var displayText =
displayTextBuffer.isNotEmpty ? displayTextBuffer.toString() : null;
var suggestion = CompletionSuggestion(
@@ -118,24 +124,6 @@ class OverrideContributor implements DartCompletionContributor {
return suggestion;
}
/// If the target looks like a partial identifier inside a class declaration
/// then return that identifier, otherwise return `null`.
SimpleIdentifier _getTargetId(CompletionTarget target) {
var node = target.containingNode;
if (node is ClassOrMixinDeclaration) {
var entity = target.entity;
if (entity is FieldDeclaration) {
return _getTargetIdFromVarList(entity.fields);
}
} else if (node is FieldDeclaration) {
var entity = target.entity;
if (entity is VariableDeclarationList) {
return _getTargetIdFromVarList(entity);
}
}
return null;
}
SimpleIdentifier _getTargetIdFromVarList(VariableDeclarationList fields) {
var variables = fields.variables;
if (variables.length == 1) {
@@ -157,6 +145,29 @@ class OverrideContributor implements DartCompletionContributor {
return null;
}
/// If the target looks like a partial identifier inside a class declaration
/// then return that identifier [SourceRange], otherwise return `null`.
SourceRange _getTargetSourceRange(CompletionTarget target) {
var containingNode = target.containingNode;
if (containingNode is ClassOrMixinDeclaration) {
if (target.entity is FieldDeclaration) {
var fieldDecl = target.entity as FieldDeclaration;
var simpleIdentifier = _getTargetIdFromVarList(fieldDecl.fields);
if (simpleIdentifier != null) {
return range.node(simpleIdentifier);
}
}
} else if (containingNode is FieldDeclaration) {
if (target.entity is VariableDeclarationList) {
var simpleIdentifier = _getTargetIdFromVarList(target.entity);
if (simpleIdentifier != null) {
return range.node(simpleIdentifier);
}
}
}
return null;
}
/// Return `true` if the given [node] has an `override` annotation.
bool _hasOverride(AstNode node) {
if (node is AnnotatedNode) {
@@ -205,4 +216,20 @@ class OverrideContributor implements DartCompletionContributor {
nullabilitySuffix: NullabilitySuffix.none,
);
}
static bool _inClassMemberBody(AstNode node) {
/// TODO(jwren) this method was copied from keyword_contributor.dart, all
/// related methods should be moved into a superclass or mixin
while (true) {
var body = node.thisOrAncestorOfType<FunctionBody>();
if (body == null) {
return false;
}
var parent = body.parent;
if (parent is ConstructorDeclaration || parent is MethodDeclaration) {
return true;
}
node = parent;
}
}
}
@@ -285,6 +285,24 @@ method() {
selectionLength: 22);
}
Future<void> test_no_target_inClass_of_interface() async {
addTestSource('''
class A {
void foo() {}
}
class B implements A {
^
}
''');
await computeSuggestions();
_assertOverride('''
@override
void foo() {
// TODO: implement foo
}''', displayText: 'foo() { … }', selectionOffset: 51, selectionLength: 0);
}
Future<void> test_outsideOfWorkspace() async {
testFile = convertPath('/home/other/lib/a.dart');
addTestSource('''