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 0b641a7756dc..914917f2f99a 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 @@ -20,6 +20,7 @@ import com.intellij.codeInsight.folding.CodeFoldingManager; import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -111,14 +112,33 @@ class MoverWrapper { 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()); + if (document instanceof DocumentEx) { + int startFirst = Math.min(start, start2); + int endFirst = Math.min(end, end2); + int startSecond = Math.max(start, start2); + int endSecond = Math.max(end, end2); + ((DocumentEx)document).moveText(startFirst, endFirst, startSecond); + ((DocumentEx)document).moveText(startSecond, endSecond, startFirst); + myInfo.range1.dispose(); + myInfo.range2.dispose(); + // we could use existing range markers, but if some range is empty, they won't be moved as expected + myInfo.range1 = document.createRangeMarker(start < start2 ? start : start2 + end - end2, + start < start2 ? start + end2 - start2 : end); + myInfo.range2 = document.createRangeMarker(start < start2 ? start + end2 - end : start2, + start < start2 ? end2 : start2 + end - start); + insertLineBreakInTheEndIfMissing(myInfo.range1); + insertLineBreakInTheEndIfMissing(myInfo.range2); + } + else { + 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); + 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(); @@ -157,6 +177,16 @@ class MoverWrapper { editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } + private static void insertLineBreakInTheEndIfMissing(@NotNull RangeMarker marker) { + Document document = marker.getDocument(); + int startOffset = marker.getStartOffset(); + int endOffset = marker.getEndOffset(); + if (startOffset == endOffset || document.getImmutableCharSequence().charAt(endOffset - 1) != '\n') { + marker.setGreedyToRight(true); + document.insertString(endOffset, "\n"); + } + } + private static FoldRegion findTopLevelRegionInRange(Editor editor, RangeMarker range) { FoldRegion result = null; for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) { diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index a47a53ff8337..9b9631b49b5b 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -312,6 +312,7 @@ public interface IdeActions { String MOVE_ELEMENT_RIGHT = "MoveElementRight"; String ACTION_MOVE_LINE_UP_ACTION = "MoveLineUp"; + String ACTION_MOVE_LINE_DOWN_ACTION = "MoveLineDown"; String ACTION_COMPARE_CLIPBOARD_WITH_SELECTION = "CompareClipboardWithSelection"; diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/MoveLineTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/MoveLineTest.java new file mode 100644 index 000000000000..83136242bc28 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/MoveLineTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.actions; + +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.impl.AbstractEditorTest; + +import static org.junit.Assert.assertArrayEquals; + +public class MoveLineTest extends AbstractEditorTest { + public void testInlaysAreMovedWithText() throws Exception { + initText("ab\ncd\n"); + addInlay(1); + executeAction(IdeActions.ACTION_MOVE_LINE_DOWN_ACTION); + checkResultByText("cd\nab\n"); + assertInlayPositions(4); + } + + private static void assertInlayPositions(int... offsets) { + int[] actualPositions = myEditor.getInlayModel().getInlineElementsInRange(0, myEditor.getDocument().getTextLength()) + .stream().mapToInt(i -> i.getOffset()).toArray(); + assertArrayEquals(offsets, actualPositions); + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/AbstractEditorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/AbstractEditorTest.java index fd1f0a7cf884..dccf197bfcf1 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/AbstractEditorTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/AbstractEditorTest.java @@ -284,4 +284,9 @@ public abstract class AbstractEditorTest extends LightPlatformCodeInsightTestCas protected static void configureSoftWraps(int charCountToWrapAt) { EditorTestUtil.configureSoftWraps(myEditor, charCountToWrapAt); } + + public static Inlay addInlay(int offset) { + return EditorTestUtil.addInlay(myEditor, offset); + } + } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorInlayTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorInlayTest.java index 71ff686ea493..0bd7d696d074 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorInlayTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorInlayTest.java @@ -19,7 +19,6 @@ import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.VisualPosition; -import com.intellij.testFramework.EditorTestUtil; import com.intellij.util.containers.ContainerUtil; import java.util.Arrays; @@ -158,8 +157,4 @@ public class EditorInlayTest extends AbstractEditorTest { assertEquals(0, myEditor.getCaretModel().getVisualPosition().line); assertEquals(visualColumn, myEditor.getCaretModel().getVisualPosition().column); } - - private static Inlay addInlay(int offset) { - return EditorTestUtil.addInlay(myEditor, offset); - } }