com.vladsch.MissingInActions.actions.line.DuplicateForClipboardCaretsActionHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.vladsch.MissingInActions.actions.line.DuplicateForClipboardCaretsActionHandler.java

Source

/*
 * Copyright (c) 2016-2016 Vladimir Schneider <vladimir.schneider@gmail.com>
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.vladsch.MissingInActions.actions.line;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.util.Couple;
import com.vladsch.MissingInActions.manager.EditorCaret;
import com.vladsch.MissingInActions.manager.EditorPosition;
import com.vladsch.MissingInActions.manager.LineSelectionManager;
import com.vladsch.MissingInActions.settings.ApplicationSettings;
import com.vladsch.MissingInActions.util.ClipboardCaretContent;
import com.vladsch.MissingInActions.util.EditHelpers;
import com.vladsch.flexmark.util.sequence.Range;
import org.jetbrains.annotations.NotNull;

import java.awt.datatransfer.Transferable;
import java.util.ArrayList;
import java.util.List;

public class DuplicateForClipboardCaretsActionHandler extends EditorWriteActionHandler {
    private final boolean myInsertBlankLine;
    private final boolean myDoPaste;

    public DuplicateForClipboardCaretsActionHandler() {
        super(false);
        myInsertBlankLine = false;
        myDoPaste = false;
    }

    public DuplicateForClipboardCaretsActionHandler(boolean doPaste, boolean insertBlankLine) {
        super(false);
        myDoPaste = doPaste;
        myInsertBlankLine = insertBlankLine;
    }

    @Override
    public void executeWriteAction(Editor editor, Caret unusedCaret, DataContext dataContext) {
        ClipboardCaretContent clipboardCaretContent = ClipboardCaretContent.studyClipboard(editor, dataContext);
        if (clipboardCaretContent == null)
            return;

        EditorEx editorEx = editor instanceof EditorEx ? (EditorEx) editor : null;
        int iMax = clipboardCaretContent.getCaretCount();
        LineSelectionManager manager = LineSelectionManager.getInstance(editor);
        Document doc = editor.getDocument();
        CaretModel caretModel = editor.getCaretModel();
        List<Couple<Integer>> copies = new ArrayList<>(iMax);
        boolean duplicateForCaretsPreserveOriginal = ApplicationSettings.getInstance()
                .isMultiPastePreserveOriginal();

        if (editor.getCaretModel().getCaretCount() > 1) {
            // already multi-caret, use the caret span to figure out which block to copy,
            // span: line block from first to last caret
            // if span == 1 line, keep all carets and all selections
            // if span > 1 line:
            // if have selections: keep carets with selections relative to start of block after duplication
            // if no selections: remove first and last, if they have no selection
            boolean haveSelections = false;
            Transferable mergedTransferable = null;

            EditHelpers.StudiedCarets studiedCarets = EditHelpers.studyCarets(editor, caretModel.getAllCarets());

            caretModel.removeSecondaryCarets();
            if (editorEx != null)
                editorEx.setColumnMode(false);

            if (studiedCarets.range.getSpan() == 0) {
                // remove carets without selections
                if (studiedCarets.caretSelections.total > 0) {
                    // remove carets without selections
                    studiedCarets.carets.removeIf(caret -> !caret.hasSelection());
                }
            } else {
                if (studiedCarets.caretSelections.total > 0) {
                    // remove carets without selections on corresponding lines
                    studiedCarets.carets.removeIf((editorCaret) -> !editorCaret.hasSelection());
                } else {
                    // if not all lines have same number of carets, remove first and last, if they have no selection assuming they are there to mark the span of copied lines
                    if (studiedCarets.eachLineCarets.total == -1 && studiedCarets.eachLineCarets.code <= 0
                            && studiedCarets.eachLineCarets.comment <= 0
                            && studiedCarets.eachLineCarets.blank <= 0) {
                        // remove first and last carets
                        studiedCarets.carets.removeIf((editorCaret) -> editorCaret == null
                                || editorCaret == studiedCarets.firstLineCaret
                                        && !studiedCarets.firstLineCaret.hasSelection()
                                || editorCaret == studiedCarets.lastLineCaret
                                        && !studiedCarets.lastLineCaret.hasSelection());
                    }
                }
            }

            ArrayList<EditorCaret> carets = studiedCarets.carets;
            Range selRange = studiedCarets.range;

            // we need to duplicate the content, each caret in content duplicated for number of caret copies
            if (carets.size() > 1) {
                // make dupes so that the block carets are duped
                mergedTransferable = EditHelpers.getSplitRepeatedTransferable(clipboardCaretContent, carets.size());
            }

            // now we are ready to duplicate selRange block, and put carets on each duplicate relative to the first block which will not be included
            EditorCaret editorCaret = carets.get(0).copy();
            EditorPosition startPosition = editorCaret.getCaretPosition().onLine(selRange.getStart())
                    .atStartOfLine();
            EditorPosition endPosition = editorCaret.getCaretPosition().onLine(selRange.getEnd())
                    .atStartOfNextLine();
            editorCaret.setCaretPosition(startPosition);
            int offset = startPosition.getOffset();

            // do it in reverse order so as not to affect the offset
            String s = doc.getCharsSequence().subSequence(startPosition.getOffset(), endPosition.getOffset())
                    .toString();
            int inserted = 0;
            int span = selRange.getSpan() + 1;
            for (int i = iMax; i-- > 0;) {
                Couple<Integer> couple;
                if (i == 0 && !duplicateForCaretsPreserveOriginal) {
                    // don't insert, re-use
                    couple = new Couple<>(selRange.getStart(), selRange.getEnd());
                } else {
                    doc.insertString(startPosition.getOffset(), s);
                    inserted += span;
                    couple = new Couple<>(selRange.getStart() + inserted, selRange.getEnd() + inserted);
                }
                copies.add(couple);
            }

            manager.guard(() -> {
                // create multiple carets, copies from carets, but relative to start of original selection
                editorCaret.removeSelection();
                caretModel.removeSecondaryCarets();

                // build the carets
                int accumulatedOffset = 0;
                boolean firstCaret = true;
                for (int i = 0; i < iMax; i++) {
                    Couple<Integer> couple = copies.get(i);

                    int firstLine = couple.first;

                    for (EditorCaret copyCaret : carets) {
                        EditorPosition editorPosition = copyCaret.getCaretPosition()
                                .onLine(copyCaret.getCaretPosition().line - selRange.getStart() + firstLine).copy();
                        EditorPosition selectionStart = copyCaret.getSelectionStart()
                                .onLine(copyCaret.getSelectionStart().line - selRange.getStart() + firstLine)
                                .copy();
                        EditorPosition selectionEnd = copyCaret.getSelectionEnd()
                                .onLine(copyCaret.getSelectionEnd().line - selRange.getStart() + firstLine).copy();

                        Caret caret = firstCaret ? caretModel.getPrimaryCaret()
                                : caretModel.addCaret(editorPosition.toVisualPosition());
                        if (caret != null) {
                            firstCaret = false;
                            accumulatedOffset += editorPosition.ensureRealSpaces();
                            accumulatedOffset += selectionStart.ensureRealSpaces();
                            accumulatedOffset += selectionEnd.ensureRealSpaces();

                            // replicate selection to this position
                            int selectionSize = selectionEnd.getOffset() - selectionStart.getOffset();
                            if (selectionSize > 0) {
                                caret.moveToOffset(selectionEnd.getOffset());
                                caret.setSelection(selectionStart.getOffset(), selectionEnd.getOffset());
                                manager.resetSelectionState(caret);
                            } else {
                                caret.moveToLogicalPosition(editorPosition);
                            }
                        }
                    }
                }

                ClipboardCaretContent.setLastPastedClipboardCarets(editor, null);
            });

            if (myDoPaste) {
                // clear last pasted information, it is no good and will be cleared by running our action
                if (mergedTransferable != null) {
                    CopyPasteManager.getInstance().setContents(mergedTransferable);
                }

                // now we paste
                final AnAction pasteAction = ActionManager.getInstance().getAction(IdeActions.ACTION_PASTE);
                LineSelectionManager.getInstance(editor).runActionWithAdjustments(pasteAction);
                //AnActionEvent newEvent = AnActionEvent.createFromDataContext("MiaMultiPaste Recreate Carets",null,dataContext);
                //pasteAction.actionPerformed(newEvent);
            } else {
                if (mergedTransferable != null) {
                    CopyPasteManager.getInstance().setContents(mergedTransferable);
                }
            }

            EditHelpers.scrollToCaret(editor);
        } else {
            manager.guard(() -> {
                EditorCaret editorCaret = manager.getEditorCaret(editor.getCaretModel().getPrimaryCaret());

                int selectionSize = editorCaret.getSelectionEnd().getOffset()
                        - editorCaret.getSelectionStart().getOffset();
                boolean isStartAnchor = editorCaret.isStartAnchor();

                if (myInsertBlankLine) {
                    // we create a line above/below and recreate carets
                    final ApplicationSettings settings = ApplicationSettings.getInstance();

                    final EditorPosition pastePosition = settings.getLinePasteCaretAdjustmentType()
                            .getPastePosition(editorCaret.getCaretPosition());
                    doc.insertString(pastePosition.atColumn(0).getOffset(), "\n");
                    editorCaret.setCaretPosition(pastePosition.atColumn(editorCaret.getColumn()));
                    editorCaret.commit();
                }

                if (editorCaret.hasLines() || editorCaret.isLine()) {
                    editorCaret.trimOrExpandToFullLines();
                    selectionSize = 0;
                } else {
                    // dupe the line with selection replicated for each caret
                    editorCaret.removeSelection();
                    editor.getSelectionModel().removeSelection();
                }

                boolean useFirstLine = myInsertBlankLine || !duplicateForCaretsPreserveOriginal;
                for (int i = 0; i < iMax; i++) {
                    final Couple<Integer> couple;
                    if (useFirstLine) {
                        useFirstLine = false;
                        EditorPosition position = editorCaret.getCaretPosition();
                        couple = new Couple<>(position.atStartOfLine().getOffset(),
                                position.atStartOfNextLine().getOffset());
                    } else {
                        couple = EditHelpers.duplicateLineOrSelectedBlockAtCaret(editor, editor.getDocument(),
                                editor.getCaretModel().getPrimaryCaret(), true);
                    }

                    if (couple != null)
                        copies.add(couple);
                }

                // create multiple carets on first line of every copy
                EditorPosition pos = editorCaret.getCaretPosition();
                editorCaret.removeSelection();

                // build the carets
                int accumulatedOffset = 0;
                for (int i = 0; i < iMax; i++) {
                    Couple<Integer> couple = copies.get(i);

                    int lineNumber = doc.getLineNumber(couple.first + accumulatedOffset);

                    EditorPosition editorPosition = pos.onLine(lineNumber);
                    Caret caret1 = i == 0 ? caretModel.getPrimaryCaret()
                            : caretModel.addCaret(editorPosition.toVisualPosition());
                    if (caret1 != null) {
                        accumulatedOffset += editorPosition.ensureRealSpaces();
                        int offset = editorPosition.getOffset();

                        caret1.moveToLogicalPosition(editorPosition);

                        // replicate selection to this position
                        if (isStartAnchor) {
                            caret1.setSelection(offset - selectionSize, offset);
                        } else {
                            caret1.setSelection(offset, offset + selectionSize);
                        }
                        manager.resetSelectionState(caret1);
                    }
                }
            });

            if (myDoPaste) {
                // clear last pasted information, it is no good and will be cleared by running our action
                ClipboardCaretContent.setLastPastedClipboardCarets(editor, null);

                // now we paste
                final AnAction pasteAction = ActionManager.getInstance().getAction(IdeActions.ACTION_PASTE);
                LineSelectionManager.getInstance(editor).runActionWithAdjustments(pasteAction);
                //AnActionEvent newEvent = AnActionEvent.createFromDataContext("MiaMultiPaste Recreate Carets",null,dataContext);
                //pasteAction.actionPerformed(newEvent);
            } else {
                // clear clipboard information so adjustments don't get messed up
                ClipboardCaretContent.setLastPastedClipboardCarets(editor, null);
            }

            EditHelpers.scrollToCaret(editor);
        }
    }

    @Override
    public boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
        return true;
    }
}