[kotlin] J2K copy-paste: share most code, create extensions for K1/K2-specific classes

J2K copy-paste processors are enabled in K2 but don't work yet.

Now all J2K-related code (except JavaToKotlinAction)
is located in `j2k` modules.

KTIJ-28714

(cherry picked from commit 71774b698b53420dc95637b720ab3e4c3ec05867)

GitOrigin-RevId: 2e5432817ec7d8d512be25704dd0bb4f57e0c999
This commit is contained in:
Alexey Belkov
2024-11-26 02:05:11 +04:00
committed by intellij-monorepo-bot
parent 9cae14134a
commit d3622f59eb
40 changed files with 221 additions and 74 deletions

View File

@@ -525,14 +525,6 @@ project.view.class.initializer=class initializer
project.view.expression=expression
project.view.class.error.name=no name provided
copy.text.adding.imports=Adding imports\u2026
copy.text.clipboard.content.seems.to.be.java.code.do.you.want.to.convert.it.to.kotlin=Clipboard content seems to be Java code. Do you want to convert it to Kotlin?
copy.text.convert.java.to.kotlin.title=Convert Java to Kotlin
copy.text.copied.kotlin.code=Copied Kotlin code
copy.text.resolving.references=Resolving references\u2026
copy.text.rendering.declaration.stubs=Rendering Declaration Stubs\u2026
copy.title.convert.code.from.java=Convert Code From Java
editor.checkbox.title.auto.add.val.keyword.to.data.value.class.constructor.parameters=Auto add 'val' keyword to data/value class constructor parameters
editor.checkbox.title.convert.pasted.java.code.to.kotlin=Convert pasted Java code to Kotlin
editor.checkbox.title.don.t.show.java.to.kotlin.conversion.dialog.on.paste=Don't show Java to Kotlin conversion dialog on paste

View File

@@ -73,8 +73,6 @@
<orderEntry type="library" name="kotlinc.kotlin-jps-common" level="project" />
<orderEntry type="library" name="kotlinc.kotlin-jps-plugin-classpath" level="project" />
<orderEntry type="module" module-name="kotlin.j2k.shared" />
<orderEntry type="module" module-name="kotlin.j2k.k1.old" />
<orderEntry type="module" module-name="kotlin.j2k.k1.new" />
<orderEntry type="module" module-name="kotlin.formatter" />
<orderEntry type="module" module-name="kotlin.scripting" />
<orderEntry type="module" module-name="kotlin.inspections" />

View File

@@ -2,12 +2,20 @@
package org.jetbrains.kotlin.idea.j2k.post.processing
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RangeMarker
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.copyPaste.K1J2KCopyPasteConverter
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.j2k.J2kConverterExtension.Kind.K1_NEW
import org.jetbrains.kotlin.j2k.copyPaste.DataForConversion
import org.jetbrains.kotlin.j2k.copyPaste.J2KCopyPasteConverter
import org.jetbrains.kotlin.j2k.copyPaste.K1PlainTextPasteImportResolver
import org.jetbrains.kotlin.j2k.copyPaste.PlainTextPasteImportResolver
import org.jetbrains.kotlin.nj2k.*
import org.jetbrains.kotlin.psi.KtFile
@@ -34,4 +42,23 @@ class NewJ2kConverterExtension : J2kConverterExtension() {
override fun getConversions(context: NewJ2kConverterContext): List<Conversion> =
getNewJ2KConversions(context)
override fun createPlainTextPasteImportResolver(
dataForConversion: DataForConversion,
targetKotlinFile: KtFile
): PlainTextPasteImportResolver {
return K1PlainTextPasteImportResolver(dataForConversion, targetKotlinFile)
}
override fun createCopyPasteConverter(
project: Project,
editor: Editor,
dataForConversion: DataForConversion,
j2kKind: Kind,
targetFile: KtFile,
targetBounds: RangeMarker,
targetDocument: Document
): J2KCopyPasteConverter {
return K1J2KCopyPasteConverter(project, editor, dataForConversion, j2kKind, targetFile, targetBounds, targetDocument)
}
}

