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);
+ }
+ }
}