private static ImmutableActionChangeRange @Nullable [] getAffectedRanges(@NotNull UndoRedoList<? extends ImmutableActionChangeRange> stack,
                                                                           @NotNull Map<Integer, ? extends ActionChangeRange> rangesToMove) {
    Map<Integer, ActionChangeRange> notSeenRanges = new HashMap<>(rangesToMove);
    ListIterator<? extends ImmutableActionChangeRange> iterator = stack.listIterator(stack.size());
    int affectedRangeCount = 0;
    boolean changed = false;
    while (iterator.hasPrevious()) {
      affectedRangeCount++;
      ImmutableActionChangeRange changeRange = iterator.previous();
      ActionChangeRange removed = notSeenRanges.remove(changeRange.getId());
      if (removed != null && removed.getTimestamp() != changeRange.getTimestamp()) {
        changed = true;
      }
      if (notSeenRanges.isEmpty()) {
        break;
      }
    }
    if (!notSeenRanges.isEmpty()) {
      throw new IllegalArgumentException("Stack doesn't contain these ranges: " + notSeenRanges);
    }
    if (affectedRangeCount == rangesToMove.size() && !changed) {
      return null;  // No need to move: changes are already on stack's top
    }
    ImmutableActionChangeRange[] affected = new ImmutableActionChangeRange[affectedRangeCount];
    for (int i = 0; i < affectedRangeCount; i++) {
      affected[i] = iterator.next();
    }
    return affected;
  }
private static ImmutableActionChangeRange @Nullable [] getAffectedRanges(@NotNull UndoRedoList<? extends ImmutableActionChangeRange> stack,
                                                                           @NotNull Map<Integer, ? extends ActionChangeRange> rangesToMove)