View File

@@ -40,5 +40,6 @@
<orderEntry type="module" module-name="kotlin.base.plugin" />
<orderEntry type="module" module-name="kotlin.j2k.shared" />
<orderEntry type="module" module-name="kotlin.base.code-insight" />
<orderEntry type="module" module-name="kotlin.idea" />
</component>
</module>

View File

@@ -16,7 +16,8 @@ import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.j2k.ConverterContext
import org.jetbrains.kotlin.j2k.J2kConverterExtension
import org.jetbrains.kotlin.j2k.ParseContext
import org.jetbrains.kotlin.j2k.ParseContext.*
import org.jetbrains.kotlin.j2k.ParseContext.CODE_BLOCK
import org.jetbrains.kotlin.j2k.ParseContext.TOP_LEVEL
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
@@ -25,7 +26,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
* Runs J2K on the pasted code and updates [targetFile] as a side effect.
* Used by [ConvertJavaCopyPasteProcessor].
*/
class J2KCopyPasteConverter(
class K1J2KCopyPasteConverter(
private val project: Project,
private val editor: Editor,
private val dataForConversion: DataForConversion,
@@ -33,7 +34,7 @@ class J2KCopyPasteConverter(
private val targetFile: KtFile,
private val targetBounds: RangeMarker,
private val targetDocument: Document
) {
) : J2KCopyPasteConverter {
/**
* @property changedText The transformed Kotlin code, or `null` if no conversion occurred (the result is the same as original code).
* @property referenceData A list of references within the converted Kotlin code that may need to be processed or resolved.
@@ -48,7 +49,7 @@ class J2KCopyPasteConverter(
private lateinit var result: Result
fun convert() {
override fun convert() {
if (!::result.isInitialized && convertAndRestoreReferencesIfTextIsUnchanged()) return
val (changedText, referenceData, importsToAdd, converterContext) = result
@@ -66,15 +67,7 @@ class J2KCopyPasteConverter(
runPostProcessing(project, targetFile, newBounds, converterContext, j2kKind)
}
/**
* This is a shortcut for copy-pasting trivial code that doesn't need to be converted (for example, a single identifier).
* In this case, we don't bother showing a J2K dialog and only restore references / insert required imports in the Kotlin file.
*
* Always runs the J2K conversion once and saves the result for later reference.
*
* @return `true` if the conversion text remains unchanged; `false` otherwise.
*/
fun convertAndRestoreReferencesIfTextIsUnchanged(): Boolean {
override fun convertAndRestoreReferencesIfTextIsUnchanged(): Boolean {
fun runConversion() {
val conversionResult = dataForConversion.elementsAndTexts.convertCodeToKotlin(project, targetFile, j2kKind)
val (text, parseContext, importsToAdd, isTextChanged, converterContext) = conversionResult
@@ -167,4 +160,4 @@ class J2KCopyPasteConverter(
return rangeMarker.asTextRange
}
}
}

View File

@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.idea.base.projectStructure.RootKindFilter
import org.jetbrains.kotlin.idea.base.projectStructure.matches
import org.jetbrains.kotlin.idea.base.projectStructure.moduleInfoOrNull
import org.jetbrains.kotlin.idea.base.psi.kotlinFqName
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.base.util.K1ModeProjectStructureApi
import org.jetbrains.kotlin.idea.base.util.runReadActionInSmartMode
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
@@ -24,24 +24,17 @@ import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMemberDescriptor
import org.jetbrains.kotlin.idea.core.isVisible
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.nj2k.KotlinNJ2KBundle
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
/**
* This class tries to prepare the plain text Java code for J2K by adding necessary imports so that
* such code can be properly resolved and converted.
*
* 1. For every Kotlin file import statement, try to convert it to a Java PSI import and add it to the Java file.
* 2. For every unresolved short reference in the dummy Java file:
* * Try to find a visible class, method, or field with the same short name
* * If such a declaration is found, add an import for it (usually to both the Java and Kotlin files).
* Note: imports for Java are added at once, but for Kotlin they are returned as a list, to be converted by J2K later.
*
* Tests: [org.jetbrains.kotlin.nj2k.TextNewJavaToKotlinCopyPasteConversionTestGenerated].
*/
class PlainTextPasteImportResolver(private val dataForConversion: DataForConversion, private val targetKotlinFile: KtFile) {
class K1PlainTextPasteImportResolver(private val dataForConversion: DataForConversion, private val targetKotlinFile: KtFile) :
PlainTextPasteImportResolver {
private val sourceJavaFile: PsiJavaFile = dataForConversion.sourceJavaFile
private val javaFileImportList: PsiImportList = sourceJavaFile.importList!!
private val project = targetKotlinFile.project
@@ -55,7 +48,7 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
private val failedToResolveReferenceNames: MutableSet<String> = mutableSetOf()
private val importsToAddToKotlinFile: MutableList<PsiImportStatementBase> = mutableListOf()
fun generateRequiredImports(): List<PsiImportStatementBase> {
override fun generateRequiredImports(): List<PsiImportStatementBase> {
addImportsToJavaFileFromKotlinFile()
tryToResolveShortReferencesByAddingImports()
return importsToAddToKotlinFile
@@ -67,14 +60,14 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
if (javaFileImportList in dataForConversion.elementsAndTexts.toList()) return
ProgressManager.getInstance().runProcessWithProgressSynchronously(
addImportsTask, KotlinBundle.message("copy.text.adding.imports"), /* canBeCanceled = */ true, project
addImportsTask, KotlinNJ2KBundle.message("copy.text.adding.imports"), /* canBeCanceled = */ true, project
)
}
private fun tryToResolveShortReferencesByAddingImports() {
ProgressManager.checkCanceled()
ProgressManager.getInstance().runProcessWithProgressSynchronously(
resolveReferencesTask, KotlinBundle.message("copy.text.resolving.references"), /* canBeCanceled = */ true, project
resolveReferencesTask, KotlinNJ2KBundle.message("copy.text.resolving.references"), /* canBeCanceled = */ true, project
)
}
@@ -236,6 +229,7 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
}
}
@OptIn(K1ModeProjectStructureApi::class)
private fun findUniqueMemberByShortName(name: String): PsiMember? {
return runReadAction {
val candidateMembers: List<PsiMember> =

View File

@@ -3,7 +3,7 @@
package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.idea.base.test.KotlinRoot
import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinCopyPasteConversionTest
import java.io.File
abstract class AbstractK1JavaToKotlinCopyPasteConversionTest : AbstractJavaToKotlinCopyPasteConversionTest() {

View File

@@ -2,7 +2,7 @@
package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.idea.conversion.copy.AbstractTextJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.j2k.AbstractTextJavaToKotlinCopyPasteConversionTest
abstract class AbstractK1TextJavaToKotlinCopyPasteConversionTest : AbstractTextJavaToKotlinCopyPasteConversionTest() {
}

View File

@@ -30,6 +30,7 @@
<orderEntry type="library" name="kotlinc.analysis-api" level="project" />
<orderEntry type="module" module-name="kotlin.base.util" />
<orderEntry type="module" module-name="kotlin.core" />
<orderEntry type="module" module-name="kotlin.idea" />
<orderEntry type="module" module-name="kotlin.fir.frontend-independent" />
<orderEntry type="module" module-name="intellij.platform.lang" />
<orderEntry type="module" module-name="intellij.java.psi.impl" />
@@ -38,5 +39,6 @@
<orderEntry type="module" module-name="intellij.java.analysis.impl" />
<orderEntry type="module" module-name="kotlin.base.plugin" />
<orderEntry type="module" module-name="kotlin.base.fe10.code-insight" />
<orderEntry type="module" module-name="kotlin.j2k.k1.new" />
</component>
</module>

View File

@@ -2,11 +2,19 @@
package org.jetbrains.kotlin.j2k
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RangeMarker
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.copyPaste.K1J2KCopyPasteConverter
import org.jetbrains.kotlin.j2k.J2kConverterExtension.Kind.K1_OLD
import org.jetbrains.kotlin.j2k.copyPaste.DataForConversion
import org.jetbrains.kotlin.j2k.copyPaste.J2KCopyPasteConverter
import org.jetbrains.kotlin.j2k.copyPaste.K1PlainTextPasteImportResolver
import org.jetbrains.kotlin.j2k.copyPaste.PlainTextPasteImportResolver
import org.jetbrains.kotlin.psi.KtFile
class OldJ2kConverterExtension : J2kConverterExtension() {
@@ -32,4 +40,23 @@ class OldJ2kConverterExtension : J2kConverterExtension() {
override fun doCheckBeforeConversion(project: Project, module: Module): Boolean =
true
override fun createPlainTextPasteImportResolver(
dataForConversion: DataForConversion,
targetKotlinFile: KtFile
): PlainTextPasteImportResolver {
return K1PlainTextPasteImportResolver(dataForConversion, targetKotlinFile)
}
override fun createCopyPasteConverter(
project: Project,
editor: Editor,
dataForConversion: DataForConversion,
j2kKind: Kind,
targetFile: KtFile,
targetBounds: RangeMarker,
targetDocument: Document
): J2KCopyPasteConverter {
return K1J2KCopyPasteConverter(project, editor, dataForConversion, j2kKind, targetFile, targetBounds, targetDocument)
}
}

View File

@@ -2,12 +2,18 @@
package org.jetbrains.kotlin.j2k.k2
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RangeMarker
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.*
import org.jetbrains.kotlin.j2k.J2kConverterExtension.Kind.K2
import org.jetbrains.kotlin.j2k.copyPaste.DataForConversion
import org.jetbrains.kotlin.j2k.copyPaste.J2KCopyPasteConverter
import org.jetbrains.kotlin.j2k.copyPaste.PlainTextPasteImportResolver
import org.jetbrains.kotlin.nj2k.Conversion
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.NewJ2kWithProgressProcessor
@@ -40,4 +46,23 @@ class K2J2KConverterExtension : J2kConverterExtension() {
override fun getConversions(context: NewJ2kConverterContext): List<Conversion> =
getK2J2KConversions(context)
override fun createPlainTextPasteImportResolver(
dataForConversion: DataForConversion,
targetKotlinFile: KtFile
): PlainTextPasteImportResolver {
TODO("Not yet implemented")
}
override fun createCopyPasteConverter(
project: Project,
editor: Editor,
dataForConversion: DataForConversion,
j2kKind: Kind,
targetFile: KtFile,
targetBounds: RangeMarker,
targetDocument: Document
): J2KCopyPasteConverter {
TODO("Not yet implemented")
}
}

View File

@@ -44,5 +44,7 @@
<orderEntry type="module" module-name="intellij.platform.core" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="module" module-name="kotlin.project-configuration" />
<orderEntry type="module" module-name="kotlin.preferences" />
<orderEntry type="module" module-name="kotlin.base.statistics" />
</component>
</module>

View File

@@ -16,4 +16,12 @@ converter.kotlin.not.configured.no.configurators.available=There are no configur
converter.kotlin.not.configured.configure=OK, Configure Kotlin In the Project
converter.kotlin.not.configured.cancel.conversion=No, Cancel Conversion
converter.kotlin.not.configured.choose.configurator=Choose configurator
converter.kotlin.wait.for.sync.to.be.finished=Waiting for sync to be finished
converter.kotlin.wait.for.sync.to.be.finished=Waiting for sync to be finished
copy.text.convert.java.to.kotlin.title=Convert Java to Kotlin
copy.title.convert.code.from.java=Convert Code From Java
copy.text.clipboard.content.seems.to.be.java.code.do.you.want.to.convert.it.to.kotlin=Clipboard content seems to be Java code. Do you want to convert it to Kotlin?
copy.text.copied.kotlin.code=Copied Kotlin code
copy.text.rendering.declaration.stubs=Rendering Declaration Stubs\u2026
copy.text.adding.imports=Adding Imports\u2026
copy.text.resolving.references=Resolving References\u2026

View File

@@ -8,18 +8,24 @@ import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.ConverterSettings.Companion.defaultSettings
import org.jetbrains.kotlin.j2k.OldJavaToKotlinConverter
import org.jetbrains.kotlin.j2k.J2kConverterExtension
import org.jetbrains.kotlin.j2k.J2kConverterExtension.Kind.K1_OLD
import org.jetbrains.kotlin.j2k.Result
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtPsiFactory
// Old J2K utilities used from "kotlin.idea" module in K1.
// They are located in "kotlin.j2k.shared" module to avoid circular dependencies.
fun PsiElement.j2kText(settings: ConverterSettings = defaultSettings): String? =
convertToKotlin(settings)?.results?.single()?.text //TODO: insert imports
fun PsiElement.convertToKotlin(settings: ConverterSettings = defaultSettings): Result? {
if (language != JavaLanguage.INSTANCE) return null
val j2kConverter = OldJavaToKotlinConverter(project, settings)
val extension = J2kConverterExtension.extension(kind = K1_OLD)
extension.createJavaToKotlinConverter(project, targetModule = null, settings)
val j2kConverter = extension.createJavaToKotlinConverter(project, targetModule = null, settings)
return j2kConverter.elementsToKotlin(listOf(this))
}

View File

@@ -2,11 +2,17 @@
package org.jetbrains.kotlin.j2k
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RangeMarker
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.psi.PsiJavaFile
import org.jetbrains.kotlin.j2k.copyPaste.DataForConversion
import org.jetbrains.kotlin.j2k.copyPaste.J2KCopyPasteConverter
import org.jetbrains.kotlin.j2k.copyPaste.PlainTextPasteImportResolver
import org.jetbrains.kotlin.nj2k.Conversion
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.psi.KtFile
@@ -44,6 +50,21 @@ abstract class J2kConverterExtension {
open fun getConversions(context: NewJ2kConverterContext): List<Conversion> =
emptyList()
abstract fun createPlainTextPasteImportResolver(
dataForConversion: DataForConversion,
targetKotlinFile: KtFile
): PlainTextPasteImportResolver
abstract fun createCopyPasteConverter(
project: Project,
editor: Editor,
dataForConversion: DataForConversion,
j2kKind: Kind,
targetFile: KtFile,
targetBounds: RangeMarker,
targetDocument: Document
): J2KCopyPasteConverter
companion object {
val EP_NAME = ExtensionPointName<J2kConverterExtension>("org.jetbrains.kotlin.j2kConverterExtension")

View File

@@ -16,6 +16,7 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
import org.jetbrains.kotlin.idea.statistics.ConversionType
import org.jetbrains.kotlin.idea.statistics.J2KFusCollector
import org.jetbrains.kotlin.j2k.J2kConverterExtension
import org.jetbrains.kotlin.j2k.J2kConverterExtension.Kind.K1_NEW
import java.awt.datatransfer.Transferable
import kotlin.system.measureTimeMillis
@@ -70,7 +71,9 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
val copiedJavaCode = values.single() as CopiedJavaCode
val dataForConversion = DataForConversion.prepare(copiedJavaCode, project)
val j2kKind = getJ2kKind(targetFile)
val converter = J2KCopyPasteConverter(project, editor, dataForConversion, j2kKind, targetFile, targetBounds, targetDocument)
val converter = J2kConverterExtension.extension(j2kKind)
.createCopyPasteConverter(project, editor, dataForConversion, j2kKind, targetFile, targetBounds, targetDocument)
val textLength = copiedJavaCode.startOffsets.indices.sumOf { copiedJavaCode.endOffsets[it] - copiedJavaCode.startOffsets[it] }
if (textLength < MAX_TEXT_LENGTH_TO_CONVERT_WITHOUT_ASKING_USER && converter.convertAndRestoreReferencesIfTextIsUnchanged()) {
@@ -97,4 +100,4 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
@get:TestOnly
var conversionPerformed: Boolean = false
}
}
}

View File

@@ -3,8 +3,7 @@
package org.jetbrains.kotlin.j2k.copyPaste
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.nj2k.KotlinNJ2KBundle
import java.awt.datatransfer.DataFlavor
/**
@@ -15,6 +14,6 @@ class CopiedKotlinCode : TextBlockTransferableData {
override fun getFlavor(): DataFlavor = DATA_FLAVOR
companion object {
val DATA_FLAVOR: DataFlavor = DataFlavor(CopiedKotlinCode::class.java, KotlinBundle.message("copy.text.copied.kotlin.code"))
val DATA_FLAVOR: DataFlavor = DataFlavor(CopiedKotlinCode::class.java, KotlinNJ2KBundle.message("copy.text.copied.kotlin.code"))
}
}

View File

@@ -10,8 +10,6 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.base.util.module
import org.jetbrains.kotlin.idea.configuration.ExperimentalFeatures.NewJ2k
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
@@ -21,6 +19,7 @@ import org.jetbrains.kotlin.j2k.J2kConverterExtension.Kind.K1_OLD
import org.jetbrains.kotlin.j2k.ParseContext.CODE_BLOCK
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.KotlinNJ2KBundle
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtStringTemplateEntryWithExpression
@@ -53,7 +52,7 @@ fun ElementAndTextList.convertCodeToKotlin(
ThrowableComputable {
runReadAction { converter.elementsToKotlin(inputElements) }
},
JavaToKotlinAction.Handler.title,
KotlinNJ2KBundle.message("copy.text.convert.java.to.kotlin.title"),
true,
project
)
@@ -162,11 +161,11 @@ fun runPostProcessing(
}
ProgressManager.getInstance().runProcessWithProgressSynchronously(
runnable,
KotlinBundle.message("copy.text.convert.java.to.kotlin.title"),
KotlinNJ2KBundle.message("copy.text.convert.java.to.kotlin.title"),
/* canBeCanceled = */ true,
project
)
} else {
J2KPostProcessingRunner.run(postProcessor, file, converterContext, bounds)
}
}
}

View File

@@ -128,4 +128,4 @@ private fun extractSignificantImportsAndPackage(sourceFile: PsiJavaFile): String
}
//TODO: static imports
}
}
}

View File

@@ -45,4 +45,4 @@ class ElementAndTextList() {
interface ElementsAndTextsProcessor {
fun processElement(element: PsiElement)
fun processText(text: String)
}
}

View File

@@ -0,0 +1,20 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.j2k.copyPaste
/**
* Runs J2K on the pasted code and updates the target Kotlin file as a side effect.
* Used by [ConvertJavaCopyPasteProcessor].
*/
interface J2KCopyPasteConverter {
fun convert()
/**
* This is a shortcut for copy-pasting trivial code that doesn't need to be converted (for example, a single identifier).
* In this case, we don't bother showing a J2K dialog and only restore references / insert required imports in the Kotlin file.
*
* Always runs the J2K conversion once and saves the result for later reference.
*
* @return `true` if the conversion text remains unchanged; `false` otherwise.
*/
fun convertAndRestoreReferencesIfTextIsUnchanged(): Boolean
}

View File

@@ -18,7 +18,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
* Runs J2K on the pasted code and updates [targetFile] as a side effect.
* Used by [ConvertTextJavaCopyPasteProcessor].
*/
class J2KTextCopyPasteConverter(
internal class J2KTextCopyPasteConverter(
private val project: Project,
private val editor: Editor,
private val dataForConversion: DataForConversion,
@@ -67,11 +67,12 @@ class J2KTextCopyPasteConverter(
}
private fun tryToResolveImports(dataForConversion: DataForConversion, targetFile: KtFile): ElementAndTextList {
val imports = PlainTextPasteImportResolver(dataForConversion, targetFile).generateRequiredImports()
val resolver = J2kConverterExtension.extension(j2kKind).createPlainTextPasteImportResolver(dataForConversion, targetFile)
val imports = resolver.generateRequiredImports()
val newlineSeparatedImports = imports.flatMap { importStatement ->
listOf("\n", importStatement)
} + "\n\n"
return ElementAndTextList(newlineSeparatedImports)
}
}
}

View File

@@ -16,15 +16,15 @@ import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.analysis.api.types.symbol
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.utils.getFqNameIfPackageOrNonLocal
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.nj2k.KotlinNJ2KBundle
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.blockExpressionsOrSingle
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
internal data class JavaContextDeclarationStubs(
data class JavaContextDeclarationStubs(
val localDeclarations: String,
val memberDeclarations: String
)
@@ -37,7 +37,7 @@ internal data class JavaContextDeclarationStubs(
* In plain text conversion, we don't have an original Java `PsiFile` from which to draw context.
* So, we do the next best thing: take an approximation of the context from the target Kotlin file.
*/
internal object JavaContextDeclarationRenderer {
object JavaContextDeclarationRenderer {
fun render(contextElement: KtElement): JavaContextDeclarationStubs {
val task: () -> JavaContextDeclarationStubs = {
runReadAction {
@@ -52,7 +52,7 @@ internal object JavaContextDeclarationRenderer {
}
return ProgressManager.getInstance().runProcessWithProgressSynchronously<JavaContextDeclarationStubs, Exception>(
task, KotlinBundle.message("copy.text.rendering.declaration.stubs"), /* canBeCanceled = */ true, contextElement.project
task, KotlinNJ2KBundle.message("copy.text.rendering.declaration.stubs"), /* canBeCanceled = */ true, contextElement.project
)
}
@@ -165,4 +165,4 @@ private class Renderer {
private fun append(s: String) {
builder.append(s)
}
}
}

View File

@@ -6,8 +6,8 @@ import com.intellij.CommonBundle;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle;
import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions;
import org.jetbrains.kotlin.nj2k.KotlinNJ2KBundle;
import javax.swing.*;
import java.awt.*;
@@ -23,10 +23,10 @@ public class KotlinPasteFromJavaDialog extends DialogWrapper {
super(project, true);
setModal(true);
getRootPane().setDefaultButton(buttonOK);
setTitle(KotlinBundle.message("copy.title.convert.code.from.java"));
setTitle(KotlinNJ2KBundle.message("copy.title.convert.code.from.java"));
if (isPlainText) {
questionLabel.setText(
KotlinBundle.message("copy.text.clipboard.content.seems.to.be.java.code.do.you.want.to.convert.it.to.kotlin"));
KotlinNJ2KBundle.message("copy.text.clipboard.content.seems.to.be.java.code.do.you.want.to.convert.it.to.kotlin"));
//TODO: should we also use different set of settings?
}
init();

View File

@@ -0,0 +1,18 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.j2k.copyPaste
import com.intellij.psi.PsiImportStatementBase
/**
* This class tries to prepare the plain text Java code for J2K by adding necessary imports so that
* such code can be properly resolved and converted.
*
* 1. For every Kotlin file import statement, try to convert it to a Java PSI import and add it to the Java file.
* 2. For every unresolved short reference in the dummy Java file:
* * Try to find a visible class, method, or field with the same short name
* * If such a declaration is found, add an import for it (usually to both the Java and Kotlin files).
* Note: imports for Java are added at once, but for Kotlin they are returned as a list, to be converted by J2K later.
*/
interface PlainTextPasteImportResolver {
fun generateRequiredImports(): List<PsiImportStatementBase>
}

View File

@@ -0,0 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@ApiStatus.Internal
package org.jetbrains.kotlin.j2k.copyPaste;
import org.jetbrains.annotations.ApiStatus;

View File

@@ -27,6 +27,7 @@
<orderEntry type="library" name="kotlinc.analysis-api" level="project" />
<orderEntry type="library" name="kotlinc.kotlin-compiler-common" level="project" />
<orderEntry type="library" scope="TEST" name="kotlinc.kotlin-compiler-tests" level="project" />
<orderEntry type="library" scope="TEST" name="kotlin-test" level="project" />
<orderEntry type="module" module-name="intellij.java.psi" scope="TEST" />
<orderEntry type="module" module-name="kotlin.fir.frontend-independent" scope="TEST" />
<orderEntry type="module" module-name="kotlin.j2k.shared" scope="TEST" />
@@ -36,5 +37,7 @@
<orderEntry type="module" module-name="kotlin.base.plugin" scope="TEST" />
<orderEntry type="module" module-name="kotlin.base.util" scope="TEST" />
<orderEntry type="module" module-name="kotlin.code-insight.utils" scope="TEST" />
<orderEntry type="module" module-name="kotlin.idea.tests" scope="TEST" />
<orderEntry type="module" module-name="kotlin.base.code-insight.minimal" scope="TEST" />
</component>
</module>

View File

@@ -1,6 +1,6 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.conversion.copy
package org.jetbrains.kotlin.j2k
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.util.ThrowableRunnable

View File

@@ -1,6 +1,6 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.conversion.copy
package org.jetbrains.kotlin.j2k
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.ide.CopyPasteManager

View File

@@ -50,5 +50,7 @@
<orderEntry type="library" scope="TEST" name="jackson" level="project" />
<orderEntry type="library" scope="TEST" name="jackson-module-kotlin" level="project" />
<orderEntry type="library" scope="TEST" name="okhttp" level="project" />
<orderEntry type="module" module-name="kotlin.j2k.shared.tests" scope="TEST" />
<orderEntry type="module" module-name="kotlin.j2k.shared" scope="TEST" />
</component>
</module>

View File

@@ -10,7 +10,7 @@ import com.intellij.testFramework.RunAll
import com.intellij.util.ThrowableRunnable
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.configuration.ExperimentalFeatures
import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.j2k.copyPaste.ConvertJavaCopyPasteProcessor
import org.jetbrains.kotlin.idea.testFramework.Stats
import org.jetbrains.kotlin.idea.testFramework.Stats.Companion.WARM_UP

View File

@@ -574,6 +574,8 @@
<!-- Copy-paste -->
<extensions defaultExtensionNs="com.intellij">
<filePasteProvider implementation="org.jetbrains.kotlin.idea.codeInsight.copyPaste.KotlinFilePasteProvider" order="first"/>
<copyPastePostProcessor implementation="org.jetbrains.kotlin.j2k.copyPaste.ConvertJavaCopyPasteProcessor"/>
<copyPastePostProcessor implementation="org.jetbrains.kotlin.j2k.copyPaste.ConvertTextJavaCopyPasteProcessor"/>
</extensions>
<actions>

View File

@@ -275,8 +275,6 @@
<!-- Copy-paste -->
<extensions defaultExtensionNs="com.intellij">
<copyPastePostProcessor implementation="org.jetbrains.kotlin.j2k.copyPaste.ConvertJavaCopyPasteProcessor"/>
<copyPastePostProcessor implementation="org.jetbrains.kotlin.j2k.copyPaste.ConvertTextJavaCopyPasteProcessor"/>
<copyPastePostProcessor implementation="org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor"/>
<copyPastePostProcessor implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsCopyPasteProcessor"/>