Source code for better_diff.unified_plus

"""A unified+ format based on the standard difflib.unified_diff."""

import collections
import difflib
import typing


[docs]def format_diff(a: str, b: str, fromfile: str = "a", tofile: str = "b") -> str: """Return a unified+ diff between two strings. Args: a: The first string to compare. b: The second string to compare. fromfile: The name of the first file. tofile: The name of the second file. """ result: typing.List[str] = [] last_line: typing.Optional[str] = None normalized_endings_a, normalized_endings_b = (a.rstrip("\r\n"), b.rstrip("\r\n")) newline_difference_message = "\n\\ Newline at end of file" len_a_difference = len(a) - len(normalized_endings_a) len_b_difference = len(b) - len(normalized_endings_b) if len_a_difference != len_b_difference: normalized_endings_a += newline_difference_message * (len(a) - len(normalized_endings_a)) normalized_endings_b += newline_difference_message * (len(b) - len(normalized_endings_b)) dangling_whitespace_run: typing.Deque[str] = collections.deque() for line in difflib.unified_diff( a=normalized_endings_a.splitlines(), b=normalized_endings_b.splitlines(), fromfile=fromfile, tofile=tofile, ): if last_line and line: doing_a_substitution = last_line.startswith("-") and line.startswith("+") last_line_had_dangling_whitespace = last_line != last_line.rstrip() new_line_is_last_line_without_whitespace = last_line[1:].rstrip() == line[1:] if all( [ doing_a_substitution, last_line_had_dangling_whitespace, new_line_is_last_line_without_whitespace, ] ): _highlight_dangling_whitespace(result, last_line, line) elif dangling_whitespace_run and line.startswith("+"): if line[1:].rstrip() == dangling_whitespace_run[0][1:].rstrip(): old_line = dangling_whitespace_run.popleft() result.append(old_line) _highlight_dangling_whitespace(result, old_line, line) result.append(line) continue else: _dump_dangling_whitespace_run(result, dangling_whitespace_run) elif last_line_had_dangling_whitespace: we_may_be_continuing = line.startswith("-") if we_may_be_continuing: dangling_whitespace_run.append(line) continue # don't print this one yet else: _dump_dangling_whitespace_run(result, dangling_whitespace_run) result.append(line.rstrip()) last_line = line _dump_dangling_whitespace_run(result, dangling_whitespace_run) if not result: return "" return "\n".join(result) + "\n"
def _highlight_dangling_whitespace(result, last_line, line): """Insert a highlight line for dangling whitespace.""" highlight = "^" * (len(last_line) - len(last_line.rstrip())) result.append("?" + " " * (len(line) - 1) + highlight) def _dump_dangling_whitespace_run(result: list, dangling_whitespace_run: collections.deque) -> None: """Dump the dangling whitespace run to the result.""" while dangling_whitespace_run: old_line = dangling_whitespace_run.popleft() result.append(old_line)