@Override
 public FoldRegion createFoldRegion(
     int startOffset,
     int endOffset,
     @NotNull String placeholder,
     @Nullable FoldingGroup group,
     boolean neverExpands) {
   FoldRegionImpl region =
       new FoldRegionImpl(myEditor, startOffset, endOffset, placeholder, group, neverExpands);
   LOG.assertTrue(region.isValid());
   return region;
 }
 public FoldRegion createFoldRegion(
     int startOffset,
     int endOffset,
     @NotNull String placeholder,
     FoldingGroup group,
     boolean neverExpands) {
   if (startOffset + 1 >= endOffset) {
     LOG.error("Invalid offsets: (" + startOffset + ", " + endOffset + ")");
   }
   FoldRegionImpl region =
       new FoldRegionImpl(myEditor, startOffset, endOffset, placeholder, group, neverExpands);
   LOG.assertTrue(region.isValid());
   return region;
 }
  public void collapseFoldRegion(FoldRegion region) {
    assertIsDispatchThreadForEditor();
    if (!region.isExpanded()) return;

    if (!myIsBatchFoldingProcessing) {
      LOG.error("Fold regions must be collapsed or expanded inside batchFoldProcessing() only.");
    }

    List<Caret> carets = myEditor.getCaretModel().getAllCarets();
    for (Caret caret : carets) {
      LogicalPosition caretPosition = caret.getLogicalPosition();
      int caretOffset = myEditor.logicalPositionToOffset(caretPosition);

      if (FoldRegionsTree.contains(region, caretOffset)) {
        if (myDoNotCollapseCaret) return;
      }
    }
    for (Caret caret : carets) {
      LogicalPosition caretPosition = caret.getLogicalPosition();
      int caretOffset = myEditor.logicalPositionToOffset(caretPosition);

      if (FoldRegionsTree.contains(region, caretOffset)) {
        if (caret.getUserData(SAVED_CARET_POSITION) == null) {
          caret.putUserData(SAVED_CARET_POSITION, caretPosition.withoutVisualPositionInfo());
        }
      }
    }

    myFoldRegionsProcessed = true;
    ((FoldRegionImpl) region).setExpandedInternal(false);
    notifyListenersOnFoldRegionStateChange(region);
  }
  public void collapseFoldRegion(FoldRegion region) {
    assertIsDispatchThread();
    if (!region.isExpanded()) return;

    if (!myIsBatchFoldingProcessing) {
      LOG.error("Fold regions must be collapsed or expanded inside batchFoldProcessing() only.");
    }

    LogicalPosition caretPosition = myEditor.getCaretModel().getLogicalPosition();

    int caretOffset = myEditor.logicalPositionToOffset(caretPosition);

    if (FoldRegionsTree.contains(region, caretOffset)) {
      if (myDoNotCollapseCaret) return;

      if (!myCaretPositionSaved) {
        mySavedCaretX = caretPosition.column;
        mySavedCaretY = caretPosition.line;
        myCaretPositionSaved = true;
      }
    }

    int selectionStart = myEditor.getSelectionModel().getSelectionStart();
    int selectionEnd = myEditor.getSelectionModel().getSelectionEnd();

    if (FoldRegionsTree.contains(region, selectionStart - 1)
        || FoldRegionsTree.contains(region, selectionEnd))
      myEditor.getSelectionModel().removeSelection();

    myFoldRegionsProcessed = true;
    ((FoldRegionImpl) region).setExpandedInternal(false);
    notifyListenersOnFoldRegionStateChange(region);
  }
  public void expandFoldRegion(FoldRegion region) {
    assertIsDispatchThread();
    if (region.isExpanded() || region.shouldNeverExpand()) return;

    if (!myIsBatchFoldingProcessing) {
      LOG.error("Fold regions must be collapsed or expanded inside batchFoldProcessing() only.");
    }

    if (myCaretPositionSaved) {
      int savedOffset =
          myEditor.logicalPositionToOffset(new LogicalPosition(mySavedCaretY, mySavedCaretX));

      FoldRegion[] allCollapsed = myFoldTree.fetchCollapsedAt(savedOffset);
      if (allCollapsed.length == 1 && allCollapsed[0] == region) {
        LogicalPosition pos = new LogicalPosition(mySavedCaretY, mySavedCaretX);
        myEditor.getCaretModel().moveToLogicalPosition(pos);
      }
    }

    myFoldRegionsProcessed = true;
    ((FoldRegionImpl) region).setExpandedInternal(true);
    notifyListenersOnFoldRegionStateChange(region);
  }
  public void expandFoldRegion(FoldRegion region) {
    assertIsDispatchThreadForEditor();
    if (region.isExpanded() || region.shouldNeverExpand()) return;

    if (!myIsBatchFoldingProcessing) {
      LOG.error("Fold regions must be collapsed or expanded inside batchFoldProcessing() only.");
    }

    for (Caret caret : myEditor.getCaretModel().getAllCarets()) {
      LogicalPosition savedPosition = caret.getUserData(SAVED_CARET_POSITION);
      if (savedPosition != null) {
        int savedOffset = myEditor.logicalPositionToOffset(savedPosition);

        FoldRegion[] allCollapsed = myFoldTree.fetchCollapsedAt(savedOffset);
        if (allCollapsed.length == 1 && allCollapsed[0] == region) {
          caret.moveToLogicalPosition(savedPosition);
        }
      }
    }

    myFoldRegionsProcessed = true;
    ((FoldRegionImpl) region).setExpandedInternal(true);
    notifyListenersOnFoldRegionStateChange(region);
  }