def show_text_diff(stream, a, b): _show_diff_header(stream, (a, b), "--text") differ = difflib.Differ() lines_a = open(a, "r", encoding=default_encoding).readlines() lines_b = open(b, "r", encoding=default_encoding).readlines() with TermColor(stream) as tc: for d in differ.compare(lines_a, lines_b): # Someday add "?" highlighting. Trick is this should change color mid-line on the # previous (one or two) lines. (Google and see if somebody else solved this one already) # https://stackoverflow.com/questions/774316/python-difflib-highlighting-differences-inline tc.color(_diff_color_mapping.get(d[0], 0)) stream.write(d) tc.reset()
def _show_diff_header(stream, files, diff_line=None): headers = [] for f in files: if isinstance(f, DiffHeader): headers.append(f) else: headers.append(DiffHeader(f)) with TermColor(stream) as tc: tc.color(ANSI_YELLOW, ANSI_BOLD) if diff_line: stream.write("diff {} {} {}\n".format(diff_line, headers[0].name, headers[1].name)) tc.reset() stream.write("--- {0}\n".format(headers[0])) stream.write("+++ {0}\n".format(headers[1]))
def show_diff(stream, diffs, headers=None): tc = TermColor(stream) def is_multiline(v): if v and "\n" in v: return True else: return False def write_key(key, value, prefix_=" "): if is_multiline(value): write_multiline_key(key, value, prefix_) else: if key.startswith("#-"): template = "{0}{2}\n" else: template = "{0}{1} = {2}\n" stream.write(template.format(prefix_, key, value)) def write_multiline_key(key, value, prefix_=" "): with tc: lines = value.replace("\n", "\\\n").split("\n") tc.color(_diff_color_mapping.get(prefix_)) stream.write("{0}{1} = {2}\n".format(prefix_, key, lines.pop(0))) for line in lines: stream.write("{0}{1}\n".format(prefix_, line)) def show_value(value, stanza_, key, prefix_=""): with tc: tc.color(_diff_color_mapping.get(prefix_)) if isinstance(value, dict): if stanza_ is not GLOBAL_STANZA: stream.write("{0}[{1}]\n".format(prefix_, stanza_)) for x, y in sorted(six.iteritems(value)): write_key(x, y, prefix_) stream.write("\n") else: write_key(key, value, prefix_) def show_multiline_diff(value_a, value_b, key): def f(v): r = "{0} = {1}".format(key, v) r = r.replace("\n", "\\\n") return r.splitlines() a = f(value_a) b = f(value_b) differ = difflib.Differ() with tc: for d in differ.compare(a, b): # Someday add "?" highlighting. Trick is this should change color mid-line on the # previous (one or two) lines. (Google and see if somebody else solved this one already) # https://stackoverflow.com/questions/774316/python-difflib-highlighting-differences-inline tc.color(_diff_color_mapping.get(d[0], 0)) # Differences in how difflib returns bytes/unicode? if not isinstance(d, six.text_type): d = d.decode(default_encoding) stream.write(d) tc.reset() stream.write("\n") # Global result: no changes between files or no commonality between files if len(diffs) == 1 and isinstance(diffs[0].location, DiffGlobal): op = diffs[0] if op.tag == DIFF_OP_EQUAL: return EXIT_CODE_DIFF_EQUAL else: if headers: _show_diff_header(stream, headers, "--ksconf -global") # This is the only place where a gets '-' and b gets '+' for (prefix, data) in [("-", op.a), ("+", op.b)]: for (stanza, keys) in sorted(data.items()): show_value(keys, stanza, None, prefix) stream.flush() return EXIT_CODE_DIFF_NO_COMMON if headers: _show_diff_header(stream, headers, "--ksconf") last_stanza = None for op in diffs: if isinstance(op.location, DiffStanza): if op.tag in (DIFF_OP_DELETE, DIFF_OP_REPLACE): show_value(op.b, op.location.stanza, None, "-") if op.tag in (DIFF_OP_INSERT, DIFF_OP_REPLACE): show_value(op.a, op.location.stanza, None, "+") continue # pragma: no cover (peephole optimization) if op.location.stanza != last_stanza: if last_stanza is not None: # Line break after last stanza stream.write("\n") stream.flush() if op.location.stanza is not GLOBAL_STANZA: stream.write(" [{0}]\n".format(op.location.stanza)) last_stanza = op.location.stanza if op.tag == DIFF_OP_INSERT: show_value(op.a, op.location.stanza, op.location.key, "+") elif op.tag == DIFF_OP_DELETE: show_value(op.b, op.location.stanza, op.location.key, "-") elif op.tag == DIFF_OP_REPLACE: if is_multiline(op.a) or is_multiline(op.b): show_multiline_diff(op.a, op.b, op.location.key) else: show_value(op.b, op.location.stanza, op.location.key, "-") show_value(op.a, op.location.stanza, op.location.key, "+") elif op.tag == DIFF_OP_EQUAL: show_value(op.b, op.location.stanza, op.location.key, " ") stream.flush() return EXIT_CODE_DIFF_CHANGE