@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;
  }
  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());
  }
 @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());
   }
 }
  @NotNull
  public TextRange getVcsTextRange(@NotNull Range range) {
    synchronized (myLock) {
      if (!range.isValid()) {
        LOG.warn("Vcs TextRange of invalid range");
      }

      return DiffUtil.getLinesRange(myVcsDocument, range.getVcsLine1(), range.getVcsLine2());
    }
  }
 private static int getVcsLine1(@Nullable Range range, int line) {
   return range == null ? line : line + range.getVcsLine2() - range.getLine2();
 }