void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,
                                                 const LineCoverageStats &Line,
                                                 unsigned ViewDepth) {
  renderLinePrefix(OS, ViewDepth);
  OS.indent(getCombinedColumnWidth(getOptions()));

  CoverageSegmentArray Segments = Line.getLineSegments();

  // Just consider the segments which start *and* end on this line.
  if (Segments.size() > 1)
    Segments = Segments.drop_back();

  unsigned PrevColumn = 1;
  for (const auto *S : Segments) {
    if (!S->IsRegionEntry)
      continue;
    if (S->Count == Line.getExecutionCount())
      continue;
    // Skip to the new region.
    if (S->Col > PrevColumn)
      OS.indent(S->Col - PrevColumn);
    PrevColumn = S->Col + 1;
    std::string C = formatCount(S->Count);
    PrevColumn += C.size();
    OS << '^' << C;

    if (getOptions().Debug)
      errs() << "Marker at " << S->Line << ":" << S->Col << " = "
            << formatCount(S->Count) << "\n";
  }
  OS << '\n';
}
void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L,
                                        const LineCoverageStats &LCS,
                                        unsigned ExpansionCol,
                                        unsigned ViewDepth) {
  StringRef Line = L.Line;
  unsigned LineNumber = L.LineNo;
  auto *WrappedSegment = LCS.getWrappedSegment();
  CoverageSegmentArray Segments = LCS.getLineSegments();

  Optional<raw_ostream::Colors> Highlight;
  SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;

  // The first segment overlaps from a previous line, so we treat it specially.
  if (WrappedSegment && !WrappedSegment->IsGapRegion &&
      WrappedSegment->HasCount && WrappedSegment->Count == 0)
    Highlight = raw_ostream::RED;

  // Output each segment of the line, possibly highlighted.
  unsigned Col = 1;
  for (const auto *S : Segments) {
    unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
    colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
                    getOptions().Colors && Highlight, /*Bold=*/false,
                    /*BG=*/true)
        << Line.substr(Col - 1, End - Col);
    if (getOptions().Debug && Highlight)
      HighlightedRanges.push_back(std::make_pair(Col, End));
    Col = End;
    if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
        S->HasCount && S->Count == 0)
      Highlight = raw_ostream::RED;
    else if (Col == ExpansionCol)
      Highlight = raw_ostream::CYAN;
    else
      Highlight = None;
  }

  // Show the rest of the line.
  colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
                  getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
      << Line.substr(Col - 1, Line.size() - Col + 1);
  OS << '\n';

  if (getOptions().Debug) {
    for (const auto &Range : HighlightedRanges)
      errs() << "Highlighted line " << LineNumber << ", " << Range.first
             << " -> " << Range.second << '\n';
    if (Highlight)
      errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
  }
}
Esempio n. 3
0
bool SourceCoverageView::shouldRenderRegionMarkers(
    const LineCoverageStats &LCS) const {
  if (!getOptions().ShowRegionMarkers)
    return false;

  CoverageSegmentArray Segments = LCS.getLineSegments();
  if (Segments.empty())
    return false;
  for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) {
    const auto *CurSeg = Segments[I];
    if (!CurSeg->IsRegionEntry || CurSeg->Count == LCS.getExecutionCount())
      continue;
    return true;
  }
  return false;
}
Esempio n. 4
0
void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L,
                                        const LineCoverageStats &LCS,
                                        unsigned ExpansionCol, unsigned) {
  StringRef Line = L.Line;
  unsigned LineNo = L.LineNo;

  // Steps for handling text-escaping, highlighting, and tooltip creation:
  //
  // 1. Split the line into N+1 snippets, where N = |Segments|. The first
  //    snippet starts from Col=1 and ends at the start of the first segment.
  //    The last snippet starts at the last mapped column in the line and ends
  //    at the end of the line. Both are required but may be empty.

  SmallVector<std::string, 8> Snippets;
  CoverageSegmentArray Segments = LCS.getLineSegments();

  unsigned LCol = 1;
  auto Snip = [&](unsigned Start, unsigned Len) {
    Snippets.push_back(Line.substr(Start, Len));
    LCol += Len;
  };

  Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));

  for (unsigned I = 1, E = Segments.size(); I < E; ++I)
    Snip(LCol - 1, Segments[I]->Col - LCol);

  // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
  Snip(LCol - 1, Line.size() + 1 - LCol);

  // 2. Escape all of the snippets.

  for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
    Snippets[I] = escape(Snippets[I], getOptions());

  // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment
  //    1 to set the highlight for snippet 2, segment 2 to set the highlight for
  //    snippet 3, and so on.

  Optional<StringRef> Color;
  SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
  auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) {
    if (getOptions().Debug)
      HighlightedRanges.emplace_back(LC, RC);
    return tag("span", Snippet, Color.getValue());
  };

  auto CheckIfUncovered = [&](const CoverageSegment *S) {
    return S && (!S->IsGapRegion || (Color && *Color == "red")) &&
           S->HasCount && S->Count == 0;
  };

  if (CheckIfUncovered(LCS.getWrappedSegment())) {
    Color = "red";
    if (!Snippets[0].empty())
      Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size());
  }

  for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
    const auto *CurSeg = Segments[I];
    if (CheckIfUncovered(CurSeg))
      Color = "red";
    else if (CurSeg->Col == ExpansionCol)
      Color = "cyan";
    else
      Color = None;

    if (Color.hasValue())
      Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col,
                                  CurSeg->Col + Snippets[I + 1].size());
  }

  if (Color.hasValue() && Segments.empty())
    Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size());

  if (getOptions().Debug) {
    for (const auto &Range : HighlightedRanges) {
      errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> ";
      if (Range.second == 0)
        errs() << "?";
      else
        errs() << Range.second;
      errs() << "\n";
    }
  }

  // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
  //    sub-line region count tooltips if needed.

  if (shouldRenderRegionMarkers(LCS)) {
    // Just consider the segments which start *and* end on this line.
    for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) {
      const auto *CurSeg = Segments[I];
      if (!CurSeg->IsRegionEntry)
        continue;
      if (CurSeg->Count == LCS.getExecutionCount())
        continue;

      Snippets[I + 1] =
          tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
                                           "tooltip-content"),
              "tooltip");

      if (getOptions().Debug)
        errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = "
               << formatCount(CurSeg->Count) << "\n";
    }
  }

  OS << BeginCodeTD;
  OS << BeginPre;
  for (const auto &Snippet : Snippets)
    OS << Snippet;
  OS << EndPre;

  // If there are no sub-views left to attach to this cell, end the cell.
  // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
  if (!hasSubViews())
    OS << EndCodeTD;
}