@Override
  // first pass - adjacent carets are grouped into blocks
  public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) {
    file = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage());

    PsiElement context = InjectedLanguageManager.getInstance(file.getProject()).getInjectionHost(file);

    if (context != null && shouldCommentInHostFile(file, context)) {
      file = context.getContainingFile();
      editor = editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor;
      caret = caret instanceof InjectedCaret ? ((InjectedCaret)caret).getDelegate() : caret;
    }

    Document document = editor.getDocument();
    boolean hasSelection = caret.hasSelection();
    int startOffset = caret.getSelectionStart();
    int endOffset = caret.getSelectionEnd();

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

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

    while (true) {
      int firstLineStart = DocumentUtil.getLineStartOffset(startOffset, document);
      FoldRegion collapsedAt = editor.getFoldingModel().getCollapsedRegionAtOffset(firstLineStart - 1);
      if (collapsedAt == null || isInvisibleRegionAtLineStart(collapsedAt) /* rendered doc case */) break;
      int regionStartOffset = collapsedAt.getStartOffset();
      if (regionStartOffset >= startOffset) break;
      startOffset = regionStartOffset;
    }
    if (!hasSelection || !DocumentUtil.isAtLineStart(endOffset, document)) {
      while (true) {
        int lastLineEnd = DocumentUtil.getLineEndOffset(endOffset, document);
        FoldRegion collapsedAt = editor.getFoldingModel().getCollapsedRegionAtOffset(lastLineEnd);
        if (collapsedAt == null) break;
        int regionEndOffset = collapsedAt.getEndOffset();
        if (regionEndOffset <= endOffset) break;
        endOffset = regionEndOffset;
      }
    }

    int startLine = document.getLineNumber(startOffset);
    int endLine = document.getLineNumber(endOffset);

    if (endLine > startLine && document.getLineStartOffset(endLine) == endOffset) {
      endLine--;
    }

    Block lastBlock = myBlocks.isEmpty() ? null : myBlocks.get(myBlocks.size() - 1);
    Block currentBlock;
    if (lastBlock == null || lastBlock.editor != editor || lastBlock.psiFile != file || startLine > (lastBlock.endLine + 1)) {
      currentBlock = new Block();
      currentBlock.editor = editor;
      currentBlock.psiFile = file;
      currentBlock.startLine = startLine;
      myBlocks.add(currentBlock);
    }
    else {
      currentBlock = lastBlock;
    }
    currentBlock.carets.add(caret);
    currentBlock.endLine = endLine;

    boolean wholeLinesSelected = !hasSelection ||
                                 startOffset == document.getLineStartOffset(document.getLineNumber(startOffset)) &&
                                 endOffset == document.getLineEndOffset(document.getLineNumber(endOffset - 1)) + 1;
    boolean startingNewLineComment = !hasSelection
                                     && DocumentUtil.isLineEmpty(document, document.getLineNumber(startOffset))
                                     && !Objects
      .equals(IdeActions.ACTION_COMMENT_LINE, ActionManagerEx.getInstanceEx().getPrevPreformedActionId());
    currentBlock.caretUpdate = startingNewLineComment ? CaretUpdate.PUT_AT_COMMENT_START :
                               !hasSelection ? (AdvancedSettings.getBoolean("editor.comment.line.caret.down") ? CaretUpdate.SHIFT_DOWN : null) :
                               wholeLinesSelected ? CaretUpdate.RESTORE_SELECTION : null;
  }
@Override
  // first pass - adjacent carets are grouped into blocks
  public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file)