KT-4727 J2K: Convert Java code copied from browser or other sources
#KT-4727 Fixed
This commit is contained in:
@@ -59,6 +59,7 @@ import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractSmartCompletio
|
||||
import org.jetbrains.kotlin.idea.completion.test.weighers.AbstractBasicCompletionWeigherTest
|
||||
import org.jetbrains.kotlin.idea.completion.test.weighers.AbstractSmartCompletionWeigherTest
|
||||
import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest
|
||||
import org.jetbrains.kotlin.idea.conversion.copy.AbstractTextJavaToKotlinCopyPasteConversionTest
|
||||
import org.jetbrains.kotlin.idea.coverage.AbstractKotlinCoverageOutputFilesTest
|
||||
import org.jetbrains.kotlin.idea.debugger.AbstractBeforeExtractFunctionInsertionTest
|
||||
import org.jetbrains.kotlin.idea.debugger.AbstractKotlinSteppingTest
|
||||
@@ -647,6 +648,10 @@ fun main(args: Array<String>) {
|
||||
model("copyPaste/conversion", pattern = """^([^\.]+)\.java$""")
|
||||
}
|
||||
|
||||
testClass<AbstractTextJavaToKotlinCopyPasteConversionTest>() {
|
||||
model("copyPaste/plainTextConversion", pattern = """^([^\.]+)\.txt$""")
|
||||
}
|
||||
|
||||
testClass<AbstractInsertImportOnPasteTest>() {
|
||||
model("copyPaste/imports", pattern = """^([^\.]+)\.kt$""", testMethod = "doTestCopy", testClassName = "Copy", recursive = false)
|
||||
model("copyPaste/imports", pattern = """^([^\.]+)\.kt$""", testMethod = "doTestCut", testClassName = "Cut", recursive = false)
|
||||
|
||||
@@ -466,6 +466,7 @@
|
||||
<backspaceHandlerDelegate implementation="org.jetbrains.kotlin.idea.editor.KotlinStringTemplateBackspaceHandler"/>
|
||||
|
||||
<copyPastePostProcessor implementation="org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPasteProcessor"/>
|
||||
<copyPastePostProcessor implementation="org.jetbrains.kotlin.idea.conversion.copy.ConvertTextJavaCopyPasteProcessor"/>
|
||||
<copyPastePostProcessor implementation="org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor"/>
|
||||
|
||||
<lang.documentationProvider language="JAVA" implementationClass="org.jetbrains.kotlin.idea.KotlinQuickDocumentationProvider" order="first"/>
|
||||
|
||||
+66
-61
@@ -25,11 +25,7 @@ import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor
|
||||
@@ -48,6 +44,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.KtStringTemplateEntryWithExpression
|
||||
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import java.awt.datatransfer.Transferable
|
||||
import java.util.*
|
||||
|
||||
@@ -83,10 +80,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
||||
val document = editor.document
|
||||
val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(document) as? KtFile ?: return
|
||||
|
||||
val elementAt = targetFile.findElementAt(caretOffset)
|
||||
if (PsiTreeUtil.getParentOfType(elementAt, KtStringTemplateExpression::class.java, false, KtStringTemplateEntryWithExpression::class.java) is KtStringTemplateExpression) {
|
||||
return
|
||||
}
|
||||
if (isNoConversionPosition(targetFile, bounds.startOffset)) return
|
||||
|
||||
data class Result(val text: String?, val referenceData: Collection<KotlinReferenceData>, val explicitImports: Set<FqName>)
|
||||
|
||||
@@ -149,7 +143,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
||||
val endOffsetAfterCopy = startOffset + text.length
|
||||
editor.caretModel.moveToOffset(endOffsetAfterCopy)
|
||||
|
||||
var newBounds = insertImports(TextRange(startOffset, endOffsetAfterCopy), referenceData, explicitImports)
|
||||
val newBounds = insertImports(TextRange(startOffset, endOffsetAfterCopy), referenceData, explicitImports)
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
AfterConversionPass(project, J2kPostProcessor(formatCode = true)).run(targetFile, newBounds)
|
||||
@@ -159,56 +153,6 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
||||
}
|
||||
}
|
||||
|
||||
private class ConversionResult(
|
||||
val text: String,
|
||||
val parseContext: ParseContext,
|
||||
val importsToAdd: Set<FqName>,
|
||||
val textChanged: Boolean
|
||||
)
|
||||
|
||||
private fun convertCopiedCodeToKotlin(elementsAndTexts: Collection<Any>, project: Project): ConversionResult {
|
||||
val converter = JavaToKotlinConverter(
|
||||
project,
|
||||
ConverterSettings.defaultSettings,
|
||||
IdeaJavaToKotlinServices
|
||||
)
|
||||
|
||||
val inputElements = elementsAndTexts.filterIsInstance<PsiElement>()
|
||||
val results = converter.elementsToKotlin(inputElements).results
|
||||
val importsToAdd = LinkedHashSet<FqName>()
|
||||
|
||||
var resultIndex = 0
|
||||
val convertedCodeBuilder = StringBuilder()
|
||||
val originalCodeBuilder = StringBuilder()
|
||||
var parseContext: ParseContext? = null
|
||||
for (o in elementsAndTexts) {
|
||||
if (o is PsiElement) {
|
||||
val originalText = o.text
|
||||
originalCodeBuilder.append(originalText)
|
||||
|
||||
val result = results[resultIndex++]
|
||||
if (result != null) {
|
||||
convertedCodeBuilder.append(result.text)
|
||||
if (parseContext == null) { // use parse context of the first converted element as parse context for the whole text
|
||||
parseContext = result.parseContext
|
||||
}
|
||||
importsToAdd.addAll(result.importsToAdd)
|
||||
}
|
||||
else { // failed to convert element to Kotlin, insert "as is"
|
||||
convertedCodeBuilder.append(originalText)
|
||||
}
|
||||
}
|
||||
else {
|
||||
originalCodeBuilder.append(o)
|
||||
convertedCodeBuilder.append(o as String)
|
||||
}
|
||||
}
|
||||
|
||||
val convertedCode = convertedCodeBuilder.toString()
|
||||
val originalCode = originalCodeBuilder.toString()
|
||||
return ConversionResult(convertedCode, parseContext ?: ParseContext.CODE_BLOCK, importsToAdd, convertedCode != originalCode)
|
||||
}
|
||||
|
||||
private fun buildReferenceData(text: String, parseContext: ParseContext, importsAndPackage: String, targetFile: KtFile): Collection<KotlinReferenceData> {
|
||||
var blockStart: Int? = null
|
||||
var blockEnd: Int? = null
|
||||
@@ -244,7 +188,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
||||
}
|
||||
|
||||
private fun okFromDialog(project: Project): Boolean {
|
||||
val dialog = KotlinPasteFromJavaDialog(project)
|
||||
val dialog = KotlinPasteFromJavaDialog(project, false)
|
||||
dialog.show()
|
||||
return dialog.isOK
|
||||
}
|
||||
@@ -253,3 +197,64 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
||||
@TestOnly var conversionPerformed: Boolean = false
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConversionResult(
|
||||
val text: String,
|
||||
val parseContext: ParseContext,
|
||||
val importsToAdd: Set<FqName>,
|
||||
val textChanged: Boolean
|
||||
)
|
||||
|
||||
internal fun convertCopiedCodeToKotlin(elementsAndTexts: Collection<Any>, project: Project): ConversionResult {
|
||||
val converter = JavaToKotlinConverter(
|
||||
project,
|
||||
ConverterSettings.defaultSettings,
|
||||
IdeaJavaToKotlinServices
|
||||
)
|
||||
|
||||
val inputElements = elementsAndTexts.filterIsInstance<PsiElement>()
|
||||
val results = converter.elementsToKotlin(inputElements).results
|
||||
val importsToAdd = LinkedHashSet<FqName>()
|
||||
|
||||
var resultIndex = 0
|
||||
val convertedCodeBuilder = StringBuilder()
|
||||
val originalCodeBuilder = StringBuilder()
|
||||
var parseContext: ParseContext? = null
|
||||
for (o in elementsAndTexts) {
|
||||
if (o is PsiElement) {
|
||||
val originalText = o.text
|
||||
originalCodeBuilder.append(originalText)
|
||||
|
||||
val result = results[resultIndex++]
|
||||
if (result != null) {
|
||||
convertedCodeBuilder.append(result.text)
|
||||
if (parseContext == null) { // use parse context of the first converted element as parse context for the whole text
|
||||
parseContext = result.parseContext
|
||||
}
|
||||
importsToAdd.addAll(result.importsToAdd)
|
||||
}
|
||||
else { // failed to convert element to Kotlin, insert "as is"
|
||||
convertedCodeBuilder.append(originalText)
|
||||
}
|
||||
}
|
||||
else {
|
||||
originalCodeBuilder.append(o)
|
||||
convertedCodeBuilder.append(o as String)
|
||||
}
|
||||
}
|
||||
|
||||
val convertedCode = convertedCodeBuilder.toString()
|
||||
val originalCode = originalCodeBuilder.toString()
|
||||
return ConversionResult(convertedCode, parseContext ?: ParseContext.CODE_BLOCK, importsToAdd, convertedCode != originalCode)
|
||||
}
|
||||
|
||||
internal fun isNoConversionPosition(file: KtFile, offset: Int): Boolean {
|
||||
if (offset == 0) return false
|
||||
val token = file.findElementAt(offset - 1)!!
|
||||
for (element in token.parentsWithSelf) {
|
||||
if (element is PsiComment) return true
|
||||
if (element is KtStringTemplateEntryWithExpression) return false
|
||||
if (element is KtStringTemplateExpression) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.conversion.copy
|
||||
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiErrorElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.LocalTimeCounter
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
|
||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.j2k.AfterConversionPass
|
||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||
import org.jetbrains.kotlin.psi.KtClassBody
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import java.awt.datatransfer.DataFlavor
|
||||
import java.awt.datatransfer.Transferable
|
||||
|
||||
class CopiedKotlinCodeTransferableData : TextBlockTransferableData {
|
||||
override fun getFlavor() = DATA_FLAVOR
|
||||
override fun getOffsetCount() = 0
|
||||
|
||||
override fun getOffsets(offsets: IntArray?, index: Int) = index
|
||||
override fun setOffsets(offsets: IntArray?, index: Int) = index
|
||||
|
||||
companion object {
|
||||
val DATA_FLAVOR: DataFlavor = DataFlavor(CopiedKotlinCodeTransferableData::class.java, "class: KotlinCodeTransferableData")
|
||||
}
|
||||
}
|
||||
|
||||
class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
|
||||
private val LOG = Logger.getInstance(ConvertTextJavaCopyPasteProcessor::class.java)
|
||||
|
||||
private class MyTransferableData(val text: String) : TextBlockTransferableData {
|
||||
|
||||
override fun getFlavor() = DATA_FLAVOR
|
||||
override fun getOffsetCount() = 0
|
||||
|
||||
override fun getOffsets(offsets: IntArray?, index: Int) = index
|
||||
override fun setOffsets(offsets: IntArray?, index: Int) = index
|
||||
|
||||
companion object {
|
||||
val DATA_FLAVOR: DataFlavor = DataFlavor(ConvertTextJavaCopyPasteProcessor::class.java, "class: ConvertTextJavaCopyPasteProcessor")
|
||||
}
|
||||
}
|
||||
|
||||
override fun collectTransferableData(file: PsiFile, editor: Editor, startOffsets: IntArray, endOffsets: IntArray): List<TextBlockTransferableData> {
|
||||
if (file is KtFile) {
|
||||
return listOf(CopiedKotlinCodeTransferableData())
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
|
||||
try {
|
||||
if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
|
||||
if (content.isDataFlavorSupported(CopiedJavaCode.DATA_FLAVOR)) return emptyList() // it's handled by ConvertJavaCopyPasteProcessor
|
||||
if (content.isDataFlavorSupported(CopiedKotlinCodeTransferableData.DATA_FLAVOR)) return emptyList() // just an optimization
|
||||
|
||||
val text = content.getTransferData(DataFlavor.stringFlavor) as String
|
||||
return listOf(MyTransferableData(text))
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
LOG.error(e)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun processTransferableData(project: Project, editor: Editor, bounds: RangeMarker, caretOffset: Int, indented: Ref<Boolean>, values: List<TextBlockTransferableData>) {
|
||||
if (DumbService.getInstance(project).isDumb) return
|
||||
val options = KotlinEditorOptions.getInstance()
|
||||
if (!options.isEnableJavaToKotlinConversion) return //TODO: use another option?
|
||||
|
||||
val data = values.single() as MyTransferableData
|
||||
val text = data.text
|
||||
|
||||
val psiDocumentManager = PsiDocumentManager.getInstance(project)
|
||||
psiDocumentManager.commitDocument(editor.document)
|
||||
val targetFile = psiDocumentManager.getPsiFile(editor.document) as? KtFile ?: return
|
||||
|
||||
val pasteContext = detectPasteContext(targetFile, bounds.startOffset, bounds.endOffset) ?: return
|
||||
|
||||
val conversionContext = detectConversionContext(pasteContext, text, project) ?: return
|
||||
|
||||
val needConvert = options.isDonTShowConversionDialog || okFromDialog(project)
|
||||
if (!needConvert) return
|
||||
|
||||
val convertedText = convertCodeToKotlin(text, conversionContext, project)
|
||||
|
||||
runWriteAction {
|
||||
val startOffset = bounds.startOffset
|
||||
editor.document.replaceString(startOffset, bounds.endOffset, convertedText)
|
||||
|
||||
val endOffsetAfterCopy = startOffset + convertedText.length
|
||||
editor.caretModel.moveToOffset(endOffsetAfterCopy)
|
||||
|
||||
val newBounds = TextRange(startOffset, startOffset + convertedText.length)
|
||||
|
||||
psiDocumentManager.commitAllDocuments()
|
||||
AfterConversionPass(project, J2kPostProcessor(formatCode = true)).run(targetFile, newBounds)
|
||||
|
||||
conversionPerformed = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectPasteContext(file: KtFile, startOffset: Int, endOffset: Int): KotlinContext? {
|
||||
if (isNoConversionPosition(file, startOffset)) return null
|
||||
|
||||
val fileText = file.text
|
||||
val dummyDeclarationText = "fun dummy(){}"
|
||||
val newFileText = fileText.substring(0, startOffset) + "\n" + dummyDeclarationText + "\n" + fileText.substring(endOffset)
|
||||
|
||||
val newFile = parseAsFile(newFileText, KotlinFileType.INSTANCE, file.project)
|
||||
val declaration = PsiTreeUtil.findElementOfClassAtRange(newFile, startOffset + 1, startOffset + 1 + dummyDeclarationText.length, KtFunction::class.java) ?: return null
|
||||
if (declaration.textLength != dummyDeclarationText.length) return null
|
||||
|
||||
val parent = declaration.parent
|
||||
return when (parent) {
|
||||
is KtFile -> KotlinContext.TOP_LEVEL
|
||||
is KtClassBody -> KotlinContext.CLASS_BODY
|
||||
is KtBlockExpression -> KotlinContext.IN_BLOCK
|
||||
else -> KotlinContext.EXPRESSION
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectConversionContext(pasteContext: KotlinContext, text: String, project: Project): JavaContext? {
|
||||
if (isParsedAsKotlinCode(text, pasteContext, project)) return null
|
||||
|
||||
fun JavaContext.check(): JavaContext? {
|
||||
return check { isParsedAsJavaCode(text, it, project) }
|
||||
}
|
||||
|
||||
when (pasteContext) {
|
||||
KotlinContext.TOP_LEVEL -> {
|
||||
JavaContext.TOP_LEVEL.check()?.let { return it }
|
||||
JavaContext.CLASS_BODY.check()?.let { return it }
|
||||
return null
|
||||
}
|
||||
|
||||
KotlinContext.CLASS_BODY -> return JavaContext.CLASS_BODY.check()
|
||||
|
||||
KotlinContext.IN_BLOCK -> return JavaContext.IN_BLOCK.check()
|
||||
|
||||
KotlinContext.EXPRESSION -> return JavaContext.EXPRESSION.check()
|
||||
}
|
||||
}
|
||||
|
||||
private enum class KotlinContext {
|
||||
TOP_LEVEL, CLASS_BODY, IN_BLOCK, EXPRESSION
|
||||
}
|
||||
|
||||
private enum class JavaContext {
|
||||
TOP_LEVEL, CLASS_BODY, IN_BLOCK, EXPRESSION
|
||||
}
|
||||
|
||||
private fun isParsedAsJavaCode(text: String, context: JavaContext, project: Project): Boolean {
|
||||
return when (context) {
|
||||
JavaContext.TOP_LEVEL -> isParsedAsJavaFile(text, project)
|
||||
JavaContext.CLASS_BODY -> isParsedAsJavaFile("class Dummy { $text\n}", project)
|
||||
JavaContext.IN_BLOCK -> isParsedAsJavaFile("class Dummy { void foo() {$text\n}\n}", project)
|
||||
JavaContext.EXPRESSION -> isParsedAsJavaFile("class Dummy { Object field = $text; }", project)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isParsedAsKotlinCode(text: String, context: KotlinContext, project: Project): Boolean {
|
||||
return when (context) {
|
||||
KotlinContext.TOP_LEVEL -> isParsedAsKotlinFile(text, project)
|
||||
KotlinContext.CLASS_BODY -> isParsedAsKotlinFile("class Dummy { $text\n}", project)
|
||||
KotlinContext.IN_BLOCK -> isParsedAsKotlinFile("fun foo() {$text\n}", project)
|
||||
KotlinContext.EXPRESSION -> isParsedAsKotlinFile("val v = $text", project)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isParsedAsJavaFile(text: String, project: Project) = isParsedAsFile(text, JavaFileType.INSTANCE, project)
|
||||
|
||||
private fun isParsedAsKotlinFile(text: String, project: Project) = isParsedAsFile(text, KotlinFileType.INSTANCE, project)
|
||||
|
||||
private fun isParsedAsFile(text: String, fileType: LanguageFileType, project: Project): Boolean {
|
||||
val psiFile = parseAsFile(text, fileType, project)
|
||||
return !psiFile.anyDescendantOfType<PsiErrorElement>()
|
||||
}
|
||||
|
||||
private fun parseAsFile(text: String, fileType: LanguageFileType, project: Project): PsiFile {
|
||||
return PsiFileFactory.getInstance(project).createFileFromText("Dummy", fileType, text, LocalTimeCounter.currentTime(), false)
|
||||
}
|
||||
|
||||
private fun convertCodeToKotlin(text: String, context: JavaContext, project: Project): String {
|
||||
val copiedJavaCode = when (context) {
|
||||
JavaContext.TOP_LEVEL -> createCopiedJavaCode("$", text)
|
||||
|
||||
JavaContext.CLASS_BODY -> createCopiedJavaCode("class Dummy {\n$\n}", text)
|
||||
|
||||
JavaContext.IN_BLOCK -> createCopiedJavaCode("class Dummy {\nvoid foo() {\n$\n}\n}", text)
|
||||
|
||||
JavaContext.EXPRESSION -> createCopiedJavaCode("class Dummy {\nObject field = $\n}", text)
|
||||
}
|
||||
|
||||
val dataForConversion = DataForConversion.prepare(copiedJavaCode, project)
|
||||
val conversionResult = convertCopiedCodeToKotlin(dataForConversion.elementsAndTexts, project)
|
||||
return conversionResult.text
|
||||
}
|
||||
|
||||
private fun createCopiedJavaCode(template: String, text: String): CopiedJavaCode {
|
||||
val index = template.indexOf("$")
|
||||
assert(index >= 0)
|
||||
val fileText = template.substring(0, index) + text + template.substring(index + 1)
|
||||
return CopiedJavaCode(fileText, intArrayOf(index), intArrayOf(index + text.length))
|
||||
}
|
||||
|
||||
private fun okFromDialog(project: Project): Boolean {
|
||||
val dialog = KotlinPasteFromJavaDialog(project, true)
|
||||
dialog.show()
|
||||
return dialog.isOK
|
||||
}
|
||||
|
||||
companion object {
|
||||
@TestOnly var conversionPerformed: Boolean = false
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.conversion.copy.KotlinPasteFromJavaDialog">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
<xy x="20" y="20" width="515" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="746fc" class="javax.swing.JLabel">
|
||||
<component id="746fc" class="javax.swing.JLabel" binding="questionLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
|
||||
@@ -30,26 +30,31 @@ import java.awt.*;
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class KotlinPasteFromJavaDialog extends DialogWrapper {
|
||||
private JPanel myPanel;
|
||||
private JPanel panel;
|
||||
private JCheckBox donTShowThisCheckBox;
|
||||
private JLabel questionLabel;
|
||||
private JButton buttonOK;
|
||||
|
||||
public KotlinPasteFromJavaDialog(Project project) {
|
||||
public KotlinPasteFromJavaDialog(@NotNull Project project, boolean isPlainText) {
|
||||
super(project, true);
|
||||
setModal(true);
|
||||
getRootPane().setDefaultButton(buttonOK);
|
||||
setTitle("Convert Code From Java");
|
||||
if (isPlainText) {
|
||||
questionLabel.setText("Clipboard content seems to be Java code. Do you want to convert it to Kotlin? ");
|
||||
//TODO: should we also use different set of settings?
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myPanel;
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Container getContentPane() {
|
||||
return myPanel;
|
||||
return panel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// String s = 1;
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
public static void main(String[] args) {
|
||||
<selection>String s = 1;</selection>
|
||||
}
|
||||
}
|
||||
|
||||
// NO_CONVERSION_EXPECTED
|
||||
@@ -0,0 +1 @@
|
||||
// <caret>
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo() {
|
||||
bar(ArrayList<String>())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo() {
|
||||
bar(<caret>)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
new ArrayList<String>()
|
||||
@@ -0,0 +1 @@
|
||||
fun foo() = ArrayList<String>()
|
||||
@@ -0,0 +1 @@
|
||||
fun foo() = <caret>
|
||||
@@ -0,0 +1 @@
|
||||
new ArrayList<String>()
|
||||
@@ -0,0 +1,2 @@
|
||||
// ArrayList<String> list = new ArrayList<String>();
|
||||
// NO_CONVERSION_EXPECTED
|
||||
@@ -0,0 +1 @@
|
||||
// <caret>
|
||||
@@ -0,0 +1,2 @@
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
// NO_CONVERSION_EXPECTED
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo() {
|
||||
val v = """
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
// NO_CONVERSION_EXPECTED
|
||||
|
||||
"""
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo() {
|
||||
val v = """
|
||||
<caret>
|
||||
"""
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
// NO_CONVERSION_EXPECTED
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo() {
|
||||
val v = "ArrayList<String> list = new ArrayList<String>();
|
||||
// NO_CONVERSION_EXPECTED
|
||||
"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo() {
|
||||
val v = "<caret>"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
// NO_CONVERSION_EXPECTED
|
||||
@@ -0,0 +1,10 @@
|
||||
class A {
|
||||
fun foo() {
|
||||
val list = ArrayList<String>()
|
||||
list.add(1)
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
<caret>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
<caret>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
void foo() {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
list.add(1);
|
||||
}
|
||||
|
||||
void bar() {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package to
|
||||
|
||||
fun foo() {
|
||||
val list = ArrayList<String>()
|
||||
list.add(1)
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
void foo() {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
list.add(1);
|
||||
}
|
||||
|
||||
void bar() {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package to
|
||||
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
|
||||
internal class JavaClass {
|
||||
fun foo(file: File?, target: MutableList<String>?) {
|
||||
val list = ArrayList<String>()
|
||||
if (file != null) {
|
||||
list.add(file.name)
|
||||
}
|
||||
target?.addAll(list)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class JavaClass {
|
||||
void foo(File file, List<String> target) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
if (file != null) {
|
||||
list.add(file.getName());
|
||||
}
|
||||
if (target != null) {
|
||||
target.addAll(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo() {
|
||||
val list = ArrayList<String>()
|
||||
list.add(1)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo() {
|
||||
<caret>
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
list.add(1);
|
||||
@@ -0,0 +1,10 @@
|
||||
package to
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
internal class JavaClass {
|
||||
fun foo() {
|
||||
val list = ArrayList<String>()
|
||||
list.add(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
class JavaClass {
|
||||
void foo() {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
list.add(1);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.conversion.copy
|
||||
|
||||
import com.intellij.openapi.actionSystem.IdeActions
|
||||
import org.jetbrains.kotlin.idea.AbstractCopyPasteTest
|
||||
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
abstract class AbstractTextJavaToKotlinCopyPasteConversionTest : AbstractCopyPasteTest() {
|
||||
private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/plainTextConversion"
|
||||
|
||||
private var oldEditorOptions: KotlinEditorOptions? = null
|
||||
|
||||
override fun getTestDataPath() = BASE_PATH
|
||||
|
||||
override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
oldEditorOptions = KotlinEditorOptions.getInstance().state
|
||||
KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion = true
|
||||
KotlinEditorOptions.getInstance().isDonTShowConversionDialog = true
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
KotlinEditorOptions.getInstance().loadState(oldEditorOptions)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
fun doTest(path: String) {
|
||||
myFixture.testDataPath = BASE_PATH
|
||||
val testName = getTestName(false)
|
||||
myFixture.configureByFiles(testName + ".txt")
|
||||
|
||||
val fileText = myFixture.editor.document.text
|
||||
val noConversionExpected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NO_CONVERSION_EXPECTED").isNotEmpty()
|
||||
|
||||
myFixture.editor.selectionModel.setSelection(0, fileText.length)
|
||||
myFixture.performEditorAction(IdeActions.ACTION_COPY)
|
||||
|
||||
configureTargetFile(testName + ".to.kt")
|
||||
|
||||
ConvertTextJavaCopyPasteProcessor.conversionPerformed = false
|
||||
|
||||
myFixture.performEditorAction(IdeActions.ACTION_PASTE)
|
||||
|
||||
assertEquals(noConversionExpected, !ConvertTextJavaCopyPasteProcessor.conversionPerformed,
|
||||
if (noConversionExpected) "Conversion to Kotlin should not be suggested" else "No conversion to Kotlin suggested")
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(File(path.replace(".txt", ".expected.kt")), myFixture.file.text)
|
||||
}
|
||||
}
|
||||
+6
@@ -173,6 +173,12 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InsertIntoComment.java")
|
||||
public void testInsertIntoComment() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/InsertIntoComment.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InsertIntoString.java")
|
||||
public void testInsertIntoString() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/InsertIntoString.java");
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.conversion.copy;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/copyPaste/plainTextConversion")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class TextJavaToKotlinCopyPasteConversionTestGenerated extends AbstractTextJavaToKotlinCopyPasteConversionTest {
|
||||
public void testAllFilesPresentInPlainTextConversion() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/copyPaste/plainTextConversion"), Pattern.compile("^([^\\.]+)\\.txt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("AsExpression.txt")
|
||||
public void testAsExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/AsExpression.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("AsExpressionBody.txt")
|
||||
public void testAsExpressionBody() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/AsExpressionBody.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("IntoComment.txt")
|
||||
public void testIntoComment() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/IntoComment.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("IntoRawStringLiteral.txt")
|
||||
public void testIntoRawStringLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/IntoRawStringLiteral.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("IntoStringLiteral.txt")
|
||||
public void testIntoStringLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/IntoStringLiteral.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MembersIntoClass.txt")
|
||||
public void testMembersIntoClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/MembersIntoClass.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MembersToTopLevel.txt")
|
||||
public void testMembersToTopLevel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/MembersToTopLevel.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PostProcessing.txt")
|
||||
public void testPostProcessing() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/PostProcessing.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StatementsIntoFunction.txt")
|
||||
public void testStatementsIntoFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/StatementsIntoFunction.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("WholeFile.txt")
|
||||
public void testWholeFile() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/plainTextConversion/WholeFile.txt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user