Files
@ ffddbd80649e
Branch filter:
Location: kallithea/pylons_app/lib/differ.py - annotation
ffddbd80649e
7.3 KiB
text/x-python
Added differ lib from mercurial.
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e ffddbd80649e | # -*- coding: utf-8 -*-
# original copyright: 2007-2008 by Armin Ronacher
# licensed under the BSD license.
import re, difflib
def render_udiff(udiff, differ='udiff'):
"""Renders the udiff into multiple chunks of nice looking tables.
The return value is a list of those tables.
"""
return DiffProcessor(udiff, differ).prepare()
class DiffProcessor(object):
"""Give it a unified diff and it returns a list of the files that were
mentioned in the diff together with a dict of meta information that
can be used to render it in a HTML template.
"""
_chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
def __init__(self, udiff, differ):
"""
:param udiff: a text in udiff format
"""
if isinstance(udiff, basestring):
udiff = udiff.splitlines(1)
self.lines = map(self.escaper, udiff)
# Select a differ.
if differ == 'difflib':
self.differ = self._highlight_line_difflib
else:
self.differ = self._highlight_line_udiff
def escaper(self, string):
return string.replace('<', '<').replace('>', '>')
def _extract_rev(self, line1, line2):
"""Extract the filename and revision hint from a line."""
try:
if line1.startswith('--- ') and line2.startswith('+++ '):
filename, old_rev = line1[4:].split(None, 1)
new_rev = line2[4:].split(None, 1)[1]
return filename, 'old', 'new'
except (ValueError, IndexError):
pass
return None, None, None
def _highlight_line_difflib(self, line, next):
"""Highlight inline changes in both lines."""
if line['action'] == 'del':
old, new = line, next
else:
old, new = next, line
oldwords = re.split(r'(\W)', old['line'])
newwords = re.split(r'(\W)', new['line'])
sequence = difflib.SequenceMatcher(None, oldwords, newwords)
oldfragments, newfragments = [], []
for tag, i1, i2, j1, j2 in sequence.get_opcodes():
oldfrag = ''.join(oldwords[i1:i2])
newfrag = ''.join(newwords[j1:j2])
if tag != 'equal':
if oldfrag:
oldfrag = '<del>%s</del>' % oldfrag
if newfrag:
newfrag = '<ins>%s</ins>' % newfrag
oldfragments.append(oldfrag)
newfragments.append(newfrag)
old['line'] = "".join(oldfragments)
new['line'] = "".join(newfragments)
def _highlight_line_udiff(self, line, next):
"""Highlight inline changes in both lines."""
start = 0
limit = min(len(line['line']), len(next['line']))
while start < limit and line['line'][start] == next['line'][start]:
start += 1
end = -1
limit -= start
while - end <= limit and line['line'][end] == next['line'][end]:
end -= 1
end += 1
if start or end:
def do(l):
last = end + len(l['line'])
if l['action'] == 'add':
tag = 'ins'
else:
tag = 'del'
l['line'] = '%s<%s>%s</%s>%s' % (
l['line'][:start],
tag,
l['line'][start:last],
tag,
l['line'][last:]
)
do(line)
do(next)
def _parse_udiff(self):
"""Parse the diff an return data for the template."""
lineiter = iter(self.lines)
files = []
try:
line = lineiter.next()
while 1:
# continue until we found the old file
if not line.startswith('--- '):
line = lineiter.next()
continue
chunks = []
filename, old_rev, new_rev = \
self._extract_rev(line, lineiter.next())
files.append({
'filename': filename,
'old_revision': old_rev,
'new_revision': new_rev,
'chunks': chunks
})
line = lineiter.next()
while line:
match = self._chunk_re.match(line)
if not match:
break
lines = []
chunks.append(lines)
old_line, old_end, new_line, new_end = \
[int(x or 1) for x in match.groups()[:-1]]
old_line -= 1
new_line -= 1
context = match.groups()[-1]
old_end += old_line
new_end += new_line
if context:
lines.append({
'old_lineno': None,
'new_lineno': None,
'action': 'context',
'line': line,
})
line = lineiter.next()
while old_line < old_end or new_line < new_end:
if line:
command, line = line[0], line[1:]
else:
command = ' '
affects_old = affects_new = False
# ignore those if we don't expect them
if command in '#@':
continue
elif command == '+':
affects_new = True
action = 'add'
elif command == '-':
affects_old = True
action = 'del'
else:
affects_old = affects_new = True
action = 'unmod'
old_line += affects_old
new_line += affects_new
lines.append({
'old_lineno': affects_old and old_line or '',
'new_lineno': affects_new and new_line or '',
'action': action,
'line': line
})
line = lineiter.next()
except StopIteration:
pass
# highlight inline changes
for file in files:
for chunk in chunks:
lineiter = iter(chunk)
first = True
try:
while 1:
line = lineiter.next()
if line['action'] != 'unmod':
nextline = lineiter.next()
if nextline['action'] == 'unmod' or \
nextline['action'] == line['action']:
continue
self.differ(line, nextline)
except StopIteration:
pass
return files
def prepare(self):
"""Prepare the passed udiff for HTML rendering."""
return self._parse_udiff()
|