summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/html.py61
-rw-r--r--coverage/htmlfiles/coverage_html.js41
-rw-r--r--coverage/htmlfiles/index.html17
-rw-r--r--coverage/htmlfiles/pyfile.html22
4 files changed, 128 insertions, 13 deletions
diff --git a/coverage/html.py b/coverage/html.py
index e18a30a1..953b2db3 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -165,6 +165,8 @@ class HtmlReporter:
self.datagen = HtmlDataGeneration(self.coverage)
self.totals = Numbers(precision=self.config.precision)
self.directory_was_empty = False
+ self.first_fr = None
+ self.final_fr = None
self.template_globals = {
# Functions available in the templates.
@@ -204,9 +206,29 @@ class HtmlReporter:
self.incr.read()
self.incr.check_global_data(self.config, self.pyfile_html_source)
- # Process all the files.
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.html_file(fr, analysis)
+ # Process all the files. For each page we need to supply a link
+ # to the next page. Therefore in each iteration of the loop we
+ # work on the fr and analysis from the previous iteration. We
+ # also need a link to the preceding page (i.e. 2 before the
+ # current iteration).
+ analysis_to_report = get_analysis_to_report(self.coverage, morfs)
+ pluprev_fr, prev_fr = None, None
+ prev_analysis = None
+
+ for fr, analysis in analysis_to_report:
+ if prev_fr is not None:
+ self.html_file(prev_fr, prev_analysis, pluprev_fr, fr)
+ else:
+ # This is the first file processed
+ self.first_fr = fr
+ pluprev_fr, prev_fr, prev_analysis = prev_fr, fr, analysis
+
+ # One more iteration for the final file. (Or not, if there are
+ # no files at all.)
+ if prev_fr is not None:
+ self.html_file(prev_fr, prev_analysis, pluprev_fr, None)
+ # This is the last file processed
+ self.final_fr = prev_fr
if not self.all_files_nums:
raise NoDataError("No data to report.")
@@ -236,10 +258,21 @@ class HtmlReporter:
if self.extra_css:
shutil.copyfile(self.config.extra_css, os.path.join(self.directory, self.extra_css))
- def html_file(self, fr, analysis):
- """Generate an HTML file for one source file."""
+ def html_file(self, fr, analysis, prev_fr=None, next_fr=None):
+ """Generate an HTML file for one source file, with links to the
+ previous and the next file, or to the index."""
rootname = flat_rootname(fr.relative_filename())
html_filename = rootname + ".html"
+ if prev_fr is not None:
+ prev_html = flat_rootname(prev_fr.relative_filename()) + ".html"
+ else:
+ prev_html = "index.html"
+
+ if next_fr is not None:
+ next_html = flat_rootname(next_fr.relative_filename()) + ".html"
+ else:
+ next_html = "index.html"
+
ensure_dir(self.directory)
if not os.listdir(self.directory):
self.directory_was_empty = True
@@ -316,7 +349,11 @@ class HtmlReporter:
css_classes.append(self.template_globals['category'][ldata.category])
ldata.css_class = ' '.join(css_classes) or "pln"
- html = self.source_tmpl.render(file_data.__dict__)
+ html = self.source_tmpl.render({
+ **file_data.__dict__,
+ 'prev_html': prev_html,
+ 'next_html': next_html,
+ })
write_html(html_path, html)
# Save this file's information for the index file.
@@ -340,11 +377,23 @@ class HtmlReporter:
n = self.skipped_empty_count
skipped_empty_msg = f"{n} empty file{plural(n)} skipped."
+ if self.first_fr is not None:
+ first_html = flat_rootname(self.first_fr.relative_filename()) + ".html"
+ else:
+ first_html = "index.html"
+
+ if self.final_fr is not None:
+ final_html = flat_rootname(self.final_fr.relative_filename()) + ".html"
+ else:
+ final_html = "index.html"
+
html = index_tmpl.render({
'files': self.file_summaries,
'totals': self.totals,
'skipped_covered_msg': skipped_covered_msg,
'skipped_empty_msg': skipped_empty_msg,
+ 'first_html': first_html,
+ 'final_html': final_html,
})
index_file = os.path.join(self.directory, "index.html")
diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js
index 00e18488..084a4970 100644
--- a/coverage/htmlfiles/coverage_html.js
+++ b/coverage/htmlfiles/coverage_html.js
@@ -25,6 +25,13 @@ function checkVisible(element) {
return !(rect.bottom < viewTop || rect.top >= viewBottom);
}
+function on_click(sel, fn) {
+ const elt = document.querySelector(sel);
+ if (elt) {
+ elt.addEventListener("click", fn);
+ }
+}
+
// Helpers for table sorting
function getCellValue(row, column = 0) {
const cell = row.cells[column]
@@ -193,6 +200,11 @@ coverage.index_ready = function () {
direction: th.getAttribute("aria-sort"),
}));
});
+
+ on_click(".button_prev_file", coverage.to_prev_file);
+ on_click(".button_next_file", coverage.to_next_file);
+
+ on_click(".button_show_hide_help", coverage.show_hide_help);
};
// -- pyfile stuff --
@@ -209,12 +221,6 @@ coverage.pyfile_ready = function () {
coverage.set_sel(0);
}
- const on_click = function(sel, fn) {
- const elt = document.querySelector(sel);
- if (elt) {
- elt.addEventListener("click", fn);
- }
- }
on_click(".button_toggle_run", coverage.toggle_lines);
on_click(".button_toggle_mis", coverage.toggle_lines);
on_click(".button_toggle_exc", coverage.toggle_lines);
@@ -225,6 +231,12 @@ coverage.pyfile_ready = function () {
on_click(".button_top_of_page", coverage.to_top);
on_click(".button_first_chunk", coverage.to_first_chunk);
+ on_click(".button_prev_file", coverage.to_prev_file);
+ on_click(".button_next_file", coverage.to_next_file);
+ on_click(".button_to_index", coverage.to_index);
+
+ on_click(".button_show_hide_help", coverage.show_hide_help);
+
coverage.filters = undefined;
try {
coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
@@ -299,6 +311,23 @@ coverage.to_first_chunk = function () {
coverage.to_next_chunk();
};
+coverage.to_prev_file = function () {
+ window.location = document.getElementById("prevFileLink").href;
+}
+
+coverage.to_next_file = function () {
+ window.location = document.getElementById("nextFileLink").href;
+}
+
+coverage.to_index = function () {
+ location.href = document.getElementById("indexLink").href;
+}
+
+coverage.show_hide_help = function () {
+ const helpCheck = document.getElementById("help_panel_state")
+ helpCheck.checked = !helpCheck.checked;
+}
+
// Return a string indicating what kind of chunk this line belongs to,
// or null if not a chunk.
coverage.chunk_indicator = function (line_elt) {
diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html
index e1d3e9b5..9f8e88e6 100644
--- a/coverage/htmlfiles/index.html
+++ b/coverage/htmlfiles/index.html
@@ -40,6 +40,14 @@
{% endif %}
<kbd>c</kbd> &nbsp; change column sorting
</p>
+ <p class="keyhelp">
+ <kbd>[</kbd>
+ <kbd>]</kbd>
+ &nbsp; prev/next file
+ </p>
+ <p class="keyhelp">
+ <kbd>?</kbd> &nbsp; show/hide this help
+ </p>
</div>
</div>
</div>
@@ -115,6 +123,15 @@
created at {{ time_stamp }}
</p>
</div>
+ <div style="display: none;">
+ <p>
+ <a id="prevFileLink" class="nav" href="{{ final_html }}">first file</a>
+ <a id="nextFileLink" class="nav" href="{{ first_html }}">final file</a>
+ </p>
+ <button type="button" class="button_prev_file" data-shortcut="[">Previous file</button>
+ <button type="button" class="button_next_file" data-shortcut="]">Next file</button>
+ <button type="button" class="button_show_hide_help" data-shortcut="?">Show/hide keyboard shortcuts</button>
+ </div>
</footer>
</body>
diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html
index fdad9d4e..7bd56d42 100644
--- a/coverage/htmlfiles/pyfile.html
+++ b/coverage/htmlfiles/pyfile.html
@@ -52,6 +52,17 @@
<p class="keyhelp">
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
+ <p class="keyhelp">
+ <kbd>[</kbd>
+ <kbd>]</kbd>
+ &nbsp; prev/next file
+ </p>
+ <p class="keyhelp">
+ <kbd>u</kbd> &nbsp; back to the index
+ </p>
+ <p class="keyhelp">
+ <kbd>?</kbd> &nbsp; show/hide this help
+ </p>
</div>
</div>
</div>
@@ -71,6 +82,10 @@
<button type="button" class="button_prev_chunk" data-shortcut="k">Previous highlighted chunk</button>
<button type="button" class="button_top_of_page" data-shortcut="0">Goto top of page</button>
<button type="button" class="button_first_chunk" data-shortcut="1">Goto first highlighted chunk</button>
+ <button type="button" class="button_prev_file" data-shortcut="[">Previous file</button>
+ <button type="button" class="button_next_file" data-shortcut="]">Next file</button>
+ <button type="button" class="button_to_index" data-shortcut="u">Back to the index</button>
+ <button type="button" class="button_show_hide_help" data-shortcut="?">Show/hide keyboard shortcuts</button>
</div>
</div>
</header>
@@ -110,7 +125,12 @@
<footer>
<div class="content">
<p>
- <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ <a id="prevFileLink" class="nav" href="{{ prev_html }}">&#xab; prev file</a> &nbsp; &nbsp;
+ <a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
+ <a id="nextFileLink" class="nav" href="{{ next_html }}">&#xbb; next file</a>
+ </p>
+ <p>
+ <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
created at {{ time_stamp }}
</p>
</div>