From 68b2b14abf90048f3f2a225f98abcd49692bb049 Mon Sep 17 00:00:00 2001 From: "alexey.ivanov" Date: Fri, 7 May 2010 20:41:02 +0400 Subject: [PATCH] improved python statement mover --- .../moveUpDown/StatementMover.java | 94 ++++++++++++++----- python/testData/mover/indent.py | 12 +++ python/testData/mover/indent_afterDown.py | 12 +++ python/testData/mover/indent_afterUp.py | 12 +++ python/testData/mover/outsideStatement.py | 9 +- .../mover/outsideStatement_afterDown.py | 11 ++- .../mover/outsideStatement_afterUp.py | 11 ++- .../python/PyStatementMoverTest.java | 6 +- 8 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 python/testData/mover/indent.py create mode 100644 python/testData/mover/indent_afterDown.py create mode 100644 python/testData/mover/indent_afterUp.py diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/StatementMover.java b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/StatementMover.java index 2c2a0aa0bd2f..0c08152c39c0 100644 --- a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/StatementMover.java +++ b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/StatementMover.java @@ -167,8 +167,8 @@ public class StatementMover extends LineMover { private @Nullable PyStatementList myStatementListToAddPass; private @Nullable PyStatementList myStatementListToRemovePass; - private @NotNull PsiElement[] myElementsToDecreaseIndent; - private @NotNull PsiElement[] myElementsToIncreaseIndent; + private @NotNull PsiElement[] myElementsToChangeIndent; + private int myIndentLevel; @Override public boolean checkAvailable(@NotNull Editor editor, @@ -177,8 +177,9 @@ public class StatementMover extends LineMover { boolean down) { myStatementListToAddPass = null; myStatementListToRemovePass = null; - myElementsToDecreaseIndent = PsiElement.EMPTY_ARRAY; - myElementsToIncreaseIndent = PsiElement.EMPTY_ARRAY; + myElementsToChangeIndent = PsiElement.EMPTY_ARRAY; + myIndentLevel = 0; + if (!(file instanceof PyFile)) { return false; } @@ -229,19 +230,27 @@ public class StatementMover extends LineMover { myStatementListToRemovePass = nextStatementPart.getStatementList(); } else { - final PyStatement parentStatement = PsiTreeUtil.getParentOfType(statementList, PyStatement.class); - if (parentStatement == null) { - return false; + PsiElement parent = statementList; + PyStatement nextStatement; + while (true) { + parent = PsiTreeUtil.getParentOfType(parent, PyStatement.class); + if (parent == null) { + return false; + } + nextStatement = PsiTreeUtil.getNextSiblingOfType(parent, PyStatement.class); + if (nextStatement instanceof PyFunction || nextStatement instanceof PyClass) { + return false; + } + if (nextStatement == null) { + continue; + } + break; } - final PyStatement nextStatement = PsiTreeUtil.getNextSiblingOfType(parentStatement, PyStatement.class); - if (nextStatement == null) { - return false; - } - final int startLine = editor.offsetToLogicalPosition(parentStatement.getTextRange().getEndOffset()).line; + final int startLine = editor.offsetToLogicalPosition(parent.getTextRange().getEndOffset()).line; final int endLine = editor.offsetToLogicalPosition(nextStatement.getTextRange().getEndOffset()).line; info.toMove2 = new LineRange(startLine + 1, endLine + 1); - - myElementsToDecreaseIndent = statements; + calculateIndent(editor, statementList, nextStatement); + myElementsToChangeIndent = statements; } } else { @@ -250,7 +259,8 @@ public class StatementMover extends LineMover { myStatementListToRemovePass = prevStatementPart.getStatementList(); } else { - myElementsToDecreaseIndent = statements; + myIndentLevel = -1; + myElementsToChangeIndent = statements; info.toMove2 = new LineRange(range.startLine - 1, range.startLine); } } @@ -284,15 +294,39 @@ public class StatementMover extends LineMover { if (startLineNumber != endLineNumber) { info.toMove2 = new LineRange(startLineNumber, startLineNumber + 1); myStatementListToRemovePass = statementPart.getStatementList(); - myElementsToIncreaseIndent = statements; + calculateIndent(editor, range.firstElement, myStatementListToRemovePass); + myElementsToChangeIndent = statements; } } return true; } + + private void calculateIndent(final Editor editor, final PsiElement firstElement, final PsiElement secondElement) { + final PsiFile file = firstElement.getContainingFile(); + final int firstIndent = getIndent(editor, file, editor.offsetToLogicalPosition(firstElement.getTextRange().getStartOffset()).line); + final int secondIndent = getIndent(editor, file, editor.offsetToLogicalPosition(secondElement.getTextRange().getEndOffset()).line); + myIndentLevel = (secondIndent - firstIndent) >> 2; + } + + private static int getIndent(final Editor editor, final PsiFile file, final int lineNumber) { + int indent = 0; + final int offset = editor.logicalPositionToOffset(new LogicalPosition(lineNumber, 0)); + PsiElement element = file.findElementAt(offset); + if (element instanceof PsiWhiteSpace) { + final String text = element.getText(); + String[] lines = text.split("\n"); + if (lines.length == 0) { + return 0; + } + indent = lines[lines.length - 1].length(); + } + return indent; + } + private void decreaseIndent(final Editor editor) { final Document document = editor.getDocument(); - for (PsiElement statement : myElementsToDecreaseIndent) { + for (PsiElement statement : myElementsToChangeIndent) { final int startOffset = statement.getTextRange().getStartOffset() - 1; PsiElement element = statement.getContainingFile().findElementAt(startOffset); assert element instanceof PsiWhiteSpace; @@ -305,9 +339,10 @@ public class StatementMover extends LineMover { final int startLine = editor.offsetToLogicalPosition(startOffset).line; final int endLine = editor.offsetToLogicalPosition(statement.getTextRange().getEndOffset()).line; for (int line = startLine; line <= endLine; ++line) { - if (indent >= 4) { + final int indentLevel = myIndentLevel * -4; + if (indent >= 4 && indentLevel <= indent) { final int lineStartOffset = document.getLineStartOffset(line); - document.deleteString(lineStartOffset, lineStartOffset + 4); + document.deleteString(lineStartOffset, lineStartOffset + indentLevel); } } } @@ -315,16 +350,25 @@ public class StatementMover extends LineMover { private void increaseIndent(final Editor editor) { final Document document = editor.getDocument(); - for (PsiElement statement : myElementsToIncreaseIndent) { + String indent = makeIndent(); + for (PsiElement statement : myElementsToChangeIndent) { final int startLine = editor.offsetToLogicalPosition(statement.getTextRange().getStartOffset()).line; final int endLine = editor.offsetToLogicalPosition(statement.getTextRange().getEndOffset()).line; for (int line = startLine; line <= endLine; ++line) { final int offset = document.getLineStartOffset(line); - document.insertString(offset, " "); + document.insertString(offset, indent); } } } + private String makeIndent() { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < myIndentLevel; ++i) { + result.append(" "); + } + return result.toString(); + } + @Override public void beforeMove(@NotNull Editor editor, @NotNull MoveInfo info, boolean down) { super.beforeMove(editor, info, down); @@ -337,8 +381,12 @@ public class StatementMover extends LineMover { info.toMove2 = new LineRange(info.toMove2.startLine, info.toMove2.endLine + 1); } } - decreaseIndent(editor); - increaseIndent(editor); + if (myIndentLevel < 0) { + decreaseIndent(editor); + } + else { + increaseIndent(editor); + } } @Override diff --git a/python/testData/mover/indent.py b/python/testData/mover/indent.py new file mode 100644 index 000000000000..f9686dab3689 --- /dev/null +++ b/python/testData/mover/indent.py @@ -0,0 +1,12 @@ +class A: + def foo(self, a, b, c, d): + if a: + if b: + if c: + if d: + self.bar() + c = 3 + a = 2 + + def bar(self): + pass \ No newline at end of file diff --git a/python/testData/mover/indent_afterDown.py b/python/testData/mover/indent_afterDown.py new file mode 100644 index 000000000000..695849124e67 --- /dev/null +++ b/python/testData/mover/indent_afterDown.py @@ -0,0 +1,12 @@ +class A: + def foo(self, a, b, c, d): + if a: + if b: + if c: + if d: + self.bar() + a = 2 + c = 3 + + def bar(self): + pass \ No newline at end of file diff --git a/python/testData/mover/indent_afterUp.py b/python/testData/mover/indent_afterUp.py new file mode 100644 index 000000000000..dae668039366 --- /dev/null +++ b/python/testData/mover/indent_afterUp.py @@ -0,0 +1,12 @@ +class A: + def foo(self, a, b, c, d): + if a: + if b: + if c: + if d: + c = 3 + self.bar() + a = 2 + + def bar(self): + pass \ No newline at end of file diff --git a/python/testData/mover/outsideStatement.py b/python/testData/mover/outsideStatement.py index 262d3c6b06a0..667d5eb3f6e1 100644 --- a/python/testData/mover/outsideStatement.py +++ b/python/testData/mover/outsideStatement.py @@ -1,6 +1,7 @@ -a = 1 +def foo(): + a = 1 -if a: - a = 3 + if a: + a = 3 -a = 4 \ No newline at end of file + a = 4 \ No newline at end of file diff --git a/python/testData/mover/outsideStatement_afterDown.py b/python/testData/mover/outsideStatement_afterDown.py index dbf351cf442f..186e43937bff 100644 --- a/python/testData/mover/outsideStatement_afterDown.py +++ b/python/testData/mover/outsideStatement_afterDown.py @@ -1,7 +1,8 @@ -a = 1 +def foo(): + a = 1 -if a: - pass + if a: + pass -a = 4 -a = 3 + a = 4 + a = 3 diff --git a/python/testData/mover/outsideStatement_afterUp.py b/python/testData/mover/outsideStatement_afterUp.py index 11eae085d793..1c2c7514183a 100644 --- a/python/testData/mover/outsideStatement_afterUp.py +++ b/python/testData/mover/outsideStatement_afterUp.py @@ -1,7 +1,8 @@ -a = 1 +def foo(): + a = 1 -a = 3 -if a: - pass + a = 3 + if a: + pass -a = 4 \ No newline at end of file + a = 4 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java b/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java index 7aa018ca6202..1e864122df03 100644 --- a/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java +++ b/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java @@ -19,7 +19,7 @@ public class PyStatementMoverTest extends PyLightFixtureTestCase { myFixture.configureByFile("mover/" + testName + ".py"); performAction(new MoveStatementUpAction().getHandler()); myFixture.checkResultByFile("mover/" + testName + "_afterUp.py", true); - + FileDocumentManager.getInstance().reloadFromDisk(myFixture.getDocument(myFixture.getFile())); myFixture.configureByFile("mover/" + getTestName(true) + ".py"); performAction(new MoveStatementDownAction().getHandler()); @@ -68,4 +68,8 @@ public class PyStatementMoverTest extends PyLightFixtureTestCase { public void testPY950() throws Exception { doTest(); } + + public void testIndent() throws Exception { + doTest(); + } }