KT-4727 J2K: Convert Java code copied from browser or other sources

#KT-4727 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-05-05 18:41:49 +03:00
parent 4cbb098671
commit cca3237e46
40 changed files with 655 additions and 68 deletions
@@ -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)
+1
View File
@@ -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"/>
@@ -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
}
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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)
}
}
@@ -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");
@@ -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);
}
}