public static PsiFile insertDummyIdentifier(final Editor editor, PsiFile file) {
   boolean selection = editor.getSelectionModel().hasSelection();
   final int startOffset =
       selection
           ? editor.getSelectionModel().getSelectionStart()
           : editor.getCaretModel().getOffset();
   final int endOffset = selection ? editor.getSelectionModel().getSelectionEnd() : startOffset;
   return insertDummyIdentifierIfNeeded(
       file, startOffset, endOffset, CompletionUtil.DUMMY_IDENTIFIER_TRIMMED);
 }
 public void startTemplateWithPrefix(
     final Editor editor,
     final TemplateImpl template,
     final int templateStart,
     @Nullable final PairProcessor<String, String> processor,
     @Nullable final String argument) {
   final int caretOffset = editor.getCaretModel().getOffset();
   final TemplateState templateState = initTemplateState(editor);
   CommandProcessor commandProcessor = CommandProcessor.getInstance();
   commandProcessor.executeCommand(
       myProject,
       () -> {
         editor.getDocument().deleteString(templateStart, caretOffset);
         editor.getCaretModel().moveToOffset(templateStart);
         editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
         editor.getSelectionModel().removeSelection();
         Map<String, String> predefinedVarValues = null;
         if (argument != null) {
           predefinedVarValues = new HashMap<String, String>();
           predefinedVarValues.put(TemplateImpl.ARG, argument);
         }
         templateState.start(template, processor, predefinedVarValues);
       },
       CodeInsightBundle.message("insert.code.template.command"),
       null);
 }
  public static List<TemplateImpl> listApplicableTemplateWithInsertingDummyIdentifier(
      Editor editor, PsiFile file, boolean selectionOnly) {
    int startOffset = editor.getSelectionModel().getSelectionStart();
    file = insertDummyIdentifier(editor, file);

    return listApplicableTemplates(file, startOffset, selectionOnly);
  }
  private static void restoreBlockSelection(
      Editor editor, List<RangeMarker> caretsAfter, int caretLine) {
    int column = -1;
    int minLine = Integer.MAX_VALUE;
    int maxLine = -1;
    for (RangeMarker marker : caretsAfter) {
      if (marker.isValid()) {
        LogicalPosition lp = editor.offsetToLogicalPosition(marker.getStartOffset());
        if (column == -1) {
          column = lp.column;
        } else if (column != lp.column) {
          return;
        }
        minLine = Math.min(minLine, lp.line);
        maxLine = Math.max(maxLine, lp.line);

        if (lp.line == caretLine) {
          editor.getCaretModel().moveToLogicalPosition(lp);
        }
      }
    }
    editor
        .getSelectionModel()
        .setBlockSelection(
            new LogicalPosition(minLine, column), new LogicalPosition(maxLine, column));
  }
