diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2009-09-23 11:00:53 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2009-09-23 11:00:53 -0400 |
commit | e3ff72a32401f97b4e3f8e627780c87366d62414 (patch) | |
tree | 9b965e865b7f3da28933dae40bd73a7b539a39c4 | |
parent | 0b96d1ba8b0fb03628a57e255b39243c66751038 (diff) | |
download | python-coveragepy-git-e3ff72a32401f97b4e3f8e627780c87366d62414.tar.gz |
Syntax coloring in the HTML reports.
-rw-r--r-- | CHANGES.txt | 2 | ||||
-rw-r--r-- | TODO.txt | 6 | ||||
-rw-r--r-- | coverage/html.py | 106 | ||||
-rw-r--r-- | coverage/htmlfiles/pyfile.html | 2 | ||||
-rw-r--r-- | coverage/htmlfiles/style.css | 14 | ||||
-rw-r--r-- | test/farm/html/gold_a/a.html | 16 | ||||
-rw-r--r-- | test/farm/html/gold_a/index.html | 2 | ||||
-rw-r--r-- | test/farm/html/run_a.py | 4 | ||||
-rw-r--r-- | test/farm/html/run_tabbed.py | 6 |
9 files changed, 117 insertions, 41 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index f59e4e77..0d8186ce 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,8 @@ Version 3.1, unreleased for DecoratorTools (including TurboGears) projects. Fixed `issue 12` and
`issue 13`.
+- HTML reports now include syntax-colored Python source.
+
- Programs that change directory will still write .coverage files in the
directory where execution started. Fixed `issue 24`_.
@@ -71,7 +71,7 @@ x Tricky swapping of collector like figleaf, pycov, et al. (Don't need to do + HTML report
- Package navigation.
- Rolled-up statistics.
- - Syntax coloring in HTML report.
+ + Syntax coloring in HTML report.
+ Dynamic effects in HTML report.
+ Footer in reports pointing to coverage home page.
+ Baseline grid for linenumber font.
@@ -183,8 +183,8 @@ x Tests about the "import __main__" in cmdline.py - Generate new sample_html to get the latest, incl footer version number:
cd C:\ned\cog\trunk
rmdir/s/q htmlcov
- coverage -e -x cogapp\test_cogapp.py CogTestsInMemory
- coverage -b -i -d htmlcov
+ coverage run cogapp\test_cogapp.py CogTestsInMemory
+ coverage html -i -d htmlcov
copy htmlcov\*.* C:\ned\coverage\trunk\doc\sample_html
- Build and publish docs:
$ make px publish
diff --git a/coverage/html.py b/coverage/html.py index c0984084..e05b0066 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -1,7 +1,8 @@ """HTML reporting for Coverage.""" -import os, re, shutil +import keyword, os, re, token, tokenize, shutil from coverage import __version__ # pylint: disable-msg=W0611 +from coverage.backward import StringIO # pylint: disable-msg=W0622 from coverage.report import Reporter from coverage.templite import Templite @@ -18,6 +19,33 @@ def data(fname): return open(data_filename(fname)).read() +def phys_tokens(toks): + """Return all physical tokens, even line continuations. + + tokenize.generate_tokens() doesn't return a token for the backslash that + continues lines. This wrapper provides those tokens so that we can + re-create a faithful representation of the original source. + + Returns the same values as generate_tokens() + + """ + last_line = None + last_lineno = -1 + for toktype, ttext, (slineno, scol), (elineno, ecol), ltext in toks: + if last_lineno != elineno: + if last_line and last_line[-2:] == "\\\n": + if toktype != token.STRING: + ccol = len(last_line.split("\n")[-2]) - 1 + yield ( + 99999, "\\\n", + (slineno, ccol), (slineno, ccol+2), + last_line + ) + last_line = ltext + yield toktype, ttext, (slineno, scol), (elineno, ecol), ltext + last_lineno = elineno + + class HtmlReporter(Reporter): """HTML reporting.""" @@ -57,10 +85,9 @@ class HtmlReporter(Reporter): def html_file(self, cu, statements, excluded, missing): """Generate an HTML file for one source file.""" - source = cu.source_file() - source_lines = source.readlines() + source = cu.source_file().read().expandtabs(4) + source_lines = source.split("\n") - n_lin = len(source_lines) n_stm = len(statements) n_exc = len(excluded) n_mis = len(missing) @@ -75,26 +102,57 @@ class HtmlReporter(Reporter): c_exc = " exc" c_mis = " mis" + ws_tokens = [token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL] lines = [] - for lineno, line in enumerate(source_lines): - lineno += 1 # enumerate is 0-based, lines are 1-based. - - css_class = "" - if lineno in statements: - css_class += " stm" - if lineno not in missing and lineno not in excluded: - css_class += c_run - if lineno in excluded: - css_class += c_exc - if lineno in missing: - css_class += c_mis - - lineinfo = { - 'text': line, - 'number': lineno, - 'class': css_class.strip() or "pln" - } - lines.append(lineinfo) + line = [] + lineno = 1 + col = 0 + tokgen = tokenize.generate_tokens(StringIO(source).readline) + for toktype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen): + mark_start = True + for part in re.split('(\n)', ttext): + if part == '\n': + + line_class = "" + if lineno in statements: + line_class += " stm" + if lineno not in missing and lineno not in excluded: + line_class += c_run + if lineno in excluded: + line_class += c_exc + if lineno in missing: + line_class += c_mis + + lineinfo = { + 'html': "".join(line), + 'number': lineno, + 'class': line_class.strip() or "pln" + } + lines.append(lineinfo) + + line = [] + lineno += 1 + col = 0 + mark_end = False + elif part == '': + mark_end = False + elif toktype in ws_tokens: + mark_end = False + else: + if mark_start and scol > col: + line.append(escape(" " * (scol - col))) + mark_start = False + css_class = tokenize.tok_name.get(toktype, 'xx').lower()[:3] + if toktype == token.NAME and keyword.iskeyword(ttext): + css_class = "key" + tok_html = escape(part) or ' ' + line.append( + "<span class='%s'>%s</span>" % (css_class, tok_html) + ) + mark_end = True + scol = 0 + if mark_end: + col = ecol # Write the HTML page for this file. html_filename = cu.flat_rootname() + ".html" @@ -139,8 +197,6 @@ class HtmlReporter(Reporter): def escape(t): """HTML-escape the text in t.""" return (t - # Change all tabs to 4 spaces. - .expandtabs(4) # Convert HTML special chars into HTML entities. .replace("&", "&").replace("<", "<").replace(">", ">") .replace("'", "'").replace('"', """) diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html index d1810fa1..a7cd1d3c 100644 --- a/coverage/htmlfiles/pyfile.html +++ b/coverage/htmlfiles/pyfile.html @@ -43,7 +43,7 @@ function toggle_lines(btn, cls) { </td>
<td class='text' valign='top'>
{% for line in lines %}
- <p class='{{line.class}}'>{{line.text.rstrip|escape|not_empty}}</p>
+ <p class='{{line.class}}'>{{line.html}}<span class="strut"> </span></p>
{% endfor %}
</td>
</tr>
diff --git a/coverage/htmlfiles/style.css b/coverage/htmlfiles/style.css index d9d324c4..ef57755b 100644 --- a/coverage/htmlfiles/style.css +++ b/coverage/htmlfiles/style.css @@ -124,6 +124,20 @@ td.text { background: inherit; } +/* Syntax coloring */ +.text .com { + color: green; + font-style: italic; + line-height: 1px; + } +.text .key { + font-weight: bold; + line-height: 1px; + } +.text .str { + color: #000080; + } + /* index styles */ #index td, #index th { text-align: right; diff --git a/test/farm/html/gold_a/a.html b/test/farm/html/gold_a/a.html index 6cc0b908..b29831b6 100644 --- a/test/farm/html/gold_a/a.html +++ b/test/farm/html/gold_a/a.html @@ -21,7 +21,7 @@ function toggle_lines(btn, cls) { <body>
<div id='header'>
<div class='content'>
- <h1>Coverage for <b>c:\ned\coverage\trunk\test\farm\html\src\a.py</b>:
+ <h1>Coverage for <b>a</b> :
<span class='pc_cov'>67%</span>
</h1>
<h2 class='stats'>
@@ -47,13 +47,13 @@ function toggle_lines(btn, cls) { </td>
<td class='text' valign='top'>
-<p class='pln'># A test file for HTML reporting by coverage.</p>
-<p class='pln'> </p>
-<p class='stm run hide'>if 1 < 2:</p>
-<p class='pln'> # Needed a < to look at HTML entities.</p>
-<p class='stm run hide'> a = 3</p>
-<p class='pln'>else:</p>
-<p class='stm mis'> a = 4</p>
+<p class='pln'><span class='com'># A test file for HTML reporting by coverage.</span><span class="strut"> </span></p>
+<p class='pln'><span class="strut"> </span></p>
+<p class='stm run hide'><span class='key'>if</span> <span class='num'>1</span> <span class='op'><</span> <span class='num'>2</span><span class='op'>:</span><span class="strut"> </span></p>
+<p class='pln'> <span class='com'># Needed a < to look at HTML entities.</span><span class="strut"> </span></p>
+<p class='stm run hide'> <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span><span class="strut"> </span></p>
+<p class='pln'><span class='key'>else</span><span class='op'>:</span><span class="strut"> </span></p>
+<p class='stm mis'> <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>4</span><span class="strut"> </span></p>
</td>
</tr>
diff --git a/test/farm/html/gold_a/index.html b/test/farm/html/gold_a/index.html index 18b8af5b..c8a0ecc7 100644 --- a/test/farm/html/gold_a/index.html +++ b/test/farm/html/gold_a/index.html @@ -45,7 +45,7 @@ <div id='footer'>
<div class='content'>
<p>
- <a class='nav' href='http://bitbucket.org/ned/coveragepy/'>coverage.py v3.0b2</a>
+ <a class='nav' href='http://nedbatchelder.com/code/coverage'>coverage v3.1b1</a>
</p>
</div>
</div>
diff --git a/test/farm/html/run_a.py b/test/farm/html/run_a.py index ab0da42e..4a8a0514 100644 --- a/test/farm/html/run_a.py +++ b/test/farm/html/run_a.py @@ -13,8 +13,8 @@ runfunc(html_it, rundir="src") # and check that certain key strings are in the output. compare("html", "gold_a", size_within=10) contains("html/a.html", - ">if 1 < 2:<", - " a = 3", + "<span class='key'>if</span> <span class='num'>1</span> <span class='op'><</span> <span class='num'>2</span>", + " <span class='nam'>a</span> <span class='op'>=</span> <span class='num'>3</span>", "<span class='pc_cov'>67%</span>" ) contains("html/index.html", diff --git a/test/farm/html/run_tabbed.py b/test/farm/html/run_tabbed.py index c49bdfbd..88ffcaef 100644 --- a/test/farm/html/run_tabbed.py +++ b/test/farm/html/run_tabbed.py @@ -13,6 +13,10 @@ runfunc(html_it, rundir="src") contains("src/tabbed.py", "\tif x:\t\t\t\t\t\t# look nice") contains("html/tabbed.html", - "<p class='stm run hide'> if x: # look nice</p>") + "> <span class='key'>if</span> <span class='nam'>x</span>" + "<span class='op'>:</span> " + " <span class='com'># look nice</span>" + ) + doesnt_contain("html/tabbed.html", "\t") clean("html") |