private void doUpdateRanges( int beforeChangedLine1, int beforeChangedLine2, int linesShift, int beforeTotalLines) { List<Range> rangesBeforeChange = new ArrayList<Range>(); List<Range> rangesAfterChange = new ArrayList<Range>(); List<Range> changedRanges = new ArrayList<Range>(); sortRanges( beforeChangedLine1, beforeChangedLine2, linesShift, rangesBeforeChange, changedRanges, rangesAfterChange); Range firstChangedRange = ContainerUtil.getFirstItem(changedRanges); Range lastChangedRange = ContainerUtil.getLastItem(changedRanges); if (firstChangedRange != null && firstChangedRange.getLine1() < beforeChangedLine1) { beforeChangedLine1 = firstChangedRange.getLine1(); } if (lastChangedRange != null && lastChangedRange.getLine2() > beforeChangedLine2) { beforeChangedLine2 = lastChangedRange.getLine2(); } doUpdateRanges( beforeChangedLine1, beforeChangedLine2, linesShift, beforeTotalLines, rangesBeforeChange, changedRanges, rangesAfterChange); }
private void doRollbackRange(@NotNull Range range) { DiffUtil.applyModification( myDocument, range.getLine1(), range.getLine2(), myVcsDocument, range.getVcsLine1(), range.getVcsLine2()); markLinesUnchanged( range.getLine1(), range.getLine1() + range.getVcsLine2() - range.getVcsLine1()); }
@Nullable private RangeHighlighter createHighlighter(@NotNull Range range) { myApplication.assertIsDispatchThread(); LOG.assertTrue(!myReleased, "Already released"); if (myMode == Mode.SILENT) return null; int first = range.getLine1() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getLine1()); int second = range.getLine2() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getLine2()); final TextAttributes attr = LineStatusTrackerDrawing.getAttributesFor(range); final RangeHighlighter highlighter = DocumentMarkupModel.forDocument(myDocument, myProject, true) .addRangeHighlighter( first, second, HighlighterLayer.FIRST - 1, attr, HighlighterTargetArea.LINES_IN_RANGE); highlighter.setThinErrorStripeMark(true); highlighter.setGreedyToLeft(true); highlighter.setGreedyToRight(true); highlighter.setLineMarkerRenderer(LineStatusTrackerDrawing.createRenderer(range, this)); highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter()); final String tooltip; if (range.getLine1() == range.getLine2()) { if (range.getVcsLine1() + 1 == range.getVcsLine2()) { tooltip = VcsBundle.message("tooltip.text.line.before.deleted", range.getLine1() + 1); } else { tooltip = VcsBundle.message( "tooltip.text.lines.before.deleted", range.getLine1() + 1, range.getVcsLine2() - range.getVcsLine1()); } } else if (range.getLine1() + 1 == range.getLine2()) { tooltip = VcsBundle.message("tooltip.text.line.changed", range.getLine1() + 1); } else { tooltip = VcsBundle.message("tooltip.text.lines.changed", range.getLine1() + 1, range.getLine2()); } highlighter.setErrorStripeTooltip(tooltip); return highlighter; }
@Override public void paint(Editor editor, Graphics g, Rectangle r) { Color gutterColor = getGutterColor(myRange, editor); Color borderColor = getGutterBorderColor(editor); Rectangle area = getMarkerArea(editor, r, myRange.getLine1(), myRange.getLine2()); final int x = area.x; final int endX = area.x + area.width; final int y = area.y; final int endY = area.y + area.height; if (myRange.getInnerRanges() == null) { // Mode.DEFAULT if (y != endY) { paintRect(g, gutterColor, borderColor, x, y, endX, endY); } else { paintTriangle(g, gutterColor, borderColor, x, endX, y); } } else { // Mode.SMART if (y == endY) { paintTriangle(g, gutterColor, borderColor, x, endX, y); } else { List<Range.InnerRange> innerRanges = myRange.getInnerRanges(); for (Range.InnerRange innerRange : innerRanges) { if (innerRange.getType() == Range.DELETED) continue; int start = lineToY(editor, innerRange.getLine1()); int end = lineToY(editor, innerRange.getLine2()); paintRect(g, getGutterColor(innerRange, editor), null, x, start, endX, end); } for (int i = 0; i < innerRanges.size(); i++) { Range.InnerRange innerRange = innerRanges.get(i); if (innerRange.getType() != Range.DELETED) continue; int start; int end; if (i == 0) { start = lineToY(editor, innerRange.getLine1()); end = lineToY(editor, innerRange.getLine2()) + 5; } else if (i == innerRanges.size() - 1) { start = lineToY(editor, innerRange.getLine1()) - 5; end = lineToY(editor, innerRange.getLine2()); } else { start = lineToY(editor, innerRange.getLine1()) - 3; end = lineToY(editor, innerRange.getLine2()) + 3; } paintRect(g, getGutterColor(innerRange, editor), null, x, start, endX, end); } paintRect(g, null, borderColor, x, y, endX, endY); } } }
@NotNull public TextRange getCurrentTextRange(@NotNull Range range) { myApplication.assertReadAccessAllowed(); synchronized (myLock) { if (!range.isValid()) { LOG.warn("Current TextRange of invalid range"); } return DiffUtil.getLinesRange(myDocument, range.getLine1(), range.getLine2()); } }
@Nullable public Range getPrevRange(int line) { synchronized (myLock) { for (int i = myRanges.size() - 1; i >= 0; i--) { Range range = myRanges.get(i); if (line > range.getLine1() && !range.isSelectedByLine(line)) { return range; } } return null; } }
@NotNull private static String getTooltipText(@NotNull Range range) { if (range.getLine1() == range.getLine2()) { if (range.getVcsLine1() + 1 == range.getVcsLine2()) { return VcsBundle.message("tooltip.text.line.before.deleted", range.getLine1() + 1); } else { return VcsBundle.message( "tooltip.text.lines.before.deleted", range.getLine1() + 1, range.getVcsLine2() - range.getVcsLine1()); } } else if (range.getLine1() + 1 == range.getLine2()) { return VcsBundle.message("tooltip.text.line.changed", range.getLine1() + 1); } else { return VcsBundle.message( "tooltip.text.lines.changed", range.getLine1() + 1, range.getLine2()); } }
private void sortRanges( int beforeChangedLine1, int beforeChangedLine2, int linesShift, @NotNull List<Range> rangesBeforeChange, @NotNull List<Range> changedRanges, @NotNull List<Range> rangesAfterChange) { if (!Registry.is("diff.status.tracker.skip.spaces")) { for (Range range : myRanges) { if (range.getLine2() < beforeChangedLine1) { rangesBeforeChange.add(range); } else if (range.getLine1() > beforeChangedLine2) { rangesAfterChange.add(range); } else { changedRanges.add(range); } } } else { int lastBefore = -1; int firstAfter = myRanges.size(); for (int i = 0; i < myRanges.size(); i++) { Range range = myRanges.get(i); if (range.getLine2() < beforeChangedLine1) { lastBefore = i; } else if (range.getLine1() > beforeChangedLine2) { firstAfter = i; break; } } // Expand on ranges, that are separated from changes only by empty/whitespaces lines // This is needed to reduce amount of confusing cases, when changed blocks are matched wrong // due to matched empty lines between them // TODO: try to simplify logic, it's too high change that current one is broken somehow CharSequence sequence = myDocument.getCharsSequence(); int lineCount = getLineCount(myDocument); while (true) { if (lastBefore == -1) break; if (lastBefore < myRanges.size() - 1 && firstAfter - lastBefore > 1) { Range firstChangedRange = myRanges.get(lastBefore + 1); if (firstChangedRange.getLine1() < beforeChangedLine1) { beforeChangedLine1 = firstChangedRange.getLine1(); } } if (beforeChangedLine1 < 0) break; if (beforeChangedLine1 >= lineCount) break; int offset1 = myDocument.getLineStartOffset(beforeChangedLine1) - 2; int deltaLines = 0; while (offset1 > 0) { char c = sequence.charAt(offset1); if (!StringUtil.isWhiteSpace(c)) break; if (c == '\n') deltaLines++; offset1--; } if (deltaLines == 0) break; beforeChangedLine1 -= deltaLines; if (myRanges.get(lastBefore).getLine2() < beforeChangedLine1) break; while (lastBefore != -1 && myRanges.get(lastBefore).getLine2() >= beforeChangedLine1) { lastBefore--; } } while (true) { if (firstAfter == myRanges.size()) break; if (firstAfter > 0 && firstAfter - lastBefore > 1) { Range lastChangedRange = myRanges.get(firstAfter - 1); if (lastChangedRange.getLine2() > beforeChangedLine2) { beforeChangedLine2 = lastChangedRange.getLine2(); } } // TODO: "afterChangedLine2 >= getLineCount(myDocument)" shouldn't ever be true, but it is // sometimes for some reason int afterChangedLine2 = beforeChangedLine2 + linesShift - 1; if (afterChangedLine2 < 0) break; if (afterChangedLine2 >= lineCount) break; int offset2 = myDocument.getLineEndOffset(afterChangedLine2) + 1; int deltaLines = 0; while (offset2 < sequence.length()) { char c = sequence.charAt(offset2); if (!StringUtil.isWhiteSpace(c)) break; if (c == '\n') deltaLines++; offset2++; } if (deltaLines == 0) break; beforeChangedLine2 += deltaLines; if (myRanges.get(firstAfter).getLine1() > beforeChangedLine2) break; while (firstAfter != myRanges.size() && myRanges.get(firstAfter).getLine1() <= beforeChangedLine2) { firstAfter++; } } for (int i = 0; i < myRanges.size(); i++) { Range range = myRanges.get(i); if (i <= lastBefore) { rangesBeforeChange.add(range); } else if (i >= firstAfter) { rangesAfterChange.add(range); } else { changedRanges.add(range); } } } }
private static int getVcsLine2( @Nullable Range range, int line, int totalLinesBefore, int totalLinesAfter) { return range == null ? totalLinesAfter - totalLinesBefore + line : line + range.getVcsLine1() - range.getLine1(); }