Working on new imports insertion on copy/paste

This commit is contained in:
Valentin Kipyatkov
2015-02-11 20:33:55 +03:00
parent 4031357ae4
commit 6acba00599
35 changed files with 279 additions and 245 deletions
@@ -22,11 +22,12 @@ import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.UserDataProperty import org.jetbrains.kotlin.psi.UserDataProperty
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import java.util.HashSet
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.psi.SmartPointerManager import com.intellij.psi.SmartPointerManager
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
import org.jetbrains.kotlin.idea.util.ShortenReferences
import java.util.*
class ShorteningRequest(val pointer: SmartPsiElementPointer<JetElement>, val options: Options) class ShorteningRequest(val pointer: SmartPsiElementPointer<JetElement>, val options: Options)
@@ -43,7 +44,7 @@ public var Project.ensureElementsToShortenIsEmptyBeforeRefactoring: Boolean
private fun Project.getOrCreateElementsToShorten(): MutableSet<ShorteningRequest> { private fun Project.getOrCreateElementsToShorten(): MutableSet<ShorteningRequest> {
var elements = elementsToShorten var elements = elementsToShorten
if (elements == null) { if (elements == null) {
elements = HashSet() elements = LinkedHashSet()
elementsToShorten = elements elementsToShorten = elements
} }
@@ -57,12 +58,14 @@ public fun JetElement.addToShorteningWaitSet(options: Options = Options.DEFAULT)
project.getOrCreateElementsToShorten().add(ShorteningRequest(elementPointer, options)) project.getOrCreateElementsToShorten().add(ShorteningRequest(elementPointer, options))
} }
public fun withElementsToShorten(project: Project, f: (Set<ShorteningRequest>) -> Unit) { public fun performDelayedShortening(project: Project) {
project.elementsToShorten?.let { requests -> project.elementsToShorten?.let { requests ->
project.elementsToShorten = null project.elementsToShorten = null
f(requests) val elements = requests.map { it.pointer.getElement() }
val options = requests.map { it.options }
val elementToOptions = (elements zip options).toMap()
ShortenReferences({ elementToOptions[it] ?: ShortenReferences.Options.DEFAULT }).process(elements.filterNotNull())
} }
} }
private val LOG = Logger.getInstance(javaClass<Project>().getCanonicalName()) private val LOG = Logger.getInstance(javaClass<Project>().getCanonicalName())
@@ -16,54 +16,42 @@
package org.jetbrains.kotlin.idea.codeInsight package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiElement
import com.intellij.codeInsight.editorActions.ReferenceData
import java.util.ArrayList
import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.codeInsight.editorActions.ReferenceTransferableData
import com.intellij.codeInsight.daemon.impl.CollectHighlightsUtil import com.intellij.codeInsight.daemon.impl.CollectHighlightsUtil
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.DumbService
import com.intellij.psi.PsiDocumentManager
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import java.awt.datatransfer.Transferable import java.awt.datatransfer.Transferable
import com.intellij.codeInsight.CodeInsightSettings import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.JetCallExpression
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.psi.JetImportDirective
import org.jetbrains.kotlin.psi.JetPackageDirective
import org.jetbrains.kotlin.idea.references.JetMultiReference
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.idea.conversion.copy.* import org.jetbrains.kotlin.idea.conversion.copy.*
import java.awt.datatransfer.UnsupportedFlavorException import java.awt.datatransfer.UnsupportedFlavorException
import java.io.IOException import java.io.IOException
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
import org.jetbrains.kotlin.psi.JetUserType
import org.jetbrains.kotlin.psi.JetTypeReference
import org.jetbrains.kotlin.idea.imports.* import org.jetbrains.kotlin.idea.imports.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.utils.*
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.references.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver.LookupMode
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.idea.caches.resolve.*
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.utils.*
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedShortening
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import com.intellij.psi.*
import java.util.*
//NOTE: this class is based on CopyPasteReferenceProcessor and JavaCopyPasteReferenceProcessor //NOTE: this class is based on CopyPasteReferenceProcessor and JavaCopyPasteReferenceProcessor
public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<ReferenceTransferableData>() { public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<KotlinReferenceTransferableData>() {
private val LOG = Logger.getInstance(javaClass<KotlinCopyPasteReferenceProcessor>()) private val LOG = Logger.getInstance(javaClass<KotlinCopyPasteReferenceProcessor>())
private val IGNORE_REFERENCES_INSIDE: Array<Class<out JetElement>?> = array( private val IGNORE_REFERENCES_INSIDE: Array<Class<out JetElement>?> = array(
@@ -71,17 +59,13 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
javaClass<JetPackageDirective>() javaClass<JetPackageDirective>()
) )
override fun extractTransferableData(content: Transferable): List<ReferenceTransferableData> { override fun extractTransferableData(content: Transferable): List<KotlinReferenceTransferableData> {
if (CodeInsightSettings.getInstance().ADD_IMPORTS_ON_PASTE != CodeInsightSettings.NO) { if (CodeInsightSettings.getInstance().ADD_IMPORTS_ON_PASTE != CodeInsightSettings.NO) {
try { try {
val flavor = ReferenceData.getDataFlavor() val flavor = KotlinReferenceData.dataFlavor ?: return listOf()
if (flavor != null) { val data = content.getTransferData(flavor) as? KotlinReferenceTransferableData ?: return listOf()
val referenceData = content.getTransferData(flavor) as? ReferenceTransferableData // copy to prevent changing of original by convertLineSeparators
if (referenceData != null) { return listOf(data.clone())
// copy to prevent changing of original by convertLineSeparators
return listOf(referenceData.clone())
}
}
} }
catch (ignored: UnsupportedFlavorException) { catch (ignored: UnsupportedFlavorException) {
} }
@@ -97,7 +81,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
editor: Editor, editor: Editor,
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray endOffsets: IntArray
): List<ReferenceTransferableData> { ): List<KotlinReferenceTransferableData> {
if (file !is JetFile || DumbService.getInstance(file.getProject()).isDumb()) return listOf() if (file !is JetFile || DumbService.getInstance(file.getProject()).isDumb()) return listOf()
val collectedData = try { val collectedData = try {
@@ -120,7 +104,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
if (collectedData.isEmpty()) return listOf() if (collectedData.isEmpty()) return listOf()
return listOf(ReferenceTransferableData(collectedData.copyToArray())) return listOf(KotlinReferenceTransferableData(collectedData.copyToArray()))
} }
private fun collectReferenceDataFromElement( private fun collectReferenceDataFromElement(
@@ -129,44 +113,51 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
startOffset: Int, startOffset: Int,
startOffsets: IntArray, startOffsets: IntArray,
endOffsets: IntArray endOffsets: IntArray
): Collection<ReferenceData> { ): Collection<KotlinReferenceData> {
fun collectReferenceData(referencedDeclaration: PsiElement, referencedDescriptor: DeclarationDescriptor): ReferenceData? { if (PsiTreeUtil.getParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null) return listOf()
if (referencedDeclaration.isInCopiedArea(file, startOffsets, endOffsets)) return null
if (isInReceiverScope(element, referencedDescriptor)) return null
val fqName = referencedDescriptor.importableFqName val reference = element.getReference() as? JetReference ?: return listOf()
if (fqName != null && referencedDescriptor.canBeReferencedViaImport()) {
return createReferenceData(element, startOffset, fqName)
}
return null
}
val isInsideIgnoredElement = PsiTreeUtil.getParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null
if (isInsideIgnoredElement) return listOf()
val reference = element.getReference()
if (reference !is JetReference) return listOf()
val resolveMap = reference.resolveMap() val resolveMap = reference.resolveMap()
//check whether this reference is unambiguous //check whether this reference is unambiguous
if (reference !is JetMultiReference<*> && resolveMap.size() > 1) return listOf() if (reference !is JetMultiReference<*> && resolveMap.size() > 1) return listOf()
val collectedData = ArrayList<ReferenceData>()
for ((referencedDescriptor, declarations) in resolveMap) { val collectedData = ArrayList<KotlinReferenceData>()
if (declarations.size() != 1) continue for ((descriptor, declarations) in resolveMap) {
collectedData.addIfNotNull(collectReferenceData(declarations.first(), referencedDescriptor)) val declaration = declarations.singleOrNull() ?: continue
if (declaration.isInCopiedArea(file, startOffsets, endOffsets)) continue
if (!descriptor.isExtension) {
if (element !is JetNameReferenceExpression) continue
if (element.getIdentifier() == null) continue // skip 'this' etc
if (element.getReceiverExpression() != null) continue
}
val fqName = descriptor.importableFqName ?: continue
if (!descriptor.canBeReferencedViaImport()) continue
val kind = when (descriptor.getImportableDescriptor()) {
is ClassDescriptor ->
KotlinReferenceData.Kind.CLASS
is PackageViewDescriptor ->
KotlinReferenceData.Kind.PACKAGE
is CallableDescriptor ->
if (descriptor.isExtension) KotlinReferenceData.Kind.EXTENSION_CALLABLE else KotlinReferenceData.Kind.NON_EXTENSION_CALLABLE
else ->
continue
}
collectedData.add(KotlinReferenceData(element.range.start - startOffset, element.range.end - startOffset, fqName.asString(), kind))
} }
return collectedData return collectedData
} }
private fun createReferenceData(element: PsiElement, startOffset: Int, fqName: FqName)
= ReferenceData(element.range.start - startOffset, element.range.end - startOffset, fqName.asString(), null)
private data class ReferenceToRestoreData( private data class ReferenceToRestoreData(
val expression: JetElement, val reference: JetReference,
val fqName: FqName, val refData: KotlinReferenceData
val lengthenReference: Boolean = false
) )
override fun processTransferableData ( override fun processTransferableData (
@@ -175,7 +166,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
bounds: RangeMarker, bounds: RangeMarker,
caretOffset: Int, caretOffset: Int,
indented: Ref<Boolean>, indented: Ref<Boolean>,
values: List<ReferenceTransferableData> values: List<KotlinReferenceTransferableData>
) { ) {
if (DumbService.getInstance(project).isDumb()) return if (DumbService.getInstance(project).isDumb()) return
@@ -187,7 +178,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
assert(values.size() == 1) assert(values.size() == 1)
val referenceData = values.single().getData()!! val referenceData = values.single().data
val referencesPossibleToRestore = findReferencesToRestore(file, bounds, referenceData) val referencesPossibleToRestore = findReferencesToRestore(file, bounds, referenceData)
val selectedReferencesToRestore = showRestoreReferencesDialog(project, referencesPossibleToRestore) val selectedReferencesToRestore = showRestoreReferencesDialog(project, referencesPossibleToRestore)
@@ -199,24 +190,19 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
PsiDocumentManager.getInstance(project).commitAllDocuments() PsiDocumentManager.getInstance(project).commitAllDocuments()
} }
private fun findReferencesToRestore(file: PsiFile, bounds: RangeMarker, referenceData: Array<out ReferenceData>): List<ReferenceToRestoreData> { private fun findReferencesToRestore(file: PsiFile, bounds: RangeMarker, referenceData: Array<out KotlinReferenceData>): List<ReferenceToRestoreData> {
if (file !is JetFile) return listOf() if (file !is JetFile) return listOf()
return referenceData.map { return referenceData.map {
if (ImportInsertHelper.getInstance(file.getProject()).needImport(it.fqName, file)) { val referenceElement = findReference(it, file, bounds)
val referenceExpression = findReference(it, file, bounds) if (referenceElement != null)
if (referenceExpression != null) createReferenceToRestoreData(referenceElement, it)
createReferenceToRestoreData(referenceExpression, it.fqName) else
else
null
}
else {
null null
}
}.filterNotNull() }.filterNotNull()
} }
private fun findReference(data: ReferenceData, file: JetFile, bounds: RangeMarker): JetElement? { private fun findReference(data: KotlinReferenceData, file: JetFile, bounds: RangeMarker): JetElement? {
val startOffset = data.startOffset + bounds.getStartOffset() val startOffset = data.startOffset + bounds.getStartOffset()
val endOffset = data.endOffset + bounds.getStartOffset() val endOffset = data.endOffset + bounds.getStartOffset()
val element = file.findElementAt(startOffset) val element = file.findElementAt(startOffset)
@@ -237,47 +223,65 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
return null return null
} }
private fun createReferenceToRestoreData(expression: JetElement, originalReferencedFqName: FqName): ReferenceToRestoreData? { private fun createReferenceToRestoreData(element: JetElement, refData: KotlinReferenceData): ReferenceToRestoreData? {
val reference = expression.getReference() as? JetReference ?: return null val reference = element.getReference() as? JetReference ?: return null
val referencedDescriptors = try { val referencedDescriptors = try {
reference.resolveToDescriptors() reference.resolveToDescriptors()
} }
catch (e: Throwable) { catch (e: Throwable) {
LOG.error("Failed to analyze reference (${expression.getText()}) after copy paste", e) LOG.error("Failed to analyze reference (${element.getText()}) after copy paste", e)
return null return null
} }
val referencedFqNames = referencedDescriptors.filterNot { ErrorUtils.isError(it) } .map { it.importableFqName } val referencedFqNames = referencedDescriptors.filterNot { ErrorUtils.isError(it) }.map { it.importableFqName }.filterNotNull()
val referencesSame = referencedFqNames any { it == originalReferencedFqName } val originalFqName = FqName(refData.fqName)
val conflict = referencedFqNames any { it != originalReferencedFqName && (it?.shortName() == originalReferencedFqName.shortName()) } val referencesSame = referencedFqNames.any { it == originalFqName }
when { val conflict = referencedFqNames.any { it != originalFqName && it.shortName() == originalFqName.shortName() }
referencesSame && !conflict -> { if (referencesSame && !conflict) return null
return null return ReferenceToRestoreData(reference, refData)
}
!referencesSame && !conflict -> {
return ReferenceToRestoreData(expression, originalReferencedFqName)
}
conflict -> {
val mustBeReferencedWithReceiver = referencedDescriptors.any { it.isExtension }
if (!mustBeReferencedWithReceiver && canLengthenReferenceExpression(expression, originalReferencedFqName)) {
return ReferenceToRestoreData(expression, originalReferencedFqName, lengthenReference = true)
}
}
}
return null
} }
private fun restoreReferences(referencesToRestore: Collection<ReferenceToRestoreData>, file: JetFile) { private fun restoreReferences(referencesToRestore: Collection<ReferenceToRestoreData>, file: JetFile) {
for ((referenceExpression, fqName, shouldLengthen) in referencesToRestore) { val importHelper = ImportInsertHelper.getInstance(file.getProject())
if (!shouldLengthen) { val smartPointerManager = SmartPointerManager.getInstance(file.getProject())
ImportInsertHelper.getInstance(file.getProject()).addImportDirectiveIfNeeded(fqName, file)
[data] class BindingRequest(
val pointer: SmartPsiElementPointer<JetSimpleNameExpression>,
val fqName: FqName
)
val bindingRequests = ArrayList<BindingRequest>()
val descriptorsToImport = ArrayList<CallableDescriptor>()
for ((reference, refData) in referencesToRestore) {
val fqName = FqName(refData.fqName)
if (refData.kind != KotlinReferenceData.Kind.EXTENSION_CALLABLE && reference is JetSimpleNameReference) {
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.getElement(), file)
bindingRequests.add(BindingRequest(pointer, fqName))
} }
else {
//TODO: try to shorten reference after (sometimes is possible), need shorten reference to support all relevant cases if (refData.kind == KotlinReferenceData.Kind.NON_EXTENSION_CALLABLE || refData.kind == KotlinReferenceData.Kind.EXTENSION_CALLABLE) {
lengthenReference(referenceExpression, fqName) descriptorsToImport.addIfNotNull(findCallableToImport(fqName, file))
} }
} }
for (descriptor in descriptorsToImport) {
importHelper.importDescriptor(file, descriptor)
}
for ((pointer, fqName) in bindingRequests) {
val reference = pointer.getElement().getReference() as JetSimpleNameReference
reference.bindToFqName(fqName, JetSimpleNameReference.ShorteningMode.DELAYED_SHORTENING)
}
performDelayedShortening(file.getProject())
}
private fun findCallableToImport(fqName: FqName, file: JetFile): CallableDescriptor? {
val importDirective = JetPsiFactory(file.getProject()).createImportDirective(ImportPath(fqName, false))
val moduleDescriptor = file.getResolutionFacade().findModuleDescriptor(file)
val scope = JetModuleUtil.getSubpackagesOfRootScope(moduleDescriptor)
val descriptors = QualifiedExpressionResolver()
.processImportReference(importDirective, scope, scope, null, BindingTraceContext(), LookupMode.EVERYTHING)
.filterIsInstance<CallableDescriptor>()
return descriptors.singleOrNull()
} }
private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> { private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> {
@@ -285,45 +289,16 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
if (!shouldShowDialog || referencesToRestore.isEmpty()) { if (!shouldShowDialog || referencesToRestore.isEmpty()) {
return referencesToRestore return referencesToRestore
} }
val fqNames = referencesToRestore. map { it.fqName.asString() }.toSortedSet()
val fqNames = referencesToRestore.map { it.refData.fqName }.toSortedSet()
val dialog = RestoreReferencesDialog(project, fqNames.copyToArray()) val dialog = RestoreReferencesDialog(project, fqNames.copyToArray())
dialog.show() dialog.show()
val selectedFqNames = dialog.getSelectedElements()!!.toSet() val selectedFqNames = dialog.getSelectedElements()!!.toSet()
return referencesToRestore.filter { ref -> selectedFqNames.contains(ref.fqName.asString()) } return referencesToRestore.filter { selectedFqNames.contains(it.refData.fqName) }
} }
private fun createQualifiedExpression(psiFactory: JetPsiFactory, text: String)
= psiFactory.createExpression(text) as JetDotQualifiedExpression
private fun lengthenReference(expression: JetElement, fqName: FqName) {
assert(canLengthenReferenceExpression(expression, fqName))
val parent = expression.getParent()
val prefixToInsert = IdeDescriptorRenderers.SOURCE_CODE.renderFqName(fqName.parent())
val psiFactory = JetPsiFactory(expression)
if (parent is JetCallExpression) {
val text = prefixToInsert + "." + parent.getText()
parent.replace(createQualifiedExpression(psiFactory, text))
}
else if (parent is JetUserType) {
val typeReference = expression.getStrictParentOfType<JetTypeReference>()!!
typeReference.replace(psiFactory.createType(prefixToInsert + "." + typeReference.getText()))
}
else {
expression.replace(createQualifiedExpression(psiFactory, IdeDescriptorRenderers.SOURCE_CODE.renderFqName(fqName)))
}
}
private fun canLengthenReferenceExpression(expression: JetElement, fqName: FqName): Boolean {
return when {
fqName.pathSegments().size() < 2 -> false
expression is JetSimpleNameExpression && expression.getReceiverExpression() == null -> true
else -> false
}
}
private val ReferenceData.fqName: FqName
get() = FqName(qClassName!!)
private fun toTextRanges(startOffsets: IntArray, endOffsets: IntArray): List<TextRange> { private fun toTextRanges(startOffsets: IntArray, endOffsets: IntArray): List<TextRange> {
assert(startOffsets.size() == endOffsets.size()) assert(startOffsets.size() == endOffsets.size())
return startOffsets.indices.map { TextRange(startOffsets[it], endOffsets[it]) } return startOffsets.indices.map { TextRange(startOffsets[it], endOffsets[it]) }
@@ -331,6 +306,6 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
private fun PsiElement.isInCopiedArea(fileCopiedFrom: JetFile, startOffsets: IntArray, endOffsets: IntArray): Boolean { private fun PsiElement.isInCopiedArea(fileCopiedFrom: JetFile, startOffsets: IntArray, endOffsets: IntArray): Boolean {
if (getContainingFile() != fileCopiedFrom) return false if (getContainingFile() != fileCopiedFrom) return false
return toTextRanges(startOffsets, endOffsets).contains(range) return toTextRanges(startOffsets, endOffsets).any { this.range in it }
} }
} }
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.codeInsight.editorActions.*
import java.io.*
import java.awt.datatransfer.*
import kotlin.properties.*
public class KotlinReferenceTransferableData(
val data: Array<KotlinReferenceData>
) : TextBlockTransferableData, Cloneable, Serializable {
override fun getFlavor() = KotlinReferenceData.dataFlavor
override fun getOffsetCount() = data.size() * 2
override fun getOffsets(offsets: IntArray, index: Int): Int {
var i = index
for (d in data) {
offsets[i++] = d.startOffset
offsets[i++] = d.endOffset
}
return i
}
override fun setOffsets(offsets: IntArray, index: Int): Int {
var i = index
for (d in data) {
d.startOffset = offsets[i++]
d.endOffset = offsets[i++]
}
return i
}
public override fun clone() = KotlinReferenceTransferableData(Array(data.size(), { data[it].clone() }))
}
public class KotlinReferenceData(
public var startOffset: Int,
public var endOffset: Int,
public val fqName: String,
public val kind: KotlinReferenceData.Kind
) : Cloneable, Serializable {
public enum class Kind {
CLASS
PACKAGE
NON_EXTENSION_CALLABLE
EXTENSION_CALLABLE
}
public override fun clone(): KotlinReferenceData {
try {
return super<Cloneable>.clone() as KotlinReferenceData
}
catch (e: CloneNotSupportedException) {
throw RuntimeException()
}
}
class object {
public val dataFlavor: DataFlavor? by Delegates.lazy {
try {
DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=" + javaClass<KotlinReferenceData>().getName(), "KotlinReferenceData")
}
catch (e: NoClassDefFoundError) {
null
}
catch (e: IllegalArgumentException) {
null
}
}
}
}
@@ -20,9 +20,8 @@ import com.intellij.refactoring.RefactoringHelper
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.kotlin.idea.codeInsight.shorten.prepareElementsToShorten
import org.jetbrains.kotlin.idea.codeInsight.shorten.withElementsToShorten
import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.codeInsight.shorten.*
public class KotlinShortenReferencesRefactoringHelper: RefactoringHelper<Any> { public class KotlinShortenReferencesRefactoringHelper: RefactoringHelper<Any> {
override fun prepareOperation(usages: Array<out UsageInfo>?): Any? { override fun prepareOperation(usages: Array<out UsageInfo>?): Any? {
@@ -35,12 +34,7 @@ public class KotlinShortenReferencesRefactoringHelper: RefactoringHelper<Any> {
override fun performOperation(project: Project, operationData: Any?) { override fun performOperation(project: Project, operationData: Any?) {
ApplicationManager.getApplication()!!.runWriteAction { ApplicationManager.getApplication()!!.runWriteAction {
withElementsToShorten(project) { requests -> performDelayedShortening(project)
val elements = requests.map { it.pointer.getElement() }
val options = requests.map { it.options }
val elementToOptions = (elements zip options).toMap()
ShortenReferences({ elementToOptions[it] ?: ShortenReferences.Options.DEFAULT }).process(elements.filterNotNull())
}
} }
} }
} }
@@ -1,21 +1,14 @@
package to package to
import a.* import a.*
import a.E.ENTRY
import a.Outer.Inner
import a.Outer.Nested
import a.Outer.NestedEnum
import a.Outer.NestedObj
import a.Outer.NestedTrait
import a.Outer.NestedAnnotation
fun f(a: A, t: T) { fun f(p: A, t: T) {
g(A(c).ext()) g(A(c).ext())
O1.f() O1.f()
O2 O2
ENTRY E.ENTRY
} }
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { fun f2(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, a: Outer.NestedAnnotation) {
ClassObject ClassObject
} }
@@ -44,7 +44,7 @@ class ClassObject {
} }
} }
<selection>fun f(a: A, t: T) { <selection>fun f(p: A, t: T) {
g(A(c).ext()) g(A(c).ext())
O1.f() O1.f()
O2 O2
@@ -1,11 +1,6 @@
package to package to
import a.Outer.Inner import a.Outer
import a.Outer.Nested
import a.Outer.NestedEnum
import a.Outer.NestedObj
import a.Outer.NestedTrait
import a.Outer.NestedAnnotation
fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { fun f(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, a: Outer.NestedAnnotation) {
} }
@@ -3,6 +3,6 @@ package to
import a.a import a.a
fun f(i: a) { fun f(i: a) {
a.a a
a() a()
} }
@@ -3,5 +3,7 @@ package to
class A<T> { class A<T> {
} }
val a: A<String>? = null
fun f(b: a.A<Int>) { fun f(b: a.A<Int>) {
} }
@@ -3,4 +3,6 @@ package to
class A<T> { class A<T> {
} }
val a: A<String>? = null
<caret> <caret>
@@ -3,5 +3,7 @@ package to
class A() { class A() {
} }
val a: A? = null
fun f(a: a.A) { fun f(a: a.A) {
} }
@@ -3,4 +3,6 @@ package to
class A() { class A() {
} }
val a: A? = null
<caret> <caret>
@@ -1,20 +1,14 @@
package to package to
import a.Outer.Nested.NN
import a.Outer.Nested.NI
import a.Outer.Inner.IN
import a.Outer.Inner.II
import a.Outer.Nested.NN2
import a.with import a.with
import a.Outer import a.Outer
import a.Outer.Inner.IN2
fun f(p1: NN, p2: NI, p3: IN, p4: II) { fun f(p1: Outer.Nested.NN, p2: Outer.Nested.NI, p3: Outer.Inner.IN, p4: Outer.Inner.II) {
NN2() Outer.Nested.NN2()
with(Outer.Nested()) { with(Outer.Nested()) {
NI2() NI2()
} }
IN2() Outer.Inner.IN2()
with(Outer().Inner()) { with(Outer().Inner()) {
II2() II2()
} }
@@ -1,9 +1,9 @@
package to package to
import a.A
import a.get import a.get
import a.set import a.set
import a.A
class B { class B {
var a by A() var v by A()
} }
@@ -12,5 +12,5 @@ fun T.set(thisRef: B, desc: PropertyMetadata, value: Int) {
class A(): T class A(): T
<selection>class B { <selection>class B {
var a by A() var v by A()
}</selection> }</selection>
@@ -11,6 +11,6 @@ public class JavaClass {
public class InnerClass { public class InnerClass {
} }
public class NestedClass { public static class NestedClass {
} }
} }
@@ -1,12 +1,10 @@
package to package to
import java.JavaClass import java.JavaClass
import java.JavaClass.staticMethod
import java.JavaClass.NestedClass
fun f(c: JavaClass) { fun f(c: JavaClass) {
staticMethod() JavaClass.staticMethod()
c.method() c.method()
c.InnerClass() c.InnerClass()
NestedClass() JavaClass.NestedClass()
} }
@@ -1,28 +1,23 @@
package to package to
import d.A
import d.T
import d.g import d.g
import d.c import d.c
import d.ext import d.ext
import d.A
import d.T
import d.Outer
import d.O1 import d.O1
import d.O2 import d.O2
import d.E.ENTRY import d.E
import d.Outer.Inner
import d.Outer.Nested
import d.Outer.NestedEnum
import d.Outer.NestedObj
import d.Outer.NestedTrait
import d.Outer.NestedAnnotation
import d.ClassObject import d.ClassObject
fun f(a: A, t: T) { fun f(a: A, t: T) {
g(A(c).ext()) g(A(c).ext())
O1.f() O1.f()
O2 O2
ENTRY E.ENTRY
} }
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { fun f2(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, a: Outer.NestedAnnotation) {
ClassObject ClassObject
} }
@@ -1,11 +1,9 @@
package to package to
import a.A.A import a.A
import a.A.B
import a.A.C
fun f() { fun f() {
A A.A
B A.B
C A.C
} }
@@ -1,8 +1,8 @@
package to package to
import a.A
import a.plus import a.plus
import a.infix import a.infix
import a.A
fun f(a: A) { fun f(a: A) {
a + a a + a
@@ -1,6 +1,8 @@
package to package to
import a.A import a.A
import a.ext
import a.p
fun A.ext() { fun A.ext() {
} }
@@ -1,6 +1,11 @@
package to package to
import a.A import a.A
import a.ext
import a.plus
import a.infix
import a.minus
import a.p
fun A.ext() { fun A.ext() {
} }
@@ -1,9 +1,9 @@
package to package to
import a.A
import a.iterator import a.iterator
import a.next import a.next
import a.hasNext import a.hasNext
import a.A
fun f() { fun f() {
for (i in A()) { for (i in A()) {
@@ -1,7 +1,7 @@
package to package to
import a.A
import a.get import a.get
import a.A
fun f() { fun f() {
A()[""] A()[""]
@@ -1,28 +1,23 @@
package to package to
import d.A
import d.T
import d.g import d.g
import d.c import d.c
import d.ext import d.ext
import d.A
import d.T
import d.Outer
import d.O1 import d.O1
import d.O2 import d.O2
import d.E.ENTRY import d.E
import d.Outer.Inner
import d.Outer.Nested
import d.Outer.NestedEnum
import d.Outer.NestedObj
import d.Outer.NestedTrait
import d.Outer.NestedAnnotation
import d.ClassObject import d.ClassObject
fun f(a: A, t: T) { fun f(a: A, t: T) {
g(A(c).ext()) g(A(c).ext())
O1.f() O1.f()
O2 O2
ENTRY E.ENTRY
} }
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { fun f2(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, a: Outer.NestedAnnotation) {
ClassObject ClassObject
} }
@@ -1,13 +1,7 @@
package to package to
import a.Outer.Inner
import a.Outer.Nested
import a.Outer.NestedEnum
import a.Outer.NestedObj
import a.Outer.NestedTrait
import a.Outer.NestedAnnotation
import a.Outer import a.Outer
fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { fun f(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, aa: Outer.NestedAnnotation) {
Outer().Inner2() Outer().Inner2()
} }
+1 -1
View File
@@ -18,6 +18,6 @@ class Outer {
annotation class NestedAnnotation annotation class NestedAnnotation
} }
<selection>fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { <selection>fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, aa: NestedAnnotation) {
Outer().Inner2() Outer().Inner2()
}</selection> }</selection>
@@ -1,7 +1,7 @@
package to package to
import a.A
import a.invoke import a.invoke
import a.A
fun f(a: A) { fun f(a: A) {
a() a()
@@ -1,8 +1,8 @@
package to package to
import a.A
import a.component1 import a.component1
import a.component2 import a.component2
import a.A
fun f() { fun f() {
val (a, b) = A() val (a, b) = A()
@@ -1,9 +1,9 @@
package to package to
import a.A
import a.B
import a.next import a.next
import a.hasNext import a.hasNext
import a.A
import a.B
fun A.iterator() = B() fun A.iterator() = B()
@@ -1,20 +1,12 @@
package a package a
import a.E.ENTRY fun f(p: A, t: T) {
import a.Outer.Inner
import a.Outer.Nested
import a.Outer.NestedEnum
import a.Outer.NestedObj
import a.Outer.NestedTrait
import a.Outer.NestedAnnotation
fun f(a: A, t: T) {
g(A(c).ext()) g(A(c).ext())
O1.f() O1.f()
O2 O2
ENTRY E.ENTRY
} }
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) { fun f2(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, a: Outer.NestedAnnotation) {
ClassObject ClassObject
} }
@@ -44,7 +44,7 @@ class ClassObject {
} }
} }
<selection>fun f(a: A, t: T) { <selection>fun f(p: A, t: T) {
g(A(c).ext()) g(A(c).ext())
O1.f() O1.f()
O2 O2
@@ -5,5 +5,7 @@ class A {
} }
} }
val v: A? = null
fun f(p: a.A.B) { fun f(p: a.A.B) {
} }
@@ -5,4 +5,6 @@ class A {
} }
} }
val v: A? = null
<caret> <caret>
@@ -1,6 +1,6 @@
package to package to
import a.T
import a.A import a.A
import a.T
fun f(): T<A> fun f(): T<A>