Import of KClass inserted when needed on conversion to Kotlin on paste

This commit is contained in:
Valentin Kipyatkov
2015-05-27 20:16:40 +03:00
parent f687a40136
commit 002312f98a
7 changed files with 51 additions and 20 deletions
@@ -30,16 +30,20 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiJavaFile
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor
import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData
import org.jetbrains.kotlin.idea.editor.JetEditorOptions import org.jetbrains.kotlin.idea.editor.JetEditorOptions
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.* import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.psi.JetPsiFactory
import java.awt.datatransfer.Transferable import java.awt.datatransfer.Transferable
import java.util.LinkedHashSet
public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBlockTransferableData>() { public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPastePostProcessor") private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPastePostProcessor")
@@ -73,34 +77,44 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
val document = editor.getDocument() val document = editor.getDocument()
val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(document) as? JetFile ?: return val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(document) as? JetFile ?: return
fun doConversion(): Pair<String?, Collection<KotlinReferenceData>> { @data class Result(val text: String?, val referenceData: Collection<KotlinReferenceData>, val explicitImports: Set<FqName>)
fun doConversion(): Result {
val dataForConversion = DataForConversion.prepare(data, project) val dataForConversion = DataForConversion.prepare(data, project)
val result = convertCopiedCodeToKotlin(dataForConversion.elementsAndTexts, project) val result = convertCopiedCodeToKotlin(dataForConversion.elementsAndTexts, project)
val referenceData = buildReferenceData(result.text, result.parseContext, dataForConversion.importsAndPackage, targetFile) val referenceData = buildReferenceData(result.text, result.parseContext, dataForConversion.importsAndPackage, targetFile)
return (if (result.textChanged) result.text else null) to referenceData val text = if (result.textChanged) result.text else null
return Result(text, referenceData, result.importsToAdd)
} }
fun insertImports(bounds: TextRange, referenceData: Collection<KotlinReferenceData>): TextRange? { fun insertImports(bounds: TextRange, referenceData: Collection<KotlinReferenceData>, explicitImports: Collection<FqName>): TextRange? {
if (referenceData.isEmpty()) return bounds if (referenceData.isEmpty() && explicitImports.isEmpty()) return bounds
PsiDocumentManager.getInstance(project).commitAllDocuments()
val rangeMarker = document.createRangeMarker(bounds) val rangeMarker = document.createRangeMarker(bounds)
rangeMarker.setGreedyToLeft(true) rangeMarker.setGreedyToLeft(true)
rangeMarker.setGreedyToRight(true) rangeMarker.setGreedyToRight(true)
explicitImports.forEach { fqName ->
targetFile.getResolutionFacade().resolveImportReference(targetFile, fqName).firstOrNull()?.let {
ImportInsertHelper.getInstance(project).importDescriptor(targetFile, it)
}
}
KotlinCopyPasteReferenceProcessor().processReferenceData(project, targetFile, bounds.start, referenceData.copyToArray()) KotlinCopyPasteReferenceProcessor().processReferenceData(project, targetFile, bounds.start, referenceData.copyToArray())
return rangeMarker.range return rangeMarker.range
} }
var conversionResult: Pair<String?, Collection<KotlinReferenceData>>? = null var conversionResult: Result? = null
fun doConversionAndInsertImportsIfUnchanged(): Boolean { fun doConversionAndInsertImportsIfUnchanged(): Boolean {
conversionResult = doConversion() conversionResult = doConversion()
val text = conversionResult!!.first if (conversionResult!!.text != null) return false
if (text != null) return false
insertImports(bounds.range ?: return true, conversionResult!!.second) insertImports(bounds.range ?: return true, conversionResult!!.referenceData, conversionResult!!.explicitImports)
return true return true
} }
@@ -114,7 +128,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
if (conversionResult == null) { if (conversionResult == null) {
if (doConversionAndInsertImportsIfUnchanged()) return if (doConversionAndInsertImportsIfUnchanged()) return
} }
val (text, referenceData) = conversionResult!! val (text, referenceData, explicitImports) = conversionResult!!
text!! // otherwise we should get true from doConversionAndInsertImportsIfUnchanged and return above text!! // otherwise we should get true from doConversionAndInsertImportsIfUnchanged and return above
runWriteAction { runWriteAction {
@@ -124,7 +138,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
val endOffsetAfterCopy = startOffset + text.length() val endOffsetAfterCopy = startOffset + text.length()
editor.getCaretModel().moveToOffset(endOffsetAfterCopy) editor.getCaretModel().moveToOffset(endOffsetAfterCopy)
var newBounds = insertImports(TextRange(startOffset, endOffsetAfterCopy), referenceData) var newBounds = insertImports(TextRange(startOffset, endOffsetAfterCopy), referenceData, explicitImports)
PsiDocumentManager.getInstance(project).commitAllDocuments() PsiDocumentManager.getInstance(project).commitAllDocuments()
AfterConversionPass(project, J2kPostProcessor(formatCode = true)).run(targetFile, newBounds) AfterConversionPass(project, J2kPostProcessor(formatCode = true)).run(targetFile, newBounds)
@@ -137,6 +151,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
private class ConversionResult( private class ConversionResult(
val text: String, val text: String,
val parseContext: ParseContext, val parseContext: ParseContext,
val importsToAdd: Set<FqName>,
val textChanged: Boolean val textChanged: Boolean
) )
@@ -150,6 +165,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
val inputElements = elementsAndTexts.filterIsInstance<PsiElement>() val inputElements = elementsAndTexts.filterIsInstance<PsiElement>()
val results = converter.elementsToKotlin(inputElements).results val results = converter.elementsToKotlin(inputElements).results
val importsToAdd = LinkedHashSet<FqName>()
var resultIndex = 0 var resultIndex = 0
val convertedCodeBuilder = StringBuilder() val convertedCodeBuilder = StringBuilder()
@@ -162,12 +178,11 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
val result = results[resultIndex++] val result = results[resultIndex++]
if (result != null) { if (result != null) {
//TODO: insert imports
convertedCodeBuilder.append(result.text) convertedCodeBuilder.append(result.text)
if (parseContext == null) { // use parse context of the first converted element as parse context for the whole text if (parseContext == null) { // use parse context of the first converted element as parse context for the whole text
parseContext = result.parseContext parseContext = result.parseContext
} }
importsToAdd.addAll(result.importsToAdd)
} }
else { // failed to convert element to Kotlin, insert "as is" else { // failed to convert element to Kotlin, insert "as is"
convertedCodeBuilder.append(originalText) convertedCodeBuilder.append(originalText)
@@ -181,7 +196,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
val convertedCode = convertedCodeBuilder.toString() val convertedCode = convertedCodeBuilder.toString()
val originalCode = originalCodeBuilder.toString() val originalCode = originalCodeBuilder.toString()
return ConversionResult(convertedCode, parseContext ?: ParseContext.TOP_LEVEL, convertedCode != originalCode) return ConversionResult(convertedCode, parseContext ?: ParseContext.TOP_LEVEL, importsToAdd, convertedCode != originalCode)
} }
private fun buildReferenceData(text: String, parseContext: ParseContext, importsAndPackage: String, targetFile: JetFile): Collection<KotlinReferenceData> { private fun buildReferenceData(text: String, parseContext: ParseContext, importsAndPackage: String, targetFile: JetFile): Collection<KotlinReferenceData> {
@@ -225,7 +240,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
} }
companion object { companion object {
[TestOnly] @TestOnly
public var conversionPerformed: Boolean = false public var conversionPerformed: Boolean = false
} }
} }
@@ -0,0 +1,5 @@
package to
import kotlin.reflect.KClass
annotation class Ann(public val value: KClass<Any>)
@@ -0,0 +1,6 @@
<selection>@interface Ann {
Class value();
}</selection>
class X {
}
@@ -61,6 +61,12 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("AddKClassImport.java")
public void testAddKClassImport() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/AddKClassImport.java");
doTest(fileName);
}
public void testAllFilesPresentInConversion() throws Exception { public void testAllFilesPresentInConversion() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/copyPaste/conversion"), Pattern.compile("^([^\\.]+)\\.java$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/copyPaste/conversion"), Pattern.compile("^([^\\.]+)\\.java$"), true);
} }
@@ -23,8 +23,7 @@ import org.jetbrains.kotlin.j2k.ast.CommentsAndSpacesInheritance
import org.jetbrains.kotlin.j2k.ast.Element import org.jetbrains.kotlin.j2k.ast.Element
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import java.util.ArrayList import java.util.*
import java.util.HashSet
import kotlin.platform.platformName import kotlin.platform.platformName
fun<T> CodeBuilder.append(generators: Collection<() -> T>, separator: String, prefix: String = "", suffix: String = ""): CodeBuilder { fun<T> CodeBuilder.append(generators: Collection<() -> T>, separator: String, prefix: String = "", suffix: String = ""): CodeBuilder {
@@ -56,7 +55,7 @@ class CodeBuilder(private val topElement: PsiElement?) {
private val commentsAndSpacesUsed = HashSet<PsiElement>() private val commentsAndSpacesUsed = HashSet<PsiElement>()
private val imports = ArrayList<FqName>() private val imports = LinkedHashSet<FqName>()
public fun append(text: String): CodeBuilder public fun append(text: String): CodeBuilder
= append(text, false) = append(text, false)
@@ -93,7 +92,7 @@ class CodeBuilder(private val topElement: PsiElement?) {
public val resultText: String public val resultText: String
get() = builder.toString() get() = builder.toString()
public val importsToAdd: Collection<FqName> public val importsToAdd: Set<FqName>
get() = imports get() = imports
public fun append(element: Element): CodeBuilder { public fun append(element: Element): CodeBuilder {
@@ -84,7 +84,7 @@ class Converter private(
public data class Result( public data class Result(
val text: String, val text: String,
val importsToAdd: Collection<FqName> val importsToAdd: Set<FqName>
) )
public fun convert(): IntermediateResult? { public fun convert(): IntermediateResult? {
@@ -89,7 +89,7 @@ public class JavaToKotlinConverter(
public fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit public fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit
} }
public data class ElementResult(val text: String, val importsToAdd: Collection<FqName>, val parseContext: ParseContext) public data class ElementResult(val text: String, val importsToAdd: Set<FqName>, val parseContext: ParseContext)
public data class Result(val results: List<ElementResult?>, val externalCodeProcessing: ExternalCodeProcessing?) public data class Result(val results: List<ElementResult?>, val externalCodeProcessing: ExternalCodeProcessing?)