예제 #1
1
  @Override
  protected void updateTitle(@Nullable final PsiVariable variable, final String value) {
    final PsiElement declarationScope =
        variable != null ? ((PsiParameter) variable).getDeclarationScope() : null;
    if (declarationScope instanceof PsiMethod) {
      final PsiMethod psiMethod = (PsiMethod) declarationScope;
      final StringBuilder buf = new StringBuilder();
      buf.append(psiMethod.getName()).append(" (");
      boolean frst = true;
      final List<TextRange> ranges2Remove = new ArrayList<>();
      TextRange addedRange = null;
      for (PsiParameter parameter : psiMethod.getParameterList().getParameters()) {
        if (frst) {
          frst = false;
        } else {
          buf.append(", ");
        }
        int startOffset = buf.length();
        if (myMustBeFinal || myPanel.isGenerateFinal()) {
          buf.append("final ");
        }
        buf.append(parameter.getType().getPresentableText())
            .append(" ")
            .append(variable == parameter ? value : parameter.getName());
        int endOffset = buf.length();
        if (variable == parameter) {
          addedRange = new TextRange(startOffset, endOffset);
        } else if (myPanel.isParamToRemove(parameter)) {
          ranges2Remove.add(new TextRange(startOffset, endOffset));
        }
      }

      buf.append(")");
      setPreviewText(buf.toString());
      final MarkupModel markupModel =
          DocumentMarkupModel.forDocument(getPreviewEditor().getDocument(), myProject, true);
      markupModel.removeAllHighlighters();
      for (TextRange textRange : ranges2Remove) {
        markupModel.addRangeHighlighter(
            textRange.getStartOffset(),
            textRange.getEndOffset(),
            0,
            getTestAttributesForRemoval(),
            HighlighterTargetArea.EXACT_RANGE);
      }
      markupModel.addRangeHighlighter(
          addedRange.getStartOffset(),
          addedRange.getEndOffset(),
          0,
          getTextAttributesForAdd(),
          HighlighterTargetArea.EXACT_RANGE);
      revalidate();
    }
  }
  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 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);
          }
        });
  }
