##// END OF EJS Templates
Fixed differ to properly extract filenames, and dates from diff file. and swaped order of columns with lines nr in diff html
Fixed differ to properly extract filenames, and dates from diff file. and swaped order of columns with lines nr in diff html

File last commit:

r152:0c00fbaf default
r152:0c00fbaf default
Show More
differ.py
216 lines | 7.5 KiB | text/x-python | PythonLexer
Added differ lib from mercurial.
r130 # -*- 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('<', '&lt;').replace('>', '&gt;')
def _extract_rev(self, line1, line2):
"""Extract the filename and revision hint from a line."""
try:
if line1.startswith('--- ') and line2.startswith('+++ '):
Fixed differ to properly extract filenames, and dates from diff file. and swaped order of columns with lines nr in diff html
r152 l1 = line1[4:].split(None, 1)
old_filename = l1[0] if len(l1) >= 1 else None
old_rev = l1[1] if len(l1) == 2 else 'old'
l2 = line1[4:].split(None, 1)
new_filename = l2[0] if len(l2) >= 1 else None
new_rev = l2[1] if len(l2) == 2 else 'new'
return old_filename, new_rev, old_rev
Added differ lib from mercurial.
r130 except (ValueError, IndexError):
pass
Fixed differ to properly extract filenames, and dates from diff file. and swaped order of columns with lines nr in diff html
r152
Added differ lib from mercurial.
r130 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()