KT-3732 "Move statement" should respect DSLs

This commit is contained in:
Alexey Sedunov
2013-07-09 16:24:18 +04:00
parent 4a19d4f77b
commit 3a881672e8
18 changed files with 210 additions and 9 deletions
@@ -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<JetElement> IS_CALL_EXPRESSION = new Predicate<JetElement>() {
@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<JetExpression> 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;
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
<caret>println("foo")
run(1, 2) {
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
run(1, 2) {
<caret>println("foo")
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
run(1, 2) {
println("bar")
}
<caret>println("foo")
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
run(1, 2) {
println("bar")
<caret>println("foo")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
<caret>println("foo")
val x = run(1, 2) {
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
val x = run(1, 2) {
<caret>println("foo")
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
val x = run(1, 2) {
println("bar")
}
<caret>println("foo")
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
val x = run(1, 2) {
println("bar")
<caret>println("foo")
}
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
run(1, 2) {
<caret>println("foo")
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
<caret>println("foo")
run(1, 2) {
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
run(1, 2) {
println("bar")
<caret>println("foo")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
run(1, 2) {
println("bar")
}
<caret>println("foo")
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
val x = run(1, 2) {
<caret>println("foo")
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: up
fun foo() {
<caret>println("foo")
val x = run(1, 2) {
println("bar")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
val x = run(1, 2) {
println("bar")
<caret>println("foo")
}
}
@@ -0,0 +1,7 @@
// MOVE: down
fun foo() {
val x = run(1, 2) {
println("bar")
}
<caret>println("foo")
}
@@ -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");