private void collectNonCoveredFileInfo(
      final File outputFile,
      final List<RangeHighlighter> highlighters,
      final MarkupModel markupModel,
      final TreeMap<Integer, LineData> executableLines,
      final boolean coverageByTestApplicable) {
    final CoverageSuitesBundle coverageSuite =
        CoverageDataManager.getInstance(myProject).getCurrentSuitesBundle();
    if (coverageSuite == null) return;
    final TIntIntHashMap mapping;
    if (outputFile.lastModified() < getVirtualFile().getTimeStamp()) {
      mapping = getOldToNewLineMapping(outputFile.lastModified());
      if (mapping == null) return;
    } else {
      mapping = null;
    }

    final List<Integer> uncoveredLines =
        coverageSuite
            .getCoverageEngine()
            .collectSrcLinesForUntouchedFile(outputFile, coverageSuite);

    final int lineCount = myDocument.getLineCount();
    if (uncoveredLines == null) {
      for (int lineNumber = 0; lineNumber < lineCount; lineNumber++) {
        addHighlighter(
            outputFile,
            highlighters,
            markupModel,
            executableLines,
            coverageByTestApplicable,
            coverageSuite,
            lineNumber,
            lineNumber);
      }
    } else {
      for (int lineNumber : uncoveredLines) {
        if (lineNumber >= lineCount) {
          continue;
        }

        final int updatedLineNumber = mapping != null ? mapping.get(lineNumber) : lineNumber;

        addHighlighter(
            outputFile,
            highlighters,
            markupModel,
            executableLines,
            coverageByTestApplicable,
            coverageSuite,
            lineNumber,
            updatedLineNumber);
      }
    }
  }
  private RangeHighlighter createRangeHighlighter(
      final long date,
      final MarkupModel markupModel,
      final boolean coverageByTestApplicable,
      final TreeMap<Integer, LineData> executableLines,
      @Nullable final String className,
      final int line,
      final int lineNumberInCurrent,
      @NotNull final CoverageSuitesBundle coverageSuite,
      Object[] lines) {
    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    final TextAttributes attributes =
        scheme.getAttributes(CoverageLineMarkerRenderer.getAttributesKey(line, executableLines));
    TextAttributes textAttributes = null;
    if (attributes.getBackgroundColor() != null) {
      textAttributes = attributes;
    }
    final int startOffset = myDocument.getLineStartOffset(lineNumberInCurrent);
    final int endOffset = myDocument.getLineEndOffset(lineNumberInCurrent);
    final RangeHighlighter highlighter =
        markupModel.addRangeHighlighter(
            startOffset,
            endOffset,
            HighlighterLayer.SELECTION - 1,
            textAttributes,
            HighlighterTargetArea.LINES_IN_RANGE);
    final Function<Integer, Integer> newToOldConverter =
        newLine -> {
          if (myEditor == null) return -1;
          final TIntIntHashMap oldLineMapping = getNewToOldLineMapping(date);
          return oldLineMapping != null
              ? oldLineMapping.get(newLine.intValue())
              : newLine.intValue();
        };
    final Function<Integer, Integer> oldToNewConverter =
        newLine -> {
          if (myEditor == null) return -1;
          final TIntIntHashMap newLineMapping = getOldToNewLineMapping(date);
          return newLineMapping != null
              ? newLineMapping.get(newLine.intValue())
              : newLine.intValue();
        };
    final CoverageLineMarkerRenderer markerRenderer =
        coverageSuite
            .getCoverageEngine()
            .getLineMarkerRenderer(
                line,
                className,
                executableLines,
                coverageByTestApplicable,
                coverageSuite,
                newToOldConverter,
                oldToNewConverter,
                CoverageDataManager.getInstance(myProject).isSubCoverageActive());
    highlighter.setLineMarkerRenderer(markerRenderer);

    final LineData lineData = className != null ? (LineData) lines[line + 1] : null;
    if (lineData != null && lineData.getStatus() == LineCoverage.NONE) {
      highlighter.setErrorStripeMarkColor(markerRenderer.getErrorStripeColor(myEditor));
      highlighter.setThinErrorStripeMark(true);
      highlighter.setGreedyToLeft(true);
      highlighter.setGreedyToRight(true);
    }
    return highlighter;
  }
  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);
  }