[kotlin] Support bindToElement for KDoc references in K2

#KTIJ-26328 Fixed

GitOrigin-RevId: d723184b11ef626459f9a2b9de400e260dde6538
This commit is contained in:
Bart van Helvert
2023-07-19 14:14:40 +02:00
committed by intellij-monorepo-bot
parent 9988f87a47
commit 55e378549e
10 changed files with 114 additions and 37 deletions

View File

@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.base.analysis.withRootPrefixIfNeeded
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinNameSuggestionProvider
@@ -30,6 +31,11 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
class K1ReferenceMutateService : KtReferenceMutateServiceBase() {
override fun bindToElement(ktReference: KtReference, element: PsiElement): PsiElement = when (ktReference) {
is KtSimpleNameReference -> bindToElement(ktReference, element, KtSimpleNameReference.ShorteningMode.DELAYED_SHORTENING)
else -> throw IncorrectOperationException()
}
override fun bindToFqName(
simpleNameReference: KtSimpleNameReference,
fqName: FqName,

View File

@@ -0,0 +1,11 @@
// BIND_TO test.bar.B
package test.bar
class A { }
class B { }
/**
* [test.bar.<caret>A]
*/
fun foo() { }

View File

@@ -0,0 +1,11 @@
// BIND_TO test.bar.B
package test.bar
class A { }
class B { }
/**
* [B]
*/
fun foo() { }

View File

@@ -0,0 +1,9 @@
// BIND_TO B
class A { }
class B { }
/**
* [<caret>A]
*/
fun foo() { }

View File

@@ -0,0 +1,9 @@
// BIND_TO B
class A { }
class B { }
/**
* [<caret>B]
*/
fun foo() { }

View File

@@ -0,0 +1,11 @@
// BIND_TO test.B
package test
class A { }
class B { }
/**
* [<caret>A]
*/
fun foo() { }

View File

@@ -0,0 +1,11 @@
// BIND_TO test.B
package test
class A { }
class B { }
/**
* [<caret>B]
*/
fun foo() { }

View File

@@ -28,11 +28,6 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
abstract class KtReferenceMutateServiceBase : KtReferenceMutateService {
override fun bindToElement(ktReference: KtReference, element: PsiElement): PsiElement = when (ktReference) {
is KtSimpleNameReference -> bindToElement(ktReference, element, KtSimpleNameReference.ShorteningMode.DELAYED_SHORTENING)
else -> throw IncorrectOperationException()
}
override fun bindToElement(
simpleNameReference: KtSimpleNameReference,
element: PsiElement,

View File

@@ -40,5 +40,6 @@
<orderEntry type="module" module-name="kotlin.refactorings.introduce.k2" />
<orderEntry type="module" module-name="kotlin.base.analysis-api.utils" />
<orderEntry type="module" module-name="kotlin.base.code-insight" />
<orderEntry type="module" module-name="kotlin.base.kdoc" />
</component>
</module>

View File

@@ -14,9 +14,12 @@ import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
import org.jetbrains.kotlin.analysis.api.symbols.KtSyntheticJavaPropertySymbol
import org.jetbrains.kotlin.idea.base.analysis.api.utils.invokeShortening
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinNameSuggester
import org.jetbrains.kotlin.idea.base.psi.kotlinFqName
import org.jetbrains.kotlin.idea.base.psi.replaced
import org.jetbrains.kotlin.idea.kdoc.KDocElementFactory
import org.jetbrains.kotlin.idea.refactoring.intentions.OperatorToFunctionConverter
import org.jetbrains.kotlin.idea.refactoring.rename.KtReferenceMutateServiceBase
import org.jetbrains.kotlin.idea.references.KDocReference
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.KtSimpleReference
@@ -29,6 +32,37 @@ import org.jetbrains.kotlin.psi.*
*/
@Suppress("UNCHECKED_CAST")
internal class K2ReferenceMutateService : KtReferenceMutateServiceBase() {
override fun bindToElement(ktReference: KtReference, element: PsiElement): PsiElement = when (ktReference) {
is KtSimpleNameReference -> bindToElement(ktReference, element, KtSimpleNameReference.ShorteningMode.DELAYED_SHORTENING)
is KDocReference -> bindToElement(ktReference, element)
else -> throw IncorrectOperationException()
}
@OptIn(KtAllowAnalysisFromWriteAction::class)
private fun <R : KtElement> KtFile.withOptimizedImports(replacement: () -> R?): PsiElement? {
fun KtFile.unusedImports(): Set<KtImportDirective> = allowAnalysisFromWriteAction { analyze(this) {
analyseImports(this@unusedImports).unusedImports
} }
val unusedImportsBefore = unusedImports()
val newElement = replacement() ?: return null
val unusedImportsAfter = unusedImports()
val importsToRemove = unusedImportsAfter - unusedImportsBefore
importsToRemove.forEach(PsiElement::delete)
val newShortenings = analyze(newElement) { collectPossibleReferenceShorteningsInElement(newElement) }
return newShortenings.invokeShortening().firstOrNull() ?: newElement
}
@RequiresWriteLock
private fun bindToElement(docReference: KDocReference, targetElement: PsiElement): PsiElement {
val docElement = docReference.element
val targetFqn = targetElement.kotlinFqName ?: return docElement
if (targetFqn.isRoot) return docElement
return docElement.containingKtFile.withOptimizedImports {
val newDocReference = KDocElementFactory(targetElement.project).createNameFromText(targetFqn.asString())
docReference.expression.replaced(newDocReference)
} ?: docElement
}
@RequiresWriteLock
override fun bindToFqName(
simpleNameReference: KtSimpleNameReference,
@@ -39,38 +73,17 @@ internal class K2ReferenceMutateService : KtReferenceMutateServiceBase() {
if (targetElement !is KtElement) operationNotSupportedInK2Error() // TODO fix reference shortener for non-Kotlin target elements
val expression = simpleNameReference.expression
if (fqName.isRoot) return expression
val containingFile = expression.containingKtFile
val unusedImportsBeforeChange = containingFile.unusedImports()
val anchorElement = expression.parentOfType<KtUserType>(withSelf = false)
?: expression.parentOfType<KtDotQualifiedExpression>(withSelf = false)
?: expression
val newElement = when (anchorElement) {
is KtUserType -> anchorElement.replaceWith(fqName)
is KtSimpleNameExpression -> anchorElement.replaceWith(fqName)
is KtDotQualifiedExpression -> anchorElement.replaceWith(fqName)
else -> null
} ?: return expression
val unusedImportsAfterChange = containingFile.unusedImports()
val importsToRemove = unusedImportsAfterChange - unusedImportsBeforeChange
importsToRemove.forEach {
it.delete()
}
val newShortenings = @OptIn(KtAllowAnalysisFromWriteAction::class) allowAnalysisFromWriteAction {
analyze(newElement) { collectPossibleReferenceShorteningsInElement(newElement) }
}
return newShortenings.invokeShortening().firstOrNull() ?: newElement
}
private fun KtFile.unusedImports(): Set<KtImportDirective> = analyze(this) {
@OptIn(KtAllowAnalysisFromWriteAction::class)
allowAnalysisFromWriteAction {
analyseImports(this@unusedImports).unusedImports
}
return expression.containingKtFile.withOptimizedImports {
val anchorElement = expression.parentOfType<KtUserType>(withSelf = false)
?: expression.parentOfType<KtDotQualifiedExpression>(withSelf = false)
?: expression
when (anchorElement) {
is KtUserType -> anchorElement.replaceWith(fqName)
is KtSimpleNameExpression -> anchorElement.replaceWith(fqName)
is KtDotQualifiedExpression -> anchorElement.replaceWith(fqName)
else -> null
}
} ?: expression
}
private fun KtTypeElement.replaceWith(fqName: FqName): KtTypeElement {