J2K, minor: clean copy-paste conversion code
This commit is contained in:
+63
-44
@@ -54,6 +54,7 @@ import java.util.*
|
|||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
|
class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
|
||||||
|
@Suppress("PrivatePropertyName")
|
||||||
private val LOG = Logger.getInstance(ConvertJavaCopyPasteProcessor::class.java)
|
private val LOG = Logger.getInstance(ConvertJavaCopyPasteProcessor::class.java)
|
||||||
|
|
||||||
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
|
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
|
||||||
@@ -61,20 +62,30 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
|||||||
if (content.isDataFlavorSupported(CopiedJavaCode.DATA_FLAVOR)) {
|
if (content.isDataFlavorSupported(CopiedJavaCode.DATA_FLAVOR)) {
|
||||||
return listOf(content.getTransferData(CopiedJavaCode.DATA_FLAVOR) as TextBlockTransferableData)
|
return listOf(content.getTransferData(CopiedJavaCode.DATA_FLAVOR) as TextBlockTransferableData)
|
||||||
}
|
}
|
||||||
}
|
} catch (e: Throwable) {
|
||||||
catch (e: Throwable) {
|
|
||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
}
|
}
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun collectTransferableData(file: PsiFile, editor: Editor, startOffsets: IntArray, endOffsets: IntArray): List<TextBlockTransferableData> {
|
override fun collectTransferableData(
|
||||||
|
file: PsiFile,
|
||||||
|
editor: Editor,
|
||||||
|
startOffsets: IntArray,
|
||||||
|
endOffsets: IntArray
|
||||||
|
): List<TextBlockTransferableData> {
|
||||||
if (file !is PsiJavaFile) return listOf()
|
if (file !is PsiJavaFile) return listOf()
|
||||||
|
|
||||||
return listOf(CopiedJavaCode(file.getText()!!, startOffsets, endOffsets))
|
return listOf(CopiedJavaCode(file.getText()!!, startOffsets, endOffsets))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processTransferableData(project: Project, editor: Editor, bounds: RangeMarker, caretOffset: Int, indented: Ref<Boolean>, values: List<TextBlockTransferableData>) {
|
override fun processTransferableData(
|
||||||
|
project: Project,
|
||||||
|
editor: Editor,
|
||||||
|
bounds: RangeMarker,
|
||||||
|
caretOffset: Int,
|
||||||
|
indented: Ref<Boolean>,
|
||||||
|
values: List<TextBlockTransferableData>
|
||||||
|
) {
|
||||||
if (DumbService.getInstance(project).isDumb) return
|
if (DumbService.getInstance(project).isDumb) return
|
||||||
if (!KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion) return
|
if (!KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion) return
|
||||||
|
|
||||||
@@ -104,7 +115,11 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
|||||||
return Result(text, referenceData, result.importsToAdd, result.converterContext)
|
return Result(text, referenceData, result.importsToAdd, result.converterContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun insertImports(bounds: TextRange, referenceData: Collection<KotlinReferenceData>, explicitImports: Collection<FqName>): TextRange? {
|
fun insertImports(
|
||||||
|
bounds: TextRange,
|
||||||
|
referenceData: Collection<KotlinReferenceData>,
|
||||||
|
explicitImports: Collection<FqName>
|
||||||
|
): TextRange? {
|
||||||
if (referenceData.isEmpty() && explicitImports.isEmpty()) return bounds
|
if (referenceData.isEmpty() && explicitImports.isEmpty()) return bounds
|
||||||
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
@@ -133,31 +148,31 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
|||||||
|
|
||||||
if (conversionResult!!.text != null) return false
|
if (conversionResult!!.text != null) return false
|
||||||
|
|
||||||
insertImports(bounds.range ?: return true, conversionResult!!.referenceData, conversionResult!!.explicitImports)
|
insertImports(
|
||||||
|
bounds.range ?: return true,
|
||||||
|
conversionResult!!.referenceData,
|
||||||
|
conversionResult!!.explicitImports
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
val textLength = data.startOffsets.indices.sumBy { data.endOffsets[it] - data.startOffsets[it] }
|
val textLength = data.startOffsets.indices.sumBy { data.endOffsets[it] - data.startOffsets[it] }
|
||||||
if (textLength < 1000) { // if the text to convert is short enough, try to do conversion without permission from user and skip the dialog if nothing converted
|
// if the text to convert is short enough, try to do conversion without permission from user and skip the dialog if nothing converted
|
||||||
if (doConversionAndInsertImportsIfUnchanged()) return
|
if (textLength < 1000 && doConversionAndInsertImportsIfUnchanged()) return
|
||||||
}
|
|
||||||
|
|
||||||
fun convert() {
|
fun convert() {
|
||||||
if (conversionResult == null) {
|
if (conversionResult == null && doConversionAndInsertImportsIfUnchanged()) return
|
||||||
if (doConversionAndInsertImportsIfUnchanged()) return
|
|
||||||
}
|
|
||||||
val (text, referenceData, explicitImports) = conversionResult!!
|
val (text, referenceData, explicitImports) = conversionResult!!
|
||||||
text!! // otherwise we should get true from doConversionAndInsertImportsIfUnchanged and return above
|
text!! // otherwise we should get true from doConversionAndInsertImportsIfUnchanged and return above
|
||||||
|
|
||||||
val boundsAfterReplace =
|
val boundsAfterReplace = runWriteAction {
|
||||||
runWriteAction {
|
val startOffset = bounds.startOffset
|
||||||
val startOffset = bounds.startOffset
|
document.replaceString(startOffset, bounds.endOffset, text)
|
||||||
document.replaceString(startOffset, bounds.endOffset, text)
|
|
||||||
|
|
||||||
val endOffsetAfterCopy = startOffset + text.length
|
val endOffsetAfterCopy = startOffset + text.length
|
||||||
editor.caretModel.moveToOffset(endOffsetAfterCopy)
|
editor.caretModel.moveToOffset(endOffsetAfterCopy)
|
||||||
TextRange(startOffset, endOffsetAfterCopy)
|
TextRange(startOffset, endOffsetAfterCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
val newBounds = insertImports(boundsAfterReplace, referenceData, explicitImports)
|
val newBounds = insertImports(boundsAfterReplace, referenceData, explicitImports)
|
||||||
|
|
||||||
@@ -181,7 +196,12 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildReferenceData(text: String, parseContext: ParseContext, importsAndPackage: String, targetFile: KtFile): Collection<KotlinReferenceData> {
|
private fun buildReferenceData(
|
||||||
|
text: String,
|
||||||
|
parseContext: ParseContext,
|
||||||
|
importsAndPackage: String,
|
||||||
|
targetFile: KtFile
|
||||||
|
): Collection<KotlinReferenceData> {
|
||||||
var blockStart: Int? = null
|
var blockStart: Int? = null
|
||||||
var blockEnd: Int? = null
|
var blockEnd: Int? = null
|
||||||
val fileText = buildString {
|
val fileText = buildString {
|
||||||
@@ -202,7 +222,6 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
|||||||
}
|
}
|
||||||
|
|
||||||
val dummyFile = KtPsiFactory(targetFile.project).createAnalyzableFile("dummy.kt", fileText, targetFile)
|
val dummyFile = KtPsiFactory(targetFile.project).createAnalyzableFile("dummy.kt", fileText, targetFile)
|
||||||
|
|
||||||
return KotlinCopyPasteReferenceProcessor().collectReferenceData(dummyFile, intArrayOf(blockStart!!), intArrayOf(blockEnd!!))
|
return KotlinCopyPasteReferenceProcessor().collectReferenceData(dummyFile, intArrayOf(blockStart!!), intArrayOf(blockEnd!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,16 +235,17 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferab
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@get:TestOnly var conversionPerformed: Boolean = false
|
@get:TestOnly
|
||||||
|
var conversionPerformed: Boolean = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ConversionResult(
|
internal class ConversionResult(
|
||||||
val text: String,
|
val text: String,
|
||||||
val parseContext: ParseContext,
|
val parseContext: ParseContext,
|
||||||
val importsToAdd: Set<FqName>,
|
val importsToAdd: Set<FqName>,
|
||||||
val textChanged: Boolean,
|
val textChanged: Boolean,
|
||||||
val converterContext: ConverterContext?
|
val converterContext: ConverterContext?
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun ElementAndTextList.convertCodeToKotlin(project: Project, targetModule: Module?, useNewJ2k: Boolean): ConversionResult {
|
internal fun ElementAndTextList.convertCodeToKotlin(project: Project, targetModule: Module?, useNewJ2k: Boolean): ConversionResult {
|
||||||
@@ -237,16 +257,16 @@ internal fun ElementAndTextList.convertCodeToKotlin(project: Project, targetModu
|
|||||||
IdeaJavaToKotlinServices
|
IdeaJavaToKotlinServices
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputElements = this.toList().filterIsInstance<PsiElement>()
|
val inputElements = toList().filterIsInstance<PsiElement>()
|
||||||
val (results, _, converterContext) =
|
val (results, _, converterContext) =
|
||||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||||
ThrowableComputable<Result, Exception> {
|
ThrowableComputable<Result, Exception> {
|
||||||
runReadAction { converter.elementsToKotlin(inputElements) }
|
runReadAction { converter.elementsToKotlin(inputElements) }
|
||||||
},
|
},
|
||||||
JavaToKotlinAction.title,
|
JavaToKotlinAction.title,
|
||||||
false,
|
false,
|
||||||
project
|
project
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
val importsToAdd = LinkedHashSet<FqName>()
|
val importsToAdd = LinkedHashSet<FqName>()
|
||||||
@@ -267,8 +287,7 @@ internal fun ElementAndTextList.convertCodeToKotlin(project: Project, targetModu
|
|||||||
parseContext = result.parseContext
|
parseContext = result.parseContext
|
||||||
}
|
}
|
||||||
importsToAdd.addAll(result.importsToAdd)
|
importsToAdd.addAll(result.importsToAdd)
|
||||||
}
|
} else { // failed to convert element to Kotlin, insert "as is"
|
||||||
else { // failed to convert element to Kotlin, insert "as is"
|
|
||||||
convertedCodeBuilder.append(originalText)
|
convertedCodeBuilder.append(originalText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,11 +316,11 @@ internal fun isNoConversionPosition(file: KtFile, offset: Int): Boolean {
|
|||||||
if (token !is PsiWhiteSpace && token.endOffset != offset) return true // pasting into the middle of token
|
if (token !is PsiWhiteSpace && token.endOffset != offset) return true // pasting into the middle of token
|
||||||
|
|
||||||
for (element in token.parentsWithSelf) {
|
for (element in token.parentsWithSelf) {
|
||||||
if (element is PsiComment) {
|
when (element) {
|
||||||
return element.node.elementType == KtTokens.EOL_COMMENT || offset != element.endOffset
|
is PsiComment -> return element.node.elementType == KtTokens.EOL_COMMENT || offset != element.endOffset
|
||||||
|
is KtStringTemplateEntryWithExpression -> return false
|
||||||
|
is KtStringTemplateExpression -> return true
|
||||||
}
|
}
|
||||||
if (element is KtStringTemplateEntryWithExpression) return false
|
|
||||||
if (element is KtStringTemplateExpression) return true
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-27
@@ -61,11 +61,17 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
override fun setOffsets(offsets: IntArray?, index: Int) = index
|
override fun setOffsets(offsets: IntArray?, index: Int) = index
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val DATA_FLAVOR: DataFlavor = DataFlavor(ConvertTextJavaCopyPasteProcessor::class.java, "class: ConvertTextJavaCopyPasteProcessor")
|
val DATA_FLAVOR: DataFlavor =
|
||||||
|
DataFlavor(ConvertTextJavaCopyPasteProcessor::class.java, "class: ConvertTextJavaCopyPasteProcessor")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun collectTransferableData(file: PsiFile, editor: Editor, startOffsets: IntArray, endOffsets: IntArray): List<TextBlockTransferableData> {
|
override fun collectTransferableData(
|
||||||
|
file: PsiFile,
|
||||||
|
editor: Editor,
|
||||||
|
startOffsets: IntArray,
|
||||||
|
endOffsets: IntArray
|
||||||
|
): List<TextBlockTransferableData> {
|
||||||
if (file is KtFile) return listOf(CopiedKotlinCode(file.text, startOffsets, endOffsets))
|
if (file is KtFile) return listOf(CopiedKotlinCode(file.text, startOffsets, endOffsets))
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
@@ -73,22 +79,28 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
|
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
|
||||||
try {
|
try {
|
||||||
if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
|
if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
|
||||||
|
|
||||||
if (content.isDataFlavorSupported(CopiedKotlinCode.DATA_FLAVOR) ||
|
if (content.isDataFlavorSupported(CopiedKotlinCode.DATA_FLAVOR) ||
|
||||||
/* Handled by ConvertJavaCopyPasteProcessor */
|
/* Handled by ConvertJavaCopyPasteProcessor */
|
||||||
content.isDataFlavorSupported(CopiedJavaCode.DATA_FLAVOR)) return emptyList()
|
content.isDataFlavorSupported(CopiedJavaCode.DATA_FLAVOR)
|
||||||
|
) return emptyList()
|
||||||
|
|
||||||
val text = content.getTransferData(DataFlavor.stringFlavor) as String
|
val text = content.getTransferData(DataFlavor.stringFlavor) as String
|
||||||
return listOf(MyTransferableData(text))
|
return listOf(MyTransferableData(text))
|
||||||
}
|
}
|
||||||
}
|
} catch (e: Throwable) {
|
||||||
catch (e: Throwable) {
|
|
||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processTransferableData(project: Project, editor: Editor, bounds: RangeMarker, caretOffset: Int, indented: Ref<Boolean>, values: List<TextBlockTransferableData>) {
|
override fun processTransferableData(
|
||||||
|
project: Project,
|
||||||
|
editor: Editor,
|
||||||
|
bounds: RangeMarker,
|
||||||
|
caretOffset: Int,
|
||||||
|
indented: Ref<Boolean>,
|
||||||
|
values: List<TextBlockTransferableData>
|
||||||
|
) {
|
||||||
if (DumbService.getInstance(project).isDumb) return
|
if (DumbService.getInstance(project).isDumb) return
|
||||||
if (!KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion) return //TODO: use another option?
|
if (!KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion) return //TODO: use another option?
|
||||||
|
|
||||||
@@ -100,11 +112,8 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
val useNewJ2k = checkUseNewJ2k(targetFile)
|
val useNewJ2k = checkUseNewJ2k(targetFile)
|
||||||
|
|
||||||
val targetModule = targetFile.module
|
val targetModule = targetFile.module
|
||||||
|
|
||||||
val pasteTarget = detectPasteTarget(targetFile, bounds.startOffset, bounds.endOffset) ?: return
|
val pasteTarget = detectPasteTarget(targetFile, bounds.startOffset, bounds.endOffset) ?: return
|
||||||
|
|
||||||
val conversionContext = detectConversionContext(pasteTarget.pasteContext, text, project) ?: return
|
val conversionContext = detectConversionContext(pasteTarget.pasteContext, text, project) ?: return
|
||||||
|
|
||||||
if (!confirmConvertJavaOnPaste(project, isPlainText = true)) return
|
if (!confirmConvertJavaOnPaste(project, isPlainText = true)) return
|
||||||
|
|
||||||
val copiedJavaCode = prepareCopiedJavaCodeByContext(text, conversionContext, pasteTarget)
|
val copiedJavaCode = prepareCopiedJavaCodeByContext(text, conversionContext, pasteTarget)
|
||||||
@@ -121,6 +130,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
val importsInsertOffset = targetFile.importList?.endOffset ?: 0
|
val importsInsertOffset = targetFile.importList?.endOffset ?: 0
|
||||||
if (targetFile.importDirectives.isEmpty() && importsInsertOffset > 0)
|
if (targetFile.importDirectives.isEmpty() && importsInsertOffset > 0)
|
||||||
convertedImportsText = "\n" + convertedImportsText
|
convertedImportsText = "\n" + convertedImportsText
|
||||||
|
|
||||||
if (convertedImportsText.isNotBlank())
|
if (convertedImportsText.isNotBlank())
|
||||||
editor.document.insertString(importsInsertOffset, convertedImportsText)
|
editor.document.insertString(importsInsertOffset, convertedImportsText)
|
||||||
|
|
||||||
@@ -139,9 +149,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
conversionPerformed = true
|
conversionPerformed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val conversionTime = measureTimeMillis {
|
val conversionTime = measureTimeMillis { convert() }
|
||||||
convert()
|
|
||||||
}
|
|
||||||
logJ2kConversionStatistics(
|
logJ2kConversionStatistics(
|
||||||
ConversionType.TEXT_EXPRESSION,
|
ConversionType.TEXT_EXPRESSION,
|
||||||
checkUseNewJ2k(targetFile),
|
checkUseNewJ2k(targetFile),
|
||||||
@@ -151,7 +159,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DataForConversion.convertCodeToKotlin(project: Project, targetModule: Module?,useNewJ2k: Boolean): ConversionResult {
|
private fun DataForConversion.convertCodeToKotlin(project: Project, targetModule: Module?, useNewJ2k: Boolean): ConversionResult {
|
||||||
return elementsAndTexts.convertCodeToKotlin(project, targetModule, useNewJ2k)
|
return elementsAndTexts.convertCodeToKotlin(project, targetModule, useNewJ2k)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +176,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
|
|
||||||
val fileText = file.text
|
val fileText = file.text
|
||||||
val dummyDeclarationText = "fun dummy(){}"
|
val dummyDeclarationText = "fun dummy(){}"
|
||||||
val newFileText = fileText.substring(0, startOffset) + " " + dummyDeclarationText + "\n" + fileText.substring(endOffset)
|
val newFileText = "${fileText.substring(0, startOffset)} $dummyDeclarationText\n${fileText.substring(endOffset)}"
|
||||||
|
|
||||||
val newFile = parseAsFile(newFileText, KotlinFileType.INSTANCE, file.project)
|
val newFile = parseAsFile(newFileText, KotlinFileType.INSTANCE, file.project)
|
||||||
(newFile as KtFile).analysisContext = file
|
(newFile as KtFile).analysisContext = file
|
||||||
@@ -183,9 +191,8 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
private fun detectConversionContext(pasteContext: KotlinContext, text: String, project: Project): JavaContext? {
|
private fun detectConversionContext(pasteContext: KotlinContext, text: String, project: Project): JavaContext? {
|
||||||
if (isParsedAsKotlinCode(text, pasteContext, project)) return null
|
if (isParsedAsKotlinCode(text, pasteContext, project)) return null
|
||||||
|
|
||||||
fun JavaContext.check(): JavaContext? {
|
fun JavaContext.check(): JavaContext? =
|
||||||
return takeIf { isParsedAsJavaCode(text, it, project) }
|
takeIf { isParsedAsJavaCode(text, it, project) }
|
||||||
}
|
|
||||||
|
|
||||||
when (pasteContext) {
|
when (pasteContext) {
|
||||||
KotlinContext.TOP_LEVEL -> {
|
KotlinContext.TOP_LEVEL -> {
|
||||||
@@ -238,19 +245,21 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseAsFile(text: String, fileType: LanguageFileType, project: Project): PsiFile {
|
private fun parseAsFile(text: String, fileType: LanguageFileType, project: Project): PsiFile {
|
||||||
return PsiFileFactory.getInstance(project).createFileFromText("Dummy." + fileType.defaultExtension, fileType, text, LocalTimeCounter.currentTime(), true)
|
return PsiFileFactory.getInstance(project)
|
||||||
|
.createFileFromText("Dummy." + fileType.defaultExtension, fileType, text, LocalTimeCounter.currentTime(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DataForConversion.tryResolveImports(targetFile: KtFile): ElementAndTextList {
|
private fun DataForConversion.tryResolveImports(targetFile: KtFile): ElementAndTextList {
|
||||||
val importResolver = PlainTextPasteImportResolver(this, targetFile)
|
val importResolver = PlainTextPasteImportResolver(this, targetFile)
|
||||||
importResolver.addImportsFromTargetFile()
|
importResolver.addImportsFromTargetFile()
|
||||||
importResolver.tryResolveReferences()
|
importResolver.tryResolveReferences()
|
||||||
return ElementAndTextList(importResolver.addedImports.flatMap { listOf("\n", it) } + "\n\n") //TODO Non-manual formatting for import list
|
return ElementAndTextList(importResolver.addedImports.flatMap { importStatement ->
|
||||||
|
listOf("\n", importStatement)
|
||||||
|
} + "\n\n") //TODO Non-manual formatting for import list
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareCopiedJavaCodeByContext(text: String, context: JavaContext, target: KtElement): CopiedJavaCode {
|
private fun prepareCopiedJavaCodeByContext(text: String, context: JavaContext, target: KtElement): CopiedJavaCode {
|
||||||
val targetFile = target.containingFile as KtFile
|
val targetFile = target.containingFile as KtFile
|
||||||
|
|
||||||
val (localDeclarations, memberDeclarations) = javaContextDeclarationRenderer.render(target)
|
val (localDeclarations, memberDeclarations) = javaContextDeclarationRenderer.render(target)
|
||||||
|
|
||||||
val prefix = buildString {
|
val prefix = buildString {
|
||||||
@@ -265,16 +274,25 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
val classDef = when (context) {
|
val classDef = when (context) {
|
||||||
JavaContext.TOP_LEVEL -> ""
|
JavaContext.TOP_LEVEL -> ""
|
||||||
|
|
||||||
JavaContext.CLASS_BODY,
|
JavaContext.CLASS_BODY, JavaContext.IN_BLOCK, JavaContext.EXPRESSION -> {
|
||||||
JavaContext.IN_BLOCK,
|
|
||||||
JavaContext.EXPRESSION -> {
|
|
||||||
val lightClass = target.getParentOfType<KtClass>(false)?.toLightClass()
|
val lightClass = target.getParentOfType<KtClass>(false)?.toLightClass()
|
||||||
|
|
||||||
buildString {
|
buildString {
|
||||||
append("class ")
|
append("class ")
|
||||||
append(lightClass?.name ?: "Dummy")
|
append(lightClass?.name ?: "Dummy")
|
||||||
lightClass?.extendsListTypes?.ifNotEmpty { joinTo(this@buildString, prefix = " extends ") { it.getCanonicalText(true) } }
|
lightClass?.extendsListTypes?.ifNotEmpty {
|
||||||
lightClass?.implementsListTypes?.ifNotEmpty { joinTo(this@buildString, prefix = " implements ") { it.getCanonicalText(true) } }
|
joinTo(
|
||||||
|
this@buildString,
|
||||||
|
prefix = " extends "
|
||||||
|
) { it.getCanonicalText(true) }
|
||||||
|
}
|
||||||
|
lightClass?.implementsListTypes?.ifNotEmpty {
|
||||||
|
joinTo(this@buildString, prefix = " implements ") {
|
||||||
|
it.getCanonicalText(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,6 +318,7 @@ class ConvertTextJavaCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransf
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@get:TestOnly var conversionPerformed: Boolean = false
|
@get:TestOnly
|
||||||
|
var conversionPerformed: Boolean = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,10 @@ import org.jetbrains.kotlin.types.KotlinType
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data class DataForConversion private constructor(
|
data class DataForConversion private constructor(
|
||||||
val elementsAndTexts: ElementAndTextList /* list consisting of PsiElement's to convert and plain String's */,
|
val elementsAndTexts: ElementAndTextList /* list consisting of PsiElement's to convert and plain String's */,
|
||||||
val importsAndPackage: String,
|
val importsAndPackage: String,
|
||||||
val file: PsiJavaFile
|
val file: PsiJavaFile
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun prepare(copiedCode: CopiedJavaCode, project: Project): DataForConversion {
|
fun prepare(copiedCode: CopiedJavaCode, project: Project): DataForConversion {
|
||||||
@@ -65,7 +64,6 @@ data class DataForConversion private constructor(
|
|||||||
return DataForConversion(elementsAndTexts, importsAndPackage, file)
|
return DataForConversion(elementsAndTexts, importsAndPackage, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun clipTextIfNeeded(file: PsiJavaFile, fileText: String, startOffsets: IntArray, endOffsets: IntArray): String? {
|
private fun clipTextIfNeeded(file: PsiJavaFile, fileText: String, startOffsets: IntArray, endOffsets: IntArray): String? {
|
||||||
val ranges = startOffsets.indices.map { TextRange(startOffsets[it], endOffsets[it]) }.sortedBy { it.start }
|
val ranges = startOffsets.indices.map { TextRange(startOffsets[it], endOffsets[it]) }.sortedBy { it.start }
|
||||||
|
|
||||||
@@ -126,8 +124,7 @@ data class DataForConversion private constructor(
|
|||||||
val offset = this[i]
|
val offset = this[i]
|
||||||
if (offset >= range.end) {
|
if (offset >= range.end) {
|
||||||
this[i] = offset - range.length
|
this[i] = offset - range.length
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
assert(offset <= range.start)
|
assert(offset <= range.start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +138,10 @@ data class DataForConversion private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun PsiElement.maximalParentToClip(range: TextRange): PsiElement? {
|
private fun PsiElement.maximalParentToClip(range: TextRange): PsiElement? {
|
||||||
val firstNotInRange = parentsWithSelf.takeWhile { it !is PsiDirectory }.firstOrNull { it.range !in range } ?: return null
|
val firstNotInRange = parentsWithSelf
|
||||||
|
.takeWhile { it !is PsiDirectory }
|
||||||
|
.firstOrNull { it.range !in range }
|
||||||
|
?: return null
|
||||||
return firstNotInRange.parentsWithSelf.lastOrNull { it.minimizedTextRange() in range }
|
return firstNotInRange.parentsWithSelf.lastOrNull { it.minimizedTextRange() in range }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,41 +155,42 @@ data class DataForConversion private constructor(
|
|||||||
private fun canDropElementFromText(element: PsiElement): Boolean {
|
private fun canDropElementFromText(element: PsiElement): Boolean {
|
||||||
return when (element) {
|
return when (element) {
|
||||||
is PsiWhiteSpace, is PsiComment, is PsiModifierList, is PsiAnnotation -> true
|
is PsiWhiteSpace, is PsiComment, is PsiModifierList, is PsiAnnotation -> true
|
||||||
|
|
||||||
is PsiJavaToken -> {
|
is PsiJavaToken -> {
|
||||||
when (element.tokenType) {
|
when (element.tokenType) {
|
||||||
// modifiers
|
// modifiers
|
||||||
JavaTokenType.PUBLIC_KEYWORD, JavaTokenType.PROTECTED_KEYWORD, JavaTokenType.PRIVATE_KEYWORD,
|
JavaTokenType.PUBLIC_KEYWORD, JavaTokenType.PROTECTED_KEYWORD, JavaTokenType.PRIVATE_KEYWORD,
|
||||||
JavaTokenType.STATIC_KEYWORD, JavaTokenType.ABSTRACT_KEYWORD, JavaTokenType.FINAL_KEYWORD,
|
JavaTokenType.STATIC_KEYWORD, JavaTokenType.ABSTRACT_KEYWORD, JavaTokenType.FINAL_KEYWORD,
|
||||||
JavaTokenType.NATIVE_KEYWORD, JavaTokenType.SYNCHRONIZED_KEYWORD, JavaTokenType.STRICTFP_KEYWORD,
|
JavaTokenType.NATIVE_KEYWORD, JavaTokenType.SYNCHRONIZED_KEYWORD, JavaTokenType.STRICTFP_KEYWORD,
|
||||||
JavaTokenType.TRANSIENT_KEYWORD, JavaTokenType.VOLATILE_KEYWORD, JavaTokenType.DEFAULT_KEYWORD -> element.getParent() is PsiModifierList
|
JavaTokenType.TRANSIENT_KEYWORD, JavaTokenType.VOLATILE_KEYWORD, JavaTokenType.DEFAULT_KEYWORD ->
|
||||||
|
element.getParent() is PsiModifierList
|
||||||
JavaTokenType.SEMICOLON -> true
|
JavaTokenType.SEMICOLON -> true
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is PsiCodeBlock -> element.getParent() is PsiMethod
|
is PsiCodeBlock -> element.getParent() is PsiMethod
|
||||||
|
|
||||||
else -> element.firstChild == null
|
else -> element.firstChild == null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryClipLeftSide(element: PsiElement, leftBound: Int)
|
private fun tryClipLeftSide(element: PsiElement, leftBound: Int) =
|
||||||
= tryClipSide(element, leftBound, { range }, { allChildren })
|
tryClipSide(element, leftBound, { range }, { allChildren })
|
||||||
|
|
||||||
private fun tryClipRightSide(element: PsiElement, rightBound: Int): Int? {
|
private fun tryClipRightSide(element: PsiElement, rightBound: Int): Int? {
|
||||||
fun Int.transform() = Int.MAX_VALUE - this
|
fun Int.transform() = Int.MAX_VALUE - this
|
||||||
fun TextRange.transform() = TextRange(end.transform(), start.transform())
|
fun TextRange.transform() = TextRange(end.transform(), start.transform())
|
||||||
return tryClipSide(element, rightBound.transform(), { range.transform() }, { lastChild.siblings(forward = false) })?.transform()
|
return tryClipSide(
|
||||||
|
element,
|
||||||
|
rightBound.transform(),
|
||||||
|
{ range.transform() },
|
||||||
|
{ lastChild.siblings(forward = false) }
|
||||||
|
)?.transform()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryClipSide(
|
private fun tryClipSide(
|
||||||
element: PsiElement,
|
element: PsiElement,
|
||||||
rangeBound: Int,
|
rangeBound: Int,
|
||||||
rangeFunction: PsiElement.() -> TextRange,
|
rangeFunction: PsiElement.() -> TextRange,
|
||||||
childrenFunction: PsiElement.() -> Sequence<PsiElement>
|
childrenFunction: PsiElement.() -> Sequence<PsiElement>
|
||||||
): Int? {
|
): Int? {
|
||||||
if (element.firstChild == null) return null
|
if (element.firstChild == null) return null
|
||||||
|
|
||||||
@@ -202,12 +203,10 @@ data class DataForConversion private constructor(
|
|||||||
|
|
||||||
if (childRange.start >= rangeBound) { // we have cut enough already
|
if (childRange.start >= rangeBound) { // we have cut enough already
|
||||||
break
|
break
|
||||||
}
|
} else if (childRange.end <= rangeBound) { // need to drop the whole element
|
||||||
else if (childRange.end <= rangeBound) { // need to drop the whole element
|
|
||||||
if (!canDropElementFromText(child)) return null
|
if (!canDropElementFromText(child)) return null
|
||||||
clipTo = childRange.end
|
clipTo = childRange.end
|
||||||
}
|
} else { // rangeBound is inside child's range
|
||||||
else { // rangeBound is inside child's range
|
|
||||||
if (child is PsiWhiteSpace) break // no need to cut whitespace - we can leave it as is
|
if (child is PsiWhiteSpace) break // no need to cut whitespace - we can leave it as is
|
||||||
return tryClipSide(child, rangeBound, rangeFunction, childrenFunction)
|
return tryClipSide(child, rangeBound, rangeFunction, childrenFunction)
|
||||||
}
|
}
|
||||||
@@ -217,15 +216,14 @@ data class DataForConversion private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ElementAndTextList.collectElementsToConvert(
|
private fun ElementAndTextList.collectElementsToConvert(
|
||||||
file: PsiJavaFile,
|
file: PsiJavaFile,
|
||||||
fileText: String,
|
fileText: String,
|
||||||
range: TextRange
|
range: TextRange
|
||||||
) {
|
) {
|
||||||
val elements = file.elementsInRange(range)
|
val elements = file.elementsInRange(range)
|
||||||
if (elements.isEmpty()) {
|
if (elements.isEmpty()) {
|
||||||
add(fileText.substring(range.start, range.end))
|
add(fileText.substring(range.start, range.end))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
add(fileText.substring(range.start, elements.first().range.start))
|
add(fileText.substring(range.start, elements.first().range.start))
|
||||||
elements.forEach {
|
elements.forEach {
|
||||||
if (shouldExpandToChildren(it))
|
if (shouldExpandToChildren(it))
|
||||||
@@ -243,7 +241,7 @@ data class DataForConversion private constructor(
|
|||||||
private fun buildImportsAndPackage(sourceFile: PsiJavaFile): String {
|
private fun buildImportsAndPackage(sourceFile: PsiJavaFile): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
val packageName = sourceFile.packageName
|
val packageName = sourceFile.packageName
|
||||||
if (!packageName.isEmpty()) {
|
if (packageName.isNotEmpty()) {
|
||||||
append("package $packageName\n")
|
append("package $packageName\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,8 +251,7 @@ data class DataForConversion private constructor(
|
|||||||
val qualifiedName = import.qualifiedName ?: continue
|
val qualifiedName = import.qualifiedName ?: continue
|
||||||
if (import.isOnDemand) {
|
if (import.isOnDemand) {
|
||||||
append("import $qualifiedName.*\n")
|
append("import $qualifiedName.*\n")
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val fqName = FqNameUnsafe(qualifiedName)
|
val fqName = FqNameUnsafe(qualifiedName)
|
||||||
// skip explicit imports of platform classes mapped into Kotlin classes
|
// skip explicit imports of platform classes mapped into Kotlin classes
|
||||||
if (fqName.isSafe && JavaToKotlinClassMap.isJavaPlatformClass(fqName.toSafe())) continue
|
if (fqName.isSafe && JavaToKotlinClassMap.isJavaPlatformClass(fqName.toSafe())) continue
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ class ElementAndTextList() {
|
|||||||
fun toList(): List<Any> = elementsAndTexts.toList()
|
fun toList(): List<Any> = elementsAndTexts.toList()
|
||||||
|
|
||||||
fun process(processor: ElementsAndTextsProcessor) {
|
fun process(processor: ElementsAndTextsProcessor) {
|
||||||
elementsAndTexts.forEach {
|
elementsAndTexts.forEach { element ->
|
||||||
when (it) {
|
when (element) {
|
||||||
is PsiElement -> processor.processElement(it)
|
is PsiElement -> processor.processElement(element)
|
||||||
is String -> processor.processText(it)
|
is String -> processor.processText(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ data class ContextDeclarations(
|
|||||||
val memberDeclarationsJavaStubs: String
|
val memberDeclarationsJavaStubs: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class JavaContextDeclarationRenderer {
|
class JavaContextDeclarationRenderer {
|
||||||
private val KtElement.memberDeclarations
|
private val KtElement.memberDeclarations
|
||||||
get() = parents()
|
get() = parents()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class KotlinFilePasteProvider : PasteProvider {
|
|||||||
|
|
||||||
val ktFile = KtPsiFactory(project).createFile(text)
|
val ktFile = KtPsiFactory(project).createFile(text)
|
||||||
val fileName = (ktFile.declarations.firstOrNull()?.name ?: return) + ".kt"
|
val fileName = (ktFile.declarations.firstOrNull()?.name ?: return) + ".kt"
|
||||||
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
val directory = ideView.getOrChooseDirectory() ?: return
|
val directory = ideView.getOrChooseDirectory() ?: return
|
||||||
project.executeWriteCommand("Create Kotlin file") {
|
project.executeWriteCommand("Create Kotlin file") {
|
||||||
val file = try {
|
val file = try {
|
||||||
@@ -47,9 +48,9 @@ class KotlinFilePasteProvider : PasteProvider {
|
|||||||
if (document != null) {
|
if (document != null) {
|
||||||
document.setText(text)
|
document.setText(text)
|
||||||
documentManager.commitDocument(document)
|
documentManager.commitDocument(document)
|
||||||
val qualName = JavaDirectoryService.getInstance()?.getPackage(directory)?.qualifiedName
|
val qualifiedName = JavaDirectoryService.getInstance()?.getPackage(directory)?.qualifiedName
|
||||||
if (qualName != null && file is KtFile) {
|
if (qualifiedName != null && file is KtFile) {
|
||||||
file.packageFqName = FqName(qualName)
|
file.packageFqName = FqName(qualifiedName)
|
||||||
}
|
}
|
||||||
OpenFileDescriptor(project, file.virtualFile).navigate(true)
|
OpenFileDescriptor(project, file.virtualFile).navigate(true)
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,8 @@ class KotlinFilePasteProvider : PasteProvider {
|
|||||||
//todo: KT-25329, to remove these heuristics
|
//todo: KT-25329, to remove these heuristics
|
||||||
if (text.contains(";\n") ||
|
if (text.contains(";\n") ||
|
||||||
((text.contains("public interface") || text.contains("public class")) &&
|
((text.contains("public interface") || text.contains("public class")) &&
|
||||||
!text.contains("fun "))) return false //Optimisation for Java. Kotlin doesn't need that...
|
!text.contains("fun "))
|
||||||
|
) return false //Optimisation for Java. Kotlin doesn't need that...
|
||||||
val file = KtPsiFactory(project).createFile(text)
|
val file = KtPsiFactory(project).createFile(text)
|
||||||
return !PsiTreeUtil.hasErrorElements(file)
|
return !PsiTreeUtil.hasErrorElements(file)
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-51
@@ -37,8 +37,7 @@ import org.jetbrains.kotlin.psi.KtImportDirective
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
|
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class PlainTextPasteImportResolver(val dataForConversion: DataForConversion, val targetFile: KtFile) {
|
class PlainTextPasteImportResolver(private val dataForConversion: DataForConversion, val targetFile: KtFile) {
|
||||||
|
|
||||||
private val file = dataForConversion.file
|
private val file = dataForConversion.file
|
||||||
private val project = targetFile.project
|
private val project = targetFile.project
|
||||||
|
|
||||||
@@ -59,8 +58,8 @@ class PlainTextPasteImportResolver(val dataForConversion: DataForConversion, val
|
|||||||
|
|
||||||
private fun canBeImported(descriptor: DeclarationDescriptorWithVisibility?): Boolean {
|
private fun canBeImported(descriptor: DeclarationDescriptorWithVisibility?): Boolean {
|
||||||
return descriptor != null
|
return descriptor != null
|
||||||
&& descriptor.canBeReferencedViaImport()
|
&& descriptor.canBeReferencedViaImport()
|
||||||
&& descriptor.isVisible(targetFile, null, bindingContext, resolutionFacade)
|
&& descriptor.isVisible(targetFile, null, bindingContext, resolutionFacade)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addImport(importStatement: PsiImportStatementBase, shouldAddToTarget: Boolean = false) {
|
private fun addImport(importStatement: PsiImportStatementBase, shouldAddToTarget: Boolean = false) {
|
||||||
@@ -76,46 +75,54 @@ class PlainTextPasteImportResolver(val dataForConversion: DataForConversion, val
|
|||||||
val importedReference = importDirective.importedReference
|
val importedReference = importDirective.importedReference
|
||||||
if (importPath != null && !importPath.hasAlias() && importedReference is KtDotQualifiedExpression) {
|
if (importPath != null && !importPath.hasAlias() && importedReference is KtDotQualifiedExpression) {
|
||||||
val receiver = importedReference
|
val receiver = importedReference
|
||||||
.receiverExpression
|
.receiverExpression
|
||||||
.referenceExpression()
|
.referenceExpression()
|
||||||
?.mainReference
|
?.mainReference
|
||||||
?.resolve()
|
?.resolve()
|
||||||
val selector = importedReference
|
val selector = importedReference
|
||||||
.selectorExpression
|
.selectorExpression
|
||||||
?.referenceExpression()
|
?.referenceExpression()
|
||||||
?.mainReference
|
?.mainReference
|
||||||
?.resolve()
|
?.resolve()
|
||||||
|
|
||||||
val isPackageReceiver = receiver is PsiPackage
|
val isPackageReceiver = receiver is PsiPackage
|
||||||
val isClassReceiver = receiver is PsiClass
|
val isClassReceiver = receiver is PsiClass
|
||||||
val isClassSelector = selector is PsiClass
|
val isClassSelector = selector is PsiClass
|
||||||
|
|
||||||
if (importPath.isAllUnder) {
|
if (importPath.isAllUnder) {
|
||||||
if (isClassReceiver)
|
when {
|
||||||
addImport(psiElementFactory.createImportStaticStatement(receiver as PsiClass, "*"))
|
isClassReceiver ->
|
||||||
else if (isPackageReceiver)
|
addImport(psiElementFactory.createImportStaticStatement(receiver as PsiClass, "*"))
|
||||||
addImport(psiElementFactory.createImportStatementOnDemand((receiver as PsiPackage).qualifiedName))
|
isPackageReceiver ->
|
||||||
}
|
addImport(psiElementFactory.createImportStatementOnDemand((receiver as PsiPackage).qualifiedName))
|
||||||
else {
|
}
|
||||||
if (isClassSelector)
|
} else {
|
||||||
addImport(psiElementFactory.createImportStatement(selector as PsiClass))
|
when {
|
||||||
else if (isClassReceiver)
|
isClassSelector ->
|
||||||
addImport(psiElementFactory.createImportStaticStatement(receiver as PsiClass, importPath.importedName!!.asString()))
|
addImport(psiElementFactory.createImportStatement(selector as PsiClass))
|
||||||
|
isClassReceiver ->
|
||||||
|
addImport(
|
||||||
|
psiElementFactory.createImportStaticStatement(
|
||||||
|
receiver as PsiClass,
|
||||||
|
importPath.importedName!!.asString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (importList !in dataForConversion.elementsAndTexts.toList())
|
if (importList !in dataForConversion.elementsAndTexts.toList()) {
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
targetFile.importDirectives.forEach(::tryConvertKotlinImport)
|
targetFile.importDirectives.forEach(::tryConvertKotlinImport)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tryResolveReferences() {
|
fun tryResolveReferences() {
|
||||||
|
val elementsWithUnresolvedRef = PsiTreeUtil.collectElements(file) { element ->
|
||||||
val elementsWithUnresolvedRef = PsiTreeUtil.collectElements(file) {
|
element.reference != null
|
||||||
it.reference != null
|
&& element.reference is PsiQualifiedReference
|
||||||
&& it.reference is PsiQualifiedReference
|
&& element.reference?.resolve() == null
|
||||||
&& it.reference?.resolve() == null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tryResolveReference(reference: PsiQualifiedReference): Boolean {
|
fun tryResolveReference(reference: PsiQualifiedReference): Boolean {
|
||||||
@@ -123,28 +130,27 @@ class PlainTextPasteImportResolver(val dataForConversion: DataForConversion, val
|
|||||||
val referenceName = reference.referenceName ?: return false
|
val referenceName = reference.referenceName ?: return false
|
||||||
if (referenceName in failedToResolveReferenceNames) return false
|
if (referenceName in failedToResolveReferenceNames) return false
|
||||||
val classes = shortNameCache.getClassesByName(referenceName, scope)
|
val classes = shortNameCache.getClassesByName(referenceName, scope)
|
||||||
.mapNotNull { psiClass ->
|
.mapNotNull { psiClass ->
|
||||||
val containingFile = psiClass.containingFile
|
val containingFile = psiClass.containingFile
|
||||||
if (ProjectRootsUtil.isInProjectOrLibraryContent(containingFile)) {
|
if (ProjectRootsUtil.isInProjectOrLibraryContent(containingFile)) {
|
||||||
psiClass to psiClass.getJavaMemberDescriptor() as? ClassDescriptor
|
psiClass to psiClass.getJavaMemberDescriptor() as? ClassDescriptor
|
||||||
}
|
} else null
|
||||||
else {
|
}.filter { canBeImported(it.second) }
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter { canBeImported(it.second) }
|
|
||||||
|
|
||||||
classes.find { (_, descriptor) -> JavaToKotlinClassMap.mapPlatformClass(descriptor!!).isNotEmpty() }
|
classes.find { (_, descriptor) ->
|
||||||
?.let { (psiClass, _) -> addImport(psiElementFactory.createImportStatement(psiClass)) }
|
JavaToKotlinClassMap.mapPlatformClass(descriptor!!).isNotEmpty()
|
||||||
|
}?.let { (psiClass, _) ->
|
||||||
|
addImport(psiElementFactory.createImportStatement(psiClass))
|
||||||
|
}
|
||||||
if (reference.resolve() != null) return true
|
if (reference.resolve() != null) return true
|
||||||
|
|
||||||
classes.singleOrNull()?.let { (psiClass, _) ->
|
classes.singleOrNull()?.let { (psiClass, _) ->
|
||||||
addImport(psiElementFactory.createImportStatement(psiClass), true)
|
addImport(psiElementFactory.createImportStatement(psiClass), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reference.resolve() != null) return true
|
when {
|
||||||
else {
|
reference.resolve() != null -> return true
|
||||||
if (classes.isNotEmpty()) {
|
classes.isNotEmpty() -> {
|
||||||
ambiguityInResolution = true
|
ambiguityInResolution = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -163,14 +169,10 @@ class PlainTextPasteImportResolver(val dataForConversion: DataForConversion, val
|
|||||||
addImport(psiElementFactory.createImportStaticStatement(psiMember.containingClass!!, psiMember.name!!), true)
|
addImport(psiElementFactory.createImportStaticStatement(psiMember.containingClass!!, psiMember.name!!), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reference.resolve() != null) return false
|
when {
|
||||||
else {
|
reference.resolve() != null -> return false
|
||||||
if (members.isNotEmpty()) {
|
members.isNotEmpty() -> ambiguityInResolution = true
|
||||||
ambiguityInResolution = true
|
else -> couldNotResolve = true
|
||||||
}
|
|
||||||
else {
|
|
||||||
couldNotResolve = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user