Apply LiftReturnOrAssignmentInspection on idea

This commit is contained in:
Dmitry Gridin
2019-06-14 19:55:17 +07:00
parent 05a132357b
commit 46d8f45c11
9 changed files with 592 additions and 522 deletions
@@ -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)
} }
} }
@@ -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 */
@@ -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)
} }
} }
@@ -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
} }
} }
} }
@@ -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)
@@ -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) {