blob: 81d91a57f6b30f1b4ae6d1d40714d49d8b0a7179 [file] [log] [blame]
// Copyright (c) 2015, 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 'dart:async';
import 'package:analysis_server/src/protocol_server.dart'
show CompletionSuggestion, CompletionSuggestionKind;
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart'
show DartCompletionRequestImpl;
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
import 'package:analyzer/dart/analysis/features.dart';
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_plugin/protocol/protocol_common.dart' as protocol
show ElementKind;
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
import 'package:analyzer_plugin/src/utilities/visitors/local_declaration_visitor.dart'
show LocalDeclarationVisitor;
/// A contributor that produces suggestions based on the constructors declared
/// in the same file in which suggestions were requested.
class LocalConstructorContributor extends DartCompletionContributor {
@override
Future<List<CompletionSuggestion>> computeSuggestions(
DartCompletionRequest request, SuggestionBuilder builder) async {
var optype = (request as DartCompletionRequestImpl).opType;
// Collect suggestions from the specific child [AstNode] that contains
// the completion offset and all of its parents recursively.
var suggestions = <CompletionSuggestion>[];
if (!optype.isPrefixed) {
if (optype.includeConstructorSuggestions) {
_Visitor(request, suggestions, optype)
.visit(request.target.containingNode);
}
}
return suggestions;
}
}
/// A visitor for collecting constructor suggestions.
class _Visitor extends LocalDeclarationVisitor {
final DartCompletionRequest request;
final OpType optype;
final List<CompletionSuggestion> suggestions;
_Visitor(DartCompletionRequest request, this.suggestions, this.optype)
: request = request,
super(request.offset);
@override
void declaredClass(ClassDeclaration declaration) {
var found = false;
for (var member in declaration.members) {
if (member is ConstructorDeclaration) {
found = true;
_addSuggestion(declaration, member);
}
}
if (!found) {
_addSuggestion(declaration, null);
}
}
@override
void declaredClassTypeAlias(ClassTypeAlias declaration) {}
@override
void declaredExtension(ExtensionDeclaration declaration) {}
@override
void declaredField(FieldDeclaration fieldDecl, VariableDeclaration varDecl) {}
@override
void declaredFunction(FunctionDeclaration declaration) {}
@override
void declaredFunctionTypeAlias(FunctionTypeAlias declaration) {}
@override
void declaredGenericTypeAlias(GenericTypeAlias declaration) {}
@override
void declaredLabel(Label label, bool isCaseLabel) {}
@override
void declaredLocalVar(SimpleIdentifier name, TypeAnnotation type) {}
@override
void declaredMethod(MethodDeclaration declaration) {}
@override
void declaredParam(SimpleIdentifier name, TypeAnnotation type) {}
@override
void declaredTopLevelVar(
VariableDeclarationList varList, VariableDeclaration varDecl) {}
/// For the given class and constructor, add a suggestion of the form `B(...)`
/// or `B.name(...)`. If the given constructor is `null` then add a default
/// constructor suggestion.
void _addSuggestion(
ClassDeclaration classDecl, ConstructorDeclaration constructorDecl) {
var completion = classDecl.name.name;
var classElement = classDecl.declaredElement;
var useNewRelevance = request.useNewRelevance;
int relevance;
if (useNewRelevance) {
var contextType = request.featureComputer
.computeContextType(request.target.containingNode);
var contextTypeFeature = request.featureComputer
.contextTypeFeature(contextType, classElement.thisType);
relevance = toRelevance(contextTypeFeature, Relevance.constructor);
} else {
relevance = optype.returnValueSuggestionsFilter(
_instantiateClassElement(classElement), DART_RELEVANCE_DEFAULT);
}
if (constructorDecl != null) {
// Build a suggestion for explicitly declared constructor.
var element = constructorDecl.declaredElement;
if (element == null) {
return;
}
if (classElement.isAbstract && !element.isFactory) {
return;
}
var name = constructorDecl.name?.name;
if (name != null && name.isNotEmpty) {
completion = '$completion.$name';
}
var suggestion = createSuggestion(request, element,
completion: completion,
relevance: relevance,
useNewRelevance: useNewRelevance);
if (suggestion != null) {
suggestions.add(suggestion);
}
} else if (!classElement.isAbstract) {
// Build a suggestion for an implicit constructor.
var element = createLocalElement(
request.source, protocol.ElementKind.CONSTRUCTOR, null,
parameters: '()');
element.returnType = classDecl.name.name;
var suggestion = CompletionSuggestion(CompletionSuggestionKind.INVOCATION,
relevance, completion, completion.length, 0, false, false,
declaringType: classDecl.name.name,
element: element,
parameterNames: [],
parameterTypes: [],
requiredParameterCount: 0,
hasNamedParameters: false);
suggestions.add(suggestion);
}
}
InterfaceType _instantiateClassElement(ClassElement element) {
var typeParameters = element.typeParameters;
var typeArguments = const <DartType>[];
if (typeParameters.isNotEmpty) {
var typeProvider = request.libraryElement.typeProvider;
typeArguments = typeParameters.map((t) {
return typeProvider.dynamicType;
}).toList();
}
var nullabilitySuffix = request.featureSet.isEnabled(Feature.non_nullable)
? NullabilitySuffix.none
: NullabilitySuffix.star;
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: nullabilitySuffix,
);
}
}