Apply LiftReturnOrAssignmentInspection on idea
This commit is contained in:
@@ -56,7 +56,8 @@ sealed class CallType<TReceiver : KtElement?>(val descriptorKindFilter: Descript
|
|||||||
|
|
||||||
object SAFE : CallType<KtExpression>(DescriptorKindFilter.ALL)
|
object SAFE : CallType<KtExpression>(DescriptorKindFilter.ALL)
|
||||||
|
|
||||||
object SUPER_MEMBERS : CallType<KtSuperExpression>(DescriptorKindFilter.CALLABLES exclude DescriptorKindExclude.Extensions exclude AbstractMembersExclude)
|
object SUPER_MEMBERS :
|
||||||
|
CallType<KtSuperExpression>(DescriptorKindFilter.CALLABLES exclude DescriptorKindExclude.Extensions exclude AbstractMembersExclude)
|
||||||
|
|
||||||
object INFIX : CallType<KtExpression>(DescriptorKindFilter.FUNCTIONS exclude NonInfixExclude)
|
object INFIX : CallType<KtExpression>(DescriptorKindFilter.FUNCTIONS exclude NonInfixExclude)
|
||||||
|
|
||||||
@@ -68,15 +69,17 @@ sealed class CallType<TReceiver : KtElement?>(val descriptorKindFilter: Descript
|
|||||||
|
|
||||||
object PACKAGE_DIRECTIVE : CallType<KtExpression?>(DescriptorKindFilter.PACKAGES)
|
object PACKAGE_DIRECTIVE : CallType<KtExpression?>(DescriptorKindFilter.PACKAGES)
|
||||||
|
|
||||||
object TYPE : CallType<KtExpression?>(DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude DescriptorKindExclude.EnumEntry)
|
object TYPE :
|
||||||
|
CallType<KtExpression?>(DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude DescriptorKindExclude.EnumEntry)
|
||||||
|
|
||||||
object DELEGATE : CallType<KtExpression?>(DescriptorKindFilter.FUNCTIONS exclude NonOperatorExclude)
|
object DELEGATE : CallType<KtExpression?>(DescriptorKindFilter.FUNCTIONS exclude NonOperatorExclude)
|
||||||
|
|
||||||
object ANNOTATION : CallType<KtExpression?>(DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude NonAnnotationClassifierExclude)
|
object ANNOTATION :
|
||||||
|
CallType<KtExpression?>(DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude NonAnnotationClassifierExclude)
|
||||||
|
|
||||||
private object NonInfixExclude : DescriptorKindExclude() {
|
private object NonInfixExclude : DescriptorKindExclude() {
|
||||||
override fun excludes(descriptor: DeclarationDescriptor) =
|
override fun excludes(descriptor: DeclarationDescriptor) =
|
||||||
!(descriptor is SimpleFunctionDescriptor && descriptor.isInfix)
|
!(descriptor is SimpleFunctionDescriptor && descriptor.isInfix)
|
||||||
|
|
||||||
override val fullyExcludedDescriptorKinds: Int
|
override val fullyExcludedDescriptorKinds: Int
|
||||||
get() = 0
|
get() = 0
|
||||||
@@ -84,15 +87,15 @@ sealed class CallType<TReceiver : KtElement?>(val descriptorKindFilter: Descript
|
|||||||
|
|
||||||
private object NonOperatorExclude : DescriptorKindExclude() {
|
private object NonOperatorExclude : DescriptorKindExclude() {
|
||||||
override fun excludes(descriptor: DeclarationDescriptor) =
|
override fun excludes(descriptor: DeclarationDescriptor) =
|
||||||
!(descriptor is SimpleFunctionDescriptor && descriptor.isOperator)
|
!(descriptor is SimpleFunctionDescriptor && descriptor.isOperator)
|
||||||
|
|
||||||
override val fullyExcludedDescriptorKinds: Int
|
override val fullyExcludedDescriptorKinds: Int
|
||||||
get() = 0
|
get() = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private object CallableReferenceExclude : DescriptorKindExclude() {
|
private object CallableReferenceExclude : DescriptorKindExclude() {
|
||||||
override fun excludes(descriptor: DeclarationDescriptor) /* currently not supported for locals and synthetic */
|
override fun excludes(descriptor: DeclarationDescriptor) /* currently not supported for locals and synthetic */ =
|
||||||
= descriptor !is CallableMemberDescriptor || descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
|
descriptor !is CallableMemberDescriptor || descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||||
|
|
||||||
override val fullyExcludedDescriptorKinds: Int
|
override val fullyExcludedDescriptorKinds: Int
|
||||||
get() = 0
|
get() = 0
|
||||||
@@ -108,8 +111,8 @@ sealed class CallType<TReceiver : KtElement?>(val descriptorKindFilter: Descript
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object AbstractMembersExclude : DescriptorKindExclude() {
|
private object AbstractMembersExclude : DescriptorKindExclude() {
|
||||||
override fun excludes(descriptor: DeclarationDescriptor)
|
override fun excludes(descriptor: DeclarationDescriptor) =
|
||||||
= descriptor is CallableMemberDescriptor && descriptor.modality == Modality.ABSTRACT
|
descriptor is CallableMemberDescriptor && descriptor.modality == Modality.ABSTRACT
|
||||||
|
|
||||||
override val fullyExcludedDescriptorKinds: Int
|
override val fullyExcludedDescriptorKinds: Int
|
||||||
get() = 0
|
get() = 0
|
||||||
@@ -117,19 +120,27 @@ sealed class CallType<TReceiver : KtElement?>(val descriptorKindFilter: Descript
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallType<TReceiver>>(
|
sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallType<TReceiver>>(
|
||||||
val callType: TCallType,
|
val callType: TCallType,
|
||||||
val receiver: TReceiver
|
val receiver: TReceiver
|
||||||
) {
|
) {
|
||||||
object UNKNOWN : CallTypeAndReceiver<Nothing?, CallType.UNKNOWN>(CallType.UNKNOWN, null)
|
object UNKNOWN : CallTypeAndReceiver<Nothing?, CallType.UNKNOWN>(CallType.UNKNOWN, null)
|
||||||
object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>(CallType.DEFAULT, null)
|
object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>(CallType.DEFAULT, null)
|
||||||
class DOT(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.DOT>(CallType.DOT, receiver)
|
class DOT(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.DOT>(CallType.DOT, receiver)
|
||||||
class SAFE(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.SAFE>(CallType.SAFE, receiver)
|
class SAFE(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.SAFE>(CallType.SAFE, receiver)
|
||||||
class SUPER_MEMBERS(receiver: KtSuperExpression) : CallTypeAndReceiver<KtSuperExpression, CallType.SUPER_MEMBERS>(CallType.SUPER_MEMBERS, receiver)
|
class SUPER_MEMBERS(receiver: KtSuperExpression) :
|
||||||
|
CallTypeAndReceiver<KtSuperExpression, CallType.SUPER_MEMBERS>(CallType.SUPER_MEMBERS, receiver)
|
||||||
|
|
||||||
class INFIX(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.INFIX>(CallType.INFIX, receiver)
|
class INFIX(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.INFIX>(CallType.INFIX, receiver)
|
||||||
class OPERATOR(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.OPERATOR>(CallType.OPERATOR, receiver)
|
class OPERATOR(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.OPERATOR>(CallType.OPERATOR, receiver)
|
||||||
class CALLABLE_REFERENCE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.CALLABLE_REFERENCE>(CallType.CALLABLE_REFERENCE, receiver)
|
class CALLABLE_REFERENCE(receiver: KtExpression?) :
|
||||||
class IMPORT_DIRECTIVE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.IMPORT_DIRECTIVE>(CallType.IMPORT_DIRECTIVE, receiver)
|
CallTypeAndReceiver<KtExpression?, CallType.CALLABLE_REFERENCE>(CallType.CALLABLE_REFERENCE, receiver)
|
||||||
class PACKAGE_DIRECTIVE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.PACKAGE_DIRECTIVE>(CallType.PACKAGE_DIRECTIVE, receiver)
|
|
||||||
|
class IMPORT_DIRECTIVE(receiver: KtExpression?) :
|
||||||
|
CallTypeAndReceiver<KtExpression?, CallType.IMPORT_DIRECTIVE>(CallType.IMPORT_DIRECTIVE, receiver)
|
||||||
|
|
||||||
|
class PACKAGE_DIRECTIVE(receiver: KtExpression?) :
|
||||||
|
CallTypeAndReceiver<KtExpression?, CallType.PACKAGE_DIRECTIVE>(CallType.PACKAGE_DIRECTIVE, receiver)
|
||||||
|
|
||||||
class TYPE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.TYPE>(CallType.TYPE, receiver)
|
class TYPE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.TYPE>(CallType.TYPE, receiver)
|
||||||
class DELEGATE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.DELEGATE>(CallType.DELEGATE, receiver)
|
class DELEGATE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.DELEGATE>(CallType.DELEGATE, receiver)
|
||||||
class ANNOTATION(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.ANNOTATION>(CallType.ANNOTATION, receiver)
|
class ANNOTATION(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.ANNOTATION>(CallType.ANNOTATION, receiver)
|
||||||
@@ -138,26 +149,26 @@ sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallTyp
|
|||||||
fun detect(expression: KtSimpleNameExpression): CallTypeAndReceiver<*, *> {
|
fun detect(expression: KtSimpleNameExpression): CallTypeAndReceiver<*, *> {
|
||||||
val parent = expression.parent
|
val parent = expression.parent
|
||||||
if (parent is KtCallableReferenceExpression && expression == parent.callableReference) {
|
if (parent is KtCallableReferenceExpression && expression == parent.callableReference) {
|
||||||
return CallTypeAndReceiver.CALLABLE_REFERENCE(parent.receiverExpression)
|
return CALLABLE_REFERENCE(parent.receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
val receiverExpression = expression.getReceiverExpression()
|
val receiverExpression = expression.getReceiverExpression()
|
||||||
|
|
||||||
if (expression.isImportDirectiveExpression()) {
|
if (expression.isImportDirectiveExpression()) {
|
||||||
return CallTypeAndReceiver.IMPORT_DIRECTIVE(receiverExpression)
|
return IMPORT_DIRECTIVE(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression.isPackageDirectiveExpression()) {
|
if (expression.isPackageDirectiveExpression()) {
|
||||||
return CallTypeAndReceiver.PACKAGE_DIRECTIVE(receiverExpression)
|
return PACKAGE_DIRECTIVE(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent is KtUserType) {
|
if (parent is KtUserType) {
|
||||||
val constructorCallee = (parent.parent as? KtTypeReference)?.parent as? KtConstructorCalleeExpression
|
val constructorCallee = (parent.parent as? KtTypeReference)?.parent as? KtConstructorCalleeExpression
|
||||||
if (constructorCallee != null && constructorCallee.parent is KtAnnotationEntry) {
|
if (constructorCallee != null && constructorCallee.parent is KtAnnotationEntry) {
|
||||||
return CallTypeAndReceiver.ANNOTATION(receiverExpression)
|
return ANNOTATION(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallTypeAndReceiver.TYPE(receiverExpression)
|
return TYPE(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (expression) {
|
when (expression) {
|
||||||
@@ -168,12 +179,12 @@ sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallTyp
|
|||||||
return when (parent) {
|
return when (parent) {
|
||||||
is KtBinaryExpression -> {
|
is KtBinaryExpression -> {
|
||||||
if (parent.operationToken == KtTokens.IDENTIFIER)
|
if (parent.operationToken == KtTokens.IDENTIFIER)
|
||||||
CallTypeAndReceiver.INFIX(receiverExpression)
|
INFIX(receiverExpression)
|
||||||
else
|
else
|
||||||
CallTypeAndReceiver.OPERATOR(receiverExpression)
|
OPERATOR(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtUnaryExpression -> CallTypeAndReceiver.OPERATOR(receiverExpression)
|
is KtUnaryExpression -> OPERATOR(receiverExpression)
|
||||||
|
|
||||||
else -> error("Unknown parent for JetOperationReferenceExpression: $parent with text '${parent.text}'")
|
else -> error("Unknown parent for JetOperationReferenceExpression: $parent with text '${parent.text}'")
|
||||||
}
|
}
|
||||||
@@ -181,26 +192,26 @@ sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallTyp
|
|||||||
|
|
||||||
is KtNameReferenceExpression -> {
|
is KtNameReferenceExpression -> {
|
||||||
if (receiverExpression == null) {
|
if (receiverExpression == null) {
|
||||||
return CallTypeAndReceiver.DEFAULT
|
return DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receiverExpression is KtSuperExpression) {
|
if (receiverExpression is KtSuperExpression) {
|
||||||
return CallTypeAndReceiver.SUPER_MEMBERS(receiverExpression)
|
return SUPER_MEMBERS(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (parent) {
|
return when (parent) {
|
||||||
is KtCallExpression -> {
|
is KtCallExpression -> {
|
||||||
if ((parent.parent as KtQualifiedExpression).operationSign == KtTokens.SAFE_ACCESS)
|
if ((parent.parent as KtQualifiedExpression).operationSign == KtTokens.SAFE_ACCESS)
|
||||||
CallTypeAndReceiver.SAFE(receiverExpression)
|
SAFE(receiverExpression)
|
||||||
else
|
else
|
||||||
CallTypeAndReceiver.DOT(receiverExpression)
|
DOT(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtQualifiedExpression -> {
|
is KtQualifiedExpression -> {
|
||||||
if (parent.operationSign == KtTokens.SAFE_ACCESS)
|
if (parent.operationSign == KtTokens.SAFE_ACCESS)
|
||||||
CallTypeAndReceiver.SAFE(receiverExpression)
|
SAFE(receiverExpression)
|
||||||
else
|
else
|
||||||
CallTypeAndReceiver.DOT(receiverExpression)
|
DOT(receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> error("Unknown parent for JetNameReferenceExpression with receiver: $parent")
|
else -> error("Unknown parent for JetNameReferenceExpression with receiver: $parent")
|
||||||
@@ -226,22 +237,22 @@ data class ReceiverType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun CallTypeAndReceiver<*, *>.receiverTypes(
|
fun CallTypeAndReceiver<*, *>.receiverTypes(
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
contextElement: PsiElement,
|
contextElement: PsiElement,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
moduleDescriptor: ModuleDescriptor,
|
||||||
resolutionFacade: ResolutionFacade,
|
resolutionFacade: ResolutionFacade,
|
||||||
stableSmartCastsOnly: Boolean
|
stableSmartCastsOnly: Boolean
|
||||||
): Collection<KotlinType>? {
|
): Collection<KotlinType>? {
|
||||||
return receiverTypesWithIndex(bindingContext, contextElement, moduleDescriptor, resolutionFacade, stableSmartCastsOnly)?.map { it.type }
|
return receiverTypesWithIndex(bindingContext, contextElement, moduleDescriptor, resolutionFacade, stableSmartCastsOnly)?.map { it.type }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
contextElement: PsiElement,
|
contextElement: PsiElement,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
moduleDescriptor: ModuleDescriptor,
|
||||||
resolutionFacade: ResolutionFacade,
|
resolutionFacade: ResolutionFacade,
|
||||||
stableSmartCastsOnly: Boolean,
|
stableSmartCastsOnly: Boolean,
|
||||||
withImplicitReceiversWhenExplicitPresent: Boolean = false
|
withImplicitReceiversWhenExplicitPresent: Boolean = false
|
||||||
): Collection<ReceiverType>? {
|
): Collection<ReceiverType>? {
|
||||||
val languageVersionSettings = resolutionFacade.frontendService<LanguageVersionSettings>()
|
val languageVersionSettings = resolutionFacade.frontendService<LanguageVersionSettings>()
|
||||||
|
|
||||||
@@ -249,20 +260,19 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
|||||||
when (this) {
|
when (this) {
|
||||||
is CallTypeAndReceiver.CALLABLE_REFERENCE -> {
|
is CallTypeAndReceiver.CALLABLE_REFERENCE -> {
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
val lhs = bindingContext[BindingContext.DOUBLE_COLON_LHS, receiver] ?: return emptyList()
|
return when (val lhs = bindingContext[BindingContext.DOUBLE_COLON_LHS, receiver] ?: return emptyList()) {
|
||||||
when (lhs) {
|
is DoubleColonLHS.Type -> listOf(ReceiverType(lhs.type, 0))
|
||||||
is DoubleColonLHS.Type -> return listOf(ReceiverType(lhs.type, 0))
|
|
||||||
|
|
||||||
is DoubleColonLHS.Expression -> {
|
is DoubleColonLHS.Expression -> {
|
||||||
val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext)
|
val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext)
|
||||||
return receiverValueTypes(receiverValue, lhs.dataFlowInfo, bindingContext,
|
receiverValueTypes(
|
||||||
moduleDescriptor, stableSmartCastsOnly, languageVersionSettings,
|
receiverValue, lhs.dataFlowInfo, bindingContext,
|
||||||
resolutionFacade.frontendService<DataFlowValueFactory>())
|
moduleDescriptor, stableSmartCastsOnly, languageVersionSettings,
|
||||||
.map { ReceiverType(it, 0) }
|
resolutionFacade.frontendService()
|
||||||
|
).map { ReceiverType(it, 0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,10 +289,10 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
|||||||
val qualifier = receiver.superTypeQualifier
|
val qualifier = receiver.superTypeQualifier
|
||||||
return if (qualifier != null) {
|
return if (qualifier != null) {
|
||||||
listOfNotNull(bindingContext.getType(receiver)).map { ReceiverType(it, 0) }
|
listOfNotNull(bindingContext.getType(receiver)).map { ReceiverType(it, 0) }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
val classDescriptor = resolutionScope.ownerDescriptor.parentsWithSelf.firstIsInstanceOrNull<ClassDescriptor>() ?: return emptyList()
|
val classDescriptor =
|
||||||
|
resolutionScope.ownerDescriptor.parentsWithSelf.firstIsInstanceOrNull<ClassDescriptor>() ?: return emptyList()
|
||||||
classDescriptor.typeConstructor.supertypesWithAny().map { ReceiverType(it, 0) }
|
classDescriptor.typeConstructor.supertypesWithAny().map { ReceiverType(it, 0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,10 +309,13 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
|||||||
|
|
||||||
val expressionReceiver = receiverExpression?.let {
|
val expressionReceiver = receiverExpression?.let {
|
||||||
val receiverType =
|
val receiverType =
|
||||||
bindingContext.getType(receiverExpression) ?:
|
bindingContext.getType(receiverExpression) ?: (bindingContext.get(
|
||||||
(bindingContext.get(BindingContext.QUALIFIER, receiverExpression) as? ClassQualifier)?.descriptor?.classValueType ?:
|
BindingContext.QUALIFIER,
|
||||||
(bindingContext.get(BindingContext.QUALIFIER, receiverExpression) as? TypeAliasQualifier)?.classDescriptor?.classValueType ?:
|
receiverExpression
|
||||||
return emptyList()
|
) as? ClassQualifier)?.descriptor?.classValueType ?: (bindingContext.get(
|
||||||
|
BindingContext.QUALIFIER,
|
||||||
|
receiverExpression
|
||||||
|
) as? TypeAliasQualifier)?.classDescriptor?.classValueType ?: return emptyList()
|
||||||
ExpressionReceiver.create(receiverExpression, receiverType, bindingContext)
|
ExpressionReceiver.create(receiverExpression, receiverType, bindingContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +332,7 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
|||||||
fun addReceiverType(receiverValue: ReceiverValue, implicit: Boolean) {
|
fun addReceiverType(receiverValue: ReceiverValue, implicit: Boolean) {
|
||||||
val types = receiverValueTypes(
|
val types = receiverValueTypes(
|
||||||
receiverValue, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly, languageVersionSettings,
|
receiverValue, dataFlowInfo, bindingContext, moduleDescriptor, stableSmartCastsOnly, languageVersionSettings,
|
||||||
resolutionFacade.frontendService<DataFlowValueFactory>()
|
resolutionFacade.frontendService()
|
||||||
)
|
)
|
||||||
|
|
||||||
types.mapTo(result) { type -> ReceiverType(type, receiverIndex, receiverValue.takeIf { implicit }) }
|
types.mapTo(result) { type -> ReceiverType(type, receiverIndex, receiverValue.takeIf { implicit }) }
|
||||||
@@ -336,26 +349,25 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun receiverValueTypes(
|
private fun receiverValueTypes(
|
||||||
receiverValue: ReceiverValue,
|
receiverValue: ReceiverValue,
|
||||||
dataFlowInfo: DataFlowInfo,
|
dataFlowInfo: DataFlowInfo,
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
moduleDescriptor: ModuleDescriptor,
|
||||||
stableSmartCastsOnly: Boolean,
|
stableSmartCastsOnly: Boolean,
|
||||||
languageVersionSettings: LanguageVersionSettings,
|
languageVersionSettings: LanguageVersionSettings,
|
||||||
dataFlowValueFactory: DataFlowValueFactory
|
dataFlowValueFactory: DataFlowValueFactory
|
||||||
): List<KotlinType> {
|
): List<KotlinType> {
|
||||||
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor)
|
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor)
|
||||||
return if (dataFlowValue.isStable || !stableSmartCastsOnly) { // we don't include smart cast receiver types for "unstable" receiver value to mark members grayed
|
return if (dataFlowValue.isStable || !stableSmartCastsOnly) { // we don't include smart cast receiver types for "unstable" receiver value to mark members grayed
|
||||||
SmartCastManager().getSmartCastVariantsWithLessSpecificExcluded(
|
SmartCastManager().getSmartCastVariantsWithLessSpecificExcluded(
|
||||||
receiverValue,
|
receiverValue,
|
||||||
bindingContext,
|
bindingContext,
|
||||||
moduleDescriptor,
|
moduleDescriptor,
|
||||||
dataFlowInfo,
|
dataFlowInfo,
|
||||||
languageVersionSettings,
|
languageVersionSettings,
|
||||||
dataFlowValueFactory
|
dataFlowValueFactory
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
listOf(receiverValue.type)
|
listOf(receiverValue.type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-82
@@ -39,11 +39,11 @@ import org.jetbrains.kotlin.idea.imports.importableFqName
|
|||||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.util.*
|
import org.jetbrains.kotlin.idea.util.*
|
||||||
|
import org.jetbrains.kotlin.platform.isCommon
|
||||||
|
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.platform.isCommon
|
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
@@ -53,28 +53,28 @@ import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CompletionSessionConfiguration(
|
class CompletionSessionConfiguration(
|
||||||
val useBetterPrefixMatcherForNonImportedClasses: Boolean,
|
val useBetterPrefixMatcherForNonImportedClasses: Boolean,
|
||||||
val nonAccessibleDeclarations: Boolean,
|
val nonAccessibleDeclarations: Boolean,
|
||||||
val javaGettersAndSetters: Boolean,
|
val javaGettersAndSetters: Boolean,
|
||||||
val javaClassesNotToBeUsed: Boolean,
|
val javaClassesNotToBeUsed: Boolean,
|
||||||
val staticMembers: Boolean,
|
val staticMembers: Boolean,
|
||||||
val dataClassComponentFunctions: Boolean
|
val dataClassComponentFunctions: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
fun CompletionSessionConfiguration(parameters: CompletionParameters) = CompletionSessionConfiguration(
|
fun CompletionSessionConfiguration(parameters: CompletionParameters) = CompletionSessionConfiguration(
|
||||||
useBetterPrefixMatcherForNonImportedClasses = parameters.invocationCount < 2,
|
useBetterPrefixMatcherForNonImportedClasses = parameters.invocationCount < 2,
|
||||||
nonAccessibleDeclarations = parameters.invocationCount >= 2,
|
nonAccessibleDeclarations = parameters.invocationCount >= 2,
|
||||||
javaGettersAndSetters = parameters.invocationCount >= 2,
|
javaGettersAndSetters = parameters.invocationCount >= 2,
|
||||||
javaClassesNotToBeUsed = parameters.invocationCount >= 2,
|
javaClassesNotToBeUsed = parameters.invocationCount >= 2,
|
||||||
staticMembers = parameters.invocationCount >= 2,
|
staticMembers = parameters.invocationCount >= 2,
|
||||||
dataClassComponentFunctions = parameters.invocationCount >= 2
|
dataClassComponentFunctions = parameters.invocationCount >= 2
|
||||||
)
|
)
|
||||||
|
|
||||||
abstract class CompletionSession(
|
abstract class CompletionSession(
|
||||||
protected val configuration: CompletionSessionConfiguration,
|
protected val configuration: CompletionSessionConfiguration,
|
||||||
protected val parameters: CompletionParameters,
|
protected val parameters: CompletionParameters,
|
||||||
protected val toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
protected val toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||||
resultSet: CompletionResultSet
|
resultSet: CompletionResultSet
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
CompletionBenchmarkSink.instance.onCompletionStarted(this)
|
CompletionBenchmarkSink.instance.onCompletionStarted(this)
|
||||||
@@ -97,13 +97,11 @@ abstract class CompletionSession(
|
|||||||
if (reference.expression is KtLabelReferenceExpression) {
|
if (reference.expression is KtLabelReferenceExpression) {
|
||||||
this.nameExpression = null
|
this.nameExpression = null
|
||||||
this.expression = reference.expression.parent.parent as? KtExpressionWithLabel
|
this.expression = reference.expression.parent.parent as? KtExpressionWithLabel
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.nameExpression = reference.expression
|
this.nameExpression = reference.expression
|
||||||
this.expression = nameExpression
|
this.expression = nameExpression
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.nameExpression = null
|
this.nameExpression = null
|
||||||
this.expression = null
|
this.expression = null
|
||||||
}
|
}
|
||||||
@@ -116,29 +114,36 @@ abstract class CompletionSession(
|
|||||||
private val kotlinIdentifierPartPattern = StandardPatterns.character().javaIdentifierPart().andNot(singleCharPattern('$'))
|
private val kotlinIdentifierPartPattern = StandardPatterns.character().javaIdentifierPart().andNot(singleCharPattern('$'))
|
||||||
|
|
||||||
protected val prefix = CompletionUtil.findIdentifierPrefix(
|
protected val prefix = CompletionUtil.findIdentifierPrefix(
|
||||||
parameters.position.containingFile,
|
parameters.position.containingFile,
|
||||||
parameters.offset,
|
parameters.offset,
|
||||||
kotlinIdentifierPartPattern or singleCharPattern('@'),
|
kotlinIdentifierPartPattern or singleCharPattern('@'),
|
||||||
kotlinIdentifierStartPattern)!!
|
kotlinIdentifierStartPattern
|
||||||
|
)!!
|
||||||
|
|
||||||
protected val prefixMatcher = CamelHumpMatcher(prefix)
|
protected val prefixMatcher = CamelHumpMatcher(prefix)
|
||||||
|
|
||||||
protected val descriptorNameFilter: (String) -> Boolean = prefixMatcher.asStringNameFilter()
|
protected val descriptorNameFilter: (String) -> Boolean = prefixMatcher.asStringNameFilter()
|
||||||
|
|
||||||
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = configuration.nonAccessibleDeclarations) }
|
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean =
|
||||||
protected val isVisibleFilterCheckAlways: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = false) }
|
{ isVisibleDescriptor(it, completeNonAccessible = configuration.nonAccessibleDeclarations) }
|
||||||
|
protected val isVisibleFilterCheckAlways: (DeclarationDescriptor) -> Boolean =
|
||||||
|
{ isVisibleDescriptor(it, completeNonAccessible = false) }
|
||||||
|
|
||||||
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext,
|
protected val referenceVariantsHelper = ReferenceVariantsHelper(
|
||||||
resolutionFacade,
|
bindingContext,
|
||||||
moduleDescriptor,
|
resolutionFacade,
|
||||||
isVisibleFilter,
|
moduleDescriptor,
|
||||||
NotPropertiesService.getNotProperties(position))
|
isVisibleFilter,
|
||||||
|
NotPropertiesService.getNotProperties(position)
|
||||||
|
)
|
||||||
|
|
||||||
protected val callTypeAndReceiver = if (nameExpression == null) CallTypeAndReceiver.UNKNOWN else CallTypeAndReceiver.detect(nameExpression)
|
protected val callTypeAndReceiver =
|
||||||
|
if (nameExpression == null) CallTypeAndReceiver.UNKNOWN else CallTypeAndReceiver.detect(nameExpression)
|
||||||
protected val receiverTypes = nameExpression?.let { detectReceiverTypes(bindingContext, nameExpression, callTypeAndReceiver) }
|
protected val receiverTypes = nameExpression?.let { detectReceiverTypes(bindingContext, nameExpression, callTypeAndReceiver) }
|
||||||
|
|
||||||
|
|
||||||
protected val basicLookupElementFactory = BasicLookupElementFactory(project, InsertHandlerProvider(callTypeAndReceiver.callType) { expectedInfos })
|
protected val basicLookupElementFactory =
|
||||||
|
BasicLookupElementFactory(project, InsertHandlerProvider(callTypeAndReceiver.callType) { expectedInfos })
|
||||||
|
|
||||||
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
|
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
|
||||||
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
|
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
@@ -155,12 +160,14 @@ abstract class CompletionSession(
|
|||||||
|
|
||||||
protected fun indicesHelper(mayIncludeInaccessible: Boolean): KotlinIndicesHelper {
|
protected fun indicesHelper(mayIncludeInaccessible: Boolean): KotlinIndicesHelper {
|
||||||
val filter = if (mayIncludeInaccessible) isVisibleFilter else isVisibleFilterCheckAlways
|
val filter = if (mayIncludeInaccessible) isVisibleFilter else isVisibleFilterCheckAlways
|
||||||
return KotlinIndicesHelper(resolutionFacade,
|
return KotlinIndicesHelper(
|
||||||
searchScope,
|
resolutionFacade,
|
||||||
filter,
|
searchScope,
|
||||||
filterOutPrivate = !mayIncludeInaccessible,
|
filter,
|
||||||
declarationTranslator = { toFromOriginalFileMapper.toSyntheticFile(it) },
|
filterOutPrivate = !mayIncludeInaccessible,
|
||||||
file = file)
|
declarationTranslator = { toFromOriginalFileMapper.toSyntheticFile(it) },
|
||||||
|
file = file
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isVisibleDescriptor(descriptor: DeclarationDescriptor, completeNonAccessible: Boolean): Boolean {
|
private fun isVisibleDescriptor(descriptor: DeclarationDescriptor, completeNonAccessible: Boolean): Boolean {
|
||||||
@@ -253,20 +260,22 @@ abstract class CompletionSession(
|
|||||||
protected open fun createSorter(): CompletionSorter {
|
protected open fun createSorter(): CompletionSorter {
|
||||||
var sorter = CompletionSorter.defaultSorter(parameters, prefixMatcher)!!
|
var sorter = CompletionSorter.defaultSorter(parameters, prefixMatcher)!!
|
||||||
|
|
||||||
sorter = sorter.weighBefore("stats", DeprecatedWeigher, PriorityWeigher, PreferGetSetMethodsToPropertyWeigher,
|
sorter = sorter.weighBefore(
|
||||||
NotImportedWeigher(importableFqNameClassifier),
|
"stats", DeprecatedWeigher, PriorityWeigher, PreferGetSetMethodsToPropertyWeigher,
|
||||||
NotImportedStaticMemberWeigher(importableFqNameClassifier),
|
NotImportedWeigher(importableFqNameClassifier),
|
||||||
KindWeigher, CallableWeigher)
|
NotImportedStaticMemberWeigher(importableFqNameClassifier),
|
||||||
|
KindWeigher, CallableWeigher
|
||||||
|
)
|
||||||
|
|
||||||
sorter = sorter.weighAfter("stats", VariableOrFunctionWeigher, ImportedWeigher(importableFqNameClassifier))
|
sorter = sorter.weighAfter("stats", VariableOrFunctionWeigher, ImportedWeigher(importableFqNameClassifier))
|
||||||
|
|
||||||
val preferContextElementsWeigher = PreferContextElementsWeigher(inDescriptor)
|
val preferContextElementsWeigher = PreferContextElementsWeigher(inDescriptor)
|
||||||
sorter = if (callTypeAndReceiver is CallTypeAndReceiver.SUPER_MEMBERS) { // for completion after "super." strictly prefer the current member
|
sorter =
|
||||||
sorter.weighBefore("kotlin.deprecated", preferContextElementsWeigher)
|
if (callTypeAndReceiver is CallTypeAndReceiver.SUPER_MEMBERS) { // for completion after "super." strictly prefer the current member
|
||||||
}
|
sorter.weighBefore("kotlin.deprecated", preferContextElementsWeigher)
|
||||||
else {
|
} else {
|
||||||
sorter.weighBefore("kotlin.proximity", preferContextElementsWeigher)
|
sorter.weighBefore("kotlin.proximity", preferContextElementsWeigher)
|
||||||
}
|
}
|
||||||
|
|
||||||
sorter = sorter.weighBefore("middleMatching", PreferMatchingItemWeigher)
|
sorter = sorter.weighBefore("middleMatching", PreferMatchingItemWeigher)
|
||||||
|
|
||||||
@@ -275,10 +284,10 @@ abstract class CompletionSession(
|
|||||||
|
|
||||||
sorter = sorter.weighAfter("kotlin.proximity", ByNameAlphabeticalWeigher, PreferLessParametersWeigher)
|
sorter = sorter.weighAfter("kotlin.proximity", ByNameAlphabeticalWeigher, PreferLessParametersWeigher)
|
||||||
|
|
||||||
if (expectedInfos.all { it.fuzzyType?.type?.isUnit() == true }) {
|
sorter = if (expectedInfos.all { it.fuzzyType?.type?.isUnit() == true }) {
|
||||||
sorter = sorter.weighBefore("prefix", PreferDslMembers)
|
sorter.weighBefore("prefix", PreferDslMembers)
|
||||||
} else {
|
} else {
|
||||||
sorter = sorter.weighAfter("kotlin.preferContextElements", PreferDslMembers)
|
sorter.weighAfter("kotlin.preferContextElements", PreferDslMembers)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sorter
|
return sorter
|
||||||
@@ -288,28 +297,29 @@ abstract class CompletionSession(
|
|||||||
if (expectedInfos.isEmpty()) return null
|
if (expectedInfos.isEmpty()) return null
|
||||||
|
|
||||||
var context = expectedInfos
|
var context = expectedInfos
|
||||||
.mapNotNull { it.fuzzyType?.type?.constructor?.declarationDescriptor?.importableFqName }
|
.mapNotNull { it.fuzzyType?.type?.constructor?.declarationDescriptor?.importableFqName }
|
||||||
.distinct()
|
.distinct()
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
?.let { "expectedType=$it" }
|
?.let { "expectedType=$it" }
|
||||||
|
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
context = expectedInfos
|
context = expectedInfos
|
||||||
.mapNotNull { it.expectedName }
|
.mapNotNull { it.expectedName }
|
||||||
.distinct()
|
.distinct()
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
?.let { "expectedName=$it" }
|
?.let { "expectedName=$it" }
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val referenceVariantsCollector = if (nameExpression != null) {
|
protected val referenceVariantsCollector = if (nameExpression != null) {
|
||||||
ReferenceVariantsCollector(referenceVariantsHelper, indicesHelper(true), prefixMatcher,
|
ReferenceVariantsCollector(
|
||||||
nameExpression, callTypeAndReceiver, resolutionFacade, bindingContext,
|
referenceVariantsHelper, indicesHelper(true), prefixMatcher,
|
||||||
importableFqNameClassifier, configuration)
|
nameExpression, callTypeAndReceiver, resolutionFacade, bindingContext,
|
||||||
}
|
importableFqNameClassifier, configuration
|
||||||
else {
|
)
|
||||||
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +329,9 @@ abstract class CompletionSession(
|
|||||||
|
|
||||||
protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? {
|
protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? {
|
||||||
val variants = referenceVariantsCollector?.allCollected ?: return null
|
val variants = referenceVariantsCollector?.allCollected ?: return null
|
||||||
val filter = { descriptor: DeclarationDescriptor -> descriptor is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(descriptor) }
|
val filter = { descriptor: DeclarationDescriptor ->
|
||||||
|
descriptor is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(descriptor)
|
||||||
|
}
|
||||||
return ReferenceVariants(variants.imported.filter(filter), variants.notImportedExtensions.filter(filter))
|
return ReferenceVariants(variants.imported.filter(filter), variants.notImportedExtensions.filter(filter))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,20 +348,21 @@ abstract class CompletionSession(
|
|||||||
|
|
||||||
val expressionReceiver = ExpressionReceiver.create(explicitReceiver, runtimeType, bindingContext)
|
val expressionReceiver = ExpressionReceiver.create(explicitReceiver, runtimeType, bindingContext)
|
||||||
val (variants, notImportedExtensions) = ReferenceVariantsCollector(
|
val (variants, notImportedExtensions) = ReferenceVariantsCollector(
|
||||||
referenceVariantsHelper, indicesHelper(true), prefixMatcher,
|
referenceVariantsHelper, indicesHelper(true), prefixMatcher,
|
||||||
nameExpression!!, callTypeAndReceiver, resolutionFacade, bindingContext,
|
nameExpression!!, callTypeAndReceiver, resolutionFacade, bindingContext,
|
||||||
importableFqNameClassifier, configuration, runtimeReceiver = expressionReceiver
|
importableFqNameClassifier, configuration, runtimeReceiver = expressionReceiver
|
||||||
).collectReferenceVariants(descriptorKindFilter!!)
|
).collectReferenceVariants(descriptorKindFilter!!)
|
||||||
val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported)
|
val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported)
|
||||||
val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions)
|
val filteredNotImportedExtensions =
|
||||||
|
filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions)
|
||||||
|
|
||||||
val runtimeVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions)
|
val runtimeVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions)
|
||||||
return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(ReceiverType(runtimeType, 0))))
|
return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(ReceiverType(runtimeType, 0))))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <TDescriptor : DeclarationDescriptor> filterVariantsForRuntimeReceiverType(
|
private fun <TDescriptor : DeclarationDescriptor> filterVariantsForRuntimeReceiverType(
|
||||||
runtimeVariants: Collection<TDescriptor>,
|
runtimeVariants: Collection<TDescriptor>,
|
||||||
baseVariants: Collection<TDescriptor>
|
baseVariants: Collection<TDescriptor>
|
||||||
): Collection<TDescriptor> {
|
): Collection<TDescriptor> {
|
||||||
val baseVariantsByName = baseVariants.groupBy { it.name }
|
val baseVariantsByName = baseVariants.groupBy { it.name }
|
||||||
val result = ArrayList<TDescriptor>()
|
val result = ArrayList<TDescriptor>()
|
||||||
@@ -371,12 +384,11 @@ abstract class CompletionSession(
|
|||||||
|
|
||||||
protected fun processTopLevelCallables(processor: (CallableDescriptor) -> Unit) {
|
protected fun processTopLevelCallables(processor: (CallableDescriptor) -> Unit) {
|
||||||
val shadowedFilter = ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression!!, callTypeAndReceiver)
|
val shadowedFilter = ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression!!, callTypeAndReceiver)
|
||||||
?.createNonImportedDeclarationsFilter<CallableDescriptor>(referenceVariantsCollector!!.allCollected.imported)
|
?.createNonImportedDeclarationsFilter<CallableDescriptor>(referenceVariantsCollector!!.allCollected.imported)
|
||||||
indicesHelper(true).processTopLevelCallables({ prefixMatcher.prefixMatches(it) }) {
|
indicesHelper(true).processTopLevelCallables({ prefixMatcher.prefixMatches(it) }) {
|
||||||
if (shadowedFilter != null) {
|
if (shadowedFilter != null) {
|
||||||
shadowedFilter(listOf(it)).singleOrNull()?.let(processor)
|
shadowedFilter(listOf(it)).singleOrNull()?.let(processor)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
processor(it)
|
processor(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,13 +407,17 @@ abstract class CompletionSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected open fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory {
|
protected open fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory {
|
||||||
return LookupElementFactory(basicLookupElementFactory, receiverTypes,
|
return LookupElementFactory(
|
||||||
callTypeAndReceiver.callType, inDescriptor, contextVariablesProvider)
|
basicLookupElementFactory, receiverTypes,
|
||||||
|
callTypeAndReceiver.callType, inDescriptor, contextVariablesProvider
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun detectReceiverTypes(bindingContext: BindingContext,
|
protected fun detectReceiverTypes(
|
||||||
nameExpression: KtSimpleNameExpression,
|
bindingContext: BindingContext,
|
||||||
callTypeAndReceiver: CallTypeAndReceiver<*, *>): Collection<ReceiverType>? {
|
nameExpression: KtSimpleNameExpression,
|
||||||
|
callTypeAndReceiver: CallTypeAndReceiver<*, *>
|
||||||
|
): Collection<ReceiverType>? {
|
||||||
var receiverTypes = callTypeAndReceiver.receiverTypesWithIndex(
|
var receiverTypes = callTypeAndReceiver.receiverTypesWithIndex(
|
||||||
bindingContext, nameExpression, moduleDescriptor, resolutionFacade,
|
bindingContext, nameExpression, moduleDescriptor, resolutionFacade,
|
||||||
stableSmartCastsOnly = true, /* we don't include smart cast receiver types for "unstable" receiver value to mark members grayed */
|
stableSmartCastsOnly = true, /* we don't include smart cast receiver types for "unstable" receiver value to mark members grayed */
|
||||||
|
|||||||
+40
-30
@@ -46,16 +46,23 @@ import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
|||||||
|
|
||||||
class KDocCompletionContributor : CompletionContributor() {
|
class KDocCompletionContributor : CompletionContributor() {
|
||||||
init {
|
init {
|
||||||
extend(CompletionType.BASIC, psiElement().inside(KDocName::class.java),
|
extend(
|
||||||
KDocNameCompletionProvider)
|
CompletionType.BASIC, psiElement().inside(KDocName::class.java),
|
||||||
|
KDocNameCompletionProvider
|
||||||
|
)
|
||||||
|
|
||||||
extend(CompletionType.BASIC,
|
extend(
|
||||||
psiElement().afterLeaf(
|
CompletionType.BASIC,
|
||||||
StandardPatterns.or(psiElement(KDocTokens.LEADING_ASTERISK), psiElement(KDocTokens.START))),
|
psiElement().afterLeaf(
|
||||||
KDocTagCompletionProvider)
|
StandardPatterns.or(psiElement(KDocTokens.LEADING_ASTERISK), psiElement(KDocTokens.START))
|
||||||
|
),
|
||||||
|
KDocTagCompletionProvider
|
||||||
|
)
|
||||||
|
|
||||||
extend(CompletionType.BASIC,
|
extend(
|
||||||
psiElement(KDocTokens.TAG_NAME), KDocTagCompletionProvider)
|
CompletionType.BASIC,
|
||||||
|
psiElement(KDocTokens.TAG_NAME), KDocTagCompletionProvider
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,9 +73,9 @@ object KDocNameCompletionProvider : CompletionProvider<CompletionParameters>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class KDocNameCompletionSession(
|
class KDocNameCompletionSession(
|
||||||
parameters: CompletionParameters,
|
parameters: CompletionParameters,
|
||||||
toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||||
resultSet: CompletionResultSet
|
resultSet: CompletionResultSet
|
||||||
) : CompletionSession(CompletionSessionConfiguration(parameters), parameters, toFromOriginalFileMapper, resultSet) {
|
) : CompletionSession(CompletionSessionConfiguration(parameters), parameters, toFromOriginalFileMapper, resultSet) {
|
||||||
|
|
||||||
override val descriptorKindFilter: DescriptorKindFilter? get() = null
|
override val descriptorKindFilter: DescriptorKindFilter? get() = null
|
||||||
@@ -81,39 +88,42 @@ class KDocNameCompletionSession(
|
|||||||
val declarationDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return
|
val declarationDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return
|
||||||
if (kdocLink.getTagIfSubject()?.knownTag == KDocKnownTag.PARAM) {
|
if (kdocLink.getTagIfSubject()?.knownTag == KDocKnownTag.PARAM) {
|
||||||
addParamCompletions(position, declarationDescriptor)
|
addParamCompletions(position, declarationDescriptor)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
addLinkCompletions(declarationDescriptor, kdocLink)
|
addLinkCompletions(declarationDescriptor, kdocLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun addParamCompletions(position: KDocName,
|
private fun addParamCompletions(
|
||||||
declarationDescriptor: DeclarationDescriptor) {
|
position: KDocName,
|
||||||
|
declarationDescriptor: DeclarationDescriptor
|
||||||
|
) {
|
||||||
val section = position.getContainingSection()
|
val section = position.getContainingSection()
|
||||||
val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet()
|
val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet()
|
||||||
getParamDescriptors(declarationDescriptor)
|
getParamDescriptors(declarationDescriptor)
|
||||||
.filter { it.name.asString() !in documentedParameters }
|
.filter { it.name.asString() !in documentedParameters }
|
||||||
.forEach {
|
.forEach {
|
||||||
collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true))
|
collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Collection<DeclarationDescriptor> {
|
private fun collectDescriptorsForLinkCompletion(
|
||||||
|
declarationDescriptor: DeclarationDescriptor,
|
||||||
|
kDocLink: KDocLink
|
||||||
|
): Collection<DeclarationDescriptor> {
|
||||||
val contextScope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor)
|
val contextScope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor)
|
||||||
|
|
||||||
val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1)
|
val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1)
|
||||||
val nameFilter = descriptorNameFilter.toNameFilter()
|
val nameFilter = descriptorNameFilter.toNameFilter()
|
||||||
if (qualifiedLink.isNotEmpty()) {
|
return if (qualifiedLink.isNotEmpty()) {
|
||||||
val parentDescriptors = resolveKDocLink(bindingContext, resolutionFacade, declarationDescriptor, kDocLink.getTagIfSubject(), qualifiedLink)
|
val parentDescriptors =
|
||||||
return parentDescriptors
|
resolveKDocLink(bindingContext, resolutionFacade, declarationDescriptor, kDocLink.getTagIfSubject(), qualifiedLink)
|
||||||
.flatMap {
|
parentDescriptors.flatMap {
|
||||||
val scope = getKDocLinkMemberScope(it, contextScope)
|
val scope = getKDocLinkMemberScope(it, contextScope)
|
||||||
scope.getContributedDescriptors(nameFilter = nameFilter)
|
scope.getContributedDescriptors(nameFilter = nameFilter)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
contextScope.collectDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter, changeNamesForAliased = true)
|
||||||
return contextScope.collectDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter, changeNamesForAliased = true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+132
-111
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLooku
|
|||||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -52,46 +51,49 @@ open class KeywordLookupObject
|
|||||||
|
|
||||||
object KeywordCompletion {
|
object KeywordCompletion {
|
||||||
private val ALL_KEYWORDS = (KEYWORDS.types + SOFT_KEYWORDS.types)
|
private val ALL_KEYWORDS = (KEYWORDS.types + SOFT_KEYWORDS.types)
|
||||||
.map { it as KtKeywordToken }
|
.map { it as KtKeywordToken }
|
||||||
|
|
||||||
private val KEYWORDS_TO_IGNORE_PREFIX = TokenSet.create(OVERRIDE_KEYWORD /* it's needed to complete overrides that should be work by member name too */)
|
private val KEYWORDS_TO_IGNORE_PREFIX =
|
||||||
|
TokenSet.create(OVERRIDE_KEYWORD /* it's needed to complete overrides that should be work by member name too */)
|
||||||
|
|
||||||
private val COMPOUND_KEYWORDS = mapOf<KtKeywordToken, KtKeywordToken>(
|
private val COMPOUND_KEYWORDS = mapOf<KtKeywordToken, KtKeywordToken>(
|
||||||
COMPANION_KEYWORD to OBJECT_KEYWORD,
|
COMPANION_KEYWORD to OBJECT_KEYWORD,
|
||||||
DATA_KEYWORD to CLASS_KEYWORD,
|
DATA_KEYWORD to CLASS_KEYWORD,
|
||||||
ENUM_KEYWORD to CLASS_KEYWORD,
|
ENUM_KEYWORD to CLASS_KEYWORD,
|
||||||
ANNOTATION_KEYWORD to CLASS_KEYWORD,
|
ANNOTATION_KEYWORD to CLASS_KEYWORD,
|
||||||
SEALED_KEYWORD to CLASS_KEYWORD,
|
SEALED_KEYWORD to CLASS_KEYWORD,
|
||||||
LATEINIT_KEYWORD to VAR_KEYWORD,
|
LATEINIT_KEYWORD to VAR_KEYWORD,
|
||||||
CONST_KEYWORD to VAL_KEYWORD,
|
CONST_KEYWORD to VAL_KEYWORD,
|
||||||
SUSPEND_KEYWORD to FUN_KEYWORD
|
SUSPEND_KEYWORD to FUN_KEYWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
private val KEYWORD_CONSTRUCTS = mapOf<KtKeywordToken, String>(
|
private val KEYWORD_CONSTRUCTS = mapOf<KtKeywordToken, String>(
|
||||||
IF_KEYWORD to "fun foo() { if (caret)",
|
IF_KEYWORD to "fun foo() { if (caret)",
|
||||||
WHILE_KEYWORD to "fun foo() { while(caret)",
|
WHILE_KEYWORD to "fun foo() { while(caret)",
|
||||||
FOR_KEYWORD to "fun foo() { for(caret)",
|
FOR_KEYWORD to "fun foo() { for(caret)",
|
||||||
TRY_KEYWORD to "fun foo() { try {\ncaret\n}",
|
TRY_KEYWORD to "fun foo() { try {\ncaret\n}",
|
||||||
CATCH_KEYWORD to "fun foo() { try {} catch (caret)",
|
CATCH_KEYWORD to "fun foo() { try {} catch (caret)",
|
||||||
FINALLY_KEYWORD to "fun foo() { try {\n}\nfinally{\ncaret\n}",
|
FINALLY_KEYWORD to "fun foo() { try {\n}\nfinally{\ncaret\n}",
|
||||||
DO_KEYWORD to "fun foo() { do {\ncaret\n}",
|
DO_KEYWORD to "fun foo() { do {\ncaret\n}",
|
||||||
INIT_KEYWORD to "class C { init {\ncaret\n}",
|
INIT_KEYWORD to "class C { init {\ncaret\n}",
|
||||||
CONSTRUCTOR_KEYWORD to "class C { constructor(caret)"
|
CONSTRUCTOR_KEYWORD to "class C { constructor(caret)"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val NO_SPACE_AFTER = listOf(THIS_KEYWORD,
|
private val NO_SPACE_AFTER = listOf(
|
||||||
SUPER_KEYWORD,
|
THIS_KEYWORD,
|
||||||
NULL_KEYWORD,
|
SUPER_KEYWORD,
|
||||||
TRUE_KEYWORD,
|
NULL_KEYWORD,
|
||||||
FALSE_KEYWORD,
|
TRUE_KEYWORD,
|
||||||
BREAK_KEYWORD,
|
FALSE_KEYWORD,
|
||||||
CONTINUE_KEYWORD,
|
BREAK_KEYWORD,
|
||||||
ELSE_KEYWORD,
|
CONTINUE_KEYWORD,
|
||||||
WHEN_KEYWORD,
|
ELSE_KEYWORD,
|
||||||
FILE_KEYWORD,
|
WHEN_KEYWORD,
|
||||||
DYNAMIC_KEYWORD,
|
FILE_KEYWORD,
|
||||||
GET_KEYWORD,
|
DYNAMIC_KEYWORD,
|
||||||
SET_KEYWORD).map { it.value} + "companion object"
|
GET_KEYWORD,
|
||||||
|
SET_KEYWORD
|
||||||
|
).map { it.value } + "companion object"
|
||||||
|
|
||||||
fun complete(position: PsiElement, prefix: String, isJvmModule: Boolean, consumer: (LookupElement) -> Unit) {
|
fun complete(position: PsiElement, prefix: String, isJvmModule: Boolean, consumer: (LookupElement) -> Unit) {
|
||||||
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
||||||
@@ -187,40 +189,40 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val GENERAL_FILTER = NotFilter(OrFilter(
|
private val GENERAL_FILTER = NotFilter(
|
||||||
|
OrFilter(
|
||||||
CommentFilter(),
|
CommentFilter(),
|
||||||
ParentFilter(ClassFilter(KtLiteralStringTemplateEntry::class.java)),
|
ParentFilter(ClassFilter(KtLiteralStringTemplateEntry::class.java)),
|
||||||
ParentFilter(ClassFilter(KtConstantExpression::class.java)),
|
ParentFilter(ClassFilter(KtConstantExpression::class.java)),
|
||||||
FileFilter(ClassFilter(KtTypeCodeFragment::class.java)),
|
FileFilter(ClassFilter(KtTypeCodeFragment::class.java)),
|
||||||
LeftNeighbour(TextFilter(".")),
|
LeftNeighbour(TextFilter(".")),
|
||||||
LeftNeighbour(TextFilter("?."))
|
LeftNeighbour(TextFilter("?."))
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private class CommentFilter() : ElementFilter {
|
private class CommentFilter() : ElementFilter {
|
||||||
override fun isAcceptable(element : Any?, context : PsiElement?)
|
override fun isAcceptable(element: Any?, context: PsiElement?) = (element is PsiElement) && KtPsiUtil.isInComment(element)
|
||||||
= (element is PsiElement) && KtPsiUtil.isInComment(element)
|
|
||||||
|
|
||||||
override fun isClassAcceptable(hintClass: Class<out Any?>)
|
override fun isClassAcceptable(hintClass: Class<out Any?>) = true
|
||||||
= true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParentFilter(filter : ElementFilter) : PositionElementFilter() {
|
private class ParentFilter(filter: ElementFilter) : PositionElementFilter() {
|
||||||
init {
|
init {
|
||||||
setFilter(filter)
|
setFilter(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isAcceptable(element : Any?, context : PsiElement?) : Boolean {
|
override fun isAcceptable(element: Any?, context: PsiElement?): Boolean {
|
||||||
val parent = (element as? PsiElement)?.parent
|
val parent = (element as? PsiElement)?.parent
|
||||||
return parent != null && (filter?.isAcceptable(parent, context) ?: true)
|
return parent != null && (filter?.isAcceptable(parent, context) ?: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileFilter(filter : ElementFilter) : PositionElementFilter() {
|
private class FileFilter(filter: ElementFilter) : PositionElementFilter() {
|
||||||
init {
|
init {
|
||||||
setFilter(filter)
|
setFilter(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isAcceptable(element : Any?, context : PsiElement?) : Boolean {
|
override fun isAcceptable(element: Any?, context: PsiElement?): Boolean {
|
||||||
val file = (element as? PsiElement)?.containingFile
|
val file = (element as? PsiElement)?.containingFile
|
||||||
return file != null && (filter?.isAcceptable(file, context) ?: true)
|
return file != null && (filter?.isAcceptable(file, context) ?: true)
|
||||||
}
|
}
|
||||||
@@ -241,20 +243,20 @@ object KeywordCompletion {
|
|||||||
|
|
||||||
var isAfterTry = false
|
var isAfterTry = false
|
||||||
var isAfterCatch = false
|
var isAfterCatch = false
|
||||||
if (prevLeaf.node.elementType == KtTokens.RBRACE) {
|
if (prevLeaf.node.elementType == RBRACE) {
|
||||||
val blockParent = (prevLeaf.parent as? KtBlockExpression)?.parent
|
when ((prevLeaf.parent as? KtBlockExpression)?.parent) {
|
||||||
when (blockParent) {
|
|
||||||
is KtTryExpression -> isAfterTry = true
|
is KtTryExpression -> isAfterTry = true
|
||||||
is KtCatchClause -> { isAfterTry = true; isAfterCatch = true }
|
is KtCatchClause -> {
|
||||||
|
isAfterTry = true; isAfterCatch = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAfterThen) {
|
if (isAfterThen) {
|
||||||
if (isAfterTry) {
|
prefixText += if (isAfterTry) {
|
||||||
prefixText += "if (a)\n"
|
"if (a)\n"
|
||||||
}
|
} else {
|
||||||
else {
|
"if (a) {}\n"
|
||||||
prefixText += "if (a) {}\n"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isAfterTry) {
|
if (isAfterTry) {
|
||||||
@@ -266,16 +268,15 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return buildFilterWithContext(prefixText, prevParent, position)
|
return buildFilterWithContext(prefixText, prevParent, position)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val lastExpression = prevParent
|
val lastExpression = prevParent
|
||||||
.siblings(forward = false, withItself = false)
|
.siblings(forward = false, withItself = false)
|
||||||
.firstIsInstanceOrNull<KtExpression>()
|
.firstIsInstanceOrNull<KtExpression>()
|
||||||
if (lastExpression != null) {
|
if (lastExpression != null) {
|
||||||
val contextAfterExpression = lastExpression
|
val contextAfterExpression = lastExpression
|
||||||
.siblings(forward = true, withItself = false)
|
.siblings(forward = true, withItself = false)
|
||||||
.takeWhile { it != prevParent }
|
.takeWhile { it != prevParent }
|
||||||
.joinToString { it.text }
|
.joinToString { it.text }
|
||||||
return buildFilterWithContext(prefixText + "x" + contextAfterExpression, prevParent, position)
|
return buildFilterWithContext(prefixText + "x" + contextAfterExpression, prevParent, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,13 +297,11 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is KtDeclaration -> {
|
is KtDeclaration -> {
|
||||||
val scope = parent.parent
|
when (parent.parent) {
|
||||||
when (scope) {
|
|
||||||
is KtClassOrObject -> {
|
is KtClassOrObject -> {
|
||||||
return if (parent is KtPrimaryConstructor) {
|
return if (parent is KtPrimaryConstructor) {
|
||||||
buildFilterWithReducedContext("class X ", parent, position)
|
buildFilterWithReducedContext("class X ", parent, position)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
buildFilterWithReducedContext("class X { ", parent, position)
|
buildFilterWithReducedContext("class X { ", parent, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,26 +329,34 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFilterWithContext(prefixText: String,
|
private fun buildFilterWithContext(
|
||||||
contextElement: PsiElement,
|
prefixText: String,
|
||||||
position: PsiElement): (KtKeywordToken) -> Boolean {
|
contextElement: PsiElement,
|
||||||
|
position: PsiElement
|
||||||
|
): (KtKeywordToken) -> Boolean {
|
||||||
val offset = position.getStartOffsetInAncestor(contextElement)
|
val offset = position.getStartOffsetInAncestor(contextElement)
|
||||||
val truncatedContext = contextElement.text!!.substring(0, offset)
|
val truncatedContext = contextElement.text!!.substring(0, offset)
|
||||||
return buildFilterByText(prefixText + truncatedContext, position)
|
return buildFilterByText(prefixText + truncatedContext, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFilterWithReducedContext(prefixText: String,
|
private fun buildFilterWithReducedContext(
|
||||||
contextElement: PsiElement?,
|
prefixText: String,
|
||||||
position: PsiElement): (KtKeywordToken) -> Boolean {
|
contextElement: PsiElement?,
|
||||||
|
position: PsiElement
|
||||||
|
): (KtKeywordToken) -> Boolean {
|
||||||
val builder = StringBuilder()
|
val builder = StringBuilder()
|
||||||
buildReducedContextBefore(builder, position, contextElement)
|
buildReducedContextBefore(builder, position, contextElement)
|
||||||
return buildFilterByText(prefixText + builder.toString(), position)
|
return buildFilterByText(prefixText + builder.toString(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun buildFilesWithKeywordApplication(keywordTokenType: KtKeywordToken, prefixText: String, psiFactory: KtPsiFactory): Sequence<KtFile> {
|
private fun buildFilesWithKeywordApplication(
|
||||||
|
keywordTokenType: KtKeywordToken,
|
||||||
|
prefixText: String,
|
||||||
|
psiFactory: KtPsiFactory
|
||||||
|
): Sequence<KtFile> {
|
||||||
return computeKeywordApplications(prefixText, keywordTokenType)
|
return computeKeywordApplications(prefixText, keywordTokenType)
|
||||||
.map { application -> psiFactory.createFile(prefixText + application) }
|
.map { application -> psiFactory.createFile(prefixText + application) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -359,7 +366,7 @@ object KeywordCompletion {
|
|||||||
val elementAt = file.findElementAt(prefixText.length)!!
|
val elementAt = file.findElementAt(prefixText.length)!!
|
||||||
|
|
||||||
val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings
|
val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings
|
||||||
?: LanguageVersionSettingsImpl.DEFAULT
|
?: LanguageVersionSettingsImpl.DEFAULT
|
||||||
|
|
||||||
when {
|
when {
|
||||||
!elementAt.node!!.elementType.matchesKeyword(keywordTokenType) -> return false
|
!elementAt.node!!.elementType.matchesKeyword(keywordTokenType) -> return false
|
||||||
@@ -387,9 +394,29 @@ object KeywordCompletion {
|
|||||||
|
|
||||||
is KtEnumEntry -> listOf(ENUM_ENTRY)
|
is KtEnumEntry -> listOf(ENUM_ENTRY)
|
||||||
|
|
||||||
is KtClassBody -> listOf(CLASS_ONLY, INTERFACE, OBJECT, ENUM_CLASS, ANNOTATION_CLASS, MEMBER_FUNCTION, MEMBER_PROPERTY, FUNCTION, PROPERTY)
|
is KtClassBody -> listOf(
|
||||||
|
CLASS_ONLY,
|
||||||
|
INTERFACE,
|
||||||
|
OBJECT,
|
||||||
|
ENUM_CLASS,
|
||||||
|
ANNOTATION_CLASS,
|
||||||
|
MEMBER_FUNCTION,
|
||||||
|
MEMBER_PROPERTY,
|
||||||
|
FUNCTION,
|
||||||
|
PROPERTY
|
||||||
|
)
|
||||||
|
|
||||||
is KtFile -> listOf(CLASS_ONLY, INTERFACE, OBJECT, ENUM_CLASS, ANNOTATION_CLASS, TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, FUNCTION, PROPERTY)
|
is KtFile -> listOf(
|
||||||
|
CLASS_ONLY,
|
||||||
|
INTERFACE,
|
||||||
|
OBJECT,
|
||||||
|
ENUM_CLASS,
|
||||||
|
ANNOTATION_CLASS,
|
||||||
|
TOP_LEVEL_FUNCTION,
|
||||||
|
TOP_LEVEL_PROPERTY,
|
||||||
|
FUNCTION,
|
||||||
|
PROPERTY
|
||||||
|
)
|
||||||
|
|
||||||
else -> listOf()
|
else -> listOf()
|
||||||
}
|
}
|
||||||
@@ -397,22 +424,22 @@ object KeywordCompletion {
|
|||||||
if (modifierTargets != null && possibleTargets.isNotEmpty() &&
|
if (modifierTargets != null && possibleTargets.isNotEmpty() &&
|
||||||
modifierTargets.none {
|
modifierTargets.none {
|
||||||
isModifierTargetSupportedAtLanguageLevel(keywordTokenType, it, languageVersionSettings)
|
isModifierTargetSupportedAtLanguageLevel(keywordTokenType, it, languageVersionSettings)
|
||||||
}) return false
|
}
|
||||||
|
) return false
|
||||||
|
|
||||||
val ownerDeclaration = container?.getParentOfType<KtDeclaration>(strict = true)
|
val parentTarget = when (val ownerDeclaration = container?.getParentOfType<KtDeclaration>(strict = true)) {
|
||||||
val parentTarget = when (ownerDeclaration) {
|
null -> FILE
|
||||||
null -> KotlinTarget.FILE
|
|
||||||
|
|
||||||
is KtClass -> {
|
is KtClass -> {
|
||||||
when {
|
when {
|
||||||
ownerDeclaration.isInterface() -> KotlinTarget.INTERFACE
|
ownerDeclaration.isInterface() -> INTERFACE
|
||||||
ownerDeclaration.isEnum() -> KotlinTarget.ENUM_CLASS
|
ownerDeclaration.isEnum() -> ENUM_CLASS
|
||||||
ownerDeclaration.isAnnotation() -> KotlinTarget.ANNOTATION_CLASS
|
ownerDeclaration.isAnnotation() -> ANNOTATION_CLASS
|
||||||
else -> KotlinTarget.CLASS_ONLY
|
else -> CLASS_ONLY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtObjectDeclaration -> if (ownerDeclaration.isObjectLiteral()) KotlinTarget.OBJECT_LITERAL else KotlinTarget.OBJECT
|
is KtObjectDeclaration -> if (ownerDeclaration.isObjectLiteral()) OBJECT_LITERAL else OBJECT
|
||||||
|
|
||||||
else -> return keywordTokenType != CONST_KEYWORD
|
else -> return keywordTokenType != CONST_KEYWORD
|
||||||
}
|
}
|
||||||
@@ -421,8 +448,8 @@ object KeywordCompletion {
|
|||||||
|
|
||||||
if (keywordTokenType == CONST_KEYWORD) {
|
if (keywordTokenType == CONST_KEYWORD) {
|
||||||
return when (parentTarget) {
|
return when (parentTarget) {
|
||||||
KotlinTarget.OBJECT -> true
|
OBJECT -> true
|
||||||
KotlinTarget.FILE -> {
|
FILE -> {
|
||||||
val prevSiblings = elementAt.parent.siblings(withItself = false, forward = false)
|
val prevSiblings = elementAt.parent.siblings(withItself = false, forward = false)
|
||||||
val hasLineBreak = prevSiblings
|
val hasLineBreak = prevSiblings
|
||||||
.takeWhile { it is PsiWhiteSpace || it.isSemicolon() }
|
.takeWhile { it is PsiWhiteSpace || it.isSemicolon() }
|
||||||
@@ -440,25 +467,25 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fun (keywordTokenType): Boolean {
|
return fun(keywordTokenType): Boolean {
|
||||||
val files = buildFilesWithKeywordApplication(keywordTokenType, prefixText, psiFactory)
|
val files = buildFilesWithKeywordApplication(keywordTokenType, prefixText, psiFactory)
|
||||||
return files.any { file -> isKeywordCorrectlyApplied(keywordTokenType, file); }
|
return files.any { file -> isKeywordCorrectlyApplied(keywordTokenType, file); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PsiElement.isSemicolon() = node.elementType == KtTokens.SEMICOLON
|
private fun PsiElement.isSemicolon() = node.elementType == SEMICOLON
|
||||||
|
|
||||||
private fun isErrorElementBefore(token: PsiElement): Boolean {
|
private fun isErrorElementBefore(token: PsiElement): Boolean {
|
||||||
for (leaf in token.prevLeafs) {
|
for (leaf in token.prevLeafs) {
|
||||||
if (leaf is PsiWhiteSpace || leaf is PsiComment) continue
|
if (leaf is PsiWhiteSpace || leaf is PsiComment) continue
|
||||||
if (leaf.parentsWithSelf.any { it is PsiErrorElement } ) return true
|
if (leaf.parentsWithSelf.any { it is PsiErrorElement }) return true
|
||||||
if (leaf.textLength != 0) break
|
if (leaf.textLength != 0) break
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IElementType.matchesKeyword(keywordType: KtKeywordToken): Boolean {
|
private fun IElementType.matchesKeyword(keywordType: KtKeywordToken): Boolean {
|
||||||
return when(this) {
|
return when (this) {
|
||||||
keywordType -> true
|
keywordType -> true
|
||||||
NOT_IN -> keywordType == IN_KEYWORD
|
NOT_IN -> keywordType == IN_KEYWORD
|
||||||
NOT_IS -> keywordType == IS_KEYWORD
|
NOT_IS -> keywordType == IS_KEYWORD
|
||||||
@@ -468,29 +495,28 @@ object KeywordCompletion {
|
|||||||
|
|
||||||
private fun isModifierSupportedAtLanguageLevel(keyword: KtKeywordToken, languageVersionSettings: LanguageVersionSettings): Boolean {
|
private fun isModifierSupportedAtLanguageLevel(keyword: KtKeywordToken, languageVersionSettings: LanguageVersionSettings): Boolean {
|
||||||
val feature = when (keyword) {
|
val feature = when (keyword) {
|
||||||
KtTokens.TYPE_ALIAS_KEYWORD -> LanguageFeature.TypeAliases
|
TYPE_ALIAS_KEYWORD -> LanguageFeature.TypeAliases
|
||||||
KtTokens.HEADER_KEYWORD, KtTokens.IMPL_KEYWORD -> return false
|
HEADER_KEYWORD, IMPL_KEYWORD -> return false
|
||||||
KtTokens.EXPECT_KEYWORD, KtTokens.ACTUAL_KEYWORD -> LanguageFeature.MultiPlatformProjects
|
EXPECT_KEYWORD, ACTUAL_KEYWORD -> LanguageFeature.MultiPlatformProjects
|
||||||
KtTokens.SUSPEND_KEYWORD -> LanguageFeature.Coroutines
|
SUSPEND_KEYWORD -> LanguageFeature.Coroutines
|
||||||
else -> return true
|
else -> return true
|
||||||
}
|
}
|
||||||
return languageVersionSettings.supportsFeature(feature)
|
return languageVersionSettings.supportsFeature(feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isModifierTargetSupportedAtLanguageLevel(
|
private fun isModifierTargetSupportedAtLanguageLevel(
|
||||||
keyword: KtKeywordToken,
|
keyword: KtKeywordToken,
|
||||||
target: KotlinTarget,
|
target: KotlinTarget,
|
||||||
languageVersionSettings: LanguageVersionSettings
|
languageVersionSettings: LanguageVersionSettings
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (keyword == KtTokens.LATEINIT_KEYWORD) {
|
if (keyword == LATEINIT_KEYWORD) {
|
||||||
val feature = when (target) {
|
val feature = when (target) {
|
||||||
TOP_LEVEL_PROPERTY -> LanguageFeature.LateinitTopLevelProperties
|
TOP_LEVEL_PROPERTY -> LanguageFeature.LateinitTopLevelProperties
|
||||||
LOCAL_VARIABLE -> LanguageFeature.LateinitLocalVariables
|
LOCAL_VARIABLE -> LanguageFeature.LateinitLocalVariables
|
||||||
else -> return true
|
else -> return true
|
||||||
}
|
}
|
||||||
return languageVersionSettings.supportsFeature(feature)
|
return languageVersionSettings.supportsFeature(feature)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,8 +545,7 @@ object KeywordCompletion {
|
|||||||
if (child == prevDeclaration) {
|
if (child == prevDeclaration) {
|
||||||
builder.appendReducedText(child)
|
builder.appendReducedText(child)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
builder.append(child!!.text)
|
builder.append(child!!.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,8 +557,7 @@ object KeywordCompletion {
|
|||||||
var child = element.firstChild
|
var child = element.firstChild
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
append(element.text!!)
|
append(element.text!!)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
when (child) {
|
when (child) {
|
||||||
is KtBlockExpression, is KtClassBody -> append("{}")
|
is KtBlockExpression, is KtClassBody -> append("{}")
|
||||||
@@ -550,14 +574,11 @@ object KeywordCompletion {
|
|||||||
return parent!!.getStartOffsetInAncestor(ancestor) + startOffsetInParent
|
return parent!!.getStartOffsetInAncestor(ancestor) + startOffsetInParent
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PsiElement.goUpWhileIsLastChild(): Sequence<PsiElement> {
|
private fun PsiElement.goUpWhileIsLastChild(): Sequence<PsiElement> = generateSequence(this) {
|
||||||
return generateSequence(this) {
|
when {
|
||||||
if (it is PsiFile)
|
it is PsiFile -> null
|
||||||
null
|
it != it.parent.lastChild -> null
|
||||||
else if (it != it.parent.lastChild)
|
else -> it.parent
|
||||||
null
|
|
||||||
else
|
|
||||||
it.parent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -115,11 +115,13 @@ class OverridesCompletion(
|
|||||||
val override = KtTokens.OVERRIDE_KEYWORD.value
|
val override = KtTokens.OVERRIDE_KEYWORD.value
|
||||||
|
|
||||||
tailrec fun calcStartOffset(startOffset: Int, diff: Int = 0): Int {
|
tailrec fun calcStartOffset(startOffset: Int, diff: Int = 0): Int {
|
||||||
if (context.document.text[startOffset - 1].isWhitespace()) {
|
return when {
|
||||||
return calcStartOffset(startOffset - 1, diff + 1)
|
context.document.text[startOffset - 1].isWhitespace() -> calcStartOffset(startOffset - 1, diff + 1)
|
||||||
} else if (context.document.text.substring(startOffset - override.length, startOffset) == override) {
|
context.document.text.substring(startOffset - override.length, startOffset) == override -> {
|
||||||
return startOffset - override.length
|
startOffset - override.length
|
||||||
} else return diff + startOffset
|
}
|
||||||
|
else -> diff + startOffset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val startOffset = calcStartOffset(context.startOffset)
|
val startOffset = calcStartOffset(context.startOffset)
|
||||||
|
|||||||
+4
-6
@@ -22,12 +22,10 @@ abstract class AbstractKeywordCompletionTest : KotlinFixtureCompletionBaseTestCa
|
|||||||
return items.filter { it.`object` is KeywordLookupObject }.toTypedArray()
|
return items.filter { it.`object` is KeywordLookupObject }.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProjectDescriptor(): KotlinLightProjectDescriptor {
|
override fun getProjectDescriptor(): KotlinLightProjectDescriptor = when {
|
||||||
when {
|
"LangLevel10" in fileName() -> KotlinProjectDescriptorWithFacet.KOTLIN_10
|
||||||
"LangLevel10" in fileName() -> return KotlinProjectDescriptorWithFacet.KOTLIN_10
|
"LangLevel11" in fileName() -> KotlinProjectDescriptorWithFacet.KOTLIN_11
|
||||||
"LangLevel11" in fileName() -> return KotlinProjectDescriptorWithFacet.KOTLIN_11
|
else -> KotlinProjectDescriptorWithFacet.KOTLIN_STABLE_WITH_MULTIPLATFORM
|
||||||
else -> return KotlinProjectDescriptorWithFacet.KOTLIN_STABLE_WITH_MULTIPLATFORM
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun defaultInvocationCount() = 1
|
override fun defaultInvocationCount() = 1
|
||||||
|
|||||||
@@ -60,20 +60,20 @@ import java.lang.reflect.Modifier
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class KotlinIndicesHelper(
|
class KotlinIndicesHelper(
|
||||||
private val resolutionFacade: ResolutionFacade,
|
private val resolutionFacade: ResolutionFacade,
|
||||||
private val scope: GlobalSearchScope,
|
private val scope: GlobalSearchScope,
|
||||||
visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||||
private val declarationTranslator: (KtDeclaration) -> KtDeclaration? = { it },
|
private val declarationTranslator: (KtDeclaration) -> KtDeclaration? = { it },
|
||||||
applyExcludeSettings: Boolean = true,
|
applyExcludeSettings: Boolean = true,
|
||||||
private val filterOutPrivate: Boolean = true,
|
private val filterOutPrivate: Boolean = true,
|
||||||
private val file: KtFile? = null
|
private val file: KtFile? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val moduleDescriptor = resolutionFacade.moduleDescriptor
|
private val moduleDescriptor = resolutionFacade.moduleDescriptor
|
||||||
private val project = resolutionFacade.project
|
private val project = resolutionFacade.project
|
||||||
private val scopeWithoutKotlin = scope.excludeKotlinSources() as GlobalSearchScope
|
private val scopeWithoutKotlin = scope.excludeKotlinSources() as GlobalSearchScope
|
||||||
|
|
||||||
private val descriptorFilter: (DeclarationDescriptor) -> Boolean = filter@ {
|
private val descriptorFilter: (DeclarationDescriptor) -> Boolean = filter@{
|
||||||
if (resolutionFacade.frontendService<DeprecationResolver>().isHiddenInResolution(it)) return@filter false
|
if (resolutionFacade.frontendService<DeprecationResolver>().isHiddenInResolution(it)) return@filter false
|
||||||
if (!visibilityFilter(it)) return@filter false
|
if (!visibilityFilter(it)) return@filter false
|
||||||
if (applyExcludeSettings && it.isExcludedFromAutoImport(project, file)) return@filter false
|
if (applyExcludeSettings && it.isExcludedFromAutoImport(project, file)) return@filter false
|
||||||
@@ -85,31 +85,32 @@ class KotlinIndicesHelper(
|
|||||||
declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name)
|
declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name)
|
||||||
declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name)
|
declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name)
|
||||||
return declarations
|
return declarations
|
||||||
.flatMap { it.resolveToDescriptors<CallableDescriptor>() }
|
.flatMap { it.resolveToDescriptors<CallableDescriptor>() }
|
||||||
.filter { descriptorFilter(it) }
|
.filter { descriptorFilter(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableSet<KtNamedDeclaration>.addTopLevelNonExtensionCallablesByName(
|
private fun MutableSet<KtNamedDeclaration>.addTopLevelNonExtensionCallablesByName(
|
||||||
index: StringStubIndexExtension<out KtNamedDeclaration>,
|
index: StringStubIndexExtension<out KtNamedDeclaration>,
|
||||||
name: String
|
name: String
|
||||||
) {
|
) {
|
||||||
index.get(name, project, scope).filterTo(this) { it.parent is KtFile && it is KtCallableDeclaration && it.receiverTypeReference == null }
|
index.get(name, project, scope)
|
||||||
|
.filterTo(this) { it.parent is KtFile && it is KtCallableDeclaration && it.receiverTypeReference == null }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTopLevelExtensionOperatorsByName(name: String): Collection<FunctionDescriptor> {
|
fun getTopLevelExtensionOperatorsByName(name: String): Collection<FunctionDescriptor> {
|
||||||
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
|
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
|
||||||
.filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
|
.filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
|
||||||
.flatMap { it.resolveToDescriptors<FunctionDescriptor>() }
|
.flatMap { it.resolveToDescriptors<FunctionDescriptor>() }
|
||||||
.filter { descriptorFilter(it) && it.extensionReceiverParameter != null }
|
.filter { descriptorFilter(it) && it.extensionReceiverParameter != null }
|
||||||
.distinct()
|
.distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMemberOperatorsByName(name: String): Collection<FunctionDescriptor> {
|
fun getMemberOperatorsByName(name: String): Collection<FunctionDescriptor> {
|
||||||
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
|
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
|
||||||
.filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
|
.filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
|
||||||
.flatMap { it.resolveToDescriptors<FunctionDescriptor>() }
|
.flatMap { it.resolveToDescriptors<FunctionDescriptor>() }
|
||||||
.filter { descriptorFilter(it) && it.extensionReceiverParameter == null }
|
.filter { descriptorFilter(it) && it.extensionReceiverParameter == null }
|
||||||
.distinct()
|
.distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processTopLevelCallables(nameFilter: (String) -> Boolean, processor: (CallableDescriptor) -> Unit) {
|
fun processTopLevelCallables(nameFilter: (String) -> Boolean, processor: (CallableDescriptor) -> Unit) {
|
||||||
@@ -135,21 +136,22 @@ class KotlinIndicesHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCallableTopLevelExtensions(
|
fun getCallableTopLevelExtensions(
|
||||||
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
|
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
|
||||||
position: KtExpression,
|
position: KtExpression,
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
nameFilter: (String) -> Boolean
|
nameFilter: (String) -> Boolean
|
||||||
): Collection<CallableDescriptor> {
|
): Collection<CallableDescriptor> {
|
||||||
val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, position, moduleDescriptor, resolutionFacade, stableSmartCastsOnly = false)
|
val receiverTypes =
|
||||||
?: return emptyList()
|
callTypeAndReceiver.receiverTypes(bindingContext, position, moduleDescriptor, resolutionFacade, stableSmartCastsOnly = false)
|
||||||
|
?: return emptyList()
|
||||||
return getCallableTopLevelExtensions(callTypeAndReceiver, receiverTypes, nameFilter)
|
return getCallableTopLevelExtensions(callTypeAndReceiver, receiverTypes, nameFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCallableTopLevelExtensions(
|
fun getCallableTopLevelExtensions(
|
||||||
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
|
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
|
||||||
receiverTypes: Collection<KotlinType>,
|
receiverTypes: Collection<KotlinType>,
|
||||||
nameFilter: (String) -> Boolean,
|
nameFilter: (String) -> Boolean,
|
||||||
declarationFilter: (KtDeclaration) -> Boolean = { true }
|
declarationFilter: (KtDeclaration) -> Boolean = { true }
|
||||||
): Collection<CallableDescriptor> {
|
): Collection<CallableDescriptor> {
|
||||||
if (receiverTypes.isEmpty()) return emptyList()
|
if (receiverTypes.isEmpty()) return emptyList()
|
||||||
|
|
||||||
@@ -159,13 +161,13 @@ class KotlinIndicesHelper(
|
|||||||
val index = KotlinTopLevelExtensionsByReceiverTypeIndex.INSTANCE
|
val index = KotlinTopLevelExtensionsByReceiverTypeIndex.INSTANCE
|
||||||
|
|
||||||
val declarations = index.getAllKeys(project)
|
val declarations = index.getAllKeys(project)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter {
|
.filter {
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
KotlinTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames
|
KotlinTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames
|
||||||
&& nameFilter(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
&& nameFilter(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
||||||
}
|
}
|
||||||
.flatMap { index.get(it, project, scope).asSequence() }.filter(declarationFilter)
|
.flatMap { index.get(it, project, scope).asSequence() }.filter(declarationFilter)
|
||||||
|
|
||||||
val suitableExtensions = findSuitableExtensions(declarations, receiverTypes, callTypeAndReceiver.callType)
|
val suitableExtensions = findSuitableExtensions(declarations, receiverTypes, callTypeAndReceiver.callType)
|
||||||
|
|
||||||
@@ -234,9 +236,9 @@ class KotlinIndicesHelper(
|
|||||||
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
||||||
*/
|
*/
|
||||||
private fun findSuitableExtensions(
|
private fun findSuitableExtensions(
|
||||||
declarations: Sequence<KtCallableDeclaration>,
|
declarations: Sequence<KtCallableDeclaration>,
|
||||||
receiverTypes: Collection<KotlinType>,
|
receiverTypes: Collection<KotlinType>,
|
||||||
callType: CallType<*>
|
callType: CallType<*>
|
||||||
): Collection<CallableDescriptor> {
|
): Collection<CallableDescriptor> {
|
||||||
val result = LinkedHashSet<CallableDescriptor>()
|
val result = LinkedHashSet<CallableDescriptor>()
|
||||||
|
|
||||||
@@ -253,24 +255,24 @@ class KotlinIndicesHelper(
|
|||||||
|
|
||||||
fun getJvmClassesByName(name: String): Collection<ClassDescriptor> {
|
fun getJvmClassesByName(name: String): Collection<ClassDescriptor> {
|
||||||
return PsiShortNamesCache.getInstance(project).getClassesByName(name, scope)
|
return PsiShortNamesCache.getInstance(project).getClassesByName(name, scope)
|
||||||
.filter { it in scope && it.containingFile != null }
|
.filter { it in scope && it.containingFile != null }
|
||||||
.mapNotNull { it.resolveToDescriptor(resolutionFacade) }
|
.mapNotNull { it.resolveToDescriptor(resolutionFacade) }
|
||||||
.filter(descriptorFilter)
|
.filter(descriptorFilter)
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKotlinEnumsByName(name: String): Collection<DeclarationDescriptor> {
|
fun getKotlinEnumsByName(name: String): Collection<DeclarationDescriptor> {
|
||||||
return KotlinClassShortNameIndex.getInstance()[name, project, scope]
|
return KotlinClassShortNameIndex.getInstance()[name, project, scope]
|
||||||
.filter { it is KtEnumEntry && it in scope }
|
.filter { it is KtEnumEntry && it in scope }
|
||||||
.mapNotNull { it.unsafeResolveToDescriptor() }
|
.mapNotNull { it.unsafeResolveToDescriptor() }
|
||||||
.filter(descriptorFilter)
|
.filter(descriptorFilter)
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processJvmCallablesByName(
|
fun processJvmCallablesByName(
|
||||||
name: String,
|
name: String,
|
||||||
filter: (PsiMember) -> Boolean,
|
filter: (PsiMember) -> Boolean,
|
||||||
processor: (CallableDescriptor) -> Unit
|
processor: (CallableDescriptor) -> Unit
|
||||||
) {
|
) {
|
||||||
val javaDeclarations = getJavaCallables(name, PsiShortNamesCache.getInstance(project))
|
val javaDeclarations = getJavaCallables(name, PsiShortNamesCache.getInstance(project))
|
||||||
val processed = HashSet<CallableDescriptor>()
|
val processed = HashSet<CallableDescriptor>()
|
||||||
@@ -295,16 +297,13 @@ class KotlinIndicesHelper(
|
|||||||
val shortNamesCache = PsiShortNamesCache.getInstance(project)
|
val shortNamesCache = PsiShortNamesCache.getInstance(project)
|
||||||
if (shortNamesCache is CompositeShortNamesCache) {
|
if (shortNamesCache is CompositeShortNamesCache) {
|
||||||
try {
|
try {
|
||||||
fun getMyCachesField(clazz: Class<out PsiShortNamesCache>): Field {
|
fun getMyCachesField(clazz: Class<out PsiShortNamesCache>): Field = try {
|
||||||
try {
|
clazz.getDeclaredField("myCaches")
|
||||||
return clazz.getDeclaredField("myCaches")
|
} catch (e: NoSuchFieldException) {
|
||||||
}
|
// In case the class is proguarded
|
||||||
catch (e: NoSuchFieldException) {
|
clazz.declaredFields.first {
|
||||||
// In case the class is proguarded
|
Modifier.isPrivate(it.modifiers) && Modifier.isFinal(it.modifiers) && !Modifier.isStatic(it.modifiers)
|
||||||
return clazz.declaredFields.first {
|
&& it.type.isArray && it.type.componentType == PsiShortNamesCache::class.java
|
||||||
Modifier.isPrivate(it.modifiers) && Modifier.isFinal(it.modifiers) && !Modifier.isStatic(it.modifiers)
|
|
||||||
&& it.type.isArray && it.type.componentType == PsiShortNamesCache::class.java
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,26 +314,25 @@ class KotlinIndicesHelper(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return@lazy (myCachesField.get(shortNamesCache) as Array<PsiShortNamesCache>).filter {
|
return@lazy (myCachesField.get(shortNamesCache) as Array<PsiShortNamesCache>).filter {
|
||||||
it !is KotlinShortNamesCache
|
it !is KotlinShortNamesCache
|
||||||
&& it::class.java.name != "com.android.tools.idea.databinding.BrShortNamesCache"
|
&& it::class.java.name != "com.android.tools.idea.databinding.BrShortNamesCache"
|
||||||
&& it::class.java.name != "com.android.tools.idea.databinding.DataBindingComponentShortNamesCache"
|
&& it::class.java.name != "com.android.tools.idea.databinding.DataBindingComponentShortNamesCache"
|
||||||
&& it::class.java.name != "com.android.tools.idea.databinding.DataBindingShortNamesCache"
|
&& it::class.java.name != "com.android.tools.idea.databinding.DataBindingShortNamesCache"
|
||||||
}
|
}
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
myCachesField.isAccessible = previousIsAccessible
|
myCachesField.isAccessible = previousIsAccessible
|
||||||
}
|
}
|
||||||
}
|
} catch (thr: Throwable) {
|
||||||
catch (thr: Throwable) {
|
|
||||||
// Our dirty hack isn't working
|
// Our dirty hack isn't working
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return@lazy null
|
return@lazy null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getJavaCallables(name: String, shortNamesCache: PsiShortNamesCache): Sequence<Any> {
|
private fun getJavaCallables(name: String, shortNamesCache: PsiShortNamesCache): Sequence<Any> {
|
||||||
filteredShortNamesCaches?.let { caches -> return getCallablesByName(name, scopeWithoutKotlin, caches) }
|
filteredShortNamesCaches?.let { caches -> return getCallablesByName(name, scopeWithoutKotlin, caches) }
|
||||||
return shortNamesCache.getFieldsByNameUnfiltered(name, scopeWithoutKotlin).asSequence() +
|
return shortNamesCache.getFieldsByNameUnfiltered(name, scopeWithoutKotlin).asSequence() +
|
||||||
shortNamesCache.getMethodsByNameUnfiltered(name, scopeWithoutKotlin).asSequence()
|
shortNamesCache.getMethodsByNameUnfiltered(name, scopeWithoutKotlin).asSequence()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCallablesByName(name: String, scope: GlobalSearchScope, caches: List<PsiShortNamesCache>): Sequence<Any> {
|
private fun getCallablesByName(name: String, scope: GlobalSearchScope, caches: List<PsiShortNamesCache>): Sequence<Any> {
|
||||||
@@ -359,9 +357,9 @@ class KotlinIndicesHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun processKotlinCallablesByName(
|
fun processKotlinCallablesByName(
|
||||||
name: String,
|
name: String,
|
||||||
filter: (KtNamedDeclaration) -> Boolean,
|
filter: (KtNamedDeclaration) -> Boolean,
|
||||||
processor: (CallableDescriptor) -> Unit
|
processor: (CallableDescriptor) -> Unit
|
||||||
) {
|
) {
|
||||||
val functions: Sequence<KtCallableDeclaration> = KotlinFunctionShortNameIndex.getInstance().get(name, project, scope).asSequence()
|
val functions: Sequence<KtCallableDeclaration> = KotlinFunctionShortNameIndex.getInstance().get(name, project, scope).asSequence()
|
||||||
val properties: Sequence<KtNamedDeclaration> = KotlinPropertyShortNameIndex.getInstance().get(name, project, scope).asSequence()
|
val properties: Sequence<KtNamedDeclaration> = KotlinPropertyShortNameIndex.getInstance().get(name, project, scope).asSequence()
|
||||||
@@ -377,45 +375,46 @@ class KotlinIndicesHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getKotlinClasses(
|
fun getKotlinClasses(
|
||||||
nameFilter: (String) -> Boolean,
|
nameFilter: (String) -> Boolean,
|
||||||
psiFilter: (KtDeclaration) -> Boolean = { true },
|
psiFilter: (KtDeclaration) -> Boolean = { true },
|
||||||
kindFilter: (ClassKind) -> Boolean = { true }): Collection<ClassDescriptor> {
|
kindFilter: (ClassKind) -> Boolean = { true }
|
||||||
|
): Collection<ClassDescriptor> {
|
||||||
val index = KotlinFullClassNameIndex.getInstance()
|
val index = KotlinFullClassNameIndex.getInstance()
|
||||||
return index.getAllKeys(project).asSequence()
|
return index.getAllKeys(project).asSequence()
|
||||||
.filter { fqName ->
|
.filter { fqName ->
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
nameFilter(fqName.substringAfterLast('.'))
|
nameFilter(fqName.substringAfterLast('.'))
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
.flatMap { fqName ->
|
||||||
|
index[fqName, project, scope].flatMap { classOrObject ->
|
||||||
|
classOrObject.resolveToDescriptorsWithHack(psiFilter).filterIsInstance<ClassDescriptor>()
|
||||||
}
|
}
|
||||||
.toList()
|
}
|
||||||
.flatMap { fqName ->
|
.filter { kindFilter(it.kind) && descriptorFilter(it) }
|
||||||
index[fqName, project, scope].flatMap { classOrObject ->
|
|
||||||
classOrObject.resolveToDescriptorsWithHack(psiFilter).filterIsInstance<ClassDescriptor>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter { kindFilter(it.kind) && descriptorFilter(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTopLevelTypeAliases(nameFilter: (String) -> Boolean): Collection<TypeAliasDescriptor> {
|
fun getTopLevelTypeAliases(nameFilter: (String) -> Boolean): Collection<TypeAliasDescriptor> {
|
||||||
val index = KotlinTopLevelTypeAliasFqNameIndex.getInstance()
|
val index = KotlinTopLevelTypeAliasFqNameIndex.getInstance()
|
||||||
return index.getAllKeys(project).asSequence()
|
return index.getAllKeys(project).asSequence()
|
||||||
.filter {
|
.filter {
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
nameFilter(it.substringAfterLast('.'))
|
nameFilter(it.substringAfterLast('.'))
|
||||||
}
|
}
|
||||||
.toList()
|
.toList()
|
||||||
.flatMap { fqName ->
|
.flatMap { fqName ->
|
||||||
index[fqName, project, scope]
|
index[fqName, project, scope]
|
||||||
.flatMap { it.resolveToDescriptors<TypeAliasDescriptor>() }
|
.flatMap { it.resolveToDescriptors<TypeAliasDescriptor>() }
|
||||||
|
|
||||||
}
|
}
|
||||||
.filter(descriptorFilter)
|
.filter(descriptorFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processObjectMembers(
|
fun processObjectMembers(
|
||||||
descriptorKindFilter: DescriptorKindFilter,
|
descriptorKindFilter: DescriptorKindFilter,
|
||||||
nameFilter: (String) -> Boolean,
|
nameFilter: (String) -> Boolean,
|
||||||
filter: (KtNamedDeclaration, KtObjectDeclaration) -> Boolean,
|
filter: (KtNamedDeclaration, KtObjectDeclaration) -> Boolean,
|
||||||
processor: (DeclarationDescriptor) -> Unit
|
processor: (DeclarationDescriptor) -> Unit
|
||||||
) {
|
) {
|
||||||
fun processIndex(index: StringStubIndexExtension<out KtNamedDeclaration>) {
|
fun processIndex(index: StringStubIndexExtension<out KtNamedDeclaration>) {
|
||||||
for (name in index.getAllKeys(project)) {
|
for (name in index.getAllKeys(project)) {
|
||||||
@@ -445,15 +444,19 @@ class KotlinIndicesHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun processJavaStaticMembers(
|
fun processJavaStaticMembers(
|
||||||
descriptorKindFilter: DescriptorKindFilter,
|
descriptorKindFilter: DescriptorKindFilter,
|
||||||
nameFilter: (String) -> Boolean,
|
nameFilter: (String) -> Boolean,
|
||||||
processor: (DeclarationDescriptor) -> Unit
|
processor: (DeclarationDescriptor) -> Unit
|
||||||
) {
|
) {
|
||||||
val idFilter = IdFilter.getProjectIdFilter(resolutionFacade.project, false)
|
val idFilter = IdFilter.getProjectIdFilter(resolutionFacade.project, false)
|
||||||
val shortNamesCache = PsiShortNamesCache.getInstance(project)
|
val shortNamesCache = PsiShortNamesCache.getInstance(project)
|
||||||
|
|
||||||
val allMethodNames = hashSetOf<String>()
|
val allMethodNames = hashSetOf<String>()
|
||||||
shortNamesCache.processAllMethodNames({ name -> if (nameFilter(name)) allMethodNames.add(name); true }, scopeWithoutKotlin, idFilter)
|
shortNamesCache.processAllMethodNames(
|
||||||
|
{ name -> if (nameFilter(name)) allMethodNames.add(name); true },
|
||||||
|
scopeWithoutKotlin,
|
||||||
|
idFilter
|
||||||
|
)
|
||||||
for (name in allMethodNames) {
|
for (name in allMethodNames) {
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
|
|
||||||
@@ -469,9 +472,9 @@ class KotlinIndicesHelper(
|
|||||||
// SAM-adapter
|
// SAM-adapter
|
||||||
val syntheticScopes = resolutionFacade.getFrontendService(SyntheticScopes::class.java).forceEnableSamAdapters()
|
val syntheticScopes = resolutionFacade.getFrontendService(SyntheticScopes::class.java).forceEnableSamAdapters()
|
||||||
syntheticScopes.collectSyntheticStaticFunctions(container.staticScope, descriptor.name, NoLookupLocation.FROM_IDE)
|
syntheticScopes.collectSyntheticStaticFunctions(container.staticScope, descriptor.name, NoLookupLocation.FROM_IDE)
|
||||||
.filterIsInstance<SamAdapterDescriptor<*>>()
|
.filterIsInstance<SamAdapterDescriptor<*>>()
|
||||||
.firstOrNull { it.baseDescriptorForSynthetic.original == descriptor.original }
|
.firstOrNull { it.baseDescriptorForSynthetic.original == descriptor.original }
|
||||||
?.let { processor(it) }
|
?.let { processor(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,15 +496,15 @@ class KotlinIndicesHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified TDescriptor : Any> KtNamedDeclaration.resolveToDescriptors(): Collection<TDescriptor> {
|
private inline fun <reified TDescriptor : Any> KtNamedDeclaration.resolveToDescriptors(): Collection<TDescriptor> {
|
||||||
return resolveToDescriptorsWithHack({ true }).filterIsInstance<TDescriptor>()
|
return resolveToDescriptorsWithHack { true }.filterIsInstance<TDescriptor>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtNamedDeclaration.resolveToDescriptorsWithHack(
|
private fun KtNamedDeclaration.resolveToDescriptorsWithHack(
|
||||||
psiFilter: (KtDeclaration) -> Boolean): Collection<DeclarationDescriptor> {
|
psiFilter: (KtDeclaration) -> Boolean
|
||||||
|
): Collection<DeclarationDescriptor> {
|
||||||
if (containingKtFile.isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
if (containingKtFile.isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||||
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!)
|
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val translatedDeclaration = declarationTranslator(this) ?: return emptyList()
|
val translatedDeclaration = declarationTranslator(this) ?: return emptyList()
|
||||||
if (!psiFilter(translatedDeclaration)) return emptyList()
|
if (!psiFilter(translatedDeclaration)) return emptyList()
|
||||||
|
|
||||||
|
|||||||
@@ -43,21 +43,21 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val handlers = listOf<ElementHandler<*>>(
|
private val handlers = listOf<ElementHandler<*>>(
|
||||||
LambdaHandler,
|
LambdaHandler,
|
||||||
AnonymousObjectHandler,
|
AnonymousObjectHandler,
|
||||||
AnonymousFunctionHandler,
|
AnonymousFunctionHandler,
|
||||||
PropertyAccessorHandler,
|
PropertyAccessorHandler,
|
||||||
DeclarationHandler,
|
DeclarationHandler,
|
||||||
IfThenHandler,
|
IfThenHandler,
|
||||||
ElseHandler,
|
ElseHandler,
|
||||||
TryHandler,
|
TryHandler,
|
||||||
CatchHandler,
|
CatchHandler,
|
||||||
FinallyHandler,
|
FinallyHandler,
|
||||||
WhileHandler,
|
WhileHandler,
|
||||||
DoWhileHandler,
|
DoWhileHandler,
|
||||||
WhenHandler,
|
WhenHandler,
|
||||||
WhenEntryHandler,
|
WhenEntryHandler,
|
||||||
ForHandler
|
ForHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
private object LambdaHandler : ElementHandler<KtFunctionLiteral>(KtFunctionLiteral::class) {
|
private object LambdaHandler : ElementHandler<KtFunctionLiteral>(KtFunctionLiteral::class) {
|
||||||
@@ -83,8 +83,7 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
|
|
||||||
if (callExpression.valueArgumentList != null) {
|
if (callExpression.valueArgumentList != null) {
|
||||||
appendCallArguments(callExpression)
|
appendCallArguments(callExpression)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (label.isNotEmpty()) append(" ")
|
if (label.isNotEmpty()) append(" ")
|
||||||
}
|
}
|
||||||
append(lambdaText)
|
append(lambdaText)
|
||||||
@@ -149,8 +148,7 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
append(",$ellipsis")
|
append(",$ellipsis")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
append(superTypeEntries.joinToString(separator = ", ") { it.typeReference?.text ?: "" }.truncateEnd(kind))
|
append(superTypeEntries.joinToString(separator = ", ") { it.typeReference?.text ?: "" }.truncateEnd(kind))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,8 +164,10 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
|
|
||||||
private fun KtNamedFunction.buildText(kind: TextKind): String {
|
private fun KtNamedFunction.buildText(kind: TextKind): String {
|
||||||
return "fun(" +
|
return "fun(" +
|
||||||
valueParameters.joinToString(separator = ", ") { if (kind == TextKind.INFO) it.name ?: "" else it.text }.truncateEnd(kind) +
|
valueParameters.joinToString(separator = ", ") { if (kind == TextKind.INFO) it.name ?: "" else it.text }.truncateEnd(
|
||||||
")"
|
kind
|
||||||
|
) +
|
||||||
|
")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,19 +211,16 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun elementTooltip(element: KtDeclaration): String {
|
override fun elementTooltip(element: KtDeclaration): String = try {
|
||||||
try {
|
ElementDescriptionUtil.getElementDescription(element, RefactoringDescriptionLocation.WITH_PARENT)
|
||||||
return ElementDescriptionUtil.getElementDescription(element, RefactoringDescriptionLocation.WITH_PARENT)
|
} catch (e: IndexNotReadyException) {
|
||||||
}
|
"Indexing..."
|
||||||
catch (e: IndexNotReadyException) {
|
|
||||||
return "Indexing..."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class ConstructWithExpressionHandler<TElement : KtElement>(
|
private abstract class ConstructWithExpressionHandler<TElement : KtElement>(
|
||||||
private val constructName: String,
|
private val constructName: String,
|
||||||
type: KClass<TElement>
|
type: KClass<TElement>
|
||||||
) : ElementHandler<TElement>(type) {
|
) : ElementHandler<TElement>(type) {
|
||||||
|
|
||||||
protected abstract fun extractExpression(element: TElement): KtExpression?
|
protected abstract fun extractExpression(element: TElement): KtExpression?
|
||||||
@@ -273,7 +270,7 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
private object ElseHandler : ElementHandler<KtContainerNode>(KtContainerNode::class) {
|
private object ElseHandler : ElementHandler<KtContainerNode>(KtContainerNode::class) {
|
||||||
override fun accepts(element: KtContainerNode): Boolean {
|
override fun accepts(element: KtContainerNode): Boolean {
|
||||||
return element.node.elementType == KtNodeTypes.ELSE
|
return element.node.elementType == KtNodeTypes.ELSE
|
||||||
&& (element.parent as KtIfExpression).`else` !is KtIfExpression // filter out "else if"
|
&& (element.parent as KtIfExpression).`else` !is KtIfExpression // filter out "else if"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun elementInfo(element: KtContainerNode): String {
|
override fun elementInfo(element: KtContainerNode): String {
|
||||||
@@ -357,20 +354,18 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
override fun elementTooltip(element: KtExpression) = element.buildText(TextKind.TOOLTIP)
|
override fun elementTooltip(element: KtExpression) = element.buildText(TextKind.TOOLTIP)
|
||||||
|
|
||||||
private fun KtExpression.buildText(kind: TextKind): String {
|
private fun KtExpression.buildText(kind: TextKind): String {
|
||||||
with (parent as KtWhenEntry) {
|
with(parent as KtWhenEntry) {
|
||||||
if (isElse) {
|
if (isElse) {
|
||||||
return "else ->"
|
return "else ->"
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val condition = conditions.firstOrNull() ?: return "->"
|
val condition = conditions.firstOrNull() ?: return "->"
|
||||||
val firstConditionText = condition.buildText(kind)
|
val firstConditionText = condition.buildText(kind)
|
||||||
|
|
||||||
return if (conditions.size == 1) {
|
return if (conditions.size == 1) {
|
||||||
firstConditionText + " ->"
|
"$firstConditionText ->"
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
//TODO: show all conditions for tooltip
|
//TODO: show all conditions for tooltip
|
||||||
(if (firstConditionText.endsWith(ellipsis)) firstConditionText else firstConditionText + ",$ellipsis") + " ->"
|
(if (firstConditionText.endsWith(ellipsis)) firstConditionText else "$firstConditionText,$ellipsis") + " ->"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,10 +397,10 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
override fun elementTooltip(element: KtContainerNode) = element.buildText(TextKind.TOOLTIP)
|
override fun elementTooltip(element: KtContainerNode) = element.buildText(TextKind.TOOLTIP)
|
||||||
|
|
||||||
private fun KtContainerNode.buildText(kind: TextKind): String {
|
private fun KtContainerNode.buildText(kind: TextKind): String {
|
||||||
with (bodyOwner() as KtForExpression) {
|
with(bodyOwner() as KtForExpression) {
|
||||||
val parameterText = loopParameter?.nameAsName?.render() ?: destructuringDeclaration?.text ?: return "for"
|
val parameterText = loopParameter?.nameAsName?.render() ?: destructuringDeclaration?.text ?: return "for"
|
||||||
val collectionText = loopRange?.text ?: ""
|
val collectionText = loopRange?.text ?: ""
|
||||||
val text = (parameterText + " in " + collectionText).truncateEnd(kind)
|
val text = ("$parameterText in $collectionText").truncateEnd(kind)
|
||||||
return labelText() + "for($text)"
|
return labelText() + "for($text)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,7 +463,7 @@ class KotlinBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() {
|
|||||||
return if (length > maxLength) ellipsis + substring(length - maxLength - 1) else this
|
return if (length > maxLength) ellipsis + substring(length - maxLength - 1) else this
|
||||||
}
|
}
|
||||||
|
|
||||||
val ellipsis = "${Typography.ellipsis}"
|
const val ellipsis = "${Typography.ellipsis}"
|
||||||
|
|
||||||
fun KtContainerNode.bodyOwner(): KtExpression? {
|
fun KtContainerNode.bodyOwner(): KtExpression? {
|
||||||
return if (node.elementType == KtNodeTypes.BODY) parent as KtExpression else null
|
return if (node.elementType == KtNodeTypes.BODY) parent as KtExpression else null
|
||||||
|
|||||||
@@ -45,18 +45,20 @@ import kotlin.math.min
|
|||||||
|
|
||||||
@Throws(IntroduceRefactoringException::class)
|
@Throws(IntroduceRefactoringException::class)
|
||||||
fun selectElement(
|
fun selectElement(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
callback: (PsiElement?) -> Unit
|
callback: (PsiElement?) -> Unit
|
||||||
) = selectElement(editor, file, true, elementKinds, callback)
|
) = selectElement(editor, file, true, elementKinds, callback)
|
||||||
|
|
||||||
@Throws(IntroduceRefactoringException::class)
|
@Throws(IntroduceRefactoringException::class)
|
||||||
fun selectElement(editor: Editor,
|
fun selectElement(
|
||||||
file: KtFile,
|
editor: Editor,
|
||||||
failOnEmptySuggestion: Boolean,
|
file: KtFile,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
failOnEmptySuggestion: Boolean,
|
||||||
callback: (PsiElement?) -> Unit) {
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
|
callback: (PsiElement?) -> Unit
|
||||||
|
) {
|
||||||
if (editor.selectionModel.hasSelection()) {
|
if (editor.selectionModel.hasSelection()) {
|
||||||
var selectionStart = editor.selectionModel.selectionStart
|
var selectionStart = editor.selectionModel.selectionStart
|
||||||
var selectionEnd = editor.selectionModel.selectionEnd
|
var selectionEnd = editor.selectionModel.selectionEnd
|
||||||
@@ -64,8 +66,17 @@ fun selectElement(editor: Editor,
|
|||||||
var firstElement: PsiElement = file.findElementAt(selectionStart)!!
|
var firstElement: PsiElement = file.findElementAt(selectionStart)!!
|
||||||
var lastElement: PsiElement = file.findElementAt(selectionEnd - 1)!!
|
var lastElement: PsiElement = file.findElementAt(selectionEnd - 1)!!
|
||||||
|
|
||||||
if (PsiTreeUtil.getParentOfType(firstElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null
|
if (PsiTreeUtil.getParentOfType(
|
||||||
&& PsiTreeUtil.getParentOfType(lastElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null) {
|
firstElement,
|
||||||
|
KtLiteralStringTemplateEntry::class.java,
|
||||||
|
KtEscapeStringTemplateEntry::class.java
|
||||||
|
) == null
|
||||||
|
&& PsiTreeUtil.getParentOfType(
|
||||||
|
lastElement,
|
||||||
|
KtLiteralStringTemplateEntry::class.java,
|
||||||
|
KtEscapeStringTemplateEntry::class.java
|
||||||
|
) == null
|
||||||
|
) {
|
||||||
firstElement = firstElement.getNextSiblingIgnoringWhitespaceAndComments(true)!!
|
firstElement = firstElement.getNextSiblingIgnoringWhitespaceAndComments(true)!!
|
||||||
lastElement = lastElement.getPrevSiblingIgnoringWhitespaceAndComments(true)!!
|
lastElement = lastElement.getPrevSiblingIgnoringWhitespaceAndComments(true)!!
|
||||||
selectionStart = firstElement.textRange.startOffset
|
selectionStart = firstElement.textRange.startOffset
|
||||||
@@ -73,11 +84,10 @@ fun selectElement(editor: Editor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val element = elementKinds.asSequence()
|
val element = elementKinds.asSequence()
|
||||||
.mapNotNull { findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, it) }
|
.mapNotNull { findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, it) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
callback(element)
|
callback(element)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val offset = editor.caretModel.offset
|
val offset = editor.caretModel.offset
|
||||||
smartSelectElement(editor, file, offset, failOnEmptySuggestion, elementKinds, callback)
|
smartSelectElement(editor, file, offset, failOnEmptySuggestion, elementKinds, callback)
|
||||||
}
|
}
|
||||||
@@ -85,9 +95,9 @@ fun selectElement(editor: Editor,
|
|||||||
|
|
||||||
@Throws(IntroduceRefactoringException::class)
|
@Throws(IntroduceRefactoringException::class)
|
||||||
fun getSmartSelectSuggestions(
|
fun getSmartSelectSuggestions(
|
||||||
file: PsiFile,
|
file: PsiFile,
|
||||||
offset: Int,
|
offset: Int,
|
||||||
elementKind: CodeInsightUtils.ElementKind
|
elementKind: CodeInsightUtils.ElementKind
|
||||||
): List<KtElement> {
|
): List<KtElement> {
|
||||||
if (offset < 0) return emptyList()
|
if (offset < 0) return emptyList()
|
||||||
|
|
||||||
@@ -97,41 +107,38 @@ fun getSmartSelectSuggestions(
|
|||||||
|
|
||||||
val elements = ArrayList<KtElement>()
|
val elements = ArrayList<KtElement>()
|
||||||
while (element != null && !(element is KtBlockExpression && element.parent !is KtFunctionLiteral) &&
|
while (element != null && !(element is KtBlockExpression && element.parent !is KtFunctionLiteral) &&
|
||||||
element !is KtNamedFunction
|
element !is KtNamedFunction
|
||||||
&& element !is KtClassBody) {
|
&& element !is KtClassBody
|
||||||
|
) {
|
||||||
var addElement = false
|
var addElement = false
|
||||||
var keepPrevious = true
|
var keepPrevious = true
|
||||||
|
|
||||||
if (element is KtTypeElement) {
|
if (element is KtTypeElement) {
|
||||||
addElement =
|
addElement =
|
||||||
elementKind == CodeInsightUtils.ElementKind.TYPE_ELEMENT
|
elementKind == CodeInsightUtils.ElementKind.TYPE_ELEMENT
|
||||||
&& element.getParentOfTypeAndBranch<KtUserType>(true) { qualifier } == null
|
&& element.getParentOfTypeAndBranch<KtUserType>(true) { qualifier } == null
|
||||||
if (!addElement) {
|
if (!addElement) {
|
||||||
keepPrevious = false
|
keepPrevious = false
|
||||||
}
|
}
|
||||||
}
|
} else if (element is KtExpression && element !is KtStatementExpression) {
|
||||||
else if (element is KtExpression && element !is KtStatementExpression) {
|
|
||||||
addElement = elementKind == CodeInsightUtils.ElementKind.EXPRESSION
|
addElement = elementKind == CodeInsightUtils.ElementKind.EXPRESSION
|
||||||
|
|
||||||
if (addElement) {
|
if (addElement) {
|
||||||
if (element is KtParenthesizedExpression) {
|
if (element is KtParenthesizedExpression) {
|
||||||
addElement = false
|
addElement = false
|
||||||
}
|
} else if (KtPsiUtil.isLabelIdentifierExpression(element)) {
|
||||||
else if (KtPsiUtil.isLabelIdentifierExpression(element)) {
|
|
||||||
addElement = false
|
addElement = false
|
||||||
}
|
} else if (element.parent is KtQualifiedExpression) {
|
||||||
else if (element.parent is KtQualifiedExpression) {
|
|
||||||
val qualifiedExpression = element.parent as KtQualifiedExpression
|
val qualifiedExpression = element.parent as KtQualifiedExpression
|
||||||
if (qualifiedExpression.receiverExpression !== element) {
|
if (qualifiedExpression.receiverExpression !== element) {
|
||||||
addElement = false
|
addElement = false
|
||||||
}
|
}
|
||||||
}
|
} else if (element.parent is KtCallElement
|
||||||
else if (element.parent is KtCallElement
|
|| element.parent is KtThisExpression
|
||||||
|| element.parent is KtThisExpression
|
|| PsiTreeUtil.getParentOfType(element, KtSuperExpression::class.java) != null
|
||||||
|| PsiTreeUtil.getParentOfType(element, KtSuperExpression::class.java) != null) {
|
) {
|
||||||
addElement = false
|
addElement = false
|
||||||
}
|
} else if (element.parent is KtOperationExpression) {
|
||||||
else if (element.parent is KtOperationExpression) {
|
|
||||||
val operationExpression = element.parent as KtOperationExpression
|
val operationExpression = element.parent as KtOperationExpression
|
||||||
if (operationExpression.operationReference === element) {
|
if (operationExpression.operationReference === element) {
|
||||||
addElement = false
|
addElement = false
|
||||||
@@ -162,12 +169,12 @@ fun getSmartSelectSuggestions(
|
|||||||
|
|
||||||
@Throws(IntroduceRefactoringException::class)
|
@Throws(IntroduceRefactoringException::class)
|
||||||
private fun smartSelectElement(
|
private fun smartSelectElement(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
file: PsiFile,
|
file: PsiFile,
|
||||||
offset: Int,
|
offset: Int,
|
||||||
failOnEmptySuggestion: Boolean,
|
failOnEmptySuggestion: Boolean,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
callback: (PsiElement?) -> Unit
|
callback: (PsiElement?) -> Unit
|
||||||
) {
|
) {
|
||||||
val elements = elementKinds.flatMap { getSmartSelectSuggestions(file, offset, it) }
|
val elements = elementKinds.flatMap { getSmartSelectSuggestions(file, offset, it) }
|
||||||
if (elements.isEmpty()) {
|
if (elements.isEmpty()) {
|
||||||
@@ -189,7 +196,13 @@ private fun smartSelectElement(
|
|||||||
val list = JBList<PsiElement>(model)
|
val list = JBList<PsiElement>(model)
|
||||||
|
|
||||||
list.cellRenderer = object : DefaultListCellRenderer() {
|
list.cellRenderer = object : DefaultListCellRenderer() {
|
||||||
override fun getListCellRendererComponent(list: JList<*>, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component {
|
override fun getListCellRendererComponent(
|
||||||
|
list: JList<*>,
|
||||||
|
value: Any?,
|
||||||
|
index: Int,
|
||||||
|
isSelected: Boolean,
|
||||||
|
cellHasFocus: Boolean
|
||||||
|
): Component {
|
||||||
val rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
|
val rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
|
||||||
val element = value as KtElement?
|
val element = value as KtElement?
|
||||||
if (element!!.isValid) {
|
if (element!!.isValid) {
|
||||||
@@ -210,28 +223,28 @@ private fun smartSelectElement(
|
|||||||
|
|
||||||
var title = "Elements"
|
var title = "Elements"
|
||||||
if (elementKinds.size == 1) {
|
if (elementKinds.size == 1) {
|
||||||
when (elementKinds.iterator().next()) {
|
title = when (elementKinds.iterator().next()) {
|
||||||
CodeInsightUtils.ElementKind.EXPRESSION -> title = "Expressions"
|
CodeInsightUtils.ElementKind.EXPRESSION -> "Expressions"
|
||||||
CodeInsightUtils.ElementKind.TYPE_ELEMENT, CodeInsightUtils.ElementKind.TYPE_CONSTRUCTOR -> title = "Types"
|
CodeInsightUtils.ElementKind.TYPE_ELEMENT, CodeInsightUtils.ElementKind.TYPE_CONSTRUCTOR -> "Types"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JBPopupFactory.getInstance()
|
JBPopupFactory.getInstance()
|
||||||
.createListPopupBuilder(list)
|
.createListPopupBuilder(list)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setMovable(false)
|
.setMovable(false)
|
||||||
.setResizable(false)
|
.setResizable(false)
|
||||||
.setRequestFocus(true)
|
.setRequestFocus(true)
|
||||||
.setItemChoosenCallback { callback(list.selectedValue as KtElement) }
|
.setItemChoosenCallback { callback(list.selectedValue as KtElement) }
|
||||||
.addListener(
|
.addListener(
|
||||||
object : JBPopupAdapter() {
|
object : JBPopupAdapter() {
|
||||||
override fun onClosed(event: LightweightWindowEvent) {
|
override fun onClosed(event: LightweightWindowEvent) {
|
||||||
highlighter.dropHighlight()
|
highlighter.dropHighlight()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.createPopup()
|
.createPopup()
|
||||||
.showInBestPositionFor(editor)
|
.showInBestPositionFor(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getExpressionShortText(element: KtElement): String {
|
fun getExpressionShortText(element: KtElement): String {
|
||||||
@@ -244,11 +257,11 @@ fun getExpressionShortText(element: KtElement): String {
|
|||||||
|
|
||||||
@Throws(IntroduceRefactoringException::class)
|
@Throws(IntroduceRefactoringException::class)
|
||||||
private fun findElement(
|
private fun findElement(
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
failOnNoExpression: Boolean,
|
failOnNoExpression: Boolean,
|
||||||
elementKind: CodeInsightUtils.ElementKind
|
elementKind: CodeInsightUtils.ElementKind
|
||||||
): PsiElement? {
|
): PsiElement? {
|
||||||
var element = CodeInsightUtils.findElement(file, startOffset, endOffset, elementKind)
|
var element = CodeInsightUtils.findElement(file, startOffset, endOffset, elementKind)
|
||||||
if (element == null && elementKind == CodeInsightUtils.ElementKind.EXPRESSION) {
|
if (element == null && elementKind == CodeInsightUtils.ElementKind.EXPRESSION) {
|
||||||
|
|||||||
Reference in New Issue
Block a user