|
|
|
@@ -14,6 +14,8 @@ import org.jetbrains.jet.renderer.DescriptorRenderer;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import com.intellij.psi.util.PsiTreeUtil
|
|
|
|
|
import com.intellij.util.containers.HashMap
|
|
|
|
|
import java.util.ArrayList
|
|
|
|
|
|
|
|
|
|
public object ShortenReferences {
|
|
|
|
|
public fun process(element: JetElement) {
|
|
|
|
@@ -23,59 +25,131 @@ public object ShortenReferences {
|
|
|
|
|
public fun process(elements: Iterable<JetElement>) {
|
|
|
|
|
val first = elements.firstOrNull()
|
|
|
|
|
if (first == null) return
|
|
|
|
|
val file = first.getContainingFile() as JetFile
|
|
|
|
|
|
|
|
|
|
val visitor = Visitor(first.getContainingFile() as JetFile)
|
|
|
|
|
// first resolve all qualified references - optimization
|
|
|
|
|
val resolveAllVisitor = ResolveAllReferencesVisitor(file)
|
|
|
|
|
processElements(elements, resolveAllVisitor)
|
|
|
|
|
|
|
|
|
|
val shortenTypesVisitor = ShortenTypesVisitor(file, resolveAllVisitor.result)
|
|
|
|
|
processElements(elements, shortenTypesVisitor)
|
|
|
|
|
shortenTypesVisitor.finish()
|
|
|
|
|
|
|
|
|
|
processElements(elements, ShortenQualifiedExpressionsVisitor(file, resolveAllVisitor.result))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun processElements(elements: Iterable<JetElement>, visitor: JetVisitorVoid) {
|
|
|
|
|
for (element in elements) {
|
|
|
|
|
element.accept(visitor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Visitor(val file: JetFile) : JetVisitorVoid() {
|
|
|
|
|
private val resolveSession : CancelableResolveSession
|
|
|
|
|
get() = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(file)
|
|
|
|
|
private class ResolveAllReferencesVisitor(file: JetFile) : JetVisitorVoid() {
|
|
|
|
|
private val resolveSession = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(file)
|
|
|
|
|
private val resolveMap = HashMap<JetReferenceExpression, BindingContext>()
|
|
|
|
|
|
|
|
|
|
public val result: Map<JetReferenceExpression, BindingContext> = resolveMap
|
|
|
|
|
|
|
|
|
|
override fun visitJetElement(element : JetElement) {
|
|
|
|
|
acceptChildren(element)
|
|
|
|
|
element.acceptChildren(this)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun visitUserType(userType: JetUserType) {
|
|
|
|
|
val resultElement = processType(userType)
|
|
|
|
|
acceptChildren(resultElement)
|
|
|
|
|
userType.acceptChildren(this)
|
|
|
|
|
|
|
|
|
|
if (userType.getQualifier() != null) {
|
|
|
|
|
val referenceExpression = userType.getReferenceExpression()
|
|
|
|
|
if (referenceExpression != null) {
|
|
|
|
|
resolveMap[referenceExpression] = resolveSession.resolveToElement(referenceExpression)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun processType(userType: JetUserType): PsiElement {
|
|
|
|
|
if (userType.getQualifier() == null) return userType
|
|
|
|
|
override fun visitDotQualifiedExpression(expression: JetDotQualifiedExpression) {
|
|
|
|
|
expression.acceptChildren(this)
|
|
|
|
|
|
|
|
|
|
val bindingContext = resolveSession.resolveToElement(userType)
|
|
|
|
|
val target = bindingContext.get(BindingContext.REFERENCE_TARGET, userType.getReferenceExpression())
|
|
|
|
|
if (target == null) return userType
|
|
|
|
|
val referenceExpression = referenceExpression(expression.getSelectorExpression())
|
|
|
|
|
if (referenceExpression != null) {
|
|
|
|
|
resolveMap[referenceExpression] = resolveSession.resolveToElement(referenceExpression)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ShortenTypesVisitor(val file: JetFile, val resolveMap: Map<JetReferenceExpression, BindingContext>) : JetVisitorVoid() {
|
|
|
|
|
private val resolveSession : CancelableResolveSession
|
|
|
|
|
get() = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(file)
|
|
|
|
|
|
|
|
|
|
private val typesToShorten = ArrayList<JetUserType>()
|
|
|
|
|
|
|
|
|
|
public fun finish() {
|
|
|
|
|
for (userType in typesToShorten) {
|
|
|
|
|
shortenType(userType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun bindingContext(expression: JetReferenceExpression): BindingContext = resolveMap[expression]!!
|
|
|
|
|
|
|
|
|
|
override fun visitJetElement(element : JetElement) {
|
|
|
|
|
element.acceptChildren(this)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun visitUserType(userType: JetUserType) {
|
|
|
|
|
userType.getTypeArgumentList()?.accept(this)
|
|
|
|
|
|
|
|
|
|
if (canShortenType(userType)) {
|
|
|
|
|
typesToShorten.add(userType)
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
userType.getQualifier()?.accept(this)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun canShortenType(userType: JetUserType): Boolean {
|
|
|
|
|
if (userType.getQualifier() == null) return false
|
|
|
|
|
val referenceExpression = userType.getReferenceExpression()
|
|
|
|
|
if (referenceExpression == null) return false
|
|
|
|
|
|
|
|
|
|
val target = bindingContext(referenceExpression).get(BindingContext.REFERENCE_TARGET, referenceExpression)
|
|
|
|
|
if (target == null) return false
|
|
|
|
|
// references to nested classes should be shortened when visiting qualifier
|
|
|
|
|
if (target.getContainingDeclaration() is ClassDescriptor) return userType
|
|
|
|
|
if (target.getContainingDeclaration() is ClassDescriptor) return false
|
|
|
|
|
|
|
|
|
|
val typeReference = PsiTreeUtil.getParentOfType(userType, javaClass<JetTypeReference>())!!
|
|
|
|
|
val scope = bindingContext.get(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference)!!
|
|
|
|
|
val scope = resolveSession.resolveToElement(typeReference).get(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference)!!
|
|
|
|
|
val name = target.getName()
|
|
|
|
|
val targetByName = scope.getClassifier(name) ?: scope.getPackage(name)
|
|
|
|
|
if (target == targetByName) {
|
|
|
|
|
return shortenType(userType)
|
|
|
|
|
if (targetByName == null) {
|
|
|
|
|
addImportIfNeeded(target, file)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
else if (targetByName == null) {
|
|
|
|
|
addImportIfNeeded(target)
|
|
|
|
|
return shortenType(userType)
|
|
|
|
|
else if (target.asString() == targetByName.asString()) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// leave FQ name
|
|
|
|
|
return userType
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun shortenType(userType: JetUserType): JetUserType {
|
|
|
|
|
private fun shortenType(userType: JetUserType) {
|
|
|
|
|
val referenceExpression = userType.getReferenceExpression()
|
|
|
|
|
if (referenceExpression == null) return userType
|
|
|
|
|
if (referenceExpression == null) return
|
|
|
|
|
val typeArgumentList = userType.getTypeArgumentList()
|
|
|
|
|
val text = referenceExpression.getText() + (if (typeArgumentList != null) typeArgumentList.getText() else "")
|
|
|
|
|
val newUserType = JetPsiFactory.createType(userType.getProject(), text).getTypeElement() as JetUserType
|
|
|
|
|
return userType.replace(newUserType) as JetUserType
|
|
|
|
|
val newUserType = JetPsiFactory.createType(userType.getProject(), text).getTypeElement()!!
|
|
|
|
|
userType.replace(newUserType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ShortenQualifiedExpressionsVisitor(val file: JetFile, val resolveMap: Map<JetReferenceExpression, BindingContext>) : JetVisitorVoid() {
|
|
|
|
|
private val resolveSession : CancelableResolveSession
|
|
|
|
|
get() = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(file)
|
|
|
|
|
|
|
|
|
|
private fun bindingContext(expression: JetReferenceExpression): BindingContext
|
|
|
|
|
= resolveMap[expression] ?: resolveSession.resolveToElement(expression) // binding context can be absent in the map if some references have been shortened already
|
|
|
|
|
|
|
|
|
|
override fun visitJetElement(element : JetElement) {
|
|
|
|
|
acceptChildren(element)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun visitDotQualifiedExpression(expression: JetDotQualifiedExpression) {
|
|
|
|
@@ -90,22 +164,22 @@ public object ShortenReferences {
|
|
|
|
|
if (calleeExpression is JetReferenceExpression) {
|
|
|
|
|
val targetClass = instantiatedClass(calleeExpression)
|
|
|
|
|
if (targetClass != null) {
|
|
|
|
|
return shortenIfPossible(qualifiedExpression, targetClass)
|
|
|
|
|
return shortenIfPossible(qualifiedExpression, targetClass, bindingContext(calleeExpression))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (selectorExpression is JetReferenceExpression) {
|
|
|
|
|
val bindingContext = resolveSession.resolveToElement(selectorExpression)
|
|
|
|
|
val bindingContext = bindingContext(selectorExpression)
|
|
|
|
|
val target = bindingContext.get(BindingContext.REFERENCE_TARGET, selectorExpression)
|
|
|
|
|
if (target is ClassDescriptor || target is PackageViewDescriptor) { //TODO: should we ever add imports to real packages?
|
|
|
|
|
return shortenIfPossible(qualifiedExpression, target)
|
|
|
|
|
return shortenIfPossible(qualifiedExpression, target, bindingContext)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return qualifiedExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun instantiatedClass(calleeExpression: JetReferenceExpression): ClassDescriptor? {
|
|
|
|
|
val bindingContext = resolveSession.resolveToElement(calleeExpression)
|
|
|
|
|
val bindingContext = bindingContext(calleeExpression)
|
|
|
|
|
val target = bindingContext.get(BindingContext.REFERENCE_TARGET, calleeExpression)
|
|
|
|
|
if (target != null) {
|
|
|
|
|
if (target is ConstructorDescriptor) {
|
|
|
|
@@ -132,47 +206,40 @@ public object ShortenReferences {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun shortenIfPossible(qualifiedExpression: JetDotQualifiedExpression, targetClassOrPackage: DeclarationDescriptor): PsiElement {
|
|
|
|
|
private fun shortenIfPossible(qualifiedExpression: JetDotQualifiedExpression, targetClassOrPackage: DeclarationDescriptor, bindingContext: BindingContext): PsiElement {
|
|
|
|
|
// references to nested classes should be shortened when visiting qualifier
|
|
|
|
|
if (targetClassOrPackage.getContainingDeclaration() is ClassDescriptor) return qualifiedExpression
|
|
|
|
|
|
|
|
|
|
var bindingContext = resolveSession.resolveToElement(qualifiedExpression)
|
|
|
|
|
|
|
|
|
|
val referenceExpression = referenceExpression(qualifiedExpression.getSelectorExpression()!!)
|
|
|
|
|
val referenceExpression = referenceExpression(qualifiedExpression.getSelectorExpression())!!
|
|
|
|
|
val resolveBefore = resolveState(referenceExpression, bindingContext)
|
|
|
|
|
|
|
|
|
|
val copy = qualifiedExpression.copy()
|
|
|
|
|
|
|
|
|
|
val selectorExpression = qualifiedExpression.getSelectorExpression()!!
|
|
|
|
|
val newExpression = qualifiedExpression.replace(selectorExpression) as JetExpression
|
|
|
|
|
val newReferenceExpression = referenceExpression(newExpression)
|
|
|
|
|
val newReferenceExpression = referenceExpression(newExpression)!!
|
|
|
|
|
|
|
|
|
|
bindingContext = resolveSession.resolveToElement(newReferenceExpression)
|
|
|
|
|
val resolveAfter = resolveState(newReferenceExpression, bindingContext)
|
|
|
|
|
val newBindingContext = resolveSession.resolveToElement(newReferenceExpression)
|
|
|
|
|
val resolveAfter = resolveState(newReferenceExpression, newBindingContext)
|
|
|
|
|
if (resolveAfter != null) {
|
|
|
|
|
if (resolveBefore == resolveAfter) return newExpression
|
|
|
|
|
return newExpression.replace(copy) // revert shortening
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addImportIfNeeded(targetClassOrPackage)
|
|
|
|
|
addImportIfNeeded(targetClassOrPackage, file)
|
|
|
|
|
return newExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun resolveState(referenceExpression: JetReferenceExpression, bindingContext: BindingContext): Any? {
|
|
|
|
|
val target = bindingContext.get(BindingContext.REFERENCE_TARGET, referenceExpression)
|
|
|
|
|
if (target != null) return DescriptorRenderer.TEXT.render(target.getOriginal())
|
|
|
|
|
if (target != null) return target.asString()
|
|
|
|
|
|
|
|
|
|
val targets = bindingContext.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression)
|
|
|
|
|
if (targets != null) return HashSet(targets.map{DescriptorRenderer.TEXT.render(it!!)})
|
|
|
|
|
if (targets != null) return HashSet(targets.map{it!!.asString()})
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//TODO: do we need this "IfNeeded" check?
|
|
|
|
|
private fun addImportIfNeeded(descriptor : DeclarationDescriptor) {
|
|
|
|
|
ImportInsertHelper.addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we do not use standard PsiElement.acceptChildren because it won't work correctly if the element is replaced by the visitor
|
|
|
|
|
private fun acceptChildren(element: PsiElement) {
|
|
|
|
|
var child = element.getFirstChild()
|
|
|
|
@@ -184,10 +251,18 @@ public object ShortenReferences {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun referenceExpression(selectorExpression: JetExpression) = if (selectorExpression is JetCallExpression)
|
|
|
|
|
selectorExpression.getCalleeExpression() as JetReferenceExpression
|
|
|
|
|
else
|
|
|
|
|
selectorExpression as JetReferenceExpression
|
|
|
|
|
private fun referenceExpression(selectorExpression: JetExpression?): JetReferenceExpression?
|
|
|
|
|
= if (selectorExpression is JetCallExpression)
|
|
|
|
|
selectorExpression.getCalleeExpression() as? JetReferenceExpression
|
|
|
|
|
else
|
|
|
|
|
selectorExpression as? JetReferenceExpression
|
|
|
|
|
|
|
|
|
|
private fun DeclarationDescriptor.asString() = DescriptorRenderer.TEXT.render(this)
|
|
|
|
|
|
|
|
|
|
//TODO: do we need this "IfNeeded" check?
|
|
|
|
|
private fun addImportIfNeeded(descriptor: DeclarationDescriptor, file: JetFile) {
|
|
|
|
|
ImportInsertHelper.addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//TODO: how about such function in stdlib?
|
|
|
|
|