diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/upDownMover/JetExpressionMover.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/upDownMover/JetExpressionMover.java index 1b3a968d083..450ffc07ace 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/upDownMover/JetExpressionMover.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/upDownMover/JetExpressionMover.java @@ -13,8 +13,17 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lexer.JetTokens; +import java.util.List; + public class JetExpressionMover extends AbstractJetUpDownMover { + private static final Predicate IS_CALL_EXPRESSION = new Predicate() { + @Override + public boolean apply(@Nullable JetElement input) { + return input instanceof JetCallExpression; + } + }; + public JetExpressionMover() { } @@ -149,14 +158,14 @@ public class JetExpressionMover extends AbstractJetUpDownMover { } @Nullable - private static JetBlockExpression findClosestBlock(@NotNull PsiElement anchor, boolean down) { - PsiElement current = PsiTreeUtil.getParentOfType(anchor, JetBlockExpression.class); + private static JetBlockExpression findClosestBlock(@NotNull PsiElement anchor, boolean down, boolean strict) { + PsiElement current = PsiTreeUtil.getParentOfType(anchor, JetBlockExpression.class, strict); while (current != null) { PsiElement parent = current.getParent(); if (parent instanceof JetClassBody || parent instanceof JetClassInitializer || parent instanceof JetNamedFunction || - parent instanceof JetProperty) { + (parent instanceof JetProperty && !((JetProperty) parent).isLocal())) { return null; } @@ -178,6 +187,18 @@ public class JetExpressionMover extends AbstractJetUpDownMover { return null; } + @Nullable + private static JetBlockExpression getDSLLambdaBlock(@NotNull PsiElement element, boolean down) { + JetCallExpression callExpression = + (JetCallExpression) JetPsiUtil.getOutermostDescendantElement(element, down, IS_CALL_EXPRESSION); + if (callExpression == null) return null; + + List functionLiterals = callExpression.getFunctionLiteralArguments(); + if (functionLiterals.isEmpty()) return null; + + return ((JetFunctionLiteralExpression) functionLiterals.get(0)).getBodyExpression(); + } + @Nullable private static LineRange getExpressionTargetRange(@NotNull Editor editor, @NotNull PsiElement sibling, boolean down) { PsiElement start = sibling; @@ -188,7 +209,14 @@ public class JetExpressionMover extends AbstractJetUpDownMover { PsiElement parent = sibling.getParent(); if (!(parent instanceof JetBlockExpression || parent instanceof JetFunctionLiteral)) return null; - JetBlockExpression newBlock = findClosestBlock(sibling, down); + JetBlockExpression newBlock; + if (parent instanceof JetFunctionLiteral) { + //noinspection ConstantConditions + newBlock = findClosestBlock(((JetFunctionLiteral) parent).getBodyExpression(), down, false); + } else { + newBlock = findClosestBlock(sibling, down, true); + } + if (newBlock == null) return null; if (PsiTreeUtil.isAncestor(newBlock, parent, true)) { @@ -210,10 +238,19 @@ public class JetExpressionMover extends AbstractJetUpDownMover { } } } + // moving into code block else { - // moving into code block - //noinspection unchecked - JetElement blockLikeElement = JetPsiUtil.getOutermostDescendantElement(sibling, down, CHECK_BLOCK_LIKE_ELEMENT); + PsiElement blockLikeElement; + + JetBlockExpression dslBlock = getDSLLambdaBlock(sibling, down); + if (dslBlock != null) { + // Use JetFunctionLiteral (since it contains braces) + blockLikeElement = dslBlock.getParent(); + } else { + // JetBlockExpression and other block-like elements + blockLikeElement = JetPsiUtil.getOutermostDescendantElement(sibling, down, CHECK_BLOCK_LIKE_ELEMENT); + } + if (blockLikeElement != null) { if (down) { end = JetPsiUtil.findChildByType(blockLikeElement, JetTokens.LBRACE); @@ -331,7 +368,7 @@ public class JetExpressionMover extends AbstractJetUpDownMover { return block.getLBrace() == null && block.getRBrace() == null; } - protected static PsiElement adjustWhiteSpaceSibling( + protected static PsiElement adjustSibling( @NotNull Editor editor, @NotNull LineRange sourceRange, @NotNull MoveInfo info, @@ -371,6 +408,18 @@ public class JetExpressionMover extends AbstractJetUpDownMover { } if (sibling == null) { + JetCallExpression callExpression = PsiTreeUtil.getParentOfType(element, JetCallExpression.class); + if (callExpression != null) { + JetBlockExpression dslBlock = getDSLLambdaBlock(callExpression, down); + if (PsiTreeUtil.isAncestor(dslBlock, element, false)) { + //noinspection ConstantConditions + PsiElement blockParent = dslBlock.getParent(); + return down + ? JetPsiUtil.findChildByType(blockParent, JetTokens.RBRACE) + : JetPsiUtil.findChildByType(blockParent, JetTokens.LBRACE); + } + } + info.toMove2 = null; return null; } @@ -419,7 +468,7 @@ public class JetExpressionMover extends AbstractJetUpDownMover { LineRange sourceRange = getSourceRange(firstElement, lastElement, editor, oldRange); if (sourceRange == null) return false; - PsiElement sibling = adjustWhiteSpaceSibling(editor, sourceRange, info, down); + PsiElement sibling = adjustSibling(editor, sourceRange, info, down); // Either reached last sibling, or jumped over multi-line whitespace if (sibling == null) return true; diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt new file mode 100644 index 00000000000..e5162bdf63b --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + println("foo") + run(1, 2) { + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt.after new file mode 100644 index 00000000000..02cc59683e8 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt.after @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + run(1, 2) { + println("foo") + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt new file mode 100644 index 00000000000..efc77f761bd --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + run(1, 2) { + println("bar") + } + println("foo") +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt.after new file mode 100644 index 00000000000..cbf22757cf7 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt.after @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + run(1, 2) { + println("bar") + println("foo") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt new file mode 100644 index 00000000000..e09920ecb45 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + println("foo") + val x = run(1, 2) { + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt.after new file mode 100644 index 00000000000..aec40a2d8f0 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt.after @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + val x = run(1, 2) { + println("foo") + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt new file mode 100644 index 00000000000..0751420910b --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + val x = run(1, 2) { + println("bar") + } + println("foo") +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt.after new file mode 100644 index 00000000000..86257cc7805 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt.after @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + val x = run(1, 2) { + println("bar") + println("foo") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt new file mode 100644 index 00000000000..ef61d689f65 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + run(1, 2) { + println("foo") + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt.after new file mode 100644 index 00000000000..86830ae2c63 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt.after @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + println("foo") + run(1, 2) { + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt new file mode 100644 index 00000000000..5f58bc8b907 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + run(1, 2) { + println("bar") + println("foo") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt.after new file mode 100644 index 00000000000..651ec08a4c9 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt.after @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + run(1, 2) { + println("bar") + } + println("foo") +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt new file mode 100644 index 00000000000..d1f804b1c56 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + val x = run(1, 2) { + println("foo") + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt.after new file mode 100644 index 00000000000..c814aaf68e3 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt.after @@ -0,0 +1,7 @@ +// MOVE: up +fun foo() { + println("foo") + val x = run(1, 2) { + println("bar") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt new file mode 100644 index 00000000000..8120deeb4e0 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + val x = run(1, 2) { + println("bar") + println("foo") + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt.after b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt.after new file mode 100644 index 00000000000..b032fc4e944 --- /dev/null +++ b/idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt.after @@ -0,0 +1,7 @@ +// MOVE: down +fun foo() { + val x = run(1, 2) { + println("bar") + } + println("foo") +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/codeInsight/moveUpDown/CodeMoverTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/codeInsight/moveUpDown/CodeMoverTestGenerated.java index 6979a50b0b2..7666e80dbd4 100644 --- a/idea/tests/org/jetbrains/jet/plugin/codeInsight/moveUpDown/CodeMoverTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/codeInsight/moveUpDown/CodeMoverTestGenerated.java @@ -847,6 +847,26 @@ public class CodeMoverTestGenerated extends AbstractCodeMoverTest { doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/if4.kt"); } + @TestMetadata("intoClosure1.kt") + public void testIntoClosure1() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/intoClosure1.kt"); + } + + @TestMetadata("intoClosure2.kt") + public void testIntoClosure2() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/intoClosure2.kt"); + } + + @TestMetadata("intoNestedClosure1.kt") + public void testIntoNestedClosure1() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure1.kt"); + } + + @TestMetadata("intoNestedClosure2.kt") + public void testIntoNestedClosure2() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/intoNestedClosure2.kt"); + } + @TestMetadata("lambda1.kt") public void testLambda1() throws Exception { doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/lambda1.kt"); @@ -862,6 +882,26 @@ public class CodeMoverTestGenerated extends AbstractCodeMoverTest { doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/lambda3.kt"); } + @TestMetadata("outOfClosure1.kt") + public void testOutOfClosure1() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/outOfClosure1.kt"); + } + + @TestMetadata("outOfClosure2.kt") + public void testOutOfClosure2() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/outOfClosure2.kt"); + } + + @TestMetadata("outOfNestedClosure1.kt") + public void testOutOfNestedClosure1() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure1.kt"); + } + + @TestMetadata("outOfNestedClosure2.kt") + public void testOutOfNestedClosure2() throws Exception { + doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/outOfNestedClosure2.kt"); + } + @TestMetadata("when1.kt") public void testWhen1() throws Exception { doTestExpression("idea/testData/codeInsight/moveUpDown/expressions/when1.kt");