AnnotatedLineModificationDetails (@NotNull String lineContentAfter, @NotNull LineOffsets afterLineOffsets, @NotNull List<? extends LineFragment> fragments, int originalLineNumber) { LineFragment lineFragment = ContainerUtil.find(fragments.iterator(), fragment -> { return fragment.getStartLine2() <= originalLineNumber && originalLineNumber < fragment.getEndLine2(); }); if (lineFragment == null) return null; // line unmodified if (lineFragment.getStartLine1() == lineFragment.getEndLine1()) { return createNewLineDetails(lineContentAfter); // the whole line is new } List<DiffFragment> innerFragments = lineFragment.getInnerFragments(); if (innerFragments == null) { return createModifiedLineDetails(lineContentAfter); // the whole line is modified } int lineStart = afterLineOffsets.getLineStart(originalLineNumber); int lineEnd = afterLineOffsets.getLineEnd(originalLineNumber); int windowStart = lineStart - lineFragment.getStartOffset2(); int windowEnd = lineEnd - lineFragment.getStartOffset2(); int lineLength = lineEnd - lineStart; List<InnerChange> changes = new ArrayList<>(); for (DiffFragment innerFragment : innerFragments) { if (innerFragment.getEndOffset2() < windowStart || innerFragment.getStartOffset2() > windowEnd) continue; int start = Math.max(0, innerFragment.getStartOffset2() - windowStart); int end = Math.min(lineLength, innerFragment.getEndOffset2() - windowStart); boolean hasDeleted = innerFragment.getStartOffset1() != innerFragment.getEndOffset1(); boolean hasInserted = innerFragment.getStartOffset2() != innerFragment.getEndOffset2(); InnerChangeType type = hasInserted ? hasDeleted ? InnerChangeType.MODIFIED : InnerChangeType.INSERTED : InnerChangeType.DELETED; changes.add(new InnerChange(start, end, type)); } return new AnnotatedLineModificationDetails(lineContentAfter, changes); }