blob: 862fe5fe73128c86e59480bbc71a0e751dfc750d [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/utilities/selection.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/type.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;
/// Initialize a newly created completion state.
CompletionState(this.request, this.selection) : assert(selection.length == 0);
/// Returns the type of value required by the context in which completion was
/// requested.
DartType? get contextType => request.contextType;
/// Return 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>();
}
/// Return `true` if the completion location is inside an instance member, and
/// hence there is a binding for `this`.
bool get inInstanceScope {
var member = enclosingMember;
return member != null && !member.isStatic;
}
/// Returns the element of the library containing the completion location.
LibraryElement get libraryElement => request.libraryElement;
/// Return 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.declaredElement;
if (element != null) {
return element.thisType;
}
case EnumDeclaration():
var element = node.declaredElement;
if (element != null) {
return element.thisType;
}
case ExtensionDeclaration():
return node.extendedType.type;
case MixinDeclaration():
var element = node.declaredElement;
if (element != null) {
return element.thisType;
}
}
node = node.parent;
}
return null;
}
/// Return `true` if 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 {
/// Return `true` if 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;
}
}