Refactor diff calculation to facilitate more context

Create groups of diff chunks and context chunks.  In a subsequent
change we can use that to better control the displayed context.

Change-Id: I75858d002e9a9fff809c91cfced81c6022d86196
This commit is contained in:
James E. Blair 2014-05-03 16:51:30 -07:00
parent 7a71108c60
commit a44a82bff3
2 changed files with 111 additions and 65 deletions

View File

@ -19,12 +19,74 @@ import re
import git import git
class DiffContextChunk(object):
context = True
def __init__(self):
self.oldlines = []
self.newlines = []
class DiffChangedChunk(object):
context = False
def __init__(self):
self.oldlines = []
self.newlines = []
class DiffFile(object): class DiffFile(object):
def __init__(self): def __init__(self):
self.newname = None self.newname = None
self.oldname = None self.oldname = None
self.oldlines = [] self.chunks = []
self.newlines = [] self.current_chunk = None
self.old_lineno = 0
self.new_lineno = 0
self.offset = 0
def finalize(self):
if not self.current_chunk:
return
self.current_chunk.lines = zip(self.current_chunk.oldlines,
self.current_chunk.newlines)
self.chunks.append(self.current_chunk)
self.current_chunk = None
def addDiffLines(self, old, new):
if (self.current_chunk and
not isinstance(self.current_chunk, DiffChangedChunk)):
self.finalize()
if not self.current_chunk:
self.current_chunk = DiffChangedChunk()
for l in old:
self.current_chunk.oldlines.append((self.old_lineno, '-', l))
self.old_lineno += 1
self.offset -= 1
for l in new:
self.current_chunk.newlines.append((self.new_lineno, '+', l))
self.new_lineno += 1
self.offset += 1
while self.offset > 0:
self.current_chunk.oldlines.append((None, '', ''))
self.offset -= 1
while self.offset < 0:
self.current_chunk.newlines.append((None, '', ''))
self.offset += 1
def addNewLine(self, line):
if (self.current_chunk and
not isinstance(self.current_chunk, DiffChangedChunk)):
self.finalize()
if not self.current_chunk:
self.current_chunk = DiffChangedChunk()
def addContextLine(self, line):
if (self.current_chunk and
not isinstance(self.current_chunk, DiffContextChunk)):
self.finalize()
if not self.current_chunk:
self.current_chunk = DiffContextChunk()
self.current_chunk.oldlines.append((self.old_lineno, ' ', line))
self.current_chunk.newlines.append((self.new_lineno, ' ', line))
self.old_lineno += 1
self.new_lineno += 1
class GitCheckoutError(Exception): class GitCheckoutError(Exception):
def __init__(self, msg): def __init__(self, msg):
@ -63,7 +125,8 @@ class Repo(object):
ret.append(x.split('\t')) ret.append(x.split('\t'))
return ret return ret
def intraline_diff(self, old, new): def intralineDiff(self, old, new):
# takes a list of old lines and a list of new lines
prevline = None prevline = None
prevstyle = None prevstyle = None
output_old = [] output_old = []
@ -138,15 +201,13 @@ class Repo(object):
newc = repo.commit(new) newc = repo.commit(new)
files = [] files = []
for diff_context in oldc.diff(newc, create_patch=True, U=context): for diff_context in oldc.diff(newc, create_patch=True, U=context):
# Each iteration of this is a file
f = DiffFile() f = DiffFile()
files.append(f) files.append(f)
if diff_context.rename_from: if diff_context.rename_from:
f.oldname = diff_context.rename_from f.oldname = diff_context.rename_from
if diff_context.rename_to: if diff_context.rename_to:
f.newname = diff_context.rename_to f.newname = diff_context.rename_to
old_lineno = 0
new_lineno = 0
offset = 0
oldchunk = [] oldchunk = []
newchunk = [] newchunk = []
prev_key = '' prev_key = ''
@ -167,8 +228,8 @@ class Repo(object):
#socket.sendall(line) #socket.sendall(line)
m = self.header_re.match(line) m = self.header_re.match(line)
#socket.sendall(str(m.groups())) #socket.sendall(str(m.groups()))
old_lineno = int(m.group(1)) f.old_lineno = int(m.group(1))
new_lineno = int(m.group(3)) f.new_lineno = int(m.group(3))
continue continue
if not line: if not line:
if prev_key != '\\': if prev_key != '\\':
@ -204,29 +265,14 @@ class Repo(object):
prev_key = '' prev_key = ''
# end of chunk # end of chunk
if oldchunk or newchunk: if oldchunk or newchunk:
oldchunk, newchunk = self.intraline_diff(oldchunk, newchunk) oldchunk, newchunk = self.intralineDiff(oldchunk, newchunk)
for l in oldchunk: f.addDiffLines(oldchunk, newchunk)
f.oldlines.append((old_lineno, '-', l))
old_lineno += 1
offset -= 1
for l in newchunk:
f.newlines.append((new_lineno, '+', l))
new_lineno += 1
offset += 1
oldchunk = [] oldchunk = []
newchunk = [] newchunk = []
while offset > 0:
f.oldlines.append((None, '', ''))
offset -= 1
while offset < 0:
f.newlines.append((None, '', ''))
offset += 1
if key == ' ': if key == ' ':
f.oldlines.append((old_lineno, ' ', rest)) f.addContextLine(rest)
f.newlines.append((new_lineno, ' ', rest))
old_lineno += 1
new_lineno += 1
continue continue
if not last_line: if not last_line:
raise Exception("Unhandled line: %s" % line) raise Exception("Unhandled line: %s" % line)
f.finalize()
return files return files

