Skip to content

Commit ab6178f

Browse files
Samuel DegrandePKEuS
authored andcommitted
HTML report: display 'verbose' message using clickable expandable divs
1 parent 45a2986 commit ab6178f

1 file changed

Lines changed: 82 additions & 6 deletions

File tree

htmlreport/cppcheck-htmlreport

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ from pygments.formatters import HtmlFormatter
1414
from xml.sax import parse as xml_parse
1515
from xml.sax import SAXParseException as XmlParseException
1616
from xml.sax.handler import ContentHandler as XmlContentHandler
17+
from xml.sax.saxutils import escape
1718

1819
"""
1920
Turns a cppcheck xml file into a browsable html report along
@@ -56,6 +57,25 @@ h1 {
5657
display: inline-block;
5758
margin-left: 4px;
5859
}
60+
61+
div.verbose {
62+
display: inline-block;
63+
vertical-align: top;
64+
cursor: help;
65+
}
66+
67+
div.verbose div.content {
68+
display: none;
69+
position: absolute;
70+
padding: 10px;
71+
margin: 4px;
72+
max-width: 40%;
73+
white-space: pre-wrap;
74+
border: 1px solid black;
75+
background-color: #FFFFCC;
76+
cursor: auto;
77+
}
78+
5979
.highlight .hll {
6080
padding: 1px;
6181
}
@@ -155,9 +175,43 @@ HTML_HEAD = """
155175
<link rel="stylesheet" href="style.css">
156176
<style>
157177
%s
158-
</style>
178+
</style>
179+
<script language="javascript">
180+
function getStyle(el,styleProp) {
181+
if (el.currentStyle)
182+
var y = el.currentStyle[styleProp];
183+
else if (window.getComputedStyle)
184+
var y = document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
185+
return y;
186+
}
187+
function toggle() {
188+
var el = this.expandable_content;
189+
var mark = this.expandable_marker;
190+
if (el.style.display == "block") {
191+
el.style.display = "none";
192+
mark.innerHTML = "[+]";
193+
} else {
194+
el.style.display = "block";
195+
mark.innerHTML = "[-]";
196+
}
197+
}
198+
function init_expandables() {
199+
var elts = document.getElementsByClassName("expandable");
200+
for (var i = 0; i < elts.length; i++) {
201+
var el = elts[i];
202+
var clickable = el.getElementsByTagName("span")[0];
203+
var marker = clickable.getElementsByClassName("marker")[0];
204+
var content = el.getElementsByClassName("content")[0];
205+
var width = clickable.clientWidth - parseInt(getStyle(content, "padding-left")) - parseInt(getStyle(content, "padding-right"));
206+
content.style.width = width + "px";
207+
clickable.expandable_content = content;
208+
clickable.expandable_marker = marker;
209+
clickable.onclick = toggle;
210+
}
211+
}
212+
</script>
159213
</head>
160-
<body>
214+
<body onload="init_expandables()">
161215
<div id="header">
162216
<h1>Cppcheck report - %s: %s </h1>
163217
</div>
@@ -189,6 +243,18 @@ HTML_FOOTER = """
189243
HTML_ERROR = "<span class='error2'>&lt;--- %s</span>\n"
190244
HTML_INCONCLUSIVE = "<span class='inconclusive2'>&lt;--- %s</span>\n"
191245

246+
HTML_EXPANDABLE_ERROR = "<div class='verbose expandable'><span class='error2'>&lt;--- %s <span class='marker'>[+]</span></span><div class='content'>%s</div></div>\n"""
247+
HTML_EXPANDABLE_INCONCLUSIVE = "<div class='verbose expandable'><span class='inconclusive2'>&lt;--- %s <span class='marker'>[+]</span></span><div class='content'>%s</div></div>\n"""
248+
249+
# escape() and unescape() takes care of &, < and >.
250+
html_escape_table = {
251+
'"': "&quot;",
252+
"'": "&apos;"
253+
}
254+
html_unescape_table = {v:k for k, v in html_escape_table.items()}
255+
256+
def html_escape(text):
257+
return escape(text, html_escape_table)
192258

193259
class AnnotateCodeFormatter(HtmlFormatter):
194260
errors = []
@@ -203,9 +269,17 @@ class AnnotateCodeFormatter(HtmlFormatter):
203269
if error['line'] == line_no:
204270
try:
205271
if error['inconclusive'] == 'true':
206-
t = t.replace('\n', HTML_INCONCLUSIVE % error['msg'])
272+
if error.get('verbose'):
273+
index = t.rfind('\n')
274+
t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index+1:]
275+
else:
276+
t = t.replace('\n', HTML_INCONCLUSIVE % error['msg'])
207277
except KeyError:
208-
t = t.replace('\n', HTML_ERROR % error['msg'])
278+
if error.get('verbose'):
279+
index = t.rfind('\n')
280+
t = t[:index] + HTML_EXPANDABLE_ERROR % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index+1:]
281+
else:
282+
t = t.replace('\n', HTML_ERROR % error['msg'])
209283

210284
line_no = line_no + 1
211285
yield i, t
@@ -253,6 +327,7 @@ class CppCheckHandler(XmlContentHandler):
253327
'id': attributes['id'],
254328
'severity': attributes['severity'],
255329
'msg': attributes['msg'],
330+
'verbose': attributes.get('verbose'),
256331
'inconclusive': attributes['inconclusive']
257332
})
258333
except KeyError:
@@ -261,14 +336,14 @@ class CppCheckHandler(XmlContentHandler):
261336
'line': 0,
262337
'id': attributes['id'],
263338
'severity': attributes['severity'],
264-
'msg': attributes['msg']
339+
'msg': attributes['msg'],
340+
'verbose': attributes.get('verbose')
265341
})
266342
elif name == 'location':
267343
assert self.errors
268344
self.errors[-1]['file'] = attributes['file']
269345
self.errors[-1]['line'] = int(attributes['line'])
270346

271-
272347
if __name__ == '__main__':
273348
# Configure all the options this little utility is using.
274349
parser = optparse.OptionParser()
@@ -374,6 +449,7 @@ if __name__ == '__main__':
374449
lineanchors='line',
375450
encoding=options.source_encoding)
376451
htmlFormatter.errors = errors
452+
377453
with io.open(os.path.join(options.report_dir, htmlfile),
378454
'w') as output_file:
379455
output_file.write(HTML_HEAD %

0 commit comments

Comments
 (0)