Rewritten BaseKotlinVariableMacro
This commit is contained in:
@@ -24,104 +24,66 @@ import com.intellij.codeInsight.template.Macro
|
|||||||
import com.intellij.codeInsight.template.Result
|
import com.intellij.codeInsight.template.Result
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.PsiNamedElement
|
import com.intellij.psi.PsiNamedElement
|
||||||
import com.intellij.util.containers.ContainerUtil
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||||
import org.jetbrains.kotlin.idea.core.IterableTypesDetection
|
import org.jetbrains.kotlin.idea.core.IterableTypesDetection
|
||||||
import org.jetbrains.kotlin.idea.core.IterableTypesDetector
|
import org.jetbrains.kotlin.idea.core.IterableTypesDetector
|
||||||
|
import org.jetbrains.kotlin.idea.core.isVisible
|
||||||
|
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallableWithImplicitReceiver
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class BaseKotlinVariableMacro : Macro() {
|
abstract class BaseKotlinVariableMacro : Macro() {
|
||||||
private fun getVariables(params: Array<Expression>, context: ExpressionContext): Collection<KtNamedDeclaration> {
|
private fun getVariables(params: Array<Expression>, context: ExpressionContext): Collection<PsiNamedElement> {
|
||||||
if (params.size != 0) return emptyList()
|
if (params.size != 0) return emptyList()
|
||||||
|
|
||||||
val project = context.project
|
val project = context.project
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
val psiDocumentManager = PsiDocumentManager.getInstance(project)
|
||||||
|
psiDocumentManager.commitAllDocuments()
|
||||||
|
|
||||||
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(context.editor!!.document) as? KtFile ?: return emptyList()
|
val psiFile = psiDocumentManager.getPsiFile(context.editor!!.document) as? KtFile ?: return emptyList()
|
||||||
|
|
||||||
val contextExpression = findContextExpression(psiFile, context.startOffset) ?: return emptyList()
|
val contextElement = psiFile.findElementAt(context.startOffset)?.getNonStrictParentOfType<KtElement>() ?: return emptyList()
|
||||||
|
|
||||||
val resolutionFacade = contextExpression.getResolutionFacade()
|
val resolutionFacade = psiFile.getResolutionFacade()
|
||||||
|
|
||||||
val bindingContext = resolutionFacade.analyze(contextExpression, BodyResolveMode.FULL)
|
val bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL_FOR_COMPLETION)
|
||||||
val scope = contextExpression.getResolutionScope(bindingContext, resolutionFacade)
|
|
||||||
|
|
||||||
|
fun isVisible(descriptor: DeclarationDescriptor): Boolean {
|
||||||
|
return descriptor !is DeclarationDescriptorWithVisibility || descriptor.isVisible(contextElement, null, bindingContext, resolutionFacade)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
val detector = resolutionFacade.getIdeService(IterableTypesDetection::class.java).createDetector(scope)
|
val detector = resolutionFacade.getIdeService(IterableTypesDetection::class.java).createDetector(scope)
|
||||||
|
|
||||||
val dataFlowInfo = bindingContext.getDataFlowInfo(contextExpression)
|
val helper = ReferenceVariantsHelper(bindingContext, resolutionFacade, resolutionFacade.moduleDescriptor, ::isVisible)
|
||||||
|
val variants = helper
|
||||||
|
.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, DescriptorKindFilter.VARIABLES, { true })
|
||||||
|
.filter { isSuitable(it as VariableDescriptor, project, detector) }
|
||||||
|
|
||||||
val filteredDescriptors = ArrayList<VariableDescriptor>()
|
val declarations = ArrayList<PsiNamedElement>()
|
||||||
for (declarationDescriptor in getAllVariables(scope)) {
|
for (descriptor in variants) {
|
||||||
if (declarationDescriptor is VariableDescriptor) {
|
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? PsiNamedElement ?: continue
|
||||||
|
declarations.add(declaration)
|
||||||
if (declarationDescriptor.extensionReceiverParameter != null && declarationDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope, bindingContext, dataFlowInfo).isEmpty()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSuitable(declarationDescriptor, project, detector)) {
|
|
||||||
filteredDescriptors.add(declarationDescriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val declarations = ArrayList<KtNamedDeclaration>()
|
|
||||||
for (declarationDescriptor in filteredDescriptors) {
|
|
||||||
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor)
|
|
||||||
assert(declaration == null || declaration is PsiNamedElement)
|
|
||||||
|
|
||||||
if (declaration is KtProperty) {
|
|
||||||
declarations.add(declaration)
|
|
||||||
}
|
|
||||||
else if (declaration is KtParameter) {
|
|
||||||
declarations.add(declaration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return declarations
|
return declarations
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAllVariables(scope: LexicalScope): Collection<DeclarationDescriptor> {
|
|
||||||
val result = ContainerUtil.newArrayList<DeclarationDescriptor>()
|
|
||||||
result.addAll(scope.collectDescriptorsFiltered(DescriptorKindFilter.VARIABLES, MemberScope.ALL_NAME_FILTER))
|
|
||||||
for (implicitReceiver in scope.getImplicitReceiversHierarchy()) {
|
|
||||||
result.addAll(DescriptorUtils.getAllDescriptors(implicitReceiver.type.memberScope))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun isSuitable(
|
protected abstract fun isSuitable(
|
||||||
variableDescriptor: VariableDescriptor,
|
variableDescriptor: VariableDescriptor,
|
||||||
project: Project,
|
project: Project,
|
||||||
iterableTypesDetector: IterableTypesDetector): Boolean
|
iterableTypesDetector: IterableTypesDetector): Boolean
|
||||||
|
|
||||||
private fun findContextExpression(psiFile: PsiFile, startOffset: Int): KtExpression? {
|
|
||||||
var e = psiFile.findElementAt(startOffset)
|
|
||||||
while (e != null) {
|
|
||||||
if (e is KtExpression) {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
e = e.parent
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculateResult(params: Array<Expression>, context: ExpressionContext): Result? {
|
override fun calculateResult(params: Array<Expression>, context: ExpressionContext): Result? {
|
||||||
val vars = getVariables(params, context)
|
val vars = getVariables(params, context)
|
||||||
if (vars.isEmpty()) return null
|
if (vars.isEmpty()) return null
|
||||||
|
|||||||
Reference in New Issue
Block a user