HTML report: display 'verbose' message using clickable expandable divs

This commit is contained in:
Samuel Degrande 2014-10-15 21:49:22 +02:00 committed by PKEuS
parent 45a2986f34
commit ab6178f739
1 changed files with 82 additions and 6 deletions

View File

@ -14,6 +14,7 @@ from pygments.formatters import HtmlFormatter
from xml.sax import parse as xml_parse from xml.sax import parse as xml_parse
from xml.sax import SAXParseException as XmlParseException from xml.sax import SAXParseException as XmlParseException
from xml.sax.handler import ContentHandler as XmlContentHandler from xml.sax.handler import ContentHandler as XmlContentHandler
from xml.sax.saxutils import escape
""" """
Turns a cppcheck xml file into a browsable html report along Turns a cppcheck xml file into a browsable html report along
@ -56,6 +57,25 @@ h1 {
display: inline-block; display: inline-block;
margin-left: 4px; margin-left: 4px;
} }
div.verbose {
display: inline-block;
vertical-align: top;
cursor: help;
}
div.verbose div.content {
display: none;
position: absolute;
padding: 10px;
margin: 4px;
max-width: 40%;
white-space: pre-wrap;
border: 1px solid black;
background-color: #FFFFCC;
cursor: auto;
}
.highlight .hll { .highlight .hll {
padding: 1px; padding: 1px;
} }
@ -155,9 +175,43 @@ HTML_HEAD = """
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<style> <style>
%s %s
</style> </style>
<script language="javascript">
function getStyle(el,styleProp) {
if (el.currentStyle)
var y = el.currentStyle[styleProp];
else if (window.getComputedStyle)
var y = document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
return y;
}
function toggle() {
var el = this.expandable_content;
var mark = this.expandable_marker;
if (el.style.display == "block") {
el.style.display = "none";
mark.innerHTML = "[+]";
} else {
el.style.display = "block";
mark.innerHTML = "[-]";
}
}
function init_expandables() {
var elts = document.getElementsByClassName("expandable");
for (var i = 0; i < elts.length; i++) {
var el = elts[i];
var clickable = el.getElementsByTagName("span")[0];
var marker = clickable.getElementsByClassName("marker")[0];
var content = el.getElementsByClassName("content")[0];
var width = clickable.clientWidth - parseInt(getStyle(content, "padding-left")) - parseInt(getStyle(content, "padding-right"));
content.style.width = width + "px";
clickable.expandable_content = content;
clickable.expandable_marker = marker;
clickable.onclick = toggle;
}
}
</script>
</head> </head>
<body> <body onload="init_expandables()">
<div id="header"> <div id="header">
<h1>Cppcheck report - %s: %s </h1> <h1>Cppcheck report - %s: %s </h1>
</div> </div>
@ -189,6 +243,18 @@ HTML_FOOTER = """
HTML_ERROR = "<span class='error2'>&lt;--- %s</span>\n" HTML_ERROR = "<span class='error2'>&lt;--- %s</span>\n"
HTML_INCONCLUSIVE = "<span class='inconclusive2'>&lt;--- %s</span>\n" HTML_INCONCLUSIVE = "<span class='inconclusive2'>&lt;--- %s</span>\n"
HTML_EXPANDABLE_ERROR = "<div class='verbose expandable'><span class='error2'>&lt;--- %s <span class='marker'>[+]</span></span><div class='content'>%s</div></div>\n"""
HTML_EXPANDABLE_INCONCLUSIVE = "<div class='verbose expandable'><span class='inconclusive2'>&lt;--- %s <span class='marker'>[+]</span></span><div class='content'>%s</div></div>\n"""
# escape() and unescape() takes care of &, < and >.
html_escape_table = {
'"': "&quot;",
"'": "&apos;"
}
html_unescape_table = {v:k for k, v in html_escape_table.items()}
def html_escape(text):
return escape(text, html_escape_table)
class AnnotateCodeFormatter(HtmlFormatter): class AnnotateCodeFormatter(HtmlFormatter):
errors = [] errors = []
@ -203,9 +269,17 @@ class AnnotateCodeFormatter(HtmlFormatter):
if error['line'] == line_no: if error['line'] == line_no:
try: try:
if error['inconclusive'] == 'true': if error['inconclusive'] == 'true':
t = t.replace('\n', HTML_INCONCLUSIVE % error['msg']) if error.get('verbose'):
index = t.rfind('\n')
t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index+1:]
else:
t = t.replace('\n', HTML_INCONCLUSIVE % error['msg'])
except KeyError: except KeyError:
t = t.replace('\n', HTML_ERROR % error['msg']) if error.get('verbose'):
index = t.rfind('\n')
t = t[:index] + HTML_EXPANDABLE_ERROR % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index+1:]
else:
t = t.replace('\n', HTML_ERROR % error['msg'])
line_no = line_no + 1 line_no = line_no + 1
yield i, t yield i, t
@ -253,6 +327,7 @@ class CppCheckHandler(XmlContentHandler):
'id': attributes['id'], 'id': attributes['id'],
'severity': attributes['severity'], 'severity': attributes['severity'],
'msg': attributes['msg'], 'msg': attributes['msg'],
'verbose': attributes.get('verbose'),
'inconclusive': attributes['inconclusive'] 'inconclusive': attributes['inconclusive']
}) })
except KeyError: except KeyError:
@ -261,14 +336,14 @@ class CppCheckHandler(XmlContentHandler):
'line': 0, 'line': 0,
'id': attributes['id'], 'id': attributes['id'],
'severity': attributes['severity'], 'severity': attributes['severity'],
'msg': attributes['msg'] 'msg': attributes['msg'],
'verbose': attributes.get('verbose')
}) })
elif name == 'location': elif name == 'location':
assert self.errors assert self.errors
self.errors[-1]['file'] = attributes['file'] self.errors[-1]['file'] = attributes['file']
self.errors[-1]['line'] = int(attributes['line']) self.errors[-1]['line'] = int(attributes['line'])
if __name__ == '__main__': if __name__ == '__main__':
# Configure all the options this little utility is using. # Configure all the options this little utility is using.
parser = optparse.OptionParser() parser = optparse.OptionParser()
@ -374,6 +449,7 @@ if __name__ == '__main__':
lineanchors='line', lineanchors='line',
encoding=options.source_encoding) encoding=options.source_encoding)
htmlFormatter.errors = errors htmlFormatter.errors = errors
with io.open(os.path.join(options.report_dir, htmlfile), with io.open(os.path.join(options.report_dir, htmlfile),
'w') as output_file: 'w') as output_file:
output_file.write(HTML_HEAD % output_file.write(HTML_HEAD %