From ab6178f73929d1592fbf81ed39a9b93a8ca234c3 Mon Sep 17 00:00:00 2001 From: Samuel Degrande Date: Wed, 15 Oct 2014 21:49:22 +0200 Subject: [PATCH] HTML report: display 'verbose' message using clickable expandable divs --- htmlreport/cppcheck-htmlreport | 88 +++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/htmlreport/cppcheck-htmlreport b/htmlreport/cppcheck-htmlreport index f8b1a7e87..3e4072b6b 100755 --- a/htmlreport/cppcheck-htmlreport +++ b/htmlreport/cppcheck-htmlreport @@ -14,6 +14,7 @@ from pygments.formatters import HtmlFormatter from xml.sax import parse as xml_parse from xml.sax import SAXParseException as XmlParseException 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 @@ -56,6 +57,25 @@ h1 { display: inline-block; 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 { padding: 1px; } @@ -155,9 +175,43 @@ HTML_HEAD = """ + + - + @@ -189,6 +243,18 @@ HTML_FOOTER = """ HTML_ERROR = "<--- %s\n" HTML_INCONCLUSIVE = "<--- %s\n" +HTML_EXPANDABLE_ERROR = "\n""" +HTML_EXPANDABLE_INCONCLUSIVE = "\n""" + +# escape() and unescape() takes care of &, < and >. +html_escape_table = { + '"': """, + "'": "'" +} +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): errors = [] @@ -203,9 +269,17 @@ class AnnotateCodeFormatter(HtmlFormatter): if error['line'] == line_no: try: 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: - 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 yield i, t @@ -253,6 +327,7 @@ class CppCheckHandler(XmlContentHandler): 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], + 'verbose': attributes.get('verbose'), 'inconclusive': attributes['inconclusive'] }) except KeyError: @@ -261,14 +336,14 @@ class CppCheckHandler(XmlContentHandler): 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], - 'msg': attributes['msg'] + 'msg': attributes['msg'], + 'verbose': attributes.get('verbose') }) elif name == 'location': assert self.errors self.errors[-1]['file'] = attributes['file'] self.errors[-1]['line'] = int(attributes['line']) - if __name__ == '__main__': # Configure all the options this little utility is using. parser = optparse.OptionParser() @@ -374,6 +449,7 @@ if __name__ == '__main__': lineanchors='line', encoding=options.source_encoding) htmlFormatter.errors = errors + with io.open(os.path.join(options.report_dir, htmlfile), 'w') as output_file: output_file.write(HTML_HEAD %