public static <T, V> void processElementIntersections(@NotNull T element1,
                                                        @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) {
    TextRange range1 = convertor1.convert(element1);
    int index = ObjectUtils.binarySearch(0, elements2.size(), mid -> {
      V item2 = elements2.get(mid);
      TextRange range2 = convertor2.convert(item2);
      return Integer.compare(range2.getEndOffset(), range1.getStartOffset());
    });
    if (index < 0) index = -index - 1;

    for (int i = index; i < elements2.size(); i++) {
      V item2 = elements2.get(i);
      TextRange range2 = convertor2.convert(item2);

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

      if (range2.getStartOffset() > range1.getEndOffset()) break;
    }
  }
public static <T, V> void processElementIntersections(@NotNull T element1,
                                                        @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)