@NotNull public DiffFragment[] buildFragments(@Nullable Diff.Change change) { while (change != null) { if (change.inserted > 0 && change.deleted > 0) { change( new TextRange(change.line0 + 1, change.line0 + change.deleted), new TextRange(change.line1 + 1, change.line1 + change.inserted) ); } else if (change.inserted > 0) { append(change.line0, new TextRange(change.line1 + 1, change.line1 + change.inserted)); } else if (change.deleted > 0) { delete(new TextRange(change.line0 + 1, change.line0 + change.deleted), change.line1); } change = change.link; } finish(); final List<DiffFragment> fragments = getFragments(); return fragments.toArray(new DiffFragment[myData.size()]); }
public int translateLineViaDiff(int line) throws FilesTooBigForDiffException { Diff.Change change = reBuildDiffIfNeeded(); if (change == null) return line; int newLine = line; while (change != null) { if (line < change.line0) break; if (line >= change.line0 + change.deleted) { newLine += change.inserted - change.deleted; } else { int delta = Math.min(change.inserted, line - change.line0); newLine = change.line1 + delta; break; } change = change.link; } return newLine; }
private static TIntIntHashMap getCoverageVersionToCurrentLineMapping(Diff.Change change, int firstNLines) { TIntIntHashMap result = new TIntIntHashMap(); int prevLineInFirst = 0; int prevLineInSecond = 0; while (change != null) { for (int l = 0; l < change.line0 - prevLineInFirst; l++) { result.put(prevLineInFirst + l, prevLineInSecond + l); } prevLineInFirst = change.line0 + change.deleted; prevLineInSecond = change.line1 + change.inserted; change = change.link; } for (int i = prevLineInFirst; i < firstNLines; i++) { result.put(i, prevLineInSecond + i - prevLineInFirst); } return result; }
private void recalculateLineNumbers(final Diff.Change changesList) { Diff.Change change = changesList; int removedLinesCount = 0; while (change != null) { for (int i = 0; i < change.inserted; i++) { myLineNumbers.remove(change.line1 - removedLinesCount); } removedLinesCount += change.inserted; change = change.link; } change = changesList; while (change != null) { for (int i = 0; i < change.deleted; i++) { myLineNumbers.add(change.line0, null); } change = change.link; } }
public DiffFragment[] buildFragments(Diff.Change change) { while (change != null) { if (change.inserted > 0 && change.deleted > 0) { change( new TextRange(change.line0 + 1, change.line0 + change.deleted), new TextRange(change.line1 + 1, change.line1 + change.inserted) ); } else if (change.inserted > 0) { append(change.line0, new TextRange(change.line1 + 1, change.line1 + change.inserted)); } else if (change.deleted > 0) { delete(new TextRange(change.line0 + 1, change.line0 + change.deleted), change.line1); } change = change.link; } finish(); final List<DiffFragment> fragments = getFragments(); return fragments.toArray(new DiffFragment[myData.size()]); }
@Nonnull public static List<Range> createRanges(@Nonnull List<String> current, @Nonnull List<String> vcs, int shift, int vcsShift, boolean innerWhitespaceChanges) throws FilesTooBigForDiffException { Diff.Change ch = Diff.buildChanges(ArrayUtil.toStringArray(vcs), ArrayUtil.toStringArray(current)); List<Range> result = new ArrayList<Range>(); while (ch != null) { if (innerWhitespaceChanges) { result.add(createOnSmart(ch, shift, vcsShift, current, vcs)); } else { result.add(createOn(ch, shift, vcsShift)); } ch = ch.link; } return result; }
@Nonnull public DiffFragment[] buildFragments(@Nullable Diff.Change change) { while (change != null) { if (change.inserted > 0 && change.deleted > 0) { change( new TextRange(change.line0 + 1, change.line0 + change.deleted), new TextRange(change.line1 + 1, change.line1 + change.inserted) ); } else if (change.inserted > 0) { append(change.line0, new TextRange(change.line1 + 1, change.line1 + change.inserted)); } else if (change.deleted > 0) { delete(new TextRange(change.line0 + 1, change.line0 + change.deleted), change.line1); } change = change.link; } finish(); final List<DiffFragment> fragments = getFragments(); return fragments.toArray(new DiffFragment[myData.size()]); }
public int translateLineViaDiff(int line) throws FilesTooBigForDiffException { Diff.Change change = reBuildDiffIfNeeded(); if (change == null) return line; int startLine = getDocument().getLineNumber(getOffset()); line -= startLine; int newLine = line; while (change != null) { if (line < change.line0) break; if (line >= change.line0 + change.deleted) { newLine += change.inserted - change.deleted; } else { int delta = Math.min(change.inserted, line - change.line0); newLine = change.line1 + delta; break; } change = change.link; } return newLine + startLine; }
@NotNull public static <T> FairDiffIterable diff(@NotNull T[] data1, @NotNull T[] data2, @NotNull ProgressIndicator indicator) { indicator.checkCanceled(); try { // TODO: use ProgressIndicator inside Diff.Change change = Diff.buildChanges(data1, data2); return fair(create(change, data1.length, data2.length)); } catch (FilesTooBigForDiffException e) { throw new DiffTooBigException(); } }
protected void addChange(int start1, int start2, int end1, int end2) { Diff.Change change = new Diff.Change(start1, start2, end1 - start1, end2 - start2, null); if (myLastChange != null) { myLastChange.link = change; } else { myFirstChange = change; } myLastChange = change; myIndex1 = end1; myIndex2 = end2; }
public void testConcatEqualsConcatenatesChanged() throws FilesTooBigForDiffException { String[] left = new String[]{"i1", "a", "i2", "a", "b"}; String[] right = new String[]{"a", "b"}; Diff.Change change = Diff.buildChanges(left, right); MultiCheck multiCheck = new MultiCheck(); multiCheck.assertEquals(0, change.line0); multiCheck.assertEquals(0, change.line1); multiCheck.assertEquals(3, change.deleted); multiCheck.assertEquals(0, change.inserted); multiCheck.assertNull(change.link); multiCheck.flush(); left = new String[]{"i1", "a", "i2", "a", "b", "*"}; right = new String[]{"a", "b", "$"}; change = Diff.buildChanges(left, right); assertNotNull(change.link); assertEquals(2, change.link.deleted); assertEquals(2, change.link.line0); Diff.Change newChange = Util.concatEquals(change, left, right); multiCheck.assertEquals(0, newChange.line0); multiCheck.assertEquals(0, newChange.line1); multiCheck.assertEquals(3, newChange.deleted); multiCheck.assertEquals(0, newChange.inserted); assertNotNull(newChange.link); newChange = newChange.link; multiCheck.assertEquals(5, newChange.line0); multiCheck.assertEquals(2, newChange.line1); multiCheck.assertEquals(1, newChange.deleted); multiCheck.assertEquals(1, newChange.inserted); multiCheck.assertNull(newChange.link); multiCheck.flush(); }
@NotNull @Override public DiffFragment[] buildFragments(@NotNull DiffString text1, @NotNull DiffString text2) throws FilesTooBigForDiffException { Word[] words1 = buildWords(text1, myComparisonPolicy); Word[] words2 = buildWords(text2, myComparisonPolicy); Diff.Change change = Diff.buildChanges(words1, words2); change = Util.concatEquals(change, words1, words2); if (Math.max(countNotWhitespaces(words1), countNotWhitespaces(words2)) > 0 && countEqual(change, words1, words2) == 0) return new DiffFragment[]{myComparisonPolicy.createFragment(text1, text2)}; FragmentBuilder result = new FragmentBuilder(words1, words2, myComparisonPolicy, text1, text2); FragmentBuilder.Version version1 = result.getVersion1(); FragmentBuilder.Version version2 = result.getVersion2(); while (change != null) { if (change.line0 > version1.getCurrentWordIndex()) { processEquals(change.line0, change.line1, result); } if (change.inserted == 0) { processOneside(version1, change.deleted); } else if (change.deleted == 0) { processOneside(version2, change.inserted); } else { DiffString prefix1 = version1.getCurrentWordPrefix(); DiffString prefix2 = version2.getCurrentWordPrefix(); if (!prefix1.isEmpty() || !prefix2.isEmpty()) result.add(myComparisonPolicy.createFragment(prefix1, prefix2)); result.addChangedWords(change.deleted, change.inserted); } change = change.link; } processEquals(words1.length, words2.length, result); result.addTails(); DiffFragment[] fragments = result.getFragments(); DiffFragment firstFragment = fragments[0]; if (firstFragment.isEmpty()) { DiffFragment[] newFragments = new DiffFragment[fragments.length - 1]; System.arraycopy(fragments, 1, newFragments, 0, newFragments.length); fragments = newFragments; } return fragments; }
private static int countEqual(Diff.Change change, @NotNull Word[] words1, @NotNull Word[] words2) { int counter = 0; int position1 = 0; int position2 = 0; while (change != null) { if (change.line0 > position1) { int same = change.line0 - position1; LOG.assertTrue(same == change.line1 - position2); for (int i = 0; i < same; i++) { if (!words1[position1 + i].isWhitespace() && !words2[position2 + i].isWhitespace()) counter++; } position1 += same; position2 += same; } position1 += change.deleted; position2 += change.inserted; change = change.link; } int tailCount = words1.length - position1; LOG.assertTrue(tailCount == words2.length - position2); while (tailCount > 0) { if (!words1[words1.length - tailCount].isWhitespace() && !words2[words2.length - tailCount].isWhitespace()) counter++; tailCount--; } return counter; }
private static Diff.Change concatSingleSide(Diff.Change change) { MyChange startChange = new MyChange(0, 0, 0, 0); MyChange lastChange = startChange; MyChange prevChange = null; while (change != null) { if (prevChange == null || (change.inserted > 0 && change.deleted > 0)) { prevChange = lastChange; lastChange = lastChange.copyNext(change); } else { MyChange newChange = null; if (change.deleted == 0 && lastChange.deleted == 0 && change.line1 == lastChange.getEnd2()) { newChange = new MyChange(lastChange.line0, lastChange.line1, 0, lastChange.inserted + change.inserted); } else if (change.inserted == 0 && lastChange.inserted == 0 && change.line0 == lastChange.getEnd1()) { newChange = new MyChange(lastChange.line0, lastChange.line1, lastChange.deleted + change.deleted, 0); } if (newChange != null) { prevChange.setNext(newChange); lastChange = newChange; } else { prevChange = lastChange; lastChange = lastChange.copyNext(change); } } change = change.link; } return startChange.link; }
@NotNull public DiffFragment[] buildFragments(@NotNull DiffString[] strings1, @NotNull DiffString[] strings2) throws FilesTooBigForDiffException { DiffFragmentBuilder builder = new DiffFragmentBuilder(strings1, strings2); Object[] wrappers1 = getWrappers(strings1); Object[] wrappers2 = getWrappers(strings2); Diff.Change change = Diff.buildChanges(wrappers1, wrappers2); return builder.buildFragments(Util.concatEquals(change, wrappers1, wrappers2)); }
@NotNull public DiffFragment[] buildDiffFragmentsFromLines(@NotNull DiffString[] lines1, @NotNull DiffString[] lines2) throws FilesTooBigForDiffException { DiffFragmentBuilder builder = new DiffFragmentBuilder(lines1, lines2); Object[] wrappers1 = getLineWrappers(lines1); Object[] wrappers2 = getLineWrappers(lines2); Diff.Change change = Diff.buildChanges(wrappers1, wrappers2); return builder.buildFragments(change); }
private Diff.Change reBuildDiffIfNeeded() throws FilesTooBigForDiffException { if (myChange == TOO_BIG_FILE) throw new FilesTooBigForDiffException(0); if (myChange == null) { try { myChange = Diff.buildChanges(myOldString, myNewString); } catch (FilesTooBigForDiffException e) { myChange = TOO_BIG_FILE; throw e; } } return myChange; }
private void processDefault(@NotNull List<String> current, @NotNull List<String> vcs, int shift, int vcsShift) throws FilesTooBigForDiffException { Diff.Change ch = Diff.buildChanges(ArrayUtil.toStringArray(vcs), ArrayUtil.toStringArray(current)); while (ch != null) { Range range = createOn(ch, shift, vcsShift); myRanges.add(range); ch = ch.link; } }
private void processSmart(@NotNull List<String> current, @NotNull List<String> vcs, int shift, int vcsShift) throws FilesTooBigForDiffException { Diff.Change ch = Diff.buildChanges(ArrayUtil.toStringArray(vcs), ArrayUtil.toStringArray(current)); while (ch != null) { Range range = createOnSmart(ch, shift, vcsShift, current, vcs); myRanges.add(range); ch = ch.link; } }
private static Range createOn(@NotNull Diff.Change change, int shift, int vcsShift) { int offset1 = shift + change.line1; int offset2 = offset1 + change.inserted; int uOffset1 = vcsShift + change.line0; int uOffset2 = uOffset1 + change.deleted; return new Range(offset1, offset2, uOffset1, uOffset2); }
private static byte getChangeType(@NotNull Diff.Change change) { if ((change.deleted > 0) && (change.inserted > 0)) return Range.MODIFIED; if ((change.deleted > 0)) return Range.DELETED; if ((change.inserted > 0)) return Range.INSERTED; LOG.error("Unknown change type"); return Range.EQUAL; }
public Block getBlockInThePrevVersion() throws FilesTooBigForDiffException { Diff.Change change = Diff.buildChanges(myResult.getSource(), myCurrentVersion.getSource()); while (change != null) { shiftIndices(change.line1, change.line1, change.line0); shiftIndices(change.line1, change.line1 + change.inserted, change.line0 + change.deleted); change = change.link; } if (myResult.getEnd() >= myResult.getSource().length){ myResult.setEnd(myResult.getSource().length - 1); } return myResult; }
@Nullable public static List<Cmd> createDiffCmds(@NotNull Model<Object> listModel, @NotNull Object[] oldElements, @NotNull Object[] newElements) { Diff.Change change = null; try { change = Diff.buildChanges(oldElements, newElements); } catch (FilesTooBigForDiffException e) { // should not occur } if (change == null) { return null; } List<Cmd> commands = new ArrayList<Cmd>(); int inserted = 0; int deleted = 0; while (change != null) { if (change.deleted > 0) { final int start = change.line0 + inserted - deleted; commands.add(new RemoveCmd<Object>(listModel, start, start + change.deleted - 1)); } if (change.inserted > 0) { for (int i = 0; i < change.inserted; i++) { commands.add(new InsertCmd<Object>(listModel, change.line0 + i + inserted - deleted, newElements[change.line1 + i])); } } deleted += change.deleted; inserted += change.inserted; change = change.link; } return commands; }
/** * @param revisions sorted list containing revisions of the annotated file. * First element of the list (with zero index) must contain revision which is being annotated, * other list elements (if any) must give all file revisions which are older than the annotated one. * @param contentProvider delegate providing file content. {@link ContentProvider#getContent(TFSFileRevision)} method of provided object * is called only for specified <code>revisions</code> */ public AnnotationBuilder(List<TFSFileRevision> revisions, ContentProvider contentProvider) throws VcsException { if (revisions == null || revisions.size() < 1) { throw new IllegalArgumentException(); } final Iterator<TFSFileRevision> iterator = revisions.iterator(); TFSFileRevision revision = iterator.next(); myAnnotatedContent = contentProvider.getContent(revision); String[] lines = splitLines(myAnnotatedContent); myLineRevisions = new VcsFileRevision[lines.length]; myLineNumbers = new ArrayList<Integer>(lines.length); for (int i = 0; i < lines.length; i++) { myLineNumbers.add(i); } while (iterator.hasNext()) { final TFSFileRevision previousRevision = iterator.next(); final String previousContent = contentProvider.getContent(previousRevision); final String[] previousLines = splitLines(previousContent); final Diff.Change change; try { change = Diff.buildChanges(previousLines, lines); } catch (FilesTooBigForDiffException e) { throw new VcsException(e); } annotateAll(change, revision); if (allLinesAnnotated()) { break; } lines = previousLines; revision = previousRevision; } fillAllNotAnnotated(revisions.get(revisions.size() - 1)); }
private void annotateAll(final Diff.Change changesList, final VcsFileRevision revision) { Diff.Change change = changesList; while (change != null) { annotate(change, revision); change = change.link; } recalculateLineNumbers(changesList); }
private void annotate(final Diff.Change change, VcsFileRevision revision) { if (change.inserted > 0) { for (int line = change.line1; line < change.line1 + change.inserted; line++) { Integer origLine = myLineNumbers.get(line); if (origLine != null) { if (myLineRevisions[origLine.intValue()] == null) { myLineRevisions[origLine.intValue()] = revision; } } } } }
public DiffFragment[] buildFragments(String text1, String text2) throws FilesTooBigForDiffException { Word[] words1 = buildWords(text1, myComparisonPolicy); Word[] words2 = buildWords(text2, myComparisonPolicy); Diff.Change change = Diff.buildChanges(words1, words2); change = Util.concatEquals(change, words1, words2); if (Math.max(countNotWhitespaces(words1), countNotWhitespaces(words2)) > 0 && countEqual(change, words1, words2) == 0) return new DiffFragment[]{myComparisonPolicy.createFragment(text1, text2)}; FragmentBuilder result = new FragmentBuilder(words1, words2, myComparisonPolicy, text1, text2); FragmentBuilder.Version version1 = result.getVersion1(); FragmentBuilder.Version version2 = result.getVersion2(); while (change != null) { if (change.line0 > version1.getCurrentWordIndex()) { processEquals(change.line0, change.line1, result); } if (change.inserted == 0) { processOneside(version1, change.deleted); } else if (change.deleted == 0) { processOneside(version2, change.inserted); } else { String prefix1 = version1.getCurrentWordPrefix(); String prefix2 = version2.getCurrentWordPrefix(); if (prefix1.length() > 0 || prefix2.length() > 0) result.add(myComparisonPolicy.createFragment(prefix1, prefix2)); result.addChangedWords(change.deleted, change.inserted); } change = change.link; } processEquals(words1.length, words2.length, result); result.addTails(); DiffFragment[] fragments = result.getFragments(); DiffFragment firstFragment = fragments[0]; if (DiffFragment.isEmpty(firstFragment)) { DiffFragment[] newFragments = new DiffFragment[fragments.length - 1]; System.arraycopy(fragments, 1, newFragments, 0, newFragments.length); fragments = newFragments; } return fragments; }
private int countEqual(Diff.Change change, Word[] words1, Word[] words2) { int counter = 0; int position1 = 0; int position2 = 0; while (change != null) { if (change.line0 > position1) { int same = change.line0 - position1; LOG.assertTrue(same == change.line1 - position2); for (int i = 0; i < same; i++) { if (!words1[position1 + i].isWhitespace() && !words2[position2 + i].isWhitespace()) counter++; } position1 += same; position2 += same; } position1 += change.deleted; position2 += change.inserted; change = change.link; } int tailCount = words1.length - position1; LOG.assertTrue(tailCount == words2.length - position2); while (tailCount > 0) { if (!words1[words1.length - tailCount].isWhitespace() && !words2[words2.length - tailCount].isWhitespace()) counter++; tailCount--; } return counter; }
public DiffFragment[] buildFragments(String[] strings1, String[] strings2) throws FilesTooBigForDiffException { DiffFragmentBuilder builder = new DiffFragmentBuilder(strings1, strings2); Object[] wrappers1 = getWrappers(strings1); Object[] wrappers2 = getWrappers(strings2); Diff.Change change = Diff.buildChanges(wrappers1, wrappers2); return builder.buildFragments(Util.concatEquals(change, wrappers1, wrappers2)); }
public DiffFragment[] buildDiffFragmentsFromLines(String[] lines1, String[] lines2) throws FilesTooBigForDiffException { DiffFragmentBuilder builder = new DiffFragmentBuilder(lines1, lines2); Object[] wrappers1 = getLineWrappers(lines1); Object[] wrappers2 = getLineWrappers(lines2); Diff.Change change = Diff.buildChanges(wrappers1, wrappers2); return builder.buildFragments(change); }
private static byte getChangeTypeFrom(Diff.Change change) { if ((change.deleted > 0) && (change.inserted > 0)) return MODIFIED; if ((change.deleted > 0)) return DELETED; if ((change.inserted > 0)) return INSERTED; LOG.error("Unknown change type"); return 0; }
private static Range createOn(@Nonnull Diff.Change change, int shift, int vcsShift) { int offset1 = shift + change.line1; int offset2 = offset1 + change.inserted; int uOffset1 = vcsShift + change.line0; int uOffset2 = uOffset1 + change.deleted; return new Range(offset1, offset2, uOffset1, uOffset2); }
private static byte getChangeType(@Nonnull Diff.Change change) { if ((change.deleted > 0) && (change.inserted > 0)) return Range.MODIFIED; if ((change.deleted > 0)) return Range.DELETED; if ((change.inserted > 0)) return Range.INSERTED; LOG.error("Unknown change type"); return Range.EQUAL; }