blob: 38730ce4cd0adc2a4b1954a0e1c79abe9d83de77 [file] [log] [blame]
// Copyright (c) 2023, 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/services/completion/dart/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
import 'package:analysis_server_plugin/src/utilities/selection.dart';
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/utilities/completion_matcher.dart';
/// The information used to compute the suggestions for a completion request.
class CompletionState {
/// The completion request being processed.
final DartCompletionRequest request;
/// The selection at the time completion was requested.
///
/// The selection is required to have a length of zero.
final Selection selection;
/// The budget controlling how much time can be spent computing completion
/// suggestions.
final CompletionBudget budget;
/// The matcher used to compute the score of a completion suggestion.
final CompletionMatcher matcher;
/// Initialize a newly created completion state.
CompletionState(this.request, this.selection, this.budget, this.matcher)
: assert(selection.length == 0);
/// The type of value required by the context in which completion was
/// requested.
DartType? get contextType => request.contextType;
/// The [ClassMember] that encloses the completion location, or `null` if the
/// completion location isn't in a class member.
ClassMember? get enclosingMember {
return selection.coveringNode.thisOrAncestorOfType<ClassMember>();
}
/// Indicates if types should be specified whenever possible.
bool get includeTypes =>
request.fileState.analysisOptions.codeStyleOptions.specifyTypes;
/// The indentation for the completion text.
String get indent => getRequestLineIndent(request);
/// Whether the completion location is inside an instance member, and hence
/// whether there is a binding for `this`.
bool get inInstanceScope {
var member = enclosingMember;
return member != null && !member.isStatic;
}
/// The element of the library containing the completion location.
LibraryElement2 get libraryElement => request.libraryElement2;
/// The type of quotes preferred for [String]s as specified in [CodeStyleOptions].
String get preferredQuoteForStrings =>
request
.fileState
.analysisOptions
.codeStyleOptions
.preferredQuoteForStrings;
/// The type of `this` at the completion location, or `null` if the completion
/// location doesn't allow `this` to be used.
DartType? get thisType {
AstNode? node = selection.coveringNode;
while (node != null) {
switch (node) {
case ClassDeclaration():
var element = node.declaredFragment?.element;
if (element != null) {
return element.thisType;
}
case EnumDeclaration():
var element = node.declaredFragment?.element;
if (element != null) {
return element.thisType;
}
case ExtensionDeclaration():
return node.onClause?.extendedType.type;
case MixinDeclaration():
var element = node.declaredFragment?.element;
if (element != null) {
return element.thisType;
}
}
node = node.parent;
}
return null;
}
/// Whether the given `feature` is enabled in the library containing the
/// selection.
bool isFeatureEnabled(Feature feature) {
return libraryElement.featureSet.isEnabled(feature);
}
}
// TODO(brianwilkerson): Move to 'package:analysis_server/src/utilities/extensions/ast.dart'
extension on ClassMember {
/// Whether this member is a static member.
bool get isStatic {
var self = this;
if (self is MethodDeclaration) {
return self.isStatic;
} else if (self is FieldDeclaration) {
return self.isStatic;
}
return false;
}
}