@NotNull
  MarkupIterator<T> overlappingIterator(@NotNull TextRange rangeInterval) {
    l.readLock().lock();

    try {
      int startOffset = rangeInterval.getStartOffset();
      int endOffset = rangeInterval.getEndOffset();
      IntervalNode<T> firstOverlap = findMinOverlappingWith(getRoot(), rangeInterval, getModCount(), 0);
      if (firstOverlap == null) {
        l.readLock().unlock();
        //noinspection unchecked
        return MarkupIterator.EMPTY;
      }
      int firstOverlapDelta = firstOverlap.computeDeltaUpToRoot();
      int firstOverlapStart = firstOverlap.intervalStart() + firstOverlapDelta;
      int modCountBefore = getModCount();

      return new MarkupIterator<T>() {
        private IntervalNode<T> currentNode = firstOverlap;
        private int deltaUpToRootExclusive = firstOverlapDelta-firstOverlap.delta;
        private int indexInCurrentList;
        private T current;

        @Override
        public boolean hasNext() {
          if (current != null) return true;
          if (currentNode == null) return false;

          if (getModCount() != modCountBefore) throw new ConcurrentModificationException();

          if (nextInterval()) {
            return true;
          }

          while (true) {
            currentNode = nextNode(currentNode);
            if (currentNode == null) {
              return false;
            }
            if (overlaps(currentNode, rangeInterval, deltaUpToRootExclusive)) {
              assert currentNode.intervalStart() + deltaUpToRootExclusive + currentNode.delta >= firstOverlapStart;
              indexInCurrentList = 0;
              if (nextInterval()) {
                return true;
              }
            }
          }
        }

        private boolean nextInterval() {
          List<Supplier<? extends T>> intervals = currentNode.intervals;
          while (indexInCurrentList < intervals.size()) {
            T t = intervals.get(indexInCurrentList).get();
            indexInCurrentList++;
            if (t != null) {
              current = t;
              return true;
            }
          }
          indexInCurrentList = 0;
          return false;
        }

        @Override
        public T next() {
          if (!hasNext()) throw new NoSuchElementException();
          T t = current;
          current = null;
          return t;
        }

        @Override
        public T peek() {
          if (!hasNext()) throw new NoSuchElementException();
          return current;
        }

        @Override
        public void remove() {
          throw new IncorrectOperationException();
        }

        @Override
        public void dispose() {
          l.readLock().unlock();
        }

        // next node in in-order traversal
        private IntervalNode<T> nextNode(@NotNull IntervalNode<T> root) {
          assert root.isValid() : root;
          int delta = deltaUpToRootExclusive + root.delta;
          int myMaxEnd = maxEndOf(root, deltaUpToRootExclusive);
          if (startOffset > myMaxEnd) return null; // tree changed

          // try to go right down
          IntervalNode<T> right = root.getRight();
          if (right != null) {
            int rightMaxEnd = maxEndOf(right, delta);
            if (startOffset <= rightMaxEnd) {
              int rightDelta = delta + right.delta;
              IntervalNode<T> left;
              while ((left = right.getLeft()) != null && startOffset <= maxEndOf(left, rightDelta)) {
                right = left;
                rightDelta += right.delta;
              }
              deltaUpToRootExclusive = rightDelta - right.delta;
              return right;
            }
          }

          // go up
          while (true) {
            IntervalNode<T> parent = root.getParent();
            if (parent == null) return null;
            if (parent.intervalStart() + deltaUpToRootExclusive > endOffset) return null; // can't move right
            deltaUpToRootExclusive -= parent.delta;

            if (parent.getLeft() == root) {
              return parent;
            }

            root = parent;
          }
        }
      };
    }
    catch (RuntimeException | Error e) {
      l.readLock().unlock();
      throw e;
    }
  }
@NotNull
  MarkupIterator<T> overlappingIterator(@NotNull TextRange rangeInterval)