diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java index 00bab74b4f6d..0b641a7756dc 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/MoverWrapper.java @@ -54,96 +54,97 @@ class MoverWrapper { assert myInfo.toMove2 != null; myMover.beforeMove(editor, myInfo, myIsDown); final Document document = editor.getDocument(); - - final int start = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.startLine); - final int end = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.endLine); - String textToInsert = document.getCharsSequence().subSequence(start, end).toString(); - if (!StringUtil.endsWithChar(textToInsert,'\n')) textToInsert += '\n'; - - final int start2 = document.getLineStartOffset(myInfo.toMove2.startLine); - final int end2 = StatementUpDownMover.getLineStartSafeOffset(document,myInfo.toMove2.endLine); - String textToInsert2 = document.getCharsSequence().subSequence(start2, end2).toString(); - if (!StringUtil.endsWithChar(textToInsert2,'\n')) textToInsert2 += '\n'; - - TextRange range = new TextRange(start, end); - TextRange range2 = new TextRange(start2, end2); - if (range.intersectsStrict(range2) && !range.equals(range2)) { - LOGGER.error("Wrong move ranges requested by " + myMover, - new Attachment("ranges.txt", - start + ":" + end + "(" + textToInsert + ")\n" + start2 + ":" + end2 + "(" + textToInsert2 + ")")); - return; - } - - myInfo.range1 = document.createRangeMarker(start, end); - myInfo.range2 = document.createRangeMarker(start2, end2); - if (myInfo.range1.getStartOffset() < myInfo.range2.getStartOffset()) { - myInfo.range1.setGreedyToLeft(true); - myInfo.range1.setGreedyToRight(false); - myInfo.range2.setGreedyToLeft(true); - myInfo.range2.setGreedyToRight(true); - } - else { - myInfo.range1.setGreedyToLeft(true); - myInfo.range1.setGreedyToRight(true); - myInfo.range2.setGreedyToLeft(true); - myInfo.range2.setGreedyToRight(false); - } - - final CaretModel caretModel = editor.getCaretModel(); - final int caretRelativePos = caretModel.getOffset() - start; - final SelectionModel selectionModel = editor.getSelectionModel(); - final int selectionStart = selectionModel.getSelectionStart(); - final int selectionEnd = selectionModel.getSelectionEnd(); - final boolean hasSelection = selectionModel.hasSelection(); - - // to prevent flicker - caretModel.moveToOffset(0); - - // There is a possible case that the user performs, say, method move. It's also possible that one (or both) of moved methods - // are folded. We want to preserve their states then. The problem is that folding processing is based on PSI element pointers - // and the pointers behave as following during move up/down: - // method1() {} - // method2() {} - // Pointer for the fold region from method1 points to 'method2()' now and vice versa (check range markers processing on - // document change for further information). I.e. information about fold regions statuses holds the data swapped for - // 'method1' and 'method2'. Hence, we want to apply correct 'collapsed' status. - final FoldRegion topRegion = findTopLevelRegionInRange(editor, myInfo.range1); - final FoldRegion bottomRegion = findTopLevelRegionInRange(editor, myInfo.range2); - - document.insertString(myInfo.range1.getStartOffset(), textToInsert2); - document.deleteString(myInfo.range1.getStartOffset()+textToInsert2.length(), myInfo.range1.getEndOffset()); - - document.insertString(myInfo.range2.getStartOffset(), textToInsert); - int s = myInfo.range2.getStartOffset() + textToInsert.length(); - int e = myInfo.range2.getEndOffset(); - if (e > s) { - document.deleteString(s, e); - } - final Project project = file.getProject(); - PsiDocumentManager.getInstance(project).commitAllDocuments(); + if (!myInfo.toMove.equals(myInfo.toMove2)) { // some movers (e.g. PyStatementMover) perform actual moving inside beforeMove/afterMove + final int start = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.startLine); + final int end = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.endLine); + String textToInsert = document.getCharsSequence().subSequence(start, end).toString(); + if (!StringUtil.endsWithChar(textToInsert,'\n')) textToInsert += '\n'; - // Swap fold regions status if necessary. - if (topRegion != null && bottomRegion != null) { - CodeFoldingManager.getInstance(project).updateFoldRegions(editor); - editor.getFoldingModel().runBatchFoldingOperation(() -> { - FoldRegion newTopRegion = findTopLevelRegionInRange(editor, myInfo.range1); - if (newTopRegion != null) { - newTopRegion.setExpanded(bottomRegion.isExpanded()); - } + final int start2 = document.getLineStartOffset(myInfo.toMove2.startLine); + final int end2 = StatementUpDownMover.getLineStartSafeOffset(document,myInfo.toMove2.endLine); + String textToInsert2 = document.getCharsSequence().subSequence(start2, end2).toString(); + if (!StringUtil.endsWithChar(textToInsert2,'\n')) textToInsert2 += '\n'; - FoldRegion newBottomRegion = findTopLevelRegionInRange(editor, myInfo.range2); - if (newBottomRegion != null) { - newBottomRegion.setExpanded(topRegion.isExpanded()); - } - }); + TextRange range = new TextRange(start, end); + TextRange range2 = new TextRange(start2, end2); + if (range.intersectsStrict(range2) && !range.equals(range2)) { + LOGGER.error("Wrong move ranges requested by " + myMover, + new Attachment("ranges.txt", + start + ":" + end + "(" + textToInsert + ")\n" + start2 + ":" + end2 + "(" + textToInsert2 + ")")); + return; + } + + myInfo.range1 = document.createRangeMarker(start, end); + myInfo.range2 = document.createRangeMarker(start2, end2); + if (myInfo.range1.getStartOffset() < myInfo.range2.getStartOffset()) { + myInfo.range1.setGreedyToLeft(true); + myInfo.range1.setGreedyToRight(false); + myInfo.range2.setGreedyToLeft(true); + myInfo.range2.setGreedyToRight(true); + } + else { + myInfo.range1.setGreedyToLeft(true); + myInfo.range1.setGreedyToRight(true); + myInfo.range2.setGreedyToLeft(true); + myInfo.range2.setGreedyToRight(false); + } + + final CaretModel caretModel = editor.getCaretModel(); + final int caretRelativePos = caretModel.getOffset() - start; + final SelectionModel selectionModel = editor.getSelectionModel(); + final int selectionStart = selectionModel.getSelectionStart(); + final int selectionEnd = selectionModel.getSelectionEnd(); + final boolean hasSelection = selectionModel.hasSelection(); + + // to prevent flicker + caretModel.moveToOffset(0); + + // There is a possible case that the user performs, say, method move. It's also possible that one (or both) of moved methods + // are folded. We want to preserve their states then. The problem is that folding processing is based on PSI element pointers + // and the pointers behave as following during move up/down: + // method1() {} + // method2() {} + // Pointer for the fold region from method1 points to 'method2()' now and vice versa (check range markers processing on + // document change for further information). I.e. information about fold regions statuses holds the data swapped for + // 'method1' and 'method2'. Hence, we want to apply correct 'collapsed' status. + final FoldRegion topRegion = findTopLevelRegionInRange(editor, myInfo.range1); + final FoldRegion bottomRegion = findTopLevelRegionInRange(editor, myInfo.range2); + + document.insertString(myInfo.range1.getStartOffset(), textToInsert2); + document.deleteString(myInfo.range1.getStartOffset()+textToInsert2.length(), myInfo.range1.getEndOffset()); + + document.insertString(myInfo.range2.getStartOffset(), textToInsert); + int s = myInfo.range2.getStartOffset() + textToInsert.length(); + int e = myInfo.range2.getEndOffset(); + if (e > s) { + document.deleteString(s, e); + } + + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + // Swap fold regions status if necessary. + if (topRegion != null && bottomRegion != null) { + CodeFoldingManager.getInstance(project).updateFoldRegions(editor); + editor.getFoldingModel().runBatchFoldingOperation(() -> { + FoldRegion newTopRegion = findTopLevelRegionInRange(editor, myInfo.range1); + if (newTopRegion != null) { + newTopRegion.setExpanded(bottomRegion.isExpanded()); + } + + FoldRegion newBottomRegion = findTopLevelRegionInRange(editor, myInfo.range2); + if (newBottomRegion != null) { + newBottomRegion.setExpanded(topRegion.isExpanded()); + } + }); + } + + if (hasSelection) { + restoreSelection(editor, selectionStart, selectionEnd, start, end, myInfo.range2.getStartOffset()); + } + + caretModel.moveToOffset(myInfo.range2.getStartOffset() + caretRelativePos); } - - if (hasSelection) { - restoreSelection(editor, selectionStart, selectionEnd, start, end, myInfo.range2.getStartOffset()); - } - - caretModel.moveToOffset(myInfo.range2.getStartOffset() + caretRelativePos); myMover.afterMove(editor, file, myInfo, myIsDown); PsiDocumentManager.getInstance(project).commitDocument(document); if (myInfo.indentTarget) { diff --git a/python/testData/mover/class_afterUp.py b/python/testData/mover/class_afterUp.py index 7864db22ba35..710862786d3c 100644 --- a/python/testData/mover/class_afterUp.py +++ b/python/testData/mover/class_afterUp.py @@ -4,4 +4,4 @@ if condition: class C: pass class B(Exception): - b=1 + b=1 \ No newline at end of file diff --git a/python/testData/mover/lastComment1_afterDown.py b/python/testData/mover/lastComment1_afterDown.py index b6ff87fd824b..4c6a890dbcad 100644 --- a/python/testData/mover/lastComment1_afterDown.py +++ b/python/testData/mover/lastComment1_afterDown.py @@ -3,4 +3,4 @@ def f(): a = 1 else: a = 2 -#comment +#comment \ No newline at end of file