Conversion from Java on paste: post processing of code really works
This commit is contained in:
@@ -448,3 +448,34 @@ public fun JetStringTemplateExpression.getContentRange(): TextRange {
|
|||||||
|
|
||||||
public fun JetStringTemplateExpression.isSingleQuoted(): Boolean
|
public fun JetStringTemplateExpression.isSingleQuoted(): Boolean
|
||||||
= getNode().getFirstChildNode().getTextLength() == 1
|
= getNode().getFirstChildNode().getTextLength() == 1
|
||||||
|
|
||||||
|
public fun PsiFile.elementsInRange(range: TextRange): List<PsiElement> {
|
||||||
|
var offset = range.getStartOffset()
|
||||||
|
val result = ArrayList<PsiElement>()
|
||||||
|
while (offset < range.getEndOffset()) {
|
||||||
|
val currentRange = TextRange(offset, range.getEndOffset())
|
||||||
|
val leaf = findFirstLeafWhollyInRange(this, currentRange) ?: break
|
||||||
|
|
||||||
|
val element = leaf
|
||||||
|
.parents(withItself = true)
|
||||||
|
.first {
|
||||||
|
val parent = it.getParent()
|
||||||
|
parent == null || parent.getTextRange() !in currentRange
|
||||||
|
}
|
||||||
|
result.add(element)
|
||||||
|
|
||||||
|
offset = element.getTextRange().getEndOffset()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findFirstLeafWhollyInRange(file: PsiFile, range: TextRange): PsiElement? {
|
||||||
|
var element = file.findElementAt(range.getStartOffset()) ?: return null
|
||||||
|
var elementRange = element.getTextRange()
|
||||||
|
if (elementRange.getStartOffset() < range.getStartOffset()) {
|
||||||
|
element = file.findElementAt(elementRange.getEndOffset()) ?: return null
|
||||||
|
elementRange = element.getTextRange()
|
||||||
|
}
|
||||||
|
assert(elementRange.getStartOffset() >= range.getStartOffset())
|
||||||
|
return if (elementRange.getEndOffset() <= range.getEndOffset()) element else null
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ public class JavaToKotlinAction : AnAction() {
|
|||||||
|
|
||||||
var convertedTexts: List<String>? = null
|
var convertedTexts: List<String>? = null
|
||||||
fun convert() {
|
fun convert() {
|
||||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter)
|
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings,
|
||||||
convertedTexts = converter.elementsToKotlin(javaFiles.map { it to J2kPostProcessor(it, formatCode = true) },
|
IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true))
|
||||||
ProgressManager.getInstance().getProgressIndicator())
|
val inputElements = javaFiles.map { JavaToKotlinConverter.InputElement(it, it) }
|
||||||
|
convertedTexts = converter.elementsToKotlin(inputElements, ProgressManager.getInstance().getProgressIndicator())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||||
|
|||||||
+17
-39
@@ -27,16 +27,17 @@ import com.intellij.openapi.project.DumbService
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Ref
|
import com.intellij.openapi.util.Ref
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
import org.jetbrains.kotlin.idea.editor.JetEditorOptions
|
import org.jetbrains.kotlin.idea.editor.JetEditorOptions
|
||||||
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
|
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
|
||||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||||
|
import org.jetbrains.kotlin.j2k.AfterConversionPass
|
||||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||||
import org.jetbrains.kotlin.j2k.IdeaReferenceSearcher
|
import org.jetbrains.kotlin.j2k.IdeaReferenceSearcher
|
||||||
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
|
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
import java.awt.datatransfer.Transferable
|
import java.awt.datatransfer.Transferable
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
@@ -73,7 +74,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
|
|||||||
fun doConversion(): String? {
|
fun doConversion(): String? {
|
||||||
val sourceFile = PsiFileFactory.getInstance(project).
|
val sourceFile = PsiFileFactory.getInstance(project).
|
||||||
createFileFromText(data.fileName, JavaLanguage.INSTANCE, data.fileText) as PsiJavaFile
|
createFileFromText(data.fileName, JavaLanguage.INSTANCE, data.fileText) as PsiJavaFile
|
||||||
return convertCopiedCodeToKotlin(data, sourceFile, targetFile)
|
return convertCopiedCodeToKotlin(data, sourceFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
var conversionResult: String? = null
|
var conversionResult: String? = null
|
||||||
@@ -96,14 +97,15 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
|
|||||||
val endOffsetAfterCopy = startOffset + conversionResult!!.length()
|
val endOffsetAfterCopy = startOffset + conversionResult!!.length()
|
||||||
editor.getCaretModel().moveToOffset(endOffsetAfterCopy)
|
editor.getCaretModel().moveToOffset(endOffsetAfterCopy)
|
||||||
|
|
||||||
CodeStyleManager.getInstance(project)!!.reformatText(targetFile, startOffset, endOffsetAfterCopy)
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
AfterConversionPass(project, J2kPostProcessor(formatCode = true)).run(targetFile, TextRange(startOffset, endOffsetAfterCopy))
|
||||||
|
|
||||||
conversionPerformed = true
|
conversionPerformed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertCopiedCodeToKotlin(code: CopiedCode, sourceFile: PsiJavaFile, targetFile: JetFile): String? {
|
private fun convertCopiedCodeToKotlin(code: CopiedCode, sourceFile: PsiJavaFile): String? {
|
||||||
assert(code.startOffsets.size() == code.endOffsets.size(), "Must have the same size")
|
assert(code.startOffsets.size() == code.endOffsets.size(), "Must have the same size")
|
||||||
val sourceFileText = code.fileText
|
val sourceFileText = code.fileText
|
||||||
|
|
||||||
@@ -118,10 +120,12 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
|
|||||||
sourceFile.getProject(),
|
sourceFile.getProject(),
|
||||||
ConverterSettings.defaultSettings,
|
ConverterSettings.defaultSettings,
|
||||||
IdeaReferenceSearcher,
|
IdeaReferenceSearcher,
|
||||||
IdeaResolverForConverter
|
IdeaResolverForConverter,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
val results = converter.elementsToKotlin(list.filterIsInstance<PsiElement>().map { it to J2kPostProcessor(targetFile, formatCode = false) })
|
val inputElements = list.filterIsInstance<PsiElement>().map { JavaToKotlinConverter.InputElement(it, null) }
|
||||||
|
val results = converter.elementsToKotlin(inputElements)
|
||||||
|
|
||||||
var resultIndex = 0
|
var resultIndex = 0
|
||||||
val convertedCodeBuilder = StringBuilder()
|
val convertedCodeBuilder = StringBuilder()
|
||||||
@@ -157,41 +161,15 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
|
|||||||
fileText: String,
|
fileText: String,
|
||||||
range: TextRange
|
range: TextRange
|
||||||
) {
|
) {
|
||||||
var currentRange = range
|
val elements = file.elementsInRange(range)
|
||||||
while (!currentRange.isEmpty()) {
|
if (elements.isEmpty()) {
|
||||||
val leaf = findFirstLeafWhollyInRange(file, currentRange)
|
add(fileText.substring(range.getStartOffset(), range.getEndOffset()))
|
||||||
if (leaf == null) {
|
|
||||||
val unconvertedSuffix = fileText.substring(currentRange.start, currentRange.end)
|
|
||||||
add(unconvertedSuffix)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
val elementToConvert = leaf
|
|
||||||
.parents(withItself = true)
|
|
||||||
.first {
|
|
||||||
val parent = it.getParent()
|
|
||||||
parent == null || parent.range !in currentRange
|
|
||||||
}
|
|
||||||
val elementToConvertRange = elementToConvert.range
|
|
||||||
|
|
||||||
val unconvertedPrefix = fileText.substring(currentRange.start, elementToConvertRange.start)
|
|
||||||
add(unconvertedPrefix)
|
|
||||||
|
|
||||||
add(elementToConvert)
|
|
||||||
|
|
||||||
currentRange = TextRange(elementToConvertRange.end, currentRange.end)
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
add(fileText.substring(range.getStartOffset(), elements.first().getTextRange().getStartOffset()))
|
||||||
private fun findFirstLeafWhollyInRange(file: PsiJavaFile, range: TextRange): PsiElement? {
|
addAll(elements)
|
||||||
var element = file.findElementAt(range.start) ?: return null
|
add(fileText.substring(elements.last().getTextRange().getEndOffset(), range.getEndOffset()))
|
||||||
var elementRange = element.range
|
|
||||||
if (elementRange.start < range.start) {
|
|
||||||
element = file.findElementAt(elementRange.end) ?: return null
|
|
||||||
elementRange = element.range
|
|
||||||
}
|
}
|
||||||
assert(elementRange.start >= range.start)
|
|
||||||
return if (elementRange.end <= range.end) element else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun okFromDialog(project: Project): Boolean {
|
private fun okFromDialog(project: Project): Boolean {
|
||||||
|
|||||||
@@ -16,15 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.j2k
|
package org.jetbrains.kotlin.idea.j2k
|
||||||
|
|
||||||
import com.intellij.openapi.editor.impl.DocumentImpl
|
import com.intellij.openapi.editor.RangeMarker
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiComment
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArguments
|
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArguments
|
||||||
import org.jetbrains.kotlin.idea.intentions.SimplifyNegatedBinaryExpressionIntention
|
import org.jetbrains.kotlin.idea.intentions.SimplifyNegatedBinaryExpressionIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention
|
||||||
@@ -33,14 +36,19 @@ import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
|||||||
import org.jetbrains.kotlin.idea.quickfix.RemoveRightPartOfBinaryExpressionFix
|
import org.jetbrains.kotlin.idea.quickfix.RemoveRightPartOfBinaryExpressionFix
|
||||||
import org.jetbrains.kotlin.j2k.PostProcessor
|
import org.jetbrains.kotlin.j2k.PostProcessor
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.Modifier
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, private val formatCode: Boolean) : PostProcessor {
|
public class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
|
||||||
override fun analyzeFile(file: JetFile): BindingContext {
|
override fun analyzeFile(file: JetFile, range: TextRange?): BindingContext {
|
||||||
return file.analyzeFullyAndGetResult().bindingContext
|
val elements = if (range == null) {
|
||||||
|
listOf(file)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
file.elementsInRange(range).filterIsInstance<JetElement>()
|
||||||
|
}
|
||||||
|
return file.getResolutionFacade().analyzeFullyAndGetResult(elements).bindingContext
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fixForProblem(problem: Diagnostic): (() -> Unit)? {
|
override fun fixForProblem(problem: Diagnostic): (() -> Unit)? {
|
||||||
@@ -69,11 +77,35 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doAdditionalProcessing(file: JetFile) {
|
private enum class RangeFilterResult {
|
||||||
|
SKIP
|
||||||
|
GO_INSIDE
|
||||||
|
PROCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doAdditionalProcessing(file: JetFile, rangeMarker: RangeMarker?) {
|
||||||
|
fun rangeFilter(element: PsiElement): RangeFilterResult {
|
||||||
|
if (rangeMarker == null) return RangeFilterResult.PROCESS
|
||||||
|
if (!rangeMarker.isValid()) return RangeFilterResult.SKIP
|
||||||
|
val range = TextRange(rangeMarker.getStartOffset(), rangeMarker.getEndOffset())
|
||||||
|
val elementRange = element.getTextRange()!!
|
||||||
|
return when {
|
||||||
|
range.contains(elementRange) -> RangeFilterResult.PROCESS
|
||||||
|
range.intersects(elementRange) -> RangeFilterResult.GO_INSIDE
|
||||||
|
else -> RangeFilterResult.SKIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val redundantTypeArgs = ArrayList<JetTypeArgumentList>()
|
val redundantTypeArgs = ArrayList<JetTypeArgumentList>()
|
||||||
file.accept(object : JetTreeVisitorVoid(){
|
file.accept(object : JetTreeVisitorVoid(){
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
if (rangeFilter(element) != RangeFilterResult.SKIP) {
|
||||||
|
super.visitElement(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitTypeArgumentList(typeArgumentList: JetTypeArgumentList) {
|
override fun visitTypeArgumentList(typeArgumentList: JetTypeArgumentList) {
|
||||||
if (RemoveExplicitTypeArguments().isApplicableTo(typeArgumentList)) {
|
if (rangeFilter(typeArgumentList) == RangeFilterResult.PROCESS && RemoveExplicitTypeArguments().isApplicableTo(typeArgumentList)) {
|
||||||
redundantTypeArgs.add(typeArgumentList)
|
redundantTypeArgs.add(typeArgumentList)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -85,7 +117,7 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva
|
|||||||
super.visitPrefixExpression(expression)
|
super.visitPrefixExpression(expression)
|
||||||
|
|
||||||
val intention = SimplifyNegatedBinaryExpressionIntention()
|
val intention = SimplifyNegatedBinaryExpressionIntention()
|
||||||
if (intention.isApplicableTo(expression)) {
|
if (rangeFilter(expression) == RangeFilterResult.PROCESS && intention.isApplicableTo(expression)) {
|
||||||
intention.applyTo(expression)
|
intention.applyTo(expression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,19 +125,21 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva
|
|||||||
override fun visitIfExpression(expression: JetIfExpression) {
|
override fun visitIfExpression(expression: JetIfExpression) {
|
||||||
super.visitIfExpression(expression)
|
super.visitIfExpression(expression)
|
||||||
|
|
||||||
run {
|
if (rangeFilter(expression) == RangeFilterResult.PROCESS) {
|
||||||
val intention = IfThenToSafeAccessIntention()
|
run {
|
||||||
if (intention.isApplicableTo(expression)) {
|
val intention = IfThenToSafeAccessIntention()
|
||||||
intention.applyTo(expression)
|
if (intention.isApplicableTo(expression)) {
|
||||||
return
|
intention.applyTo(expression)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
run {
|
||||||
val intention = IfThenToElvisIntention()
|
val intention = IfThenToElvisIntention()
|
||||||
if (intention.isApplicableTo(expression)) {
|
if (intention.isApplicableTo(expression)) {
|
||||||
intention.applyTo(expression)
|
intention.applyTo(expression)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +150,15 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (formatCode) {
|
if (formatCode) {
|
||||||
CodeStyleManager.getInstance(contextToAnalyzeIn.getProject()).reformat(file)
|
val codeStyleManager = CodeStyleManager.getInstance(file.getProject())
|
||||||
|
if (rangeMarker != null) {
|
||||||
|
if (rangeMarker.isValid()) {
|
||||||
|
codeStyleManager.reformatRange(file, rangeMarker.getStartOffset(), rangeMarker.getEndOffset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
codeStyleManager.reformat(file)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
fun foo(o: Any) {
|
||||||
|
if (o is String) {
|
||||||
|
val l = o.length()
|
||||||
|
}
|
||||||
|
somethingElse()
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class C {
|
||||||
|
void foo(Object o) {
|
||||||
|
<selection> if (o instanceof String) {
|
||||||
|
int l = ((String) o).length();
|
||||||
|
}
|
||||||
|
</selection> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(o: Any) {
|
||||||
|
<caret> somethingElse()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(o: Any) {
|
||||||
|
if (o !is String) return
|
||||||
|
val l = o.length()
|
||||||
|
somethingElse(o as String/* we should not remove this cast because it's not in pasted range*/)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
class C {
|
||||||
|
void foo(Object o) {
|
||||||
|
<selection> if (!(o instanceof String)) return
|
||||||
|
int l = ((String) o).length();
|
||||||
|
</selection> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(o: Any) {
|
||||||
|
<caret> somethingElse(o as String/* we should not remove this cast because it's not in pasted range*/)
|
||||||
|
}
|
||||||
+12
@@ -102,6 +102,18 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("RedundantTypeCast.java")
|
||||||
|
public void testRedundantTypeCast() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/RedundantTypeCast.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("RedundantTypeCast2.java")
|
||||||
|
public void testRedundantTypeCast2() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/RedundantTypeCast2.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("SampleBlock.java")
|
@TestMetadata("SampleBlock.java")
|
||||||
public void testSampleBlock() throws Exception {
|
public void testSampleBlock() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/SampleBlock.java");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/SampleBlock.java");
|
||||||
|
|||||||
@@ -16,26 +16,33 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.j2k
|
package org.jetbrains.kotlin.j2k
|
||||||
|
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
|
||||||
import com.intellij.openapi.project.Project
|
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.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetUnaryExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetProperty
|
|
||||||
|
|
||||||
class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) {
|
public class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) {
|
||||||
public fun run(kotlinCode: String): String {
|
public fun run(kotlinFile: JetFile, range: TextRange?) {
|
||||||
//TODO: it's more correct to analyze all converted files together
|
val bindingContext = postProcessor.analyzeFile(kotlinFile, range)
|
||||||
val kotlinFile = JetPsiFactory(project).createAnalyzableFile(
|
|
||||||
"fileForAfterConversionPass.kt", kotlinCode, postProcessor.contextToAnalyzeIn
|
|
||||||
)
|
|
||||||
val bindingContext = postProcessor.analyzeFile(kotlinFile)
|
|
||||||
|
|
||||||
val fixes = bindingContext.getDiagnostics().map {
|
fun fixForProblem(diagnostic: Diagnostic): (() -> Unit)? {
|
||||||
val fix = postProcessor.fixForProblem(it)
|
val psiElement = diagnostic.getPsiElement()
|
||||||
if (fix != null) it.getPsiElement() to fix else null
|
if (range != null && psiElement.getTextRange() !in range) return null
|
||||||
}.filterNotNull()
|
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) {
|
for ((psiElement, fix) in fixes) {
|
||||||
if (psiElement.isValid()) {
|
if (psiElement.isValid()) {
|
||||||
@@ -43,8 +50,6 @@ class AfterConversionPass(val project: Project, val postProcessor: PostProcessor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
postProcessor.doAdditionalProcessing(kotlinFile)
|
postProcessor.doAdditionalProcessing(kotlinFile, rangeMarker)
|
||||||
|
|
||||||
return kotlinFile.getText()!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ class Converter private(
|
|||||||
val inConversionScope: (PsiElement) -> Boolean,
|
val inConversionScope: (PsiElement) -> Boolean,
|
||||||
val referenceSearcher: ReferenceSearcher,
|
val referenceSearcher: ReferenceSearcher,
|
||||||
val resolverForConverter: ResolverForConverter,
|
val resolverForConverter: ResolverForConverter,
|
||||||
private val postProcessor: PostProcessor?,
|
|
||||||
private val commonState: Converter.CommonState,
|
private val commonState: Converter.CommonState,
|
||||||
private val personalState: Converter.PersonalState
|
private val personalState: Converter.PersonalState
|
||||||
) {
|
) {
|
||||||
@@ -60,16 +59,16 @@ class Converter private(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, inConversionScope: (PsiElement) -> Boolean,
|
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, inConversionScope: (PsiElement) -> Boolean,
|
||||||
referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter, postProcessor: PostProcessor?,
|
referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter,
|
||||||
usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
|
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))
|
public fun withSpecialContext(context: PsiElement): Converter = withState(PersonalState(context))
|
||||||
|
|
||||||
private fun withState(state: PersonalState): Converter
|
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)
|
private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,15 @@
|
|||||||
package org.jetbrains.kotlin.j2k
|
package org.jetbrains.kotlin.j2k
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import com.intellij.openapi.editor.RangeMarker
|
||||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
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.progress.ProgressManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiJavaFile
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.j2k.ast.Element
|
import org.jetbrains.kotlin.j2k.ast.Element
|
||||||
@@ -36,8 +37,7 @@ import java.util.ArrayList
|
|||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
|
||||||
public trait PostProcessor {
|
public trait PostProcessor {
|
||||||
public val contextToAnalyzeIn: PsiElement
|
public fun analyzeFile(file: JetFile, range: TextRange?): BindingContext
|
||||||
public fun analyzeFile(file: JetFile): BindingContext
|
|
||||||
|
|
||||||
public open fun fixForProblem(problem: Diagnostic): (() -> Unit)? {
|
public open fun fixForProblem(problem: Diagnostic): (() -> Unit)? {
|
||||||
val psiElement = problem.getPsiElement()
|
val psiElement = problem.getPsiElement()
|
||||||
@@ -51,7 +51,7 @@ public trait PostProcessor {
|
|||||||
Errors.VAL_REASSIGNMENT -> { ->
|
Errors.VAL_REASSIGNMENT -> { ->
|
||||||
val property = (psiElement as? JetSimpleNameExpression)?.getReference()?.resolve() as? JetProperty
|
val property = (psiElement as? JetSimpleNameExpression)?.getReference()?.resolve() as? JetProperty
|
||||||
if (property != null && !property.isVar()) {
|
if (property != null && !property.isVar()) {
|
||||||
val factory = JetPsiFactory(contextToAnalyzeIn.getProject())
|
val factory = JetPsiFactory(psiElement.getProject())
|
||||||
property.getValOrVarNode().getPsi()!!.replace(factory.createVarNode().getPsi()!!)
|
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,
|
public class JavaToKotlinConverter(private val project: Project,
|
||||||
private val settings: ConverterSettings,
|
private val settings: ConverterSettings,
|
||||||
private val referenceSearcher: ReferenceSearcher,
|
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")
|
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.j2k.JavaToKotlinConverter")
|
||||||
|
|
||||||
|
public data class InputElement(
|
||||||
|
val element: PsiElement,
|
||||||
|
val postProcessingContext: PsiElement?
|
||||||
|
)
|
||||||
|
|
||||||
public fun elementsToKotlin(
|
public fun elementsToKotlin(
|
||||||
psiElementsAndProcessors: List<Pair<PsiElement, PostProcessor?>>,
|
inputElements: List<InputElement>,
|
||||||
progress: ProgressIndicator = EmptyProgressIndicator()
|
progress: ProgressIndicator = EmptyProgressIndicator()
|
||||||
): List<String> {
|
): List<String> {
|
||||||
try {
|
try {
|
||||||
val elementCount = psiElementsAndProcessors.size()
|
val elementCount = inputElements.size()
|
||||||
val intermediateResults = ArrayList<((Map<PsiElement, UsageProcessing>) -> String)?>(elementCount)
|
val intermediateResults = ArrayList<((Map<PsiElement, UsageProcessing>) -> String)?>(elementCount)
|
||||||
val usageProcessings = HashMap<PsiElement, UsageProcessing>()
|
val usageProcessings = HashMap<PsiElement, UsageProcessing>()
|
||||||
val usageProcessingCollector: (UsageProcessing) -> Unit = { 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")
|
progress.setText("$progressText ($fileCountText) - pass $pass of 3")
|
||||||
|
|
||||||
val filesCount = psiElementsAndProcessors.indices
|
val filesCount = inputElements.indices
|
||||||
for (i in filesCount) {
|
for (i in filesCount) {
|
||||||
progress.checkCanceled()
|
progress.checkCanceled()
|
||||||
progress.setFraction(fraction + passFraction * i / elementCount)
|
progress.setFraction(fraction + passFraction * i / elementCount)
|
||||||
|
|
||||||
val psiFile = psiElementsAndProcessors[i].first as? PsiFile
|
val psiFile = inputElements[i].element as? PsiFile
|
||||||
if (psiFile != null) {
|
if (psiFile != null) {
|
||||||
progress.setText2(psiFile.getVirtualFile().getPresentableUrl())
|
progress.setText2(psiFile.getVirtualFile().getPresentableUrl())
|
||||||
}
|
}
|
||||||
@@ -114,13 +120,12 @@ public class JavaToKotlinConverter(private val project: Project,
|
|||||||
}
|
}
|
||||||
|
|
||||||
processFilesWithProgress(0.25) { i ->
|
processFilesWithProgress(0.25) { i ->
|
||||||
val psiElement = psiElementsAndProcessors[i].first
|
val psiElement = inputElements[i].element
|
||||||
val postProcessor = psiElementsAndProcessors[i].second
|
|
||||||
|
|
||||||
fun inConversionScope(element: PsiElement)
|
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()
|
val result = converter.convert()
|
||||||
intermediateResults.add(result)
|
intermediateResults.add(result)
|
||||||
}
|
}
|
||||||
@@ -135,10 +140,11 @@ public class JavaToKotlinConverter(private val project: Project,
|
|||||||
val finalResults = ArrayList<String>(elementCount)
|
val finalResults = ArrayList<String>(elementCount)
|
||||||
processFilesWithProgress(0.5) { i ->
|
processFilesWithProgress(0.5) { i ->
|
||||||
val result = results[i]
|
val result = results[i]
|
||||||
val postProcessor = psiElementsAndProcessors[i].second
|
|
||||||
if (postProcessor != null) {
|
if (postProcessor != null) {
|
||||||
try {
|
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) {
|
catch(e: ProcessCanceledException) {
|
||||||
throw e
|
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
|
// if we got this exception then we need to turn element creation stack traces on to get better diagnostic
|
||||||
Element.saveCreationStacktraces = true
|
Element.saveCreationStacktraces = true
|
||||||
try {
|
try {
|
||||||
return elementsToKotlin(psiElementsAndProcessors)
|
return elementsToKotlin(inputElements)
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
Element.saveCreationStacktraces = false
|
Element.saveCreationStacktraces = false
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ public object JavaToKotlinTranslator {
|
|||||||
fun generateKotlinCode(javaCode: String): String {
|
fun generateKotlinCode(javaCode: String): String {
|
||||||
val file = createFile(javaCode)
|
val file = createFile(javaCode)
|
||||||
if (file is PsiJavaFile) {
|
if (file is PsiJavaFile) {
|
||||||
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter)
|
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter, null)
|
||||||
return prettify(converter.elementsToKotlin(listOf(file to null))[0])
|
return prettify(converter.elementsToKotlin(listOf(JavaToKotlinConverter.InputElement(file, null))).single())
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,10 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT
|
|||||||
psiFiles.add(psiFile)
|
psiFiles.add(psiFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter)
|
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings,
|
||||||
val results: List<String> = converter.elementsToKotlin(psiFiles.map { it to J2kPostProcessor(it, formatCode = true) })
|
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"))
|
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 {
|
private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
||||||
val file = createJavaFile(text)
|
val file = createJavaFile(text)
|
||||||
val converter = JavaToKotlinConverter(project, settings, IdeaReferenceSearcher, IdeaResolverForConverter)
|
val converter = JavaToKotlinConverter(project, settings,
|
||||||
return converter.elementsToKotlin(listOf(file to J2kPostProcessor(file, formatCode = false)))[0]
|
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 {
|
private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String {
|
||||||
|
|||||||
Reference in New Issue
Block a user