diff --git a/idea/resources/intentionDescriptions/ValToObjectIntention/after.kt.template b/idea/resources/intentionDescriptions/ValToObjectIntention/after.kt.template new file mode 100644 index 00000000000..82bf7a15b76 --- /dev/null +++ b/idea/resources/intentionDescriptions/ValToObjectIntention/after.kt.template @@ -0,0 +1,9 @@ +interface B { + fun c() +} + + +object a : B { + override fun c() { + } +} diff --git a/idea/resources/intentionDescriptions/ValToObjectIntention/before.kt.template b/idea/resources/intentionDescriptions/ValToObjectIntention/before.kt.template new file mode 100644 index 00000000000..853634a21fb --- /dev/null +++ b/idea/resources/intentionDescriptions/ValToObjectIntention/before.kt.template @@ -0,0 +1,9 @@ +interface B { + fun c() +} + + +val a = object: B { + override fun c() { + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ValToObjectIntention/description.html b/idea/resources/intentionDescriptions/ValToObjectIntention/description.html new file mode 100644 index 00000000000..48c2a6556d9 --- /dev/null +++ b/idea/resources/intentionDescriptions/ValToObjectIntention/description.html @@ -0,0 +1,5 @@ + + +Converts a top-level property that is initialized with an object expression to an object declaration. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 55ee89547d5..71f57ed11e3 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1574,6 +1574,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ValToObjectIntention + Kotlin + + (KtProperty::class.java, "Convert to object declaration") { + + override fun isApplicableTo(element: KtProperty, caretOffset: Int): Boolean { + if (element.isVar) return false + if (!element.isTopLevel) return false + + val initializer = element.initializer as? KtObjectLiteralExpression ?: return false + if (initializer.objectDeclaration.getBody() == null) return false + + if (element.getter != null) return false + if (element.annotationEntries.isNotEmpty()) return false + + // disable if has non-Kotlin usages + return ReferencesSearch.search(element).all { it is KtReference && it.element.parent !is KtCallableReferenceExpression } + } + + override fun applyTo(element: KtProperty, editor: Editor?) { + val modifier = element.visibilityModifier() + val name = element.name ?: return + val objectLiteral = element.initializer as? KtObjectLiteralExpression ?: return + val declaration = objectLiteral.objectDeclaration + val superTypeList = declaration.getSuperTypeList() + val body = declaration.getBody() ?: return + + val prefix = modifier?.text?.plus(" ") ?: "" + val superTypesText = superTypeList?.text?.plus(" ") ?: "" + + val replacementText = "${prefix}object $name: $superTypesText${body.text}" + val replaced = element.replaced(KtPsiFactory(element).createDeclarationByPattern(replacementText)) + + editor?.caretModel?.moveToOffset(replaced.nameIdentifier?.endOffset ?: return) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/.intention b/idea/testData/intentions/valToObject/.intention new file mode 100644 index 00000000000..c4289714dd6 --- /dev/null +++ b/idea/testData/intentions/valToObject/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ValToObjectIntention \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/annotations.kt b/idea/testData/intentions/valToObject/annotations.kt new file mode 100644 index 00000000000..c6b438c431e --- /dev/null +++ b/idea/testData/intentions/valToObject/annotations.kt @@ -0,0 +1,10 @@ +// IS_APPLICABLE: false + +interface B { +} + +annotation class Ann + +@Ann +val a = object : B { +} \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/callableReference.kt b/idea/testData/intentions/valToObject/callableReference.kt new file mode 100644 index 00000000000..0a859f0b655 --- /dev/null +++ b/idea/testData/intentions/valToObject/callableReference.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +interface B { +} + +val a = object : B { +} + +fun foo() { + val ref = ::a +} diff --git a/idea/testData/intentions/valToObject/getter.kt b/idea/testData/intentions/valToObject/getter.kt new file mode 100644 index 00000000000..7dc93b51ad2 --- /dev/null +++ b/idea/testData/intentions/valToObject/getter.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false + +interface B { +} + +val a = object : B { +} + get() = field diff --git a/idea/testData/intentions/valToObject/nonTopLevel.kt b/idea/testData/intentions/valToObject/nonTopLevel.kt new file mode 100644 index 00000000000..bd4db1682d3 --- /dev/null +++ b/idea/testData/intentions/valToObject/nonTopLevel.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +interface B { +} + +class Foo { + val a = object : B { + } +} \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/simple.kt b/idea/testData/intentions/valToObject/simple.kt new file mode 100644 index 00000000000..660242a398f --- /dev/null +++ b/idea/testData/intentions/valToObject/simple.kt @@ -0,0 +1,7 @@ +interface B { +} + +val a = object : B { +} + +val c = a \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/simple.kt.after b/idea/testData/intentions/valToObject/simple.kt.after new file mode 100644 index 00000000000..dc65ef1eb0f --- /dev/null +++ b/idea/testData/intentions/valToObject/simple.kt.after @@ -0,0 +1,7 @@ +interface B { +} + +object a : B { +} + +val c = a \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/var.kt b/idea/testData/intentions/valToObject/var.kt new file mode 100644 index 00000000000..3419d9dfe42 --- /dev/null +++ b/idea/testData/intentions/valToObject/var.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +interface B { +} + +var a = object : B { +} diff --git a/idea/testData/intentions/valToObject/withJavaUsage.1.java b/idea/testData/intentions/valToObject/withJavaUsage.1.java new file mode 100644 index 00000000000..1673be2b571 --- /dev/null +++ b/idea/testData/intentions/valToObject/withJavaUsage.1.java @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +import test.WithJavaUsageKt + +class A { + void b() { + WithJavaUsageKt.getA(); + } +} \ No newline at end of file diff --git a/idea/testData/intentions/valToObject/withJavaUsage.kt b/idea/testData/intentions/valToObject/withJavaUsage.kt new file mode 100644 index 00000000000..cce2d3ee430 --- /dev/null +++ b/idea/testData/intentions/valToObject/withJavaUsage.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +package test + +interface B { +} + +val a = object : B { +} \ 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 032c2da192c..73dc2300eb6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -15616,4 +15616,55 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } } + + @TestMetadata("idea/testData/intentions/valToObject") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValToObject extends AbstractIntentionTest { + public void testAllFilesPresentInValToObject() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/valToObject"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("annotations.kt") + public void testAnnotations() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/annotations.kt"); + doTest(fileName); + } + + @TestMetadata("callableReference.kt") + public void testCallableReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/callableReference.kt"); + doTest(fileName); + } + + @TestMetadata("getter.kt") + public void testGetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/getter.kt"); + doTest(fileName); + } + + @TestMetadata("nonTopLevel.kt") + public void testNonTopLevel() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/nonTopLevel.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/simple.kt"); + doTest(fileName); + } + + @TestMetadata("var.kt") + public void testVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/var.kt"); + doTest(fileName); + } + + @TestMetadata("withJavaUsage.kt") + public void testWithJavaUsage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/valToObject/withJavaUsage.kt"); + doTest(fileName); + } + } }