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; }
void SourceCoverageViewHTML::renderLine( raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment, CoverageSegmentArray Segments, 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; 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<std::string> 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 coverage::CoverageSegment *S) { return S && S->HasCount && S->Count == 0; }; if (CheckIfUncovered(WrappedSegment)) { 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 (CurSeg->Col == ExpansionCol) Color = "cyan"; else if (CheckIfUncovered(CurSeg)) Color = "red"; 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. bool HasMultipleRegions = [&] { unsigned RegionCount = 0; for (const auto *S : Segments) if (S->HasCount && S->IsRegionEntry) if (++RegionCount > 1) return true; return false; }(); if (shouldRenderRegionMarkers(HasMultipleRegions)) { for (unsigned I = 0, E = Segments.size(); I < E; ++I) { const auto *CurSeg = Segments[I]; if (!CurSeg->IsRegionEntry || !CurSeg->HasCount) continue; Snippets[I + 1] = tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count), "tooltip-content"), "tooltip"); } } 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; }