mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] K2: consider properties in context conversion intentions
KTIJ-33555 KTIJ-33556 KTIJ-34531 GitOrigin-RevId: 8ca65b88632b7ea3258fc7bb331fe8d55d4282fe
This commit is contained in:
committed by
intellij-monorepo-bot
parent
10d6fe208f
commit
bef84b9b2e
+3
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.contexts.ContextParam
|
||||
import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.contexts.ContextParameterUtils.isConvertibleContextParameter
|
||||
import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.contexts.ContextParameterUtils.runChangeSignatureForParameter
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.KotlinChangeInfo
|
||||
import org.jetbrains.kotlin.psi.KtContextReceiverList
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
|
||||
class ConvertContextParameterToRegularParameterIntention : SelfTargetingIntention<KtParameter>(
|
||||
@@ -18,6 +20,7 @@ class ConvertContextParameterToRegularParameterIntention : SelfTargetingIntentio
|
||||
|
||||
override fun isApplicableTo(element: KtParameter, caretOffset: Int): Boolean {
|
||||
return isConvertibleContextParameter(element)
|
||||
&& (element.parent as? KtContextReceiverList)?.ownerDeclaration is KtNamedFunction
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtParameter, editor: Editor?) {
|
||||
|
||||
+11
-8
@@ -11,10 +11,12 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention
|
||||
import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.contexts.ContextParameterUtils.getContextParameters
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.KotlinChangeInfo
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.KotlinChangeSignatureProcessor
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.KotlinMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinMemberInplaceRenameHandler
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
@@ -27,19 +29,20 @@ class ConvertReceiverParameterToContextParameterIntention : SelfTargetingIntenti
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
|
||||
override fun isApplicableTo(element: KtTypeReference, caretOffset: Int): Boolean {
|
||||
return element.languageVersionSettings.supportsFeature(LanguageFeature.ContextParameters)
|
||||
if (!element.languageVersionSettings.supportsFeature(LanguageFeature.ContextParameters)) return false
|
||||
return (element.parent as? KtNamedFunction)?.receiverTypeReference == element // disabled for properties, TODO KTIJ-34531
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtTypeReference, editor: Editor?) {
|
||||
val ktFunction = element.getStrictParentOfType<KtNamedFunction>() ?: return
|
||||
val methodDescriptor = KotlinMethodDescriptor(ktFunction)
|
||||
val ktCallable = element.getStrictParentOfType<KtCallableDeclaration>() ?: return
|
||||
val methodDescriptor = KotlinMethodDescriptor(ktCallable)
|
||||
val changeInfo = KotlinChangeInfo(methodDescriptor)
|
||||
if (!configureChangeInfo(changeInfo)) return
|
||||
object : KotlinChangeSignatureProcessor(element.project, changeInfo) {
|
||||
override fun performRefactoring(usages: Array<out UsageInfo?>) {
|
||||
super.performRefactoring(usages)
|
||||
DumbService.getInstance(element.project).smartInvokeLater {
|
||||
renameLastContextParameter(ktFunction, editor)
|
||||
renameLastContextParameter(ktCallable, editor)
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
@@ -54,11 +57,11 @@ class ConvertReceiverParameterToContextParameterIntention : SelfTargetingIntenti
|
||||
return true
|
||||
}
|
||||
|
||||
private fun renameLastContextParameter(ktFunction: KtNamedFunction, editor: Editor?) {
|
||||
if (!ktFunction.isValid || editor == null || editor.isDisposed) return
|
||||
val lastContextParameter = ktFunction.contextReceiverList?.contextParameters()?.lastOrNull() ?: return
|
||||
private fun renameLastContextParameter(ktCallable: KtCallableDeclaration, editor: Editor?) {
|
||||
if (!ktCallable.isValid || editor == null || editor.isDisposed) return
|
||||
val lastContextParameter = ktCallable.getContextParameters()?.lastOrNull() ?: return
|
||||
editor.caretModel.moveToOffset(lastContextParameter.startOffset)
|
||||
PsiDocumentManager.getInstance(ktFunction.project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
PsiDocumentManager.getInstance(ktCallable.project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
KotlinMemberInplaceRenameHandler().doRename(lastContextParameter, editor, null)
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -14193,6 +14193,11 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("contextProperty.kt")
|
||||
public void testContextProperty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/contextParameterToReceiver/contextProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("contextUsage1.kt")
|
||||
public void testContextUsage1() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/contextParameterToReceiver/contextUsage1.kt");
|
||||
@@ -14386,6 +14391,11 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/contextMemberExtensionFunCallInBody.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("contextValPropertyWithReceiver.kt")
|
||||
public void testContextValPropertyWithReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/contextValPropertyWithReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("explicitExtensionArgument.kt")
|
||||
public void testExplicitExtensionArgument() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/explicitExtensionArgument.kt");
|
||||
@@ -14426,6 +14436,16 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/memberFunCallInBody.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("valPropertyWithReceiver.kt")
|
||||
public void testValPropertyWithReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/valPropertyWithReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("varPropertyWithReceiver.kt")
|
||||
public void testVarPropertyWithReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/varPropertyWithReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withAnotherContextBefore.kt")
|
||||
public void testWithAnotherContextBefore() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/contextParameters/receiverToContextParameter/withAnotherContextBefore.kt");
|
||||
|
||||
+20
-12
@@ -10,13 +10,8 @@ import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.KotlinMethodDesc
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.KotlinParameterInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtContextReceiverList
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.psi.KtParameterList
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifFalse
|
||||
|
||||
object ContextParameterUtils {
|
||||
/**
|
||||
@@ -26,8 +21,7 @@ object ContextParameterUtils {
|
||||
if (!ktParameter.languageVersionSettings.supportsFeature(LanguageFeature.ContextParameters)) return false
|
||||
val contextParameterList = ktParameter.parent as? KtContextReceiverList ?: return false
|
||||
val contextParameterListOwner = contextParameterList.ownerDeclaration
|
||||
// Change Signature support for context properties is required KTIJ-34042
|
||||
if (contextParameterListOwner !is KtNamedFunction) return false
|
||||
if (contextParameterListOwner !is KtCallableDeclaration) return false
|
||||
if (contextParameterListOwner.isOpenAbstractOrOverride()) return false
|
||||
|
||||
return true
|
||||
@@ -36,7 +30,7 @@ object ContextParameterUtils {
|
||||
fun isValueParameterConvertibleToContext(ktParameter: KtParameter): Boolean {
|
||||
if (!ktParameter.languageVersionSettings.supportsFeature(LanguageFeature.ContextParameters)) return false
|
||||
val valueParameterList = ktParameter.parent as? KtParameterList ?: return false
|
||||
val owner = valueParameterList.ownerFunction as? KtNamedFunction ?: return false
|
||||
val owner = valueParameterList.ownerFunction as? KtCallableDeclaration ?: return false
|
||||
if (owner.isOpenAbstractOrOverride()) return false
|
||||
|
||||
return true
|
||||
@@ -47,10 +41,10 @@ object ContextParameterUtils {
|
||||
* The Change Signature refactoring runs with this info if [configureChangeInfo] returns `true`.
|
||||
*/
|
||||
fun runChangeSignatureForParameter(element: KtParameter, configureChangeInfo: (KotlinChangeInfo) -> Boolean) {
|
||||
val ktFunction = element.getStrictParentOfType<KtNamedFunction>() ?: return
|
||||
val methodDescriptor = KotlinMethodDescriptor(ktFunction)
|
||||
val ktCallable = element.getStrictParentOfType<KtCallableDeclaration>() ?: return
|
||||
val methodDescriptor = KotlinMethodDescriptor(ktCallable)
|
||||
val changeInfo = KotlinChangeInfo(methodDescriptor)
|
||||
configureChangeInfo(changeInfo).ifFalse { return }
|
||||
if (!configureChangeInfo(changeInfo)) return
|
||||
KotlinChangeSignatureProcessor(element.project, changeInfo).also {
|
||||
it.prepareSuccessfulSwingThreadCallback = Runnable { }
|
||||
}.run()
|
||||
@@ -72,6 +66,20 @@ object ContextParameterUtils {
|
||||
!it.isContextParameter && it.oldName == ktParameter.name
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for getting context parameters from a callable declaration.
|
||||
* Returns the list of context parameters if the declaration is a function or a property with context parameters, and null otherwise.
|
||||
*
|
||||
* The utility mitigates the awkward declaration of context parameters in the Kotlin PSI hierarchy.
|
||||
*/
|
||||
fun KtCallableDeclaration.getContextParameters(): List<KtParameter>? {
|
||||
return when (this) {
|
||||
is KtNamedFunction -> contextReceiverList?.contextParameters()
|
||||
is KtProperty -> contextReceiverList?.contextParameters()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
// to avoid overrides and overridable declarations KTIJ-34463
|
||||
private fun KtDeclaration.isOpenAbstractOrOverride(): Boolean =
|
||||
isAbstract() || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// COMPILER_ARGUMENTS: -Xcontext-parameters
|
||||
|
||||
context(<caret>c1: String)
|
||||
var prop: Int
|
||||
get() = c1.length
|
||||
set(value) {
|
||||
println(c1)
|
||||
foo()
|
||||
c1.bar()
|
||||
}
|
||||
|
||||
context(c: String)
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
fun String.bar() {
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// COMPILER_ARGUMENTS: -Xcontext-parameters
|
||||
|
||||
var String.prop: Int
|
||||
get() = length
|
||||
set(value) {
|
||||
println(this)
|
||||
foo()
|
||||
bar()
|
||||
}
|
||||
|
||||
context(c: String)
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
fun String.bar() {
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xcontext-parameters
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
context(i: Int)
|
||||
val <caret>String.foo: Int
|
||||
get() = i + length
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xcontext-parameters
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
val <caret>String.foo: Int
|
||||
get() = length
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xcontext-parameters
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
var <caret>String.foo: Int
|
||||
get() = length
|
||||
set(value) { println(this) }
|
||||
Reference in New Issue
Block a user