From 5fba7db3802df336b8f56468fe18b13601f57e24 Mon Sep 17 00:00:00 2001 From: "andrey.matveev" Date: Fri, 26 Feb 2021 23:33:52 +0700 Subject: [PATCH] PY-41375 PY-37765 Fix selection problem in PyStatementMover Also fix unchecked exception in PY-37765 (cherry picked from commit 3b8f3ca228d8e649a63d277314bf13994b80ffd3) IJ-MR-6623 GitOrigin-RevId: 22026406e912c057bb7d81cb59d7a4e21da6f0f5 --- .../moveUpDown/PyStatementMover.java | 41 +++++++++++++++---- ...electionDifferentLevelsMoveIntoFunction.py | 11 +++++ ...fferentLevelsMoveIntoFunction_afterDown.py | 12 ++++++ ...DifferentLevelsMoveIntoFunction_afterUp.py | 11 +++++ ...ctionDifferentLevelsMoveOutFromFunction.py | 4 ++ ...rentLevelsMoveOutFromFunction_afterDown.py | 5 +++ ...ferentLevelsMoveOutFromFunction_afterUp.py | 5 +++ ...ifferentLevelsMoveOutFromNestedFunction.py | 5 +++ ...velsMoveOutFromNestedFunction_afterDown.py | 6 +++ ...LevelsMoveOutFromNestedFunction_afterUp.py | 6 +++ .../singleLineSelectionOutFromFunction.py | 2 + ...eLineSelectionOutFromFunction_afterDown.py | 3 ++ ...gleLineSelectionOutFromFunction_afterUp.py | 3 ++ .../python/PyStatementMoverTest.java | 20 +++++++++ 14 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterDown.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterUp.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterDown.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterUp.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterDown.py create mode 100644 python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterUp.py create mode 100644 python/testData/mover/singleLineSelectionOutFromFunction.py create mode 100644 python/testData/mover/singleLineSelectionOutFromFunction_afterDown.py create mode 100644 python/testData/mover/singleLineSelectionOutFromFunction_afterUp.py diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java index a1f04eaa57ff..4525afaa26e7 100644 --- a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java +++ b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java @@ -341,6 +341,8 @@ public class PyStatementMover extends LineMover { final CaretModel caretModel = editor.getCaretModel(); final int selectionStart = selectionModel.getSelectionStart(); + final LogicalPosition selectionBeforeMoveStart = editor.offsetToLogicalPosition(selectionStart); + final LogicalPosition selectionBeforeMoveEnd = editor.offsetToLogicalPosition(selectionModel.getSelectionEnd()); boolean isSelectionStartAtCaret = caretModel.getOffset() == selectionStart; final SelectionContainer selectionLen = getSelectionLenContainer(editor, ((MyLineRange)toMove)); @@ -354,8 +356,12 @@ public class PyStatementMover extends LineMover { else { offset = moveInOut(((MyLineRange)toMove), editor, info); } + final LogicalPosition positionOffsetAfterMove = editor.offsetToLogicalPosition(offset); restoreCaretAndSelection(file, editor, isSelectionStartAtCaret, hasSelection, selectionLen, - shift, offset, (MyLineRange)toMove); + shift, offset, (MyLineRange)toMove, + selectionBeforeMoveStart, + selectionBeforeMoveEnd, + positionOffsetAfterMove); info.toMove2 = info.toMove; //do not move further }); } @@ -388,7 +394,10 @@ public class PyStatementMover extends LineMover { private static void restoreCaretAndSelection(@NotNull final PsiFile file, @NotNull final Editor editor, boolean selectionStartAtCaret, boolean hasSelection, @NotNull final SelectionContainer selectionContainer, int shift, - int offset, @NotNull final MyLineRange toMove) { + int offset, @NotNull final MyLineRange toMove, + LogicalPosition selectionBeforeMoveStart, + LogicalPosition selectionBeforeMoveEnd, + LogicalPosition positionOffsetAfterMove) { final Document document = editor.getDocument(); final SelectionModel selectionModel = editor.getSelectionModel(); final CaretModel caretModel = editor.getCaretModel(); @@ -427,13 +436,31 @@ public class PyStatementMover extends LineMover { caretModel.moveToOffset(newCaretOffset); if (hasSelection) { - if (selectionStartAtCaret) { - int newSelectionEnd = newCaretOffset + selectionLen; - selectionModel.setSelection(newCaretOffset, newSelectionEnd); + int selectionLinesDiff = selectionBeforeMoveEnd.line - selectionBeforeMoveStart.line; + if (selectionLinesDiff > 1) { + int endOffsetColumn = selectionBeforeMoveEnd.column; + if (endOffsetColumn > 0) endOffsetColumn += positionOffsetAfterMove.column - selectionBeforeMoveStart.column; + int startOffsetColumn = positionOffsetAfterMove.column; + if (selectionBeforeMoveStart.column == 0) startOffsetColumn = 0; + int startOffset = editor.logicalPositionToOffset(new LogicalPosition(positionOffsetAfterMove.line, startOffsetColumn)); + int endOffset = editor.logicalPositionToOffset(new LogicalPosition(positionOffsetAfterMove.line + selectionLinesDiff, endOffsetColumn)); + selectionModel.setSelection(startOffset, endOffset); + if (selectionStartAtCaret) { + caretModel.moveToOffset(startOffset); + } + else { + caretModel.moveToOffset(endOffset); + } } else { - int newSelectionStart = newCaretOffset - selectionLen; - selectionModel.setSelection(newSelectionStart, newCaretOffset); + if (selectionStartAtCaret) { + int newSelectionEnd = newCaretOffset + selectionLen; + selectionModel.setSelection(newCaretOffset, newSelectionEnd); + } + else { + int newSelectionStart = newCaretOffset - selectionLen; + selectionModel.setSelection(newSelectionStart, newCaretOffset); + } } } } diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction.py new file mode 100644 index 000000000000..7e0e8233ca66 --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction.py @@ -0,0 +1,11 @@ +import numpy as np + + +def split_by_words(X): + X = np.core.chararray.lower(X) + return np.core.chararray.split(X) +DELIMITERS = "!?:;,.\'-+/\\()" + +def parse(string): + return "".join((" " if char in DELIMITERS else char) for char in string).split() + \ No newline at end of file diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterDown.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterDown.py new file mode 100644 index 000000000000..534fefb52e10 --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterDown.py @@ -0,0 +1,12 @@ +import numpy as np + + +def split_by_words(X): + X = np.core.chararray.lower(X) + return np.core.chararray.split(X) + +DELIMITERS = "!?:;,.\'-+/\\()" + +def parse(string): + return "".join((" " if char in DELIMITERS else char) for char in string).split() + \ No newline at end of file diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterUp.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterUp.py new file mode 100644 index 000000000000..9bb00dba1fb6 --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveIntoFunction_afterUp.py @@ -0,0 +1,11 @@ +import numpy as np + + +def split_by_words(X): + X = np.core.chararray.lower(X) + return np.core.chararray.split(X) + DELIMITERS = "!?:;,.\'-+/\\()" + + def parse(string): + return "".join((" " if char in DELIMITERS else char) for char in string).split() + \ No newline at end of file diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction.py new file mode 100644 index 000000000000..ec3cb5784dbe --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction.py @@ -0,0 +1,4 @@ +def func(): + n = 0 + while n: + print("spam") diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterDown.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterDown.py new file mode 100644 index 000000000000..b79d83dcdb63 --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterDown.py @@ -0,0 +1,5 @@ +def func(): + pass +n = 0 +while n: + print("spam") diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterUp.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterUp.py new file mode 100644 index 000000000000..475ee3af816a --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromFunction_afterUp.py @@ -0,0 +1,5 @@ +n = 0 +while n: + print("spam") +def func(): + pass diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction.py new file mode 100644 index 000000000000..008f5a39ad2a --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction.py @@ -0,0 +1,5 @@ +def func(): + def nested_func(): + n = 0 + while n: + print("spam") diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterDown.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterDown.py new file mode 100644 index 000000000000..4a51027425b1 --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterDown.py @@ -0,0 +1,6 @@ +def func(): + def nested_func(): + pass + n = 0 + while n: + print("spam") diff --git a/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterUp.py b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterUp.py new file mode 100644 index 000000000000..b178cc6080b9 --- /dev/null +++ b/python/testData/mover/multiLineSelectionDifferentLevelsMoveOutFromNestedFunction_afterUp.py @@ -0,0 +1,6 @@ +def func(): + n = 0 + while n: + print("spam") + def nested_func(): + pass diff --git a/python/testData/mover/singleLineSelectionOutFromFunction.py b/python/testData/mover/singleLineSelectionOutFromFunction.py new file mode 100644 index 000000000000..a2582d0a8300 --- /dev/null +++ b/python/testData/mover/singleLineSelectionOutFromFunction.py @@ -0,0 +1,2 @@ +def foo(): + a = 42 diff --git a/python/testData/mover/singleLineSelectionOutFromFunction_afterDown.py b/python/testData/mover/singleLineSelectionOutFromFunction_afterDown.py new file mode 100644 index 000000000000..5db354ee0f0b --- /dev/null +++ b/python/testData/mover/singleLineSelectionOutFromFunction_afterDown.py @@ -0,0 +1,3 @@ +def foo(): + pass +a = 42 diff --git a/python/testData/mover/singleLineSelectionOutFromFunction_afterUp.py b/python/testData/mover/singleLineSelectionOutFromFunction_afterUp.py new file mode 100644 index 000000000000..23d08c0df9b0 --- /dev/null +++ b/python/testData/mover/singleLineSelectionOutFromFunction_afterUp.py @@ -0,0 +1,3 @@ +a = 42 +def foo(): + pass diff --git a/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java b/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java index c57faedfd50c..350f87496f04 100644 --- a/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java +++ b/python/testSrc/com/jetbrains/python/PyStatementMoverTest.java @@ -248,6 +248,26 @@ public class PyStatementMoverTest extends PyTestCase { doTest(); } + // PY-41375 + public void testMultiLineSelectionDifferentLevelsMoveOutFromFunction() { + doTest(); + } + + // PY-37765 + public void testMultiLineSelectionDifferentLevelsMoveIntoFunction() { + doTest(); + } + + // PY-41375 + public void testSingleLineSelectionOutFromFunction() { + doTest(); + } + + // PY-41375 + public void testMultiLineSelectionDifferentLevelsMoveOutFromNestedFunction() { + doTest(); + } + public void testTheSameLevelMultiple() { //PY-10947 doTest(); }