boolean stripTrailingSpaces(@Nullable Project project, boolean inChangedLinesOnly, int @Nullable [] caretOffsets) {
    if (!isStripTrailingSpacesEnabled) {
      return true;
    }
    List<StripTrailingSpacesFilter> filters = new ArrayList<>();
    StripTrailingSpacesFilter specialFilter = null;
    for (StripTrailingSpacesFilterFactory filterFactory : StripTrailingSpacesFilterFactory.EXTENSION_POINT.getExtensionList()) {
      StripTrailingSpacesFilter filter = filterFactory.createFilter(project, this);
      if (specialFilter == null &&
          (filter == StripTrailingSpacesFilter.NOT_ALLOWED || filter == StripTrailingSpacesFilter.POSTPONED)) {
        specialFilter = filter;
      }
      else if (filter == StripTrailingSpacesFilter.ENFORCED_REMOVAL) {
        specialFilter = null;
        filters.clear();
        break;
      }
      else {
        filters.add(filter);
      }
    }

    if (specialFilter != null) {
      return specialFilter == StripTrailingSpacesFilter.NOT_ALLOWED;
    }

    Int2IntMap caretPositions = null;
    if (caretOffsets != null) {
      caretPositions = new Int2IntOpenHashMap(caretOffsets.length);
      for (int caretOffset : caretOffsets) {
        int line = getLineNumber(caretOffset);
        // need to remember only maximum caret offset on a line
        caretPositions.put(line, Math.max(caretOffset, caretPositions.get(line)));
      }
    }

    LineSet lineSet = getLineSet();
    int lineCount = getLineCount();
    int[] targetOffsets = new int[lineCount * 2];
    int targetOffsetPos = 0;
    boolean markAsNeedsStrippingLater = false;
    CharSequence text = myText;
    for (int line = 0; line < lineCount; line++) {
      int maxSpacesToLeave = getMaxSpacesToLeave(line, filters);
      if (inChangedLinesOnly && !lineSet.isModified(line) || maxSpacesToLeave < 0) continue;

      int whiteSpaceStart = -1;
      int lineEnd = lineSet.getLineEnd(line) - lineSet.getSeparatorLength(line);
      int lineStart = lineSet.getLineStart(line);
      for (int offset = lineEnd - 1; offset >= lineStart; offset--) {
        char c = text.charAt(offset);
        if (c != ' ' && c != '\t') {
          break;
        }
        whiteSpaceStart = offset;
      }
      if (whiteSpaceStart == -1) continue;

      if (caretPositions != null) {
        int caretPosition = caretPositions.get(line);
        if (whiteSpaceStart < caretPosition) {
          markAsNeedsStrippingLater = true;
          continue;
        }
      }

      int finalStart = whiteSpaceStart + maxSpacesToLeave;
      if (finalStart < lineEnd) {
        targetOffsets[targetOffsetPos++] = finalStart;
        targetOffsets[targetOffsetPos++] = lineEnd;
      }
    }
    int finalTargetOffsetPos = targetOffsetPos;
    // Document must be unblocked by now. If not, some Save handler attempted to modify PSI
    // which should have been caught by assertion in com.intellij.pom.core.impl.PomModelImpl.runTransaction
    DocumentUtil.writeInRunUndoTransparentAction(new DocumentRunnable(this, project) {
      @Override
      public void run() {
        DocumentUtil.executeInBulk(DocumentImpl.this, finalTargetOffsetPos > STRIP_TRAILING_SPACES_BULK_MODE_LINES_LIMIT * 2, () -> {
          int pos = finalTargetOffsetPos;
          while (pos > 0) {
            int endOffset = targetOffsets[--pos];
            int startOffset = targetOffsets[--pos];
            deleteString(startOffset, endOffset);
          }
        });
      }
    });
    return markAsNeedsStrippingLater;
  }
boolean stripTrailingSpaces(@Nullable Project project, boolean inChangedLinesOnly, int @Nullable [] caretOffsets)