diff options
-rw-r--r-- | coverage/html.py | 61 | ||||
-rw-r--r-- | coverage/htmlfiles/coverage_html.js | 41 | ||||
-rw-r--r-- | coverage/htmlfiles/index.html | 17 | ||||
-rw-r--r-- | coverage/htmlfiles/pyfile.html | 22 |
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> change column sorting </p> + <p class="keyhelp"> + <kbd>[</kbd> + <kbd>]</kbd> + prev/next file + </p> + <p class="keyhelp"> + <kbd>?</kbd> 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> (one) first highlighted chunk </p> + <p class="keyhelp"> + <kbd>[</kbd> + <kbd>]</kbd> + prev/next file + </p> + <p class="keyhelp"> + <kbd>u</kbd> back to the index + </p> + <p class="keyhelp"> + <kbd>?</kbd> 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">« index</a> <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>, + <a id="prevFileLink" class="nav" href="{{ prev_html }}">« prev file</a> + <a id="indexLink" class="nav" href="index.html">^ index</a> + <a id="nextFileLink" class="nav" href="{{ next_html }}">» next file</a> + </p> + <p> + <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>, created at {{ time_stamp }} </p> </div> |