Intentions: Implement intention to replace camel-case test function name with a space-separated one
#KT-12489 Fixed (cherry picked from commit 21e24a1)
This commit is contained in:
@@ -258,6 +258,7 @@ Using 'this' as function argument in constructor of non-final class
|
|||||||
- [`KT-7492`](https://youtrack.jetbrains.com/issue/KT-7492) Support "Create abstract function/property" inside an abstract class
|
- [`KT-7492`](https://youtrack.jetbrains.com/issue/KT-7492) Support "Create abstract function/property" inside an abstract class
|
||||||
- [`KT-10668`](https://youtrack.jetbrains.com/issue/KT-10668) Support "Create member/extension" corresponding to the extension receiver of enclosing function
|
- [`KT-10668`](https://youtrack.jetbrains.com/issue/KT-10668) Support "Create member/extension" corresponding to the extension receiver of enclosing function
|
||||||
- [`KT-12553`](https://youtrack.jetbrains.com/issue/KT-12553) Show versions in inspection about different version of Kotlin plugin in Maven and IDE plugin
|
- [`KT-12553`](https://youtrack.jetbrains.com/issue/KT-12553) Show versions in inspection about different version of Kotlin plugin in Maven and IDE plugin
|
||||||
|
- [`KT-12489`](https://youtrack.jetbrains.com/issue/KT-12489) Implement intention to replace camel-case test function name with a space-separated one
|
||||||
- [`KT-12730`](https://youtrack.jetbrains.com/issue/KT-12730) Warn about using different versions of Kotlin Gradle plugin and bundled compiler
|
- [`KT-12730`](https://youtrack.jetbrains.com/issue/KT-12730) Warn about using different versions of Kotlin Gradle plugin and bundled compiler
|
||||||
- [`KT-13173`](https://youtrack.jetbrains.com/issue/KT-13173) Handle more cases in "Add Const Modifier" Intention
|
- [`KT-13173`](https://youtrack.jetbrains.com/issue/KT-13173) Handle more cases in "Add Const Modifier" Intention
|
||||||
- [`KT-12628`](https://youtrack.jetbrains.com/issue/KT-12628) Quickfix for `invoke` operator unsafe calls
|
- [`KT-12628`](https://youtrack.jetbrains.com/issue/KT-12628) Quickfix for `invoke` operator unsafe calls
|
||||||
|
|||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
class MyTest {
|
||||||
|
@Test fun <spot>`test two plus two equals four`</spot>() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
class MyTest {
|
||||||
|
@Test fun <spot>testTwoPlusTwoEqualsFour</spot>() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention changes camel-case name of the given test function to space-separated one
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1336,6 +1336,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.ConvertCamelCaseTestFunctionToSpacedIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||||
displayName="Object literal can be converted to lambda"
|
displayName="Object literal can be converted to lambda"
|
||||||
groupName="Kotlin"
|
groupName="Kotlin"
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.TestFrameworks
|
||||||
|
import com.intellij.codeInsight.template.Template
|
||||||
|
import com.intellij.codeInsight.template.TemplateBuilderImpl
|
||||||
|
import com.intellij.codeInsight.template.TemplateEditingAdapter
|
||||||
|
import com.intellij.codeInsight.template.TemplateManager
|
||||||
|
import com.intellij.codeInsight.template.impl.TemplateImpl
|
||||||
|
import com.intellij.codeInsight.template.impl.TemplateState
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.refactoring.rename.RenameProcessor
|
||||||
|
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||||
|
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
||||||
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
|
||||||
|
class ConvertCamelCaseTestFunctionToSpacedIntention : SelfTargetingRangeIntention<KtNamedFunction>(
|
||||||
|
KtNamedFunction::class.java, "Replace camel-case name with spaces"
|
||||||
|
) {
|
||||||
|
override fun applicabilityRange(element: KtNamedFunction): TextRange? {
|
||||||
|
val range = element.nameIdentifier?.textRange ?: return null
|
||||||
|
|
||||||
|
val name = element.name ?: return null
|
||||||
|
val newName = decamelize(name)
|
||||||
|
if (newName == name) return null
|
||||||
|
|
||||||
|
val lightMethod = element.toLightMethods().firstOrNull() ?: return null
|
||||||
|
if (!TestFrameworks.getInstance().isTestMethod(lightMethod)) return null
|
||||||
|
|
||||||
|
text = "Rename to $newName"
|
||||||
|
|
||||||
|
return range
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Case {
|
||||||
|
LOWER, UPPER, OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun splitCamelName(name: String): List<String> {
|
||||||
|
if (name === "") return emptyList()
|
||||||
|
|
||||||
|
val result = SmartList<String>()
|
||||||
|
var previousCase = Case.OTHER
|
||||||
|
var from = 0
|
||||||
|
for (i in 0..name.length - 1) {
|
||||||
|
val c = name[i]
|
||||||
|
val currentCase = when {
|
||||||
|
Character.isUpperCase(c) -> Case.UPPER
|
||||||
|
Character.isLowerCase(c) -> Case.LOWER
|
||||||
|
else -> Case.OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
i == name.lastIndex -> result += name.substring(from)
|
||||||
|
i > 0 && currentCase != previousCase && currentCase != Case.LOWER -> {
|
||||||
|
result += name.substring(from, i)
|
||||||
|
from = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousCase = currentCase
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decamelize(name: String) = splitCamelName(name).joinToString(separator = " ") { it.decapitalizeSmart().trim() }.quoteIfNeeded()
|
||||||
|
|
||||||
|
private fun startRename(element: KtNamedFunction, newName: String) {
|
||||||
|
RenameProcessor(element.project, element, newName, false, false).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startInWriteAction() = false
|
||||||
|
|
||||||
|
override fun applyTo(element: KtNamedFunction, editor: Editor?) {
|
||||||
|
val nameIdentifier = element.nameIdentifier ?: return
|
||||||
|
val oldName = element.name ?: return
|
||||||
|
val oldId = oldName.quoteIfNeeded()
|
||||||
|
val newId = decamelize(oldName)
|
||||||
|
|
||||||
|
if (editor != null) {
|
||||||
|
val builder = TemplateBuilderImpl(nameIdentifier)
|
||||||
|
builder.replaceElement(nameIdentifier, newId)
|
||||||
|
val template = runWriteAction { builder.buildInlineTemplate() }
|
||||||
|
TemplateManager.getInstance(element.project).startTemplate(
|
||||||
|
editor,
|
||||||
|
template,
|
||||||
|
object : TemplateEditingAdapter() {
|
||||||
|
private var chosenId: String = newId
|
||||||
|
private var range: TextRange? = null
|
||||||
|
|
||||||
|
override fun beforeTemplateFinished(state: TemplateState?, template: Template?) {
|
||||||
|
val varName = (template as? TemplateImpl)?.getVariableNameAt(0) ?: return
|
||||||
|
chosenId = state?.getVariableValue(varName)?.text?.quoteIfNeeded() ?: return
|
||||||
|
range = state?.getVariableRange(varName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun templateFinished(template: Template?, brokenOff: Boolean) {
|
||||||
|
range?.let {
|
||||||
|
val doc = editor.document
|
||||||
|
runWriteAction { doc.replaceString(it.startOffset, it.endOffset, oldId) }
|
||||||
|
PsiDocumentManager.getInstance(element.project).commitDocument(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!brokenOff && chosenId != oldId) {
|
||||||
|
startRename(element, chosenId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startRename(element, newId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.ConvertCamelCaseTestFunctionToSpacedIntention
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||||
|
// ERROR: Cannot access class 'java.lang.Class'. Check your module classpath for missing or conflicting dependencies
|
||||||
|
// ERROR: This annotation is not applicable to target 'member function'
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@Test fun <caret>testTwoPlusTwoEqualsFour() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A().testTwoPlusTwoEqualsFour()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
class A {
|
||||||
|
val <caret>foo = 1
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||||
|
// ERROR: Cannot access class 'java.lang.Class'. Check your module classpath for missing or conflicting dependencies
|
||||||
|
// ERROR: This annotation is not applicable to target 'member function'
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@Test fun <caret>`testTwo + Two==Four`() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A().`testTwo + Two==Four`()
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
class A {
|
||||||
|
<caret>fun foo() {}
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
class A {
|
||||||
|
fun <caret>foo() {}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||||
|
// ERROR: Cannot access class 'java.lang.Class'. Check your module classpath for missing or conflicting dependencies
|
||||||
|
// ERROR: This annotation is not applicable to target 'member function'
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@Test fun `<caret>foo bar`() {}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
|||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||||
|
import com.intellij.testFramework.PlatformTestUtil;
|
||||||
import com.intellij.util.PathUtil;
|
import com.intellij.util.PathUtil;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import com.intellij.util.containers.Convertor;
|
import com.intellij.util.containers.Convertor;
|
||||||
@@ -145,6 +146,8 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase {
|
|||||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(getModule(), PluginTestCaseBase.mockJdk());
|
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(getModule(), PluginTestCaseBase.mockJdk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigLibraryUtil.configureLibrariesByDirective(getModule(), PlatformTestUtil.getCommunityPath(), fileText);
|
||||||
|
|
||||||
if (getFile() instanceof KtFile && !InTextDirectivesUtils.isDirectiveDefined(fileText, "// SKIP_ERRORS_BEFORE")) {
|
if (getFile() instanceof KtFile && !InTextDirectivesUtils.isDirectiveDefined(fileText, "// SKIP_ERRORS_BEFORE")) {
|
||||||
DirectiveBasedActionUtils.INSTANCE.checkForUnexpectedErrors((KtFile) getFile());
|
DirectiveBasedActionUtils.INSTANCE.checkForUnexpectedErrors((KtFile) getFile());
|
||||||
}
|
}
|
||||||
@@ -156,6 +159,7 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
ConfigLibraryUtil.unconfigureLibrariesByDirective(myModule, fileText);
|
||||||
if (isWithRuntime) {
|
if (isWithRuntime) {
|
||||||
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(getModule(), getTestProjectJdk());
|
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(getModule(), getTestProjectJdk());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3509,6 +3509,33 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/intentions/convertCamelCaseTestFunctionToSpaced")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ConvertCamelCaseTestFunctionToSpaced extends AbstractIntentionTest {
|
||||||
|
public void testAllFilesPresentInConvertCamelCaseTestFunctionToSpaced() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertCamelCaseTestFunctionToSpaced"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nonFunction.kt")
|
||||||
|
public void testNonFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertCamelCaseTestFunctionToSpaced/nonFunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notAtIdentifier.kt")
|
||||||
|
public void testNotAtIdentifier() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertCamelCaseTestFunctionToSpaced/notAtIdentifier.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notTestFunction.kt")
|
||||||
|
public void testNotTestFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertCamelCaseTestFunctionToSpaced/notTestFunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/intentions/convertForEachToForLoop")
|
@TestMetadata("idea/testData/intentions/convertForEachToForLoop")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user