public TrafficLightRenderer(Project project, Document document, PsiFile file) {
    myProject = project;
    myDaemonCodeAnalyzer =
        project == null ? null : (DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(project);
    myDocument = document;
    myFile = file;
    mySeverityRegistrar = SeverityRegistrar.getInstance(myProject);
    refresh();

    if (project != null) {
      MarkupModelEx model =
          (MarkupModelEx) DocumentMarkupModel.forDocument(document, project, true);
      model.addMarkupModelListener(
          this,
          new MarkupModelListener() {
            @Override
            public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
              incErrorCount(highlighter, 1);
            }

            @Override
            public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
              incErrorCount(highlighter, -1);
            }

            @Override
            public void attributesChanged(@NotNull RangeHighlighterEx highlighter) {}
          });
      for (RangeHighlighter rangeHighlighter : model.getAllHighlighters()) {
        incErrorCount(rangeHighlighter, 1);
      }
    }
  }
  public void testRangeHighlighterLinesInRangeForLongLinePerformance() throws Exception {
    final int N = 50000;
    Document document =
        EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol('x', 2 * N));

    final MarkupModelEx markupModel =
        (MarkupModelEx) DocumentMarkupModel.forDocument(document, ourProject, true);
    for (int i = 0; i < N - 1; i++) {
      markupModel.addRangeHighlighter(2 * i, 2 * i + 1, 0, null, HighlighterTargetArea.EXACT_RANGE);
    }
    markupModel.addRangeHighlighter(
        N / 2, N / 2 + 1, 0, null, HighlighterTargetArea.LINES_IN_RANGE);

    PlatformTestUtil.startPerformanceTest(
            "slow highlighters lookup",
            (int) (N * Math.log(N) / 1000),
            new ThrowableRunnable() {
              @Override
              public void run() {
                List<RangeHighlighterEx> list = new ArrayList<RangeHighlighterEx>();
                CommonProcessors.CollectProcessor<RangeHighlighterEx> coll =
                    new CommonProcessors.CollectProcessor<RangeHighlighterEx>(list);
                for (int i = 0; i < N - 1; i++) {
                  list.clear();
                  markupModel.processRangeHighlightersOverlappingWith(2 * i, 2 * i + 1, coll);
                  assertEquals(2, list.size()); // 1 line plus one exact range marker
                }
              }
            })
        .assertTiming();
  }
  public static boolean processHighlightsOverlappingOutside(
      @NotNull Document document,
      @NotNull Project project,
      @Nullable("null means all") final HighlightSeverity minSeverity,
      final int startOffset,
      final int endOffset,
      @NotNull final Processor<HighlightInfo> processor) {
    LOG.assertTrue(ApplicationManager.getApplication().isReadAccessAllowed());

    final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(project);
    MarkupModelEx model = (MarkupModelEx) DocumentMarkupModel.forDocument(document, project, true);
    return model.processRangeHighlightersOutside(
        startOffset,
        endOffset,
        new Processor<RangeHighlighterEx>() {
          public boolean process(RangeHighlighterEx marker) {
            Object tt = marker.getErrorStripeTooltip();
            if (!(tt instanceof HighlightInfo)) return true;
            HighlightInfo info = (HighlightInfo) tt;
            return minSeverity != null
                    && severityRegistrar.compare(info.getSeverity(), minSeverity) < 0
                || info.highlighter == null
                || processor.process(info);
          }
        });
  }
  public void testRangeHighlighterIteratorOrder() throws Exception {
    Document document = EditorFactory.getInstance().createDocument("1234567890");

    final MarkupModelEx markupModel =
        (MarkupModelEx) DocumentMarkupModel.forDocument(document, ourProject, true);
    RangeHighlighter exact =
        markupModel.addRangeHighlighter(3, 6, 0, null, HighlighterTargetArea.EXACT_RANGE);
    RangeHighlighter line =
        markupModel.addRangeHighlighter(4, 5, 0, null, HighlighterTargetArea.LINES_IN_RANGE);
    List<RangeHighlighter> list = new ArrayList<RangeHighlighter>();
    markupModel.processRangeHighlightersOverlappingWith(
        2, 9, new CommonProcessors.CollectProcessor<RangeHighlighter>(list));
    assertEquals(Arrays.asList(line, exact), list);
  }
  private static void clearLineMarkers(Editor editor) {
    final List<RangeHighlighter> oldHighlighters =
        editor.getUserData(TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY);

    if (oldHighlighters != null) {
      final MarkupModelEx markupModel = (MarkupModelEx) editor.getMarkupModel();

      for (RangeHighlighter highlighter : oldHighlighters) {
        if (markupModel.containsHighlighter(highlighter)) {
          highlighter.dispose();
        }
      }
      editor.putUserData(TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY, null);
    }
  }
 private void paintBorderEffect(
     final Graphics2D g,
     final ClipDetector clipDetector,
     MarkupModelEx markupModel,
     int clipStartOffset,
     int clipEndOffset) {
   markupModel.processRangeHighlightersOverlappingWith(
       clipStartOffset,
       clipEndOffset,
       new Processor<RangeHighlighterEx>() {
         @Override
         public boolean process(RangeHighlighterEx rangeHighlighter) {
           if (rangeHighlighter.getEditorFilter().avaliableIn(myEditor)) {
             TextAttributes attributes = rangeHighlighter.getTextAttributes();
             if (isBorder(attributes)) {
               paintBorderEffect(
                   g,
                   clipDetector,
                   rangeHighlighter.getAffectedAreaStartOffset(),
                   rangeHighlighter.getAffectedAreaEndOffset(),
                   attributes);
             }
           }
           return true;
         }
       });
 }
  private RangeHighlighter doHightlightRange(
      final TextRange textRange,
      final TextAttributes attributes,
      Set<RangeHighlighter> highlighters) {
    HighlightManager highlightManager = HighlightManager.getInstance(mySearchResults.getProject());

    MarkupModelEx markupModel = (MarkupModelEx) mySearchResults.getEditor().getMarkupModel();

    final RangeHighlighter[] candidate = new RangeHighlighter[1];

    boolean notFound =
        markupModel.processRangeHighlightersOverlappingWith(
            textRange.getStartOffset(),
            textRange.getEndOffset(),
            new Processor<RangeHighlighterEx>() {
              @Override
              public boolean process(RangeHighlighterEx highlighter) {
                TextAttributes textAttributes = highlighter.getTextAttributes();
                if (highlighter.getUserData(SEARCH_MARKER) != null
                    && textAttributes != null
                    && textAttributes.equals(attributes)
                    && highlighter.getStartOffset() == textRange.getStartOffset()
                    && highlighter.getEndOffset() == textRange.getEndOffset()) {
                  candidate[0] = highlighter;
                  return false;
                }
                return true;
              }
            });

    if (!notFound && highlighters.contains(candidate[0])) {
      return candidate[0];
    }
    final ArrayList<RangeHighlighter> dummy = new ArrayList<RangeHighlighter>();
    highlightManager.addRangeHighlight(
        mySearchResults.getEditor(),
        textRange.getStartOffset(),
        textRange.getEndOffset(),
        attributes,
        false,
        dummy);
    final RangeHighlighter h = dummy.get(0);
    highlighters.add(h);
    h.putUserData(SEARCH_MARKER, YES);
    return h;
  }
 public void paintHighlighters(MarkupModelEx markupModel) {
   markupModel.processRangeHighlightersOverlappingWith(
       myStartOffset,
       myEndOffset,
       new Processor<RangeHighlighterEx>() {
         @Override
         public boolean process(RangeHighlighterEx rangeHighlighter) {
           TextAttributes textAttributes = rangeHighlighter.getTextAttributes();
           if (isBorder(textAttributes)) {
             paintBorder(rangeHighlighter, textAttributes);
           }
           return true;
         }
       });
 }
 private void paintHighlightersAfterEndOfLine(
     final Graphics2D g, MarkupModelEx markupModel, final int startOffset, int endOffset) {
   markupModel.processRangeHighlightersOverlappingWith(
       startOffset,
       endOffset,
       new Processor<RangeHighlighterEx>() {
         @Override
         public boolean process(RangeHighlighterEx highlighter) {
           if (highlighter.getEditorFilter().avaliableIn(myEditor)
               && highlighter.getStartOffset() >= startOffset) {
             paintHighlighterAfterEndOfLine(g, highlighter);
           }
           return true;
         }
       });
 }
 private void getNearestHighlighters(
     MarkupModelEx markupModel,
     MouseEvent e,
     final double width,
     final Collection<RangeHighlighter> nearest) {
   if (0 > e.getX() || e.getX() >= width) return;
   int startOffset = yPositionToOffset(e.getY() - getMinHeight(), true);
   int endOffset = yPositionToOffset(e.getY() + getMinHeight(), false);
   markupModel.processHighlightsOverlappingWith(
       startOffset,
       endOffset,
       new Processor<RangeHighlighterEx>() {
         public boolean process(RangeHighlighterEx highlighter) {
           if (highlighter.getErrorStripeMarkColor() != null) nearest.add(highlighter);
           return true;
         }
       });
 }
 private void paintLineMarkersSeparators(
     final Graphics g,
     final Rectangle clip,
     MarkupModelEx markupModel,
     int startOffset,
     int endOffset) {
   markupModel.processRangeHighlightersOverlappingWith(
       startOffset,
       endOffset,
       new Processor<RangeHighlighterEx>() {
         @Override
         public boolean process(RangeHighlighterEx highlighter) {
           if (highlighter.getEditorFilter().avaliableIn(myEditor)) {
             paintLineMarkerSeparator(highlighter, clip, g);
           }
           return true;
         }
       });
 }
    private void drawMarkup(
        final Graphics g, final int width, int startOffset, int endOffset, MarkupModelEx markup) {
      final Queue<PositionedStripe> thinEnds =
          new PriorityQueue<PositionedStripe>(
              5,
              new Comparator<PositionedStripe>() {
                public int compare(PositionedStripe o1, PositionedStripe o2) {
                  return o1.yEnd - o2.yEnd;
                }
              });
      final Queue<PositionedStripe> wideEnds =
          new PriorityQueue<PositionedStripe>(
              5,
              new Comparator<PositionedStripe>() {
                public int compare(PositionedStripe o1, PositionedStripe o2) {
                  return o1.yEnd - o2.yEnd;
                }
              });
      // sorted by layer
      final List<PositionedStripe> thinStripes = new ArrayList<PositionedStripe>();
      final List<PositionedStripe> wideStripes = new ArrayList<PositionedStripe>();
      final int[] thinYStart = new int[1]; // in range 0..yStart all spots are drawn
      final int[] wideYStart = new int[1]; // in range 0..yStart all spots are drawn

      markup.processHighlightsOverlappingWith(
          startOffset,
          endOffset,
          new Processor<RangeHighlighterEx>() {
            public boolean process(RangeHighlighterEx highlighter) {
              Color color = highlighter.getErrorStripeMarkColor();
              if (color == null) return true;
              boolean isThin = highlighter.isThinErrorStripeMark();
              int[] yStart = isThin ? thinYStart : wideYStart;
              List<PositionedStripe> stripes = isThin ? thinStripes : wideStripes;
              Queue<PositionedStripe> ends = isThin ? thinEnds : wideEnds;

              ProperTextRange range =
                  offsetToYPosition(highlighter.getStartOffset(), highlighter.getEndOffset());
              final int ys = range.getStartOffset();
              int ye = range.getEndOffset();
              if (ye - ys < getMinHeight()) ye = ys + getMinHeight();

              yStart[0] = drawStripesEndingBefore(ys, ends, stripes, g, width, yStart[0]);

              final int layer = highlighter.getLayer();

              PositionedStripe stripe = null;
              int i;
              for (i = 0; i < stripes.size(); i++) {
                PositionedStripe s = stripes.get(i);
                if (s.layer == layer) {
                  stripe = s;
                  break;
                }
                if (s.layer < layer) {
                  break;
                }
              }
              if (stripe == null) {
                // started new stripe, draw previous above
                if (yStart[0] != ys) {
                  if (!stripes.isEmpty()) {
                    PositionedStripe top = stripes.get(0);
                    drawSpot(g, width, top.thin, yStart[0], ys, top.color, true, true);
                  }
                  yStart[0] = ys;
                }
                stripe = new PositionedStripe(color, ys, ye, isThin, layer);
                stripes.add(i, stripe);
                ends.offer(stripe);
              } else {
                // key changed, reinsert into queue
                if (stripe.yEnd != ye) {
                  ends.remove(stripe);
                  stripe.yEnd = ye;
                  ends.offer(stripe);
                }
              }

              return true;
            }
          });

      drawStripesEndingBefore(Integer.MAX_VALUE, thinEnds, thinStripes, g, width, thinYStart[0]);
      drawStripesEndingBefore(Integer.MAX_VALUE, wideEnds, wideStripes, g, width, wideYStart[0]);
    }