diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt index d4343bcf469..e749d0ac702 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt @@ -16,45 +16,64 @@ package org.jetbrains.kotlin.idea.actions +import com.intellij.codeInsight.actions.ReformatCodeProcessor import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.ex.MessagesEx +import com.intellij.openapi.vfs.CharsetToolkit +import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileVisitor +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiManager +import com.intellij.psi.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter +import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor import org.jetbrains.kotlin.j2k.ConverterSettings import org.jetbrains.kotlin.j2k.FilesConversionScope import org.jetbrains.kotlin.j2k.IdeaReferenceSearcher import org.jetbrains.kotlin.j2k.JavaToKotlinConverter -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiJavaFile -import com.intellij.psi.PsiManager -import java.util.ArrayList -import com.intellij.openapi.application.ApplicationManager -import com.intellij.psi.codeStyle.CodeStyleManager -import java.io.IOException -import com.intellij.openapi.ui.ex.MessagesEx -import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor -import com.intellij.openapi.vfs.CharsetToolkit import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.utils.addIfNotNull -import com.intellij.openapi.vfs.VfsUtilCore -import com.intellij.openapi.vfs.VirtualFileVisitor -import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter +import java.io.IOException +import java.util.ArrayList public class JavaToKotlinAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { - val selectedJavaFiles = selectedJavaFiles(e).toList() + val javaFiles = selectedJavaFiles(e).toList() val project = CommonDataKeys.PROJECT.getData(e.getDataContext())!! + var convertedTexts: List? = null + fun convert() { + val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, FilesConversionScope(javaFiles), IdeaReferenceSearcher, IdeaResolverForConverter) + convertedTexts = converter.elementsToKotlin(javaFiles.map { it to J2kPostProcessor(it, formatCode = true) }, + ProgressManager.getInstance().getProgressIndicator()) + } + + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously( + { + ApplicationManager.getApplication().runReadAction(::convert) + }, + "Converting Java to Kotlin", + true, + project)) return + + CommandProcessor.getInstance().executeCommand(project, object : Runnable { override fun run() { - val newFiles = convertFiles(selectedJavaFiles, project) - deleteFiles(selectedJavaFiles) - reformatFiles(newFiles, project) + CommandProcessor.getInstance().markCurrentCommandAsGlobal(project) - if (newFiles.size() == 1) { - FileEditorManager.getInstance(project).openFile(newFiles.single(), true) + val newFiles = saveResults(javaFiles, convertedTexts!!, project) + deleteFiles(javaFiles) + + newFiles.singleOrNull()?.let { + FileEditorManager.getInstance(project).openFile(it, true) } } }, "Convert files from Java to Kotlin", null) @@ -65,16 +84,16 @@ public class JavaToKotlinAction : AnAction() { e.getPresentation().setEnabled(enabled) } - private fun selectedJavaFiles(e: AnActionEvent): Stream { - val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return streamOf() - val project = CommonDataKeys.PROJECT.getData(e.getDataContext()) ?: return streamOf() + private fun selectedJavaFiles(e: AnActionEvent): Sequence { + val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf() + val project = CommonDataKeys.PROJECT.getData(e.getDataContext()) ?: return sequenceOf() return allJavaFiles(virtualFiles, project) } - private fun allJavaFiles(filesOrDirs: Array, project: Project): Stream { + private fun allJavaFiles(filesOrDirs: Array, project: Project): Sequence { val manager = PsiManager.getInstance(project) return allFiles(filesOrDirs) - .stream() + .sequence() .map { manager.findFile(it) as? PsiJavaFile } .filterNotNull() } @@ -92,22 +111,7 @@ public class JavaToKotlinAction : AnAction() { return result } - private fun reformatFiles(kotlinFiles: List, project: Project) { - for (file in kotlinFiles) { - ApplicationManager.getApplication().runWriteAction { - val jetFile = PsiManager.getInstance(project).findFile(file) as? JetFile - if (jetFile != null) { - CodeStyleManager.getInstance(project).reformat(jetFile) - } - } - } - } - - private fun convertFiles(javaFiles: List, project: Project): List { - val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, FilesConversionScope(javaFiles), IdeaReferenceSearcher, IdeaResolverForConverter) - - val convertedTexts = converter.elementsToKotlin(javaFiles.map { it to J2kPostProcessor(it) }) - + private fun saveResults(javaFiles: List, convertedTexts: List, project: Project): List { val result = ArrayList() for ((i, psiFile) in javaFiles.withIndex()) { ApplicationManager.getApplication().runWriteAction { diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt index cd70dd7987b..7b359f5ddfb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -127,7 +127,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor>): List { + public fun elementsToKotlin( + psiElementsAndProcessors: List>, + progress: ProgressIndicator = EmptyProgressIndicator() + ): List { try { - val intermediateResults = ArrayList<((Map) -> String)?>(psiElementsAndProcessors.size()) + val elementCount = psiElementsAndProcessors.size() + val intermediateResults = ArrayList<((Map) -> String)?>(elementCount) val usageProcessings = HashMap() val usageProcessingCollector: (UsageProcessing) -> Unit = { usageProcessing -> assert(!usageProcessings.containsKey(usageProcessing.targetElement)) { "Duplicated UsageProcessing for target element ${usageProcessing.targetElement}" } usageProcessings.put(usageProcessing.targetElement, usageProcessing) } - for ((psiElement, postProcessor) in psiElementsAndProcessors) { + + val progressText = "Converting Java to Kotlin" + val fileCountText = elementCount.toString() + " " + if (elementCount > 1) "files" else "file" + var fraction = 0.0 + var pass = 1 + + fun processFilesWithProgress(passFraction: Double, processFile: (Int) -> Unit) { + // we use special process with EmptyProgressIndicator to avoid changing text in our progress by inheritors search inside etc + ProgressManager.getInstance().runProcess( + { + progress.setText("$progressText ($fileCountText) - pass $pass of 3") + + val filesCount = psiElementsAndProcessors.indices + for (i in filesCount) { + progress.checkCanceled() + progress.setFraction(fraction + passFraction * i / elementCount) + + val psiFile = psiElementsAndProcessors[i].first as? PsiFile + if (psiFile != null) { + progress.setText2(psiFile.getVirtualFile().getPresentableUrl()) + } + + processFile(i) + } + + pass++ + fraction += passFraction + }, + EmptyProgressIndicator()) + } + + processFilesWithProgress(0.25) { i -> + val psiElement = psiElementsAndProcessors[i].first + val postProcessor = psiElementsAndProcessors[i].second val converter = Converter.create(psiElement, settings, conversionScope, referenceSearcher, resolverForConverter, postProcessor, usageProcessingCollector) val result = converter.convert() intermediateResults.add(result) } - val results = ArrayList(psiElementsAndProcessors.size()) - for ((i, result) in intermediateResults.withIndex()) { + val results = ArrayList(elementCount) + processFilesWithProgress(0.25) { i -> + val result = intermediateResults[i] results.add(if (result != null) result(usageProcessings) else "") intermediateResults[i] = null // to not hold unused objects in the heap } - val finalResults = ArrayList(psiElementsAndProcessors.size()) - for ((i, result) in results.withIndex()) { + val finalResults = ArrayList(elementCount) + processFilesWithProgress(0.5) { i -> + val result = results[i] val postProcessor = psiElementsAndProcessors[i].second if (postProcessor != null) { try { @@ -113,6 +156,7 @@ public class JavaToKotlinConverter(private val project: Project, finalResults.add(result) } } + return finalResults } catch(e: ElementCreationStackTraceRequiredException) { diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt index c4f4bb1d91b..19eef3c8b7f 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt @@ -35,7 +35,7 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT val project = LightPlatformTestCase.getProject()!! val psiManager = PsiManager.getInstance(project) - val javaFiles = File(dirPath).listFiles {(file, name): Boolean -> name.endsWith(".java") } + val javaFiles = File(dirPath).listFiles { file, name -> name.endsWith(".java") } val psiFiles = ArrayList() for (javaFile: File in javaFiles) { val virtualFile = addFile(javaFile, "test") @@ -44,8 +44,7 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT } val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, FilesConversionScope(psiFiles), IdeaReferenceSearcher, IdeaResolverForConverter) - val results: List = converter.elementsToKotlin(psiFiles.map { it to J2kPostProcessor(it) }) - .map { reformat(it, project) } + val results: List = converter.elementsToKotlin(psiFiles.map { it to J2kPostProcessor(it, formatCode = true) }) fun expectedFile(i: Int) = File(javaFiles[i].getPath().replace(".java", ".kt")) @@ -61,14 +60,6 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT } } - private fun reformat(text: String, project: Project): String { - val convertedFile = JetTestUtils.createFile("converted", text, project) - WriteCommandAction.runWriteCommandAction(project) { - CodeStyleManager.getInstance(project)!!.reformat(convertedFile) - } - return convertedFile.getText()!! - } - override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE } diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt index 18338b14b3c..3e3397f2810 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt @@ -66,7 +66,6 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava } val rawConverted = when (prefix) { - "element" -> elementToKotlin(javaCode, settings, project) "expression" -> expressionToKotlin(javaCode, settings, project) "statement" -> statementToKotlin(javaCode, settings, project) "method" -> methodToKotlin(javaCode, settings, project) @@ -105,17 +104,10 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava reformattedText } - private fun elementToKotlin(text: String, settings: ConverterSettings, project: Project): String { - val fileWithText = createJavaFile(text) - val converter = JavaToKotlinConverter(project, settings, FilesConversionScope(listOf(fileWithText)), IdeaReferenceSearcher, IdeaResolverForConverter) - val element = fileWithText.getFirstChild()!! - return converter.elementsToKotlin(listOf(element to J2kPostProcessor(fileWithText)))[0] - } - private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String { val file = createJavaFile(text) val converter = JavaToKotlinConverter(project, settings, FilesConversionScope(listOf(file)), IdeaReferenceSearcher, IdeaResolverForConverter) - return converter.elementsToKotlin(listOf(file to J2kPostProcessor(file)))[0] + return converter.elementsToKotlin(listOf(file to J2kPostProcessor(file, formatCode = false)))[0] } private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String {