Used analyze to multiple elements for copy/paste

This commit is contained in:
Valentin Kipyatkov
2016-09-05 21:42:27 +03:00
parent 5a5a6d47b3
commit dd6b5ab5a7
2 changed files with 39 additions and 23 deletions
@@ -50,6 +50,7 @@ internal class ResolutionFacadeImpl(
} }
override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext { override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
if (elements.isEmpty()) return BindingContext.EMPTY
val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java) val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java)
return resolveElementCache.resolveToElements(elements, bodyResolveMode) return resolveElementCache.resolveToElements(elements, bodyResolveMode)
} }
@@ -33,7 +33,6 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedShortening import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedShortening
@@ -48,11 +47,13 @@ import org.jetbrains.kotlin.idea.util.getFileResolutionScope
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.findFunction import org.jetbrains.kotlin.resolve.scopes.utils.findFunction
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
@@ -122,28 +123,35 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray endOffsets: IntArray
): List<KotlinReferenceData> { ): List<KotlinReferenceData> {
val ranges = toTextRanges(startOffsets, endOffsets)
val elementsByRange = ranges.associateBy({ it }, { file.elementsInRange(it).filterIsInstance<KtElement>() })
val allElementsToResolve = elementsByRange.values.flatMap { it }.flatMap { it.collectDescendantsOfType<KtElement>() }
val bindingContext = file.getResolutionFacade().analyze(allElementsToResolve, BodyResolveMode.PARTIAL)
val result = ArrayList<KotlinReferenceData>() val result = ArrayList<KotlinReferenceData>()
for (range in toTextRanges(startOffsets, endOffsets)) { for ((range, elements) in elementsByRange) {
for (element in file.elementsInRange(range)) { for (element in elements) {
result.addReferenceDataInsideElement(element, file, range.start, startOffsets, endOffsets) result.addReferenceDataInsideElement(element, file, range.start, startOffsets, endOffsets, bindingContext)
} }
} }
return result return result
} }
private fun MutableCollection<KotlinReferenceData>.addReferenceDataInsideElement( private fun MutableCollection<KotlinReferenceData>.addReferenceDataInsideElement(
element: PsiElement, element: KtElement,
file: KtFile, file: KtFile,
startOffset: Int, startOffset: Int,
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray endOffsets: IntArray,
bindingContext: BindingContext
) { ) {
if (PsiTreeUtil.getNonStrictParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null) return if (PsiTreeUtil.getNonStrictParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null) return
element.forEachDescendantOfType<KtElement>(canGoInside = { it.javaClass as Class<*> !in IGNORE_REFERENCES_INSIDE }) { element -> element.forEachDescendantOfType<KtElement>(canGoInside = { it.javaClass as Class<*> !in IGNORE_REFERENCES_INSIDE }) { element ->
val reference = element.mainReference ?: return@forEachDescendantOfType val reference = element.mainReference ?: return@forEachDescendantOfType
val descriptors = resolveReference(reference) val descriptors = resolveReference(reference, bindingContext)
//check whether this reference is unambiguous //check whether this reference is unambiguous
if (reference !is KtMultiReference<*> && descriptors.size > 1) return@forEachDescendantOfType if (reference !is KtMultiReference<*> && descriptors.size > 1) return@forEachDescendantOfType
@@ -183,7 +191,7 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
val referenceData = values.single().data val referenceData = values.single().data
processReferenceData(project, file, bounds.getStartOffset(), referenceData) processReferenceData(project, file, bounds.startOffset, referenceData)
} }
fun processReferenceData(project: Project, file: KtFile, blockStart: Int, referenceData: Array<KotlinReferenceData>) { fun processReferenceData(project: Project, file: KtFile, blockStart: Int, referenceData: Array<KotlinReferenceData>) {
@@ -202,11 +210,20 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
private fun findReferencesToRestore(file: PsiFile, blockStart: Int, referenceData: Array<out KotlinReferenceData>): List<ReferenceToRestoreData> { private fun findReferencesToRestore(file: PsiFile, blockStart: Int, referenceData: Array<out KotlinReferenceData>): List<ReferenceToRestoreData> {
if (file !is KtFile) return listOf() if (file !is KtFile) return listOf()
val references = referenceData.map { it to findReference(it, file, blockStart) }
val bindingContext = try {
file.getResolutionFacade().analyze(references.mapNotNull { it.second?.element }, BodyResolveMode.PARTIAL)
}
catch (e: Throwable) {
LOG.error("Failed to analyze references after copy paste", e)
return emptyList()
}
val fileResolutionScope = file.getResolutionFacade().getFileResolutionScope(file) val fileResolutionScope = file.getResolutionFacade().getFileResolutionScope(file)
return referenceData.mapNotNull { return references.mapNotNull { pair ->
val reference = findReference(it, file, blockStart) val data = pair.first
val reference = pair.second
if (reference != null) if (reference != null)
createReferenceToRestoreData(reference, it, file, fileResolutionScope) createReferenceToRestoreData(reference, data, file, fileResolutionScope, bindingContext)
else else
null null
} }
@@ -227,7 +244,13 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
return null return null
} }
private fun createReferenceToRestoreData(reference: KtReference, refData: KotlinReferenceData, file: KtFile, fileResolutionScope: LexicalScope): ReferenceToRestoreData? { private fun createReferenceToRestoreData(
reference: KtReference,
refData: KotlinReferenceData,
file: KtFile,
fileResolutionScope: LexicalScope,
bindingContext: BindingContext
): ReferenceToRestoreData? {
val originalFqName = FqName(refData.fqName) val originalFqName = FqName(refData.fqName)
val name = originalFqName.shortName() val name = originalFqName.shortName()
@@ -242,13 +265,7 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
} }
} }
val referencedDescriptors = try { val referencedDescriptors = resolveReference(reference, bindingContext)
resolveReference(reference)
}
catch (e: Throwable) {
LOG.error("Failed to analyze reference ($reference) after copy paste", e)
return null
}
val referencedFqNames = referencedDescriptors val referencedFqNames = referencedDescriptors
.filterNot { ErrorUtils.isError(it) } .filterNot { ErrorUtils.isError(it) }
.mapNotNull { it.importableFqName } .mapNotNull { it.importableFqName }
@@ -263,10 +280,8 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
return ReferenceToRestoreData(reference, refData) return ReferenceToRestoreData(reference, refData)
} }
private fun resolveReference(reference: KtReference): Collection<DeclarationDescriptor> { private fun resolveReference(reference: KtReference, bindingContext: BindingContext): Collection<DeclarationDescriptor> {
val element = reference.element val element = reference.element
val bindingContext = element.analyze() //TODO: we could use partial body resolve for all references together
if (element is KtNameReferenceExpression && reference is KtSimpleNameReference) { if (element is KtNameReferenceExpression && reference is KtSimpleNameReference) {
bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element]
?.let { return listOf(it) } ?.let { return listOf(it) }
@@ -290,7 +305,7 @@ class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferen
val fqName = FqName(refData.fqName) val fqName = FqName(refData.fqName)
if (!refData.kind.isExtension() && reference is KtSimpleNameReference) { if (!refData.kind.isExtension() && reference is KtSimpleNameReference) {
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.getElement(), file) val pointer = smartPointerManager.createSmartPsiElementPointer(reference.element, file)
bindingRequests.add(BindingRequest(pointer, fqName)) bindingRequests.add(BindingRequest(pointer, fqName))
} }