From dc963e4ff6cf39b494730a1c34938d9925924b88 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 27 Jul 2020 14:34:53 +0300 Subject: [PATCH] Improve J2K progress reporting --- .../kotlin/j2k/JavaToKotlinConverter.kt | 11 +++ .../nj2k/postProcessing/J2kPostProcessor.kt | 2 +- .../messages/KotlinNJ2KBundle.properties | 1 + .../jetbrains/kotlin/nj2k/BaseConversion.kt | 4 +- .../kotlin/nj2k/ConversionsRunner.kt | 16 +++- .../kotlin/nj2k/NewJavaToKotlinConverter.kt | 79 ++++++++++++++++--- 6 files changed, 95 insertions(+), 18 deletions(-) diff --git a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt index 2dfaca74136..62145fd7fe7 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt @@ -298,6 +298,7 @@ interface WithProgressProcessor { ): List fun updateState(fileIndex: Int?, phase: Int, description: String) + fun updateState(phase: Int, subPhase: Int, subPhaseCount: Int, fileIndex: Int?, description: String) fun process(action: () -> T): T } @@ -348,6 +349,16 @@ class OldWithProgressProcessor(private val progress: ProgressIndicator?, private override fun updateState(fileIndex: Int?, phase: Int, description: String) { throw AbstractMethodError("Should not be called for old J2K") } + + override fun updateState( + phase: Int, + subPhase: Int, + subPhaseCount: Int, + fileIndex: Int?, + description: String + ) { + error("Should not be called for old J2K") + } } class ProgressPortionReporter( diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt index 02313f1acfe..64e78bf12ca 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt @@ -54,7 +54,7 @@ class NewJ2kPostProcessor : PostProcessor { ) { if (converterContext !is NewJ2kConverterContext) error("Invalid converter context for new J2K") for ((i, group) in processings.withIndex()) { - onPhaseChanged?.invoke(i + 1, group.description) + onPhaseChanged?.invoke(i, group.description) for (processing in group.processings) { try { processing.runProcessingConsideringOptions(target, converterContext) diff --git a/nj2k/resources/messages/KotlinNJ2KBundle.properties b/nj2k/resources/messages/KotlinNJ2KBundle.properties index 5f2ec69da47..44592810285 100644 --- a/nj2k/resources/messages/KotlinNJ2KBundle.properties +++ b/nj2k/resources/messages/KotlinNJ2KBundle.properties @@ -1,4 +1,5 @@ progress.text={0} - phase {1,number,#} of {2,number,#} +subphase.progress.text={0} ({1,number,#}/{2,number,#}) - phase {3,number,#} of {4,number,#} progress.searching.usages.to.update=Searching usages to update... phase.converting.j2k=Converting Java code to Kotlin code \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/BaseConversion.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/BaseConversion.kt index 3fcc9a7287d..db453af3a9b 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/BaseConversion.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/BaseConversion.kt @@ -18,13 +18,13 @@ interface Conversion { val typeFactory: JKTypeFactory get() = context.typeFactory - fun runConversion(treeRoots: List, context: NewJ2kConverterContext): Boolean + fun runConversion(treeRoots: Sequence, context: NewJ2kConverterContext): Boolean } interface SequentialBaseConversion : Conversion { fun runConversion(treeRoot: JKTreeElement, context: NewJ2kConverterContext): Boolean - override fun runConversion(treeRoots: List, context: NewJ2kConverterContext): Boolean { + override fun runConversion(treeRoots: Sequence, context: NewJ2kConverterContext): Boolean { return treeRoots.asSequence().map { runConversion(it, context) }.max() ?: false } } \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt index b9b9dd2ef9c..d8d09e45ccd 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt @@ -85,10 +85,20 @@ object ConversionsRunner { AddElementsInfoConversion(context) ) - fun doApply(trees: List, context: NewJ2kConverterContext) { + + fun doApply( + trees: List, + context: NewJ2kConverterContext, + updateProgress: (conversionIndex: Int, conversionCount: Int, fileIndex: Int, String) -> Unit + ) { + val conversions = createConversions(context) - for (conversion in conversions) { - conversion.runConversion(trees, context) + for ((conversionIndex, conversion) in conversions.withIndex()) { + val treeSequence = trees.asSequence().onEachIndexed { index, _ -> + updateProgress(conversionIndex, conversions.size, index, conversion::class.simpleName ?: "Converting...") + } + + conversion.runConversion(treeSequence, context) } } } \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt index 8cf95adf5fe..36b3306a8ad 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt @@ -52,14 +52,15 @@ class NewJavaToKotlinConverter( private val LOG = Logger.getInstance("#org.jetbrains.kotlin.j2k.JavaToKotlinConverter") + private val phasesCount = J2KConversionPhase.values().size + override fun filesToKotlin( files: List, postProcessor: PostProcessor, progress: ProgressIndicator ): FilesResult { progress.isIndeterminate = false - val phasesCount = postProcessor.phasesCount + 1 - val withProgressProcessor = NewJ2kWithProgressProcessor(progress, files, phasesCount) + val withProgressProcessor = NewJ2kWithProgressProcessor(progress, files, postProcessor.phasesCount + phasesCount) return withProgressProcessor.process { val (results, externalCodeProcessing, context) = ApplicationManager.getApplication().runReadAction(Computable { @@ -69,6 +70,11 @@ class NewJavaToKotlinConverter( val kotlinFiles = results.mapIndexed { i, result -> runUndoTransparentActionInEdt(inWriteAction = true) { val javaFile = files[i] + withProgressProcessor.updateState( + fileIndex = i, + phase = J2KConversionPhase.CREATE_FILES, + description = "Creating files..." + ) KtPsiFactory(project).createFileWithLightClassSupport( javaFile.name.replace(".java", ".kt"), result!!.text, @@ -84,7 +90,7 @@ class NewJavaToKotlinConverter( JKMultipleFilesPostProcessingTarget(kotlinFiles), context ) { phase, description -> - withProgressProcessor.updateState(fileIndex = null, phase = phase + 1, description = description) + withProgressProcessor.updateState(fileIndex = null, phase = phase + phasesCount, description = description) } FilesResult(kotlinFiles.map { it.text }, externalCodeProcessing) } @@ -103,7 +109,7 @@ class NewJavaToKotlinConverter( if (importList == null) { addImportList(createdImportList) } else { - val updatedList = if(importList.firstChild != null) { + val updatedList = if (importList.firstChild != null) { createdImportList.addRangeBefore(importList.firstChild, importList.lastChild, createdImportList.firstChild) } else createdImportList importList.replace(updatedList) @@ -149,7 +155,7 @@ class NewJavaToKotlinConverter( } val asts = inputElements.mapIndexed { i, element -> - processor.updateState(i, 1, phaseDescription) + processor.updateState(i, J2KConversionPhase.BUILD_AST, phaseDescription) element to treeBuilder.buildTree(element, saveImports) } val inConversionContext = { element: PsiElement -> @@ -172,13 +178,18 @@ class NewJavaToKotlinConverter( externalCodeProcessing, languageVersion.supportsFeature(LanguageFeature.FunctionalInterfaceConversion) ) - ConversionsRunner.doApply(asts.withIndex().mapNotNull { (i, ast) -> - processor.updateState(i, 1, phaseDescription) - ast.second - }, context) + ConversionsRunner.doApply(asts.mapNotNull { it.second }, context) { conversionIndex, conversionCount, i, desc -> + processor.updateState( + J2KConversionPhase.RUN_CONVERSIONS.phaseNumber, + conversionIndex, + conversionCount, + i, + desc + ) + } val results = asts.mapIndexed { i, elementWithAst -> - processor.updateState(i, 1, phaseDescription) + processor.updateState(i, J2KConversionPhase.PRINT_CODE, phaseDescription) val (element, ast) = elementWithAst if (ast == null) return@mapIndexed null val code = JKCodeBuilder(context).run { printCodeOut(ast) } @@ -215,9 +226,42 @@ class NewJ2kWithProgressProcessor( } override fun updateState(fileIndex: Int?, phase: Int, description: String) { + if (fileIndex == null) + updateState(phase, 1, 1, fileIndex, description) + else + updateState(phase, 0, 1, fileIndex, description) + } + + override fun updateState( + phase: Int, + subPhase: Int, + subPhaseCount: Int, + fileIndex: Int?, + description: String + ) { progress?.checkCanceled() - progress?.fraction = phase / phasesCount.toDouble() - progress?.text = KotlinNJ2KBundle.message("progress.text", description, phase, phasesCount) + val singlePhaseFraction = 1.0 / phasesCount.toDouble() + val singleSubPhaseFraction = singlePhaseFraction / subPhaseCount.toDouble() + + var resultFraction = phase * singlePhaseFraction + subPhase * singleSubPhaseFraction + if (files != null && fileIndex != null && files.isNotEmpty()) { + val fileFraction = singleSubPhaseFraction / files.size.toDouble() + resultFraction += fileFraction * fileIndex + } + progress?.fraction = resultFraction + + if (subPhaseCount > 1) { + progress?.text = KotlinNJ2KBundle.message( + "subphase.progress.text", + description, + subPhase, + subPhaseCount, + phase + 1, + phasesCount + ) + } else { + progress?.text = KotlinNJ2KBundle.message("progress.text", description, phase + 1, phasesCount) + } progress?.text2 = when { files != null && files.isNotEmpty() && fileIndex != null -> files[fileIndex].virtualFile.presentableUrl + if (files.size > 1) " ($fileIndex/${files.size})" else "" else -> "" @@ -238,4 +282,15 @@ class NewJ2kWithProgressProcessor( return result!! } +} + +internal fun WithProgressProcessor.updateState(fileIndex: Int?, phase: J2KConversionPhase, description: String) { + updateState(fileIndex, phase.phaseNumber, description) +} + +internal enum class J2KConversionPhase(val phaseNumber: Int) { + BUILD_AST(0), + RUN_CONVERSIONS(1), + PRINT_CODE(2), + CREATE_FILES(3) } \ No newline at end of file