public static <T, V> void processIntersections(@NotNull List<? extends T> elements1,
                                                 @NotNull List<? extends V> elements2,
                                                 @NotNull Convertor<? super T, ? extends TextRange> convertor1,
                                                 @NotNull Convertor<? super V, ? extends TextRange> convertor2,
                                                 @NotNull PairConsumer<? super T, ? super V> intersectionConsumer) {
    PeekableIteratorWrapper<T> peekIterator1 = new PeekableIteratorWrapper<>(elements1.iterator());

    outerLoop:
    for (V item2 : elements2) {
      TextRange range2 = convertor2.convert(item2);

      while (peekIterator1.hasNext()) {
        T item1 = peekIterator1.peek();
        TextRange range1 = convertor1.convert(item1);

        if (range1.intersects(range2)) {
          intersectionConsumer.consume(item1, item2);
        }

        if (range2.getEndOffset() < range1.getEndOffset()) {
          continue outerLoop;
        }
        else {
          peekIterator1.next();
        }
      }

      break;
    }
  }
public static <T, V> void processIntersections(@NotNull List<? extends T> elements1,
                                                 @NotNull List<? extends V> elements2,
                                                 @NotNull Convertor<? super T, ? extends TextRange> convertor1,
                                                 @NotNull Convertor<? super V, ? extends TextRange> convertor2,
                                                 @NotNull PairConsumer<? super T, ? super V> intersectionConsumer)