From c5a785577b2727c0ee0a6c6af9509756d720101b Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 9 Apr 2015 12:18:05 +0300 Subject: [PATCH] Imports on paste: code refactoring --- .../copy/ConvertJavaCopyPastePostProcessor.kt | 78 +++------------- .../idea/conversion/copy/DataForConversion.kt | 91 +++++++++++++++++++ 2 files changed, 105 insertions(+), 64 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt 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 200cedeebc0..1364e616483 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -18,8 +18,6 @@ package org.jetbrains.kotlin.idea.conversion.copy import com.intellij.codeInsight.editorActions.CopyPastePostProcessor import com.intellij.codeInsight.editorActions.TextBlockTransferableData -import com.intellij.lang.java.JavaLanguage -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.RangeMarker @@ -27,7 +25,10 @@ import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.util.Ref import com.intellij.openapi.util.TextRange -import com.intellij.psi.* +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiJavaFile import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData import org.jetbrains.kotlin.idea.editor.JetEditorOptions @@ -35,14 +36,9 @@ import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.j2k.* -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.FqNameUnsafe -import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetPsiFactory -import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import java.awt.datatransfer.Transferable -import java.util.ArrayList public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor() { private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPastePostProcessor") @@ -77,9 +73,9 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor> { - val sourceFile = PsiFileFactory.getInstance(project).createFileFromText(JavaLanguage.INSTANCE, data.fileText) as PsiJavaFile - val result = convertCopiedCodeToKotlin(data, sourceFile) - val referenceData = buildReferenceData(result.text, result.parseContext, sourceFile, targetFile) + val dataForConversion = DataForConversion.prepare(data, project) + val result = convertCopiedCodeToKotlin(dataForConversion.elementsAndTexts, project) + val referenceData = buildReferenceData(result.text, result.parseContext, dataForConversion.importsAndPackage, targetFile) return (if (result.textChanged) result.text else null) to referenceData } @@ -137,37 +133,29 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor() - for (i in code.startOffsets.indices) { - list.collectElementsToConvert(sourceFile, sourceFileText, TextRange(code.startOffsets[i], code.endOffsets[i])) - } - + private fun convertCopiedCodeToKotlin(elementsAndTexts: Collection, project: Project): ConversionResult { val converter = JavaToKotlinConverter( - sourceFile.getProject(), + project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter, null ) - val inputElements = list.filterIsInstance().map { JavaToKotlinConverter.InputElement(it, null) } + val inputElements = elementsAndTexts.filterIsInstance().map { JavaToKotlinConverter.InputElement(it, null) } val results = converter.elementsToKotlin(inputElements).results var resultIndex = 0 val convertedCodeBuilder = StringBuilder() val originalCodeBuilder = StringBuilder() var parseContext: ParseContext? = null - for (o in list) { + for (o in elementsAndTexts) { if (o is PsiElement) { val originalText = o.getText() originalCodeBuilder.append(originalText) @@ -194,49 +182,11 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor.collectElementsToConvert( - file: PsiJavaFile, - fileText: String, - range: TextRange - ) { - val elements = file.elementsInRange(range) - if (elements.isEmpty()) { - add(fileText.substring(range.getStartOffset(), range.getEndOffset())) - } - else { - add(fileText.substring(range.getStartOffset(), elements.first().getTextRange().getStartOffset())) - addAll(elements) - add(fileText.substring(elements.last().getTextRange().getEndOffset(), range.getEndOffset())) - } - } - - private fun buildReferenceData(text: String, parseContext: ParseContext, sourceFile: PsiJavaFile, targetFile: JetFile): Collection { + private fun buildReferenceData(text: String, parseContext: ParseContext, importsAndPackage: String, targetFile: JetFile): Collection { var blockStart: Int? = null var blockEnd: Int? = null val fileText = StringBuilder { - val packageName = sourceFile.getPackageName() - if (!packageName.isEmpty()) { - append("package $packageName\n") - } - - val importList = sourceFile.getImportList() - if (importList != null) { - for (import in importList.getImportStatements()) { - val qualifiedName = import.getQualifiedName() ?: continue - if (import.isOnDemand()) { - append("import $qualifiedName.*\n") - } - else { - val fqName = FqNameUnsafe(qualifiedName) - // skip explicit imports of platform classes mapped into Kotlin classes - if (fqName.isSafe() && JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName.toSafe()).isNotEmpty()) continue - append("import $qualifiedName\n") - } - } - //TODO: static imports - } - + append(importsAndPackage) val (contextPrefix, contextSuffix) = when (parseContext) { ParseContext.CODE_BLOCK -> "fun ${generateDummyFunctionName(text)}() {\n" to "\n}" diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt new file mode 100644 index 00000000000..23d328a631b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.conversion.copy + +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiFileFactory +import com.intellij.psi.PsiJavaFile +import org.jetbrains.kotlin.name.FqNameUnsafe +import org.jetbrains.kotlin.platform.JavaToKotlinClassMap +import org.jetbrains.kotlin.psi.psiUtil.elementsInRange +import java.util.ArrayList + +data class DataForConversion private( + val elementsAndTexts: Collection /* list consisting of PsiElement's to convert and plain String's */, + val importsAndPackage: String +) { + companion object { + fun prepare(copiedCode: CopiedJavaCode, project: Project): DataForConversion { + assert(copiedCode.startOffsets.size() == copiedCode.endOffsets.size(), "Must have the same size") + + val sourceFileText = copiedCode.fileText + val sourceFile = PsiFileFactory.getInstance(project).createFileFromText(JavaLanguage.INSTANCE, sourceFileText) as PsiJavaFile + val importsAndPackage = buildImportsAndPackage(sourceFile) + + val elementsAndTexts = ArrayList() + for (i in copiedCode.startOffsets.indices) { + elementsAndTexts.collectElementsToConvert(sourceFile, sourceFileText, TextRange(copiedCode.startOffsets[i], copiedCode.endOffsets[i])) + } + + return DataForConversion(elementsAndTexts, importsAndPackage) + } + + private fun MutableList.collectElementsToConvert( + file: PsiJavaFile, + fileText: String, + range: TextRange + ) { + val elements = file.elementsInRange(range) + if (elements.isEmpty()) { + add(fileText.substring(range.getStartOffset(), range.getEndOffset())) + } + else { + add(fileText.substring(range.getStartOffset(), elements.first().getTextRange().getStartOffset())) + addAll(elements) + add(fileText.substring(elements.last().getTextRange().getEndOffset(), range.getEndOffset())) + } + } + + private fun buildImportsAndPackage(sourceFile: PsiJavaFile): String { + return StringBuilder { + val packageName = sourceFile.getPackageName() + if (!packageName.isEmpty()) { + append("package $packageName\n") + } + + val importList = sourceFile.getImportList() + if (importList != null) { + for (import in importList.getImportStatements()) { + val qualifiedName = import.getQualifiedName() ?: continue + if (import.isOnDemand()) { + append("import $qualifiedName.*\n") + } + else { + val fqName = FqNameUnsafe(qualifiedName) + // skip explicit imports of platform classes mapped into Kotlin classes + if (fqName.isSafe() && JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName.toSafe()).isNotEmpty()) continue + append("import $qualifiedName\n") + } + } + //TODO: static imports + } + }.toString() + } + } +}