mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 16:19:07 +00:00
[code completion] Move completion generation for initializing and super parameters
Change-Id: Ie9d09b323c7072c4fe375b6de12347a16065ac5e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341823 Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
07bba0a7d2
commit
ae15831811
|
@ -356,6 +356,19 @@ final class StaticFieldSuggestion extends ImportableSuggestion {
|
|||
}
|
||||
}
|
||||
|
||||
/// The information about a candidate suggestion based on a parameter from a
|
||||
/// super constructor.
|
||||
final class SuperParameterSuggestion extends CandidateSuggestion {
|
||||
/// The element on which the suggestion is based.
|
||||
final ParameterElement element;
|
||||
|
||||
/// Initialize a newly created candidate suggestion to suggest the [element].
|
||||
SuperParameterSuggestion(this.element);
|
||||
|
||||
@override
|
||||
String get completion => element.name;
|
||||
}
|
||||
|
||||
/// The information about a candidate suggestion based on a top-level getter or
|
||||
/// setter.
|
||||
final class TopLevelFunctionSuggestion extends ImportableSuggestion {
|
||||
|
@ -516,6 +529,8 @@ extension SuggestionBuilderExtension on SuggestionBuilder {
|
|||
libraryUriStr = suggestion.libraryUriStr;
|
||||
suggestStaticField(suggestion.element, prefix: suggestion.prefix);
|
||||
libraryUriStr = null;
|
||||
case SuperParameterSuggestion():
|
||||
suggestSuperFormalParameter(suggestion.element);
|
||||
case TopLevelFunctionSuggestion():
|
||||
libraryUriStr = suggestion.libraryUriStr;
|
||||
suggestTopLevelFunction(suggestion.element,
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'package:analysis_server/src/services/completion/dart/completion_state.da
|
|||
import 'package:analysis_server/src/services/completion/dart/enum_constant_constructor_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/extension_member_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/field_formal_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/in_scope_completion_pass.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/library_member_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/library_prefix_contributor.dart';
|
||||
|
@ -24,7 +23,6 @@ import 'package:analysis_server/src/services/completion/dart/redirecting_contrib
|
|||
import 'package:analysis_server/src/services/completion/dart/static_member_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/suggestion_collector.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/super_formal_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/type_member_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/uri_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/variable_name_contributor.dart';
|
||||
|
@ -137,7 +135,6 @@ class DartCompletionManager {
|
|||
ClosureContributor(request, builder),
|
||||
EnumConstantConstructorContributor(request, builder),
|
||||
ExtensionMemberContributor(request, builder),
|
||||
FieldFormalContributor(request, builder),
|
||||
LibraryMemberContributor(request, builder),
|
||||
LibraryPrefixContributor(request, builder),
|
||||
NamedConstructorContributor(request, builder),
|
||||
|
@ -145,7 +142,6 @@ class DartCompletionManager {
|
|||
RecordLiteralContributor(request, builder),
|
||||
RedirectingContributor(request, builder),
|
||||
StaticMemberContributor(request, builder),
|
||||
SuperFormalContributor(request, builder),
|
||||
TypeMemberContributor(request, builder),
|
||||
if (enableUriContributor) UriContributor(request, builder),
|
||||
VariableNameContributor(request, builder),
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:analysis_server/src/services/completion/dart/visibility_tracker.
|
|||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/resolver/scope.dart';
|
||||
|
||||
/// A helper class that produces candidate suggestions for all of the
|
||||
|
@ -114,8 +115,10 @@ class DeclarationHelper {
|
|||
}
|
||||
|
||||
/// Add any fields that can be initialized in the initializer list of the
|
||||
/// given [constructor].
|
||||
void addFieldsForInitializers(ConstructorDeclaration constructor) {
|
||||
/// given [constructor]. If a [fieldToInclude] is provided, then it should not
|
||||
/// be skipped because the cursor is inside that field's name.
|
||||
void addFieldsForInitializers(
|
||||
ConstructorDeclaration constructor, FieldElement? fieldToInclude) {
|
||||
var containingElement = constructor.declaredElement?.enclosingElement;
|
||||
if (containingElement == null) {
|
||||
return;
|
||||
|
@ -133,6 +136,9 @@ class DeclarationHelper {
|
|||
}
|
||||
// Skip fields that are already initialized in the parameter list.
|
||||
for (var parameter in constructor.parameters.parameters) {
|
||||
if (parameter is DefaultFormalParameter) {
|
||||
parameter = parameter.parameter;
|
||||
}
|
||||
if (parameter is FieldFormalParameter) {
|
||||
var parameterElement = parameter.declaredElement;
|
||||
if (parameterElement is FieldFormalParameterElement) {
|
||||
|
@ -143,10 +149,14 @@ class DeclarationHelper {
|
|||
}
|
||||
}
|
||||
}
|
||||
fieldsToSkip.remove(fieldToInclude);
|
||||
|
||||
for (var field in containingElement.fields) {
|
||||
// Skip fields that are already initialized at their declaration.
|
||||
if (!fieldsToSkip.contains(field) && !field.hasInitializer) {
|
||||
if (!field.isStatic &&
|
||||
!field.isSynthetic &&
|
||||
!fieldsToSkip.contains(field) &&
|
||||
(!(field.isFinal || field.isConst) || !field.hasInitializer)) {
|
||||
_suggestField(field, containingElement);
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +214,57 @@ class DeclarationHelper {
|
|||
// TODO(brianwilkerson): Implement this.
|
||||
}
|
||||
|
||||
/// Add any parameters from the super constructor of the constructor
|
||||
/// containing the [node] that can be referenced as a super parameter.
|
||||
void addParametersFromSuperConstructor(SuperFormalParameter node) {
|
||||
var element = node.declaredElement;
|
||||
if (element is! SuperFormalParameterElementImpl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var constructor = node.thisOrAncestorOfType<ConstructorDeclaration>();
|
||||
if (constructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var constructorElement = constructor.declaredElement;
|
||||
if (constructorElement is! ConstructorElementImpl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var superConstructor = constructorElement.superConstructor;
|
||||
if (superConstructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.isNamed) {
|
||||
var superConstructorInvocation = constructor.initializers
|
||||
.whereType<SuperConstructorInvocation>()
|
||||
.singleOrNull;
|
||||
var specified = <String>{
|
||||
...constructorElement.parameters.map((e) => e.name),
|
||||
...?superConstructorInvocation?.argumentList.arguments
|
||||
.whereType<NamedExpression>()
|
||||
.map((e) => e.name.label.name),
|
||||
};
|
||||
for (var superParameter in superConstructor.parameters) {
|
||||
if (superParameter.isNamed &&
|
||||
!specified.contains(superParameter.name)) {
|
||||
collector.addSuggestion(SuperParameterSuggestion(superParameter));
|
||||
}
|
||||
}
|
||||
} else if (node.isPositional) {
|
||||
var indexOfThis = element.indexIn(constructorElement);
|
||||
var superPositionalList = superConstructor.parameters
|
||||
.where((parameter) => parameter.isPositional)
|
||||
.toList();
|
||||
if (indexOfThis >= 0 && indexOfThis < superPositionalList.length) {
|
||||
var superPositional = superPositionalList[indexOfThis];
|
||||
collector.addSuggestion(SuperParameterSuggestion(superPositional));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add suggestions for any constructors that are declared within the
|
||||
/// [library].
|
||||
void _addConstructors(LibraryElement library, String? prefix) {
|
||||
|
|
|
@ -1,78 +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.
|
||||
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/src/util/performance/operation_performance.dart';
|
||||
|
||||
/// A contributor that produces suggestions for field formal parameters that are
|
||||
/// based on the fields declared directly by the enclosing class that are not
|
||||
/// already initialized. More concretely, this class produces suggestions for
|
||||
/// expressions of the form `this.^` in a constructor's parameter list.
|
||||
class FieldFormalContributor extends DartCompletionContributor {
|
||||
FieldFormalContributor(super.request, super.builder);
|
||||
|
||||
@override
|
||||
Future<void> computeSuggestions({
|
||||
required OperationPerformanceImpl performance,
|
||||
}) async {
|
||||
var node = request.target.containingNode;
|
||||
// TODO(brianwilkerson): We should suggest field formal parameters even if
|
||||
// the user hasn't already typed the `this.` prefix, by including the
|
||||
// prefix in the completion.
|
||||
if (node is! FieldFormalParameter) {
|
||||
return;
|
||||
}
|
||||
|
||||
var constructor = node.thisOrAncestorOfType<ConstructorDeclaration>();
|
||||
if (constructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the list of fields already referenced in the constructor.
|
||||
// TODO(brianwilkerson): This doesn't include fields in initializers, which
|
||||
// shouldn't be suggested.
|
||||
var referencedFields = <String>[];
|
||||
for (var param in constructor.parameters.parameters) {
|
||||
if (param is DefaultFormalParameter) {
|
||||
param = param.parameter;
|
||||
}
|
||||
if (param is FieldFormalParameter) {
|
||||
var fieldId = param.name;
|
||||
if (fieldId != request.target.entity) {
|
||||
var fieldName = fieldId.lexeme;
|
||||
if (fieldName.isNotEmpty) {
|
||||
referencedFields.add(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceElement? enclosingClass;
|
||||
var constructorParent = constructor.parent;
|
||||
if (constructorParent is ClassDeclaration) {
|
||||
enclosingClass = constructorParent.declaredElement;
|
||||
} else if (constructorParent is EnumDeclaration) {
|
||||
enclosingClass = constructorParent.declaredElement;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (enclosingClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add suggestions for fields that are not already referenced.
|
||||
for (var field in enclosingClass.fields) {
|
||||
if (!field.isSynthetic && !field.isEnumConstant && !field.isStatic) {
|
||||
var fieldName = field.name;
|
||||
if (fieldName.isNotEmpty) {
|
||||
if (!referencedFields.contains(fieldName)) {
|
||||
builder.suggestFieldFormalParameter(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -759,6 +759,22 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFieldFormalParameter(FieldFormalParameter node) {
|
||||
var constructor = node.parent?.parent;
|
||||
if (constructor is FormalParameterList) {
|
||||
constructor = constructor.parent;
|
||||
}
|
||||
if (constructor is ConstructorDeclaration) {
|
||||
var declaredElement = node.declaredElement;
|
||||
FieldElement? field;
|
||||
if (declaredElement is FieldFormalParameterElement) {
|
||||
field = declaredElement.field;
|
||||
}
|
||||
declarationHelper().addFieldsForInitializers(constructor, field);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
|
||||
_visitForEachParts(node);
|
||||
|
@ -1475,6 +1491,11 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
|
|||
node.parent as ConstructorDeclaration, node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperFormalParameter(SuperFormalParameter node) {
|
||||
declarationHelper().addParametersFromSuperConstructor(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSwitchCase(SwitchCase node) {
|
||||
_forStatement(node);
|
||||
|
@ -1941,8 +1962,13 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
|
|||
/// beginning of a constructor's initializer.
|
||||
void _forConstructorInitializer(ConstructorDeclaration constructor,
|
||||
ConstructorFieldInitializer? initializer) {
|
||||
var element = initializer?.fieldName.staticElement;
|
||||
FieldElement? field;
|
||||
if (element is FieldElement) {
|
||||
field = element;
|
||||
}
|
||||
keywordHelper.addConstructorInitializerKeywords(constructor, initializer);
|
||||
declarationHelper().addFieldsForInitializers(constructor);
|
||||
declarationHelper().addFieldsForInitializers(constructor, field);
|
||||
}
|
||||
|
||||
/// Add the suggestions that are appropriate when the selection is at the
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2022, 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.
|
||||
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/util/performance/operation_performance.dart';
|
||||
|
||||
/// A contributor that produces suggestions for super formal parameters that
|
||||
/// are based on the parameters declared by the invoked super-constructor.
|
||||
/// The enclosing declaration is expected to be a constructor.
|
||||
class SuperFormalContributor extends DartCompletionContributor {
|
||||
SuperFormalContributor(super.request, super.builder);
|
||||
|
||||
@override
|
||||
Future<void> computeSuggestions({
|
||||
required OperationPerformanceImpl performance,
|
||||
}) async {
|
||||
var node = request.target.containingNode;
|
||||
if (node is! SuperFormalParameter) {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = node.declaredElement as SuperFormalParameterElementImpl;
|
||||
|
||||
var constructor = node.thisOrAncestorOfType<ConstructorDeclaration>();
|
||||
if (constructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var constructorElement = constructor.declaredElement;
|
||||
constructorElement as ConstructorElementImpl;
|
||||
|
||||
var superConstructor = constructorElement.superConstructor;
|
||||
if (superConstructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.isNamed) {
|
||||
var superConstructorInvocation = constructor.initializers
|
||||
.whereType<SuperConstructorInvocation>()
|
||||
.singleOrNull;
|
||||
var specified = <String>{
|
||||
...constructorElement.parameters.map((e) => e.name),
|
||||
...?superConstructorInvocation?.argumentList.arguments
|
||||
.whereType<NamedExpression>()
|
||||
.map((e) => e.name.label.name),
|
||||
};
|
||||
for (var superParameter in superConstructor.parameters) {
|
||||
if (superParameter.isNamed &&
|
||||
!specified.contains(superParameter.name)) {
|
||||
builder.suggestSuperFormalParameter(superParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node.isPositional) {
|
||||
var indexOfThis = element.indexIn(constructorElement);
|
||||
var superPositionalList = superConstructor.parameters
|
||||
.where((parameter) => parameter.isPositional)
|
||||
.toList();
|
||||
if (indexOfThis >= 0 && indexOfThis < superPositionalList.length) {
|
||||
var superPositional = superPositionalList[indexOfThis];
|
||||
builder.suggestSuperFormalParameter(superPositional);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -92,6 +92,8 @@ class A {
|
|||
suggestions
|
||||
assert
|
||||
kind: keyword
|
||||
f0
|
||||
kind: field
|
||||
f1
|
||||
kind: field
|
||||
super
|
||||
|
|
Loading…
Reference in a new issue