1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
# -*- coding: utf-8 -*-
"""
lodgeit.lib.diff
~~~~~~~~~~~~~~~~
Render a nice diff between two things.
:copyright: 2007 by Armin Ronacher.
:license: BSD
"""
from difflib import SequenceMatcher
from klaus.utils import escape_html as e
def highlight_line(old_line, new_line):
"""Highlight inline changes in both lines."""
start = 0
limit = min(len(old_line), len(new_line))
while start < limit and old_line[start] == new_line[start]:
start += 1
end = -1
limit -= start
while -end <= limit and old_line[end] == new_line[end]:
end -= 1
end += 1
if start or end:
def do(l, tag):
last = end + len(l)
return b"".join(
[l[:start], b"<", tag, b">", l[start:last], b"</", tag, b">", l[last:]]
)
old_line = do(old_line, b"del")
new_line = do(new_line, b"ins")
return old_line, new_line
def render_diff(a, b, n=3):
"""Parse the diff an return data for the template."""
actions = []
chunks = []
for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n):
old_line, old_end, new_line, new_end = (
group[0][1],
group[-1][2],
group[0][3],
group[-1][4],
)
lines = []
def add_line(old_lineno, new_lineno, action, line):
actions.append(action)
lines.append(
{
"old_lineno": old_lineno,
"new_lineno": new_lineno,
"action": action,
"line": line,
"no_newline": not line.endswith(b"\n"),
}
)
chunks.append(lines)
for tag, i1, i2, j1, j2 in group:
if tag == "equal":
for c, line in enumerate(a[i1:i2]):
add_line(i1 + c, j1 + c, "unmod", e(line))
elif tag == "insert":
for c, line in enumerate(b[j1:j2]):
add_line(None, j1 + c, "add", e(line))
elif tag == "delete":
for c, line in enumerate(a[i1:i2]):
add_line(i1 + c, None, "del", e(line))
elif tag == "replace":
for c, line in enumerate(a[i1:i2]):
add_line(i1 + c, None, "del", e(line))
for c, line in enumerate(b[j1:j2]):
add_line(None, j1 + c, "add", e(line))
else:
raise AssertionError("unknown tag %s" % tag)
return actions.count("add"), actions.count("del"), chunks
|