void (@NotNull Range range, @NotNull ChangedLines<T> newChange) { ChangedLines<T> lastItem = ContainerUtil.getLastItem(myBlock.changes); if (lastItem != null && lastItem.y2 < newChange.y1) { finishBlock(); } List<ChangedLines<T>> changes = myBlock.changes; List<Range> ranges = myBlock.ranges; if (ContainerUtil.getLastItem(ranges) != range) { ranges.add(range); } if (changes.isEmpty()) { changes.add(newChange); return; } ChangedLines<T> lastChange = changes.remove(changes.size() - 1); if (lastChange.y1 == lastChange.y2 && newChange.y1 == newChange.y2) { assert lastChange.y1 == newChange.y1; byte type = mergeTypes(lastChange, newChange); T flags = myFlagsProvider.mergeFlags(lastChange.flags, newChange.flags); changes.add(new ChangedLines<>(lastChange.y1, lastChange.y2, type, flags)); } else if (lastChange.y1 == lastChange.y2 && newChange.type == Range.EQUAL || newChange.y1 == newChange.y2 && lastChange.type == Range.EQUAL) { changes.add(lastChange); changes.add(newChange); } else if (lastChange.type == newChange.type && Objects.equals(lastChange.flags, newChange.flags)) { int union1 = Math.min(lastChange.y1, newChange.y1); int union2 = Math.max(lastChange.y2, newChange.y2); changes.add(new ChangedLines<>(union1, union2, lastChange.type, lastChange.flags)); } else { int intersection1 = Math.max(lastChange.y1, newChange.y1); int intersection2 = Math.min(lastChange.y2, newChange.y2); if (lastChange.y1 != intersection1) { changes.add(new ChangedLines<>(lastChange.y1, intersection1, lastChange.type, lastChange.flags)); } if (intersection1 != intersection2) { byte type = mergeTypes(lastChange, newChange); T flags = myFlagsProvider.mergeFlags(lastChange.flags, newChange.flags); changes.add(new ChangedLines<>(intersection1, intersection2, type, flags)); } if (newChange.y2 != intersection2) { changes.add(new ChangedLines<>(intersection2, newChange.y2, newChange.type, newChange.flags)); } } }