Expand context as needed to include all comments

Use methods that can be re-used to support manually expanding
context by the user.

Also fix a s/old/new/ typo bug.

Change-Id: I5b247204b959add9fbfd51af7e508d3d07b7c001
This commit is contained in:
James E. Blair 2014-05-04 13:32:28 -07:00
parent 067aa68dcd
commit 227b6a0add
2 changed files with 118 additions and 5 deletions

View File

@ -19,12 +19,53 @@ import re
import git
OLD = 0
NEW = 1
START = 0
END = 1
LINENO = 0
LINE = 1
class DiffChunk(object):
def __init__(self):
self.oldlines = []
self.newlines = []
self.first = False
self.last = False
self.lines = []
self.calcRange()
def __repr__(self):
return '<%s old lines %s-%s / new lines %s-%s>' % (
self.__class__.__name__,
self.range[OLD][START], self.range[OLD][END],
self.range[NEW][START], self.range[NEW][END])
def calcRange(self):
self.range = [[0, 0],
[0, 0]]
for l in self.lines:
if self.range[OLD][START] == 0 and l[OLD][LINENO] is not None:
self.range[OLD][START] = l[OLD][LINENO]
if self.range[NEW][START] == 0 and l[NEW][LINENO] is not None:
self.range[NEW][START] = l[NEW][LINENO]
if (self.range[OLD][START] != 0 and
self.range[NEW][START] != 0):
break
for l in self.lines[::-1]:
if self.range[OLD][END] == 0 and l[OLD][LINENO] is not None:
self.range[OLD][END] = l[OLD][LINENO]
if self.range[NEW][END] == 0 and l[NEW][LINENO] is not None:
self.range[NEW][END] = l[NEW][LINENO]
if (self.range[OLD][END] != 0 and
self.range[NEW][END] != 0):
break
def indexOfLine(self, oldnew, lineno):
for i, l in enumerate(self.lines):
if l[oldnew][LINENO] == lineno:
return i
class DiffContextChunk(DiffChunk):
context = True
@ -52,6 +93,7 @@ class DiffFile(object):
else:
self.chunks[-1].last = False
self.current_chunk.last = True
self.current_chunk.calcRange()
self.chunks.append(self.current_chunk)
self.current_chunk = None

View File

@ -18,6 +18,7 @@ import logging
import urwid
from gertty import mywid
from gertty import gitrepo
class LineContext(object):
def __init__(self, old_revision_key, new_revision_key,
@ -102,6 +103,14 @@ class DiffLine(urwid.Button):
}
self._w = urwid.AttrMap(col, None, focus_map=map)
class DiffContextButton(urwid.Button):
def selectable(self):
return False #TODO: change
def __init__(self, chunk):
super(DiffContextButton, self).__init__('...')
self.chunk = chunk
class DiffView(urwid.WidgetWrap):
help = mywid.GLOBAL_HELP + """
This Screen
@ -146,11 +155,14 @@ This Screen
repo = self.app.getRepo(self.project_name)
self._w.contents.append((app.header, ('pack', 1)))
self._w.contents.append((urwid.Divider(), ('pack', 1)))
lines = []
lines = [] # The initial set of lines to display
self.file_diffs = [{}, {}] # Mapping of fn -> DiffFile object (old, new)
# this is a list of files:
for i, diff in enumerate(repo.diff(self.parent, self.commit)):
if i > 0:
lines.append(urwid.Text(''))
self.file_diffs[gitrepo.OLD][diff.oldname] = diff
self.file_diffs[gitrepo.NEW][diff.newname] = diff
lines.append(urwid.Columns([
urwid.Text(diff.oldname),
urwid.Text(diff.newname)]))
@ -158,19 +170,78 @@ This Screen
if chunk.context:
if not chunk.first:
lines += self.makeLines(diff, chunk.lines[:10], comment_lists)
lines.append(urwid.Text('...'))
del chunk.lines[:10]
button = DiffContextButton(chunk)
chunk.button = button
lines.append(button)
if not chunk.last:
lines += self.makeLines(diff, chunk.lines[-10:], comment_lists)
del chunk.lines[-10:]
chunk.calcRange()
if not chunk.lines:
lines.remove(button)
else:
lines += self.makeLines(diff, chunk.lines, comment_lists)
if comment_lists:
self.log.debug("Undisplayed comments: %s" % comment_lists)
listwalker = urwid.SimpleFocusListWalker(lines)
self.listbox = urwid.ListBox(listwalker)
self._w.contents.append((self.listbox, ('weight', 1)))
self.old_focus = 2
self.draft_comments = []
self._w.set_focus(self.old_focus)
self.handleUndisplayedComments(comment_lists)
def handleUndisplayedComments(self, comment_lists):
# Handle comments that landed outside our default diff context
import time
lastlen = 0
while comment_lists:
if len(comment_lists.keys()) == lastlen:
self.log.error("Unable to display all comments: %s" % comment_lists)
return
lastlen = len(comment_lists.keys())
key = comment_lists.keys()[0]
kind, lineno, path = key.split('-', 2)
lineno = int(lineno)
if kind.startswith('old'):
oldnew = gitrepo.OLD
else:
oldnew = gitrepo.NEW
diff = self.file_diffs[oldnew][path]
for chunk in diff.chunks:
if (chunk.range[oldnew][gitrepo.START] <= lineno and
chunk.range[oldnew][gitrepo.END] >= lineno):
i = chunk.indexOfLine(oldnew, lineno)
if i < (len(chunk.lines) / 2):
from_start = True
else:
from_start = False
if chunk.first and from_start:
from_start = False
if chunk.last and (not from_start):
from_start = True
if from_start:
self.expandChunk(diff, chunk, comment_lists, from_start=i+10)
else:
self.expandChunk(diff, chunk, comment_lists, from_end=i-10)
break
def expandChunk(self, diff, chunk, comment_lists, from_start=None, from_end=None):
self.log.debug("Expand chunk %s %s %s" % (chunk, from_start, from_end))
add_lines = []
if from_start is not None:
index = self.listbox.body.index(chunk.button)
add_lines = chunk.lines[:from_start]
del chunk.lines[:from_start]
if from_end is not None:
index = self.listbox.body.index(chunk.button)+1
add_lines = chunk.lines[from_end:]
del chunk.lines[from_end:]
if add_lines:
lines = self.makeLines(diff, add_lines, comment_lists)
self.listbox.body[index:index] = lines
chunk.calcRange()
if not chunk.lines:
self.listbox.body.remove(chunk.button)
def makeLines(self, diff, lines_to_add, comment_lists):
lines = []
@ -198,7 +269,7 @@ This Screen
# see if there are any draft comments for this line
key = 'olddraft-%s-%s' % (old[0], diff.oldname)
old_list = comment_lists.pop(key, [])
key = 'newdraft-%s-%s' % (old[0], diff.oldname)
key = 'newdraft-%s-%s' % (new[0], diff.newname)
new_list = comment_lists.pop(key, [])
while old_list or new_list:
old_comment_key = new_comment_key = None