Show Review Added Imports on paste action

Fixed multi-caret selection in kotlin-to-kotlin copy-paste processor

Relates to #KT-33939
This commit is contained in:
Vladimir Dolzhenko
2020-01-20 21:39:12 +01:00
parent 84fd5b35eb
commit db8b0a6593
19 changed files with 463 additions and 48 deletions
@@ -14,6 +14,7 @@ class BasicKotlinReferenceTransferableData(
val sourceFileUrl: String,
val packageName: String,
val imports: List<String>,
val sourceTextOffset: Int,
val sourceText: String,
val textRanges: List<TextRange>
) : TextBlockTransferableData, Cloneable, Serializable {
@@ -37,10 +37,13 @@ import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.concurrency.AppExecutorUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.allowResolveInDispatchThread
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.ReviewAddedImports.reviewAddedImports
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
import org.jetbrains.kotlin.idea.core.util.end
import org.jetbrains.kotlin.idea.core.util.range
@@ -62,6 +65,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.findFunction
@@ -109,15 +113,14 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
val endOfImportsOffset = file.importDirectives.map { it.endOffset }.max() ?: file.packageDirective?.endOffset ?: 0
val offsetDelta = if (startOffsets.any { it <= endOfImportsOffset }) 0 else endOfImportsOffset
val text = file.text.substring(offsetDelta)
val ranges = startOffsets.indices.map {
TextRange(startOffsets[it] - offsetDelta, endOffsets[it] - offsetDelta)
}
val ranges = startOffsets.indices.map { TextRange(startOffsets[it], endOffsets[it]) }
return listOf(
BasicKotlinReferenceTransferableData(
sourceFileUrl = file.virtualFile.url,
packageName = packageName,
imports = imports,
sourceTextOffset = offsetDelta,
sourceText = text,
textRanges = ranges
)
@@ -165,8 +168,8 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
for ((range, elements) in elementsByRange) {
elements.forEachDescendant { ktElement ->
result.addReferenceDataInsideElement(
ktElement, file, range.start, ranges, bindingContext,
fakePackageName = fakePackageName, sourcePackageName = sourcePackageName, targetPackageName = targetPackageName
ktElement, file, ranges, bindingContext, fakePackageName = fakePackageName,
sourcePackageName = sourcePackageName, targetPackageName = targetPackageName
)
}
}
@@ -190,7 +193,6 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
private fun MutableCollection<KotlinReferenceData>.addReferenceDataInsideElement(
ktElement: KtElement,
file: KtFile,
startOffset: Int,
textRanges: List<TextRange>,
bindingContext: BindingContext,
fakePackageName: String? = null,
@@ -230,14 +232,14 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
val kind = KotlinReferenceData.Kind.fromDescriptor(descriptor) ?: continue
val isQualifiable = KotlinReferenceData.isQualifiable(ktElement, descriptor)
val relativeStart = ktElement.range.start - startOffset
val relativeEnd = ktElement.range.end - startOffset
val relativeStart = ktElement.range.start
val relativeEnd = ktElement.range.end
add(KotlinReferenceData(relativeStart, relativeEnd, fqName, isQualifiable, kind))
}
}
private data class ReferenceToRestoreData(val reference: KtReference, val refData: KotlinReferenceData)
private data class PsiElementByTextRange(val textRange: TextRange, val element: PsiElement)
private data class PsiElementByTextRange(val originalTextRange: TextRange, val element: PsiElement)
override fun processTransferableData(
project: Project,
@@ -255,30 +257,42 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
val file = PsiDocumentManager.getInstance(project).getPsiFile(document)
if (file !is KtFile) return
processReferenceData(project, file, bounds.startOffset, values.single())
processReferenceData(project, editor, file, bounds.startOffset, values.single())
}
private fun processReferenceData(
project: Project,
editor: Editor,
file: KtFile,
blockStart: Int,
transferableData: BasicKotlinReferenceTransferableData
) {
PsiDocumentManager.getInstance(project).commitAllDocuments()
var startOffsetDelta = blockStart
// figure out candidate elements in UI thread in paste phase
// as psi elements could be changed later on - relative offsets are used for mapping purposes
val elementsByRange = transferableData.textRanges.flatMap { originalTextRange ->
val textRange = TextRange(blockStart, blockStart + originalTextRange.endOffset - originalTextRange.startOffset)
val textRange = TextRange(
startOffsetDelta,
startOffsetDelta + originalTextRange.endOffset - originalTextRange.startOffset
)
startOffsetDelta = startOffsetDelta + originalTextRange.endOffset - originalTextRange.startOffset + 1
file.elementsInRange(textRange)
.filter { it is KtElement || it is KDocElement }
.map { PsiElementByTextRange(it.textRange, it) }
.map {
val range = TextRange(
originalTextRange.startOffset + it.textRange.startOffset - textRange.startOffset,
originalTextRange.startOffset + it.textRange.endOffset - textRange.startOffset
)
PsiElementByTextRange(range, it)
}
}
processReferenceData(project, file) { indicator: ProgressIndicator ->
processReferenceData(project, editor, file) { indicator: ProgressIndicator ->
findReferenceDataToRestore(
file,
blockStart,
indicator,
elementsByRange,
transferableData
@@ -286,14 +300,15 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
}
}
fun processReferenceData(project: Project, file: KtFile, blockStart: Int, referenceData: Array<KotlinReferenceData>) {
processReferenceData(project, file) { indicator: ProgressIndicator ->
fun processReferenceData(project: Project, editor: Editor, file: KtFile, blockStart: Int, referenceData: Array<KotlinReferenceData>) {
processReferenceData(project, editor, file) { indicator: ProgressIndicator ->
findReferencesToRestore(file, blockStart, referenceData)
}
}
private fun processReferenceData(
project: Project,
editor: Editor,
file: KtFile,
findReferenceProvider: (indicator: ProgressIndicator) -> List<ReferenceToRestoreData>
) {
@@ -309,13 +324,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
.finishOnUiThread(
ModalityState.defaultModalityState(),
Consumer<List<ReferenceToRestoreData>> { referencesPossibleToRestore ->
val selectedReferencesToRestore =
showRestoreReferencesDialog(project, referencesPossibleToRestore)
if (selectedReferencesToRestore.isEmpty()) return@Consumer
project.executeWriteCommand("resolve pasted references") {
restoreReferences(selectedReferencesToRestore, file)
}
applyResolvedImports(project, referencesPossibleToRestore, file, editor)
}
)
.withDocumentsCommitted(project)
@@ -326,9 +335,26 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
ProgressManager.getInstance().run(task)
}
private fun applyResolvedImports(
project: Project,
referencesPossibleToRestore: List<ReferenceToRestoreData>,
file: KtFile,
editor: Editor
) {
val selectedReferencesToRestore =
showRestoreReferencesDialog(project, referencesPossibleToRestore)
if (selectedReferencesToRestore.isEmpty()) return
project.executeWriteCommand("resolve pasted references") {
val imported = TreeSet<String>()
restoreReferences(selectedReferencesToRestore, file, imported)
reviewAddedImports(project, editor, file, imported)
}
}
private fun findReferenceDataToRestore(
file: PsiFile,
blockStart: Int,
indicator: ProgressIndicator,
elementsByRange: List<PsiElementByTextRange>,
transferableData: BasicKotlinReferenceTransferableData
@@ -339,7 +365,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
val element = elementByTextRange.element
if (!element.isValid) return@flatMap emptyList<Pair<TextRange, KtReference>>()
val refElementsRanges = mutableListOf<Pair<TextRange, KtReference>>()
val offsetDelta = element.startOffset - elementByTextRange.textRange.startOffset
val offsetDelta = elementByTextRange.originalTextRange.startOffset - element.textRange.startOffset
element.forEachDescendant { ktElement ->
indicator.checkCanceled()
@@ -347,12 +373,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
val textRange = ref.element.textRange
findReference(file, textRange)?.let {
// remap reference to the original (as it was on paste phase) text range
val range = if (offsetDelta == 0) textRange
else
TextRange(
textRange.startOffset - offsetDelta,
textRange.endOffset - offsetDelta
)
val range = TextRange(textRange.startOffset + offsetDelta, textRange.endOffset + offsetDelta)
refElementsRanges.add(range to it)
}
}
@@ -384,7 +405,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
ctxFile
)
val offsetDelta = dummyOrigFileProlog.length
val offsetDelta = dummyOrigFileProlog.length - transferableData.sourceTextOffset
val dummyOriginalFileTextRanges =
// it is required as it is shifted by dummy prolog
@@ -392,12 +413,14 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
// Step 1. Find references in copied blocks of (recreated) source file
val sourceFileBasedReferences =
collectReferenceData(dummyOriginalFileTextRanges, dummyOriginalFile, file, fakePackageName, sourcePackageName)
collectReferenceData(dummyOriginalFileTextRanges, dummyOriginalFile, file, fakePackageName, sourcePackageName).map {
it.copy(startOffset = it.startOffset - offsetDelta, endOffset = it.endOffset - offsetDelta)
}
indicator.checkCanceled()
// Step 2. Find references to restore in a target file
return findReferencesToRestore(file, blockStart, sourceFileBasedReferences, referencesByRange)
return findReferencesToRestore(file, sourceFileBasedReferences, referencesByRange)
}
private fun buildDummySourceScope(
@@ -487,17 +510,15 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
private fun findReferencesToRestore(
file: PsiFile,
blockStart: Int,
referenceData: List<KotlinReferenceData>,
referencesByPosition: Map<TextRange, KtReference>
): List<ReferenceToRestoreData> =
// use already found reference candidates - so file could be changed
findReferences(file, referenceData
.map {
val textRange = TextRange(it.startOffset + blockStart, it.endOffset + blockStart)
val reference = referencesByPosition[textRange]
it to if (reference?.element?.isValid == true) reference else null
})
findReferences(file, referenceData.map {
val textRange = TextRange(it.startOffset, it.endOffset)
val reference = referencesByPosition[textRange]
it to if (reference?.element?.isValid == true) reference else null
})
private fun findReferences(
file: PsiFile,
@@ -575,7 +596,11 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
return reference.resolveToDescriptors(bindingContext).toList()
}
private fun restoreReferences(referencesToRestore: Collection<ReferenceToRestoreData>, file: KtFile) {
private fun restoreReferences(
referencesToRestore: Collection<ReferenceToRestoreData>,
file: KtFile,
imported: MutableSet<String>
) {
val importHelper = ImportInsertHelper.getInstance(file.project)
val smartPointerManager = SmartPointerManager.getInstance(file.project)
@@ -606,9 +631,14 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
for (descriptor in descriptorsToImport) {
importHelper.importDescriptor(file, descriptor)
descriptor.getImportableDescriptor().importableFqName?.let { imported.add(it.asString()) }
}
for ((pointer, fqName) in bindingRequests) {
pointer.element?.mainReference?.bindToFqName(fqName, KtSimpleNameReference.ShorteningMode.DELAYED_SHORTENING)
pointer.element?.mainReference?.let {
it.bindToFqName(fqName, KtSimpleNameReference.ShorteningMode.DELAYED_SHORTENING)
imported.add(fqName.asString())
}
}
performDelayedRefactoringRequests(file.project)
}
@@ -0,0 +1,108 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.hint.HintManager
import com.intellij.codeInsight.hint.HintManagerImpl
import com.intellij.codeInsight.hint.HintUtil
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.ui.LightweightHint
import com.intellij.util.ArrayUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.imports.KotlinImportOptimizer
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.*
import javax.swing.event.HyperlinkEvent
import javax.swing.event.HyperlinkListener
object ReviewAddedImports {
@get:TestOnly
var importsToBeReviewed: Collection<String> = emptyList()
@get:TestOnly
var importsToBeDeleted: Collection<String> = emptyList()
fun reviewAddedImports(
project: Project,
editor: Editor,
file: KtFile,
imported: TreeSet<String>
) {
if (CodeInsightSettings.getInstance().ADD_IMPORTS_ON_PASTE == CodeInsightSettings.YES &&
!imported.isEmpty()
) {
if (ApplicationManager.getApplication().isUnitTestMode) {
importsToBeReviewed = imported
removeImports(project, file, importsToBeDeleted)
return
}
val notificationText = CodeInsightBundle
.message("copy.paste.reference.notification", imported.size)
ApplicationManager.getApplication().invokeLater(
Runnable {
showHint(
editor,
notificationText,
HyperlinkListener { e: HyperlinkEvent ->
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
reviewImports(project, file, imported)
}
}
)
}, ModalityState.NON_MODAL
)
}
}
private fun showHint(
editor: Editor,
info: String,
hyperlinkListener: HyperlinkListener
) {
if (ApplicationManager.getApplication().isUnitTestMode) return
val hint = LightweightHint(HintUtil.createInformationLabel(info, hyperlinkListener, null, null))
val flags = HintManager.HIDE_BY_ANY_KEY or HintManager.HIDE_BY_TEXT_CHANGE
HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, HintManager.UNDER, flags, 0, false)
}
private fun reviewImports(
project: Project,
file: KtFile,
importedClasses: Set<String>
) {
val dialog = RestoreReferencesDialog(project, ArrayUtil.toObjectArray(importedClasses))
dialog.title = CodeInsightBundle.message("dialog.import.on.paste.title3")
if (dialog.showAndGet()) {
removeImports(project, file, dialog.selectedElements.map { it as String }.toSortedSet())
}
}
private fun removeImports(
project: Project,
file: KtFile,
importsToRemove: Collection<String>
) {
if (importsToRemove.isEmpty()) return
WriteCommandAction.runWriteCommandAction(project, "revert applied imports", null, Runnable {
val newImports = file.importDirectives.mapNotNull {
val importedFqName = it.importedFqName ?: return@mapNotNull null
if (importsToRemove.contains(importedFqName.asString())) return@mapNotNull null
ImportPath(importedFqName, it.isAllUnder, it.aliasName?.let { alias -> Name.identifier(alias) })
}
KotlinImportOptimizer.replaceImports(file, newImports)
})
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.hint.HintManager
import com.intellij.codeInsight.hint.HintManagerImpl
import com.intellij.codeInsight.hint.HintUtil
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.ui.LightweightHint
import com.intellij.util.ArrayUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.imports.KotlinImportOptimizer
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.*
import javax.swing.event.HyperlinkEvent
import javax.swing.event.HyperlinkListener
object ReviewAddedImports {
@get:TestOnly
var importsToBeReviewed: Collection<String> = emptyList()
@get:TestOnly
var importsToBeDeleted: Collection<String> = emptyList()
fun reviewAddedImports(
project: Project,
editor: Editor,
file: KtFile,
imported: TreeSet<String>
) {
if (CodeInsightSettings.getInstance().ADD_IMPORTS_ON_PASTE == CodeInsightSettings.YES &&
!imported.isEmpty()
) {
if (ApplicationManager.getApplication().isUnitTestMode) {
importsToBeReviewed = imported
removeImports(project, file, importsToBeDeleted)
return
}
// there is actual no such functionality in 191
}
}
private fun removeImports(
project: Project,
file: KtFile,
importsToRemove: Collection<String>
) {
if (importsToRemove.isEmpty()) return
WriteCommandAction.runWriteCommandAction(project, "revert applied imports", null, Runnable {
val newImports = file.importDirectives.mapNotNull {
val importedFqName = it.importedFqName ?: return@mapNotNull null
if (importsToRemove.contains(importedFqName.asString())) return@mapNotNull null
ImportPath(importedFqName, it.isAllUnder, it.aliasName?.let { alias -> Name.identifier(alias) })
}
KotlinImportOptimizer.replaceImports(file, newImports)
})
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.hint.HintManager
import com.intellij.codeInsight.hint.HintManagerImpl
import com.intellij.codeInsight.hint.HintUtil
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.ui.LightweightHint
import com.intellij.util.ArrayUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.imports.KotlinImportOptimizer
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.*
import javax.swing.event.HyperlinkEvent
import javax.swing.event.HyperlinkListener
object ReviewAddedImports {
@get:TestOnly
var importsToBeReviewed: Collection<String> = emptyList()
@get:TestOnly
var importsToBeDeleted: Collection<String> = emptyList()
fun reviewAddedImports(
project: Project,
editor: Editor,
file: KtFile,
imported: TreeSet<String>
) {
if (CodeInsightSettings.getInstance().ADD_IMPORTS_ON_PASTE == CodeInsightSettings.YES &&
!imported.isEmpty()
) {
if (ApplicationManager.getApplication().isUnitTestMode) {
importsToBeReviewed = imported
removeImports(project, file, importsToBeDeleted)
return
}
// there is actual no such functionality in 192
}
}
private fun removeImports(
project: Project,
file: KtFile,
importsToRemove: Collection<String>
) {
if (importsToRemove.isEmpty()) return
WriteCommandAction.runWriteCommandAction(project, "revert applied imports", null, Runnable {
val newImports = file.importDirectives.mapNotNull {
val importedFqName = it.importedFqName ?: return@mapNotNull null
if (importsToRemove.contains(importedFqName.asString())) return@mapNotNull null
ImportPath(importedFqName, it.isAllUnder, it.aliasName?.let { alias -> Name.identifier(alias) })
}
KotlinImportOptimizer.replaceImports(file, newImports)
})
}
}
@@ -130,7 +130,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
rangeMarker.isGreedyToLeft = true
rangeMarker.isGreedyToRight = true
KotlinCopyPasteReferenceProcessor().processReferenceData(project, targetFile, bounds.start, referenceData.toTypedArray())
KotlinCopyPasteReferenceProcessor().processReferenceData(project, editor, targetFile, bounds.start, referenceData.toTypedArray())
runWriteAction {
explicitImports.forEach { fqName ->
@@ -224,7 +224,11 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
}
val dummyFile = KtPsiFactory(targetFile.project).createAnalyzableFile("dummy.kt", fileText, targetFile)
return KotlinCopyPasteReferenceProcessor().collectReferenceData(dummyFile, intArrayOf(blockStart!!), intArrayOf(blockEnd!!))
val startOffset = blockStart!!
val endOffset = blockEnd!!
return KotlinCopyPasteReferenceProcessor().collectReferenceData(dummyFile, intArrayOf(startOffset), intArrayOf(endOffset)).map {
it.copy(startOffset = it.startOffset - startOffset, endOffset = it.endOffset - startOffset)
}
}
private fun generateDummyFunctionName(convertedCode: String): String {
@@ -0,0 +1,20 @@
// ERROR: Unresolved reference: B
// ERROR: Unresolved reference: B
// ERROR: Unresolved reference: B
// ERROR: A 'return' expression required in a function with a block body ('{...}')
package to
import a.A
import a.T
import java.util.*
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.AtomicLong as ALong
fun g(t: T): Int {
g(A)
B.f()
A
B
A.Companion
B.Companion
}
@@ -0,0 +1,3 @@
a.A
a.B
a.T
@@ -0,0 +1 @@
a.B
@@ -0,0 +1,25 @@
package a
interface T
class A {
companion object: T {
}
}
class B {
companion object {
fun f(): Int
}
}
<selection>fun g(t: T): Int {
g(A)
B.f()
A
B
A.Companion
B.Companion
}</selection>
@@ -0,0 +1,7 @@
package to
import java.util.*
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.AtomicLong as ALong
<caret>
+1 -1
View File
@@ -14,7 +14,7 @@ package d
import foo.Bar
/**
* Some kdoc for class Foo
* Some kdoc [Bar] for class Foo
*/
class Foo(val a: A) {
}
+1 -1
View File
@@ -12,7 +12,7 @@ package d
import foo.Bar
/**
* Some kdoc for class Foo
* Some kdoc [Bar] for class Foo
*/
class Foo(val a: A) {
}
@@ -0,0 +1,3 @@
package foo
class Bar(val v: Int){}
@@ -0,0 +1,19 @@
package to
import d.A
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package d
import foo.Bar
/**
* Some kdoc [Bar] for class Foo
*/
class Foo(val a: A) {
}
@@ -0,0 +1 @@
d.A
@@ -0,0 +1,22 @@
// WITH_LIBRARY: copyPaste/imports/KotlinLibrary
<selection>
<caret>/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package d</selection>
/*
* Some comment in front of imports
*/
<selection>
<caret>import foo.Bar</selection>
<selection>
<caret>/**
* Some kdoc [Bar] for class Foo
*/</selection>
<selection>
<caret>class Foo(val a: A) {
}
</selection>
@@ -47,6 +47,14 @@ abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest() {
}
KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested = emptyList()
ReviewAddedImports.importsToBeReviewed = emptyList()
val importsToBeDeletedFile = testDataFile(testFileName.replace(".kt", ".imports_to_delete"))
ReviewAddedImports.importsToBeDeleted = if (importsToBeDeletedFile.exists()) {
importsToBeDeletedFile.readLines()
} else {
emptyList()
}
configureTargetFile(testFileName.replace(".kt", ".to.kt"))
performNotWriteEditorAction(IdeActions.ACTION_PASTE)
@@ -59,6 +67,7 @@ abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest() {
val namesToImportDump = KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested.joinToString("\n")
KotlinTestUtils.assertEqualsToFile(testDataFile(testFileName.replace(".kt", ".expected.names")), namesToImportDump)
assertEquals(namesToImportDump, ReviewAddedImports.importsToBeReviewed.joinToString("\n"))
val resultFile = myFixture.file as KtFile
val resultText = if (InTextDirectivesUtils.isDirectiveDefined(testFileText, NO_ERRORS_DUMP_DIRECTIVE))
@@ -55,6 +55,11 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
runTest("idea/testData/copyPaste/imports/ClassObject.kt");
}
@TestMetadata("ClassObjectAndDropImports.kt")
public void testClassObjectAndDropImports() throws Exception {
runTest("idea/testData/copyPaste/imports/ClassObjectAndDropImports.kt");
}
@TestMetadata("ClassObjectFunInsideClass.kt")
public void testClassObjectFunInsideClass() throws Exception {
runTest("idea/testData/copyPaste/imports/ClassObjectFunInsideClass.kt");
@@ -245,6 +250,11 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
runTest("idea/testData/copyPaste/imports/Local.kt");
}
@TestMetadata("MultiCaretSelectionEntireFile.kt")
public void testMultiCaretSelectionEntireFile() throws Exception {
runTest("idea/testData/copyPaste/imports/MultiCaretSelectionEntireFile.kt");
}
@TestMetadata("MultiDeclaration.kt")
public void testMultiDeclaration() throws Exception {
runTest("idea/testData/copyPaste/imports/MultiDeclaration.kt");
@@ -408,6 +418,11 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
runTest("idea/testData/copyPaste/imports/ClassObject.kt");
}
@TestMetadata("ClassObjectAndDropImports.kt")
public void testClassObjectAndDropImports() throws Exception {
runTest("idea/testData/copyPaste/imports/ClassObjectAndDropImports.kt");
}
@TestMetadata("ClassObjectFunInsideClass.kt")
public void testClassObjectFunInsideClass() throws Exception {
runTest("idea/testData/copyPaste/imports/ClassObjectFunInsideClass.kt");
@@ -598,6 +613,11 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
runTest("idea/testData/copyPaste/imports/Local.kt");
}
@TestMetadata("MultiCaretSelectionEntireFile.kt")
public void testMultiCaretSelectionEntireFile() throws Exception {
runTest("idea/testData/copyPaste/imports/MultiCaretSelectionEntireFile.kt");
}
@TestMetadata("MultiDeclaration.kt")
public void testMultiDeclaration() throws Exception {
runTest("idea/testData/copyPaste/imports/MultiDeclaration.kt");