@Override
    public void beforeDocumentChange(@NotNull DocumentEvent e) {
      if (isDisposed()) return;
      enterBulkChangeUpdateBlock();

      if (getChangesCount() == 0) return;

      LineRange lineRange = DiffUtil.getAffectedLineRange(e);
      int shift = DiffUtil.countLinesShift(e);

      List<S> corruptedStates = new SmartList<>();
      for (int index = 0; index < getChangesCount(); index++) {
        S oldState = processDocumentChange(index, lineRange.start, lineRange.end, shift);
        if (oldState == null) continue;

        invalidateHighlighters(index);
        if (!isInsideCommand()) corruptedStates.add(oldState);
      }

      if (myUndoManager != null && !corruptedStates.isEmpty()) {
        // document undo is registered inside onDocumentChange, so our undo() will be called after its undo().
        // thus thus we can avoid checks for isUndoInProgress() (to avoid modification of the same TextMergeChange by this listener)
        myUndoManager.undoableActionPerformed(new MyUndoableAction(MergeModelBase.this, corruptedStates, true));
      }
    }
@Override
    public void beforeDocumentChange(@NotNull DocumentEvent e)