xref: /aosp_15_r20/external/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp (revision 9880d6810fe72a1726cb53787c6711e909410d58)
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 += "&amp;";
191*9880d681SAndroid Build Coastguard Worker     else if (C == '<')
192*9880d681SAndroid Build Coastguard Worker       Result += "&lt;";
193*9880d681SAndroid Build Coastguard Worker     else if (C == '>')
194*9880d681SAndroid Build Coastguard Worker       Result += "&gt;";
195*9880d681SAndroid Build Coastguard Worker     else if (C == '\"')
196*9880d681SAndroid Build Coastguard Worker       Result += "&quot;";
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