예제 #4
0
 /** Patches attributes to be visible under debugger active line */
 @SuppressWarnings("UseJBColor")
 public static TextAttributes patchAttributesColor(
     TextAttributes attributes, @NotNull TextRange range, @NotNull Editor editor) {
   if (attributes.getForegroundColor() == null && attributes.getEffectColor() == null)
     return attributes;
   MarkupModel model =
       DocumentMarkupModel.forDocument(editor.getDocument(), editor.getProject(), false);
   if (model != null) {
     if (!((MarkupModelEx) model)
         .processRangeHighlightersOverlappingWith(
             range.getStartOffset(),
             range.getEndOffset(),
             highlighter -> {
               if (highlighter.isValid()
                   && highlighter.getTargetArea() == HighlighterTargetArea.LINES_IN_RANGE) {
                 TextAttributes textAttributes = highlighter.getTextAttributes();
                 if (textAttributes != null) {
                   Color color = textAttributes.getBackgroundColor();
                   return !(color != null
                       && color.getBlue() > 128
                       && color.getRed() < 128
                       && color.getGreen() < 128);
                 }
               }
               return true;
             })) {
       TextAttributes clone = attributes.clone();
       clone.setForegroundColor(Color.orange);
       clone.setEffectColor(Color.orange);
       return clone;
     }
   }
   return attributes;
 }
  @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;
  }
 public static void clearMyHighlights(Document document, Project project) {
   MarkupModel markupModel = DocumentMarkupModel.forDocument(document, project, true);
   for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) {
     Object tooltip = highlighter.getErrorStripeTooltip();
     if (!(tooltip instanceof HighlightInfo)) {
       continue;
     }
     HighlightInfo info = (HighlightInfo) tooltip;
     if (info.type == HighlightInfoType.ELEMENT_UNDER_CARET_READ
         || info.type == HighlightInfoType.ELEMENT_UNDER_CARET_WRITE) {
       highlighter.dispose();
     }
   }
 }
  public static void clearHighlightingAndLineMarkers(
      final Editor editor, @NotNull Project project) {
    final MarkupModel markupModel =
        DocumentMarkupModel.forDocument(editor.getDocument(), project, true);

    for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) {
      Object tooltip = highlighter.getErrorStripeTooltip();

      if (!(tooltip instanceof HighlightInfo)) {
        continue;
      }

      if (((HighlightInfo) tooltip).type == TYPE) {
        highlighter.dispose();
      }
    }

    clearLineMarkers(editor);
  }
 public void printToHistory(String text, final TextAttributes attributes) {
   ApplicationManager.getApplication().assertIsDispatchThread();
   text = StringUtil.convertLineSeparators(text);
   final boolean scrollToEnd = shouldScrollHistoryToEnd();
   final Document history = myHistoryViewer.getDocument();
   final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
   final int offset = history.getTextLength();
   appendToHistoryDocument(history, text);
   markupModel.addRangeHighlighter(
       offset,
       history.getTextLength(),
       HighlighterLayer.SYNTAX,
       attributes,
       HighlighterTargetArea.EXACT_RANGE);
   if (scrollToEnd) {
     scrollHistoryToEnd();
   }
   queueUiUpdate(scrollToEnd);
 }
 public void printToHistory(@NotNull final List<Pair<String, TextAttributes>> attributedText) {
   ApplicationManager.getApplication().assertIsDispatchThread();
   if (LOG.isDebugEnabled()) {
     LOG.debug("printToHistory(): " + attributedText.size());
   }
   final boolean scrollToEnd = shouldScrollHistoryToEnd();
   final int[] offsets = new int[attributedText.size() + 1];
   int i = 0;
   offsets[i] = 0;
   final StringBuilder sb = new StringBuilder();
   for (final Pair<String, TextAttributes> pair : attributedText) {
     sb.append(StringUtil.convertLineSeparators(pair.getFirst()));
     offsets[++i] = sb.length();
   }
   final DocumentEx history = myHistoryViewer.getDocument();
   final int oldHistoryLength = history.getTextLength();
   appendToHistoryDocument(history, sb.toString());
   assert oldHistoryLength + offsets[i] == history.getTextLength()
       : "unexpected history length "
           + oldHistoryLength
           + " "
           + offsets[i]
           + " "
           + history.getTextLength();
   LOG.debug("printToHistory(): text processed");
   final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
   i = 0;
   for (final Pair<String, TextAttributes> pair : attributedText) {
     markupModel.addRangeHighlighter(
         oldHistoryLength + offsets[i],
         oldHistoryLength + offsets[i + 1],
         HighlighterLayer.SYNTAX,
         pair.getSecond(),
         HighlighterTargetArea.EXACT_RANGE);
     ++i;
   }
   LOG.debug("printToHistory(): markup added");
   if (scrollToEnd) {
     scrollHistoryToEnd();
   }
   queueUiUpdate(scrollToEnd);
   LOG.debug("printToHistory(): completed");
 }
  @Nullable
  protected static RangeHighlighter createHighlighter(
      @NotNull Project project, @NotNull Document document, int lineIndex) {
    if (lineIndex < 0 || lineIndex >= document.getLineCount()) {
      return null;
    }

    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    TextAttributes attributes = scheme.getAttributes(DebuggerColors.BREAKPOINT_ATTRIBUTES);

    RangeHighlighter highlighter =
        ((MarkupModelEx) DocumentMarkupModel.forDocument(document, project, true))
            .addPersistentLineHighlighter(
                lineIndex, DebuggerColors.BREAKPOINT_HIGHLIGHTER_LAYER, attributes);
    if (highlighter == null || !highlighter.isValid()) {
      return null;
    }
    highlighter.putUserData(DebuggerColors.BREAKPOINT_HIGHLIGHTER_KEY, Boolean.TRUE);
    highlighter.setErrorStripeTooltip(
        DebuggerBundle.message("breakpoint.tooltip.text", lineIndex + 1));
    return highlighter;
  }
  void paint(Graphics2D g) {
    Rectangle clip = g.getClipBounds();

    if (myEditor.getContentComponent().isOpaque()) {
      g.setColor(myEditor.getBackgroundColor());
      g.fillRect(clip.x, clip.y, clip.width, clip.height);
    }

    if (paintPlaceholderText(g)) {
      paintCaret(g);
      return;
    }

    int startLine = myView.yToVisualLine(Math.max(clip.y, 0));
    int endLine = myView.yToVisualLine(Math.max(clip.y + clip.height, 0));
    int startOffset = myView.visualPositionToOffset(new VisualPosition(startLine, 0));
    int endOffset = myView.visualPositionToOffset(new VisualPosition(endLine + 1, 0, true));
    ClipDetector clipDetector = new ClipDetector(myEditor, clip);

    paintBackground(g, clip, startLine, endLine);
    paintRightMargin(g, clip);
    paintCustomRenderers(g, startOffset, endOffset);
    MarkupModelEx docMarkup =
        (MarkupModelEx) DocumentMarkupModel.forDocument(myDocument, myEditor.getProject(), true);
    paintLineMarkersSeparators(g, clip, docMarkup, startOffset, endOffset);
    paintLineMarkersSeparators(g, clip, myEditor.getMarkupModel(), startOffset, endOffset);
    paintTextWithEffects(g, clip, startLine, endLine);
    paintHighlightersAfterEndOfLine(g, docMarkup, startOffset, endOffset);
    paintHighlightersAfterEndOfLine(g, myEditor.getMarkupModel(), startOffset, endOffset);
    paintBorderEffect(g, clipDetector, myEditor.getHighlighter(), startOffset, endOffset);
    paintBorderEffect(g, clipDetector, docMarkup, startOffset, endOffset);
    paintBorderEffect(g, clipDetector, myEditor.getMarkupModel(), startOffset, endOffset);

    paintCaret(g);

    paintComposedTextDecoration(g);
  }
  @Nullable
  @Override
  public TextBlockTransferableData collectTransferableData(
      PsiFile file, Editor editor, int[] startOffsets, int[] endOffsets) {
    if (!Registry.is("editor.richcopy.enable")) {
      return null;
    }

    try {
      for (TextWithMarkupBuilder builder : myBuilders) {
        builder.reset();
      }
      SelectionModel selectionModel = editor.getSelectionModel();
      if (selectionModel.hasBlockSelection()) {
        return null; // unsupported legacy mode
      }

      RichCopySettings settings = RichCopySettings.getInstance();
      List<Caret> carets = editor.getCaretModel().getAllCarets();
      Caret firstCaret = carets.get(0);
      final int indentSymbolsToStrip;
      final int firstLineStartOffset;
      if (settings.isStripIndents() && carets.size() == 1) {
        Pair<Integer, Integer> p =
            calcIndentSymbolsToStrip(
                editor.getDocument(), firstCaret.getSelectionStart(), firstCaret.getSelectionEnd());
        firstLineStartOffset = p.first;
        indentSymbolsToStrip = p.second;
      } else {
        firstLineStartOffset = firstCaret.getSelectionStart();
        indentSymbolsToStrip = 0;
      }
      logInitial(editor, startOffsets, endOffsets, indentSymbolsToStrip, firstLineStartOffset);
      CharSequence text = editor.getDocument().getCharsSequence();
      EditorColorsScheme schemeToUse = settings.getColorsScheme(editor.getColorsScheme());
      EditorHighlighter highlighter =
          HighlighterFactory.createHighlighter(
              file.getVirtualFile(), schemeToUse, file.getProject());
      highlighter.setText(text);
      MarkupModel markupModel =
          DocumentMarkupModel.forDocument(editor.getDocument(), file.getProject(), false);
      Context context = new Context(text, schemeToUse, indentSymbolsToStrip);
      int shift = 0;
      int endOffset = 0;
      Caret prevCaret = null;

      for (Caret caret : carets) {
        int caretSelectionStart = caret.getSelectionStart();
        int caretSelectionEnd = caret.getSelectionEnd();
        int startOffsetToUse;
        if (caret == firstCaret) {
          startOffsetToUse = firstLineStartOffset;
        } else {
          startOffsetToUse = caretSelectionStart;
          assert prevCaret != null;
          String prevCaretSelectedText = prevCaret.getSelectedText();
          // Block selection fills short lines by white spaces.
          int fillStringLength =
              prevCaretSelectedText == null
                  ? 0
                  : prevCaretSelectedText.length()
                      - (prevCaret.getSelectionEnd() - prevCaret.getSelectionStart());
          int endLineOffset = endOffset + shift + fillStringLength;
          context.builder.addText(endLineOffset, endLineOffset + 1);
          shift++; // Block selection ends '\n' at line end
          shift += fillStringLength;
        }
        shift += endOffset - caretSelectionStart;
        endOffset = caretSelectionEnd;
        context.reset(shift);
        prevCaret = caret;
        if (endOffset <= startOffsetToUse) {
          continue;
        }
        MarkupIterator markupIterator =
            new MarkupIterator(
                text,
                new CompositeRangeIterator(
                    schemeToUse,
                    new HighlighterRangeIterator(highlighter, startOffsetToUse, endOffset),
                    new MarkupModelRangeIterator(
                        markupModel, schemeToUse, startOffsetToUse, endOffset)),
                schemeToUse);
        try {
          context.iterate(markupIterator, endOffset);
        } finally {
          markupIterator.dispose();
        }
      }
      SyntaxInfo syntaxInfo = context.finish();
      logSyntaxInfo(syntaxInfo);

      for (TextWithMarkupBuilder builder : myBuilders) {
        builder.build(syntaxInfo);
      }
    } catch (Exception e) {
      // catching the exception so that the rest of copy/paste functionality can still work fine
      LOG.error(e);
    }
    return null;
  }
  protected String addTextRangeToHistory(
      TextRange textRange, final EditorEx consoleEditor, boolean preserveMarkup) {
    final Document history = myHistoryViewer.getDocument();
    final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
    if (myPrompt != null) {
      appendToHistoryDocument(history, myPrompt);
    }
    markupModel.addRangeHighlighter(
        history.getTextLength() - StringUtil.length(myPrompt),
        history.getTextLength(),
        HighlighterLayer.SYNTAX,
        ConsoleViewContentType.USER_INPUT.getAttributes(),
        HighlighterTargetArea.EXACT_RANGE);
    final int localStartOffset = textRange.getStartOffset();
    String text;
    EditorHighlighter highlighter;
    if (consoleEditor instanceof EditorWindow) {
      EditorWindow editorWindow = (EditorWindow) consoleEditor;
      EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
      PsiFile file = editorWindow.getInjectedFile();
      final VirtualFile virtualFile = file.getVirtualFile();
      assert virtualFile != null;
      highlighter = HighlighterFactory.createHighlighter(virtualFile, scheme, getProject());
      String fullText = InjectedLanguageUtil.getUnescapedText(file, null, null);
      highlighter.setText(fullText);
      text = textRange.substring(fullText);
    } else {
      text = consoleEditor.getDocument().getText(textRange);
      highlighter = consoleEditor.getHighlighter();
    }
    // offset can be changed after text trimming after insert due to buffer constraints
    appendToHistoryDocument(history, text);
    int offset = history.getTextLength() - text.length();

    final HighlighterIterator iterator = highlighter.createIterator(localStartOffset);
    final int localEndOffset = textRange.getEndOffset();
    while (!iterator.atEnd()) {
      final int itStart = iterator.getStart();
      if (itStart > localEndOffset) break;
      final int itEnd = iterator.getEnd();
      if (itEnd >= localStartOffset) {
        final int start = Math.max(itStart, localStartOffset) - localStartOffset + offset;
        final int end = Math.min(itEnd, localEndOffset) - localStartOffset + offset;
        markupModel.addRangeHighlighter(
            start,
            end,
            HighlighterLayer.SYNTAX,
            iterator.getTextAttributes(),
            HighlighterTargetArea.EXACT_RANGE);
      }
      iterator.advance();
    }
    if (preserveMarkup) {
      duplicateHighlighters(
          markupModel,
          DocumentMarkupModel.forDocument(consoleEditor.getDocument(), myProject, true),
          offset,
          textRange);
      duplicateHighlighters(markupModel, consoleEditor.getMarkupModel(), offset, textRange);
    }
    if (!text.endsWith("\n")) {
      appendToHistoryDocument(history, "\n");
    }
    return text;
  }
 public static void setLineMarkers(
     @NotNull Document document, List<LineMarkerInfo> lineMarkers, Project project) {
   ApplicationManager.getApplication().assertIsDispatchThread();
   MarkupModel markup = DocumentMarkupModel.forDocument(document, project, true);
   markup.putUserData(MARKERS_IN_EDITOR_DOCUMENT_KEY, lineMarkers);
 }
 @Nullable
 public static List<LineMarkerInfo> getLineMarkers(Document document, Project project) {
   ApplicationManager.getApplication().assertIsDispatchThread();
   MarkupModel markup = DocumentMarkupModel.forDocument(document, project, true);
   return markup.getUserData(MARKERS_IN_EDITOR_DOCUMENT_KEY);
 }
  public void showCoverageInformation(final CoverageSuitesBundle suite) {
    // Store the values of myFile and myEditor in local variables to avoid an NPE after dispose()
    // has been called in the EDT.
    final PsiFile psiFile = myFile;
    final Editor editor = myEditor;
    final Document document = myDocument;
    if (editor == null || psiFile == null || document == null) return;
    final MarkupModel markupModel = DocumentMarkupModel.forDocument(document, myProject, true);
    final List<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
    final ProjectData data = suite.getCoverageData();
    if (data == null) {
      coverageDataNotFound(suite);
      return;
    }
    final CoverageEngine engine = suite.getCoverageEngine();
    final Set<String> qualifiedNames = engine.getQualifiedNames(psiFile);

    // let's find old content in local history and build mapping from old lines to new one
    // local history doesn't index libraries, so let's distinguish libraries content with other one
    final ProjectFileIndex projectFileIndex =
        ProjectRootManager.getInstance(myProject).getFileIndex();
    final VirtualFile file = psiFile.getVirtualFile();
    LOG.assertTrue(file != null);

    final long fileTimeStamp = file.getTimeStamp();
    final long coverageTimeStamp = suite.getLastCoverageTimeStamp();
    final TIntIntHashMap oldToNewLineMapping;

    // do not show coverage info over cls
    if (engine.isInLibraryClasses(myProject, file)) {
      return;
    }
    // if in libraries content
    if (projectFileIndex.isInLibrarySource(file)) {
      // compare file and coverage timestamps
      if (fileTimeStamp > coverageTimeStamp) {
        showEditorWarningMessage(CodeInsightBundle.message("coverage.data.outdated"));
        return;
      }
      oldToNewLineMapping = null;
    } else {
      // check local history
      oldToNewLineMapping = getOldToNewLineMapping(coverageTimeStamp);
      if (oldToNewLineMapping == null) {

        // if history for file isn't available let's check timestamps
        if (fileTimeStamp > coverageTimeStamp
            && classesArePresentInCoverageData(data, qualifiedNames)) {
          showEditorWarningMessage(CodeInsightBundle.message("coverage.data.outdated"));
          return;
        }
      }
    }

    if (editor.getUserData(COVERAGE_HIGHLIGHTERS) != null) {
      // highlighters already collected - no need to do it twice
      return;
    }

    final Module module =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<Module>() {
                  @Nullable
                  @Override
                  public Module compute() {
                    return ModuleUtilCore.findModuleForPsiElement(psiFile);
                  }
                });
    if (module != null) {
      if (engine.recompileProjectAndRerunAction(
          module,
          suite,
          () -> CoverageDataManager.getInstance(myProject).chooseSuitesBundle(suite))) {
        return;
      }
    }

    // now if oldToNewLineMapping is null we should use f(x)=id(x) mapping

    // E.g. all *.class files for java source file with several classes
    final Set<File> outputFiles = engine.getCorrespondingOutputFiles(psiFile, module, suite);

    final boolean subCoverageActive =
        CoverageDataManager.getInstance(myProject).isSubCoverageActive();
    final boolean coverageByTestApplicable =
        suite.isCoverageByTestApplicable()
            && !(subCoverageActive && suite.isCoverageByTestEnabled());
    final TreeMap<Integer, LineData> executableLines = new TreeMap<Integer, LineData>();
    final TreeMap<Integer, Object[]> classLines = new TreeMap<Integer, Object[]>();
    final TreeMap<Integer, String> classNames = new TreeMap<Integer, String>();
    class HighlightersCollector {
      private void collect(File outputFile, final String qualifiedName) {
        final ClassData fileData = data.getClassData(qualifiedName);
        if (fileData != null) {
          final Object[] lines = fileData.getLines();
          if (lines != null) {
            final Object[] postProcessedLines =
                suite.getCoverageEngine().postProcessExecutableLines(lines, editor);
            for (Object lineData : postProcessedLines) {
              if (lineData instanceof LineData) {
                final int line = ((LineData) lineData).getLineNumber() - 1;
                final int lineNumberInCurrent;
                if (oldToNewLineMapping != null) {
                  // use mapping based on local history
                  if (!oldToNewLineMapping.contains(line)) {
                    continue;
                  }
                  lineNumberInCurrent = oldToNewLineMapping.get(line);
                } else {
                  // use id mapping
                  lineNumberInCurrent = line;
                }
                LOG.assertTrue(lineNumberInCurrent < document.getLineCount());
                executableLines.put(line, (LineData) lineData);

                classLines.put(line, postProcessedLines);
                classNames.put(line, qualifiedName);

                ApplicationManager.getApplication()
                    .invokeLater(
                        () -> {
                          if (lineNumberInCurrent >= document.getLineCount()) return;
                          final RangeHighlighter highlighter =
                              createRangeHighlighter(
                                  suite.getLastCoverageTimeStamp(),
                                  markupModel,
                                  coverageByTestApplicable,
                                  executableLines,
                                  qualifiedName,
                                  line,
                                  lineNumberInCurrent,
                                  suite,
                                  postProcessedLines);
                          highlighters.add(highlighter);
                        });
              }
            }
          }
        } else if (outputFile != null
            && !subCoverageActive
            && engine.includeUntouchedFileInCoverage(qualifiedName, outputFile, psiFile, suite)) {
          collectNonCoveredFileInfo(
              outputFile, highlighters, markupModel, executableLines, coverageByTestApplicable);
        }
      }
    }

    final HighlightersCollector collector = new HighlightersCollector();
    if (!outputFiles.isEmpty()) {
      for (File outputFile : outputFiles) {
        final String qualifiedName = engine.getQualifiedName(outputFile, psiFile);
        if (qualifiedName != null) {
          collector.collect(outputFile, qualifiedName);
        }
      }
    } else { // check non-compilable classes which present in ProjectData
      for (String qName : qualifiedNames) {
        collector.collect(null, qName);
      }
    }
    ApplicationManager.getApplication()
        .invokeLater(
            () -> {
              if (myEditor != null && highlighters.size() > 0) {
                editor.putUserData(COVERAGE_HIGHLIGHTERS, highlighters);
              }
            });

    final DocumentListener documentListener =
        new DocumentAdapter() {
          @Override
          public void documentChanged(final DocumentEvent e) {
            myNewToOldLines = null;
            myOldToNewLines = null;
            List<RangeHighlighter> rangeHighlighters = editor.getUserData(COVERAGE_HIGHLIGHTERS);
            if (rangeHighlighters == null) rangeHighlighters = new ArrayList<RangeHighlighter>();
            int offset = e.getOffset();
            final int lineNumber = document.getLineNumber(offset);
            final int lastLineNumber = document.getLineNumber(offset + e.getNewLength());
            final TextRange changeRange =
                new TextRange(
                    document.getLineStartOffset(lineNumber),
                    document.getLineEndOffset(lastLineNumber));
            for (Iterator<RangeHighlighter> it = rangeHighlighters.iterator(); it.hasNext(); ) {
              final RangeHighlighter highlighter = it.next();
              if (!highlighter.isValid() || TextRange.create(highlighter).intersects(changeRange)) {
                highlighter.dispose();
                it.remove();
              }
            }
            final List<RangeHighlighter> highlighters = rangeHighlighters;
            myUpdateAlarm.cancelAllRequests();
            if (!myUpdateAlarm.isDisposed()) {
              myUpdateAlarm.addRequest(
                  () -> {
                    final TIntIntHashMap newToOldLineMapping =
                        getNewToOldLineMapping(suite.getLastCoverageTimeStamp());
                    if (newToOldLineMapping != null) {
                      ApplicationManager.getApplication()
                          .invokeLater(
                              () -> {
                                if (editor.isDisposed()) return;
                                for (int line = lineNumber; line <= lastLineNumber; line++) {
                                  final int oldLineNumber = newToOldLineMapping.get(line);
                                  final LineData lineData = executableLines.get(oldLineNumber);
                                  if (lineData != null) {
                                    RangeHighlighter rangeHighlighter =
                                        createRangeHighlighter(
                                            suite.getLastCoverageTimeStamp(),
                                            markupModel,
                                            coverageByTestApplicable,
                                            executableLines,
                                            classNames.get(oldLineNumber),
                                            oldLineNumber,
                                            line,
                                            suite,
                                            classLines.get(oldLineNumber));
                                    highlighters.add(rangeHighlighter);
                                  }
                                }
                                editor.putUserData(
                                    COVERAGE_HIGHLIGHTERS,
                                    highlighters.size() > 0 ? highlighters : null);
                              });
                    }
                  },
                  100);
            }
          }
        };
    document.addDocumentListener(documentListener);
    editor.putUserData(COVERAGE_DOCUMENT_LISTENER, documentListener);
  }