1*9880d681SAndroid Build Coastguard Worker //===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
2*9880d681SAndroid Build Coastguard Worker //
3*9880d681SAndroid Build Coastguard Worker // The LLVM Compiler Infrastructure
4*9880d681SAndroid Build Coastguard Worker //
5*9880d681SAndroid Build Coastguard Worker // This file is distributed under the University of Illinois Open Source
6*9880d681SAndroid Build Coastguard Worker // License. See LICENSE.TXT for details.
7*9880d681SAndroid Build Coastguard Worker //
8*9880d681SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
9*9880d681SAndroid Build Coastguard Worker ///
10*9880d681SAndroid Build Coastguard Worker /// \file This file implements the html coverage renderer.
11*9880d681SAndroid Build Coastguard Worker ///
12*9880d681SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
13*9880d681SAndroid Build Coastguard Worker
14*9880d681SAndroid Build Coastguard Worker #include "SourceCoverageViewHTML.h"
15*9880d681SAndroid Build Coastguard Worker #include "llvm/ADT/Optional.h"
16*9880d681SAndroid Build Coastguard Worker #include "llvm/ADT/SmallString.h"
17*9880d681SAndroid Build Coastguard Worker #include "llvm/ADT/StringExtras.h"
18*9880d681SAndroid Build Coastguard Worker #include "llvm/Support/Path.h"
19*9880d681SAndroid Build Coastguard Worker
20*9880d681SAndroid Build Coastguard Worker using namespace llvm;
21*9880d681SAndroid Build Coastguard Worker
22*9880d681SAndroid Build Coastguard Worker namespace {
23*9880d681SAndroid Build Coastguard Worker
24*9880d681SAndroid Build Coastguard Worker const char *BeginHeader =
25*9880d681SAndroid Build Coastguard Worker "<head>"
26*9880d681SAndroid Build Coastguard Worker "<meta name='viewport' content='width=device-width,initial-scale=1'>"
27*9880d681SAndroid Build Coastguard Worker "<meta charset='UTF-8'>";
28*9880d681SAndroid Build Coastguard Worker
29*9880d681SAndroid Build Coastguard Worker const char *CSSForCoverage =
30*9880d681SAndroid Build Coastguard Worker "<style>"
31*9880d681SAndroid Build Coastguard Worker R"(
32*9880d681SAndroid Build Coastguard Worker
33*9880d681SAndroid Build Coastguard Worker .red {
34*9880d681SAndroid Build Coastguard Worker background-color: #FFD0D0;
35*9880d681SAndroid Build Coastguard Worker }
36*9880d681SAndroid Build Coastguard Worker .cyan {
37*9880d681SAndroid Build Coastguard Worker background-color: cyan;
38*9880d681SAndroid Build Coastguard Worker }
39*9880d681SAndroid Build Coastguard Worker .black {
40*9880d681SAndroid Build Coastguard Worker background-color: black;
41*9880d681SAndroid Build Coastguard Worker color: white;
42*9880d681SAndroid Build Coastguard Worker }
43*9880d681SAndroid Build Coastguard Worker .green {
44*9880d681SAndroid Build Coastguard Worker background-color: #98FFA6;
45*9880d681SAndroid Build Coastguard Worker color: white;
46*9880d681SAndroid Build Coastguard Worker }
47*9880d681SAndroid Build Coastguard Worker .magenta {
48*9880d681SAndroid Build Coastguard Worker background-color: #F998FF;
49*9880d681SAndroid Build Coastguard Worker color: white;
50*9880d681SAndroid Build Coastguard Worker }
51*9880d681SAndroid Build Coastguard Worker body {
52*9880d681SAndroid Build Coastguard Worker font-family: -apple-system, sans-serif;
53*9880d681SAndroid Build Coastguard Worker }
54*9880d681SAndroid Build Coastguard Worker pre {
55*9880d681SAndroid Build Coastguard Worker margin-top: 0px !important;
56*9880d681SAndroid Build Coastguard Worker margin-bottom: 0px !important;
57*9880d681SAndroid Build Coastguard Worker }
58*9880d681SAndroid Build Coastguard Worker .source-name-title {
59*9880d681SAndroid Build Coastguard Worker padding: 5px 10px;
60*9880d681SAndroid Build Coastguard Worker border-bottom: 1px solid #dbdbdb;
61*9880d681SAndroid Build Coastguard Worker background-color: #eee;
62*9880d681SAndroid Build Coastguard Worker }
63*9880d681SAndroid Build Coastguard Worker .centered {
64*9880d681SAndroid Build Coastguard Worker display: table;
65*9880d681SAndroid Build Coastguard Worker margin-left: auto;
66*9880d681SAndroid Build Coastguard Worker margin-right: auto;
67*9880d681SAndroid Build Coastguard Worker border: 1px solid #dbdbdb;
68*9880d681SAndroid Build Coastguard Worker border-radius: 3px;
69*9880d681SAndroid Build Coastguard Worker }
70*9880d681SAndroid Build Coastguard Worker .expansion-view {
71*9880d681SAndroid Build Coastguard Worker background-color: rgba(0, 0, 0, 0);
72*9880d681SAndroid Build Coastguard Worker margin-left: 0px;
73*9880d681SAndroid Build Coastguard Worker margin-top: 5px;
74*9880d681SAndroid Build Coastguard Worker margin-right: 5px;
75*9880d681SAndroid Build Coastguard Worker margin-bottom: 5px;
76*9880d681SAndroid Build Coastguard Worker border: 1px solid #dbdbdb;
77*9880d681SAndroid Build Coastguard Worker border-radius: 3px;
78*9880d681SAndroid Build Coastguard Worker }
79*9880d681SAndroid Build Coastguard Worker table {
80*9880d681SAndroid Build Coastguard Worker border-collapse: collapse;
81*9880d681SAndroid Build Coastguard Worker }
82*9880d681SAndroid Build Coastguard Worker .line-number {
83*9880d681SAndroid Build Coastguard Worker text-align: right;
84*9880d681SAndroid Build Coastguard Worker color: #aaa;
85*9880d681SAndroid Build Coastguard Worker }
86*9880d681SAndroid Build Coastguard Worker .covered-line {
87*9880d681SAndroid Build Coastguard Worker text-align: right;
88*9880d681SAndroid Build Coastguard Worker color: #0080ff;
89*9880d681SAndroid Build Coastguard Worker }
90*9880d681SAndroid Build Coastguard Worker .uncovered-line {
91*9880d681SAndroid Build Coastguard Worker text-align: right;
92*9880d681SAndroid Build Coastguard Worker color: #ff3300;
93*9880d681SAndroid Build Coastguard Worker }
94*9880d681SAndroid Build Coastguard Worker .tooltip {
95*9880d681SAndroid Build Coastguard Worker position: relative;
96*9880d681SAndroid Build Coastguard Worker display: inline;
97*9880d681SAndroid Build Coastguard Worker background-color: #b3e6ff;
98*9880d681SAndroid Build Coastguard Worker text-decoration: none;
99*9880d681SAndroid Build Coastguard Worker }
100*9880d681SAndroid Build Coastguard Worker .tooltip span.tooltip-content {
101*9880d681SAndroid Build Coastguard Worker position: absolute;
102*9880d681SAndroid Build Coastguard Worker width: 100px;
103*9880d681SAndroid Build Coastguard Worker margin-left: -50px;
104*9880d681SAndroid Build Coastguard Worker color: #FFFFFF;
105*9880d681SAndroid Build Coastguard Worker background: #000000;
106*9880d681SAndroid Build Coastguard Worker height: 30px;
107*9880d681SAndroid Build Coastguard Worker line-height: 30px;
108*9880d681SAndroid Build Coastguard Worker text-align: center;
109*9880d681SAndroid Build Coastguard Worker visibility: hidden;
110*9880d681SAndroid Build Coastguard Worker border-radius: 6px;
111*9880d681SAndroid Build Coastguard Worker }
112*9880d681SAndroid Build Coastguard Worker .tooltip span.tooltip-content:after {
113*9880d681SAndroid Build Coastguard Worker content: '';
114*9880d681SAndroid Build Coastguard Worker position: absolute;
115*9880d681SAndroid Build Coastguard Worker top: 100%;
116*9880d681SAndroid Build Coastguard Worker left: 50%;
117*9880d681SAndroid Build Coastguard Worker margin-left: -8px;
118*9880d681SAndroid Build Coastguard Worker width: 0; height: 0;
119*9880d681SAndroid Build Coastguard Worker border-top: 8px solid #000000;
120*9880d681SAndroid Build Coastguard Worker border-right: 8px solid transparent;
121*9880d681SAndroid Build Coastguard Worker border-left: 8px solid transparent;
122*9880d681SAndroid Build Coastguard Worker }
123*9880d681SAndroid Build Coastguard Worker :hover.tooltip span.tooltip-content {
124*9880d681SAndroid Build Coastguard Worker visibility: visible;
125*9880d681SAndroid Build Coastguard Worker opacity: 0.8;
126*9880d681SAndroid Build Coastguard Worker bottom: 30px;
127*9880d681SAndroid Build Coastguard Worker left: 50%;
128*9880d681SAndroid Build Coastguard Worker z-index: 999;
129*9880d681SAndroid Build Coastguard Worker }
130*9880d681SAndroid Build Coastguard Worker th, td {
131*9880d681SAndroid Build Coastguard Worker vertical-align: top;
132*9880d681SAndroid Build Coastguard Worker padding: 2px 5px;
133*9880d681SAndroid Build Coastguard Worker border-collapse: collapse;
134*9880d681SAndroid Build Coastguard Worker border-right: solid 1px #eee;
135*9880d681SAndroid Build Coastguard Worker border-left: solid 1px #eee;
136*9880d681SAndroid Build Coastguard Worker }
137*9880d681SAndroid Build Coastguard Worker td:first-child {
138*9880d681SAndroid Build Coastguard Worker border-left: none;
139*9880d681SAndroid Build Coastguard Worker }
140*9880d681SAndroid Build Coastguard Worker td:last-child {
141*9880d681SAndroid Build Coastguard Worker border-right: none;
142*9880d681SAndroid Build Coastguard Worker }
143*9880d681SAndroid Build Coastguard Worker
144*9880d681SAndroid Build Coastguard Worker )"
145*9880d681SAndroid Build Coastguard Worker "</style>";
146*9880d681SAndroid Build Coastguard Worker
147*9880d681SAndroid Build Coastguard Worker const char *EndHeader = "</head>";
148*9880d681SAndroid Build Coastguard Worker
149*9880d681SAndroid Build Coastguard Worker const char *BeginCenteredDiv = "<div class='centered'>";
150*9880d681SAndroid Build Coastguard Worker
151*9880d681SAndroid Build Coastguard Worker const char *EndCenteredDiv = "</div>";
152*9880d681SAndroid Build Coastguard Worker
153*9880d681SAndroid Build Coastguard Worker const char *BeginSourceNameDiv = "<div class='source-name-title'>";
154*9880d681SAndroid Build Coastguard Worker
155*9880d681SAndroid Build Coastguard Worker const char *EndSourceNameDiv = "</div>";
156*9880d681SAndroid Build Coastguard Worker
157*9880d681SAndroid Build Coastguard Worker const char *BeginCodeTD = "<td class='code'>";
158*9880d681SAndroid Build Coastguard Worker
159*9880d681SAndroid Build Coastguard Worker const char *EndCodeTD = "</td>";
160*9880d681SAndroid Build Coastguard Worker
161*9880d681SAndroid Build Coastguard Worker const char *BeginPre = "<pre>";
162*9880d681SAndroid Build Coastguard Worker
163*9880d681SAndroid Build Coastguard Worker const char *EndPre = "</pre>";
164*9880d681SAndroid Build Coastguard Worker
165*9880d681SAndroid Build Coastguard Worker const char *BeginExpansionDiv = "<div class='expansion-view'>";
166*9880d681SAndroid Build Coastguard Worker
167*9880d681SAndroid Build Coastguard Worker const char *EndExpansionDiv = "</div>";
168*9880d681SAndroid Build Coastguard Worker
169*9880d681SAndroid Build Coastguard Worker const char *BeginTable = "<table>";
170*9880d681SAndroid Build Coastguard Worker
171*9880d681SAndroid Build Coastguard Worker const char *EndTable = "</table>";
172*9880d681SAndroid Build Coastguard Worker
emitPrelude(raw_ostream & OS)173*9880d681SAndroid Build Coastguard Worker void emitPrelude(raw_ostream &OS) {
174*9880d681SAndroid Build Coastguard Worker OS << "<!doctype html>"
175*9880d681SAndroid Build Coastguard Worker "<html>"
176*9880d681SAndroid Build Coastguard Worker << BeginHeader << CSSForCoverage << EndHeader << "<body>"
177*9880d681SAndroid Build Coastguard Worker << BeginCenteredDiv;
178*9880d681SAndroid Build Coastguard Worker }
179*9880d681SAndroid Build Coastguard Worker
emitEpilog(raw_ostream & OS)180*9880d681SAndroid Build Coastguard Worker void emitEpilog(raw_ostream &OS) {
181*9880d681SAndroid Build Coastguard Worker OS << EndCenteredDiv << "</body>"
182*9880d681SAndroid Build Coastguard Worker "</html>";
183*9880d681SAndroid Build Coastguard Worker }
184*9880d681SAndroid Build Coastguard Worker
185*9880d681SAndroid Build Coastguard Worker // Return a string with the special characters in \p Str escaped.
escape(StringRef Str)186*9880d681SAndroid Build Coastguard Worker std::string escape(StringRef Str) {
187*9880d681SAndroid Build Coastguard Worker std::string Result;
188*9880d681SAndroid Build Coastguard Worker for (char C : Str) {
189*9880d681SAndroid Build Coastguard Worker if (C == '&')
190*9880d681SAndroid Build Coastguard Worker Result += "&";
191*9880d681SAndroid Build Coastguard Worker else if (C == '<')
192*9880d681SAndroid Build Coastguard Worker Result += "<";
193*9880d681SAndroid Build Coastguard Worker else if (C == '>')
194*9880d681SAndroid Build Coastguard Worker Result += ">";
195*9880d681SAndroid Build Coastguard Worker else if (C == '\"')
196*9880d681SAndroid Build Coastguard Worker Result += """;
197*9880d681SAndroid Build Coastguard Worker else
198*9880d681SAndroid Build Coastguard Worker Result += C;
199*9880d681SAndroid Build Coastguard Worker }
200*9880d681SAndroid Build Coastguard Worker return Result;
201*9880d681SAndroid Build Coastguard Worker }
202*9880d681SAndroid Build Coastguard Worker
203*9880d681SAndroid Build Coastguard Worker // Create a \p Name tag around \p Str, and optionally set its \p ClassName.
tag(const std::string & Name,const std::string & Str,const std::string & ClassName="")204*9880d681SAndroid Build Coastguard Worker std::string tag(const std::string &Name, const std::string &Str,
205*9880d681SAndroid Build Coastguard Worker const std::string &ClassName = "") {
206*9880d681SAndroid Build Coastguard Worker std::string Tag = "<" + Name;
207*9880d681SAndroid Build Coastguard Worker if (ClassName != "")
208*9880d681SAndroid Build Coastguard Worker Tag += " class='" + ClassName + "'";
209*9880d681SAndroid Build Coastguard Worker return Tag + ">" + Str + "</" + Name + ">";
210*9880d681SAndroid Build Coastguard Worker }
211*9880d681SAndroid Build Coastguard Worker
212*9880d681SAndroid Build Coastguard Worker // Create an anchor to \p Link with the label \p Str.
a(const std::string & Link,const std::string & Str)213*9880d681SAndroid Build Coastguard Worker std::string a(const std::string &Link, const std::string &Str) {
214*9880d681SAndroid Build Coastguard Worker return "<a href='" + Link + "'>" + Str + "</a>";
215*9880d681SAndroid Build Coastguard Worker }
216*9880d681SAndroid Build Coastguard Worker
217*9880d681SAndroid Build Coastguard Worker } // anonymous namespace
218*9880d681SAndroid Build Coastguard Worker
219*9880d681SAndroid Build Coastguard Worker Expected<CoveragePrinter::OwnedStream>
createViewFile(StringRef Path,bool InToplevel)220*9880d681SAndroid Build Coastguard Worker CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
221*9880d681SAndroid Build Coastguard Worker auto OSOrErr = createOutputStream(Path, "html", InToplevel);
222*9880d681SAndroid Build Coastguard Worker if (!OSOrErr)
223*9880d681SAndroid Build Coastguard Worker return OSOrErr;
224*9880d681SAndroid Build Coastguard Worker
225*9880d681SAndroid Build Coastguard Worker OwnedStream OS = std::move(OSOrErr.get());
226*9880d681SAndroid Build Coastguard Worker emitPrelude(*OS.get());
227*9880d681SAndroid Build Coastguard Worker return std::move(OS);
228*9880d681SAndroid Build Coastguard Worker }
229*9880d681SAndroid Build Coastguard Worker
closeViewFile(OwnedStream OS)230*9880d681SAndroid Build Coastguard Worker void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
231*9880d681SAndroid Build Coastguard Worker emitEpilog(*OS.get());
232*9880d681SAndroid Build Coastguard Worker }
233*9880d681SAndroid Build Coastguard Worker
createIndexFile(ArrayRef<StringRef> SourceFiles)234*9880d681SAndroid Build Coastguard Worker Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
235*9880d681SAndroid Build Coastguard Worker auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
236*9880d681SAndroid Build Coastguard Worker if (Error E = OSOrErr.takeError())
237*9880d681SAndroid Build Coastguard Worker return E;
238*9880d681SAndroid Build Coastguard Worker auto OS = std::move(OSOrErr.get());
239*9880d681SAndroid Build Coastguard Worker raw_ostream &OSRef = *OS.get();
240*9880d681SAndroid Build Coastguard Worker
241*9880d681SAndroid Build Coastguard Worker // Emit a table containing links to reports for each file in the covmapping.
242*9880d681SAndroid Build Coastguard Worker emitPrelude(OSRef);
243*9880d681SAndroid Build Coastguard Worker OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv;
244*9880d681SAndroid Build Coastguard Worker OSRef << BeginTable;
245*9880d681SAndroid Build Coastguard Worker for (StringRef SF : SourceFiles) {
246*9880d681SAndroid Build Coastguard Worker std::string LinkText = escape(sys::path::relative_path(SF));
247*9880d681SAndroid Build Coastguard Worker std::string LinkTarget =
248*9880d681SAndroid Build Coastguard Worker escape(getOutputPath(SF, "html", /*InToplevel=*/false));
249*9880d681SAndroid Build Coastguard Worker OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code")));
250*9880d681SAndroid Build Coastguard Worker }
251*9880d681SAndroid Build Coastguard Worker OSRef << EndTable;
252*9880d681SAndroid Build Coastguard Worker emitEpilog(OSRef);
253*9880d681SAndroid Build Coastguard Worker
254*9880d681SAndroid Build Coastguard Worker return Error::success();
255*9880d681SAndroid Build Coastguard Worker }
256*9880d681SAndroid Build Coastguard Worker
renderViewHeader(raw_ostream & OS)257*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
258*9880d681SAndroid Build Coastguard Worker OS << BeginTable;
259*9880d681SAndroid Build Coastguard Worker }
260*9880d681SAndroid Build Coastguard Worker
renderViewFooter(raw_ostream & OS)261*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
262*9880d681SAndroid Build Coastguard Worker OS << EndTable;
263*9880d681SAndroid Build Coastguard Worker }
264*9880d681SAndroid Build Coastguard Worker
renderSourceName(raw_ostream & OS)265*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS) {
266*9880d681SAndroid Build Coastguard Worker OS << BeginSourceNameDiv << tag("pre", escape(getSourceName()))
267*9880d681SAndroid Build Coastguard Worker << EndSourceNameDiv;
268*9880d681SAndroid Build Coastguard Worker }
269*9880d681SAndroid Build Coastguard Worker
renderLinePrefix(raw_ostream & OS,unsigned)270*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
271*9880d681SAndroid Build Coastguard Worker OS << "<tr>";
272*9880d681SAndroid Build Coastguard Worker }
273*9880d681SAndroid Build Coastguard Worker
renderLineSuffix(raw_ostream & OS,unsigned)274*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
275*9880d681SAndroid Build Coastguard Worker // If this view has sub-views, renderLine() cannot close the view's cell.
276*9880d681SAndroid Build Coastguard Worker // Take care of it here, after all sub-views have been rendered.
277*9880d681SAndroid Build Coastguard Worker if (hasSubViews())
278*9880d681SAndroid Build Coastguard Worker OS << EndCodeTD;
279*9880d681SAndroid Build Coastguard Worker OS << "</tr>";
280*9880d681SAndroid Build Coastguard Worker }
281*9880d681SAndroid Build Coastguard Worker
renderViewDivider(raw_ostream &,unsigned)282*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
283*9880d681SAndroid Build Coastguard Worker // The table-based output makes view dividers unnecessary.
284*9880d681SAndroid Build Coastguard Worker }
285*9880d681SAndroid Build Coastguard Worker
renderLine(raw_ostream & OS,LineRef L,const coverage::CoverageSegment * WrappedSegment,CoverageSegmentArray Segments,unsigned ExpansionCol,unsigned)286*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderLine(
287*9880d681SAndroid Build Coastguard Worker raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
288*9880d681SAndroid Build Coastguard Worker CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned) {
289*9880d681SAndroid Build Coastguard Worker StringRef Line = L.Line;
290*9880d681SAndroid Build Coastguard Worker
291*9880d681SAndroid Build Coastguard Worker // Steps for handling text-escaping, highlighting, and tooltip creation:
292*9880d681SAndroid Build Coastguard Worker //
293*9880d681SAndroid Build Coastguard Worker // 1. Split the line into N+1 snippets, where N = |Segments|. The first
294*9880d681SAndroid Build Coastguard Worker // snippet starts from Col=1 and ends at the start of the first segment.
295*9880d681SAndroid Build Coastguard Worker // The last snippet starts at the last mapped column in the line and ends
296*9880d681SAndroid Build Coastguard Worker // at the end of the line. Both are required but may be empty.
297*9880d681SAndroid Build Coastguard Worker
298*9880d681SAndroid Build Coastguard Worker SmallVector<std::string, 8> Snippets;
299*9880d681SAndroid Build Coastguard Worker
300*9880d681SAndroid Build Coastguard Worker unsigned LCol = 1;
301*9880d681SAndroid Build Coastguard Worker auto Snip = [&](unsigned Start, unsigned Len) {
302*9880d681SAndroid Build Coastguard Worker assert(Start + Len <= Line.size() && "Snippet extends past the EOL");
303*9880d681SAndroid Build Coastguard Worker Snippets.push_back(Line.substr(Start, Len));
304*9880d681SAndroid Build Coastguard Worker LCol += Len;
305*9880d681SAndroid Build Coastguard Worker };
306*9880d681SAndroid Build Coastguard Worker
307*9880d681SAndroid Build Coastguard Worker Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));
308*9880d681SAndroid Build Coastguard Worker
309*9880d681SAndroid Build Coastguard Worker for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
310*9880d681SAndroid Build Coastguard Worker assert(LCol == Segments[I - 1]->Col && "Snippet start position is wrong");
311*9880d681SAndroid Build Coastguard Worker Snip(LCol - 1, Segments[I]->Col - LCol);
312*9880d681SAndroid Build Coastguard Worker }
313*9880d681SAndroid Build Coastguard Worker
314*9880d681SAndroid Build Coastguard Worker // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
315*9880d681SAndroid Build Coastguard Worker Snip(LCol - 1, Line.size() + 1 - LCol);
316*9880d681SAndroid Build Coastguard Worker assert(LCol == Line.size() + 1 && "Final snippet doesn't reach the EOL");
317*9880d681SAndroid Build Coastguard Worker
318*9880d681SAndroid Build Coastguard Worker // 2. Escape all of the snippets.
319*9880d681SAndroid Build Coastguard Worker
320*9880d681SAndroid Build Coastguard Worker for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
321*9880d681SAndroid Build Coastguard Worker Snippets[I] = escape(Snippets[I]);
322*9880d681SAndroid Build Coastguard Worker
323*9880d681SAndroid Build Coastguard Worker // 3. Use \p WrappedSegment to set the highlight for snippets 0 and 1. Use
324*9880d681SAndroid Build Coastguard Worker // segment 1 to set the highlight for snippet 2, segment 2 to set the
325*9880d681SAndroid Build Coastguard Worker // highlight for snippet 3, and so on.
326*9880d681SAndroid Build Coastguard Worker
327*9880d681SAndroid Build Coastguard Worker Optional<std::string> Color;
328*9880d681SAndroid Build Coastguard Worker auto Highlight = [&](const std::string &Snippet) {
329*9880d681SAndroid Build Coastguard Worker return tag("span", Snippet, Color.getValue());
330*9880d681SAndroid Build Coastguard Worker };
331*9880d681SAndroid Build Coastguard Worker
332*9880d681SAndroid Build Coastguard Worker auto CheckIfUncovered = [](const coverage::CoverageSegment *S) {
333*9880d681SAndroid Build Coastguard Worker return S && S->HasCount && S->Count == 0;
334*9880d681SAndroid Build Coastguard Worker };
335*9880d681SAndroid Build Coastguard Worker
336*9880d681SAndroid Build Coastguard Worker if (CheckIfUncovered(WrappedSegment) ||
337*9880d681SAndroid Build Coastguard Worker CheckIfUncovered(Segments.empty() ? nullptr : Segments.front())) {
338*9880d681SAndroid Build Coastguard Worker Color = "red";
339*9880d681SAndroid Build Coastguard Worker Snippets[0] = Highlight(Snippets[0]);
340*9880d681SAndroid Build Coastguard Worker Snippets[1] = Highlight(Snippets[1]);
341*9880d681SAndroid Build Coastguard Worker }
342*9880d681SAndroid Build Coastguard Worker
343*9880d681SAndroid Build Coastguard Worker for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
344*9880d681SAndroid Build Coastguard Worker const auto *CurSeg = Segments[I];
345*9880d681SAndroid Build Coastguard Worker if (CurSeg->Col == ExpansionCol)
346*9880d681SAndroid Build Coastguard Worker Color = "cyan";
347*9880d681SAndroid Build Coastguard Worker else if (CheckIfUncovered(CurSeg))
348*9880d681SAndroid Build Coastguard Worker Color = "red";
349*9880d681SAndroid Build Coastguard Worker else
350*9880d681SAndroid Build Coastguard Worker Color = None;
351*9880d681SAndroid Build Coastguard Worker
352*9880d681SAndroid Build Coastguard Worker if (Color.hasValue())
353*9880d681SAndroid Build Coastguard Worker Snippets[I + 1] = Highlight(Snippets[I + 1]);
354*9880d681SAndroid Build Coastguard Worker }
355*9880d681SAndroid Build Coastguard Worker
356*9880d681SAndroid Build Coastguard Worker // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
357*9880d681SAndroid Build Coastguard Worker // sub-line region count tooltips if needed.
358*9880d681SAndroid Build Coastguard Worker
359*9880d681SAndroid Build Coastguard Worker bool HasMultipleRegions = [&] {
360*9880d681SAndroid Build Coastguard Worker unsigned RegionCount = 0;
361*9880d681SAndroid Build Coastguard Worker for (const auto *S : Segments)
362*9880d681SAndroid Build Coastguard Worker if (S->HasCount && S->IsRegionEntry)
363*9880d681SAndroid Build Coastguard Worker if (++RegionCount > 1)
364*9880d681SAndroid Build Coastguard Worker return true;
365*9880d681SAndroid Build Coastguard Worker return false;
366*9880d681SAndroid Build Coastguard Worker }();
367*9880d681SAndroid Build Coastguard Worker
368*9880d681SAndroid Build Coastguard Worker if (shouldRenderRegionMarkers(HasMultipleRegions)) {
369*9880d681SAndroid Build Coastguard Worker for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
370*9880d681SAndroid Build Coastguard Worker const auto *CurSeg = Segments[I];
371*9880d681SAndroid Build Coastguard Worker if (!CurSeg->IsRegionEntry || !CurSeg->HasCount)
372*9880d681SAndroid Build Coastguard Worker continue;
373*9880d681SAndroid Build Coastguard Worker
374*9880d681SAndroid Build Coastguard Worker Snippets[I + 1] =
375*9880d681SAndroid Build Coastguard Worker tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
376*9880d681SAndroid Build Coastguard Worker "tooltip-content"),
377*9880d681SAndroid Build Coastguard Worker "tooltip");
378*9880d681SAndroid Build Coastguard Worker }
379*9880d681SAndroid Build Coastguard Worker }
380*9880d681SAndroid Build Coastguard Worker
381*9880d681SAndroid Build Coastguard Worker OS << BeginCodeTD;
382*9880d681SAndroid Build Coastguard Worker OS << BeginPre;
383*9880d681SAndroid Build Coastguard Worker for (const auto &Snippet : Snippets)
384*9880d681SAndroid Build Coastguard Worker OS << Snippet;
385*9880d681SAndroid Build Coastguard Worker OS << EndPre;
386*9880d681SAndroid Build Coastguard Worker
387*9880d681SAndroid Build Coastguard Worker // If there are no sub-views left to attach to this cell, end the cell.
388*9880d681SAndroid Build Coastguard Worker // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
389*9880d681SAndroid Build Coastguard Worker if (!hasSubViews())
390*9880d681SAndroid Build Coastguard Worker OS << EndCodeTD;
391*9880d681SAndroid Build Coastguard Worker }
392*9880d681SAndroid Build Coastguard Worker
renderLineCoverageColumn(raw_ostream & OS,const LineCoverageStats & Line)393*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderLineCoverageColumn(
394*9880d681SAndroid Build Coastguard Worker raw_ostream &OS, const LineCoverageStats &Line) {
395*9880d681SAndroid Build Coastguard Worker std::string Count = "";
396*9880d681SAndroid Build Coastguard Worker if (Line.isMapped())
397*9880d681SAndroid Build Coastguard Worker Count = tag("pre", formatCount(Line.ExecutionCount));
398*9880d681SAndroid Build Coastguard Worker std::string CoverageClass =
399*9880d681SAndroid Build Coastguard Worker (Line.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
400*9880d681SAndroid Build Coastguard Worker OS << tag("td", Count, CoverageClass);
401*9880d681SAndroid Build Coastguard Worker }
402*9880d681SAndroid Build Coastguard Worker
renderLineNumberColumn(raw_ostream & OS,unsigned LineNo)403*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
404*9880d681SAndroid Build Coastguard Worker unsigned LineNo) {
405*9880d681SAndroid Build Coastguard Worker OS << tag("td", tag("pre", utostr(uint64_t(LineNo))), "line-number");
406*9880d681SAndroid Build Coastguard Worker }
407*9880d681SAndroid Build Coastguard Worker
renderRegionMarkers(raw_ostream &,CoverageSegmentArray,unsigned)408*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
409*9880d681SAndroid Build Coastguard Worker CoverageSegmentArray,
410*9880d681SAndroid Build Coastguard Worker unsigned) {
411*9880d681SAndroid Build Coastguard Worker // Region markers are rendered in-line using tooltips.
412*9880d681SAndroid Build Coastguard Worker }
413*9880d681SAndroid Build Coastguard Worker
renderExpansionSite(raw_ostream & OS,LineRef L,const coverage::CoverageSegment * WrappedSegment,CoverageSegmentArray Segments,unsigned ExpansionCol,unsigned ViewDepth)414*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderExpansionSite(
415*9880d681SAndroid Build Coastguard Worker raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
416*9880d681SAndroid Build Coastguard Worker CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
417*9880d681SAndroid Build Coastguard Worker // Render the line containing the expansion site. No extra formatting needed.
418*9880d681SAndroid Build Coastguard Worker renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
419*9880d681SAndroid Build Coastguard Worker }
420*9880d681SAndroid Build Coastguard Worker
renderExpansionView(raw_ostream & OS,ExpansionView & ESV,unsigned ViewDepth)421*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
422*9880d681SAndroid Build Coastguard Worker ExpansionView &ESV,
423*9880d681SAndroid Build Coastguard Worker unsigned ViewDepth) {
424*9880d681SAndroid Build Coastguard Worker OS << BeginExpansionDiv;
425*9880d681SAndroid Build Coastguard Worker ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
426*9880d681SAndroid Build Coastguard Worker ViewDepth + 1);
427*9880d681SAndroid Build Coastguard Worker OS << EndExpansionDiv;
428*9880d681SAndroid Build Coastguard Worker }
429*9880d681SAndroid Build Coastguard Worker
renderInstantiationView(raw_ostream & OS,InstantiationView & ISV,unsigned ViewDepth)430*9880d681SAndroid Build Coastguard Worker void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
431*9880d681SAndroid Build Coastguard Worker InstantiationView &ISV,
432*9880d681SAndroid Build Coastguard Worker unsigned ViewDepth) {
433*9880d681SAndroid Build Coastguard Worker OS << BeginExpansionDiv;
434*9880d681SAndroid Build Coastguard Worker ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
435*9880d681SAndroid Build Coastguard Worker OS << EndExpansionDiv;
436*9880d681SAndroid Build Coastguard Worker }
437