Move statement: Add or remove empty lines correctly

#KT-14946 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-08-05 19:18:34 +09:00
committed by klunnii
parent 73dec25eb1
commit 8a595ad165
38 changed files with 232 additions and 17 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.codeInsight.upDownMover; package org.jetbrains.kotlin.idea.codeInsight.upDownMover;
import com.intellij.codeInsight.editorActions.moveUpDown.LineRange; import com.intellij.codeInsight.editorActions.moveUpDown.LineRange;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
@@ -36,6 +37,8 @@ import java.util.List;
public class KotlinDeclarationMover extends AbstractKotlinUpDownMover { public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
private boolean moveEnumConstant = false; private boolean moveEnumConstant = false;
private boolean moveOutOfBlock = false;
private boolean moveIntoBlock = false;
private static int findNearestNonWhitespace(@NotNull CharSequence sequence, int index) { private static int findNearestNonWhitespace(@NotNull CharSequence sequence, int index) {
char ch = sequence.charAt(--index); char ch = sequence.charAt(--index);
@@ -48,8 +51,8 @@ public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
@Override @Override
public void afterMove(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) { public void afterMove(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) {
super.afterMove(editor, file, info, down); super.afterMove(editor, file, info, down);
Document document = editor.getDocument();
if (moveEnumConstant) { if (moveEnumConstant) {
Document document = editor.getDocument();
CharSequence cs = document.getCharsSequence(); CharSequence cs = document.getCharsSequence();
int end1 = findNearestNonWhitespace(cs, info.range1.getEndOffset()); int end1 = findNearestNonWhitespace(cs, info.range1.getEndOffset());
char c1 = cs.charAt(end1); char c1 = cs.charAt(end1);
@@ -72,6 +75,27 @@ public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
document.insertString(end1 + 1, ","); document.insertString(end1 + 1, ",");
} }
} }
if (moveIntoBlock || moveOutOfBlock) {
if (down) {
if (moveIntoBlock) {
document.deleteString(info.range1.getEndOffset(), info.range2.getStartOffset());
}
else {
int offset = info.range1.getEndOffset();
document.insertString(offset, "\n");
CaretModel caretModel = editor.getCaretModel();
if (document.getCharsSequence().charAt(caretModel.getOffset() - 1) == '\n') caretModel.moveToOffset(offset + 1);
}
}
else {
if (moveIntoBlock) {
document.deleteString(info.range2.getEndOffset(), info.range1.getStartOffset());
}
else {
document.insertString(info.range2.getEndOffset(), "\n");
}
}
}
} }
@NotNull @NotNull
@@ -228,7 +252,7 @@ public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
PsiElement nextParent = null; PsiElement nextParent = null;
// moving out of code block // moving out of code block
if (sibling.getNode().getElementType() == (down ? KtTokens.RBRACE : KtTokens.LBRACE)) { if (isMovingOutOfBlock(sibling, down)) {
// elements which aren't immediately placed in class body can't leave the block // elements which aren't immediately placed in class body can't leave the block
PsiElement parent = sibling.getParent(); PsiElement parent = sibling.getParent();
if (!(parent instanceof KtClassBody)) return null; if (!(parent instanceof KtClassBody)) return null;
@@ -246,18 +270,14 @@ public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
// moving into code block // moving into code block
// element may move only into class body // element may move only into class body
else { else {
if (sibling instanceof KtClassOrObject) { KtClassBody classBody = getClassBody(sibling);
KtClassOrObject ktClassOrObject = (KtClassOrObject) sibling; // confined elements can't leave their block
KtClassBody classBody = ktClassOrObject.getBody(); if (classBody != null) {
nextParent = classBody;
// confined elements can't leave their block if (!down) {
if (classBody != null) { start = classBody.getRBrace();
nextParent = classBody;
if (!down) {
start = classBody.getRBrace();
}
end = down ? classBody.getLBrace() : classBody.getRBrace();
} }
end = down ? classBody.getLBrace() : classBody.getRBrace();
} }
} }
@@ -279,6 +299,19 @@ public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
return start != null && end != null ? new LineRange(start, end, editor.getDocument()) : null; return start != null && end != null ? new LineRange(start, end, editor.getDocument()) : null;
} }
private static boolean isMovingOutOfBlock(@NotNull PsiElement sibling, boolean down) {
return sibling.getNode().getElementType() == (down ? KtTokens.RBRACE : KtTokens.LBRACE);
}
@Nullable
private static KtClassBody getClassBody(PsiElement element) {
if (element instanceof KtClassOrObject) {
return ((KtClassOrObject) element).getBody();
} else {
return null;
}
}
@Override @Override
public boolean checkAvailable(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) { public boolean checkAvailable(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) {
if (!super.checkAvailable(editor, file, info, down)) return false; if (!super.checkAvailable(editor, file, info, down)) return false;
@@ -306,6 +339,8 @@ public class KotlinDeclarationMover extends AbstractKotlinUpDownMover {
info.toMove2 = null; info.toMove2 = null;
return true; return true;
} }
moveOutOfBlock = isMovingOutOfBlock(sibling, down);
moveIntoBlock = getClassBody(sibling) != null;
info.toMove = sourceRange; info.toMove = sourceRange;
info.toMove2 = getTargetRange(editor, sibling, down, sourceRange.firstElement); info.toMove2 = getTargetRange(editor, sibling, down, sourceRange.firstElement);
@@ -5,6 +5,7 @@ class A {
val x = "" val x = ""
} }
// class B // class B
<caret>class B { <caret>class B {
@@ -2,6 +2,7 @@
<caret>class B { <caret>class B {
} }
// MOVE: up // MOVE: up
// class A // class A
class A { class A {
@@ -4,6 +4,7 @@ class A {
// class B // class B
class B { class B {
} }
// class C // class C
<caret>class C { <caret>class C {
@@ -5,6 +5,7 @@ class A {
<caret>class C { <caret>class C {
} }
// class B // class B
class B { class B {
} }
@@ -4,6 +4,7 @@ fun foo() {
// class A // class A
class A { class A {
} }
// class B // class B
<caret>class B { <caret>class B {
@@ -5,6 +5,7 @@ fun foo() {
<caret>class B { <caret>class B {
} }
// class A // class A
class A { class A {
} }
@@ -1,6 +1,5 @@
// class D // class D
class D { class D {
// MOVE: down // MOVE: down
// class A // class A
<caret>class A <caret>class A
@@ -3,7 +3,6 @@
class D { class D {
// class A // class A
<caret>class A <caret>class A
} }
// class C // class C
@@ -1,6 +1,5 @@
// class D // class D
class D { class D {
// MOVE: down // MOVE: down
// class A // class A
<caret>class A <caret>class A
@@ -3,5 +3,4 @@
class D { class D {
// class A // class A
<caret>class A <caret>class A
} }
@@ -0,0 +1,10 @@
// MOVE: down
class Outer {
<caret>class N1 {
fun foo1() {}
}
class N2 {
fun foo2() {}
}
}
@@ -0,0 +1,9 @@
// MOVE: down
class Outer {
class N2 {
<caret>class N1 {
fun foo1() {}
}
fun foo2() {}
}
}
@@ -0,0 +1,9 @@
// MOVE: down
class Outer {
class N2 {
<caret>class N1 {
fun foo1() {}
}
fun foo2() {}
}
}
@@ -0,0 +1,9 @@
// MOVE: down
class Outer {
class N2 {
fun foo2() {}
<caret>class N1 {
fun foo1() {}
}
}
}
@@ -0,0 +1,9 @@
// MOVE: down
class Outer {
class N2 {
fun foo2() {}
<caret>class N1 {
fun foo1() {}
}
}
}
@@ -0,0 +1,10 @@
// MOVE: down
class Outer {
class N2 {
fun foo2() {}
}
<caret>class N1 {
fun foo1() {}
}
}
@@ -0,0 +1,10 @@
// MOVE: up
class Outer {
class N2 {
fun foo2() {}
}
<caret>class N1 {
fun foo1() {}
}
}
@@ -0,0 +1,9 @@
// MOVE: up
class Outer {
class N2 {
fun foo2() {}
<caret>class N1 {
fun foo1() {}
}
}
}
@@ -0,0 +1,9 @@
// MOVE: up
class Outer {
class N2 {
fun foo2() {}
<caret>class N1 {
fun foo1() {}
}
}
}
@@ -0,0 +1,9 @@
// MOVE: up
class Outer {
class N2 {
<caret>class N1 {
fun foo1() {}
}
fun foo2() {}
}
}
@@ -0,0 +1,9 @@
// MOVE: up
class Outer {
class N2 {
<caret>class N1 {
fun foo1() {}
}
fun foo2() {}
}
}
@@ -0,0 +1,10 @@
// MOVE: up
class Outer {
<caret>class N1 {
fun foo1() {}
}
class N2 {
fun foo2() {}
}
}
@@ -0,0 +1,9 @@
// MOVE: down
class Outer {
class N2 {
fun foo2() {}
<caret> class N1 {
fun foo1() {}
}
}
}
@@ -0,0 +1,10 @@
// MOVE: down
class Outer {
class N2 {
fun foo2() {}
}
<caret> class N1 {
fun foo1() {}
}
}
@@ -5,6 +5,7 @@ class A {
val x = "" val x = ""
} }
// fun foo // fun foo
<caret>fun foo() { <caret>fun foo() {
@@ -2,6 +2,7 @@
<caret>fun foo() { <caret>fun foo() {
} }
// MOVE: up // MOVE: up
// class A // class A
class A { class A {
@@ -4,6 +4,7 @@ class A {
// class B // class B
class B { class B {
} }
// fun foo // fun foo
<caret>fun foo() { <caret>fun foo() {
@@ -5,6 +5,7 @@ class A {
<caret>fun foo() { <caret>fun foo() {
} }
// class B // class B
class B { class B {
} }
@@ -4,6 +4,7 @@ fun foo() {
// class B // class B
class B { class B {
} }
// fun foo // fun foo
<caret>fun foo() { <caret>fun foo() {
@@ -5,6 +5,7 @@ fun foo() {
<caret>fun foo() { <caret>fun foo() {
} }
// class B // class B
class B { class B {
} }
@@ -5,5 +5,6 @@ class A {
val x = "" val x = ""
} }
// val y // val y
<caret>val y = "" <caret>val y = ""
@@ -1,5 +1,6 @@
// val x // val x
<caret>val x = "" <caret>val x = ""
// MOVE: up // MOVE: up
// class A // class A
class A { class A {
@@ -4,6 +4,7 @@ class A {
// class B // class B
class B { class B {
} }
// val y // val y
<caret>val y = "" <caret>val y = ""
} }
@@ -3,6 +3,7 @@
class A { class A {
// val y // val y
<caret>val y = "" <caret>val y = ""
// class B // class B
class B { class B {
} }
@@ -4,6 +4,7 @@ fun foo() {
// class B // class B
class B { class B {
} }
// val y // val y
<caret>val y = "" <caret>val y = ""
} }
@@ -3,6 +3,7 @@
fun foo() { fun foo() {
// val y // val y
<caret>val y = "" <caret>val y = ""
// class B // class B
class B { class B {
} }
@@ -194,6 +194,41 @@ public class MoveStatementTestGenerated extends AbstractMoveStatementTest {
public void testClassWithoutBody4() throws Exception { public void testClassWithoutBody4() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/classWithoutBody4.kt"); runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/classWithoutBody4.kt");
} }
@TestMetadata("kt-14946-1.kt")
public void testKt_14946_1() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-1.kt");
}
@TestMetadata("kt-14946-2.kt")
public void testKt_14946_2() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-2.kt");
}
@TestMetadata("kt-14946-3.kt")
public void testKt_14946_3() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-3.kt");
}
@TestMetadata("kt-14946-4.kt")
public void testKt_14946_4() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-4.kt");
}
@TestMetadata("kt-14946-5.kt")
public void testKt_14946_5() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-5.kt");
}
@TestMetadata("kt-14946-6.kt")
public void testKt_14946_6() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-6.kt");
}
@TestMetadata("kt-14946-7.kt")
public void testKt_14946_7() throws Exception {
runTest("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/class/kt-14946-7.kt");
}
} }
@TestMetadata("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/classInitializer") @TestMetadata("idea/testData/codeInsight/moveUpDown/classBodyDeclarations/classInitializer")