Extract Function: Add support of initializer expressions
This commit is contained in:
+19
-2
@@ -53,6 +53,11 @@ import com.intellij.openapi.ui.popup.Balloon.Position
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
||||
import java.util.Collections
|
||||
import org.jetbrains.jet.lang.psi.JetProperty
|
||||
import org.jetbrains.jet.lang.psi.JetParameterList
|
||||
import org.jetbrains.jet.lang.psi.JetClassInitializer
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
|
||||
public class ExtractKotlinFunctionHandler(public val allContainersEnabled: Boolean = false) : RefactoringActionHandler {
|
||||
fun doInvoke(
|
||||
@@ -178,7 +183,13 @@ fun selectElements(
|
||||
val declaration = element.getParentByType(javaClass<JetDeclaration>(), strict)
|
||||
if (declaration == null) return Collections.emptyList()
|
||||
|
||||
val parent = declaration.getParent()
|
||||
val parent = declaration.getParent()?.let {
|
||||
when (it) {
|
||||
is JetProperty -> it.getParent()
|
||||
is JetParameterList -> it.getParent()?.getParent()
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
return when (parent) {
|
||||
is JetFile -> Collections.singletonList(parent)
|
||||
is JetClassBody -> {
|
||||
@@ -189,7 +200,13 @@ fun selectElements(
|
||||
.dropWhile { it !is JetClassBody }
|
||||
}
|
||||
else -> {
|
||||
val targetContainer = parent?.getParentByType(javaClass<JetDeclarationWithBody>())?.getBodyExpression()
|
||||
val enclosingDeclaration =
|
||||
PsiTreeUtil.getNonStrictParentOfType(parent, javaClass<JetDeclarationWithBody>(), javaClass<JetClassInitializer>())
|
||||
val targetContainer = when (enclosingDeclaration) {
|
||||
is JetDeclarationWithBody -> enclosingDeclaration.getBodyExpression()
|
||||
is JetClassInitializer -> enclosingDeclaration.getBody()
|
||||
else -> null
|
||||
}
|
||||
if (targetContainer is JetBlockExpression) Collections.singletonList(targetContainer) else Collections.emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.jet.lang.psi.JetPsiFactory
|
||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
import org.jetbrains.jet.lang.psi.JetClassInitializer
|
||||
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getResolvedCall
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
||||
|
||||
@@ -79,7 +80,9 @@ class ExtractionData(
|
||||
) {
|
||||
val project: Project = originalFile.getProject()
|
||||
|
||||
val insertBefore: Boolean = targetSibling.getParentByType(javaClass<JetDeclaration>(), true) is JetDeclarationWithBody
|
||||
val insertBefore: Boolean = targetSibling.getParentByType(javaClass<JetDeclaration>(), true)?.let {
|
||||
it is JetDeclarationWithBody || it is JetClassInitializer
|
||||
} ?: false
|
||||
|
||||
fun getExpressions(): List<JetExpression> = originalElements.filterIsInstance(javaClass<JetExpression>())
|
||||
|
||||
|
||||
+19
-8
@@ -541,17 +541,28 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.NO_EXPRESSION))
|
||||
}
|
||||
|
||||
val noContainerError = AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.NO_CONTAINER))
|
||||
|
||||
val commonParent = PsiTreeUtil.findCommonParent(originalElements)!!
|
||||
|
||||
val enclosingDeclaration = commonParent.getParentByType(javaClass<JetDeclarationWithBody>())
|
||||
if (enclosingDeclaration == null) {
|
||||
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.NO_CONTAINER))
|
||||
val enclosingDeclaration = commonParent.getParentByType(javaClass<JetDeclaration>(), true)
|
||||
val bodyElement = when (enclosingDeclaration) {
|
||||
is JetDeclarationWithBody -> enclosingDeclaration.getBodyExpression()
|
||||
is JetWithExpressionInitializer -> enclosingDeclaration.getInitializer()
|
||||
is JetParameter -> enclosingDeclaration.getDefaultValue()
|
||||
is JetClassInitializer -> enclosingDeclaration.getBody()
|
||||
is JetClass -> {
|
||||
if (commonParent.isInsideOf(enclosingDeclaration.getDelegationSpecifiers())) enclosingDeclaration else return noContainerError
|
||||
}
|
||||
else -> return noContainerError
|
||||
}
|
||||
val bindingContext = originalFile.getLazyResolveSession().resolveToElement(bodyElement)
|
||||
|
||||
val resolveSession = originalFile.getLazyResolveSession()
|
||||
val bindingContext = resolveSession.resolveToElement(enclosingDeclaration.getBodyExpression())
|
||||
|
||||
val pseudocode = PseudocodeUtil.generatePseudocode(enclosingDeclaration, bindingContext)
|
||||
val pseudocodeDeclaration = PsiTreeUtil.getParentOfType(
|
||||
commonParent, javaClass<JetDeclarationWithBody>(), javaClass<JetClassOrObject>()
|
||||
) ?: commonParent.getParentByType(javaClass<JetProperty>())
|
||||
?: return noContainerError
|
||||
val pseudocode = PseudocodeUtil.generatePseudocode(pseudocodeDeclaration, bindingContext)
|
||||
val localInstructions = getLocalInstructions(pseudocode)
|
||||
|
||||
val replacementMap = HashMap<Int, Replacement>()
|
||||
@@ -587,7 +598,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
val functionNameValidator =
|
||||
JetNameValidatorImpl(
|
||||
targetSibling.getParent(),
|
||||
targetSibling,
|
||||
if (targetSibling is JetClassInitializer) targetSibling.getParent() else targetSibling,
|
||||
JetNameValidatorImpl.Target.FUNCTIONS_AND_CLASSES
|
||||
)
|
||||
val functionName = JetNameSuggester.suggestNames(controlFlow.returnType, functionNameValidator, DEFAULT_FUNCTION_NAME).first()
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class A(val a: Int, val b: Int) {
|
||||
val foo: Int get() = <selection>a + b</selection> - 1
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class A(val a: Int, val b: Int) {
|
||||
val foo: Int get() = i() - 1
|
||||
|
||||
fun i(): Int {
|
||||
return a + b
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
val a = 1
|
||||
val b = 1
|
||||
val foo: Int get() = <selection>a + b</selection> - 1
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
val a = 1
|
||||
val b = 1
|
||||
val foo: Int get() = i() - 1
|
||||
|
||||
fun i(): Int {
|
||||
return a + b
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val a: Int, b: Int) {
|
||||
{
|
||||
println(<selection>a + b</selection> - 1)
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val a: Int, b: Int) {
|
||||
{
|
||||
println(i(b) - 1)
|
||||
}
|
||||
|
||||
fun i(b: Int): Int {
|
||||
return a + b
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
val n = 1
|
||||
class A(val a: Int, val b: Int = <selection>a + n</selection>)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
val n = 1
|
||||
class A(val a: Int, val b: Int = i(a))
|
||||
|
||||
fun i(a: Int): Int {
|
||||
return a + n
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
fun bar(n: Int) {
|
||||
fun foo(a: Int, b: Int) = <selection>a + b - n</selection> - 1
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
fun bar(n: Int) {
|
||||
fun i(a: Int, b: Int): Int {
|
||||
return a + b - n
|
||||
}
|
||||
|
||||
fun foo(a: Int, b: Int) = i(a, b) - 1
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
fun bar(n: Int) {
|
||||
fun foo(a: Int, b: Int = <selection>a + n</selection>) = a + b - n - 1
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Cannot extract method since following types are not denotable in the target scope: [ERROR : ]
|
||||
@@ -0,0 +1,5 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val n: Int) {
|
||||
fun foo(a: Int, b: Int) = <selection>a + b - n</selection> - 1
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val n: Int) {
|
||||
fun foo(a: Int, b: Int) = i(a, b) - 1
|
||||
|
||||
fun i(a: Int, b: Int): Int {
|
||||
return a + b - n
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val n: Int) {
|
||||
fun foo(a: Int, b: Int = <selection>a + n</selection>) = a + b - n - 1
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val n: Int) {
|
||||
fun foo(a: Int, b: Int = i(a)) = a + b - n - 1
|
||||
|
||||
fun i(a: Int): Int {
|
||||
return a + n
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
fun foo(a: Int, b: Int) = <selection>a + b</selection> - 1
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
fun foo(a: Int, b: Int) = i(a, b) - 1
|
||||
|
||||
fun i(a: Int, b: Int): Int {
|
||||
return a + b
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
val n = 1
|
||||
fun foo(a: Int, b: Int = <selection>a + n</selection>) = a + b - 1
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
val n = 1
|
||||
fun foo(a: Int, b: Int = i(a)) = a + b - 1
|
||||
|
||||
fun i(a: Int): Int {
|
||||
return a + n
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(a: Int, b: Int) {
|
||||
val foo = <selection>a + b</selection> - 1
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun bar(a: Int, b: Int) {
|
||||
fun i(): Int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
val foo = i() - 1
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val a: Int, b: Int) {
|
||||
val foo = <selection>a + b</selection> - 1
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
class A(val a: Int, b: Int) {
|
||||
val foo = i(b) - 1
|
||||
|
||||
fun i(b: Int): Int {
|
||||
return a + b
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
val a = 1
|
||||
val b = 1
|
||||
val foo = <selection>a + b</selection> - 1
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
val a = 1
|
||||
val b = 1
|
||||
val foo = i() - 1
|
||||
|
||||
fun i(): Int {
|
||||
return a + b
|
||||
}
|
||||
+117
-1
@@ -211,7 +211,7 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction")
|
||||
@InnerTestClasses({ExtractFunction.Basic.class, ExtractFunction.ControlFlow.class, ExtractFunction.DefaultContainer.class, ExtractFunction.Parameters.class, ExtractFunction.TypeParameters.class})
|
||||
@InnerTestClasses({ExtractFunction.Basic.class, ExtractFunction.ControlFlow.class, ExtractFunction.DefaultContainer.class, ExtractFunction.Initializers.class, ExtractFunction.Parameters.class, ExtractFunction.TypeParameters.class})
|
||||
public static class ExtractFunction extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInExtractFunction() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/refactoring/extractFunction"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
@@ -685,6 +685,121 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/initializers")
|
||||
@InnerTestClasses({Initializers.Accessors.class, Initializers.Classes.class, Initializers.Functions.class, Initializers.Properties.class})
|
||||
public static class Initializers extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInInitializers() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/refactoring/extractFunction/initializers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/initializers/accessors")
|
||||
public static class Accessors extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInAccessors() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/refactoring/extractFunction/initializers/accessors"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("memberProperty.kt")
|
||||
public void testMemberProperty() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/accessors/memberProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/accessors/topLevelProperty.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/initializers/classes")
|
||||
public static class Classes extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInClasses() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/refactoring/extractFunction/initializers/classes"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("classInitializer.kt")
|
||||
public void testClassInitializer() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/classes/classInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classParameters.kt")
|
||||
public void testClassParameters() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/classes/classParameters.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/initializers/functions")
|
||||
public static class Functions extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInFunctions() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/refactoring/extractFunction/initializers/functions"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("localFunction.kt")
|
||||
public void testLocalFunction() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/functions/localFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localFunctionParameters.kt")
|
||||
public void testLocalFunctionParameters() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/functions/localFunctionParameters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("memberFunction.kt")
|
||||
public void testMemberFunction() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/functions/memberFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("memberFunctionParameters.kt")
|
||||
public void testMemberFunctionParameters() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/functions/memberFunctionParameters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunction.kt")
|
||||
public void testTopLevelFunction() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/functions/topLevelFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunctionParameters.kt")
|
||||
public void testTopLevelFunctionParameters() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/functions/topLevelFunctionParameters.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/initializers/properties")
|
||||
public static class Properties extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInProperties() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/refactoring/extractFunction/initializers/properties"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("localProperty.kt")
|
||||
public void testLocalProperty() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/properties/localProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("memberProperty.kt")
|
||||
public void testMemberProperty() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/properties/memberProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
doExtractFunctionTest("idea/testData/refactoring/extractFunction/initializers/properties/topLevelProperty.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Test innerSuite() {
|
||||
TestSuite suite = new TestSuite("Initializers");
|
||||
suite.addTestSuite(Initializers.class);
|
||||
suite.addTestSuite(Accessors.class);
|
||||
suite.addTestSuite(Classes.class);
|
||||
suite.addTestSuite(Functions.class);
|
||||
suite.addTestSuite(Properties.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/parameters")
|
||||
@InnerTestClasses({Parameters.CandidateTypes.class, Parameters.ExtractSuper.class, Parameters.ExtractThis.class, Parameters.It.class, Parameters.Misc.class, Parameters.NonDenotableTypes.class})
|
||||
public static class Parameters extends AbstractJetExtractionTest {
|
||||
@@ -1022,6 +1137,7 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
suite.addTestSuite(Basic.class);
|
||||
suite.addTest(ControlFlow.innerSuite());
|
||||
suite.addTestSuite(DefaultContainer.class);
|
||||
suite.addTest(Initializers.innerSuite());
|
||||
suite.addTest(Parameters.innerSuite());
|
||||
suite.addTestSuite(TypeParameters.class);
|
||||
return suite;
|
||||
|
||||
Reference in New Issue
Block a user