Add unified diff view
Refactor commonalities into base diff view, and implement unified and side-by-side diff views. Configurable in the config yaml file. Change-Id: Ia8d9241d04f3e7f480618e7efe1d3792f530e173
This commit is contained in:
parent
21411d2978
commit
11bdd67e86
|
@ -37,6 +37,10 @@ commentlinks:
|
||||||
#
|
#
|
||||||
# change-list-query: "status:open not label:Workflow=-1"
|
# change-list-query: "status:open not label:Workflow=-1"
|
||||||
|
|
||||||
|
# Uncomment the following line to use a unified diff view instead
|
||||||
|
# of the default side-by-side:
|
||||||
|
# diff-view: unified
|
||||||
|
|
||||||
dashboards:
|
dashboards:
|
||||||
- name: "My changes"
|
- name: "My changes"
|
||||||
query: "owner:self status:open"
|
query: "owner:self status:open"
|
||||||
|
|
|
@ -90,6 +90,7 @@ class ConfigSchema(object):
|
||||||
'dashboards': self.dashboards,
|
'dashboards': self.dashboards,
|
||||||
'reviewkeys': self.reviewkeys,
|
'reviewkeys': self.reviewkeys,
|
||||||
'change-list-query': str,
|
'change-list-query': str,
|
||||||
|
'diff-view': str,
|
||||||
})
|
})
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
|
@ -144,6 +145,8 @@ class Config(object):
|
||||||
|
|
||||||
self.project_change_list_query = self.config.get('change-list-query', 'status:open')
|
self.project_change_list_query = self.config.get('change-list-query', 'status:open')
|
||||||
|
|
||||||
|
self.diff_view = self.config.get('diff-view', 'side-by-side')
|
||||||
|
|
||||||
self.dashboards = OrderedDict()
|
self.dashboards = OrderedDict()
|
||||||
for d in self.config.get('dashboards', []):
|
for d in self.config.get('dashboards', []):
|
||||||
self.dashboards[d['key']] = d
|
self.dashboards[d['key']] = d
|
||||||
|
|
|
@ -20,7 +20,8 @@ import urwid
|
||||||
from gertty import gitrepo
|
from gertty import gitrepo
|
||||||
from gertty import mywid
|
from gertty import mywid
|
||||||
from gertty import sync
|
from gertty import sync
|
||||||
from gertty.view import diff as view_diff
|
from gertty.view import side_diff as view_side_diff
|
||||||
|
from gertty.view import unified_diff as view_unified_diff
|
||||||
import gertty.view
|
import gertty.view
|
||||||
|
|
||||||
class ReviewDialog(urwid.WidgetWrap):
|
class ReviewDialog(urwid.WidgetWrap):
|
||||||
|
@ -609,7 +610,11 @@ class ChangeView(urwid.WidgetWrap):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def diff(self, revision_key):
|
def diff(self, revision_key):
|
||||||
self.app.changeScreen(view_diff.DiffView(self.app, revision_key))
|
if self.app.config.diff_view == 'unified':
|
||||||
|
screen = view_unified_diff.UnifiedDiffView(self.app, revision_key)
|
||||||
|
else:
|
||||||
|
screen = view_side_diff.SideDiffView(self.app, revision_key)
|
||||||
|
self.app.changeScreen(screen)
|
||||||
|
|
||||||
def reviewKey(self, reviewkey):
|
def reviewKey(self, reviewkey):
|
||||||
approvals = {}
|
approvals = {}
|
||||||
|
|
|
@ -93,91 +93,20 @@ class LineContext(object):
|
||||||
self.old_ln = old_ln
|
self.old_ln = old_ln
|
||||||
self.new_ln = new_ln
|
self.new_ln = new_ln
|
||||||
|
|
||||||
class DiffCommentEdit(urwid.Columns):
|
class BaseDiffCommentEdit(urwid.Columns):
|
||||||
def __init__(self, context, old_key=None, new_key=None, old=u'', new=u''):
|
pass
|
||||||
super(DiffCommentEdit, self).__init__([])
|
|
||||||
self.context = context
|
|
||||||
# If we save a comment, the resulting key will be stored here
|
|
||||||
self.old_key = old_key
|
|
||||||
self.new_key = new_key
|
|
||||||
self.old = urwid.Edit(edit_text=old, multiline=True)
|
|
||||||
self.new = urwid.Edit(edit_text=new, multiline=True)
|
|
||||||
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
|
||||||
self.contents.append((urwid.AttrMap(self.old, 'draft-comment'), ('weight', 1, False)))
|
|
||||||
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
|
||||||
self.contents.append((urwid.AttrMap(self.new, 'draft-comment'), ('weight', 1, False)))
|
|
||||||
self.focus_position = 3
|
|
||||||
|
|
||||||
def keypress(self, size, key):
|
class BaseDiffComment(urwid.Columns):
|
||||||
r = super(DiffCommentEdit, self).keypress(size, key)
|
pass
|
||||||
if r in ['tab', 'shift tab']:
|
|
||||||
if self.focus_position == 3:
|
|
||||||
self.focus_position = 1
|
|
||||||
else:
|
|
||||||
self.focus_position = 3
|
|
||||||
return None
|
|
||||||
return r
|
|
||||||
|
|
||||||
class DiffComment(urwid.Columns):
|
class BaseDiffLine(urwid.Button):
|
||||||
def __init__(self, context, old, new):
|
|
||||||
super(DiffComment, self).__init__([])
|
|
||||||
self.context = context
|
|
||||||
oldt = urwid.Text(old)
|
|
||||||
newt = urwid.Text(new)
|
|
||||||
if old:
|
|
||||||
oldt = urwid.AttrMap(oldt, 'comment')
|
|
||||||
if new:
|
|
||||||
newt = urwid.AttrMap(newt, 'comment')
|
|
||||||
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
|
||||||
self.contents.append((oldt, ('weight', 1, False)))
|
|
||||||
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
|
||||||
self.contents.append((newt, ('weight', 1, False)))
|
|
||||||
|
|
||||||
class DiffLine(urwid.Button):
|
|
||||||
def selectable(self):
|
def selectable(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __init__(self, app, context, old, new, callback=None):
|
class BaseFileHeader(urwid.Button):
|
||||||
super(DiffLine, self).__init__('', on_press=callback)
|
|
||||||
self.context = context
|
|
||||||
columns = []
|
|
||||||
for (ln, action, line) in (old, new):
|
|
||||||
if ln is None:
|
|
||||||
ln = ''
|
|
||||||
else:
|
|
||||||
ln = str(ln)
|
|
||||||
ln_col = urwid.Text(('line-number', ln))
|
|
||||||
ln_col.set_wrap_mode('clip')
|
|
||||||
line_col = urwid.Text(line)
|
|
||||||
line_col.set_wrap_mode('clip')
|
|
||||||
if action == '':
|
|
||||||
line_col = urwid.AttrMap(line_col, 'nonexistent')
|
|
||||||
columns += [(4, ln_col), line_col]
|
|
||||||
col = urwid.Columns(columns)
|
|
||||||
map = {None: 'focused',
|
|
||||||
'added-line': 'focused-added-line',
|
|
||||||
'added-word': 'focused-added-word',
|
|
||||||
'removed-line': 'focused-removed-line',
|
|
||||||
'removed-word': 'focused-removed-word',
|
|
||||||
'nonexistent': 'focused-nonexistent',
|
|
||||||
'line-number': 'focused-line-number',
|
|
||||||
}
|
|
||||||
self._w = urwid.AttrMap(col, None, focus_map=map)
|
|
||||||
|
|
||||||
class FileHeader(urwid.Button):
|
|
||||||
def selectable(self):
|
def selectable(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __init__(self, app, context, old, new, callback=None):
|
|
||||||
super(FileHeader, self).__init__('', on_press=callback)
|
|
||||||
self.context = context
|
|
||||||
col = urwid.Columns([
|
|
||||||
urwid.Text(('filename', old)),
|
|
||||||
urwid.Text(('filename', new))])
|
|
||||||
map = {None: 'focused-filename',
|
|
||||||
'filename': 'focused-filename'}
|
|
||||||
self._w = urwid.AttrMap(col, None, focus_map=map)
|
|
||||||
|
|
||||||
class DiffContextButton(urwid.WidgetWrap):
|
class DiffContextButton(urwid.WidgetWrap):
|
||||||
def selectable(self):
|
def selectable(self):
|
||||||
return True
|
return True
|
||||||
|
@ -214,7 +143,7 @@ class DiffContextButton(urwid.WidgetWrap):
|
||||||
def next(self, button):
|
def next(self, button):
|
||||||
self.view.expandChunk(self.diff, self.chunk, from_end=-10)
|
self.view.expandChunk(self.diff, self.chunk, from_end=-10)
|
||||||
|
|
||||||
class DiffView(urwid.WidgetWrap):
|
class BaseDiffView(urwid.WidgetWrap):
|
||||||
_help = """
|
_help = """
|
||||||
<Enter> Add an inline comment
|
<Enter> Add an inline comment
|
||||||
<p> Select old/new patchsets to diff
|
<p> Select old/new patchsets to diff
|
||||||
|
@ -224,7 +153,7 @@ class DiffView(urwid.WidgetWrap):
|
||||||
return self._help
|
return self._help
|
||||||
|
|
||||||
def __init__(self, app, new_revision_key):
|
def __init__(self, app, new_revision_key):
|
||||||
super(DiffView, self).__init__(urwid.Pile([]))
|
super(BaseDiffView, self).__init__(urwid.Pile([]))
|
||||||
self.log = logging.getLogger('gertty.view.diff')
|
self.log = logging.getLogger('gertty.view.diff')
|
||||||
self.app = app
|
self.app = app
|
||||||
self.old_revision_key = None # Base
|
self.old_revision_key = None # Base
|
||||||
|
@ -404,86 +333,10 @@ class DiffView(urwid.WidgetWrap):
|
||||||
chunk.button.update()
|
chunk.button.update()
|
||||||
|
|
||||||
def makeLines(self, diff, lines_to_add, comment_lists):
|
def makeLines(self, diff, lines_to_add, comment_lists):
|
||||||
lines = []
|
raise NotImplementedError
|
||||||
for old, new in lines_to_add:
|
|
||||||
context = LineContext(
|
|
||||||
self.old_revision_key, self.new_revision_key,
|
|
||||||
self.old_revision_num, self.new_revision_num,
|
|
||||||
diff.oldname, diff.newname,
|
|
||||||
old[0], new[0])
|
|
||||||
lines.append(DiffLine(self.app, context, old, new,
|
|
||||||
callback=self.onSelect))
|
|
||||||
# see if there are any comments for this line
|
|
||||||
key = 'old-%s-%s' % (old[0], diff.oldname)
|
|
||||||
old_list = comment_lists.pop(key, [])
|
|
||||||
key = 'new-%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
|
|
||||||
old_comment = new_comment = u''
|
|
||||||
if old_list:
|
|
||||||
(old_comment_key, old_comment) = old_list.pop(0)
|
|
||||||
if new_list:
|
|
||||||
(new_comment_key, new_comment) = new_list.pop(0)
|
|
||||||
lines.append(DiffComment(context, old_comment, new_comment))
|
|
||||||
# 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' % (new[0], diff.newname)
|
|
||||||
new_list = comment_lists.pop(key, [])
|
|
||||||
while old_list or new_list:
|
|
||||||
old_comment_key = new_comment_key = None
|
|
||||||
old_comment = new_comment = u''
|
|
||||||
if old_list:
|
|
||||||
(old_comment_key, old_comment) = old_list.pop(0)
|
|
||||||
if new_list:
|
|
||||||
(new_comment_key, new_comment) = new_list.pop(0)
|
|
||||||
lines.append(DiffCommentEdit(context,
|
|
||||||
old_comment_key,
|
|
||||||
new_comment_key,
|
|
||||||
old_comment, new_comment))
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def makeFileHeader(self, diff, comment_lists):
|
def makeFileHeader(self, diff, comment_lists):
|
||||||
context = LineContext(
|
raise NotImplementedError
|
||||||
self.old_revision_key, self.new_revision_key,
|
|
||||||
self.old_revision_num, self.new_revision_num,
|
|
||||||
diff.oldname, diff.newname,
|
|
||||||
None, None)
|
|
||||||
lines = []
|
|
||||||
lines.append(FileHeader(self.app, context, diff.oldname, diff.newname,
|
|
||||||
callback=self.onSelect))
|
|
||||||
|
|
||||||
# see if there are any comments for this file
|
|
||||||
key = 'old-None-%s' % (diff.oldname,)
|
|
||||||
old_list = comment_lists.pop(key, [])
|
|
||||||
key = 'new-None-%s' % (diff.newname,)
|
|
||||||
new_list = comment_lists.pop(key, [])
|
|
||||||
while old_list or new_list:
|
|
||||||
old_comment_key = new_comment_key = None
|
|
||||||
old_comment = new_comment = u''
|
|
||||||
if old_list:
|
|
||||||
(old_comment_key, old_comment) = old_list.pop(0)
|
|
||||||
if new_list:
|
|
||||||
(new_comment_key, new_comment) = new_list.pop(0)
|
|
||||||
lines.append(DiffComment(context, old_comment, new_comment))
|
|
||||||
# see if there are any draft comments for this file
|
|
||||||
key = 'olddraft-None-%s' % (diff.oldname,)
|
|
||||||
old_list = comment_lists.pop(key, [])
|
|
||||||
key = 'newdraft-None-%s' % (diff.newname,)
|
|
||||||
new_list = comment_lists.pop(key, [])
|
|
||||||
while old_list or new_list:
|
|
||||||
old_comment_key = new_comment_key = None
|
|
||||||
old_comment = new_comment = u''
|
|
||||||
if old_list:
|
|
||||||
(old_comment_key, old_comment) = old_list.pop(0)
|
|
||||||
if new_list:
|
|
||||||
(new_comment_key, new_comment) = new_list.pop(0)
|
|
||||||
lines.append(DiffCommentEdit(context,
|
|
||||||
old_comment_key,
|
|
||||||
new_comment_key,
|
|
||||||
old_comment, new_comment))
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
#TODO
|
#TODO
|
||||||
|
@ -491,9 +344,9 @@ class DiffView(urwid.WidgetWrap):
|
||||||
|
|
||||||
def keypress(self, size, key):
|
def keypress(self, size, key):
|
||||||
old_focus = self.listbox.focus
|
old_focus = self.listbox.focus
|
||||||
r = super(DiffView, self).keypress(size, key)
|
r = super(BaseDiffView, self).keypress(size, key)
|
||||||
new_focus = self.listbox.focus
|
new_focus = self.listbox.focus
|
||||||
if (isinstance(old_focus, DiffCommentEdit) and
|
if (isinstance(old_focus, BaseDiffCommentEdit) and
|
||||||
(old_focus != new_focus or key == 'esc')):
|
(old_focus != new_focus or key == 'esc')):
|
||||||
self.cleanupEdit(old_focus)
|
self.cleanupEdit(old_focus)
|
||||||
if r == 'p':
|
if r == 'p':
|
||||||
|
@ -503,36 +356,23 @@ class DiffView(urwid.WidgetWrap):
|
||||||
|
|
||||||
def mouse_event(self, size, event, button, x, y, focus):
|
def mouse_event(self, size, event, button, x, y, focus):
|
||||||
old_focus = self.listbox.focus
|
old_focus = self.listbox.focus
|
||||||
r = super(DiffView, self).mouse_event(size, event, button, x, y, focus)
|
r = super(BaseDiffView, self).mouse_event(size, event, button, x, y, focus)
|
||||||
new_focus = self.listbox.focus
|
new_focus = self.listbox.focus
|
||||||
if old_focus != new_focus and isinstance(old_focus, DiffCommentEdit):
|
if old_focus != new_focus and isinstance(old_focus, BaseDiffCommentEdit):
|
||||||
self.cleanupEdit(old_focus)
|
self.cleanupEdit(old_focus)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
def makeCommentEdit(self, edit):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def onSelect(self, button):
|
def onSelect(self, button):
|
||||||
pos = self.listbox.focus_position
|
pos = self.listbox.focus_position
|
||||||
e = DiffCommentEdit(self.listbox.body[pos].context)
|
e = self.makeCommentEdit(self.listbox.body[pos])
|
||||||
self.listbox.body.insert(pos+1, e)
|
self.listbox.body.insert(pos+1, e)
|
||||||
self.listbox.focus_position = pos+1
|
self.listbox.focus_position = pos+1
|
||||||
|
|
||||||
def cleanupEdit(self, edit):
|
def cleanupEdit(self, edit):
|
||||||
if edit.old_key:
|
raise NotImplementedError
|
||||||
self.deleteComment(edit.old_key)
|
|
||||||
edit.old_key = None
|
|
||||||
if edit.new_key:
|
|
||||||
self.deleteComment(edit.new_key)
|
|
||||||
edit.new_key = None
|
|
||||||
old = edit.old.edit_text.strip()
|
|
||||||
new = edit.new.edit_text.strip()
|
|
||||||
if old or new:
|
|
||||||
if old:
|
|
||||||
edit.old_key = self.saveComment(
|
|
||||||
edit.context, old, new=False)
|
|
||||||
if new:
|
|
||||||
edit.new_key = self.saveComment(
|
|
||||||
edit.context, new, new=True)
|
|
||||||
else:
|
|
||||||
self.listbox.body.remove(edit)
|
|
||||||
|
|
||||||
def deleteComment(self, comment_key):
|
def deleteComment(self, comment_key):
|
||||||
with self.app.db.getSession() as session:
|
with self.app.db.getSession() as session:
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
# Copyright 2014 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
from gertty import mywid
|
||||||
|
from gertty import gitrepo
|
||||||
|
from gertty.view.diff import *
|
||||||
|
|
||||||
|
class SideDiffCommentEdit(BaseDiffCommentEdit):
|
||||||
|
def __init__(self, context, old_key=None, new_key=None, old=u'', new=u''):
|
||||||
|
super(SideDiffCommentEdit, self).__init__([])
|
||||||
|
self.context = context
|
||||||
|
# If we save a comment, the resulting key will be stored here
|
||||||
|
self.old_key = old_key
|
||||||
|
self.new_key = new_key
|
||||||
|
self.old = urwid.Edit(edit_text=old, multiline=True)
|
||||||
|
self.new = urwid.Edit(edit_text=new, multiline=True)
|
||||||
|
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
||||||
|
self.contents.append((urwid.AttrMap(self.old, 'draft-comment'), ('weight', 1, False)))
|
||||||
|
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
||||||
|
self.contents.append((urwid.AttrMap(self.new, 'draft-comment'), ('weight', 1, False)))
|
||||||
|
self.focus_position = 3
|
||||||
|
|
||||||
|
def keypress(self, size, key):
|
||||||
|
r = super(SideDiffCommentEdit, self).keypress(size, key)
|
||||||
|
if r in ['tab', 'shift tab']:
|
||||||
|
if self.focus_position == 3:
|
||||||
|
self.focus_position = 1
|
||||||
|
else:
|
||||||
|
self.focus_position = 3
|
||||||
|
return None
|
||||||
|
return r
|
||||||
|
|
||||||
|
class SideDiffComment(BaseDiffComment):
|
||||||
|
def __init__(self, context, old, new):
|
||||||
|
super(SideDiffComment, self).__init__([])
|
||||||
|
self.context = context
|
||||||
|
oldt = urwid.Text(old)
|
||||||
|
newt = urwid.Text(new)
|
||||||
|
if old:
|
||||||
|
oldt = urwid.AttrMap(oldt, 'comment')
|
||||||
|
if new:
|
||||||
|
newt = urwid.AttrMap(newt, 'comment')
|
||||||
|
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
||||||
|
self.contents.append((oldt, ('weight', 1, False)))
|
||||||
|
self.contents.append((urwid.Text(u''), ('given', 4, False)))
|
||||||
|
self.contents.append((newt, ('weight', 1, False)))
|
||||||
|
|
||||||
|
class SideDiffLine(BaseDiffLine):
|
||||||
|
def __init__(self, app, context, old, new, callback=None):
|
||||||
|
super(SideDiffLine, self).__init__('', on_press=callback)
|
||||||
|
self.context = context
|
||||||
|
columns = []
|
||||||
|
for (ln, action, line) in (old, new):
|
||||||
|
if ln is None:
|
||||||
|
ln = ''
|
||||||
|
else:
|
||||||
|
ln = str(ln)
|
||||||
|
ln_col = urwid.Text(('line-number', ln))
|
||||||
|
ln_col.set_wrap_mode('clip')
|
||||||
|
line_col = urwid.Text(line)
|
||||||
|
line_col.set_wrap_mode('clip')
|
||||||
|
if action == '':
|
||||||
|
line_col = urwid.AttrMap(line_col, 'nonexistent')
|
||||||
|
columns += [(4, ln_col), line_col]
|
||||||
|
col = urwid.Columns(columns)
|
||||||
|
map = {None: 'focused',
|
||||||
|
'added-line': 'focused-added-line',
|
||||||
|
'added-word': 'focused-added-word',
|
||||||
|
'removed-line': 'focused-removed-line',
|
||||||
|
'removed-word': 'focused-removed-word',
|
||||||
|
'nonexistent': 'focused-nonexistent',
|
||||||
|
'line-number': 'focused-line-number',
|
||||||
|
}
|
||||||
|
self._w = urwid.AttrMap(col, None, focus_map=map)
|
||||||
|
|
||||||
|
class SideFileHeader(BaseFileHeader):
|
||||||
|
def __init__(self, app, context, old, new, callback=None):
|
||||||
|
super(SideFileHeader, self).__init__('', on_press=callback)
|
||||||
|
self.context = context
|
||||||
|
col = urwid.Columns([
|
||||||
|
urwid.Text(('filename', old)),
|
||||||
|
urwid.Text(('filename', new))])
|
||||||
|
map = {None: 'focused-filename',
|
||||||
|
'filename': 'focused-filename'}
|
||||||
|
self._w = urwid.AttrMap(col, None, focus_map=map)
|
||||||
|
|
||||||
|
|
||||||
|
class SideDiffView(BaseDiffView):
|
||||||
|
def makeLines(self, diff, lines_to_add, comment_lists):
|
||||||
|
lines = []
|
||||||
|
for old, new in lines_to_add:
|
||||||
|
context = LineContext(
|
||||||
|
self.old_revision_key, self.new_revision_key,
|
||||||
|
self.old_revision_num, self.new_revision_num,
|
||||||
|
diff.oldname, diff.newname,
|
||||||
|
old[0], new[0])
|
||||||
|
lines.append(SideDiffLine(self.app, context, old, new,
|
||||||
|
callback=self.onSelect))
|
||||||
|
# see if there are any comments for this line
|
||||||
|
key = 'old-%s-%s' % (old[0], diff.oldname)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
key = 'new-%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
|
||||||
|
old_comment = new_comment = u''
|
||||||
|
if old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
if new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(SideDiffComment(context, old_comment, new_comment))
|
||||||
|
# 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' % (new[0], diff.newname)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while old_list or new_list:
|
||||||
|
old_comment_key = new_comment_key = None
|
||||||
|
old_comment = new_comment = u''
|
||||||
|
if old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
if new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(SideDiffCommentEdit(context,
|
||||||
|
old_comment_key,
|
||||||
|
new_comment_key,
|
||||||
|
old_comment, new_comment))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def makeFileHeader(self, diff, comment_lists):
|
||||||
|
context = LineContext(
|
||||||
|
self.old_revision_key, self.new_revision_key,
|
||||||
|
self.old_revision_num, self.new_revision_num,
|
||||||
|
diff.oldname, diff.newname,
|
||||||
|
None, None)
|
||||||
|
lines = []
|
||||||
|
lines.append(SideFileHeader(self.app, context, diff.oldname, diff.newname,
|
||||||
|
callback=self.onSelect))
|
||||||
|
|
||||||
|
# see if there are any comments for this file
|
||||||
|
key = 'old-None-%s' % (diff.oldname,)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
key = 'new-None-%s' % (diff.newname,)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while old_list or new_list:
|
||||||
|
old_comment_key = new_comment_key = None
|
||||||
|
old_comment = new_comment = u''
|
||||||
|
if old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
if new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(SideDiffComment(context, old_comment, new_comment))
|
||||||
|
# see if there are any draft comments for this file
|
||||||
|
key = 'olddraft-None-%s' % (diff.oldname,)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
key = 'newdraft-None-%s' % (diff.newname,)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while old_list or new_list:
|
||||||
|
old_comment_key = new_comment_key = None
|
||||||
|
old_comment = new_comment = u''
|
||||||
|
if old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
if new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(SideDiffCommentEdit(context,
|
||||||
|
old_comment_key,
|
||||||
|
new_comment_key,
|
||||||
|
old_comment, new_comment))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def makeCommentEdit(self, edit):
|
||||||
|
return SideDiffCommentEdit(edit.context)
|
||||||
|
|
||||||
|
def cleanupEdit(self, edit):
|
||||||
|
if edit.old_key:
|
||||||
|
self.deleteComment(edit.old_key)
|
||||||
|
edit.old_key = None
|
||||||
|
if edit.new_key:
|
||||||
|
self.deleteComment(edit.new_key)
|
||||||
|
edit.new_key = None
|
||||||
|
old = edit.old.edit_text.strip()
|
||||||
|
new = edit.new.edit_text.strip()
|
||||||
|
if old or new:
|
||||||
|
if old:
|
||||||
|
edit.old_key = self.saveComment(
|
||||||
|
edit.context, old, new=False)
|
||||||
|
if new:
|
||||||
|
edit.new_key = self.saveComment(
|
||||||
|
edit.context, new, new=True)
|
||||||
|
else:
|
||||||
|
self.listbox.body.remove(edit)
|
|
@ -0,0 +1,217 @@
|
||||||
|
# Copyright 2014 OpenStack Foundation
|
||||||
|
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
from gertty import mywid
|
||||||
|
from gertty import gitrepo
|
||||||
|
from gertty.view.diff import *
|
||||||
|
|
||||||
|
class UnifiedDiffCommentEdit(BaseDiffCommentEdit):
|
||||||
|
def __init__(self, context, oldnew, key=None, comment=u''):
|
||||||
|
super(UnifiedDiffCommentEdit, self).__init__([])
|
||||||
|
self.context = context
|
||||||
|
self.oldnew = oldnew
|
||||||
|
# If we save a comment, the resulting key will be stored here
|
||||||
|
self.key = key
|
||||||
|
self.comment = urwid.Edit(edit_text=comment, multiline=True)
|
||||||
|
self.contents.append((urwid.Text(u''), ('given', 8, False)))
|
||||||
|
self.contents.append((urwid.AttrMap(self.comment, 'draft-comment'),
|
||||||
|
('weight', 1, False)))
|
||||||
|
self.focus_position = 1
|
||||||
|
|
||||||
|
class UnifiedDiffComment(BaseDiffComment):
|
||||||
|
def __init__(self, context, oldnew, comment):
|
||||||
|
super(UnifiedDiffComment, self).__init__([])
|
||||||
|
self.context = context
|
||||||
|
text = urwid.AttrMap(urwid.Text(comment), 'comment')
|
||||||
|
self.contents.append((urwid.Text(u''), ('given', 8, False)))
|
||||||
|
self.contents.append((text, ('weight', 1, False)))
|
||||||
|
|
||||||
|
class UnifiedDiffLine(BaseDiffLine):
|
||||||
|
def __init__(self, app, context, oldnew, old, new, callback=None):
|
||||||
|
super(UnifiedDiffLine, self).__init__('', on_press=callback)
|
||||||
|
self.context = context
|
||||||
|
self.oldnew = oldnew
|
||||||
|
(old_ln, old_action, old_line) = old
|
||||||
|
(new_ln, new_action, new_line) = new
|
||||||
|
if old_ln is None:
|
||||||
|
old_ln = ''
|
||||||
|
else:
|
||||||
|
old_ln = str(old_ln)
|
||||||
|
if new_ln is None:
|
||||||
|
new_ln = ''
|
||||||
|
else:
|
||||||
|
new_ln = str(new_ln)
|
||||||
|
old_ln_col = urwid.Text(('line-number', old_ln))
|
||||||
|
old_ln_col.set_wrap_mode('clip')
|
||||||
|
new_ln_col = urwid.Text(('line-number', new_ln))
|
||||||
|
new_ln_col.set_wrap_mode('clip')
|
||||||
|
if oldnew == gitrepo.OLD:
|
||||||
|
action = old_action
|
||||||
|
line = old_line
|
||||||
|
columns = [(4, old_ln_col), (4, urwid.Text(u''))]
|
||||||
|
elif oldnew == gitrepo.NEW:
|
||||||
|
action = new_action
|
||||||
|
line = new_line
|
||||||
|
columns = [(4, urwid.Text(u'')), (4, new_ln_col)]
|
||||||
|
if new_action == ' ':
|
||||||
|
columns = [(4, old_ln_col), (4, new_ln_col)]
|
||||||
|
line_col = urwid.Text(line)
|
||||||
|
if action == '':
|
||||||
|
line_col = urwid.AttrMap(line_col, 'nonexistent')
|
||||||
|
columns += [line_col]
|
||||||
|
col = urwid.Columns(columns)
|
||||||
|
map = {None: 'focused',
|
||||||
|
'added-line': 'focused-added-line',
|
||||||
|
'added-word': 'focused-added-word',
|
||||||
|
'removed-line': 'focused-removed-line',
|
||||||
|
'removed-word': 'focused-removed-word',
|
||||||
|
'nonexistent': 'focused-nonexistent',
|
||||||
|
'line-number': 'focused-line-number',
|
||||||
|
}
|
||||||
|
self._w = urwid.AttrMap(col, None, focus_map=map)
|
||||||
|
|
||||||
|
class UnifiedFileHeader(BaseFileHeader):
|
||||||
|
def __init__(self, app, context, oldnew, old, new, callback=None):
|
||||||
|
super(UnifiedFileHeader, self).__init__('', on_press=callback)
|
||||||
|
self.context = context
|
||||||
|
self.oldnew = oldnew
|
||||||
|
if oldnew == gitrepo.OLD:
|
||||||
|
col = urwid.Columns([
|
||||||
|
urwid.Text(('filename', old))])
|
||||||
|
elif oldnew == gitrepo.NEW:
|
||||||
|
col = urwid.Columns([
|
||||||
|
(4, urwid.Text(u'')),
|
||||||
|
urwid.Text(('filename', new))])
|
||||||
|
map = {None: 'focused-filename',
|
||||||
|
'filename': 'focused-filename'}
|
||||||
|
self._w = urwid.AttrMap(col, None, focus_map=map)
|
||||||
|
|
||||||
|
class UnifiedDiffView(BaseDiffView):
|
||||||
|
def makeLines(self, diff, lines_to_add, comment_lists):
|
||||||
|
lines = []
|
||||||
|
for old, new in lines_to_add:
|
||||||
|
context = LineContext(
|
||||||
|
self.old_revision_key, self.new_revision_key,
|
||||||
|
self.old_revision_num, self.new_revision_num,
|
||||||
|
diff.oldname, diff.newname,
|
||||||
|
old[0], new[0])
|
||||||
|
if context.old_ln is not None:
|
||||||
|
lines.append(UnifiedDiffLine(self.app, context, gitrepo.OLD, old, new,
|
||||||
|
callback=self.onSelect))
|
||||||
|
# see if there are any comments for this line
|
||||||
|
key = 'old-%s-%s' % (old[0], diff.oldname)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
while old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffComment(context, gitrepo.OLD, old_comment))
|
||||||
|
# see if there are any draft comments for this line
|
||||||
|
key = 'olddraft-%s-%s' % (old[0], diff.oldname)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
while old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffCommentEdit(context,
|
||||||
|
gitrepo.OLD,
|
||||||
|
old_comment_key,
|
||||||
|
old_comment))
|
||||||
|
# new line
|
||||||
|
if context.new_ln is not None and new[1] != ' ':
|
||||||
|
lines.append(UnifiedDiffLine(self.app, context, gitrepo.NEW, old, new,
|
||||||
|
callback=self.onSelect))
|
||||||
|
# see if there are any comments for this line
|
||||||
|
key = 'new-%s-%s' % (new[0], diff.newname)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffComment(context, gitrepo.NEW, new_comment))
|
||||||
|
# see if there are any draft comments for this line
|
||||||
|
key = 'newdraft-%s-%s' % (new[0], diff.newname)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffCommentEdit(context,
|
||||||
|
gitrepo.NEW,
|
||||||
|
new_comment_key,
|
||||||
|
new_comment))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def makeFileHeader(self, diff, comment_lists):
|
||||||
|
context = LineContext(
|
||||||
|
self.old_revision_key, self.new_revision_key,
|
||||||
|
self.old_revision_num, self.new_revision_num,
|
||||||
|
diff.oldname, diff.newname,
|
||||||
|
None, None)
|
||||||
|
lines = []
|
||||||
|
lines.append(UnifiedFileHeader(self.app, context, gitrepo.OLD,
|
||||||
|
diff.oldname, diff.newname,
|
||||||
|
callback=self.onSelect))
|
||||||
|
# see if there are any comments for this file
|
||||||
|
key = 'old-None-%s' % (diff.oldname,)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
while old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffComment(context, gitrepo.OLD, old_comment))
|
||||||
|
# see if there are any draft comments for this file
|
||||||
|
key = 'olddraft-None-%s' % (diff.oldname,)
|
||||||
|
old_list = comment_lists.pop(key, [])
|
||||||
|
while old_list:
|
||||||
|
(old_comment_key, old_comment) = old_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffCommentEdit(context,
|
||||||
|
gitrepo.OLD,
|
||||||
|
old_comment_key,
|
||||||
|
old_comment))
|
||||||
|
# new line
|
||||||
|
lines.append(UnifiedFileHeader(self.app, context, gitrepo.NEW,
|
||||||
|
diff.oldname, diff.newname,
|
||||||
|
callback=self.onSelect))
|
||||||
|
|
||||||
|
# see if there are any comments for this file
|
||||||
|
key = 'new-None-%s' % (diff.newname,)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffComment(context, gitrepo.NEW, new_comment))
|
||||||
|
# see if there are any draft comments for this file
|
||||||
|
key = 'newdraft-None-%s' % (diff.newname,)
|
||||||
|
new_list = comment_lists.pop(key, [])
|
||||||
|
while new_list:
|
||||||
|
(new_comment_key, new_comment) = new_list.pop(0)
|
||||||
|
lines.append(UnifiedDiffCommentEdit(context,
|
||||||
|
gitrepo.NEW,
|
||||||
|
new_comment_key,
|
||||||
|
new_comment))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def makeCommentEdit(self, edit):
|
||||||
|
return UnifiedDiffCommentEdit(edit.context,
|
||||||
|
edit.oldnew)
|
||||||
|
|
||||||
|
def cleanupEdit(self, edit):
|
||||||
|
if edit.key:
|
||||||
|
self.deleteComment(edit.key)
|
||||||
|
edit.key = None
|
||||||
|
comment = edit.comment.edit_text.strip()
|
||||||
|
if comment:
|
||||||
|
new = False
|
||||||
|
if edit.oldnew == gitrepo.NEW:
|
||||||
|
new = True
|
||||||
|
edit.key = self.saveComment(
|
||||||
|
edit.context, comment, new=new)
|
||||||
|
else:
|
||||||
|
self.listbox.body.remove(edit)
|
Loading…
Reference in New Issue