diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 2222b17c94a..9a988cc614f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -379,9 +379,9 @@ public fun ASTNode.siblings(forward: Boolean = true, withItself: Boolean = true) return if (withItself) stream else stream.drop(1) } -public fun PsiElement.parents(withItself: Boolean = true): Stream { - val stream = stream(this) { if (it is PsiFile) null else it.getParent() } - return if (withItself) stream else stream.drop(1) +public fun PsiElement.parents(withItself: Boolean = true): Sequence { + val sequence = sequence(this) { if (it is PsiFile) null else it.getParent() } + return if (withItself) sequence else sequence.drop(1) } public fun ASTNode.parents(withItself: Boolean = true): Stream { diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt index 7b359f5ddfb..0e52e9be7f7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -36,8 +36,10 @@ import com.intellij.lang.java.JavaLanguage import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter import com.intellij.openapi.project.DumbService +import org.jetbrains.kotlin.psi.psiUtil.parents -public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor() { +public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor() { + private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPastePostProcessor") override fun extractTransferableData(content: Transferable): List { try { @@ -60,118 +62,98 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor, values: List) { if (DumbService.getInstance(project).isDumb()) return - assert(values.size() == 1) + val data = values.single() - val value = values.first() - - if (value !is CopiedCode) return + if (data !is CopiedCode) return val sourceFile = PsiFileFactory.getInstance(project). - createFileFromText(value.fileName, JavaLanguage.INSTANCE, value.fileText) as? PsiJavaFile ?: return + createFileFromText(data.fileName, JavaLanguage.INSTANCE, data.fileText) as? PsiJavaFile ?: return - val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) - if (targetFile !is JetFile) return + val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as? JetFile ?: return val jetEditorOptions = JetEditorOptions.getInstance()!! val needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project)) if (needConvert) { - val text = convertCopiedCodeToKotlin(value, sourceFile, targetFile) - if (text.isNotEmpty()) { + val text = convertCopiedCodeToKotlin(data, sourceFile, data.fileText, targetFile) + if (!text.isEmpty()) { ApplicationManager.getApplication()!!.runWriteAction { val startOffset = bounds.getStartOffset() - editor.getDocument().replaceString(bounds.getStartOffset(), bounds.getEndOffset(), text) + editor.getDocument().replaceString(startOffset, bounds.getEndOffset(), text) + val endOffsetAfterCopy = startOffset + text.length() editor.getCaretModel().moveToOffset(endOffsetAfterCopy) + CodeStyleManager.getInstance(project)!!.reformatText(targetFile, startOffset, endOffsetAfterCopy) - PsiDocumentManager.getInstance(targetFile.getProject()).commitDocument(editor.getDocument()) } } } } - private fun convertCopiedCodeToKotlin(code: CopiedCode, fileCopiedFrom: PsiJavaFile, fileCopiedTo: JetFile): String { + private fun convertCopiedCodeToKotlin(code: CopiedCode, sourceFile: PsiJavaFile, sourceFileText: String, targetFile: JetFile): String { val converter = JavaToKotlinConverter( - fileCopiedFrom.getProject(), + sourceFile.getProject(), ConverterSettings.defaultSettings, - FilesConversionScope(listOf(fileCopiedFrom)), + FilesConversionScope(listOf(sourceFile)), IdeaReferenceSearcher, IdeaResolverForConverter ) - val startOffsets = code.startOffsets - val endOffsets = code.endOffsets - assert(startOffsets.size() == endOffsets.size()) { "Must have the same size" } - val result = StringBuilder() - for (i in startOffsets.indices) { - val startOffset = startOffsets[i] - val endOffset = endOffsets[i] - result.append(convertRangeToKotlin(fileCopiedFrom, fileCopiedTo, TextRange(startOffset, endOffset), converter)) + assert(code.startOffsets.size() == code.endOffsets.size(), "Must have the same size") + val builder = StringBuilder() + for (i in code.startOffsets.indices) { + val textRange = TextRange(code.startOffsets[i], code.endOffsets[i]) + builder.append(convertRangeToKotlin(sourceFile, sourceFileText, targetFile, textRange, converter)) } - return StringUtil.convertLineSeparators(result.toString()) + return StringUtil.convertLineSeparators(builder.toString()) } private fun convertRangeToKotlin(file: PsiJavaFile, - fileCopiedTo: JetFile, + fileText: String, + targetFile: JetFile, range: TextRange, converter: JavaToKotlinConverter): String { - val result = StringBuilder() + val builder = StringBuilder() var currentRange = range - //TODO: probably better to use document to get text by range - val fileText = file.getText()!! while (!currentRange.isEmpty()) { - val leafElement = findFirstLeafElementWhollyInRange(file, currentRange) - if (leafElement == null) { + val leaf = findFirstLeafWhollyInRange(file, currentRange) + if (leaf == null) { val unconvertedSuffix = fileText.substring(currentRange.start, currentRange.end) - result.append(unconvertedSuffix) + builder.append(unconvertedSuffix) break } - val elementToConvert = findTopMostParentWhollyInRange(currentRange, leafElement) - val unconvertedPrefix = fileText.substring(currentRange.start, elementToConvert.range.start) - result.append(unconvertedPrefix) - val converted = converter.elementsToKotlin(listOf(elementToConvert to J2kPostProcessor(fileCopiedTo, formatCode = false)))[0] - if (converted.isNotEmpty()) { - result.append(converted) + + 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) + builder.append(unconvertedPrefix) + + val converted = converter.elementsToKotlin(listOf(elementToConvert to J2kPostProcessor(targetFile, formatCode = false))).single() + if (!converted.isEmpty()) { + builder.append(converted) } else { - result.append(elementToConvert.getText()) + builder.append(fileText.substring(elementToConvertRange.start, elementToConvertRange.end)) } - val endOfConverted = elementToConvert.range.end - currentRange = TextRange(endOfConverted, currentRange.end) + + currentRange = TextRange(elementToConvertRange.end, currentRange.end) } - return result.toString() + return builder.toString() } - private fun findFirstLeafElementWhollyInRange(file: PsiJavaFile, range: TextRange): PsiElement? { - var i = range.start - while (i < range.end) { - val element = file.findElementAt(i) - if (element == null) { - ++i - continue - } - val elemRange = element.range - if (elemRange !in range) { - i = elemRange.end - continue - } - return element + private fun findFirstLeafWhollyInRange(file: PsiJavaFile, range: TextRange): PsiElement? { + var element = file.findElementAt(range.start) ?: return null + var elementRange = element.range + if (elementRange.start < range.start) { + element = file.findElementAt(elementRange.end) ?: return null + elementRange = element.range } - return null - } - - private fun findTopMostParentWhollyInRange(range: TextRange, - base: PsiElement): PsiElement { - assert(base.range in range) { - "Base element out of range. Range: $range, element: ${base.getText()}, element's range: ${base.range}." - } - var elem = base - while (true) { - val parent = elem.getParent() - if (parent == null || parent is PsiJavaFile || parent.range !in range) { - break - } - elem = parent - } - return elem + assert(elementRange.start >= range.start) + return if (elementRange.end <= range.end) element else null } private fun okFromDialog(project: Project): Boolean { @@ -179,8 +161,4 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor