Add a simple kill ring
Create a global app kill ring and a custom edit widget that can kill and yank from it. Also, add Emacs beginning/end of line keys to the default keymap. Change-Id: I18d8d47694c89ede4dcec7eaf5d3fb2210ef4438
This commit is contained in:
parent
b0f595b1c1
commit
c8d81b7693
@ -141,7 +141,8 @@ class SearchDialog(mywid.ButtonDialog):
|
||||
entry_prompt="Search: ",
|
||||
entry_text=default,
|
||||
buttons=[search_button,
|
||||
cancel_button])
|
||||
cancel_button],
|
||||
ring=app.ring)
|
||||
|
||||
def keypress(self, size, key):
|
||||
r = super(SearchDialog, self).keypress(size, key)
|
||||
@ -207,6 +208,7 @@ class App(object):
|
||||
self.log = logging.getLogger('gertty.App')
|
||||
self.log.debug("Starting")
|
||||
|
||||
self.ring = mywid.KillRing()
|
||||
webbrowser.register('xdg-open', None, BackgroundBrowser("xdg-open"))
|
||||
|
||||
self.fetch_missing_refs = fetch_missing_refs
|
||||
|
@ -29,6 +29,9 @@ CURSOR_PAGE_DOWN = urwid.CURSOR_PAGE_DOWN
|
||||
CURSOR_MAX_LEFT = urwid.CURSOR_MAX_LEFT
|
||||
CURSOR_MAX_RIGHT = urwid.CURSOR_MAX_RIGHT
|
||||
ACTIVATE = urwid.ACTIVATE
|
||||
KILL = 'kill'
|
||||
YANK = 'yank'
|
||||
YANK_POP = 'yank pop'
|
||||
# Global gertty commands:
|
||||
PREV_SCREEN = 'previous screen'
|
||||
TOP_SCREEN = 'top screen'
|
||||
@ -79,9 +82,12 @@ DEFAULT_KEYMAP = {
|
||||
CURSOR_RIGHT: 'right',
|
||||
CURSOR_PAGE_UP: 'page up',
|
||||
CURSOR_PAGE_DOWN: 'page down',
|
||||
CURSOR_MAX_LEFT: 'home',
|
||||
CURSOR_MAX_RIGHT: 'end',
|
||||
CURSOR_MAX_LEFT: ['home', 'ctrl a'],
|
||||
CURSOR_MAX_RIGHT: ['end', 'ctrl e'],
|
||||
ACTIVATE: 'enter',
|
||||
KILL: 'ctrl k',
|
||||
YANK: 'ctrl y',
|
||||
YANK_POP: 'meta y',
|
||||
|
||||
PREV_SCREEN: 'esc',
|
||||
TOP_SCREEN: 'meta home',
|
||||
@ -136,6 +142,9 @@ URWID_COMMANDS = frozenset((
|
||||
urwid.CURSOR_MAX_LEFT,
|
||||
urwid.CURSOR_MAX_RIGHT,
|
||||
urwid.ACTIVATE,
|
||||
KILL,
|
||||
YANK,
|
||||
YANK_POP,
|
||||
))
|
||||
|
||||
FORMAT_SUBS = (
|
||||
|
@ -74,9 +74,58 @@ class Table(urwid.WidgetWrap):
|
||||
for i, widget in enumerate(cells):
|
||||
self._w.contents[i][0].contents.append((widget, ('pack', None)))
|
||||
|
||||
class KillRing(object):
|
||||
def __init__(self):
|
||||
self.ring = []
|
||||
|
||||
def kill(self, text):
|
||||
self.ring.append(text)
|
||||
|
||||
def yank(self, repeat=False):
|
||||
if not self.ring:
|
||||
return None
|
||||
if repeat:
|
||||
t = self.ring.pop()
|
||||
self.ring.insert(0, t)
|
||||
return self.ring[-1]
|
||||
|
||||
class MyEdit(urwid.Edit):
|
||||
def __init__(self, *args, **kw):
|
||||
self.ring = kw.pop('ring', None)
|
||||
if not self.ring:
|
||||
self.ring = KillRing()
|
||||
self.last_yank = None
|
||||
super(MyEdit, self).__init__(*args, **kw)
|
||||
|
||||
def keypress(self, size, key):
|
||||
(maxcol,) = size
|
||||
if self._command_map[key] == keymap.YANK:
|
||||
text = self.ring.yank()
|
||||
if text:
|
||||
self.last_yank = (self.edit_pos, self.edit_pos+len(text))
|
||||
self.insert_text(text)
|
||||
return
|
||||
if self._command_map[key] == keymap.YANK_POP:
|
||||
if not self.last_yank:
|
||||
return
|
||||
text = self.ring.yank(True)
|
||||
if text:
|
||||
self.edit_text = (self.edit_text[:self.last_yank[0]] +
|
||||
self.edit_text[self.last_yank[1]:])
|
||||
self.last_yank = (self.edit_pos, self.edit_pos+len(text))
|
||||
self.insert_text(text)
|
||||
return
|
||||
self.last_yank = None
|
||||
if self._command_map[key] == keymap.KILL:
|
||||
text = self.edit_text[self.edit_pos:]
|
||||
self.edit_text = self.edit_text[:self.edit_pos]
|
||||
self.ring.kill(text)
|
||||
return super(MyEdit, self).keypress(size, key)
|
||||
|
||||
@mouse_scroll_decorator.ScrollByWheel
|
||||
class ButtonDialog(urwid.WidgetWrap):
|
||||
def __init__(self, title, message, entry_prompt=None, entry_text='', buttons=[]):
|
||||
def __init__(self, title, message, entry_prompt=None,
|
||||
entry_text='', buttons=[], ring=None):
|
||||
button_widgets = []
|
||||
for button in buttons:
|
||||
button_widgets.append(('pack', button))
|
||||
@ -84,7 +133,7 @@ class ButtonDialog(urwid.WidgetWrap):
|
||||
rows = []
|
||||
rows.append(urwid.Text(message))
|
||||
if entry_prompt:
|
||||
self.entry = urwid.Edit(entry_prompt, edit_text=entry_text)
|
||||
self.entry = MyEdit(entry_prompt, edit_text=entry_text, ring=ring)
|
||||
rows.append(self.entry)
|
||||
else:
|
||||
self.entry = None
|
||||
@ -95,7 +144,7 @@ class ButtonDialog(urwid.WidgetWrap):
|
||||
|
||||
class TextEditDialog(urwid.WidgetWrap):
|
||||
signals = ['save', 'cancel']
|
||||
def __init__(self, title, prompt, button, text):
|
||||
def __init__(self, title, prompt, button, text, ring=None):
|
||||
save_button = FixedButton(button)
|
||||
cancel_button = FixedButton('Cancel')
|
||||
urwid.connect_signal(save_button, 'click',
|
||||
@ -106,7 +155,7 @@ class TextEditDialog(urwid.WidgetWrap):
|
||||
('pack', cancel_button)]
|
||||
button_columns = urwid.Columns(button_widgets, dividechars=2)
|
||||
rows = []
|
||||
self.entry = urwid.Edit(edit_text=text, multiline=True)
|
||||
self.entry = MyEdit(edit_text=text, multiline=True, ring=ring)
|
||||
rows.append(urwid.Text(prompt))
|
||||
rows.append(self.entry)
|
||||
rows.append(urwid.Divider())
|
||||
|
@ -43,7 +43,8 @@ class EditTopicDialog(mywid.ButtonDialog):
|
||||
entry_prompt="Topic: ",
|
||||
entry_text=topic,
|
||||
buttons=[save_button,
|
||||
cancel_button])
|
||||
cancel_button],
|
||||
ring=app.ring)
|
||||
|
||||
def keypress(self, size, key):
|
||||
r = super(EditTopicDialog, self).keypress(size, key)
|
||||
@ -55,7 +56,7 @@ class EditTopicDialog(mywid.ButtonDialog):
|
||||
|
||||
class CherryPickDialog(urwid.WidgetWrap):
|
||||
signals = ['save', 'cancel']
|
||||
def __init__(self, change):
|
||||
def __init__(self, app, change):
|
||||
save_button = mywid.FixedButton('Propose Change')
|
||||
cancel_button = mywid.FixedButton('Cancel')
|
||||
urwid.connect_signal(save_button, 'click',
|
||||
@ -66,7 +67,8 @@ class CherryPickDialog(urwid.WidgetWrap):
|
||||
('pack', cancel_button)]
|
||||
button_columns = urwid.Columns(button_widgets, dividechars=2)
|
||||
rows = []
|
||||
self.entry = urwid.Edit(edit_text=change.revisions[-1].message, multiline=True)
|
||||
self.entry = mywid.MyEdit(edit_text=change.revisions[-1].message,
|
||||
multiline=True, ring=app.ring)
|
||||
self.branch_buttons = []
|
||||
rows.append(urwid.Text(u"Branch:"))
|
||||
for branch in change.project.branches:
|
||||
@ -166,7 +168,8 @@ class ReviewDialog(urwid.WidgetWrap):
|
||||
m = revision.getDraftMessage()
|
||||
if m:
|
||||
message = m.message
|
||||
self.message = urwid.Edit("Message: \n", edit_text=message, multiline=True)
|
||||
self.message = mywid.MyEdit("Message: \n", edit_text=message,
|
||||
multiline=True, ring=app.ring)
|
||||
rows.append(self.message)
|
||||
rows.append(urwid.Divider())
|
||||
rows.append(buttons)
|
||||
@ -956,7 +959,7 @@ class ChangeView(urwid.WidgetWrap):
|
||||
def cherryPickChange(self):
|
||||
with self.app.db.getSession() as session:
|
||||
change = session.getChange(self.change_key)
|
||||
dialog = CherryPickDialog(change)
|
||||
dialog = CherryPickDialog(self.app, change)
|
||||
urwid.connect_signal(dialog, 'cancel', self.app.backScreen)
|
||||
urwid.connect_signal(dialog, 'save', lambda button:
|
||||
self.doCherryPickChange(dialog))
|
||||
|
@ -15,6 +15,7 @@
|
||||
import urwid
|
||||
|
||||
from gertty import keymap
|
||||
from gertty import mywid
|
||||
from gertty.view.diff import BaseDiffComment, BaseDiffCommentEdit, BaseDiffLine
|
||||
from gertty.view.diff import BaseFileHeader, BaseFileReminder, BaseDiffView
|
||||
|
||||
@ -28,8 +29,8 @@ class SideDiffCommentEdit(BaseDiffCommentEdit):
|
||||
# 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.old = mywid.MyEdit(edit_text=old, multiline=True, ring=app.ring)
|
||||
self.new = mywid.MyEdit(edit_text=new, multiline=True, ring=app.ring)
|
||||
self.contents.append((urwid.Text(u''), ('given', LN_COL_WIDTH, False)))
|
||||
if context.old_file_key and (context.old_ln is not None or context.header):
|
||||
self.contents.append((urwid.AttrMap(self.old, 'draft-comment'), ('weight', 1, False)))
|
||||
|
@ -16,19 +16,21 @@
|
||||
import urwid
|
||||
|
||||
from gertty import gitrepo
|
||||
from gertty import mywid
|
||||
from gertty.view.diff import BaseDiffCommentEdit, BaseDiffComment, BaseDiffLine
|
||||
from gertty.view.diff import BaseFileHeader, BaseFileReminder, BaseDiffView
|
||||
|
||||
LN_COL_WIDTH = 5
|
||||
|
||||
class UnifiedDiffCommentEdit(BaseDiffCommentEdit):
|
||||
def __init__(self, context, oldnew, key=None, comment=u''):
|
||||
def __init__(self, app, 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.comment = mywid.MyEdit(edit_text=comment, multiline=True,
|
||||
ring=app.ring)
|
||||
self.contents.append((urwid.Text(u''), ('given', 8, False)))
|
||||
self.contents.append((urwid.AttrMap(self.comment, 'draft-comment'),
|
||||
('weight', 1, False)))
|
||||
@ -135,7 +137,8 @@ class UnifiedDiffView(BaseDiffView):
|
||||
old_list = comment_lists.pop(key, [])
|
||||
while old_list:
|
||||
(old_comment_key, old_comment) = old_list.pop(0)
|
||||
lines.append(UnifiedDiffCommentEdit(context,
|
||||
lines.append(UnifiedDiffCommentEdit(self.app,
|
||||
context,
|
||||
gitrepo.OLD,
|
||||
old_comment_key,
|
||||
old_comment))
|
||||
@ -154,7 +157,8 @@ class UnifiedDiffView(BaseDiffView):
|
||||
new_list = comment_lists.pop(key, [])
|
||||
while new_list:
|
||||
(new_comment_key, new_comment) = new_list.pop(0)
|
||||
lines.append(UnifiedDiffCommentEdit(context,
|
||||
lines.append(UnifiedDiffCommentEdit(self.app,
|
||||
context,
|
||||
gitrepo.NEW,
|
||||
new_comment_key,
|
||||
new_comment))
|
||||
@ -180,7 +184,8 @@ class UnifiedDiffView(BaseDiffView):
|
||||
old_list = comment_lists.pop(key, [])
|
||||
while old_list:
|
||||
(old_comment_key, old_comment) = old_list.pop(0)
|
||||
lines.append(UnifiedDiffCommentEdit(context,
|
||||
lines.append(UnifiedDiffCommentEdit(self.app,
|
||||
context,
|
||||
gitrepo.OLD,
|
||||
old_comment_key,
|
||||
old_comment))
|
||||
@ -200,14 +205,16 @@ class UnifiedDiffView(BaseDiffView):
|
||||
new_list = comment_lists.pop(key, [])
|
||||
while new_list:
|
||||
(new_comment_key, new_comment) = new_list.pop(0)
|
||||
lines.append(UnifiedDiffCommentEdit(context,
|
||||
lines.append(UnifiedDiffCommentEdit(self.app,
|
||||
context,
|
||||
gitrepo.NEW,
|
||||
new_comment_key,
|
||||
new_comment))
|
||||
return lines
|
||||
|
||||
def makeCommentEdit(self, edit):
|
||||
return UnifiedDiffCommentEdit(edit.context,
|
||||
return UnifiedDiffCommentEdit(self.app,
|
||||
edit.context,
|
||||
edit.oldnew)
|
||||
|
||||
def cleanupEdit(self, edit):
|
||||
|
Loading…
x
Reference in New Issue
Block a user