"Convert object literal to class" should create inner class if necessary #KT-20091 Fixed

This commit is contained in:
Toshiaki Kameyama
2017-09-14 21:09:59 +09:00
committed by asedunov
parent 299a4b7f69
commit e76d8bc793
10 changed files with 132 additions and 1 deletions
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiComment
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.replaced
@@ -33,6 +35,9 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -65,6 +70,13 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
val targetSibling = element.parentsWithSelf.first { it.parent == targetParent }
val objectDeclaration = element.objectDeclaration
val containingClass = element.containingClass()
val hasMemberReference = containingClass?.getBody()?.allChildren?.any {
(it is KtProperty || it is KtNamedFunction) &&
ReferencesSearch.search(it, element.useScope).findFirst() != null
} ?: false
val newClass = psiFactory.createClass("class $className")
objectDeclaration.getSuperTypeList()?.let {
newClass.add(psiFactory.createColon())
@@ -104,6 +116,7 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
}
}
functionDeclaration.replaced(newClass).apply {
if (hasMemberReference && containingClass == (parent.parent as? KtClass)) addModifier(KtTokens.INNER_KEYWORD)
primaryConstructor?.let { CodeStyleManager.getInstance(project).reformat(it) }
}
}
@@ -116,7 +129,11 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
val containers = element.getExtractionContainers(strict = true, includeAll = true)
if (ApplicationManager.getApplication().isUnitTestMode) {
return doApply(editor, element, containers.last())
val targetComment = element.containingKtFile.findDescendantOfType<PsiComment>()?.takeIf {
it.text == "// TARGET_BLOCK:"
}
val target = containers.firstOrNull { it == targetComment?.parent } ?: containers.last()
return doApply(editor, element, target)
}
chooseContainerElementIfNecessary(
@@ -0,0 +1,8 @@
class Test { // TARGET_BLOCK:
fun foo() {
<caret>object : Runnable {
override fun run() {
}
}
}
}
@@ -0,0 +1,10 @@
class Test { // TARGET_BLOCK:
fun foo() {
O()
}
class O : Runnable {
override fun run() {
}
}
}
@@ -0,0 +1,11 @@
class Test { // TARGET_BLOCK:
fun method() = 1
fun foo() {
<caret>object : Runnable {
override fun run() {
method()
}
}
}
}
@@ -0,0 +1,13 @@
class Test { // TARGET_BLOCK:
fun method() = 1
fun foo() {
O()
}
inner class O : Runnable {
override fun run() {
method()
}
}
}
@@ -0,0 +1,11 @@
class Test { // TARGET_BLOCK:
var field = 1
fun foo() {
<caret>object : Runnable {
override fun run() {
field = 2
}
}
}
}
@@ -0,0 +1,13 @@
class Test { // TARGET_BLOCK:
var field = 1
fun foo() {
O()
}
inner class O : Runnable {
override fun run() {
field = 2
}
}
}
@@ -0,0 +1,11 @@
class Test {
var field = 1
fun foo() { // TARGET_BLOCK:
<caret>object : Runnable {
override fun run() {
field = 2
}
}
}
}
@@ -0,0 +1,13 @@
class Test {
var field = 1
fun foo() { // TARGET_BLOCK:
class O : Runnable {
override fun run() {
field = 2
}
}
O()
}
}
@@ -5390,6 +5390,30 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertObjectLiteralToClass"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("inClass.kt")
public void testInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertObjectLiteralToClass/inClass.kt");
doTest(fileName);
}
@TestMetadata("inClassHasMethodReference.kt")
public void testInClassHasMethodReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertObjectLiteralToClass/inClassHasMethodReference.kt");
doTest(fileName);
}
@TestMetadata("inClassHasPropertyReference.kt")
public void testInClassHasPropertyReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertObjectLiteralToClass/inClassHasPropertyReference.kt");
doTest(fileName);
}
@TestMetadata("inFunction.kt")
public void testInFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertObjectLiteralToClass/inFunction.kt");
doTest(fileName);
}
@TestMetadata("objectLiteralNoCapture.kt")
public void testObjectLiteralNoCapture() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertObjectLiteralToClass/objectLiteralNoCapture.kt");