Insert imports on copy/paste

Introduce KotlinCopyPasteReferenceProcessor
This commit is contained in:
Pavel V. Talanov
2014-01-22 20:29:46 +04:00
parent 2f55959578
commit 61153c87e0
138 changed files with 2813 additions and 0 deletions
@@ -90,6 +90,7 @@ import org.jetbrains.jet.completion.AbstractCompiledKotlinInJavaCompletionTest
import org.jetbrains.jet.completion.AbstractKotlinSourceInJavaCompletionTest
import org.jetbrains.jet.plugin.intentions.AbstractIntentionTest
import org.jetbrains.jet.checkers.AbstractJetDiagnosticsTestWithStdLib
import org.jetbrains.jet.plugin.codeInsight.AbstractInsertImportOnPasteTest
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
@@ -441,6 +442,11 @@ fun main(args: Array<String>) {
model("copyPaste/conversion", extension = "java")
}
testClass(javaClass<AbstractInsertImportOnPasteTest>()) {
model("copyPaste/imports", pattern = """^([^\.]+)\.kt$""", testMethod = "doTestCopy", testClassName = "Copy", recursive = false)
model("copyPaste/imports", pattern = """^([^\.]+)\.kt$""", testMethod = "doTestCut", testClassName = "Cut", recursive = false)
}
testClass(javaClass<AbstractShortenRefsTest>()) {
model("shortenRefs", pattern = """^([^\.]+)\.kt$""")
}
+2
View File
@@ -250,6 +250,8 @@
<backspaceHandlerDelegate implementation="org.jetbrains.jet.plugin.editor.KotlinBackspaceHandler"/>
<copyPastePostProcessor implementation="org.jetbrains.jet.plugin.conversion.copy.ConvertJavaCopyPastePostProcessor"/>
<copyPastePostProcessor implementation="org.jetbrains.jet.plugin.codeInsight.KotlinCopyPasteReferenceProcessor"/>
<lang.documentationProvider language="JAVA" implementationClass="org.jetbrains.jet.plugin.JetQuickDocumentationProvider" order="first"/>
<documentationProvider implementation="org.jetbrains.jet.plugin.JetQuickDocumentationProvider"/>
<configurationType implementation="org.jetbrains.jet.plugin.run.JetRunConfigurationType"/>
@@ -0,0 +1,390 @@
/*
* Copyright 2010-2014 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.jet.plugin.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.Editor
import com.intellij.codeInsight.editorActions.ReferenceTransferableData
import com.intellij.codeInsight.daemon.impl.CollectHighlightsUtil
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper
import org.jetbrains.jet.lang.resolve.name.FqName
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref
import com.intellij.openapi.project.DumbService
import com.intellij.psi.PsiDocumentManager
import com.intellij.openapi.application.ApplicationManager
import java.awt.datatransfer.Transferable
import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
import org.jetbrains.jet.plugin.references.JetReference
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import com.intellij.openapi.util.TextRange
import java.util.Collections
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
import org.jetbrains.jet.lang.types.ErrorUtils
import org.jetbrains.jet.lang.psi.JetImportDirective
import org.jetbrains.jet.lang.psi.JetPackageDirective
import org.jetbrains.jet.plugin.references.JetMultiReference
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.lang.psi.JetThisExpression
import org.jetbrains.jet.lang.psi.JetSuperExpression
import org.jetbrains.jet.plugin.conversion.copy.*
import java.awt.datatransfer.UnsupportedFlavorException
import java.io.IOException
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.jet.lang.psi.JetUserType
import org.jetbrains.jet.lang.psi.JetTypeReference
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
//NOTE: this class is based on CopyPasteReferenceProcessor and JavaCopyPasteReferenceProcessor
public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<ReferenceTransferableData?> {
override fun extractTransferableData(content: Transferable): ReferenceTransferableData? {
//NOTE: copied code
var referenceData: ReferenceTransferableData? = null
if (CodeInsightSettings.getInstance()!!.ADD_IMPORTS_ON_PASTE != CodeInsightSettings.NO) {
try {
val flavor = ReferenceData.getDataFlavor()
if (flavor != null) {
referenceData = content.getTransferData(flavor) as ReferenceTransferableData
}
}
catch (ignored: UnsupportedFlavorException) {
}
catch (ignored: IOException) {
}
}
if (referenceData != null) {
// copy to prevent changing of original by convertLineSeparators
return referenceData!!.clone()
}
return null
}
override fun collectTransferableData(
file: PsiFile,
editor: Editor,
startOffsets: IntArray,
endOffsets: IntArray
): ReferenceTransferableData? {
if (file !is JetFile) {
return null
}
val collectedData = zip(startOffsets, endOffsets).toList().flatMap {
val (startOffset, endOffset) = it
CollectHighlightsUtil.getElementsInRange(file, startOffset, endOffset).flatMap { element ->
collectReferenceDataFromElement(element, file, startOffset, startOffsets, endOffsets)
}
}
if (collectedData.isEmpty()) {
return null
}
return ReferenceTransferableData(collectedData.copyToArray())
}
private fun collectReferenceDataFromElement(
element: PsiElement,
file: JetFile,
startOffset: Int,
startOffsets: IntArray,
endOffsets: IntArray
): Collection<ReferenceData> {
fun collectReferenceData(referencedDeclaration: PsiElement, referencedDescriptor: DeclarationDescriptor): ReferenceData? {
if (referencedDeclaration.isInCopiedArea(file, startOffsets, endOffsets)) {
return null
}
val isExpressionWithReceiver = element is JetSimpleNameExpression && element.getReceiverExpression() != null
if (isExpressionWithReceiver && !referencedDescriptor.isExtension) {
return null
}
val fqName = referencedDescriptor.importableFqName
if (fqName != null && referencedDescriptor.canBeReferencedViaImport()) {
return createReferenceData(element, startOffset, fqName)
}
return null
}
val isInsideIgnoredElement = PsiTreeUtil.getParentOfType(element, *IGNORE_REFERENCES_INSIDE) != null
if (isInsideIgnoredElement) {
return Collections.emptyList()
}
val reference = element.getReference()
if (reference !is JetReference) {
return Collections.emptyList()
}
val resolveMap = reference.resolveMap()
//check whether this reference is unambiguous
if (reference !is JetMultiReference<*> && resolveMap.size > 1) {
return Collections.emptyList()
}
val collectedData = ArrayList<ReferenceData>()
for ((referencedDescriptor, declarations) in resolveMap) {
if (declarations.size != 1) {
continue
}
collectedData.addIfNotNull(collectReferenceData(declarations.first(), referencedDescriptor))
}
return collectedData
}
private fun DeclarationDescriptor.canBeReferencedViaImport(): Boolean {
if (this is PackageViewDescriptor || DescriptorUtils.isTopLevelDeclaration(this)) {
return true
}
val parent = getContainingDeclaration()!!
if (parent !is ClassDescriptor || !parent.canBeReferencedViaImport()) {
return false
}
// inner class constructors can't be referenced via import
if (this is ConstructorDescriptor && parent.isInner()) {
return false
}
return this is ClassDescriptor || this is ConstructorDescriptor
}
private fun createReferenceData(element: PsiElement, startOffset: Int, fqName: FqName): ReferenceData {
val range = element.range
return ReferenceData(range.start - startOffset, range.end - startOffset, fqName.asString(), null)
}
private data class ReferenceToRestoreData(
val expression: JetElement,
val fqName: FqName,
val lengthenReference: Boolean = false
)
override fun processTransferableData (
project: Project,
editor: Editor,
bounds: RangeMarker,
caretOffset: Int,
indented: Ref<Boolean>,
value: ReferenceTransferableData?
) {
if (DumbService.getInstance(project)!!.isDumb()) {
return
}
val document = editor.getDocument()
val file = PsiDocumentManager.getInstance(project).getPsiFile(document)
if (file !is JetFile) {
return
}
PsiDocumentManager.getInstance(project).commitAllDocuments()
val referenceData = value!!.getData()!!
val referencesPossibleToRestore = findReferencesToRestore(file, bounds, referenceData)
val selectedReferencesToRestore = showRestoreReferencesDialog(project, referencesPossibleToRestore)
if (selectedReferencesToRestore.isEmpty()) {
return
}
ApplicationManager.getApplication()!!.runWriteAction(Runnable {
restoreReferences(selectedReferencesToRestore, file)
})
PsiDocumentManager.getInstance(project).commitAllDocuments()
}
private fun findReferencesToRestore(file: PsiFile, bounds: RangeMarker, referenceData: Array<out ReferenceData>): List<ReferenceToRestoreData> {
if (file !is JetFile) {
return Collections.emptyList()
}
return referenceData.map {
val referenceExpression = findReference(it, file, bounds)
if (referenceExpression != null) createReferenceToRestoreData(referenceExpression, it.fqName) else null
}.filterNotNull()
}
private fun findReference(data: ReferenceData, file: JetFile, bounds: RangeMarker): JetElement? {
val startOffset = data.startOffset + bounds.getStartOffset()
val endOffset = data.endOffset + bounds.getStartOffset()
val element = file.findElementAt(startOffset)
val desiredRange = TextRange(startOffset, endOffset)
var expression = element
while (expression != null) {
val range = expression!!.range
if (range == desiredRange && expression!!.getReference() != null) {
return expression as? JetElement
}
if (range in desiredRange) {
expression = expression!!.getParent()
}
else {
return null
}
}
return null
}
private fun createReferenceToRestoreData(expression: JetElement, originalReferencedFqName: FqName): ReferenceToRestoreData? {
val reference = expression.getReference() as? JetReference
if (reference == null) {
return null
}
val referencedDescriptors = reference.resolveToDescriptors()
val referencedFqNames = referencedDescriptors.filterNot { ErrorUtils.isError(it) } .map { it.importableFqName }
val referencesSame = referencedFqNames any { it == originalReferencedFqName }
val conflict = referencedFqNames any { it != originalReferencedFqName && (it?.shortName() == originalReferencedFqName.shortName()) }
when {
referencesSame && !conflict -> {
return null
}
!referencesSame && !conflict -> {
return ReferenceToRestoreData(expression, originalReferencedFqName)
}
conflict -> {
val mustBeReferencedWithReceiver = referencedDescriptors.any { it.isExtension }
if (!mustBeReferencedWithReceiver && LengthenReferences.canLengthenReferenceExpression(expression, originalReferencedFqName)) {
return ReferenceToRestoreData(expression, originalReferencedFqName, lengthenReference = true)
}
}
}
return null
}
private fun restoreReferences(referencesToRestore: Collection<ReferenceToRestoreData>, file: JetFile) {
for ((referenceExpression, fqName, shouldLengthen) in referencesToRestore) {
if (!shouldLengthen) {
ImportInsertHelper.addImportDirectiveIfNeeded(fqName, file)
}
else {
//TODO: try to shorten reference after (sometimes is possible), need shorten reference to support all relevant cases
LengthenReferences.lengthenReference(referenceExpression, fqName)
}
}
}
private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> {
val shouldShowDialog = CodeInsightSettings.getInstance()!!.ADD_IMPORTS_ON_PASTE == CodeInsightSettings.ASK
if (!shouldShowDialog || referencesToRestore.isEmpty()) {
return referencesToRestore
}
val fqNames = referencesToRestore. map { it.fqName.asString() }.toSortedSet()
val dialog = RestoreReferencesDialog(project, fqNames.copyToArray())
dialog.show()
val selectedFqNames = dialog.getSelectedElements()!!.toSet()
return referencesToRestore.filter { ref -> selectedFqNames.contains(ref.fqName.asString()) }
}
class object {
private val IGNORE_REFERENCES_INSIDE: Array<Class<out JetElement>?> = array(
javaClass<JetImportDirective>(),
javaClass<JetPackageDirective>(),
javaClass<JetSuperExpression>(),
javaClass<JetThisExpression>()
)
}
private object LengthenReferences {
private val LOG = Logger.getInstance(javaClass<KotlinCopyPasteReferenceProcessor>())
private fun createQualifiedExpression(project: Project, text: String): JetDotQualifiedExpression {
val newExpression = JetPsiFactory.createExpression(project, text)
LOG.assertTrue(newExpression is JetDotQualifiedExpression,
"\"${newExpression.getText()}\" is ${newExpression.javaClass}," +
"not ${javaClass<JetDotQualifiedExpression>().getSimpleName()}."
)
return newExpression as JetDotQualifiedExpression
}
fun lengthenReference(expression: JetElement, fqName: FqName) {
assert(canLengthenReferenceExpression(expression, fqName))
val project = expression.getProject()
val parent = expression.getParent()
val prefixToInsert = fqName.parent().asString()
if (parent is JetCallExpression) {
val text = "$prefixToInsert.${parent.getText()}"
parent.replace(createQualifiedExpression(project, text))
}
else if (parent is JetUserType) {
val typeReference = PsiTreeUtil.getParentOfType(expression, javaClass<JetTypeReference>())
LOG.assertTrue(typeReference != null, "JetUserType is expected to have parent of type JetTypeReference:\n" +
"At: ${DiagnosticUtils.atLocation(expression)}\nFILE:\n${expression.getContainingFile()!!.getText()}")
typeReference!!.replace(JetPsiFactory.createType(project, "$prefixToInsert.${typeReference.getText()}"))
}
else {
expression.replace(createQualifiedExpression(project, fqName.asString()))
}
}
fun canLengthenReferenceExpression(expression: JetElement, fqName: FqName): Boolean {
if (fqName.pathSegments().size() < 2) {
return false
}
if (expression is JetSimpleNameExpression && expression.getReceiverExpression() == null) {
return true
}
return false
}
}
}
private val ReferenceData.fqName: FqName
get() = FqName(qClassName!!)
private fun zip(first: IntArray, second: IntArray): Iterator<Pair<Int, Int>> {
assert(first.size == second.size)
return first.iterator().zip(second.iterator())
}
private fun PsiElement.isInCopiedArea(fileCopiedFrom: JetFile, startOffsets: IntArray, endOffsets: IntArray): Boolean {
if (getContainingFile() != fileCopiedFrom) {
return false
}
return zip(startOffsets, endOffsets).any {
val (start, end) = it
range in TextRange(start, end)
}
}
private val DeclarationDescriptor.importableFqName: FqName?
get() {
if (this is ConstructorDescriptor) return getContainingDeclaration().importableFqName
val mayBeUnsafe = DescriptorUtils.getFqName(this)
return if (mayBeUnsafe.isSafe()) {
mayBeUnsafe.toSafe()
}
else {
null
}
}
private val DeclarationDescriptor.isExtension: Boolean
get() = this is CallableDescriptor && getReceiverParameter() != null
private fun <T : Any> MutableCollection<T>.addIfNotNull(el: T?) = ContainerUtil.addIfNotNull(this, el)
@@ -0,0 +1,105 @@
/*
* Copyright 2010-2014 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.jet.plugin.codeInsight;
import com.intellij.CommonBundle;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.ide.util.FQNameCellRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.psi.PsiClass;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import java.awt.*;
import static com.intellij.util.ui.UIUtil.ComponentStyle.SMALL;
import static com.intellij.util.ui.UIUtil.FontColor.BRIGHTER;
//TODO: this is a copy of com.intellij.codeInsight.editorActions.RestoreReferencesDialog
//consider improving rendering of list cells if you decide to submit pull request to idea (or improve this version)
public class RestoreReferencesDialog extends DialogWrapper {
private final Object[] myNamedElements;
private JList myList;
private Object[] mySelectedElements = PsiClass.EMPTY_ARRAY;
private boolean myContainsClassesOnly = true;
public RestoreReferencesDialog(final Project project, final Object[] elements) {
super(project, true);
myNamedElements = elements;
for (Object element : elements) {
if (!(element instanceof PsiClass)) {
myContainsClassesOnly = false;
break;
}
}
if (myContainsClassesOnly) {
setTitle(CodeInsightBundle.message("dialog.import.on.paste.title"));
}
else {
setTitle(CodeInsightBundle.message("dialog.import.on.paste.title2"));
}
init();
myList.setSelectionInterval(0, myNamedElements.length - 1);
}
@Override
protected void doOKAction() {
Object[] values = myList.getSelectedValues();
mySelectedElements = new Object[values.length];
System.arraycopy(values, 0, mySelectedElements, 0, values.length);
super.doOKAction();
}
@Override
protected JComponent createCenterPanel() {
final JPanel panel = new JPanel(new BorderLayout(UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
myList = new JBList(myNamedElements);
myList.setCellRenderer(new FQNameCellRenderer());
panel.add(ScrollPaneFactory.createScrollPane(myList), BorderLayout.CENTER);
panel.add(new JBLabel(myContainsClassesOnly ?
CodeInsightBundle.message("dialog.paste.on.import.text") :
CodeInsightBundle.message("dialog.paste.on.import.text2"), SMALL, BRIGHTER), BorderLayout.NORTH);
final JPanel buttonPanel = new JPanel(new VerticalFlowLayout());
final JButton okButton = new JButton(CommonBundle.getOkButtonText());
getRootPane().setDefaultButton(okButton);
buttonPanel.add(okButton);
final JButton cancelButton = new JButton(CommonBundle.getCancelButtonText());
buttonPanel.add(cancelButton);
panel.setPreferredSize(new Dimension(500, 400));
return panel;
}
@Override
protected String getDimensionServiceKey(){
return "#com.intellij.codeInsight.editorActions.RestoreReferencesDialog";
}
public Object[] getSelectedElements(){
return mySelectedElements;
}
}
@@ -0,0 +1,21 @@
package to
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) {
g(A(c).ext())
O1.f()
O2
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
ClassObject
}
@@ -0,0 +1,56 @@
package a
import a.E.ENTRY
import a.Outer.*
trait T
class A(i: Int) {}
val c = 0
fun g(a: A) {}
fun A.ext()
object O1 {
fun f() {
}
}
object O2 {
}
enum class E {
ENTRY
}
class Outer {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
class ClassObject {
class object {
}
}
<selection>fun f(a: A, t: T) {
g(A(c).ext())
O1.f()
O2
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
ClassObject
}</selection>
@@ -0,0 +1,5 @@
package to
import a.*
<caret>
@@ -0,0 +1,7 @@
package b
import a.A
fun f(a: A) {
A()
}
@@ -0,0 +1,7 @@
package a
class A() {}
<selection>fun f(a: A) {
A()
}</selection>
@@ -0,0 +1,5 @@
package b
import a.A
<caret>
@@ -0,0 +1,3 @@
package to
fun f() = c + d + g + j()
@@ -0,0 +1,14 @@
package a
// ALLOW_UNRESOLVED
class A(val c: Int = 1) {
var d = 2
val g: Int
get() = d
fun j() = c
<selection>fun f() = c + d + g + j()</selection>
}
@@ -0,0 +1,10 @@
package to
import a.T
import a.A
import a.B
fun g(t: T): Int {
g(A)
B.f()
}
@@ -0,0 +1,21 @@
package a
trait T
class A {
class object: T {
}
}
class B {
class object {
fun f(): Int
}
}
<selection>fun g(t: T): Int {
g(A)
B.f()
}</selection>
@@ -0,0 +1,5 @@
package to
fun g() {
f()
}
@@ -0,0 +1,13 @@
package a
// ALLOW_UNRESOLVED
//NOTE: is is an unsupported case
class A {
<selection>fun g() {
f()
}</selection>
class object {
fun f()
}
}
@@ -0,0 +1,11 @@
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
fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
}
@@ -0,0 +1,22 @@
package a
import a.Outer.*
class Outer {
class object {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
}
<selection>fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
}</selection>
@@ -0,0 +1,6 @@
package to
fun f(i: a.a) {
a.a
a.a()
}
@@ -0,0 +1,12 @@
package a
//NOTE: this case can be improved by using shorten reference after importing
class a {
class object {
}
}
<selection>fun f(i: a) {
a
a()
}</selection>
@@ -0,0 +1,6 @@
package to
import a.A
fun f(a: A) {
}
@@ -0,0 +1,6 @@
package a
class A() {}
<selection>fun f(a: A) {
}</selection>
@@ -0,0 +1,19 @@
package to
class C {
}
val c = 1
fun g() {
}
fun C.ext() {
}
fun f() {
a.c
a.g()
a.C()
a.C().ext()
}
@@ -0,0 +1,21 @@
package a
// ALLOW_UNRESOLVED
class C {
}
val c = 1
fun g() {
}
fun C.ext() {
}
<selection>fun f() {
c
g()
C()
C().ext()
}</selection>
@@ -0,0 +1,14 @@
package to
class C {
}
val c = 1
fun g() {
}
fun C.ext() {
}
<caret>
@@ -0,0 +1,7 @@
package to
class A<T> {
}
fun f(b: a.A<Int>) {
}
@@ -0,0 +1,7 @@
package a
class A<T> {
}
<selection>fun f(b: A<Int>) {
}</selection>
@@ -0,0 +1,6 @@
package to
class A<T> {
}
<caret>
@@ -0,0 +1,7 @@
package to
class A() {
}
fun f(a: a.A) {
}
@@ -0,0 +1,7 @@
package a
class A() {
}
<selection>fun f(a: A) {
}</selection>
@@ -0,0 +1,6 @@
package to
class A() {
}
<caret>
@@ -0,0 +1,6 @@
package to
fun f() {
}
fun g() = a.f()
@@ -0,0 +1,6 @@
package a
fun f() {
}
<selection>fun g() = f()</selection>
@@ -0,0 +1,6 @@
package to
fun f() {
}
<caret>
@@ -0,0 +1,7 @@
package b
import a.A
fun b() {
A()
}
@@ -0,0 +1,5 @@
package a
class A() {}
val c = <selection>A()</selection>
@@ -0,0 +1,5 @@
package b
fun b() {
<caret>
}
@@ -0,0 +1,21 @@
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.Outer
import a.Outer.Inner.IN2
fun f(p1: NN, p2: NI, p3: IN, p4: II) {
NN2()
with(Outer.Nested()) {
NI2()
}
IN2()
with(Outer().Inner()) {
II2()
}
}
@@ -0,0 +1,41 @@
package a
import a.Outer.Nested.*
import a.Outer.Inner.*
class Outer {
class Nested {
class NN {
}
class NN2 {
}
inner class NI {
}
inner class NI2 {
}
}
inner class Inner {
class IN {
}
class IN2 {
}
inner class II {
}
inner class II2 {
}
}
}
fun <T> with(v: T, body: T.() -> Unit) = v.body()
<selection>fun f(p1: NN, p2: NI, p3: IN, p4: II) {
NN2()
with(Outer.Nested()) {
NI2()
}
IN2()
with(Outer().Inner()) {
II2()
}
}</selection>
@@ -0,0 +1,3 @@
package to
val c = A()
@@ -0,0 +1,6 @@
// ALLOW_UNRESOLVED
class A {
}
<selection>val c = A()</selection>
@@ -0,0 +1,9 @@
package to
import a.A
import a.get
import a.set
class B {
var a by A()
}
@@ -0,0 +1,16 @@
package a
trait T
fun T.get(thisRef: B, desc: PropertyMetadata): Int {
return 3
}
fun T.set(thisRef: B, desc: PropertyMetadata, value: Int) {
}
class A(): T
<selection>class B {
var a by A()
}</selection>
@@ -0,0 +1,16 @@
package java;
public class JavaClass {
public static int staticMethod() {
return 3;
}
public void method() {
}
public class InnerClass {
}
public class NestedClass {
}
}
@@ -0,0 +1,12 @@
package to
import java.JavaClass
import java.JavaClass.staticMethod
import java.JavaClass.NestedClass
fun f(c: JavaClass) {
staticMethod()
c.method()
c.InnerClass()
NestedClass()
}
@@ -0,0 +1,13 @@
package a
import java.JavaClass
import java.JavaClass.NestedClass
import java.JavaClass.staticMethod
<selection>fun f(c: JavaClass) {
staticMethod()
c.method()
c.InnerClass()
NestedClass()
}</selection>
@@ -0,0 +1,28 @@
package to
import d.A
import d.T
import d.g
import d.c
import d.ext
import d.O1
import d.O2
import d.E.ENTRY
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
fun f(a: A, t: T) {
g(A(c).ext())
O1.f()
O2
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
ClassObject
}
@@ -0,0 +1,18 @@
// WITH_LIBRARY: copyPaste/imports/KotlinLibrary
package a
import d.*
import d.E.ENTRY
import d.Outer.*
<selection>fun f(a: A, t: T) {
g(A(c).ext())
O1.f()
O2
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
ClassObject
}</selection>
@@ -0,0 +1,5 @@
package to
import kotlin.concurrent.FunctionalList
fun f(l: FunctionalList<Int>) {}
@@ -0,0 +1,7 @@
// RUNTIME
package g
import kotlin.concurrent.FunctionalList
<selection>fun f(l: FunctionalList<Int>) {}</selection>
@@ -0,0 +1,11 @@
package to
import a.A.A
import a.A.B
import a.A.C
fun f() {
A
B
C
}
@@ -0,0 +1,13 @@
package a
enum class A {
A
B
C
<selection>fun f() {
A
B
C
}</selection>
}
@@ -0,0 +1,11 @@
package to
import a.f
import a.p
import a.g
fun foo() {
3.f()
2.p
3.g = 5
}
@@ -0,0 +1,16 @@
package a
fun Int.f() {}
val Int.p: Int
get() = 2
var Int.g: Int
get() = 1
set(i : Int) = Unit
<selection>fun foo() {
3.f()
2.p
3.g = 5
}</selection>
@@ -0,0 +1,10 @@
package to
import a.A
import a.plus
import a.infix
fun f(a: A) {
a + a
a infix 1
}
@@ -0,0 +1,12 @@
package a
class A() {}
fun A.plus(a: A) = this
fun A.infix(i: Int) = i
<selection>fun f(a: A) {
a + a
a infix 1
}</selection>
@@ -0,0 +1,16 @@
package to
import a.A
fun A.ext() {
}
var A.p: Int
get() = 2
set(i: Int) = throw UnsupportedOperationException()
fun A.f() {
ext()
p
p = 3
}
@@ -0,0 +1,17 @@
package a
class A() {
}
fun A.ext() {
}
var A.p: Int
get() = 2
set(i: Int) = throw UnsupportedOperationException()
<selection>fun A.f() {
ext()
p
p = 3
}</selection>
@@ -0,0 +1,12 @@
package to
import a.A
fun A.ext() {
}
var A.p: Int
get() = 2
set(i: Int) = throw UnsupportedOperationException()
<caret>
@@ -0,0 +1,26 @@
package to
import a.A
fun A.ext() {
}
fun A.infix(a: A) {
}
fun A.plus(a: A) {
}
fun A.minus() {
}
val A.p: Int
get() = 2
fun f() {
A().ext()
A() + A()
A() infix A()
-A()
A().p
}
@@ -0,0 +1,27 @@
package a
class A {
}
fun A.ext() {
}
fun A.infix(a: A) {
}
fun A.plus(a: A) {
}
fun A.minus() {
}
val A.p: Int
get() = 2
<selection>fun f() {
A().ext()
A() + A()
A() infix A()
-A()
A().p
}</selection>
@@ -0,0 +1,20 @@
package to
import a.A
fun A.ext() {
}
fun A.infix(a: A) {
}
fun A.plus(a: A) {
}
fun A.minus() {
}
val A.p: Int
get() = 2
<caret>
@@ -0,0 +1,11 @@
package to
import a.A
import a.iterator
import a.next
import a.hasNext
fun f() {
for (i in A()) {
}
}
@@ -0,0 +1,18 @@
package a
class A() {
}
class B() {
}
fun B.next(): Int = 3
fun B.hasNext(): Boolean = false
fun A.iterator() = B()
<selection>fun f() {
for (i in A()) {
}
}</selection>
@@ -0,0 +1,3 @@
package to
fun f(t: a.b.c.A): a.b.c.B = a.b.c.B()
@@ -0,0 +1,7 @@
package a.b.c
class A() {}
class B() {}
<selection>fun f(t: a.b.c.A): a.b.c.B = a.b.c.B()</selection>
@@ -0,0 +1,7 @@
package to
import a.g
fun f() {
g()
}
@@ -0,0 +1,8 @@
package a
fun g() {
}
<selection>fun f() {
g()
}</selection>
@@ -0,0 +1,7 @@
package b
import a.g
fun f() {
g()
}
@@ -0,0 +1,8 @@
package a
fun g() {
}
<selection>fun f() {
g()
}</selection>
@@ -0,0 +1,5 @@
package b
import a.g
<caret>
@@ -0,0 +1,7 @@
package to
fun f() {
val r = arrayListOf<T>()
r.addAll(b)
val x = c
}
@@ -0,0 +1,10 @@
//RUNTIME
// ALLOW_UNRESOLVED
package a
fun <T> f(c: Int, b: List<T>) {
<selection>val r = arrayListOf<T>()
r.addAll(b)
val x = c</selection>
}
@@ -0,0 +1,5 @@
package to
fun f() {
<caret>
}
@@ -0,0 +1,8 @@
package to
import a.A
import a.get
fun f() {
A()[""]
}
@@ -0,0 +1,11 @@
package a
class A {
}
fun A.get(s: String) {
}
<selection>fun f() {
A()[""]
}</selection>
@@ -0,0 +1,42 @@
package d
trait T
class A(i: Int) {}
val c = 0
fun g(a: A) {}
fun A.ext()
object O1 {
fun f() {
}
}
object O2 {
}
enum class E {
ENTRY
}
class Outer {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
class ClassObject {
class object {
}
}
@@ -0,0 +1,28 @@
package to
import d.A
import d.T
import d.g
import d.c
import d.ext
import d.O1
import d.O2
import d.E.ENTRY
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
fun f(a: A, t: T) {
g(A(c).ext())
O1.f()
O2
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
ClassObject
}
@@ -0,0 +1,16 @@
package a
import d.*
import d.E.ENTRY
import d.Outer.*
<selection>fun f(a: A, t: T) {
g(A(c).ext())
O1.f()
O2
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
ClassObject
}</selection>
@@ -0,0 +1,5 @@
package to
package a
import a.A
@@ -0,0 +1,8 @@
// ALLOW_UNRESOLVED
<selection>package a
import a.A</selection>
class A() {
}
@@ -0,0 +1,3 @@
package to
<caret>
@@ -0,0 +1,14 @@
package to
import a.f
import a.A.Nested
import a.ext
fun g() {
f {
Inner()
Nested()
foo()
ext()
}
}
@@ -0,0 +1,28 @@
package a
import a.A.Inner
import a.A.Nested
class A {
inner class Inner {
}
class Nested {
}
fun foo() {
}
}
fun A.ext() {
}
fun f(body: A.() -> Unit) {
}
<selection>fun g() {
f {
Inner()
Nested()
foo()
ext()
}
}</selection>
@@ -0,0 +1,22 @@
package to
class Outer {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
enum class E {
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
}
@@ -0,0 +1,30 @@
package a
// ALLOW_UNRESOLVED
//NOTE: This test shows a corner case which is not covered fully by current implementation
// All we do now is avoid inserting wrong imports (we do not import anything for declaration from copied block)
// To cover this case properly and insert good imports some other approach should be used (some sophisticated heuristic might do the job)
import a.Outer.*
import a.E.ENTRY
<selection>class Outer {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
enum class E {
ENTRY
}
fun f2(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
}</selection>
@@ -0,0 +1,13 @@
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
fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
Outer().Inner2()
}
+23
View File
@@ -0,0 +1,23 @@
package a
import a.Outer.*
class Outer {
inner class Inner {
}
inner class Inner2 {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
<selection>fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
Outer().Inner2()
}</selection>
@@ -0,0 +1,8 @@
package to
import a.A
import a.invoke
fun f(a: A) {
a()
}
+11
View File
@@ -0,0 +1,11 @@
package a
class A {
}
fun A.invoke() {
}
<selection>fun f(a: A) {
a()
}</selection>
@@ -0,0 +1,10 @@
package a;
class JavaM {
static void staticMethod() {
}
}
class JavaF {
static int staticField = 0;
}
@@ -0,0 +1,9 @@
package to
import a.JavaM
import a.JavaF
fun f() {
JavaM.staticMethod()
JavaF.staticField()
}
@@ -0,0 +1,6 @@
package a
<selection>fun f() {
JavaM.staticMethod()
JavaF.staticField()
}</selection>
@@ -0,0 +1,43 @@
package d
trait T
class A(i: Int) {}
val c: Int = 0
fun g(a: A) {}
fun A.ext() {
}
object O1 {
fun f() {
}
}
object O2 {
}
enum class E {
ENTRY
}
class Outer {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
}
object NestedObj {
}
trait NestedTrait {
}
annotation class NestedAnnotation
}
class ClassObject {
class object {
}
}
@@ -0,0 +1,6 @@
package to
fun f() {
val b = A()
val a = g()
}
+14
View File
@@ -0,0 +1,14 @@
package a
// ALLOW_UNRESOLVED
fun f() {
fun g(): Int {
}
class A() {
}
<selection>val b = A()
val a = g()</selection>
}
@@ -0,0 +1,5 @@
package to
fun f() {
<caret>
}
@@ -0,0 +1,13 @@
package to
import a.A
import a.B
import a.next
import a.hasNext
fun A.iterator() = B()
fun f() {
for (i in A()) {
}
}
@@ -0,0 +1,18 @@
package a
class A() {
}
class B() {
}
fun B.next(): Int = 3
fun B.hasNext(): Boolean = false
<selection>fun A.iterator() = B()
fun f() {
for (i in A()) {
}
}</selection>
@@ -0,0 +1,6 @@
package to
import a.d
import a.b
fun f(c: IntRange) = d + b

Some files were not shown because too many files have changed in this diff Show More