From faa72d6b4266a520c31075c44bd7ce6b3ca0cd1d Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 9 May 2025 10:39:08 +0200 Subject: [PATCH] Stop Move line up/down at the top/bottom correctly with multi-caret (IJ-CR-160892) GitOrigin-RevId: adec4f82c09efaa52db71b94ebaa5ac9d89271c5 --- .../moveUpDown/BaseMoveHandler.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/BaseMoveHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/BaseMoveHandler.java index ba9cbd0d118d..b9cd51a24467 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/BaseMoveHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/BaseMoveHandler.java @@ -7,6 +7,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -20,6 +21,7 @@ import java.util.List; */ @ApiStatus.Internal public abstract class BaseMoveHandler extends EditorWriteActionHandler.ForEachCaret { + private static final Key> FIRST_CARET_VETO = new Key<>("FIRST_CARET_VETO"); protected final boolean isDown; public BaseMoveHandler(boolean down) { @@ -36,7 +38,14 @@ public abstract class BaseMoveHandler extends EditorWriteActionHandler.ForEachCa final Document document = editor.getDocument(); int textLength = document.getTextLength(); List adjusted = null; - for (Caret c : editor.getCaretModel().getAllCarets()) { + final List carets = editor.getCaretModel().getAllCarets(); + final Project project = editor.getProject(); + assert project != null; + final PsiFile file = getPsiFile(project, editor); + + final MoverWrapper mover = getSuitableMover(editor, file); + if (movementVetoed(caret, carets, mover == null)) return; + for (Caret c : carets) { if (c != caret && c.getLogicalPosition().column == 0) { if (adjusted == null) adjusted = new ArrayList<>(); int offset = c.getOffset(); @@ -45,11 +54,6 @@ public abstract class BaseMoveHandler extends EditorWriteActionHandler.ForEachCa } } - final Project project = editor.getProject(); - assert project != null; - final PsiFile file = getPsiFile(project, editor); - - final MoverWrapper mover = getSuitableMover(editor, file); if (mover != null && mover.getInfo().toMove2 != null) { LineRange range = mover.getInfo().toMove; if ((range.startLine > 0 || isDown) && (range.endLine < document.getLineCount() || !isDown)) { @@ -65,12 +69,30 @@ public abstract class BaseMoveHandler extends EditorWriteActionHandler.ForEachCa } } + private boolean movementVetoed(@NotNull Caret caret, List carets, boolean blocked) { + Caret firstCaret = isDown ? carets.get(carets.size() - 1) : carets.get(0); + if (caret == firstCaret) { + if (blocked) { + firstCaret.putUserData(FIRST_CARET_VETO, FIRST_CARET_VETO); + return true; + } + } + else { + boolean vetoed = firstCaret.getUserData(FIRST_CARET_VETO) != null; + if (vetoed) { + Caret lastCaret = isDown ? carets.get(0) : carets.get(carets.size() - 1); + if (caret == lastCaret) firstCaret.putUserData(FIRST_CARET_VETO, null); + return true; + } + } + return false; + } + @Override public boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { if (editor.isViewer() || editor.isOneLineMode()) return false; final Project project = editor.getProject(); - if (project == null || project.isDisposed()) return false; - return true; + return project != null && !project.isDisposed(); } protected abstract @Nullable PsiFile getPsiFile(@NotNull Project project, @NotNull Editor editor);