You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
11 KiB
263 lines
11 KiB
# 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 |
|
|
|
class LineContext(object): |
|
def __init__(self, old_revision_key, new_revision_key, |
|
old_revision_num, new_revision_num, |
|
old_fn, new_fn, old_ln, new_ln): |
|
self.old_revision_key = old_revision_key |
|
self.new_revision_key = new_revision_key |
|
self.old_revision_num = old_revision_num |
|
self.new_revision_num = new_revision_num |
|
self.old_fn = old_fn |
|
self.new_fn = new_fn |
|
self.old_ln = old_ln |
|
self.new_ln = new_ln |
|
|
|
class DiffCommentEdit(urwid.Columns): |
|
def __init__(self, context, old_key=None, new_key=None, old=u'', new=u''): |
|
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): |
|
r = super(DiffCommentEdit, 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 DiffComment(urwid.Columns): |
|
def __init__(self, context, old, new): |
|
super(DiffComment, self).__init__([]) |
|
self.context = context |
|
self.old = urwid.AttrMap(urwid.Text(old), 'comment') |
|
self.new = urwid.AttrMap(urwid.Text(new), 'comment') |
|
self.contents.append((urwid.Text(u''), ('given', 4, False))) |
|
self.contents.append((self.old, ('weight', 1, False))) |
|
self.contents.append((urwid.Text(u''), ('given', 4, False))) |
|
self.contents.append((self.new, ('weight', 1, False))) |
|
|
|
class DiffLine(urwid.Button): |
|
def selectable(self): |
|
return True |
|
|
|
def __init__(self, app, context, old, new, callback=None): |
|
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(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: 'reversed', |
|
'added-line': 'reversed-added-line', |
|
'added-word': 'reversed-added-word', |
|
'removed-line': 'reversed-removed-line', |
|
'removed-word': 'reversed-removed-word', |
|
'nonexistent': 'reversed-nonexistent', |
|
} |
|
self._w = urwid.AttrMap(col, None, focus_map=map) |
|
|
|
class DiffView(urwid.WidgetWrap): |
|
help = """ |
|
<Enter> Add an inline comment. |
|
<ESC> Go back to the previous screen. |
|
""" |
|
|
|
def __init__(self, app, new_revision_key): |
|
super(DiffView, self).__init__(urwid.Pile([])) |
|
self.log = logging.getLogger('gertty.view.diff') |
|
self.app = app |
|
self.new_revision_key = new_revision_key |
|
with self.app.db.getSession() as session: |
|
revision = session.getRevision(new_revision_key) |
|
self.title = u'Diff of %s change %s patchset %s' % ( |
|
revision.change.project.name, |
|
revision.change.number, |
|
revision.number) |
|
self.new_revision_num = revision.number |
|
self.change_key = revision.change.key |
|
self.project_name = revision.change.project.name |
|
self.parent = revision.parent |
|
self.commit = revision.commit |
|
comment_lists = {} |
|
for comment in revision.comments: |
|
if comment.parent: |
|
key = 'old' |
|
else: |
|
key = 'new' |
|
if comment.pending: |
|
key += 'draft' |
|
key += '-' + str(comment.line) |
|
key += '-' + str(comment.file) |
|
comment_list = comment_lists.get(key, []) |
|
comment_list.append((comment.key, comment.message)) |
|
comment_lists[key] = comment_list |
|
repo = self.app.getRepo(self.project_name) |
|
self._w.contents.append((app.header, ('pack', 1))) |
|
self._w.contents.append((urwid.Divider(), ('pack', 1))) |
|
lines = [] |
|
# this is a list of files: |
|
for i, diff in enumerate(repo.diff(self.parent, self.commit)): |
|
if i > 0: |
|
lines.append(urwid.Text('')) |
|
lines.append(urwid.Columns([ |
|
urwid.Text(diff.oldname), |
|
urwid.Text(diff.newname)])) |
|
for i, old in enumerate(diff.oldlines): |
|
new = diff.newlines[i] |
|
context = LineContext( |
|
None, self.new_revision_key, |
|
None, 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.get(key, []) |
|
key = 'new-%s-%s' % (old[0], diff.oldname) |
|
new_list = comment_lists.get(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.get(key, []) |
|
key = 'newdraft-%s-%s' % (old[0], diff.oldname) |
|
new_list = comment_lists.get(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)) |
|
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) |
|
|
|
def refresh(self): |
|
#TODO |
|
pass |
|
|
|
def keypress(self, size, key): |
|
old_focus = self.listbox.focus |
|
r = super(DiffView, self).keypress(size, key) |
|
new_focus = self.listbox.focus |
|
if old_focus != new_focus and isinstance(old_focus, DiffCommentEdit): |
|
self.cleanupEdit(old_focus) |
|
return r |
|
|
|
def mouse_event(self, size, event, button, x, y, focus): |
|
old_focus = self.listbox.focus |
|
r = super(DiffView, self).mouse_event(size, event, button, x, y, focus) |
|
new_focus = self.listbox.focus |
|
if old_focus != new_focus and isinstance(old_focus, DiffCommentEdit): |
|
self.cleanupEdit(old_focus) |
|
return r |
|
|
|
def onSelect(self, button): |
|
pos = self.listbox.focus_position |
|
e = DiffCommentEdit(self.listbox.body[pos].context) |
|
self.listbox.body.insert(pos+1, e) |
|
self.listbox.focus_position = pos+1 |
|
|
|
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) |
|
|
|
def deleteComment(self, comment_key): |
|
with self.app.db.getSession() as session: |
|
comment = session.getComment(comment_key) |
|
session.delete(comment) |
|
|
|
def saveComment(self, context, text, new=True): |
|
if (not new) and (not context.old_revision_num): |
|
parent = True |
|
revision_key = context.new_revision_key |
|
else: |
|
parent = False |
|
if new: |
|
revision_key = context.new_revision_key |
|
else: |
|
revision_key = context.old_revision_key |
|
if new: |
|
line_num = context.new_ln |
|
filename = context.new_fn |
|
else: |
|
line_num = context.old_ln |
|
filename = context.old_fn |
|
with self.app.db.getSession() as session: |
|
revision = session.getRevision(revision_key) |
|
comment = revision.createComment(None, None, |
|
datetime.datetime.utcnow(), |
|
None, filename, parent, |
|
line_num, text, pending=True) |
|
key = comment.key |
|
return key
|
|
|