New J2K: add conversion progress bar

Relates to #KT-31812
This commit is contained in:
Ilya Kirillov
2019-06-05 17:18:34 +03:00
parent f51e28ea68
commit 1a9deed5d8
17 changed files with 277 additions and 79 deletions
@@ -27,8 +27,8 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.core.util.EDT import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -42,6 +42,9 @@ import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import java.util.* import java.util.*
class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor { class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
override val phasesCount: Int = 1
override fun insertImport(file: KtFile, fqName: FqName) { override fun insertImport(file: KtFile, fqName: FqName) {
ApplicationManager.getApplication().invokeAndWait { ApplicationManager.getApplication().invokeAndWait {
runWriteAction { runWriteAction {
@@ -57,7 +60,12 @@ class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
PROCESS PROCESS
} }
override fun doAdditionalProcessing(file: KtFile, converterContext: ConverterContext?, rangeMarker: RangeMarker?) = override fun doAdditionalProcessing(
file: KtFile,
converterContext: ConverterContext?,
rangeMarker: RangeMarker?,
onPhaseChanged: ((Int, String) -> Unit)?
) =
runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) { runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) {
do { do {
var modificationStamp: Long? = file.modificationStamp var modificationStamp: Long? = file.modificationStamp
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.idea.j2k package org.jetbrains.kotlin.idea.j2k
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.j2k.* import org.jetbrains.kotlin.j2k.*
object JavaToKotlinConverterFactory { object JavaToKotlinConverterFactory {
@@ -20,6 +22,17 @@ object JavaToKotlinConverterFactory {
fun createPostProcessor(formatCode: Boolean): PostProcessor = fun createPostProcessor(formatCode: Boolean): PostProcessor =
J2kConverterExtension.extension.createPostProcessor(formatCode) J2kConverterExtension.extension.createPostProcessor(formatCode)
fun createWithProgressIndicator(
progress: ProgressIndicator?,
files: List<PsiJavaFile>?,
phasesCount: Int
): WithProgressProcessor =
J2kConverterExtension.extension.createWithProgressProcessor(progress, files, phasesCount)
fun doCheckBeforeConversion(project: Project, module: Module): Boolean = fun doCheckBeforeConversion(project: Project, module: Module): Boolean =
J2kConverterExtension.extension.doCheckBeforeConversion(project, module) J2kConverterExtension.extension.doCheckBeforeConversion(project, module)
val isNewJ2k: Boolean
get() = J2kConverterExtension.extension.isNewJ2k
} }
@@ -1,6 +1,8 @@
package org.jetbrains.kotlin.idea.j2k package org.jetbrains.kotlin.idea.j2k
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.j2k.* import org.jetbrains.kotlin.j2k.*
class OldJ2kConverterExtension : J2kConverterExtension() { class OldJ2kConverterExtension : J2kConverterExtension() {
@@ -15,4 +17,11 @@ class OldJ2kConverterExtension : J2kConverterExtension() {
override fun createPostProcessor(formatCode: Boolean): PostProcessor = override fun createPostProcessor(formatCode: Boolean): PostProcessor =
J2kPostProcessor(formatCode) J2kPostProcessor(formatCode)
override fun createWithProgressProcessor(
progress: ProgressIndicator?,
files: List<PsiJavaFile>?,
phasesCount: Int
): WithProgressProcessor =
OldWithProgressProcessor(progress, files)
} }
@@ -257,7 +257,8 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
.run( .run(
convertedFragment!!, convertedFragment!!,
conversionContext, conversionContext,
range = null range = null,
onPhaseChanged = null
) )
} }
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -33,8 +33,8 @@ import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
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.core.util.start
import org.jetbrains.kotlin.idea.core.util.range import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.core.util.start
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.JavaToKotlinConverterFactory import org.jetbrains.kotlin.idea.j2k.JavaToKotlinConverterFactory
@@ -161,12 +161,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
val newBounds = insertImports(boundsAfterReplace, referenceData, explicitImports) val newBounds = insertImports(boundsAfterReplace, referenceData, explicitImports)
PsiDocumentManager.getInstance(project).commitAllDocuments() PsiDocumentManager.getInstance(project).commitAllDocuments()
AfterConversionPass(project, JavaToKotlinConverterFactory.createPostProcessor(formatCode = true)) runPostProcessing(project, targetFile, newBounds, conversionResult?.converterContext)
.run(
targetFile,
conversionResult?.converterContext,
newBounds
)
conversionPerformed = true conversionPerformed = true
} }
@@ -304,3 +299,37 @@ internal fun confirmConvertJavaOnPaste(project: Project, isPlainText: Boolean):
return dialog.isOK return dialog.isOK
} }
fun runPostProcessing(project: Project, file: KtFile, bounds: TextRange?, converterContext: ConverterContext?) {
val postProcessor = JavaToKotlinConverterFactory.createPostProcessor(formatCode = true)
if (JavaToKotlinConverterFactory.isNewJ2k) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(
{
val processor =
JavaToKotlinConverterFactory.createWithProgressIndicator(
ProgressManager.getInstance().progressIndicator!!,
emptyList(),
postProcessor.phasesCount
)
AfterConversionPass(project, postProcessor)
.run(
file,
converterContext,
bounds
) { phase, description ->
processor.updateState(0, phase, description)
}
},
"Convert Java to Kotlin",
true,
project
)
} else {
AfterConversionPass(project, postProcessor)
.run(
file,
converterContext,
bounds
)
}
}
@@ -36,9 +36,7 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
import org.jetbrains.kotlin.idea.j2k.JavaToKotlinConverterFactory
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.AfterConversionPass
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
@@ -130,12 +128,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
} }
psiDocumentManager.commitAllDocuments() psiDocumentManager.commitAllDocuments()
AfterConversionPass(project, JavaToKotlinConverterFactory.createPostProcessor(formatCode = true)) runPostProcessing(project, targetFile, newBounds, convertedResult.converterContext)
.run(
targetFile,
convertedResult.converterContext,
newBounds
)
conversionPerformed = true conversionPerformed = true
} }
@@ -18,20 +18,25 @@ package org.jetbrains.kotlin.j2k
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) { class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) {
fun run(kotlinFile: KtFile, converterContext: ConverterContext?, range: TextRange?) { @JvmOverloads
fun run(
kotlinFile: KtFile,
converterContext: ConverterContext?,
range: TextRange?,
onPhaseChanged: ((Int, String) -> Unit)? = null
) {
val rangeMarker = if (range != null) { val rangeMarker = if (range != null) {
val document = kotlinFile.viewProvider.document!! val document = kotlinFile.viewProvider.document!!
val marker = document.createRangeMarker(range.startOffset, range.endOffset) val marker = runReadAction { document.createRangeMarker(range.startOffset, range.endOffset) }
marker.isGreedyToLeft = true marker.isGreedyToLeft = true
marker.isGreedyToRight = true marker.isGreedyToRight = true
marker marker
} else { } else null
null
}
postProcessor.doAdditionalProcessing(kotlinFile, converterContext, rangeMarker) postProcessor.doAdditionalProcessing(kotlinFile, converterContext, rangeMarker, onPhaseChanged)
} }
} }
@@ -3,8 +3,10 @@ package org.jetbrains.kotlin.j2k
import com.intellij.openapi.extensions.AbstractExtensionPointBean import com.intellij.openapi.extensions.AbstractExtensionPointBean
import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiJavaFile
abstract class J2kConverterExtension : AbstractExtensionPointBean() { abstract class J2kConverterExtension : AbstractExtensionPointBean() {
abstract val isNewJ2k: Boolean abstract val isNewJ2k: Boolean
@@ -20,6 +22,12 @@ abstract class J2kConverterExtension : AbstractExtensionPointBean() {
open fun doCheckBeforeConversion(project: Project, module: Module): Boolean = open fun doCheckBeforeConversion(project: Project, module: Module): Boolean =
true true
abstract fun createWithProgressProcessor(
progress: ProgressIndicator?,
files: List<PsiJavaFile>?,
phasesCount: Int
): WithProgressProcessor
companion object { companion object {
private fun useNewJ2k() = Registry.`is`("kotlin.use.new.j2k", false) private fun useNewJ2k() = Registry.`is`("kotlin.use.new.j2k", false)
@@ -40,7 +40,14 @@ import java.util.*
interface PostProcessor { interface PostProcessor {
fun insertImport(file: KtFile, fqName: FqName) fun insertImport(file: KtFile, fqName: FqName)
fun doAdditionalProcessing(file: KtFile, converterContext: ConverterContext?, rangeMarker: RangeMarker?) val phasesCount: Int
fun doAdditionalProcessing(
file: KtFile,
converterContext: ConverterContext?,
rangeMarker: RangeMarker?,
onPhaseChanged: ((Int, String) -> Unit)?
)
} }
enum class ParseContext { enum class ParseContext {
@@ -73,9 +80,7 @@ abstract class JavaToKotlinConverter {
progress: ProgressIndicator = EmptyProgressIndicator() progress: ProgressIndicator = EmptyProgressIndicator()
): FilesResult ): FilesResult
fun elementsToKotlin(inputElements: List<PsiElement>): Result { abstract fun elementsToKotlin(inputElements: List<PsiElement>): Result
return elementsToKotlin(inputElements, WithProgressProcessor.DEFAULT)
}
} }
class OldJavaToKotlinConverter( class OldJavaToKotlinConverter(
@@ -91,7 +96,7 @@ class OldJavaToKotlinConverter(
postProcessor: PostProcessor, postProcessor: PostProcessor,
progress: ProgressIndicator progress: ProgressIndicator
): FilesResult { ): FilesResult {
val withProgressProcessor = WithProgressProcessor(progress, files) val withProgressProcessor = OldWithProgressProcessor(progress, files)
val (results, externalCodeProcessing) = ApplicationManager.getApplication().runReadAction(Computable { val (results, externalCodeProcessing) = ApplicationManager.getApplication().runReadAction(Computable {
elementsToKotlin(files, withProgressProcessor) elementsToKotlin(files, withProgressProcessor)
}) })
@@ -106,7 +111,7 @@ class OldJavaToKotlinConverter(
result!!.importsToAdd.forEach { postProcessor.insertImport(kotlinFile, it) } result!!.importsToAdd.forEach { postProcessor.insertImport(kotlinFile, it) }
AfterConversionPass(project, postProcessor).run(kotlinFile, converterContext = null, range = null) AfterConversionPass(project, postProcessor).run(kotlinFile, converterContext = null, range = null, onPhaseChanged = null)
kotlinFile.text kotlinFile.text
} catch (e: ProcessCanceledException) { } catch (e: ProcessCanceledException) {
@@ -157,6 +162,11 @@ class OldJavaToKotlinConverter(
} }
} }
override fun elementsToKotlin(inputElements: List<PsiElement>): Result {
return elementsToKotlin(inputElements, OldWithProgressProcessor.DEFAULT)
}
private data class ReferenceInfo( private data class ReferenceInfo(
val reference: PsiReference, val reference: PsiReference,
val target: PsiElement, val target: PsiElement,
@@ -252,10 +262,21 @@ class OldJavaToKotlinConverter(
} }
} }
interface WithProgressProcessor {
fun <TInputItem, TOutputItem> processItems(
fractionPortion: Double,
inputItems: Iterable<TInputItem>,
processItem: (TInputItem) -> TOutputItem
): List<TOutputItem>
class WithProgressProcessor(private val progress: ProgressIndicator?, private val files: List<PsiJavaFile>?) { fun updateState(fileIndex: Int, phase: Int, description: String)
fun <T> process(action: () -> T): T
}
class OldWithProgressProcessor(private val progress: ProgressIndicator?, private val files: List<PsiJavaFile>?) : WithProgressProcessor {
companion object { companion object {
val DEFAULT = WithProgressProcessor(null, null) val DEFAULT = OldWithProgressProcessor(null, null)
} }
private val progressText = "Converting Java to Kotlin" private val progressText = "Converting Java to Kotlin"
@@ -264,7 +285,7 @@ class WithProgressProcessor(private val progress: ProgressIndicator?, private va
private var fraction = 0.0 private var fraction = 0.0
private var pass = 1 private var pass = 1
fun <TInputItem, TOutputItem> processItems( override fun <TInputItem, TOutputItem> processItems(
fractionPortion: Double, fractionPortion: Double,
inputItems: Iterable<TInputItem>, inputItems: Iterable<TInputItem>,
processItem: (TInputItem) -> TOutputItem processItem: (TInputItem) -> TOutputItem
@@ -291,6 +312,14 @@ class WithProgressProcessor(private val progress: ProgressIndicator?, private va
) )
return outputItems return outputItems
} }
override fun <T> process(action: () -> T): T {
throw AbstractMethodError("Should not be called for old J2K")
}
override fun updateState(fileIndex: Int, phase: Int, description: String) {
throw AbstractMethodError("Should not be called for old J2K")
}
} }
private class ProgressPortionReporter( private class ProgressPortionReporter(
@@ -1,8 +1,10 @@
package org.jetbrains.kotlin.nj2k package org.jetbrains.kotlin.nj2k
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.idea.configuration.getAbleToRunConfigurators import org.jetbrains.kotlin.idea.configuration.getAbleToRunConfigurators
import org.jetbrains.kotlin.idea.configuration.hasAnyKotlinRuntimeInScope import org.jetbrains.kotlin.idea.configuration.hasAnyKotlinRuntimeInScope
import org.jetbrains.kotlin.idea.configuration.isModuleConfigured import org.jetbrains.kotlin.idea.configuration.isModuleConfigured
@@ -27,6 +29,13 @@ class NewJ2kConverterExtension : J2kConverterExtension() {
override fun doCheckBeforeConversion(project: Project, module: Module): Boolean = override fun doCheckBeforeConversion(project: Project, module: Module): Boolean =
checkKotlinIsConfigured(project, module) checkKotlinIsConfigured(project, module)
override fun createWithProgressProcessor(
progress: ProgressIndicator?,
files: List<PsiJavaFile>?,
phasesCount: Int
): WithProgressProcessor =
NewJ2kWithProgressProcessor(progress, files, phasesCount)
private fun checkKotlinIsConfigured(project: Project, module: Module): Boolean { private fun checkKotlinIsConfigured(project: Project, module: Module): Boolean {
val kotlinIsConfigured = val kotlinIsConfigured =
hasAnyKotlinRuntimeInScope(module) || isModuleConfigured(module.toModuleGroup()) hasAnyKotlinRuntimeInScope(module) || isModuleConfigured(module.toModuleGroup())
@@ -58,9 +58,17 @@ class NewJ2kPostProcessor : PostProcessor {
} }
} }
override fun doAdditionalProcessing(file: KtFile, converterContext: ConverterContext?, rangeMarker: RangeMarker?) { override val phasesCount = processings.size
override fun doAdditionalProcessing(
file: KtFile,
converterContext: ConverterContext?,
rangeMarker: RangeMarker?,
onPhaseChanged: ((Int, String) -> Unit)?
) {
runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) { runBlocking(EDT.ModalityStateElement(ModalityState.defaultModalityState())) {
for (processing in processings) { for ((i, processing) in processings.withIndex()) {
onPhaseChanged?.invoke(i, processing.description)
processing.runProcessing(file, rangeMarker, converterContext as NewJ2kConverterContext) processing.runProcessing(file, rangeMarker, converterContext as NewJ2kConverterContext)
commitFile(file) commitFile(file)
} }
@@ -82,15 +90,20 @@ private val processings: List<GeneralPostProcessing> = listOf(
nullabilityProcessing, nullabilityProcessing,
formatCodeProcessing, formatCodeProcessing,
shortenReferencesProcessing, shortenReferencesProcessing,
InspectionLikeProcessingGroup(VarToValProcessing()), InspectionLikeProcessingGroup(ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION, VarToValProcessing()),
ConvertGettersAndSettersToPropertyProcessing(), ConvertGettersAndSettersToPropertyProcessing(),
InspectionLikeProcessingGroup(MoveGetterAndSetterAnnotationsToPropertyProcessing()),
InspectionLikeProcessingGroup( InspectionLikeProcessingGroup(
ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION,
MoveGetterAndSetterAnnotationsToPropertyProcessing()
),
InspectionLikeProcessingGroup(
ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION,
generalInspectionBasedProcessing(RedundantGetterInspection()), generalInspectionBasedProcessing(RedundantGetterInspection()),
generalInspectionBasedProcessing(RedundantSetterInspection()) generalInspectionBasedProcessing(RedundantSetterInspection())
), ),
ConvertToDataClassProcessing(), ConvertToDataClassProcessing(),
InspectionLikeProcessingGroup( InspectionLikeProcessingGroup(
"Cleaning up Kotlin code",
RemoveRedundantVisibilityModifierProcessing(), RemoveRedundantVisibilityModifierProcessing(),
RemoveRedundantModalityModifierProcessing(), RemoveRedundantModalityModifierProcessing(),
RemoveRedundantConstructorKeywordProcessing(), RemoveRedundantConstructorKeywordProcessing(),
@@ -16,8 +16,8 @@ import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.core.util.EDT import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.core.util.range
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.idea.quickfix.QuickFixActionBase
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.ConverterSettings import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.nj2k.* import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
@@ -36,13 +36,18 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.utils.mapToIndex import org.jetbrains.kotlin.utils.mapToIndex
import java.util.ArrayList import java.util.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List<InspectionLikeProcessing>) : ProcessingGroup { class InspectionLikeProcessingGroup(
constructor(vararg inspectionLikeProcessings: InspectionLikeProcessing) : this(inspectionLikeProcessings.toList()) override val description: String,
val inspectionLikeProcessings: List<InspectionLikeProcessing>
) : ProcessingGroup {
constructor(description: String, vararg inspectionLikeProcessings: InspectionLikeProcessing) :
this(description, inspectionLikeProcessings.toList())
private val processingsToPriorityMap = inspectionLikeProcessings.mapToIndex() private val processingsToPriorityMap = inspectionLikeProcessings.mapToIndex()
fun priority(processing: InspectionLikeProcessing): Int = processingsToPriorityMap.getValue(processing) fun priority(processing: InspectionLikeProcessing): Int = processingsToPriorityMap.getValue(processing)
@@ -18,10 +18,11 @@ import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
interface GeneralPostProcessing { interface GeneralPostProcessing {
val description: String
suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext)
} }
abstract class SimplePostProcessing : GeneralPostProcessing { abstract class SimplePostProcessing(override val description: String) : GeneralPostProcessing {
final override suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { final override suspend fun runProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
withContext(EDT) { withContext(EDT) {
CommandProcessor.getInstance().runUndoTransparentAction { CommandProcessor.getInstance().runUndoTransparentAction {
@@ -35,7 +36,7 @@ abstract class SimplePostProcessing : GeneralPostProcessing {
abstract fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) abstract fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext)
} }
abstract class ElementsBasedPostProcessing : SimplePostProcessing() { abstract class ElementsBasedPostProcessing(description: String) : SimplePostProcessing(description) {
final override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { final override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
val elements = val elements =
rangeMarker?.let { marker -> rangeMarker?.let { marker ->
@@ -49,8 +50,11 @@ abstract class ElementsBasedPostProcessing : SimplePostProcessing() {
interface ProcessingGroup : GeneralPostProcessing interface ProcessingGroup : GeneralPostProcessing
fun postProcessing(action: (file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) -> Unit): GeneralPostProcessing = fun postProcessing(
object : SimplePostProcessing() { description: String,
action: (file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) -> Unit
): GeneralPostProcessing =
object : SimplePostProcessing(description) {
override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) { override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
action(file, rangeMarker, converterContext) action(file, rangeMarker, converterContext)
} }
@@ -11,7 +11,10 @@ import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.rename.RenamePsiElementProcessor import com.intellij.refactoring.rename.RenamePsiElementProcessor
import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.setVisibility import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.idea.refactoring.isAbstract import org.jetbrains.kotlin.idea.refactoring.isAbstract
@@ -21,11 +24,13 @@ import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.readWriteAccess import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.nj2k.* import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asGetterName
import org.jetbrains.kotlin.nj2k.asSetterName
import org.jetbrains.kotlin.nj2k.parentOfType
import org.jetbrains.kotlin.nj2k.postProcessing.* import org.jetbrains.kotlin.nj2k.postProcessing.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -38,7 +43,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.mapToIndex import org.jetbrains.kotlin.utils.mapToIndex
class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing() { class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing(DESCRIPTION) {
private fun KtNamedFunction.hasOverrides(): Boolean = private fun KtNamedFunction.hasOverrides(): Boolean =
toLightMethods().singleOrNull()?.let { lightMethod -> toLightMethods().singleOrNull()?.let { lightMethod ->
OverridingMethodsSearch.search(lightMethod).findFirst() OverridingMethodsSearch.search(lightMethod).findFirst()
@@ -520,6 +525,9 @@ class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing
} }
} }
} }
companion object {
const val DESCRIPTION = "Converting POJOs to data classes"
}
} }
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
class ConvertToDataClassProcessing : ElementsBasedPostProcessing() { class ConvertToDataClassProcessing : ElementsBasedPostProcessing(ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION) {
private fun KtCallableDeclaration.rename(newName: String) { private fun KtCallableDeclaration.rename(newName: String) {
val factory = KtPsiFactory(this) val factory = KtPsiFactory(this)
val escapedName = newName.escaped() val escapedName = newName.escaped()
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
val formatCodeProcessing = val formatCodeProcessing =
postProcessing { file, rangeMarker, _ -> postProcessing("Formating code") { file, rangeMarker, _ ->
file.commitAndUnblockDocument() file.commitAndUnblockDocument()
val codeStyleManager = CodeStyleManager.getInstance(file.project) val codeStyleManager = CodeStyleManager.getInstance(file.project)
if (rangeMarker != null) { if (rangeMarker != null) {
@@ -35,7 +35,7 @@ val formatCodeProcessing =
} }
val nullabilityProcessing = val nullabilityProcessing =
postProcessing { file, rangeMarker, converterContext -> postProcessing("Inferring declarations nullability") { file, rangeMarker, converterContext ->
NullabilityAnalysisFacade( NullabilityAnalysisFacade(
converterContext, converterContext,
getTypeElementNullability = ::nullabilityByUndefinedNullabilityComment, getTypeElementNullability = ::nullabilityByUndefinedNullabilityComment,
@@ -45,7 +45,7 @@ val nullabilityProcessing =
} }
val shortenReferencesProcessing = val shortenReferencesProcessing =
postProcessing { file, rangeMarker, _ -> postProcessing("Shortening fully-qualified references") { file, rangeMarker, _ ->
if (rangeMarker != null) { if (rangeMarker != null) {
ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset) ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset)
} else { } else {
@@ -54,7 +54,7 @@ val shortenReferencesProcessing =
} }
val optimizeImportsProcessing = val optimizeImportsProcessing =
postProcessing { file, rangeMarker, _ -> postProcessing("Optimizing imports") { file, rangeMarker, _ ->
val elements = if (rangeMarker != null) { val elements = if (rangeMarker != null) {
file.elementsInRange(TextRange(rangeMarker.startOffset, rangeMarker.endOffset)) file.elementsInRange(TextRange(rangeMarker.startOffset, rangeMarker.endOffset))
} else file.children.asList() } else file.children.asList()
@@ -20,8 +20,10 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Computable import com.intellij.openapi.util.Computable
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
@@ -50,13 +52,15 @@ class NewJavaToKotlinConverter(
postProcessor: PostProcessor, postProcessor: PostProcessor,
progress: ProgressIndicator progress: ProgressIndicator
): FilesResult { ): FilesResult {
val withProgressProcessor = WithProgressProcessor(progress, files) val phasesCount = postProcessor.phasesCount + 1
val (results, externalCodeProcessing, context) = ApplicationManager.getApplication().runReadAction(Computable { val withProgressProcessor = NewJ2kWithProgressProcessor(progress, files, phasesCount)
elementsToKotlin(files, withProgressProcessor) val (results, externalCodeProcessing, context) = withProgressProcessor.process {
}) ApplicationManager.getApplication().runReadAction(Computable {
elementsToKotlin(files, withProgressProcessor)
})
}
val texts = withProgressProcessor.processItems(0.5, results.withIndex()) { pair -> val texts = results.mapIndexed { i, result ->
val (i, result) = pair
try { try {
val kotlinFile = ApplicationManager.getApplication().runReadAction(Computable { val kotlinFile = ApplicationManager.getApplication().runReadAction(Computable {
KtPsiFactory(project).createFileWithLightClassSupport("dummy.kt", result!!.text, files[i]) KtPsiFactory(project).createFileWithLightClassSupport("dummy.kt", result!!.text, files[i])
@@ -71,9 +75,15 @@ class NewJavaToKotlinConverter(
} }
} }
} }
withProgressProcessor.process {
AfterConversionPass(project, postProcessor).run(kotlinFile, context, range = null) AfterConversionPass(project, postProcessor).run(
kotlinFile,
context,
range = null,
onPhaseChanged = { phase, description ->
withProgressProcessor.updateState(i, phase + 1, description)
})
}
kotlinFile.text kotlinFile.text
} catch (e: ProcessCanceledException) { } catch (e: ProcessCanceledException) {
throw e throw e
@@ -87,12 +97,19 @@ class NewJavaToKotlinConverter(
} }
override fun elementsToKotlin(inputElements: List<PsiElement>, processor: WithProgressProcessor): Result { override fun elementsToKotlin(inputElements: List<PsiElement>, processor: WithProgressProcessor): Result {
@Suppress("NAME_SHADOWING")
val processor = processor as NewJ2kWithProgressProcessor
val phaseDescription = "Converting Java code to Kotlin code"
val symbolProvider = JKSymbolProvider() val symbolProvider = JKSymbolProvider()
symbolProvider.preBuildTree(inputElements) symbolProvider.preBuildTree(inputElements)
val importStorage = ImportStorage() val importStorage = ImportStorage()
val treeBuilder = JavaToJKTreeBuilder(symbolProvider, converterServices, importStorage) val treeBuilder = JavaToJKTreeBuilder(symbolProvider, converterServices, importStorage)
val asts = inputElements.map { element -> val asts = processor.process {
element to treeBuilder.buildTree(element) inputElements.mapIndexed { i, element ->
processor.updateState(i, 1, phaseDescription)
element to treeBuilder.buildTree(element)
}
} }
val context = NewJ2kConverterContext( val context = NewJ2kConverterContext(
@@ -102,22 +119,69 @@ class NewJavaToKotlinConverter(
importStorage, importStorage,
JKElementInfoStorage() JKElementInfoStorage()
) )
processor.process {
ConversionsRunner.doApply(asts.mapNotNull { it.second }, context) ConversionsRunner.doApply(asts.withIndex().mapNotNull { (i, ast) ->
val results = asts.map { (element, ast) -> processor.updateState(i, 1, phaseDescription)
if (ast == null) return@map null ast.second
val code = NewCodeBuilder(context).run { printCodeOut(ast) } }, context)
val parseContext = when (element) { }
is PsiStatement, is PsiExpression -> ParseContext.CODE_BLOCK val results = processor.process {
else -> ParseContext.TOP_LEVEL asts.mapIndexed { i, elementWithAst ->
processor.updateState(i, 1, phaseDescription)
val (element, ast) = elementWithAst
if (ast == null) return@mapIndexed null
val code = NewCodeBuilder(context).run { printCodeOut(ast) }
val parseContext = when (element) {
is PsiStatement, is PsiExpression -> ParseContext.CODE_BLOCK
else -> ParseContext.TOP_LEVEL
}
ElementResult(
code,
importsToAdd = importStorage.getImports(),
parseContext = parseContext
)
} }
ElementResult(
code,
importsToAdd = importStorage.getImports(),
parseContext = parseContext
)
} }
return Result(results, null, context) return Result(results, null, context)
} }
override fun elementsToKotlin(inputElements: List<PsiElement>): Result {
return elementsToKotlin(inputElements, NewJ2kWithProgressProcessor.DEFAULT)
}
}
class NewJ2kWithProgressProcessor(
private val progress: ProgressIndicator?,
private val files: List<PsiJavaFile>?,
private val phasesCount: Int
) : WithProgressProcessor {
companion object {
val DEFAULT = NewJ2kWithProgressProcessor(null, null, 0)
}
override fun updateState(fileIndex: Int, phase: Int, description: String) {
progress?.checkCanceled()
progress?.fraction = phase / phasesCount.toDouble()
progress?.text = "$description - phase $phase of $phasesCount"
if (files != null && files.isNotEmpty()) {
progress?.text2 = files[fileIndex].virtualFile.presentableUrl
}
}
override fun <TInputItem, TOutputItem> processItems(
fractionPortion: Double,
inputItems: Iterable<TInputItem>,
processItem: (TInputItem) -> TOutputItem
): List<TOutputItem> {
throw AbstractMethodError("Should not be called for new J2K")
}
override fun <T> process(action: () -> T): T {
// we use special process with EmptyProgressIndicator to avoid changing text in our progress by inheritors search inside etc
var result: T? = null
ProgressManager.getInstance().runProcess({ result = action() }, EmptyProgressIndicator())
return result!!
}
} }