Commits (6)
......@@ -26,8 +26,7 @@ _diffoscope() {
'--max-report-size=[Maximum bytes of a report in a given format, across all of its pages. Note that some formats, such as --html, may be restricted by even smaller limits such as --max-page-size. (0 to disable, default: 41943040)]:' \
'--max-diff-block-lines=[Maximum number of lines output per unified-diff block, across all pages. (0 to disable, default: 1024)]:' \
'--max-page-size=[Maximum bytes of the top-level (--html-dir) or sole (--html) page. (default: %(default)s, remains in effect even with --no-default-limits)]:' \
'--max-page-size-child=[In --html-dir output, this is the maximum bytes of each child page (default: %(default)s, remains in effect even with --no-default-limits)]:' \
'--max-page-diff-block-lines=[Maximum number of lines output per unified-diff block on the top-level (--html-dir) or sole (--html) page, before spilling it into child pages (--html-dir) or skipping the rest of the diff block. Child pages are limited instead by --max-page-size-child. (default: %(default)s, remains in effect even with --no-default-limits)]:' \
'--max-page-diff-block-lines=[Maximum number of lines output per unified-diff block on the top-level (--html-dir) or sole (--html) page, before spilling it into a child page (--html-dir) or skipping the rest of the diff block. (default: %(default)s, remains in effect even with --no-default-limits)]:' \
'--new-file[Treat absent files as empty]' \
'--exclude=[Exclude files whose names (including any directory part) match %(metavar)s. Use this option to ignore files based on their names.]:' \
'--exclude-command=[Exclude commands that match %(metavar)s. For example "^readelf.*\s--debug-dump=info" can take a long time and differences here are likely secondary differences caused by something represented elsewhere. Use this option to disable commands that use a lot of resources.]:' \
......
......@@ -50,7 +50,6 @@ class Config:
# structural limits, restricts single-file formats
# semi-restricts multi-file formats
self.max_page_size = defaultint(40 * 2 ** 20) # 4 MB
self.max_page_size_child = defaultint(200 * 2 ** 10) # 200 kB
self.max_page_diff_block_lines = defaultint(2 ** 7) # 128 lines
self.max_text_report_size = 0
......@@ -94,4 +93,3 @@ class Config:
def check_constraints(self):
self.check_ge("max_diff_block_lines", "max_page_diff_block_lines")
self.check_ge("max_report_size", "max_page_size")
self.check_ge("max_report_size", "max_page_size_child")
......@@ -256,24 +256,14 @@ def create_parser():
"even with --no-default-limits)",
default=Config().max_page_size,
).completer = RangeCompleter(Config().max_page_size)
group2.add_argument(
"--max-page-size-child",
metavar="BYTES",
type=int,
help="In --html-dir output, this is the maximum bytes of "
"each child page (default: %(default)s, remains in "
"effect even with --no-default-limits)",
default=str(Config().max_page_size_child),
).completer = RangeCompleter(Config().max_page_size_child)
group2.add_argument(
"--max-page-diff-block-lines",
metavar="LINES",
type=int,
help="Maximum number of lines output per unified-diff block "
"on the top-level (--html-dir) or sole (--html) page, before "
"spilling it into child pages (--html-dir) or skipping the "
"rest of the diff block. Child pages are limited instead by "
"--max-page-size-child. (default: %(default)s, remains in "
"spilling it into a child page (--html-dir) or skipping the "
"rest of the diff block. (default: %(default)s, remains in "
"effect even with --no-default-limits)",
default=Config().max_page_diff_block_lines,
).completer = RangeCompleter(Config().max_page_diff_block_lines)
......@@ -608,7 +598,6 @@ def configure(parsed_args):
setattr(Config(), x, float("inf"))
Config().max_page_size = parsed_args.max_page_size
Config().max_page_size_child = parsed_args.max_page_size_child
Config().max_page_diff_block_lines = parsed_args.max_page_diff_block_lines
Config().difftool = parsed_args.difftool
......
......@@ -384,18 +384,23 @@ class HTMLSideBySidePresenter:
def __init__(self):
self.max_lines = Config().max_diff_block_lines # only for html-dir
self.max_lines_parent = Config().max_page_diff_block_lines
self.max_page_size_child = Config().max_page_size_child
def new_unified_diff(self):
self.spl_rows = 0
self.spl_current_page = 0
self.spl_print_func = None
self._spl_print_func = None
self.spl_print_ctrl = None
# the below apply to child pages only, the parent page limit works
# differently and is controlled by output_difference later below
self.bytes_max_total = 0
self.bytes_written = 0
self.error_row = None
self.write_memory = None
def spl_print_func(self, line):
if self.write_memory is not None:
self.write_memory += line
self._spl_print_func(line)
def output_hunk_header(self, hunk_off1, hunk_size1, hunk_off2, hunk_size2):
self.spl_print_func('<tr class="diffhunk">')
......@@ -439,7 +444,7 @@ class HTMLSideBySidePresenter:
def spl_print_enter(self, print_context, rotation_params):
# Takes ownership of print_context
self.spl_print_ctrl = print_context.__exit__, rotation_params
self.spl_print_func = print_context.__enter__()
self._spl_print_func = print_context.__enter__()
ctx, _ = rotation_params
# Print file and table headers
self.spl_print_func(
......@@ -456,9 +461,10 @@ class HTMLSideBySidePresenter:
def spl_print_exit(self, *exc_info):
if not self.spl_had_entered_child():
return False
self.spl_print_func(templates.EXPANDED_UD_FOOTER)
self.spl_print_func(output_footer())
_exit, _ = self.spl_print_ctrl
self.spl_print_func = None
self._spl_print_func = None
self.spl_print_ctrl = None
return _exit(*exc_info)
......@@ -486,12 +492,7 @@ class HTMLSideBySidePresenter:
and self.bytes_written > self.bytes_max_total
):
raise PrintLimitReached()
if self.spl_print_func.bytes_written < self.max_page_size_child:
return False
logger.debug(
"new unified-diff subpage, previous subpage went over %s bytes",
self.max_page_size_child,
)
return False
return True
......@@ -499,21 +500,19 @@ class HTMLSideBySidePresenter:
_, rotation_params = self.spl_print_ctrl
ctx, mainname = rotation_params
self.spl_current_page += 1
filename = "%s-%s.html" % (mainname, self.spl_current_page)
if self.spl_current_page > 1:
# previous page was a child, close it
self.spl_print_func(
templates.UD_TABLE_FOOTER
% {"filename": html.escape(filename), "text": "load diff"}
)
self.spl_print_func("</table>\n")
self.spl_print_exit(None, None, None)
filename = "%s.html" % (mainname)
# rotate to the next child page
memory = self.write_memory
context = spl_file_printer(ctx.directory, filename, self)
self.spl_print_enter(context, rotation_params)
# Make sure child pages are embedded in a "difference" div so the
# css still works
self.spl_print_func(templates.EXPANDED_UD_HEADER)
self.spl_print_func(templates.UD_TABLE_HEADER)
self.spl_print_func(memory)
self.write_memory = None
def output_limit_reached(self, limit_type, total, bytes_processed):
logger.debug("%s print limit reached", limit_type)
......@@ -534,6 +533,9 @@ class HTMLSideBySidePresenter:
Yields None for each extra child page, and then True or False depending
on whether the whole output was truncated.
"""
# We need to memorize what is written in case a new page is created,
# which will have to host parts of the previous content
self.write_memory = ""
try:
ydiff = SideBySideDiff(unified_diff)
for t, args in ydiff.items():
......@@ -570,6 +572,7 @@ class HTMLSideBySidePresenter:
finally:
# no footer on the last page, just a close tag
self.spl_print_func("</table>")
self.write_memory = None
yield wrote_all
def output_unified_diff(self, ctx, unified_diff, has_internal_linenos):
......@@ -582,7 +585,7 @@ class HTMLSideBySidePresenter:
try:
udiff = io.StringIO()
udiff.write(templates.UD_TABLE_HEADER)
self.spl_print_func = udiff.write
self._spl_print_func = udiff.write
self.spl_print_ctrl = None, rotation_params
it = self.output_unified_diff_table(
......@@ -591,8 +594,9 @@ class HTMLSideBySidePresenter:
wrote_all = next(it)
if wrote_all is None:
assert self.spl_current_page == 1
# now pause the iteration and wait for consumer to give us a
# size-limit to write the remaining pages with
# size-limit to write the remaining page with
# exhaust the iterator and save the last item in wrote_all
new_limit = yield PartialString(
PartialString.escape(udiff.getvalue()) + "{0}</table>\n",
......@@ -618,7 +622,7 @@ class HTMLSideBySidePresenter:
self.spl_print_exit(None, None, None)
finally:
self.spl_print_ctrl = None
self.spl_print_func = None
self._spl_print_func = None
truncated = not wrote_all
child_rows_written = self.spl_rows - self.max_lines_parent
......@@ -627,14 +631,11 @@ class HTMLSideBySidePresenter:
# on the parent page
parent_last_row = self.error_row
else:
noun = "pieces" if self.spl_current_page > 1 else "piece"
text = "load diff (%s %s%s)" % (
self.spl_current_page,
noun,
(", truncated" if truncated else ""),
)
text = "Open expanded diff"
if truncated:
text += " (truncated)"
parent_last_row = templates.UD_TABLE_FOOTER % {
"filename": html.escape("%s-1.html" % mainname),
"filename": html.escape("%s.html" % mainname),
"text": text,
}
yield self.bytes_written, parent_last_row
......@@ -693,7 +694,6 @@ class HTMLPresenter(Presenter):
return templates.DIFFNODE_LAZY_LOAD % {
"pagename": pagename,
"pagesize": sizeof_fmt(Config().max_page_size_child),
"size": sizeof_fmt(size),
}
......@@ -735,7 +735,7 @@ class HTMLPresenter(Presenter):
page_limit = (
Config().max_page_size
if ancestor is root_difference
else Config().max_page_size_child
else None
)
page_current = outputs[ancestor].size(placeholder_len)
report_current = self.report_printed + sum(
......@@ -752,6 +752,8 @@ class HTMLPresenter(Presenter):
)
if report_current + want_to_add > self.report_limit:
make_new_subpage = False
elif ancestor is not root_difference:
add_to_existing = True
elif page_current + want_to_add < page_limit:
add_to_existing = True
else:
......
......@@ -143,38 +143,20 @@ STYLES = """body.diffoscope {
.diffoscope .diffsize {
float: right;
}
.diffoscope table.diff tr.ondemand td, .diffoscope div.ondemand-details {
background: #f99;
text-align: center;
padding: 0.5em 0;
}
.diffoscope table.diff tr.ondemand:hover td, .diffoscope div.ondemand-details:hover {
background: #faa;
cursor: pointer;
}
"""
SCRIPTS = r"""<script src="%(jquery_url)s"></script>
<script type="text/javascript">
$(function() {
// activate "loading" controls
var load_cont, load_generic = function(selector, target, getInfo, postLoad) {
return function() {
var a = $(this).find("a");
var filename = a.attr('href');
var info = getInfo ? getInfo(a) : null;
var button = a.parent();
button.text('... loading ...');
(target ? target(button) : button).load(filename + " " + selector, function() {
// https://stackoverflow.com/a/8452751/946226
var elems = $(this).children(':first').unwrap();
// set this behaviour for the next link too
var td = elems.parent().find(".ondemand td");
td.on('click', load_cont);
postLoad ? postLoad(td, info) : null;
});
return false;
};
};
load_cont = load_generic("tr", function(x) { return x.parent(); }, function(a) {
var textparts = /^(.*)\((\d+) pieces?(.*)\)$/.exec(a.text());
var numleft = Number.parseInt(textparts[2]) - 1;
var noun = numleft == 1 ? "piece" : "pieces";
return textparts[1] + "(" + numleft + " " + noun + textparts[3] + ")";
}, function(td, info) { td.find("a").text(info); });
$(".ondemand td").on('click', load_cont);
$(".ondemand-details").on('click', load_generic("div.difference > *"));
// activate [+]/[-] controls
var diffcontrols = $(".diffcontrol");
$(".diffheader").on('click', function(evt) {
......@@ -201,15 +183,6 @@ $(function() {
});
</script>
<style>
.diffoscope table.diff tr.ondemand td, .diffoscope div.ondemand-details {
background: #f99;
text-align: center;
padding: 0.5em 0;
}
.diffoscope table.diff tr.ondemand:hover td, .diffoscope div.ondemand-details:hover {
background: #faa;
cursor: pointer;
}
.diffoscope .diffheader {
cursor: pointer;
}
......@@ -223,9 +196,8 @@ $(function() {
</style>
"""
DIFFNODE_LAZY_LOAD = """<div class="ondemand-details" title="the size refers to the raw diff and includes all children;
only the top %(pagesize)s of the HTML are loaded at a time">... <a
href="%(pagename)s.html">load details (total %(size)s)</a> ...</div>
DIFFNODE_LAZY_LOAD = """<div class="ondemand-details" title="the size refers to the raw diff and includes all children">... <a
href="%(pagename)s.html" target="_blank">open details (total %(size)s)</a> ...</div>
"""
DIFFNODE_LIMIT = """<div class="error">Max HTML report size reached</div>
......@@ -238,10 +210,14 @@ UD_TABLE_HEADER = """<table class="diff">
"""
UD_TABLE_FOOTER = """<tr class="ondemand"><td colspan="4">
... <a href="%(filename)s">%(text)s</a> ...
... <a href="%(filename)s" target="_blank">%(text)s</a> ...
</td></tr>
"""
UD_TABLE_LIMIT_FOOTER = """<tr class="error"><td colspan="4">
Max %(limit_type)s reached; %(bytes_left)s/%(bytes_total)s bytes (%(percent).2f%%) of diff not shown.
</td></tr>"""
EXPANDED_UD_HEADER = """<div class="difference">"""
EXPANDED_UD_FOOTER = """</div>"""