@NotNull
 public RangeHighlighter[] getHighlighters(@NotNull Editor editor) {
   Map<RangeHighlighter, HighlightInfo> highlightersMap = getHighlightInfoMap(editor, false);
   if (highlightersMap == null) return RangeHighlighter.EMPTY_ARRAY;
   Set<RangeHighlighter> set = new HashSet<RangeHighlighter>();
   for (Map.Entry<RangeHighlighter, HighlightInfo> entry : highlightersMap.entrySet()) {
     HighlightInfo info = entry.getValue();
     if (info.editor.equals(editor)) set.add(entry.getKey());
   }
   return set.toArray(new RangeHighlighter[set.size()]);
 }
  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);
  }