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 org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
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.range
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -42,6 +42,9 @@ import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import java.util.*
class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
override val phasesCount: Int = 1
override fun insertImport(file: KtFile, fqName: FqName) {
ApplicationManager.getApplication().invokeAndWait {
runWriteAction {
@@ -57,7 +60,12 @@ class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
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())) {
do {
var modificationStamp: Long? = file.modificationStamp
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.idea.j2k
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.j2k.*
object JavaToKotlinConverterFactory {
@@ -20,6 +22,17 @@ object JavaToKotlinConverterFactory {
fun createPostProcessor(formatCode: Boolean): PostProcessor =
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 =
J2kConverterExtension.extension.doCheckBeforeConversion(project, module)
val isNewJ2k: Boolean
get() = J2kConverterExtension.extension.isNewJ2k
}
@@ -1,6 +1,8 @@
package org.jetbrains.kotlin.idea.j2k
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.j2k.*
class OldJ2kConverterExtension : J2kConverterExtension() {
@@ -15,4 +17,11 @@ class OldJ2kConverterExtension : J2kConverterExtension() {
override fun createPostProcessor(formatCode: Boolean): PostProcessor =
J2kPostProcessor(formatCode)
override fun createWithProgressProcessor(
progress: ProgressIndicator?,
files: List<PsiJavaFile>?,
phasesCount: Int
): WithProgressProcessor =
OldWithProgressProcessor(progress, files)
}
@@ -257,7 +257,8 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
.run(
convertedFragment!!,
conversionContext,
range = null
range = null,
onPhaseChanged = null
)
}
} 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.codeInsight.KotlinCopyPasteReferenceProcessor
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.start
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.JavaToKotlinConverterFactory
@@ -161,12 +161,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
val newBounds = insertImports(boundsAfterReplace, referenceData, explicitImports)
PsiDocumentManager.getInstance(project).commitAllDocuments()
AfterConversionPass(project, JavaToKotlinConverterFactory.createPostProcessor(formatCode = true))
.run(
targetFile,
conversionResult?.converterContext,
newBounds
)
runPostProcessing(project, targetFile, newBounds, conversionResult?.converterContext)
conversionPerformed = true
}
@@ -304,3 +299,37 @@ internal fun confirmConvertJavaOnPaste(project: Project, isPlainText: Boolean):
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.idea.KotlinFileType
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.j2k.AfterConversionPass
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
@@ -130,12 +128,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
}
psiDocumentManager.commitAllDocuments()
AfterConversionPass(project, JavaToKotlinConverterFactory.createPostProcessor(formatCode = true))
.run(
targetFile,
convertedResult.converterContext,
newBounds
)
runPostProcessing(project, targetFile, newBounds, convertedResult.converterContext)
conversionPerformed = true
}
@@ -18,20 +18,25 @@ package org.jetbrains.kotlin.j2k
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtFile
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 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.isGreedyToRight = true
marker
} else {
null
}
} else 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.ExtensionPointName
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiJavaFile
abstract class J2kConverterExtension : AbstractExtensionPointBean() {
abstract val isNewJ2k: Boolean
@@ -20,6 +22,12 @@ abstract class J2kConverterExtension : AbstractExtensionPointBean() {
open fun doCheckBeforeConversion(project: Project, module: Module): Boolean =
true
abstract fun createWithProgressProcessor(
progress: ProgressIndicator?,
files: List<PsiJavaFile>?,
phasesCount: Int
): WithProgressProcessor
companion object {
private fun useNewJ2k() = Registry.`is`("kotlin.use.new.j2k", false)
@@ -40,7 +40,14 @@ import java.util.*
interface PostProcessor {
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 {
@@ -73,9 +80,7 @@ abstract class JavaToKotlinConverter {
progress: ProgressIndicator = EmptyProgressIndicator()
): FilesResult
fun elementsToKotlin(inputElements: List<PsiElement>): Result {
return elementsToKotlin(inputElements, WithProgressProcessor.DEFAULT)
}
abstract fun elementsToKotlin(inputElements: List<PsiElement>): Result
}
class OldJavaToKotlinConverter(
@@ -91,7 +96,7 @@ class OldJavaToKotlinConverter(
postProcessor: PostProcessor,
progress: ProgressIndicator
): FilesResult {
val withProgressProcessor = WithProgressProcessor(progress, files)
val withProgressProcessor = OldWithProgressProcessor(progress, files)
val (results, externalCodeProcessing) = ApplicationManager.getApplication().runReadAction(Computable {
elementsToKotlin(files, withProgressProcessor)
})
@@ -106,7 +111,7 @@ class OldJavaToKotlinConverter(
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
} 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(
val reference: PsiReference,
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 {
val DEFAULT = WithProgressProcessor(null, null)
val DEFAULT = OldWithProgressProcessor(null, null)
}
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 pass = 1
fun <TInputItem, TOutputItem> processItems(
override fun <TInputItem, TOutputItem> processItems(
fractionPortion: Double,
inputItems: Iterable<TInputItem>,
processItem: (TInputItem) -> TOutputItem
@@ -291,6 +312,14 @@ class WithProgressProcessor(private val progress: ProgressIndicator?, private va
)
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(
@@ -1,8 +1,10 @@
package org.jetbrains.kotlin.nj2k
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.idea.configuration.getAbleToRunConfigurators
import org.jetbrains.kotlin.idea.configuration.hasAnyKotlinRuntimeInScope
import org.jetbrains.kotlin.idea.configuration.isModuleConfigured
@@ -27,6 +29,13 @@ class NewJ2kConverterExtension : J2kConverterExtension() {
override fun doCheckBeforeConversion(project: Project, module: Module): Boolean =
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 {
val kotlinIsConfigured =
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())) {
for (processing in processings) {
for ((i, processing) in processings.withIndex()) {
onPhaseChanged?.invoke(i, processing.description)
processing.runProcessing(file, rangeMarker, converterContext as NewJ2kConverterContext)
commitFile(file)
}
@@ -82,15 +90,20 @@ private val processings: List<GeneralPostProcessing> = listOf(
nullabilityProcessing,
formatCodeProcessing,
shortenReferencesProcessing,
InspectionLikeProcessingGroup(VarToValProcessing()),
InspectionLikeProcessingGroup(ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION, VarToValProcessing()),
ConvertGettersAndSettersToPropertyProcessing(),
InspectionLikeProcessingGroup(MoveGetterAndSetterAnnotationsToPropertyProcessing()),
InspectionLikeProcessingGroup(
ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION,
MoveGetterAndSetterAnnotationsToPropertyProcessing()
),
InspectionLikeProcessingGroup(
ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION,
generalInspectionBasedProcessing(RedundantGetterInspection()),
generalInspectionBasedProcessing(RedundantSetterInspection())
),
ConvertToDataClassProcessing(),
InspectionLikeProcessingGroup(
"Cleaning up Kotlin code",
RemoveRedundantVisibilityModifierProcessing(),
RemoveRedundantModalityModifierProcessing(),
RemoveRedundantConstructorKeywordProcessing(),
@@ -16,8 +16,8 @@ import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
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.range
import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument
import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
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.runWriteAction
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.KtFile
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.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.utils.mapToIndex
import java.util.ArrayList
import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
class InspectionLikeProcessingGroup(val inspectionLikeProcessings: List<InspectionLikeProcessing>) : ProcessingGroup {
constructor(vararg inspectionLikeProcessings: InspectionLikeProcessing) : this(inspectionLikeProcessings.toList())
class InspectionLikeProcessingGroup(
override val description: String,
val inspectionLikeProcessings: List<InspectionLikeProcessing>
) : ProcessingGroup {
constructor(description: String, vararg inspectionLikeProcessings: InspectionLikeProcessing) :
this(description, inspectionLikeProcessings.toList())
private val processingsToPriorityMap = inspectionLikeProcessings.mapToIndex()
fun priority(processing: InspectionLikeProcessing): Int = processingsToPriorityMap.getValue(processing)
@@ -18,10 +18,11 @@ import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
interface GeneralPostProcessing {
val description: String
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) {
withContext(EDT) {
CommandProcessor.getInstance().runUndoTransparentAction {
@@ -35,7 +36,7 @@ abstract class SimplePostProcessing : GeneralPostProcessing {
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) {
val elements =
rangeMarker?.let { marker ->
@@ -49,8 +50,11 @@ abstract class ElementsBasedPostProcessing : SimplePostProcessing() {
interface ProcessingGroup : GeneralPostProcessing
fun postProcessing(action: (file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) -> Unit): GeneralPostProcessing =
object : SimplePostProcessing() {
fun postProcessing(
description: String,
action: (file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) -> Unit
): GeneralPostProcessing =
object : SimplePostProcessing(description) {
override fun applySimpleProcessing(file: KtFile, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
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.refactoring.rename.RenamePsiElementProcessor
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.core.setVisibility
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.util.CommentSaver
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.psi.*
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.getSuperInterfaces
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.mapToIndex
class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing() {
class ConvertGettersAndSettersToPropertyProcessing : ElementsBasedPostProcessing(DESCRIPTION) {
private fun KtNamedFunction.hasOverrides(): Boolean =
toLightMethods().singleOrNull()?.let { lightMethod ->
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.types.typeUtil.makeNotNullable
class ConvertToDataClassProcessing : ElementsBasedPostProcessing() {
class ConvertToDataClassProcessing : ElementsBasedPostProcessing(ConvertGettersAndSettersToPropertyProcessing.DESCRIPTION) {
private fun KtCallableDeclaration.rename(newName: String) {
val factory = KtPsiFactory(this)
val escapedName = newName.escaped()
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
val formatCodeProcessing =
postProcessing { file, rangeMarker, _ ->
postProcessing("Formating code") { file, rangeMarker, _ ->
file.commitAndUnblockDocument()
val codeStyleManager = CodeStyleManager.getInstance(file.project)
if (rangeMarker != null) {
@@ -35,7 +35,7 @@ val formatCodeProcessing =
}
val nullabilityProcessing =
postProcessing { file, rangeMarker, converterContext ->
postProcessing("Inferring declarations nullability") { file, rangeMarker, converterContext ->
NullabilityAnalysisFacade(
converterContext,
getTypeElementNullability = ::nullabilityByUndefinedNullabilityComment,
@@ -45,7 +45,7 @@ val nullabilityProcessing =
}
val shortenReferencesProcessing =
postProcessing { file, rangeMarker, _ ->
postProcessing("Shortening fully-qualified references") { file, rangeMarker, _ ->
if (rangeMarker != null) {
ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset)
} else {
@@ -54,7 +54,7 @@ val shortenReferencesProcessing =
}
val optimizeImportsProcessing =
postProcessing { file, rangeMarker, _ ->
postProcessing("Optimizing imports") { file, rangeMarker, _ ->
val elements = if (rangeMarker != null) {
file.elementsInRange(TextRange(rangeMarker.startOffset, rangeMarker.endOffset))
} else file.children.asList()
@@ -20,8 +20,10 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Computable
import com.intellij.psi.PsiElement
@@ -50,13 +52,15 @@ class NewJavaToKotlinConverter(
postProcessor: PostProcessor,
progress: ProgressIndicator
): FilesResult {
val withProgressProcessor = WithProgressProcessor(progress, files)
val (results, externalCodeProcessing, context) = ApplicationManager.getApplication().runReadAction(Computable {
elementsToKotlin(files, withProgressProcessor)
})
val phasesCount = postProcessor.phasesCount + 1
val withProgressProcessor = NewJ2kWithProgressProcessor(progress, files, phasesCount)
val (results, externalCodeProcessing, context) = withProgressProcessor.process {
ApplicationManager.getApplication().runReadAction(Computable {
elementsToKotlin(files, withProgressProcessor)
})
}
val texts = withProgressProcessor.processItems(0.5, results.withIndex()) { pair ->
val (i, result) = pair
val texts = results.mapIndexed { i, result ->
try {
val kotlinFile = ApplicationManager.getApplication().runReadAction(Computable {
KtPsiFactory(project).createFileWithLightClassSupport("dummy.kt", result!!.text, files[i])
@@ -71,9 +75,15 @@ class NewJavaToKotlinConverter(
}
}
}
AfterConversionPass(project, postProcessor).run(kotlinFile, context, range = null)
withProgressProcessor.process {
AfterConversionPass(project, postProcessor).run(
kotlinFile,
context,
range = null,
onPhaseChanged = { phase, description ->
withProgressProcessor.updateState(i, phase + 1, description)
})
}
kotlinFile.text
} catch (e: ProcessCanceledException) {
throw e
@@ -87,12 +97,19 @@ class NewJavaToKotlinConverter(
}
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()
symbolProvider.preBuildTree(inputElements)
val importStorage = ImportStorage()
val treeBuilder = JavaToJKTreeBuilder(symbolProvider, converterServices, importStorage)
val asts = inputElements.map { element ->
element to treeBuilder.buildTree(element)
val asts = processor.process {
inputElements.mapIndexed { i, element ->
processor.updateState(i, 1, phaseDescription)
element to treeBuilder.buildTree(element)
}
}
val context = NewJ2kConverterContext(
@@ -102,22 +119,69 @@ class NewJavaToKotlinConverter(
importStorage,
JKElementInfoStorage()
)
ConversionsRunner.doApply(asts.mapNotNull { it.second }, context)
val results = asts.map { (element, ast) ->
if (ast == null) return@map null
val code = NewCodeBuilder(context).run { printCodeOut(ast) }
val parseContext = when (element) {
is PsiStatement, is PsiExpression -> ParseContext.CODE_BLOCK
else -> ParseContext.TOP_LEVEL
processor.process {
ConversionsRunner.doApply(asts.withIndex().mapNotNull { (i, ast) ->
processor.updateState(i, 1, phaseDescription)
ast.second
}, context)
}
val results = processor.process {
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)
}
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!!
}
}