@ApiStatus.Internal
  public void replaceString(int startOffset, int endOffset, int moveOffset, @NotNull CharSequence s,
                            long newModificationStamp, boolean wholeTextReplaced) {
    assertBounds(startOffset, endOffset);

    assertWriteAccess();
    assertValidSeparators(s);

    if (moveOffset != startOffset && startOffset != endOffset && s.length() != 0) {
      throw new IllegalArgumentException(
        "moveOffset != startOffset for a modification which is neither an insert nor deletion." +
        " startOffset: " + startOffset + "; endOffset: " + endOffset + ";" + "; moveOffset: " + moveOffset + ";");
    }

    int initialStartOffset = startOffset;
    int initialOldLength = endOffset - startOffset;

    int newStringLength = s.length();
    CharSequence chars = myText;
    int newStartInString = 0;
    while (newStartInString < newStringLength &&
           startOffset < endOffset &&
           s.charAt(newStartInString) == chars.charAt(startOffset)) {
      startOffset++;
      newStartInString++;
    }
    if (newStartInString == newStringLength &&
        startOffset == endOffset &&
        !wholeTextReplaced) {
      return;
    }

    int newEndInString = newStringLength;
    while (endOffset > startOffset &&
           newEndInString > newStartInString &&
           s.charAt(newEndInString - 1) == chars.charAt(endOffset - 1)) {
      newEndInString--;
      endOffset--;
    }

    if (startOffset == 0 && endOffset == getTextLength()) {
      wholeTextReplaced = true;
    }

    CharSequence changedPart = s.subSequence(newStartInString, newEndInString);
    CharSequence sToDelete = myText.subtext(startOffset, endOffset);
    boolean isForceIgnoreGuardsOnFullUpdate = getUserData(IGNORE_RANGE_GUARDS_ON_FULL_UPDATE) == Boolean.TRUE && wholeTextReplaced;
    if (!isForceIgnoreGuardsOnFullUpdate) {
      RangeMarker guard = getRangeGuard(startOffset, endOffset);
      if (guard != null) {
        throwGuardedFragment(guard, startOffset, sToDelete, changedPart);
      }
    }


    ImmutableCharSequence newText;
    if (wholeTextReplaced && s instanceof ImmutableCharSequence) {
      newText = (ImmutableCharSequence)s;
    }
    else {
      newText = myText.replace(startOffset, endOffset, changedPart);
      changedPart = newText.subtext(startOffset, startOffset + changedPart.length());
    }
    boolean wasOptimized = initialStartOffset != startOffset || endOffset - startOffset != initialOldLength;
    updateText(newText, startOffset, sToDelete, changedPart, wholeTextReplaced, newModificationStamp,
               initialStartOffset, initialOldLength, wasOptimized ? startOffset : moveOffset);
    trimToSize();
  }
@ApiStatus.Internal
  public void replaceString(int startOffset, int endOffset, int moveOffset, @NotNull CharSequence s,
                            long newModificationStamp, boolean wholeTextReplaced)