@NotNull
  public static List<LineBlock> compareAndSplit(@NotNull CharSequence text1,
                                                @NotNull CharSequence text2,
                                                @NotNull ComparisonPolicy policy,
                                                @NotNull CancellationChecker indicator) {
    indicator.checkCanceled();

    // TODO: figure out, what do we exactly want from 'Split' logic
    // -- it is used for trimming of ignored blocks. So we want whitespace-only leading/trailing lines to be separate block.
    // -- old approach: split by matched '\n's

    // TODO: other approach could lead to better results:
    // * Compare words-only
    // * prefer big chunks
    // -- here we can try to minimize number of matched pairs 'pair[i]' and 'pair[i+1]' such that
    //    containsNewline(pair[i].left .. pair[i+1].left) XOR containsNewline(pair[i].right .. pair[i+1].right) == true
    //    ex: "A X C" - "A Y C \n M C" - do not match with last 'C'
    //    ex: "A \n" - "A B \n \n" - do not match with last '\n'
    //    Try some greedy approach ?
    // * split into blocks
    // -- squash blocks with too small unchanged words count (1 matched word out of 40 - is a bad reason to create new block)
    // * match adjustment punctuation
    // * match adjustment whitespaces ('\n' are matched here)

    List<InlineChunk> words1 = getInlineChunks(text1);
    List<InlineChunk> words2 = getInlineChunks(text2);

    FairDiffIterable wordChanges = diff(words1, words2, indicator);
    wordChanges = optimizeWordChunks(text1, text2, words1, words2, wordChanges, indicator);

    List<WordBlock> wordBlocks = new LineFragmentSplitter(text1, text2, words1, words2, wordChanges, indicator).run();

    List<FairDiffIterable> subIterables = collectWordBlockSubIterables(wordChanges, wordBlocks);

    List<LineBlock> lineBlocks = new ArrayList<>(wordBlocks.size());
    for (int i = 0; i < wordBlocks.size(); i++) {
      WordBlock block = wordBlocks.get(i);
      Range offsets = block.offsets;
      Range words = block.words;

      CharSequence subtext1 = text1.subSequence(offsets.start1, offsets.end1);
      CharSequence subtext2 = text2.subSequence(offsets.start2, offsets.end2);

      List<InlineChunk> subwords1 = words1.subList(words.start1, words.end1);
      List<InlineChunk> subwords2 = words2.subList(words.start2, words.end2);

      FairDiffIterable subiterable = subIterables.get(i);

      FairDiffIterable delimitersIterable = matchAdjustmentDelimiters(subtext1, subtext2, subwords1, subwords2, subiterable,
                                                                      offsets.start1, offsets.start2, indicator);
      DiffIterable iterable = matchAdjustmentWhitespaces(subtext1, subtext2, delimitersIterable, policy, indicator);

      List<DiffFragment> fragments = convertIntoDiffFragments(iterable);

      int newlines1 = countNewlines(subwords1);
      int newlines2 = countNewlines(subwords2);

      lineBlocks.add(new LineBlock(fragments, offsets, newlines1, newlines2));
    }

    return lineBlocks;
  }
@NotNull
  public static List<LineBlock> compareAndSplit(@NotNull CharSequence text1,
                                                @NotNull CharSequence text2,
                                                @NotNull ComparisonPolicy policy,
                                                @NotNull CancellationChecker indicator)