Conversion from Java on paste: post processing of code really works
This commit is contained in:
@@ -16,26 +16,33 @@
|
||||
|
||||
package org.jetbrains.kotlin.j2k
|
||||
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.JetUnaryExpression
|
||||
import org.jetbrains.kotlin.psi.JetProperty
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
|
||||
class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) {
|
||||
public fun run(kotlinCode: String): String {
|
||||
//TODO: it's more correct to analyze all converted files together
|
||||
val kotlinFile = JetPsiFactory(project).createAnalyzableFile(
|
||||
"fileForAfterConversionPass.kt", kotlinCode, postProcessor.contextToAnalyzeIn
|
||||
)
|
||||
val bindingContext = postProcessor.analyzeFile(kotlinFile)
|
||||
public class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) {
|
||||
public fun run(kotlinFile: JetFile, range: TextRange?) {
|
||||
val bindingContext = postProcessor.analyzeFile(kotlinFile, range)
|
||||
|
||||
val fixes = bindingContext.getDiagnostics().map {
|
||||
val fix = postProcessor.fixForProblem(it)
|
||||
if (fix != null) it.getPsiElement() to fix else null
|
||||
}.filterNotNull()
|
||||
fun fixForProblem(diagnostic: Diagnostic): (() -> Unit)? {
|
||||
val psiElement = diagnostic.getPsiElement()
|
||||
if (range != null && psiElement.getTextRange() !in range) return null
|
||||
return postProcessor.fixForProblem(diagnostic)
|
||||
}
|
||||
|
||||
val fixes = bindingContext.getDiagnostics()
|
||||
.map {
|
||||
val fix = fixForProblem(it)
|
||||
if (fix != null) Pair(it.getPsiElement(), fix) else null
|
||||
}
|
||||
.filterNotNull()
|
||||
|
||||
val document = kotlinFile.getViewProvider().getDocument()!!
|
||||
val rangeMarker = if (range != null) document.createRangeMarker(range.getStartOffset(), range.getEndOffset()) else null
|
||||
rangeMarker?.setGreedyToLeft(true)
|
||||
rangeMarker?.setGreedyToRight(true)
|
||||
|
||||
for ((psiElement, fix) in fixes) {
|
||||
if (psiElement.isValid()) {
|
||||
@@ -43,8 +50,6 @@ class AfterConversionPass(val project: Project, val postProcessor: PostProcessor
|
||||
}
|
||||
}
|
||||
|
||||
postProcessor.doAdditionalProcessing(kotlinFile)
|
||||
|
||||
return kotlinFile.getText()!!
|
||||
postProcessor.doAdditionalProcessing(kotlinFile, rangeMarker)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ class Converter private(
|
||||
val inConversionScope: (PsiElement) -> Boolean,
|
||||
val referenceSearcher: ReferenceSearcher,
|
||||
val resolverForConverter: ResolverForConverter,
|
||||
private val postProcessor: PostProcessor?,
|
||||
private val commonState: Converter.CommonState,
|
||||
private val personalState: Converter.PersonalState
|
||||
) {
|
||||
@@ -60,16 +59,16 @@ class Converter private(
|
||||
|
||||
companion object {
|
||||
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, inConversionScope: (PsiElement) -> Boolean,
|
||||
referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter, postProcessor: PostProcessor?,
|
||||
referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter,
|
||||
usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
|
||||
return Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, postProcessor, CommonState(usageProcessingsCollector), PersonalState(null))
|
||||
return Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, CommonState(usageProcessingsCollector), PersonalState(null))
|
||||
}
|
||||
}
|
||||
|
||||
public fun withSpecialContext(context: PsiElement): Converter = withState(PersonalState(context))
|
||||
|
||||
private fun withState(state: PersonalState): Converter
|
||||
= Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, postProcessor, commonState, state)
|
||||
= Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, commonState, state)
|
||||
|
||||
private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
|
||||
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
package org.jetbrains.kotlin.j2k
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
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.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.j2k.ast.Element
|
||||
@@ -36,8 +37,7 @@ import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
|
||||
public trait PostProcessor {
|
||||
public val contextToAnalyzeIn: PsiElement
|
||||
public fun analyzeFile(file: JetFile): BindingContext
|
||||
public fun analyzeFile(file: JetFile, range: TextRange?): BindingContext
|
||||
|
||||
public open fun fixForProblem(problem: Diagnostic): (() -> Unit)? {
|
||||
val psiElement = problem.getPsiElement()
|
||||
@@ -51,7 +51,7 @@ public trait PostProcessor {
|
||||
Errors.VAL_REASSIGNMENT -> { ->
|
||||
val property = (psiElement as? JetSimpleNameExpression)?.getReference()?.resolve() as? JetProperty
|
||||
if (property != null && !property.isVar()) {
|
||||
val factory = JetPsiFactory(contextToAnalyzeIn.getProject())
|
||||
val factory = JetPsiFactory(psiElement.getProject())
|
||||
property.getValOrVarNode().getPsi()!!.replace(factory.createVarNode().getPsi()!!)
|
||||
}
|
||||
}
|
||||
@@ -60,21 +60,27 @@ public trait PostProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
public fun doAdditionalProcessing(file: JetFile)
|
||||
public fun doAdditionalProcessing(file: JetFile, rangeMarker: RangeMarker?)
|
||||
}
|
||||
|
||||
public class JavaToKotlinConverter(private val project: Project,
|
||||
private val settings: ConverterSettings,
|
||||
private val referenceSearcher: ReferenceSearcher,
|
||||
private val resolverForConverter: ResolverForConverter) {
|
||||
private val resolverForConverter: ResolverForConverter,
|
||||
private val postProcessor: PostProcessor?) {
|
||||
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.j2k.JavaToKotlinConverter")
|
||||
|
||||
public data class InputElement(
|
||||
val element: PsiElement,
|
||||
val postProcessingContext: PsiElement?
|
||||
)
|
||||
|
||||
public fun elementsToKotlin(
|
||||
psiElementsAndProcessors: List<Pair<PsiElement, PostProcessor?>>,
|
||||
inputElements: List<InputElement>,
|
||||
progress: ProgressIndicator = EmptyProgressIndicator()
|
||||
): List<String> {
|
||||
try {
|
||||
val elementCount = psiElementsAndProcessors.size()
|
||||
val elementCount = inputElements.size()
|
||||
val intermediateResults = ArrayList<((Map<PsiElement, UsageProcessing>) -> String)?>(elementCount)
|
||||
val usageProcessings = HashMap<PsiElement, UsageProcessing>()
|
||||
val usageProcessingCollector: (UsageProcessing) -> Unit = { usageProcessing ->
|
||||
@@ -94,12 +100,12 @@ public class JavaToKotlinConverter(private val project: Project,
|
||||
{
|
||||
progress.setText("$progressText ($fileCountText) - pass $pass of 3")
|
||||
|
||||
val filesCount = psiElementsAndProcessors.indices
|
||||
val filesCount = inputElements.indices
|
||||
for (i in filesCount) {
|
||||
progress.checkCanceled()
|
||||
progress.setFraction(fraction + passFraction * i / elementCount)
|
||||
|
||||
val psiFile = psiElementsAndProcessors[i].first as? PsiFile
|
||||
val psiFile = inputElements[i].element as? PsiFile
|
||||
if (psiFile != null) {
|
||||
progress.setText2(psiFile.getVirtualFile().getPresentableUrl())
|
||||
}
|
||||
@@ -114,13 +120,12 @@ public class JavaToKotlinConverter(private val project: Project,
|
||||
}
|
||||
|
||||
processFilesWithProgress(0.25) { i ->
|
||||
val psiElement = psiElementsAndProcessors[i].first
|
||||
val postProcessor = psiElementsAndProcessors[i].second
|
||||
val psiElement = inputElements[i].element
|
||||
|
||||
fun inConversionScope(element: PsiElement)
|
||||
= psiElementsAndProcessors.any { it.first.isAncestor(element, strict = false) }
|
||||
= inputElements.any { it.element.isAncestor(element, strict = false) }
|
||||
|
||||
val converter = Converter.create(psiElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, postProcessor, usageProcessingCollector)
|
||||
val converter = Converter.create(psiElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, usageProcessingCollector)
|
||||
val result = converter.convert()
|
||||
intermediateResults.add(result)
|
||||
}
|
||||
@@ -135,10 +140,11 @@ public class JavaToKotlinConverter(private val project: Project,
|
||||
val finalResults = ArrayList<String>(elementCount)
|
||||
processFilesWithProgress(0.5) { i ->
|
||||
val result = results[i]
|
||||
val postProcessor = psiElementsAndProcessors[i].second
|
||||
if (postProcessor != null) {
|
||||
try {
|
||||
finalResults.add(AfterConversionPass(project, postProcessor).run(result))
|
||||
val kotlinFile = JetPsiFactory(project).createAnalyzableFile("dummy.kt", result, inputElements[i].postProcessingContext!!)
|
||||
AfterConversionPass(project, postProcessor).run(kotlinFile, null)
|
||||
finalResults.add(kotlinFile.getText())
|
||||
}
|
||||
catch(e: ProcessCanceledException) {
|
||||
throw e
|
||||
@@ -159,7 +165,7 @@ public class JavaToKotlinConverter(private val project: Project,
|
||||
// if we got this exception then we need to turn element creation stack traces on to get better diagnostic
|
||||
Element.saveCreationStacktraces = true
|
||||
try {
|
||||
return elementsToKotlin(psiElementsAndProcessors)
|
||||
return elementsToKotlin(inputElements)
|
||||
}
|
||||
finally {
|
||||
Element.saveCreationStacktraces = false
|
||||
|
||||
@@ -134,8 +134,8 @@ public object JavaToKotlinTranslator {
|
||||
fun generateKotlinCode(javaCode: String): String {
|
||||
val file = createFile(javaCode)
|
||||
if (file is PsiJavaFile) {
|
||||
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter)
|
||||
return prettify(converter.elementsToKotlin(listOf(file to null))[0])
|
||||
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter, null)
|
||||
return prettify(converter.elementsToKotlin(listOf(JavaToKotlinConverter.InputElement(file, null))).single())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -43,8 +43,10 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT
|
||||
psiFiles.add(psiFile)
|
||||
}
|
||||
|
||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter)
|
||||
val results: List<String> = converter.elementsToKotlin(psiFiles.map { it to J2kPostProcessor(it, formatCode = true) })
|
||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings,
|
||||
IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true))
|
||||
val inputElements = psiFiles.map { JavaToKotlinConverter.InputElement(it, it) }
|
||||
val results: List<String> = converter.elementsToKotlin(inputElements)
|
||||
|
||||
fun expectedFile(i: Int) = File(javaFiles[i].getPath().replace(".java", ".kt"))
|
||||
|
||||
|
||||
@@ -106,8 +106,10 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava
|
||||
|
||||
private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
||||
val file = createJavaFile(text)
|
||||
val converter = JavaToKotlinConverter(project, settings, IdeaReferenceSearcher, IdeaResolverForConverter)
|
||||
return converter.elementsToKotlin(listOf(file to J2kPostProcessor(file, formatCode = false)))[0]
|
||||
val converter = JavaToKotlinConverter(project, settings,
|
||||
IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = false))
|
||||
val inputElements = listOf(JavaToKotlinConverter.InputElement(file, file))
|
||||
return converter.elementsToKotlin(inputElements).single()
|
||||
}
|
||||
|
||||
private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
||||
|
||||
Reference in New Issue
Block a user