View File

@ -154,44 +154,44 @@ This Screen
lines.append(urwid.Columns([ lines.append(urwid.Columns([
urwid.Text(diff.oldname), urwid.Text(diff.oldname),
urwid.Text(diff.newname)])) urwid.Text(diff.newname)]))
for i, old in enumerate(diff.oldlines): for chunk in diff.chunks:
new = diff.newlines[i] for old, new in chunk.lines:
context = LineContext( context = LineContext(
None, self.new_revision_key, None, self.new_revision_key,
None, self.new_revision_num, None, self.new_revision_num,
diff.oldname, diff.newname, diff.oldname, diff.newname,
old[0], new[0]) old[0], new[0])
lines.append(DiffLine(self.app, context, old, new, lines.append(DiffLine(self.app, context, old, new,
callback=self.onSelect)) callback=self.onSelect))
# see if there are any comments for this line # see if there are any comments for this line
key = 'old-%s-%s' % (old[0], diff.oldname) key = 'old-%s-%s' % (old[0], diff.oldname)
old_list = comment_lists.get(key, []) old_list = comment_lists.get(key, [])
key = 'new-%s-%s' % (new[0], diff.newname) key = 'new-%s-%s' % (new[0], diff.newname)
new_list = comment_lists.get(key, []) new_list = comment_lists.get(key, [])
while old_list or new_list: while old_list or new_list:
old_comment_key = new_comment_key = None old_comment_key = new_comment_key = None
old_comment = new_comment = u'' old_comment = new_comment = u''
if old_list: if old_list:
(old_comment_key, old_comment) = old_list.pop(0) (old_comment_key, old_comment) = old_list.pop(0)
if new_list: if new_list:
(new_comment_key, new_comment) = new_list.pop(0) (new_comment_key, new_comment) = new_list.pop(0)
lines.append(DiffComment(context, old_comment, new_comment)) lines.append(DiffComment(context, old_comment, new_comment))
# see if there are any draft comments for this line # see if there are any draft comments for this line
key = 'olddraft-%s-%s' % (old[0], diff.oldname) key = 'olddraft-%s-%s' % (old[0], diff.oldname)
old_list = comment_lists.get(key, []) old_list = comment_lists.get(key, [])
key = 'newdraft-%s-%s' % (old[0], diff.oldname) key = 'newdraft-%s-%s' % (old[0], diff.oldname)
new_list = comment_lists.get(key, []) new_list = comment_lists.get(key, [])
while old_list or new_list: while old_list or new_list:
old_comment_key = new_comment_key = None old_comment_key = new_comment_key = None
old_comment = new_comment = u'' old_comment = new_comment = u''
if old_list: if old_list:
(old_comment_key, old_comment) = old_list.pop(0) (old_comment_key, old_comment) = old_list.pop(0)
if new_list: if new_list:
(new_comment_key, new_comment) = new_list.pop(0) (new_comment_key, new_comment) = new_list.pop(0)
lines.append(DiffCommentEdit(context, lines.append(DiffCommentEdit(context,
old_comment_key, old_comment_key,
new_comment_key, new_comment_key,
old_comment, new_comment)) old_comment, new_comment))
listwalker = urwid.SimpleFocusListWalker(lines) listwalker = urwid.SimpleFocusListWalker(lines)
self.listbox = urwid.ListBox(listwalker) self.listbox = urwid.ListBox(listwalker)
self._w.contents.append((self.listbox, ('weight', 1))) self._w.contents.append((self.listbox, ('weight', 1)))