Refactored to be more type safe

This commit is contained in:
Valentin Kipyatkov
2015-11-23 17:43:42 +03:00
parent 931463af0d
commit fc34557a7a
3 changed files with 22 additions and 28 deletions
@@ -17,12 +17,16 @@
package org.jetbrains.kotlin.idea.liveTemplates.macro
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.UserDataHolder
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.BindingContext
class AnyVariableMacro : BaseKotlinVariableMacro() {
class AnyVariableMacro : BaseKotlinVariableMacro<Unit>() {
override fun getName() = "kotlinAnyVariable"
override fun getPresentableName() = "kotlinAnyVariable()"
override fun isSuitable(variableDescriptor: VariableDescriptor, project: Project, userData: UserDataHolder) = true
override fun initState(contextElement: KtElement, bindingContext: BindingContext) {
}
override fun isSuitable(variableDescriptor: VariableDescriptor, project: Project, state: Unit) = true
}
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.liveTemplates.macro
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.template.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.UserDataHolder
import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
@@ -38,9 +37,8 @@ import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.util.slicedMap.UserDataHolderImpl
abstract class BaseKotlinVariableMacro : Macro() {
abstract class BaseKotlinVariableMacro<TState> : Macro() {
private fun getVariables(params: Array<Expression>, context: ExpressionContext): Collection<VariableDescriptor> {
if (params.size != 0) return emptyList()
@@ -60,23 +58,21 @@ abstract class BaseKotlinVariableMacro : Macro() {
return descriptor !is DeclarationDescriptorWithVisibility || descriptor.isVisible(contextElement, null, bindingContext, resolutionFacade)
}
val userData = UserDataHolderImpl()
initUserData(userData, contextElement, bindingContext)
val state = initState(contextElement, bindingContext)
val helper = ReferenceVariantsHelper(bindingContext, resolutionFacade, resolutionFacade.moduleDescriptor, ::isVisible)
return helper
.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, DescriptorKindFilter.VARIABLES, { true })
.map { it as VariableDescriptor }
.filter { isSuitable(it, project, userData) }
.filter { isSuitable(it, project, state) }
}
protected open fun initUserData(userData: UserDataHolder, contextElement: KtElement, bindingContext: BindingContext) {
}
protected abstract fun initState(contextElement: KtElement, bindingContext: BindingContext): TState
protected abstract fun isSuitable(
variableDescriptor: VariableDescriptor,
project: Project,
userData: UserDataHolder): Boolean
state: TState): Boolean
override fun calculateResult(params: Array<Expression>, context: ExpressionContext): Result? {
val vars = getVariables(params, context)
@@ -17,8 +17,6 @@
package org.jetbrains.kotlin.idea.liveTemplates.macro
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.ExpectedInfo
@@ -31,36 +29,32 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
class SuitableVariableMacro : BaseKotlinVariableMacro() {
private companion object {
val EXPECTED_INFOS_KEY = Key<Collection<ExpectedInfo>>("EXPECTED_INFOS_KEY")
val SMART_CAST_CALCULATOR_KEY = Key<SmartCastCalculator>("SMART_CAST_CALCULATOR_KEY")
}
class SuitableVariableMacro : BaseKotlinVariableMacro<SuitableVariableMacro.State?>() {
class State(val expectedInfos: Collection<ExpectedInfo>, val smartCastCalculator: SmartCastCalculator)
override fun getName() = "kotlinVariable"
override fun getPresentableName() = "kotlinVariable()"
override fun initUserData(userData: UserDataHolder, contextElement: KtElement, bindingContext: BindingContext) {
override fun initState(contextElement: KtElement, bindingContext: BindingContext): State? {
val resolutionFacade = contextElement.getResolutionFacade()
if (contextElement is KtNameReferenceExpression) {
val callTypeAndReceiver = CallTypeAndReceiver.detect(contextElement)
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade).calculate(contextElement)
if (expectedInfos.isNotEmpty()) {
userData.putUserData(EXPECTED_INFOS_KEY, expectedInfos)
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
val smartCastCalculator = SmartCastCalculator(bindingContext, scope.ownerDescriptor, contextElement, null, resolutionFacade)
userData.putUserData(SMART_CAST_CALCULATOR_KEY, smartCastCalculator)
return State(expectedInfos, smartCastCalculator)
}
}
}
return null
}
override fun isSuitable(variableDescriptor: VariableDescriptor, project: Project, userData: UserDataHolder): Boolean {
val expectedInfos = userData.getUserData(EXPECTED_INFOS_KEY) ?: return true
val smartCastCalculator = userData.getUserData(SMART_CAST_CALCULATOR_KEY)!!
val types = smartCastCalculator.types(variableDescriptor)
return expectedInfos.any { expectedInfo -> types.any { expectedInfo.filter.matchingSubstitutor(FuzzyType(it, emptyList())) != null } }
override fun isSuitable(variableDescriptor: VariableDescriptor, project: Project, state: State?): Boolean {
if (state == null) return true
val types = state.smartCastCalculator.types(variableDescriptor)
return state.expectedInfos.any { expectedInfo -> types.any { expectedInfo.filter.matchingSubstitutor(FuzzyType(it, emptyList())) != null } }
}
}