From 2c59f96ca48adc761ca925ffdda23a06d304bc19 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 11 May 2018 06:57:25 +0300 Subject: [PATCH] Introduce "Add remaining when branches" intention #KT-23306 Fixed --- .../after.kt.template | 12 +++ .../before.kt.template | 10 ++ .../description.html | 5 + idea/src/META-INF/plugin.xml | 5 + idea/src/META-INF/plugin.xml.172 | 5 + idea/src/META-INF/plugin.xml.173 | 5 + idea/src/META-INF/plugin.xml.182 | 5 + idea/src/META-INF/plugin.xml.as31 | 5 + idea/src/META-INF/plugin.xml.as32 | 5 + .../AddWhenRemainingBranchesIntention.kt | 23 +++++ .../quickfix/AddWhenRemainingBranchesFix.kt | 94 +++++++++++-------- .../addWhenRemainingBranches/.intention | 1 + .../addWhenRemainingBranches/noElse.kt | 11 +++ .../noRemainingBranches.kt | 13 +++ .../addWhenRemainingBranches/simple.kt | 12 +++ .../addWhenRemainingBranches/simple.kt.after | 14 +++ .../intentions/IntentionTestGenerated.java | 28 ++++++ 17 files changed, 212 insertions(+), 41 deletions(-) create mode 100644 idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/AddWhenRemainingBranchesIntention.kt create mode 100644 idea/testData/intentions/addWhenRemainingBranches/.intention create mode 100644 idea/testData/intentions/addWhenRemainingBranches/noElse.kt create mode 100644 idea/testData/intentions/addWhenRemainingBranches/noRemainingBranches.kt create mode 100644 idea/testData/intentions/addWhenRemainingBranches/simple.kt create mode 100644 idea/testData/intentions/addWhenRemainingBranches/simple.kt.after diff --git a/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/after.kt.template b/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/after.kt.template new file mode 100644 index 00000000000..18050671af0 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/after.kt.template @@ -0,0 +1,12 @@ +enum class Entry { + FOO, BAR, BAZ +} + +fun test(e: Entry): Int { + return when (e) { + Entry.FOO -> 1 + Entry.BAR -> TODO() + Entry.BAZ -> TODO() + else -> 0 + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/before.kt.template b/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/before.kt.template new file mode 100644 index 00000000000..1821a49ec2c --- /dev/null +++ b/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/before.kt.template @@ -0,0 +1,10 @@ +enum class Entry { + FOO, BAR, BAZ +} + +fun test(e: Entry): Int { + return when (e) { + Entry.FOO -> 1 + else -> 0 + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/description.html b/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/description.html new file mode 100644 index 00000000000..4680d1b1d19 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddWhenRemainingBranchesIntention/description.html @@ -0,0 +1,5 @@ + + +This intention adds remaining branches on a when expression. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 3ca61100277..2084a17b018 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1633,6 +1633,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. Kotlin + + org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention + Kotlin + + Kotlin + + org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention + Kotlin + + ( + KtWhenExpression::class.java, "Add remaining branches" +) { + override fun isApplicableTo(element: KtWhenExpression, caretOffset: Int): Boolean { + if (element.entries.none { it.isElse }) return false + return AddWhenRemainingBranchesFix.isAvailable(element) + } + + override fun applyTo(element: KtWhenExpression, editor: Editor?) { + AddWhenRemainingBranchesFix.addRemainingBranches(element) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt index 6a56036e34a..81a005148ea 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddWhenRemainingBranchesFix.kt @@ -31,8 +31,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult class AddWhenRemainingBranchesFix( - expression: KtWhenExpression, - val withImport: Boolean = false + expression: KtWhenExpression, + val withImport: Boolean = false ) : KotlinQuickFixAction(expression) { override fun getFamilyName() = text @@ -40,48 +40,11 @@ class AddWhenRemainingBranchesFix( override fun getText() = "Add remaining branches" + if (withImport) " with import" else "" override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean { - val element = element ?: return false - return element.closeBrace != null && - with(WhenChecker.getMissingCases(element, element.analyze())) { isNotEmpty() && !hasUnknown } + return isAvailable(element) } override fun invoke(project: Project, editor: Editor?, file: KtFile) { - val element = element ?: return - val missingCases = WhenChecker.getMissingCases(element, element.analyze()) - - val whenCloseBrace = element.closeBrace ?: throw AssertionError("isAvailable should check if close brace exist") - val psiFactory = KtPsiFactory(file) - - for (case in missingCases) { - val branchConditionText = when (case) { - UnknownMissingCase, NullMissingCase, is BooleanMissingCase -> - case.branchConditionText - is ClassMissingCase -> - if (case.classIsSingleton) { - case.classFqName.quoteIfNeeded().asString() - } - else { - "is " + case.classFqName.quoteIfNeeded().asString() - } - } - val entry = psiFactory.createWhenEntry("$branchConditionText -> TODO()") - element.addBefore(entry, whenCloseBrace) - } - - if (withImport) { - importAllEntries(element) - } - } - - private fun importAllEntries(element: KtWhenExpression) { - with (ImportAllMembersIntention) { - element.entries - .map { it.conditions.toList() } - .flatten() - .firstNotNullResult { - (it as? KtWhenConditionWithExpression)?.expression as? KtDotQualifiedExpression - }?.importReceiverMembers() - } + addRemainingBranches(element, withImport) } companion object : KotlinIntentionActionsFactory() { @@ -99,5 +62,54 @@ class AddWhenRemainingBranchesFix( } return actions } + + fun isAvailable(element: KtWhenExpression?): Boolean { + if (element == null) return false + return element.closeBrace != null && + with(WhenChecker.getMissingCases(element, element.analyze())) { isNotEmpty() && !hasUnknown } + } + + fun addRemainingBranches(element: KtWhenExpression?, withImport: Boolean = false) { + if (element == null) return + val missingCases = WhenChecker.getMissingCases(element, element.analyze()) + + val whenCloseBrace = element.closeBrace ?: throw AssertionError("isAvailable should check if close brace exist") + val elseBranch = element.entries.find { it.isElse } + val psiFactory = KtPsiFactory(element) + + for (case in missingCases) { + val branchConditionText = when (case) { + UnknownMissingCase, NullMissingCase, is BooleanMissingCase -> + case.branchConditionText + is ClassMissingCase -> + if (case.classIsSingleton) { + case.classFqName.quoteIfNeeded().asString() + } else { + "is " + case.classFqName.quoteIfNeeded().asString() + } + } + val entry = psiFactory.createWhenEntry("$branchConditionText -> TODO()") + if (elseBranch != null) { + element.addBefore(entry, elseBranch) + } else { + element.addBefore(entry, whenCloseBrace) + } + } + + if (withImport) { + importAllEntries(element) + } + } + + private fun importAllEntries(element: KtWhenExpression) { + with(ImportAllMembersIntention) { + element.entries + .map { it.conditions.toList() } + .flatten() + .firstNotNullResult { + (it as? KtWhenConditionWithExpression)?.expression as? KtDotQualifiedExpression + }?.importReceiverMembers() + } + } } } \ No newline at end of file diff --git a/idea/testData/intentions/addWhenRemainingBranches/.intention b/idea/testData/intentions/addWhenRemainingBranches/.intention new file mode 100644 index 00000000000..5ddf71e23da --- /dev/null +++ b/idea/testData/intentions/addWhenRemainingBranches/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention \ No newline at end of file diff --git a/idea/testData/intentions/addWhenRemainingBranches/noElse.kt b/idea/testData/intentions/addWhenRemainingBranches/noElse.kt new file mode 100644 index 00000000000..70f62167247 --- /dev/null +++ b/idea/testData/intentions/addWhenRemainingBranches/noElse.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +enum class Entry { + FOO, BAR, BAZ +} + +fun test(e: Entry) { + when (e) { + Entry.FOO -> {} + } +} \ No newline at end of file diff --git a/idea/testData/intentions/addWhenRemainingBranches/noRemainingBranches.kt b/idea/testData/intentions/addWhenRemainingBranches/noRemainingBranches.kt new file mode 100644 index 00000000000..b2eb648df55 --- /dev/null +++ b/idea/testData/intentions/addWhenRemainingBranches/noRemainingBranches.kt @@ -0,0 +1,13 @@ +// IS_APPLICABLE: false + +enum class Entry { + FOO, BAR, BAZ +} + +fun test(e: Entry) { + when (e) { + Entry.FOO -> {} + Entry.BAR -> {} + Entry.BAZ -> {} + } +} \ No newline at end of file diff --git a/idea/testData/intentions/addWhenRemainingBranches/simple.kt b/idea/testData/intentions/addWhenRemainingBranches/simple.kt new file mode 100644 index 00000000000..1c9eeb45a0b --- /dev/null +++ b/idea/testData/intentions/addWhenRemainingBranches/simple.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME + +enum class Entry { + FOO, BAR, BAZ +} + +fun test(e: Entry) { + when (e) { + Entry.FOO -> {} + else -> {} + } +} \ No newline at end of file diff --git a/idea/testData/intentions/addWhenRemainingBranches/simple.kt.after b/idea/testData/intentions/addWhenRemainingBranches/simple.kt.after new file mode 100644 index 00000000000..c8924b4b8ac --- /dev/null +++ b/idea/testData/intentions/addWhenRemainingBranches/simple.kt.after @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +enum class Entry { + FOO, BAR, BAZ +} + +fun test(e: Entry) { + when (e) { + Entry.FOO -> {} + Entry.BAR -> TODO() + Entry.BAZ -> TODO() + else -> {} + } +} \ 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 4809d8e4ac7..0f8b7e91db6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -1671,6 +1671,34 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/addWhenRemainingBranches") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddWhenRemainingBranches extends AbstractIntentionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInAddWhenRemainingBranches() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addWhenRemainingBranches"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("noElse.kt") + public void testNoElse() throws Exception { + runTest("idea/testData/intentions/addWhenRemainingBranches/noElse.kt"); + } + + @TestMetadata("noRemainingBranches.kt") + public void testNoRemainingBranches() throws Exception { + runTest("idea/testData/intentions/addWhenRemainingBranches/noRemainingBranches.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("idea/testData/intentions/addWhenRemainingBranches/simple.kt"); + } + } + @TestMetadata("idea/testData/intentions/anonymousFunctionToLambda") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)