public static void processLeavesAtOffsets(int[] offsets, @NotNull LighterAST tree, @NotNull BiConsumer<? super LighterASTTokenNode, ? super Integer> consumer) {
    if (offsets.length == 0) return;

    int[] sortedOffsets = offsets.clone();
    Arrays.sort(sortedOffsets);
    new RecursiveLighterASTNodeWalkingVisitor(tree) {
      int nextIndex = 0;
      int nextOffset = sortedOffsets[0];

      @Override
      public void visitNode(@NotNull LighterASTNode element) {
        if (containsNextOffset(element)) {
          super.visitNode(element);
        }
      }

      @Override
      public void visitTokenNode(@NotNull LighterASTTokenNode node) {
        if (containsNextOffset(node)) {
          consumer.accept(node, nextOffset);
          while (containsNextOffset(node)) {
            advanceOffset();
          }
        }
      }

      private boolean containsNextOffset(@NotNull LighterASTNode element) {
        ProgressManager.checkCanceled();
        return nextIndex < sortedOffsets.length && element.getStartOffset() <= nextOffset && nextOffset < element.getEndOffset();
      }

      private void advanceOffset() {
        nextIndex++;
        if (nextIndex < sortedOffsets.length) {
          nextOffset = sortedOffsets[nextIndex];
        }
      }
    }.visitNode(tree.getRoot());
  }
public static void processLeavesAtOffsets(int[] offsets, @NotNull LighterAST tree, @NotNull BiConsumer<? super LighterASTTokenNode, ? super Integer> consumer)