KotlinCopyPasteReferenceProcessor: cleanup code

This commit is contained in:
Dmitry Gridin
2019-08-14 13:54:00 +07:00
parent bd5476082b
commit 693f42f33e
@@ -74,8 +74,8 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
private val LOG = Logger.getInstance(KotlinCopyPasteReferenceProcessor::class.java) private val LOG = Logger.getInstance(KotlinCopyPasteReferenceProcessor::class.java)
private val IGNORE_REFERENCES_INSIDE: Array<Class<out KtElement>> = arrayOf( private val IGNORE_REFERENCES_INSIDE: Array<Class<out KtElement>> = arrayOf(
KtImportList::class.java, KtImportList::class.java,
KtPackageDirective::class.java KtPackageDirective::class.java
) )
override fun extractTransferableData(content: Transferable): List<KotlinReferenceTransferableData> { override fun extractTransferableData(content: Transferable): List<KotlinReferenceTransferableData> {
@@ -85,10 +85,8 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
val data = content.getTransferData(flavor) as? KotlinReferenceTransferableData ?: return listOf() val data = content.getTransferData(flavor) as? KotlinReferenceTransferableData ?: return listOf()
// copy to prevent changing of original by convertLineSeparators // copy to prevent changing of original by convertLineSeparators
return listOf(data.clone()) return listOf(data.clone())
} } catch (ignored: UnsupportedFlavorException) {
catch (ignored: UnsupportedFlavorException) { } catch (ignored: IOException) {
}
catch (ignored: IOException) {
} }
} }
@@ -96,23 +94,21 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
override fun collectTransferableData( override fun collectTransferableData(
file: PsiFile, file: PsiFile,
editor: Editor, editor: Editor,
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray endOffsets: IntArray
): List<KotlinReferenceTransferableData> { ): List<KotlinReferenceTransferableData> {
if (file !is KtFile || DumbService.getInstance(file.getProject()).isDumb) return listOf() if (file !is KtFile || DumbService.getInstance(file.getProject()).isDumb) return listOf()
val collectedData = try { val collectedData = try {
collectReferenceData(file, startOffsets, endOffsets) collectReferenceData(file, startOffsets, endOffsets)
} } catch (e: ProcessCanceledException) {
catch (e: ProcessCanceledException) {
// supposedly analysis can only be canceled from another thread // supposedly analysis can only be canceled from another thread
// do not log ProcessCanceledException as it is rethrown by IdeaLogger and code won't be copied // do not log ProcessCanceledException as it is rethrown by IdeaLogger and code won't be copied
LOG.debug("ProcessCanceledException while analyzing references in ${file.getName()}. References can't be processed.") LOG.debug("ProcessCanceledException while analyzing references in ${file.getName()}. References can't be processed.")
return listOf() return listOf()
} } catch (e: Throwable) {
catch (e: Throwable) {
LOG.error("Exception in processing references for copy paste in file ${file.getName()}}", e) LOG.error("Exception in processing references for copy paste in file ${file.getName()}}", e)
return listOf() return listOf()
} }
@@ -123,13 +119,13 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
fun collectReferenceData( fun collectReferenceData(
file: KtFile, file: KtFile,
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray endOffsets: IntArray
): List<KotlinReferenceData> { ): List<KotlinReferenceData> {
val ranges = toTextRanges(startOffsets, endOffsets) val ranges = toTextRanges(startOffsets, endOffsets)
val elementsByRange = ranges.associateBy({ it }, { val elementsByRange = ranges.associateBy({ it }, { textRange ->
file.elementsInRange(it).filter { it is KtElement || it is KDocElement } file.elementsInRange(textRange).filter { it is KtElement || it is KDocElement }
}) })
val allElementsToResolve = elementsByRange.values.flatten().flatMap { it.collectDescendantsOfType<KtElement>() } val allElementsToResolve = elementsByRange.values.flatten().flatMap { it.collectDescendantsOfType<KtElement>() }
@@ -148,12 +144,12 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
private fun MutableCollection<KotlinReferenceData>.addReferenceDataInsideElement( private fun MutableCollection<KotlinReferenceData>.addReferenceDataInsideElement(
element: PsiElement, element: PsiElement,
file: KtFile, file: KtFile,
startOffset: Int, startOffset: Int,
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray, endOffsets: IntArray,
bindingContext: BindingContext bindingContext: BindingContext
) { ) {
if (PsiTreeUtil.getNonStrictParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null) return if (PsiTreeUtil.getNonStrictParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null) return
@@ -167,8 +163,8 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
for (descriptor in descriptors) { for (descriptor in descriptors) {
val effectiveReferencedDescriptors = DescriptorToSourceUtils.getEffectiveReferencedDescriptors(descriptor).asSequence() val effectiveReferencedDescriptors = DescriptorToSourceUtils.getEffectiveReferencedDescriptors(descriptor).asSequence()
val declaration = effectiveReferencedDescriptors val declaration = effectiveReferencedDescriptors
.map { DescriptorToSourceUtils.getSourceFromDescriptor(it) } .map { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
.singleOrNull() .singleOrNull()
if (declaration != null && declaration.isInCopiedArea(file, startOffsets, endOffsets)) continue if (declaration != null && declaration.isInCopiedArea(file, startOffsets, endOffsets)) continue
if (!reference.canBeResolvedViaImport(descriptor, bindingContext)) continue if (!reference.canBeResolvedViaImport(descriptor, bindingContext)) continue
@@ -184,17 +180,17 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
private data class ReferenceToRestoreData( private data class ReferenceToRestoreData(
val reference: KtReference, val reference: KtReference,
val refData: KotlinReferenceData val refData: KotlinReferenceData
) )
override fun processTransferableData( override fun processTransferableData(
project: Project, project: Project,
editor: Editor, editor: Editor,
bounds: RangeMarker, bounds: RangeMarker,
caretOffset: Int, caretOffset: Int,
indented: Ref<Boolean>, indented: Ref<Boolean>,
values: List<KotlinReferenceTransferableData> values: List<KotlinReferenceTransferableData>
) { ) {
if (DumbService.getInstance(project).isDumb) return if (DumbService.getInstance(project).isDumb) return
@@ -220,14 +216,17 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
} }
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 references = referenceData.map { it to findReference(it, file, blockStart) }
val bindingContext = try { val bindingContext = try {
file.getResolutionFacade().analyze(references.mapNotNull { it.second?.element }, BodyResolveMode.PARTIAL) file.getResolutionFacade().analyze(references.mapNotNull { it.second?.element }, BodyResolveMode.PARTIAL)
} } catch (e: Throwable) {
catch (e: Throwable) {
LOG.error("Failed to analyze references after copy paste", e) LOG.error("Failed to analyze references after copy paste", e)
return emptyList() return emptyList()
} }
@@ -258,11 +257,11 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
private fun createReferenceToRestoreData( private fun createReferenceToRestoreData(
reference: KtReference, reference: KtReference,
refData: KotlinReferenceData, refData: KotlinReferenceData,
file: KtFile, file: KtFile,
fileResolutionScope: LexicalScope, fileResolutionScope: LexicalScope,
bindingContext: BindingContext bindingContext: BindingContext
): ReferenceToRestoreData? { ): ReferenceToRestoreData? {
val originalFqName = FqName(refData.fqName) val originalFqName = FqName(refData.fqName)
val name = originalFqName.shortName() val name = originalFqName.shortName()
@@ -272,8 +271,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
if (fileResolutionScope.findFunction(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) { if (fileResolutionScope.findFunction(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
return null // already imported return null // already imported
} }
} } else if (refData.kind == KotlinReferenceData.Kind.PROPERTY) {
else if (refData.kind == KotlinReferenceData.Kind.PROPERTY) {
if (fileResolutionScope.findVariable(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) { if (fileResolutionScope.findVariable(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
return null // already imported return null // already imported
} }
@@ -282,9 +280,9 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
val referencedDescriptors = resolveReference(reference, bindingContext) val referencedDescriptors = resolveReference(reference, bindingContext)
val referencedFqNames = referencedDescriptors val referencedFqNames = referencedDescriptors
.filterNot { ErrorUtils.isError(it) } .filterNot { ErrorUtils.isError(it) }
.mapNotNull { it.importableFqName } .mapNotNull { it.importableFqName }
.toSet() .toSet()
if (referencedFqNames.singleOrNull() == originalFqName) return null if (referencedFqNames.singleOrNull() == originalFqName) return null
// check that descriptor to import exists and is accessible from the current module // check that descriptor to import exists and is accessible from the current module
@@ -299,7 +297,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
val element = reference.element val element = reference.element
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) }
} }
return reference.resolveToDescriptors(bindingContext) return reference.resolveToDescriptors(bindingContext)
@@ -310,8 +308,8 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
val smartPointerManager = SmartPointerManager.getInstance(file.project) val smartPointerManager = SmartPointerManager.getInstance(file.project)
data class BindingRequest( data class BindingRequest(
val pointer: SmartPsiElementPointer<KtSimpleNameExpression>, val pointer: SmartPsiElementPointer<KtSimpleNameExpression>,
val fqName: FqName val fqName: FqName
) )
val bindingRequests = ArrayList<BindingRequest>() val bindingRequests = ArrayList<BindingRequest>()
@@ -324,8 +322,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
if (reference is KtSimpleNameReference) { if (reference is KtSimpleNameReference) {
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.element, file) val pointer = smartPointerManager.createSmartPsiElementPointer(reference.element, file)
bindingRequests.add(BindingRequest(pointer, fqName)) bindingRequests.add(BindingRequest(pointer, fqName))
} } else if (reference is KDocReference) {
else if (reference is KDocReference) {
descriptorsToImport.addAll(findImportableDescriptors(fqName, file)) descriptorsToImport.addAll(findImportableDescriptors(fqName, file))
} }
} else { } else {
@@ -350,10 +347,13 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
} }
private fun findCallableToImport(fqName: FqName, file: KtFile): CallableDescriptor? private fun findCallableToImport(fqName: FqName, file: KtFile): CallableDescriptor? =
= findImportableDescriptors(fqName, file).firstIsInstanceOrNull<CallableDescriptor>() findImportableDescriptors(fqName, file).firstIsInstanceOrNull()
private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> { private fun showRestoreReferencesDialog(
project: Project,
referencesToRestore: List<ReferenceToRestoreData>
): Collection<ReferenceToRestoreData> {
val fqNames = referencesToRestore.asSequence().map { it.refData.fqName }.toSortedSet() val fqNames = referencesToRestore.asSequence().map { it.refData.fqName }.toSortedSet()
if (ApplicationManager.getApplication().isUnitTestMode) { if (ApplicationManager.getApplication().isUnitTestMode) {
@@ -383,6 +383,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
} }
companion object { companion object {
@get:TestOnly var declarationsToImportSuggested: Collection<String> = emptyList() @get:TestOnly
var declarationsToImportSuggested: Collection<String> = emptyList()
} }
} }