diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt index 280efc07315..714ef3ca2b1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.editor.Editor +import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -21,21 +22,23 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.CONSTRUCTOR import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType +import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.parents -class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention( - KtPrimaryConstructor::class.java, +class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingRangeIntention( + KtClass::class.java, KotlinBundle.lazyMessage("convert.to.secondary.constructor") ) { - override fun isApplicableTo(element: KtPrimaryConstructor, caretOffset: Int): Boolean { - val containingClass = element.containingClassOrObject as? KtClass ?: return false - if (containingClass.isAnnotation() || containingClass.isData() - || containingClass.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry } - ) return false - return element.valueParameters.all { !it.hasValOrVar() || (it.name != null && it.annotationEntries.isEmpty()) } + override fun applicabilityRange(element: KtClass): TextRange? { + val primaryCtor = element.primaryConstructor ?: return null + val startOffset = + (if (primaryCtor.getConstructorKeyword() != null) primaryCtor else element.nameIdentifier)?.startOffset ?: return null + if (element.isAnnotation() || element.isData() || element.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }) return null + if (primaryCtor.valueParameters.any { it.hasValOrVar() && (it.name == null || it.annotationEntries.isNotEmpty()) }) return null + return TextRange(startOffset, primaryCtor.endOffset) } private fun KtReferenceExpression.isIndependent(classDescriptor: ClassDescriptor, context: BindingContext): Boolean = @@ -54,15 +57,15 @@ class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention { !it.isIndependent(classDescriptor, context) } } - override fun applyTo(element: KtPrimaryConstructor, editor: Editor?) { - val klass = element.containingClassOrObject as? KtClass ?: return - if (klass.isAnnotation()) return - val context = klass.analyze() - val factory = KtPsiFactory(klass) - val commentSaver = CommentSaver(element) + override fun applyTo(element: KtClass, editor: Editor?) { + val primaryCtor = element.primaryConstructor ?: return + if (element.isAnnotation()) return + val context = element.analyze() + val factory = KtPsiFactory(element) + val commentSaver = CommentSaver(primaryCtor) val initializerMap = mutableMapOf() - for (property in klass.getProperties()) { - if (property.isIndependent(klass, context)) continue + for (property in element.getProperties()) { + if (property.isIndependent(element, context)) continue if (property.typeReference == null) { with(SpecifyTypeExplicitlyIntention()) { if (applicabilityRange(property) != null) { @@ -77,10 +80,10 @@ class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention + stmt.text } ?: "" } } @@ -124,20 +127,20 @@ class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention{ + fun bar() {} +} \ No newline at end of file diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt new file mode 100644 index 00000000000..70c4386d3b3 --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt @@ -0,0 +1 @@ +class Foo(val x: Int) \ No newline at end of file diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt.after b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt.after new file mode 100644 index 00000000000..1d5ddae62fb --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt.after @@ -0,0 +1,7 @@ +class Foo { + val x: Int + + constructor(x: Int) { + this.x = x + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithConstructorKeyword.kt b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithConstructorKeyword.kt new file mode 100644 index 00000000000..5a604301014 --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithConstructorKeyword.kt @@ -0,0 +1,2 @@ +// IS_APPLICABLE: false +class Foo constructor() \ No newline at end of file diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithoutPrimaryConstructor.kt b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithoutPrimaryConstructor.kt new file mode 100644 index 00000000000..6f2363e340a --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithoutPrimaryConstructor.kt @@ -0,0 +1,2 @@ +// IS_APPLICABLE: false +class Foo \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 373bc9190b0..db1b2b4dc6f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -5962,6 +5962,26 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/convertPrimaryConstructorToSecondary/annotationClass.kt"); } + @TestMetadata("caretOnClassBody.kt") + public void testCaretOnClassBody() throws Exception { + runTest("idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassBody.kt"); + } + + @TestMetadata("caretOnClassName.kt") + public void testCaretOnClassName() throws Exception { + runTest("idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassName.kt"); + } + + @TestMetadata("caretOnClassNameWithConstructorKeyword.kt") + public void testCaretOnClassNameWithConstructorKeyword() throws Exception { + runTest("idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithConstructorKeyword.kt"); + } + + @TestMetadata("caretOnClassNameWithoutPrimaryConstructor.kt") + public void testCaretOnClassNameWithoutPrimaryConstructor() throws Exception { + runTest("idea/testData/intentions/convertPrimaryConstructorToSecondary/caretOnClassNameWithoutPrimaryConstructor.kt"); + } + @TestMetadata("dataClass.kt") public void testDataClass() throws Exception { runTest("idea/testData/intentions/convertPrimaryConstructorToSecondary/dataClass.kt");