diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml
index d858b03ee53..cc029cabf06 100644
--- a/idea/resources/META-INF/plugin-common.xml
+++ b/idea/resources/META-INF/plugin-common.xml
@@ -1732,6 +1732,11 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.RenameClassToContainingFileNameIntention
+ Kotlin
+
+
Bar {
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RenameClassToContainingFileNameIntention/before.kt.template b/idea/resources/intentionDescriptions/RenameClassToContainingFileNameIntention/before.kt.template
new file mode 100644
index 00000000000..b41029b4800
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RenameClassToContainingFileNameIntention/before.kt.template
@@ -0,0 +1,4 @@
+// Bar.kt
+
+class Foo {
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RenameClassToContainingFileNameIntention/description.html b/idea/resources/intentionDescriptions/RenameClassToContainingFileNameIntention/description.html
new file mode 100644
index 00000000000..89aa9f8eb08
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RenameClassToContainingFileNameIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention renames the class to containing file name.
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/RenameClassToContainingFileNameIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/RenameClassToContainingFileNameIntention.kt
new file mode 100644
index 00000000000..66ea6929246
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/RenameClassToContainingFileNameIntention.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.intentions
+
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.util.TextRange
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.refactoring.RefactoringSettings
+import com.intellij.refactoring.rename.RenameProcessor
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.renderer.render
+
+class RenameClassToContainingFileNameIntention : SelfTargetingRangeIntention(
+ KtClassOrObject::class.java, "Rename class to containing file name"
+) {
+ override fun startInWriteAction() = false
+
+ override fun applicabilityRange(element: KtClassOrObject): TextRange? {
+ if (!element.isTopLevel()) return null
+ val fileName = FileUtil.getNameWithoutExtension(element.containingKtFile.name)
+ if (fileName == element.name
+ || fileName[0].isLowerCase()
+ || !Name.isValidIdentifier(fileName)
+ || Name.identifier(fileName).render() != fileName
+ || element.containingKtFile.declarations.any { it is KtClassOrObject && it.name == fileName }
+ ) return null
+ text = "Rename class to $fileName"
+ return element.nameIdentifier?.textRange
+ }
+
+ override fun applyTo(element: KtClassOrObject, editor: Editor?) {
+ val file = element.containingKtFile
+ val fileName = FileUtil.getNameWithoutExtension(element.containingKtFile.name)
+ RenameProcessor(file.project, element, fileName, false, false).run()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/.intention b/idea/testData/intentions/renameClassToContainingFileName/.intention
new file mode 100644
index 00000000000..140bcf8492e
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.RenameClassToContainingFileNameIntention
diff --git a/idea/testData/intentions/renameClassToContainingFileName/Basic.kt b/idea/testData/intentions/renameClassToContainingFileName/Basic.kt
new file mode 100644
index 00000000000..2689a664dcc
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/Basic.kt
@@ -0,0 +1 @@
+class Foo
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/Basic.kt.after b/idea/testData/intentions/renameClassToContainingFileName/Basic.kt.after
new file mode 100644
index 00000000000..db46da87f1b
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/Basic.kt.after
@@ -0,0 +1 @@
+class Basic
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/Basic2.kt b/idea/testData/intentions/renameClassToContainingFileName/Basic2.kt
new file mode 100644
index 00000000000..efbbd4f785f
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/Basic2.kt
@@ -0,0 +1 @@
+object Foo
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/Basic2.kt.after b/idea/testData/intentions/renameClassToContainingFileName/Basic2.kt.after
new file mode 100644
index 00000000000..78cbe5815e0
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/Basic2.kt.after
@@ -0,0 +1 @@
+object Basic2
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/HasSameNameClass.kt b/idea/testData/intentions/renameClassToContainingFileName/HasSameNameClass.kt
new file mode 100644
index 00000000000..c3e085cb778
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/HasSameNameClass.kt
@@ -0,0 +1,4 @@
+// IS_APPLICABLE: false
+class Foo
+
+class HasSameNameClass
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/HasSameNameObject.kt b/idea/testData/intentions/renameClassToContainingFileName/HasSameNameObject.kt
new file mode 100644
index 00000000000..401a352b194
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/HasSameNameObject.kt
@@ -0,0 +1,4 @@
+// IS_APPLICABLE: false
+class Foo
+
+object HasSameNameObject
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/SameName.kt b/idea/testData/intentions/renameClassToContainingFileName/SameName.kt
new file mode 100644
index 00000000000..baf22b6e310
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/SameName.kt
@@ -0,0 +1,2 @@
+// IS_APPLICABLE: false
+class SameName
\ No newline at end of file
diff --git a/idea/testData/intentions/renameClassToContainingFileName/startWithLowerCase.kt b/idea/testData/intentions/renameClassToContainingFileName/startWithLowerCase.kt
new file mode 100644
index 00000000000..fead0993d3b
--- /dev/null
+++ b/idea/testData/intentions/renameClassToContainingFileName/startWithLowerCase.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 1a7122a60f8..e44d5f0be66 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
@@ -14837,6 +14837,49 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
+ @TestMetadata("idea/testData/intentions/renameClassToContainingFileName")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class RenameClassToContainingFileName extends AbstractIntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInRenameClassToContainingFileName() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/renameClassToContainingFileName"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("Basic.kt")
+ public void testBasic() throws Exception {
+ runTest("idea/testData/intentions/renameClassToContainingFileName/Basic.kt");
+ }
+
+ @TestMetadata("Basic2.kt")
+ public void testBasic2() throws Exception {
+ runTest("idea/testData/intentions/renameClassToContainingFileName/Basic2.kt");
+ }
+
+ @TestMetadata("HasSameNameClass.kt")
+ public void testHasSameNameClass() throws Exception {
+ runTest("idea/testData/intentions/renameClassToContainingFileName/HasSameNameClass.kt");
+ }
+
+ @TestMetadata("HasSameNameObject.kt")
+ public void testHasSameNameObject() throws Exception {
+ runTest("idea/testData/intentions/renameClassToContainingFileName/HasSameNameObject.kt");
+ }
+
+ @TestMetadata("SameName.kt")
+ public void testSameName() throws Exception {
+ runTest("idea/testData/intentions/renameClassToContainingFileName/SameName.kt");
+ }
+
+ @TestMetadata("startWithLowerCase.kt")
+ public void testStartWithLowerCase() throws Exception {
+ runTest("idea/testData/intentions/renameClassToContainingFileName/startWithLowerCase.kt");
+ }
+ }
+
@TestMetadata("idea/testData/intentions/replaceAddWithPlusAssign")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)