Example #5
0
  @Override
  public void execute(
      final Editor editor,
      final DataContext dataContext,
      @Nullable final Producer<Transferable> producer) {
    if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;

    final Document document = editor.getDocument();
    if (!FileDocumentManager.getInstance()
        .requestWriting(document, CommonDataKeys.PROJECT.getData(dataContext))) {
      return;
    }

    DataContext context = dataContext;
    if (producer != null) {
      context =
          new DataContext() {
            @Override
            public Object getData(@NonNls String dataId) {
              return PasteAction.TRANSFERABLE_PROVIDER.is(dataId)
                  ? producer
                  : dataContext.getData(dataId);
            }
          };
    }

    final Project project = editor.getProject();
    if (project == null
        || editor.isColumnMode()
        || editor.getSelectionModel().hasBlockSelection()
        || editor.getCaretModel().getCaretCount() > 1) {
      if (myOriginalHandler != null) {
        myOriginalHandler.execute(editor, context);
      }
      return;
    }

    final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
    if (file == null) {
      if (myOriginalHandler != null) {
        myOriginalHandler.execute(editor, context);
      }
      return;
    }

    document.startGuardedBlockChecking();
    try {
      for (PasteProvider provider : Extensions.getExtensions(EP_NAME)) {
        if (provider.isPasteEnabled(context)) {
          provider.performPaste(context);
          return;
        }
      }
      doPaste(editor, project, file, document, producer);
    } catch (ReadOnlyFragmentModificationException e) {
      EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(document).handle(e);
    } finally {
      document.stopGuardedBlockChecking();
    }
  }
  private void removeFromEditor() {
    Editor editor = mySearchResults.getEditor();
    if (myReplacementBalloon != null) {
      myReplacementBalloon.hide();
    }

    if (editor != null) {

      for (VisibleAreaListener visibleAreaListener : myVisibleAreaListenersToRemove) {
        editor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener);
      }
      myVisibleAreaListenersToRemove.clear();
      Project project = mySearchResults.getProject();
      if (project != null && !project.isDisposed()) {
        for (RangeHighlighter h : myHighlighters) {
          HighlightManager.getInstance(project).removeSegmentHighlighter(editor, h);
        }
        if (myCursorHighlighter != null) {
          HighlightManager.getInstance(project)
              .removeSegmentHighlighter(editor, myCursorHighlighter);
          myCursorHighlighter = null;
        }
      }
      myHighlighters.clear();
      if (myListeningSelection) {
        editor.getSelectionModel().removeSelectionListener(this);
        myListeningSelection = false;
      }
    }
  }
  public static void typeInStringAtCaretHonorBlockSelection(
      final Editor editor, final String str, final boolean toProcessOverwriteMode)
      throws ReadOnlyFragmentModificationException {
    Document doc = editor.getDocument();
    final SelectionModel selectionModel = editor.getSelectionModel();
    if (selectionModel.hasBlockSelection()) {
      RangeMarker guard = selectionModel.getBlockSelectionGuard();
      if (guard != null) {
        DocumentEvent evt = new MockDocumentEvent(doc, editor.getCaretModel().getOffset());
        ReadOnlyFragmentModificationException e =
            new ReadOnlyFragmentModificationException(evt, guard);
        EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
      } else {
        final LogicalPosition start = selectionModel.getBlockStart();
        final LogicalPosition end = selectionModel.getBlockEnd();
        assert start != null;
        assert end != null;

        int column = Math.min(start.column, end.column);
        int startLine = Math.min(start.line, end.line);
        int endLine = Math.max(start.line, end.line);
        deleteBlockSelection(editor);
        for (int i = startLine; i <= endLine; i++) {
          editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, column));
          insertStringAtCaret(editor, str, toProcessOverwriteMode, true);
        }
        selectionModel.setBlockSelection(
            new LogicalPosition(startLine, column + str.length()),
            new LogicalPosition(endLine, column + str.length()));
      }
    } else {
      insertStringAtCaret(editor, str, toProcessOverwriteMode, true);
    }
  }
  private void updateCursorHighlighting(boolean scroll) {
    hideBalloon();

    if (myCursorHighlighter != null) {
      HighlightManager.getInstance(mySearchResults.getProject())
          .removeSegmentHighlighter(mySearchResults.getEditor(), myCursorHighlighter);
      myCursorHighlighter = null;
    }

    final FindResult cursor = mySearchResults.getCursor();
    Editor editor = mySearchResults.getEditor();
    SelectionModel selection = editor.getSelectionModel();
    if (cursor != null) {
      Set<RangeHighlighter> dummy = new HashSet<RangeHighlighter>();
      highlightRange(
          cursor, new TextAttributes(null, null, Color.BLACK, EffectType.ROUNDED_BOX, 0), dummy);
      if (!dummy.isEmpty()) {
        myCursorHighlighter = dummy.iterator().next();
      }

      if (scroll) {
        if (mySearchResults.getFindModel().isGlobal()) {
          FoldingModel foldingModel = editor.getFoldingModel();
          final FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions();

          foldingModel.runBatchFoldingOperation(
              new Runnable() {
                @Override
                public void run() {
                  for (FoldRegion region : allRegions) {
                    if (!region.isValid()) continue;
                    if (cursor.intersects(TextRange.create(region))) {
                      region.setExpanded(true);
                    }
                  }
                }
              });
          selection.setSelection(cursor.getStartOffset(), cursor.getEndOffset());

          editor.getCaretModel().moveToOffset(cursor.getEndOffset());
          editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
        } else {
          if (!SearchResults.insideVisibleArea(editor, cursor)) {
            LogicalPosition pos = editor.offsetToLogicalPosition(cursor.getStartOffset());
            editor.getScrollingModel().scrollTo(pos, ScrollType.CENTER);
          }
        }
      }
      editor
          .getScrollingModel()
          .runActionOnScrollingFinished(
              new Runnable() {
                @Override
                public void run() {
                  showReplacementPreview();
                }
              });
    }
  }
 private static void zeroWidthBlockSelectionAtCaretColumn(
     final Editor editor, final int startLine, final int endLine) {
   int caretColumn = editor.getCaretModel().getLogicalPosition().column;
   editor
       .getSelectionModel()
       .setBlockSelection(
           new LogicalPosition(startLine, caretColumn), new LogicalPosition(endLine, caretColumn));
 }
  public static int insertStringAtCaret(
      Editor editor,
      @NotNull String s,
      boolean toProcessOverwriteMode,
      boolean toMoveCaret,
      int caretShift) {
    final SelectionModel selectionModel = editor.getSelectionModel();
    if (selectionModel.hasSelection()) {
      editor.getCaretModel().moveToOffset(selectionModel.getSelectionStart(), true);
    }

    // There is a possible case that particular soft wraps become hard wraps if the caret is located
    // at soft wrap-introduced virtual
    // space, hence, we need to give editor a chance to react accordingly.
    editor.getSoftWrapModel().beforeDocumentChangeAtCaret();
    int oldOffset = editor.getCaretModel().getOffset();

    String filler = calcStringToFillVirtualSpace(editor);
    if (filler.length() > 0) {
      s = filler + s;
    }

    Document document = editor.getDocument();
    if (editor.isInsertMode() || !toProcessOverwriteMode) {
      if (selectionModel.hasSelection()) {
        oldOffset = selectionModel.getSelectionStart();
        document.replaceString(
            selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(), s);
      } else {
        document.insertString(oldOffset, s);
      }
    } else {
      deleteSelectedText(editor);
      int lineNumber = editor.getCaretModel().getLogicalPosition().line;
      if (lineNumber >= document.getLineCount()) {
        return insertStringAtCaret(editor, s, false, toMoveCaret);
      }

      int endOffset = document.getLineEndOffset(lineNumber);
      document.replaceString(oldOffset, Math.min(endOffset, oldOffset + s.length()), s);
    }

    int offset = oldOffset + filler.length() + caretShift;
    if (toMoveCaret) {
      editor.getCaretModel().moveToOffset(offset, true);
      editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      selectionModel.removeSelection();
    } else if (editor.getCaretModel().getOffset()
        != oldOffset) { // handling the case when caret model tracks document changes
      editor.getCaretModel().moveToOffset(oldOffset);
    }

    return offset;
  }
 private static void restoreSelection(
     final Editor editor,
     final int selectionStart,
     final int selectionEnd,
     final int moveOffset,
     int insOffset) {
   final int selectionRelativeOffset = selectionStart - moveOffset;
   int newSelectionStart = insOffset + selectionRelativeOffset;
   int newSelectionEnd = newSelectionStart + selectionEnd - selectionStart;
   editor.getSelectionModel().setSelection(newSelectionStart, newSelectionEnd);
 }
  /**
   * Emulates pressing <code>Enter</code> at current caret position.
   *
   * @param editor target editor
   * @param project target project
   * @param shifts two-elements array which is expected to be filled with the following info: 1. The
   *     first element holds added lines number; 2. The second element holds added symbols number;
   */
  private static void emulateEnter(
      @NotNull final Editor editor, @NotNull Project project, int[] shifts) {
    final DataContext dataContext = prepareContext(editor.getComponent(), project);
    int caretOffset = editor.getCaretModel().getOffset();
    Document document = editor.getDocument();
    SelectionModel selectionModel = editor.getSelectionModel();
    int startSelectionOffset = 0;
    int endSelectionOffset = 0;
    boolean restoreSelection = selectionModel.hasSelection();
    if (restoreSelection) {
      startSelectionOffset = selectionModel.getSelectionStart();
      endSelectionOffset = selectionModel.getSelectionEnd();
      selectionModel.removeSelection();
    }
    int textLengthBeforeWrap = document.getTextLength();
    int lineCountBeforeWrap = document.getLineCount();

    DataManager.getInstance()
        .saveInDataContext(dataContext, WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY, true);
    CommandProcessor commandProcessor = CommandProcessor.getInstance();
    try {
      Runnable command =
          new Runnable() {
            @Override
            public void run() {
              EditorActionManager.getInstance()
                  .getActionHandler(IdeActions.ACTION_EDITOR_ENTER)
                  .execute(editor, dataContext);
            }
          };
      if (commandProcessor.getCurrentCommand() == null) {
        commandProcessor.executeCommand(editor.getProject(), command, WRAP_LINE_COMMAND_NAME, null);
      } else {
        command.run();
      }
    } finally {
      DataManager.getInstance()
          .saveInDataContext(dataContext, WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY, null);
    }
    int symbolsDiff = document.getTextLength() - textLengthBeforeWrap;
    if (restoreSelection) {
      int newSelectionStart = startSelectionOffset;
      int newSelectionEnd = endSelectionOffset;
      if (startSelectionOffset >= caretOffset) {
        newSelectionStart += symbolsDiff;
      }
      if (endSelectionOffset >= caretOffset) {
        newSelectionEnd += symbolsDiff;
      }
      selectionModel.setSelection(newSelectionStart, newSelectionEnd);
    }
    shifts[0] = document.getLineCount() - lineCountBeforeWrap;
    shifts[1] = symbolsDiff;
  }
  public static void pasteTransferableAsBlock(
      Editor editor, @Nullable Producer<Transferable> producer) {
    String text = getStringContent(producer);
    if (text == null) return;

    int caretLine = editor.getCaretModel().getLogicalPosition().line;
    int originalCaretLine = caretLine;
    int selectedLinesCount = 0;

    final SelectionModel selectionModel = editor.getSelectionModel();
    if (selectionModel.hasBlockSelection()) {
      final LogicalPosition start = selectionModel.getBlockStart();
      final LogicalPosition end = selectionModel.getBlockEnd();
      assert start != null;
      assert end != null;
      LogicalPosition caret =
          new LogicalPosition(Math.min(start.line, end.line), Math.min(start.column, end.column));
      selectedLinesCount = Math.abs(end.line - start.line);
      caretLine = caret.line;

      deleteSelectedText(editor);
      editor.getCaretModel().moveToLogicalPosition(caret);
    }

    LogicalPosition caretToRestore = editor.getCaretModel().getLogicalPosition();

    String[] lines = LineTokenizer.tokenize(text.toCharArray(), false);
    if (lines.length > 1 || selectedLinesCount == 0) {
      int longestLineLength = 0;
      for (int i = 0; i < lines.length; i++) {
        String line = lines[i];
        longestLineLength = Math.max(longestLineLength, line.length());
        editor
            .getCaretModel()
            .moveToLogicalPosition(new LogicalPosition(caretLine + i, caretToRestore.column));
        insertStringAtCaret(editor, line, false, true);
      }
      caretToRestore =
          new LogicalPosition(originalCaretLine, caretToRestore.column + longestLineLength);
    } else {
      for (int i = 0; i <= selectedLinesCount; i++) {
        editor
            .getCaretModel()
            .moveToLogicalPosition(new LogicalPosition(caretLine + i, caretToRestore.column));
        insertStringAtCaret(editor, text, false, true);
      }
      caretToRestore =
          new LogicalPosition(originalCaretLine, caretToRestore.column + text.length());
    }

    editor.getCaretModel().moveToLogicalPosition(caretToRestore);
    zeroWidthBlockSelectionAtCaretColumn(editor, caretLine, caretLine + selectedLinesCount);
  }
 /**
  * Applies given caret/selection state to the editor. Editor text must have been set up
  * previously.
  */
 public static void setCaretsAndSelection(Editor editor, CaretAndSelectionState caretsState) {
   CaretModel caretModel = editor.getCaretModel();
   if (caretModel.supportsMultipleCarets()) {
     List<CaretState> states = new ArrayList<CaretState>(caretsState.carets.size());
     for (CaretInfo caret : caretsState.carets) {
       states.add(
           new CaretState(
               caret.position == null
                   ? null
                   : editor.offsetToLogicalPosition(caret.getCaretOffset(editor.getDocument())),
               caret.selection == null
                   ? null
                   : editor.offsetToLogicalPosition(caret.selection.getStartOffset()),
               caret.selection == null
                   ? null
                   : editor.offsetToLogicalPosition(caret.selection.getEndOffset())));
     }
     caretModel.setCaretsAndSelections(states);
   } else {
     assertEquals("Multiple carets are not supported by the model", 1, caretsState.carets.size());
     CaretInfo caret = caretsState.carets.get(0);
     if (caret.position != null) {
       caretModel.moveToOffset(caret.getCaretOffset(editor.getDocument()));
     }
     if (caret.selection != null) {
       editor
           .getSelectionModel()
           .setSelection(caret.selection.getStartOffset(), caret.selection.getEndOffset());
     } else {
       editor.getSelectionModel().removeSelection();
     }
   }
   if (caretsState.blockSelection != null) {
     editor
         .getSelectionModel()
         .setBlockSelection(
             editor.offsetToLogicalPosition(caretsState.blockSelection.getStartOffset()),
             editor.offsetToLogicalPosition(caretsState.blockSelection.getEndOffset()));
   }
 }
  public static void deleteSelectedText(Editor editor) {
    SelectionModel selectionModel = editor.getSelectionModel();
    if (selectionModel.hasBlockSelection()) deleteBlockSelection(editor);
    if (!selectionModel.hasSelection()) return;

    int selectionStart = selectionModel.getSelectionStart();
    int selectionEnd = selectionModel.getSelectionEnd();

    editor.getCaretModel().moveToOffset(selectionStart);
    selectionModel.removeSelection();
    editor.getDocument().deleteString(selectionStart, selectionEnd);
    editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
  }
  private static Runnable rememberDocumentState(final Editor _editor) {
    final Editor editor = InjectedLanguageUtil.getTopLevelEditor(_editor);
    final String documentText = editor.getDocument().getText();
    final int caret = editor.getCaretModel().getOffset();
    final int selStart = editor.getSelectionModel().getSelectionStart();
    final int selEnd = editor.getSelectionModel().getSelectionEnd();

    final int vOffset = editor.getScrollingModel().getVerticalScrollOffset();
    final int hOffset = editor.getScrollingModel().getHorizontalScrollOffset();

    return new Runnable() {
      @Override
      public void run() {
        DocumentEx document = (DocumentEx) editor.getDocument();

        document.replaceString(0, document.getTextLength(), documentText);
        editor.getCaretModel().moveToOffset(caret);
        editor.getSelectionModel().setSelection(selStart, selEnd);

        editor.getScrollingModel().scrollHorizontally(hOffset);
        editor.getScrollingModel().scrollVertically(vOffset);
      }
    };
  }
  @Nullable
  public Runnable prepareTemplate(
      final Editor editor,
      char shortcutChar,
      @Nullable final PairProcessor<String, String> processor) {
    if (editor.getSelectionModel().hasSelection()) {
      return null;
    }

    PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, myProject);
    if (file == null) return null;

    Map<TemplateImpl, String> template2argument =
        findMatchingTemplates(file, editor, shortcutChar, TemplateSettings.getInstance());

    List<CustomLiveTemplate> customCandidates =
        ContainerUtil.findAll(
            CustomLiveTemplate.EP_NAME.getExtensions(),
            customLiveTemplate ->
                shortcutChar == customLiveTemplate.getShortcut()
                    && (editor.getCaretModel().getCaretCount() <= 1
                        || supportsMultiCaretMode(customLiveTemplate)));
    if (!customCandidates.isEmpty()) {
      int caretOffset = editor.getCaretModel().getOffset();
      PsiFile fileCopy = insertDummyIdentifierIfNeeded(file, caretOffset, caretOffset, "");
      Document document = editor.getDocument();

      for (final CustomLiveTemplate customLiveTemplate : customCandidates) {
        if (isApplicable(customLiveTemplate, editor, fileCopy)) {
          final String key =
              customLiveTemplate.computeTemplateKey(new CustomTemplateCallback(editor, fileCopy));
          if (key != null) {
            int offsetBeforeKey = caretOffset - key.length();
            CharSequence text = document.getImmutableCharSequence();
            if (template2argument == null
                || !containsTemplateStartingBefore(
                    template2argument, offsetBeforeKey, caretOffset, text)) {
              return () -> customLiveTemplate.expand(key, new CustomTemplateCallback(editor, file));
            }
          }
        }
      }
    }

    return startNonCustomTemplates(template2argument, editor, processor);
  }
  public void navigateIn(@NotNull Editor e) {
    final int offset = getOffset();
    CaretModel caretModel = e.getCaretModel();
    boolean caretMoved = false;
    if (myLogicalLine >= 0) {
      LogicalPosition pos = new LogicalPosition(myLogicalLine, Math.max(myLogicalColumn, 0));
      if (offset < 0 || offset == e.logicalPositionToOffset(pos)) {
        caretModel.moveToLogicalPosition(pos);
        caretMoved = true;
      }
    }
    if (!caretMoved && offset >= 0) {
      caretModel.moveToOffset(Math.min(offset, e.getDocument().getTextLength()));
      caretMoved = true;
    }

    if (caretMoved) {
      e.getSelectionModel().removeSelection();
      scrollToCaret(e);
      unfoldCurrentLine(e);
    }
  }
  private void startTemplate(
      final Editor editor,
      final String selectionString,
      final Template template,
      boolean inSeparateCommand,
      TemplateEditingListener listener,
      final PairProcessor<String, String> processor,
      final Map<String, String> predefinedVarValues) {
    final TemplateState templateState = initTemplateState(editor);

    //noinspection unchecked
    templateState.getProperties().put(ExpressionContext.SELECTION, selectionString);

    if (listener != null) {
      templateState.addTemplateStateListener(listener);
    }
    Runnable r =
        () -> {
          if (selectionString != null) {
            ApplicationManager.getApplication()
                .runWriteAction(() -> EditorModificationUtil.deleteSelectedText(editor));
          } else {
            editor.getSelectionModel().removeSelection();
          }
          templateState.start((TemplateImpl) template, processor, predefinedVarValues);
        };
    if (inSeparateCommand) {
      CommandProcessor.getInstance()
          .executeCommand(
              myProject, r, CodeInsightBundle.message("insert.code.template.command"), null);
    } else {
      r.run();
    }

    if (shouldSkipInTests()) {
      if (!templateState.isFinished()) templateState.gotoEnd(false);
    }
  }
  public static void deleteBlockSelection(Editor editor) {
    SelectionModel selectionModel = editor.getSelectionModel();
    if (!selectionModel.hasBlockSelection()) return;

    LogicalPosition blockStart = selectionModel.getBlockStart();
    LogicalPosition blockEnd = selectionModel.getBlockEnd();
    if (blockStart == null || blockEnd == null) {
      return;
    }

    int startLine = blockStart.line;
    int endLine = blockEnd.line;

    int[] starts = selectionModel.getBlockSelectionStarts();
    int[] ends = selectionModel.getBlockSelectionEnds();

    for (int i = starts.length - 1; i >= 0; i--) {
      editor.getDocument().deleteString(starts[i], ends[i]);
    }

    editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);

    zeroWidthBlockSelectionAtCaretColumn(editor, startLine, endLine);
  }
  private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection(
      CompletionProgressIndicator indicator,
      LookupElement item,
      char completionChar,
      List<LookupElement> items,
      CompletionLookupArranger.StatisticsUpdate update) {
    final Editor editor = indicator.getEditor();

    final int caretOffset = editor.getCaretModel().getOffset();
    int idEndOffset = indicator.getIdentifierEndOffset();
    if (idEndOffset < 0) {
      idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset);
    }

    CompletionAssertions.WatchingInsertionContext context = null;
    if (editor.getSelectionModel().hasBlockSelection()
        && editor.getSelectionModel().getBlockSelectionEnds().length > 0) {
      List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>();
      int idDelta = 0;
      Document document = editor.getDocument();
      int caretLine = document.getLineNumber(editor.getCaretModel().getOffset());

      for (int point : editor.getSelectionModel().getBlockSelectionEnds()) {
        insertionPoints.add(document.createRangeMarker(point, point));
        if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) {
          idDelta = idEndOffset - point;
        }
      }

      List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>();
      for (RangeMarker marker : insertionPoints) {
        if (marker.isValid()) {
          int insertionPoint = marker.getStartOffset();
          context =
              insertItem(
                  indicator,
                  item,
                  completionChar,
                  items,
                  update,
                  editor,
                  insertionPoint,
                  idDelta + insertionPoint);
          int offset = editor.getCaretModel().getOffset();
          caretsAfter.add(document.createRangeMarker(offset, offset));
        }
      }
      assert context != null;

      restoreBlockSelection(editor, caretsAfter, caretLine);

      for (RangeMarker insertionPoint : insertionPoints) {
        insertionPoint.dispose();
      }
      for (RangeMarker marker : caretsAfter) {
        marker.dispose();
      }

    } else {
      context =
          insertItem(
              indicator, item, completionChar, items, update, editor, caretOffset, idEndOffset);
    }
    return context;
  }
  private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection(
      final CompletionProgressIndicator indicator,
      final LookupElement item,
      final char completionChar,
      final List<LookupElement> items,
      final CompletionLookupArranger.StatisticsUpdate update) {
    final Editor editor = indicator.getEditor();

    final int caretOffset = editor.getCaretModel().getOffset();
    int idEndOffset = indicator.getIdentifierEndOffset();
    if (idEndOffset < 0) {
      idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset);
    }
    final int idEndOffsetDelta = idEndOffset - caretOffset;

    CompletionAssertions.WatchingInsertionContext context = null;
    if (editor.getSelectionModel().hasBlockSelection()
        && editor.getSelectionModel().getBlockSelectionEnds().length > 0) {
      List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>();
      int idDelta = 0;
      Document document = editor.getDocument();
      int caretLine = document.getLineNumber(editor.getCaretModel().getOffset());

      for (int point : editor.getSelectionModel().getBlockSelectionEnds()) {
        insertionPoints.add(document.createRangeMarker(point, point));
        if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) {
          idDelta = idEndOffset - point;
        }
      }

      List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>();
      for (RangeMarker marker : insertionPoints) {
        if (marker.isValid()) {
          int insertionPoint = marker.getStartOffset();
          context =
              insertItem(
                  indicator,
                  item,
                  completionChar,
                  items,
                  update,
                  editor,
                  insertionPoint,
                  idDelta + insertionPoint);
          int offset = editor.getCaretModel().getOffset();
          caretsAfter.add(document.createRangeMarker(offset, offset));
        }
      }
      assert context != null;

      restoreBlockSelection(editor, caretsAfter, caretLine);

      for (RangeMarker insertionPoint : insertionPoints) {
        insertionPoint.dispose();
      }
      for (RangeMarker marker : caretsAfter) {
        marker.dispose();
      }

    } else {
      final Ref<CompletionAssertions.WatchingInsertionContext> contextRef =
          new Ref<CompletionAssertions.WatchingInsertionContext>();
      editor
          .getCaretModel()
          .runForEachCaret(
              new CaretAction() {
                @Override
                public void perform(Caret caret) {
                  CompletionAssertions.WatchingInsertionContext currentContext =
                      insertItem(
                          indicator,
                          item,
                          completionChar,
                          items,
                          update,
                          editor,
                          caret.getOffset(),
                          caret.getOffset() + idEndOffsetDelta);
                  if (caret
                      .getVisualPosition()
                      .equals(editor.getCaretModel().getPrimaryCaret().getVisualPosition())) {
                    contextRef.set(currentContext);
                  }
                }
              });
      context = contextRef.get();
    }
    return context;
  }
  public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
    myProject = project;
    myFile = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage());
    myEditor = editor;

    PsiElement context = myFile.getContext();

    if (context != null && (context.textContains('\'') || context.textContains('\"'))) {
      String s = context.getText();
      if (StringUtil.startsWith(s, "\"") || StringUtil.startsWith(s, "\'")) {
        myFile = context.getContainingFile();
        myEditor = editor instanceof EditorWindow ? ((EditorWindow) editor).getDelegate() : editor;
      }
    }

    myDocument = myEditor.getDocument();
    if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) {
      return;
    }

    PsiDocumentManager.getInstance(project).commitDocument(myDocument);

    FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line");

    myCodeStyleManager = CodeStyleManager.getInstance(myProject);

    final SelectionModel selectionModel = myEditor.getSelectionModel();

    boolean hasSelection = selectionModel.hasSelection();
    myStartOffset = selectionModel.getSelectionStart();
    myEndOffset = selectionModel.getSelectionEnd();

    FoldRegion fold = myEditor.getFoldingModel().getCollapsedRegionAtOffset(myStartOffset);
    if (fold != null
        && fold.shouldNeverExpand()
        && fold.getStartOffset() == myStartOffset
        && fold.getEndOffset() == myEndOffset) {
      // Foldings that never expand are automatically selected, so the fact it is selected must not
      // interfer with commenter's logic
      hasSelection = false;
    }

    if (myDocument.getTextLength() == 0) return;

    while (true) {
      int lastLineEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset));
      FoldRegion collapsedAt = myEditor.getFoldingModel().getCollapsedRegionAtOffset(lastLineEnd);
      if (collapsedAt != null) {
        final int endOffset = collapsedAt.getEndOffset();
        if (endOffset <= myEndOffset) {
          break;
        }
        myEndOffset = endOffset;
      } else {
        break;
      }
    }

    boolean wholeLinesSelected =
        !hasSelection
            || myStartOffset
                    == myDocument.getLineStartOffset(myDocument.getLineNumber(myStartOffset))
                && myEndOffset
                    == myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset - 1)) + 1;

    boolean startingNewLineComment =
        !hasSelection
            && isLineEmpty(myDocument.getLineNumber(myStartOffset))
            && !Comparing.equal(
                IdeActions.ACTION_COMMENT_LINE,
                ActionManagerEx.getInstanceEx().getPrevPreformedActionId());
    doComment();

    if (startingNewLineComment) {
      final Commenter commenter = myCommenters[0];
      if (commenter != null) {
        String prefix;
        if (commenter instanceof SelfManagingCommenter) {
          prefix =
              ((SelfManagingCommenter) commenter)
                  .getCommentPrefix(
                      myStartLine,
                      myDocument,
                      myCommenterStateMap.get((SelfManagingCommenter) commenter));
          if (prefix == null) prefix = ""; // TODO
        } else {
          prefix = commenter.getLineCommentPrefix();
          if (prefix == null) prefix = commenter.getBlockCommentPrefix();
        }

        int lineStart = myDocument.getLineStartOffset(myStartLine);
        lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t");
        lineStart += prefix.length();
        lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t");
        if (lineStart > myDocument.getTextLength()) lineStart = myDocument.getTextLength();
        myEditor.getCaretModel().moveToOffset(lineStart);
        myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      }
    } else {
      if (!hasSelection) {
        // Don't tweak caret position if we're already located on the last document line.
        LogicalPosition position = myEditor.getCaretModel().getLogicalPosition();
        if (position.line < myDocument.getLineCount() - 1) {
          int verticalShift =
              1
                  + myEditor.getSoftWrapModel().getSoftWrapsForLine(position.line).size()
                  - position.softWrapLinesOnCurrentLogicalLine;
          myEditor.getCaretModel().moveCaretRelatively(0, verticalShift, false, false, true);
        }
      } else {
        if (wholeLinesSelected) {
          selectionModel.setSelection(myStartOffset, selectionModel.getSelectionEnd());
        }
      }
    }
  }
  private void dumpEditorMarkupAndSelection(PrintStream dumpStream) {
    dumpStream.println(mySearchResults.getFindModel());
    if (myReplacementPreviewText != null) {
      dumpStream.println("--");
      dumpStream.println("Replacement Preview: " + myReplacementPreviewText);
    }
    dumpStream.println("--");

    Editor editor = mySearchResults.getEditor();

    RangeHighlighter[] highlighters = editor.getMarkupModel().getAllHighlighters();
    List<Pair<Integer, Character>> ranges = new ArrayList<Pair<Integer, Character>>();
    for (RangeHighlighter highlighter : highlighters) {
      ranges.add(new Pair<Integer, Character>(highlighter.getStartOffset(), '['));
      ranges.add(new Pair<Integer, Character>(highlighter.getEndOffset(), ']'));
    }

    SelectionModel selectionModel = editor.getSelectionModel();

    if (selectionModel.getSelectionStart() != selectionModel.getSelectionEnd()) {
      ranges.add(new Pair<Integer, Character>(selectionModel.getSelectionStart(), '<'));
      ranges.add(new Pair<Integer, Character>(selectionModel.getSelectionEnd(), '>'));
    }
    ranges.add(new Pair<Integer, Character>(-1, '\n'));
    ranges.add(new Pair<Integer, Character>(editor.getDocument().getTextLength() + 1, '\n'));
    ContainerUtil.sort(
        ranges,
        new Comparator<Pair<Integer, Character>>() {
          @Override
          public int compare(Pair<Integer, Character> pair, Pair<Integer, Character> pair2) {
            int res = pair.first - pair2.first;
            if (res == 0) {

              Character c1 = pair.second;
              Character c2 = pair2.second;
              if (c1 == '<' && c2 == '[') {
                return 1;
              } else if (c1 == '[' && c2 == '<') {
                return -1;
              }
              return c1.compareTo(c2);
            }
            return res;
          }
        });

    Document document = editor.getDocument();
    for (int i = 0; i < ranges.size() - 1; ++i) {
      Pair<Integer, Character> pair = ranges.get(i);
      Pair<Integer, Character> pair1 = ranges.get(i + 1);
      dumpStream.print(
          pair.second
              + document.getText(
                  TextRange.create(
                      Math.max(pair.first, 0), Math.min(pair1.first, document.getTextLength()))));
    }
    dumpStream.println("\n--");

    if (NotFound) {
      dumpStream.println("Not Found");
      dumpStream.println("--");
      NotFound = false;
    }

    for (RangeHighlighter highlighter : highlighters) {
      dumpStream.println(highlighter + " : " + highlighter.getTextAttributes());
    }
    dumpStream.println("------------");
  }
  public final void move(Editor editor, final PsiFile file) {
    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);
    myInfo.range1 = document.createRangeMarker(start, end);

    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';
    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.
    FoldRegion topRegion = null;
    FoldRegion bottomRegion = null;
    for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) {
      if (!foldRegion.isValid()
          || (!contains(myInfo.range1, foldRegion) && !contains(myInfo.range2, foldRegion))) {
        continue;
      }
      if (contains(myInfo.range1, foldRegion) && !contains(topRegion, foldRegion)) {
        topRegion = foldRegion;
      } else if (contains(myInfo.range2, foldRegion) && !contains(bottomRegion, foldRegion)) {
        bottomRegion = foldRegion;
      }
    }

    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();

    // Swap fold regions status if necessary.
    if (topRegion != null && bottomRegion != null) {
      final FoldRegion finalTopRegion = topRegion;
      final FoldRegion finalBottomRegion = bottomRegion;
      editor
          .getFoldingModel()
          .runBatchFoldingOperation(
              new Runnable() {
                @Override
                public void run() {
                  boolean topExpanded = finalTopRegion.isExpanded();
                  finalTopRegion.setExpanded(finalBottomRegion.isExpanded());
                  finalBottomRegion.setExpanded(topExpanded);
                }
              });
    }
    CodeFoldingManager.getInstance(project).allowFoldingOnCaretLine(editor);

    if (hasSelection) {
      restoreSelection(editor, selectionStart, selectionEnd, start, myInfo.range2.getStartOffset());
    }

    caretModel.moveToOffset(myInfo.range2.getStartOffset() + caretRelativePos);
    if (myInfo.indentTarget) {
      indentLinesIn(editor, file, document, project, myInfo.range2);
    }
    if (myInfo.indentSource) {
      indentLinesIn(editor, file, document, project, myInfo.range1);
    }

    myMover.afterMove(editor, file, myInfo, myIsDown);
    editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
  }
    @Override
    public void run() {
      CaretModel caretModel = myEditor.getCaretModel();
      try {
        final CharSequence chars = myDocument.getCharsSequence();
        int i = CharArrayUtil.shiftBackwardUntil(chars, myOffset - 1, LINE_SEPARATOR) - 1;
        i = CharArrayUtil.shiftBackwardUntil(chars, i, LINE_SEPARATOR) + 1;
        if (i < 0) i = 0;
        int lineStart = CharArrayUtil.shiftForward(chars, i, " \t");
        CodeDocumentationUtil.CommentContext commentContext =
            CodeDocumentationUtil.tryParseCommentContext(myFile, chars, myOffset, lineStart);

        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(getProject());
        if (commentContext.docStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          final String text = element.getText();
          final PsiElement parent = element.getParent();

          if (text.equals(commentContext.commenter.getDocumentationCommentPrefix())
                  && isDocComment(parent, commentContext.commenter)
              || text.startsWith(commentContext.commenter.getDocumentationCommentPrefix())
                  && element instanceof PsiComment) {
            PsiComment comment =
                isDocComment(parent, commentContext.commenter)
                    ? (PsiComment) parent
                    : (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();

            if (myOffset >= commentEnd) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                generateJavadoc(commentContext.commenter);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        } else if (commentContext.cStyleStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          if (element instanceof PsiComment
              && commentContext.commenter.getBlockCommentTokenType()
                  == ((PsiComment) element).getTokenType()) {
            final PsiComment comment = (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();
            if (myOffset >= commentEnd && myOffset < myFile.getTextRange().getEndOffset()) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                final int currentEndOfLine = CharArrayUtil.shiftForwardUntil(chars, myOffset, "\n");
                myDocument.insertString(
                    currentEndOfLine, " " + commentContext.commenter.getBlockCommentSuffix());
                int lstart = CharArrayUtil.shiftBackwardUntil(chars, myOffset, "\n");
                myDocument.insertString(currentEndOfLine, chars.subSequence(lstart, myOffset));
                psiDocumentManager.commitDocument(myDocument);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        }

        String indentInsideJavadoc = null;
        if (myOffset < myDocument.getTextLength()) {
          final int line = myDocument.getLineNumber(myOffset);
          if (line > 0 && (commentContext.docAsterisk || commentContext.docStart)) {
            indentInsideJavadoc =
                CodeDocumentationUtil.getIndentInsideJavadoc(
                    myDocument, myDocument.getLineStartOffset(line - 1));
          }
        }

        if (commentContext.docAsterisk) {
          commentContext.docAsterisk =
              insertDocAsterisk(
                  commentContext.lineStart,
                  commentContext.docAsterisk,
                  !StringUtil.isEmpty(indentInsideJavadoc),
                  commentContext.commenter);
        }

        boolean docIndentApplied = false;
        CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance();
        if (codeInsightSettings.SMART_INDENT_ON_ENTER
            || myForceIndent
            || commentContext.docStart
            || commentContext.docAsterisk
            || commentContext.slashSlash) {
          final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
          myOffset = codeStyleManager.adjustLineIndent(myFile, myOffset);
          psiDocumentManager.commitAllDocuments();

          if (!StringUtil.isEmpty(indentInsideJavadoc) && myOffset < myDocument.getTextLength()) {
            myDocument.insertString(myOffset + 1, indentInsideJavadoc);
            myOffset += indentInsideJavadoc.length();
            docIndentApplied = true;
          }

          if (myForceIndent && indentInsideJavadoc != null) {
            int indentSize =
                CodeStyleSettingsManager.getSettings(myProject).getIndentSize(myFile.getFileType());
            myDocument.insertString(myOffset + 1, StringUtil.repeatSymbol(' ', indentSize));
            myCaretAdvance += indentSize;
          }
        }

        if ((commentContext.docAsterisk || commentContext.docStart || commentContext.slashSlash)
            && !docIndentApplied) {
          if (myInsertSpace) {
            if (myOffset == myDocument.getTextLength()) {
              myDocument.insertString(myOffset, " ");
            }
            myDocument.insertString(myOffset + 1, " ");
          }

          final char c = myDocument.getCharsSequence().charAt(myOffset);
          if (c != '\n') {
            myOffset += 1;
          }
        }

        if ((commentContext.docAsterisk || commentContext.slashSlash) && !commentContext.docStart) {
          myCaretAdvance +=
              commentContext.slashSlash
                  ? commentContext.commenter.getLineCommentPrefix().length()
                  : 1;
        }
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }

      myOffset = Math.min(myOffset, myDocument.getTextLength());
      caretModel.moveToOffset(myOffset);
      myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      myEditor.getSelectionModel().removeSelection();
      if (myCaretAdvance != 0) {
        LogicalPosition caretPosition = caretModel.getLogicalPosition();
        LogicalPosition pos =
            new LogicalPosition(caretPosition.line, caretPosition.column + myCaretAdvance);
        caretModel.moveToLogicalPosition(pos);
      }
    }
Example #27
0
  private static void doPaste(
      final Editor editor,
      final Project project,
      final PsiFile file,
      final Document document,
      final Producer<Transferable> producer) {
    Transferable content = null;

    if (producer != null) {
      content = producer.produce();
    } else {
      CopyPasteManager manager = CopyPasteManager.getInstance();
      if (manager.areDataFlavorsAvailable(DataFlavor.stringFlavor)) {
        content = manager.getContents();
        if (content != null) {
          manager.stopKillRings();
        }
      }
    }

    if (content != null) {
      String text = null;
      try {
        text = (String) content.getTransferData(DataFlavor.stringFlavor);
      } catch (Exception e) {
        editor.getComponent().getToolkit().beep();
      }
      if (text == null) return;

      final CodeInsightSettings settings = CodeInsightSettings.getInstance();

      final Map<CopyPastePostProcessor, TextBlockTransferableData> extraData =
          new HashMap<CopyPastePostProcessor, TextBlockTransferableData>();
      for (CopyPastePostProcessor processor :
          Extensions.getExtensions(CopyPastePostProcessor.EP_NAME)) {
        TextBlockTransferableData data = processor.extractTransferableData(content);
        if (data != null) {
          extraData.put(processor, data);
        }
      }

      text = TextBlockTransferable.convertLineSeparators(text, "\n", extraData.values());

      final CaretModel caretModel = editor.getCaretModel();
      final SelectionModel selectionModel = editor.getSelectionModel();
      final int col = caretModel.getLogicalPosition().column;

      // There is a possible case that we want to perform paste while there is an active selection
      // at the editor and caret is located
      // inside it (e.g. Ctrl+A is pressed while caret is not at the zero column). We want to insert
      // the text at selection start column
      // then, hence, inserted block of text should be indented according to the selection start as
      // well.
      final int blockIndentAnchorColumn;
      final int caretOffset = caretModel.getOffset();
      if (selectionModel.hasSelection() && caretOffset >= selectionModel.getSelectionStart()) {
        blockIndentAnchorColumn =
            editor.offsetToLogicalPosition(selectionModel.getSelectionStart()).column;
      } else {
        blockIndentAnchorColumn = col;
      }

      // We assume that EditorModificationUtil.insertStringAtCaret() is smart enough to remove
      // currently selected text (if any).

      RawText rawText = RawText.fromTransferable(content);
      String newText = text;
      for (CopyPastePreProcessor preProcessor :
          Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) {
        newText = preProcessor.preprocessOnPaste(project, file, editor, newText, rawText);
      }
      int indentOptions =
          text.equals(newText) ? settings.REFORMAT_ON_PASTE : CodeInsightSettings.REFORMAT_BLOCK;
      text = newText;

      if (LanguageFormatting.INSTANCE.forContext(file) == null
          && indentOptions != CodeInsightSettings.NO_REFORMAT) {
        indentOptions = CodeInsightSettings.INDENT_BLOCK;
      }

      final String _text = text;
      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                @Override
                public void run() {
                  EditorModificationUtil.insertStringAtCaret(editor, _text, false, true);
                }
              });

      int length = text.length();
      int offset = caretModel.getOffset() - length;
      if (offset < 0) {
        length += offset;
        offset = 0;
      }
      final RangeMarker bounds = document.createRangeMarker(offset, offset + length);

      caretModel.moveToOffset(bounds.getEndOffset());
      editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      selectionModel.removeSelection();

      final Ref<Boolean> indented = new Ref<Boolean>(Boolean.FALSE);
      for (Map.Entry<CopyPastePostProcessor, TextBlockTransferableData> e : extraData.entrySet()) {
        //noinspection unchecked
        e.getKey()
            .processTransferableData(project, editor, bounds, caretOffset, indented, e.getValue());
      }

      boolean pastedTextContainsWhiteSpacesOnly =
          CharArrayUtil.shiftForward(document.getCharsSequence(), bounds.getStartOffset(), " \n\t")
              >= bounds.getEndOffset();

      VirtualFile virtualFile = file.getVirtualFile();
      if (!pastedTextContainsWhiteSpacesOnly
          && (virtualFile == null
              || !SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile))) {
        final int indentOptions1 = indentOptions;
        ApplicationManager.getApplication()
            .runWriteAction(
                new Runnable() {
                  @Override
                  public void run() {
                    switch (indentOptions1) {
                      case CodeInsightSettings.INDENT_BLOCK:
                        if (!indented.get()) {
                          indentBlock(
                              project,
                              editor,
                              bounds.getStartOffset(),
                              bounds.getEndOffset(),
                              blockIndentAnchorColumn);
                        }
                        break;

                      case CodeInsightSettings.INDENT_EACH_LINE:
                        if (!indented.get()) {
                          indentEachLine(
                              project, editor, bounds.getStartOffset(), bounds.getEndOffset());
                        }
                        break;

                      case CodeInsightSettings.REFORMAT_BLOCK:
                        indentEachLine(
                            project,
                            editor,
                            bounds.getStartOffset(),
                            bounds
                                .getEndOffset()); // this is needed for example when inserting a
                                                  // comment before method
                        reformatBlock(
                            project, editor, bounds.getStartOffset(), bounds.getEndOffset());
                        break;
                    }
                  }
                });
      }

      if (bounds.isValid()) {
        caretModel.moveToOffset(bounds.getEndOffset());
        editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
        selectionModel.removeSelection();
        editor.putUserData(EditorEx.LAST_PASTED_REGION, TextRange.create(bounds));
      }
    }
  }
  private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection(
      final CompletionProgressIndicator indicator,
      final LookupElement item,
      final char completionChar,
      final List<LookupElement> items,
      final CompletionLookupArranger.StatisticsUpdate update) {
    final Editor editor = indicator.getEditor();

    final int caretOffset = editor.getCaretModel().getOffset();
    int idEndOffset = indicator.getIdentifierEndOffset();
    if (idEndOffset < 0) {
      idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset);
    }
    final int idEndOffsetDelta = idEndOffset - caretOffset;

    CompletionAssertions.WatchingInsertionContext context = null;
    if (editor.getSelectionModel().hasBlockSelection()
        && editor.getSelectionModel().getBlockSelectionEnds().length > 0) {
      List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>();
      int idDelta = 0;
      Document document = editor.getDocument();
      int caretLine = document.getLineNumber(editor.getCaretModel().getOffset());

      for (int point : editor.getSelectionModel().getBlockSelectionEnds()) {
        insertionPoints.add(document.createRangeMarker(point, point));
        if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) {
          idDelta = idEndOffset - point;
        }
      }

      List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>();
      for (RangeMarker marker : insertionPoints) {
        if (marker.isValid()) {
          int insertionPoint = marker.getStartOffset();
          context =
              insertItem(
                  indicator,
                  item,
                  completionChar,
                  items,
                  update,
                  editor,
                  indicator.getParameters().getOriginalFile(),
                  insertionPoint,
                  idDelta + insertionPoint);
          int offset = editor.getCaretModel().getOffset();
          caretsAfter.add(document.createRangeMarker(offset, offset));
        }
      }
      assert context != null;

      restoreBlockSelection(editor, caretsAfter, caretLine);

      for (RangeMarker insertionPoint : insertionPoints) {
        insertionPoint.dispose();
      }
      for (RangeMarker marker : caretsAfter) {
        marker.dispose();
      }

    } else if (editor.getCaretModel().supportsMultipleCarets()) {
      final List<CompletionAssertions.WatchingInsertionContext> contexts =
          new ArrayList<CompletionAssertions.WatchingInsertionContext>();
      final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
      hostEditor
          .getCaretModel()
          .runForEachCaret(
              new CaretAction() {
                @Override
                public void perform(Caret caret) {
                  PsiFile hostFile =
                      InjectedLanguageUtil.getTopLevelFile(
                          indicator.getParameters().getOriginalFile());
                  PsiFile targetFile =
                      InjectedLanguageUtil.findInjectedPsiNoCommit(hostFile, caret.getOffset());
                  Editor targetEditor =
                      InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, targetFile);
                  int targetCaretOffset = targetEditor.getCaretModel().getOffset();
                  CompletionAssertions.WatchingInsertionContext currentContext =
                      insertItem(
                          indicator,
                          item,
                          completionChar,
                          items,
                          update,
                          targetEditor,
                          targetFile == null ? hostFile : targetFile,
                          targetCaretOffset,
                          targetCaretOffset + idEndOffsetDelta);
                  contexts.add(currentContext);
                }
              },
              true);
      context = contexts.get(contexts.size() - 1);
      if (context.shouldAddCompletionChar()
          && context.getCompletionChar() != Lookup.COMPLETE_STATEMENT_SELECT_CHAR) {
        ApplicationManager.getApplication()
            .runWriteAction(
                new Runnable() {
                  @Override
                  public void run() {
                    DataContext dataContext =
                        DataManager.getInstance().getDataContext(editor.getContentComponent());
                    EditorActionManager.getInstance()
                        .getTypedAction()
                        .getHandler()
                        .execute(editor, completionChar, dataContext);
                  }
                });
      }
      for (CompletionAssertions.WatchingInsertionContext insertionContext : contexts) {
        insertionContext.stopWatching();
      }
    } else {
      context =
          insertItem(
              indicator,
              item,
              completionChar,
              items,
              update,
              editor,
              indicator.getParameters().getOriginalFile(),
              caretOffset,
              idEndOffset);
    }
    return context;
  }
  private void addListeners() {
    myEditor
        .getDocument()
        .addDocumentListener(
            new DocumentAdapter() {
              @Override
              public void documentChanged(DocumentEvent e) {
                if (!myChangeGuard && !myFinishing) {
                  hide();
                }
              }
            },
            this);

    final CaretListener caretListener =
        new CaretAdapter() {
          @Override
          public void caretPositionChanged(CaretEvent e) {
            if (!myChangeGuard && !myFinishing) {
              hide();
            }
          }
        };
    final SelectionListener selectionListener =
        new SelectionListener() {
          @Override
          public void selectionChanged(final SelectionEvent e) {
            if (!myChangeGuard && !myFinishing) {
              hide();
            }
          }
        };
    final EditorMouseListener mouseListener =
        new EditorMouseAdapter() {
          @Override
          public void mouseClicked(EditorMouseEvent e) {
            e.consume();
            hide();
          }
        };

    myEditor.getCaretModel().addCaretListener(caretListener);
    myEditor.getSelectionModel().addSelectionListener(selectionListener);
    myEditor.addEditorMouseListener(mouseListener);
    Disposer.register(
        this,
        new Disposable() {
          @Override
          public void dispose() {
            myEditor.getCaretModel().removeCaretListener(caretListener);
            myEditor.getSelectionModel().removeSelectionListener(selectionListener);
            myEditor.removeEditorMouseListener(mouseListener);
          }
        });

    JComponent editorComponent = myEditor.getContentComponent();
    if (editorComponent.isShowing()) {
      Disposer.register(
          this,
          new UiNotifyConnector(
              editorComponent,
              new Activatable() {
                @Override
                public void showNotify() {}

                @Override
                public void hideNotify() {
                  hideLookup(false);
                }
              }));
    }

    myList.addListSelectionListener(
        new ListSelectionListener() {
          private LookupElement oldItem = null;

          @Override
          public void valueChanged(@NotNull ListSelectionEvent e) {
            if (!myUpdating) {
              final LookupElement item = getCurrentItem();
              fireCurrentItemChanged(oldItem, item);
              oldItem = item;
            }
          }
        });

    new ClickListener() {
      @Override
      public boolean onClick(@NotNull MouseEvent e, int clickCount) {
        setFocusDegree(FocusDegree.FOCUSED);
        markSelectionTouched();

        if (clickCount == 2) {
          CommandProcessor.getInstance()
              .executeCommand(
                  myProject,
                  new Runnable() {
                    @Override
                    public void run() {
                      finishLookup(NORMAL_SELECT_CHAR);
                    }
                  },
                  "",
                  null);
        }
        return true;
      }
    }.installOn(myList);
  }
  @Nullable
  @Override
  public TextBlockTransferableData collectTransferableData(
      PsiFile file, Editor editor, int[] startOffsets, int[] endOffsets) {
    if (!Registry.is("editor.richcopy.enable")) {
      return null;
    }

    try {
      for (TextWithMarkupBuilder builder : myBuilders) {
        builder.reset();
      }
      SelectionModel selectionModel = editor.getSelectionModel();
      if (selectionModel.hasBlockSelection()) {
        return null; // unsupported legacy mode
      }

      RichCopySettings settings = RichCopySettings.getInstance();
      List<Caret> carets = editor.getCaretModel().getAllCarets();
      Caret firstCaret = carets.get(0);
      final int indentSymbolsToStrip;
      final int firstLineStartOffset;
      if (settings.isStripIndents() && carets.size() == 1) {
        Pair<Integer, Integer> p =
            calcIndentSymbolsToStrip(
                editor.getDocument(), firstCaret.getSelectionStart(), firstCaret.getSelectionEnd());
        firstLineStartOffset = p.first;
        indentSymbolsToStrip = p.second;
      } else {
        firstLineStartOffset = firstCaret.getSelectionStart();
        indentSymbolsToStrip = 0;
      }
      logInitial(editor, startOffsets, endOffsets, indentSymbolsToStrip, firstLineStartOffset);
      CharSequence text = editor.getDocument().getCharsSequence();
      EditorColorsScheme schemeToUse = settings.getColorsScheme(editor.getColorsScheme());
      EditorHighlighter highlighter =
          HighlighterFactory.createHighlighter(
              file.getVirtualFile(), schemeToUse, file.getProject());
      highlighter.setText(text);
      MarkupModel markupModel =
          DocumentMarkupModel.forDocument(editor.getDocument(), file.getProject(), false);
      Context context = new Context(text, schemeToUse, indentSymbolsToStrip);
      int shift = 0;
      int endOffset = 0;
      Caret prevCaret = null;

      for (Caret caret : carets) {
        int caretSelectionStart = caret.getSelectionStart();
        int caretSelectionEnd = caret.getSelectionEnd();
        int startOffsetToUse;
        if (caret == firstCaret) {
          startOffsetToUse = firstLineStartOffset;
        } else {
          startOffsetToUse = caretSelectionStart;
          assert prevCaret != null;
          String prevCaretSelectedText = prevCaret.getSelectedText();
          // Block selection fills short lines by white spaces.
          int fillStringLength =
              prevCaretSelectedText == null
                  ? 0
                  : prevCaretSelectedText.length()
                      - (prevCaret.getSelectionEnd() - prevCaret.getSelectionStart());
          int endLineOffset = endOffset + shift + fillStringLength;
          context.builder.addText(endLineOffset, endLineOffset + 1);
          shift++; // Block selection ends '\n' at line end
          shift += fillStringLength;
        }
        shift += endOffset - caretSelectionStart;
        endOffset = caretSelectionEnd;
        context.reset(shift);
        prevCaret = caret;
        if (endOffset <= startOffsetToUse) {
          continue;
        }
        MarkupIterator markupIterator =
            new MarkupIterator(
                text,
                new CompositeRangeIterator(
                    schemeToUse,
                    new HighlighterRangeIterator(highlighter, startOffsetToUse, endOffset),
                    new MarkupModelRangeIterator(
                        markupModel, schemeToUse, startOffsetToUse, endOffset)),
                schemeToUse);
        try {
          context.iterate(markupIterator, endOffset);
        } finally {
          markupIterator.dispose();
        }
      }
      SyntaxInfo syntaxInfo = context.finish();
      logSyntaxInfo(syntaxInfo);

      for (TextWithMarkupBuilder builder : myBuilders) {
        builder.build(syntaxInfo);
      }
    } catch (Exception e) {
      // catching the exception so that the rest of copy/paste functionality can still work fine
      LOG.error(e);
    }
    return null;
  }