Introduce "Add remaining when branches" intention #KT-23306 Fixed

This commit is contained in:
Toshiaki Kameyama
2018-05-11 06:57:25 +03:00
committed by Mikhail Glukhikh
parent bde9a57c9e
commit 2c59f96ca4
17 changed files with 212 additions and 41 deletions
@@ -0,0 +1,12 @@
enum class Entry {
FOO, BAR, BAZ
}
fun test(e: Entry): Int {
return when (e) {
<spot>Entry.FOO -> 1
Entry.BAR -> TODO()
Entry.BAZ -> TODO()
else -> 0</spot>
}
}
@@ -0,0 +1,10 @@
enum class Entry {
FOO, BAR, BAZ
}
fun test(e: Entry): Int {
return when (e) {
<spot>Entry.FOO -> 1
else -> 0</spot>
}
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention adds remaining branches on a <b>when</b> expression.
</body>
</html>
+5
View File
@@ -1633,6 +1633,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
+5
View File
@@ -1633,6 +1633,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
+5
View File
@@ -1633,6 +1633,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
+5
View File
@@ -1634,6 +1634,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
+5
View File
@@ -1633,6 +1633,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
+5
View File
@@ -1633,6 +1633,11 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 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 org.jetbrains.kotlin.idea.quickfix.AddWhenRemainingBranchesFix
import org.jetbrains.kotlin.psi.KtWhenExpression
class AddWhenRemainingBranchesIntention : SelfTargetingIntention<KtWhenExpression>(
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)
}
}
@@ -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<KtWhenExpression>(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()
}
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.AddWhenRemainingBranchesIntention
@@ -0,0 +1,11 @@
// IS_APPLICABLE: false
enum class Entry {
FOO, BAR, BAZ
}
fun test(e: Entry) {
<caret>when (e) {
Entry.FOO -> {}
}
}
@@ -0,0 +1,13 @@
// IS_APPLICABLE: false
enum class Entry {
FOO, BAR, BAZ
}
fun test(e: Entry) {
<caret>when (e) {
Entry.FOO -> {}
Entry.BAR -> {}
Entry.BAZ -> {}
}
}
@@ -0,0 +1,12 @@
// WITH_RUNTIME
enum class Entry {
FOO, BAR, BAZ
}
fun test(e: Entry) {
<caret>when (e) {
Entry.FOO -> {}
else -> {}
}
}
@@ -0,0 +1,14 @@
// WITH_RUNTIME
enum class Entry {
FOO, BAR, BAZ
}
fun test(e: Entry) {
<caret>when (e) {
Entry.FOO -> {}
Entry.BAR -> TODO()
Entry.BAZ -> TODO()
else -> {}
}
}
@@ -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)