Show potential completions

When entering multi-key commands, display potential completions
in the status bar as a reminder to the user.

Change-Id: I498781576a60789b0f810f08cbc9c8c74d52784f
This commit is contained in:
James E. Blair 2016-04-29 19:16:29 -05:00
parent 2efa0606f7
commit 7a149db6cf
6 changed files with 120 additions and 69 deletions

@ -465,18 +465,24 @@ class App(object):
self.screens.append(self.frame.body) self.screens.append(self.frame.body)
self.frame.body = overlay self.frame.body = overlay
def getGlobalCommands(self):
return list(mywid.GLOBAL_HELP)
def getGlobalHelp(self):
keys = [(k, self.config.keymap.formatKeys(k), t) for (k, t) in self.getGlobalCommands()]
for d in self.config.dashboards.values():
keys.append(('', d['key'], d['name']))
return keys
def help(self): def help(self):
if not hasattr(self.frame.body, 'help'): if not hasattr(self.frame.body, 'help'):
return return
global_help = [(self.config.keymap.formatKeys(k), t) global_help = self.getGlobalHelp()
for (k, t) in mywid.GLOBAL_HELP]
for d in self.config.dashboards.values():
global_help.append((keymap.formatKey(d['key']), d['name']))
parts = [('Global Keys', global_help), parts = [('Global Keys', global_help),
('This Screen', self.frame.body.help())] ('This Screen', self.frame.body.help())]
keylen = 0 keylen = 0
for title, items in parts: for title, items in parts:
for keys, text in items: for cmd, keys, text in items:
keylen = max(len(keys), keylen) keylen = max(len(keys), keylen)
text = '' text = ''
for title, items in parts: for title, items in parts:
@ -484,7 +490,7 @@ class App(object):
text += '\n' text += '\n'
text += title+'\n' text += title+'\n'
text += '%s\n' % ('='*len(title),) text += '%s\n' % ('='*len(title),)
for keys, cmdtext in items: for cmd, keys, cmdtext in items:
text += '{keys:{width}} {text}\n'.format( text += '{keys:{width}} {text}\n'.format(
keys=keys, width=keylen, text=cmdtext) keys=keys, width=keylen, text=cmdtext)
dialog = mywid.MessageDialog('Help for %s' % version(), text) dialog = mywid.MessageDialog('Help for %s' % version(), text)
@ -675,7 +681,19 @@ class App(object):
self.changeScreen(view) self.changeScreen(view)
elif keymap.FURTHER_INPUT in commands: elif keymap.FURTHER_INPUT in commands:
self.input_buffer.append(key) self.input_buffer.append(key)
self.status.update(message=''.join(self.input_buffer)) msg = ''.join(self.input_buffer)
commands = dict(self.getGlobalCommands())
if hasattr(self.frame.body, 'getCommands'):
commands.update(dict(self.frame.body.getCommands()))
further_commands = self.config.keymap.getFurtherCommands(keys)
completions = []
for (key, cmds) in further_commands:
for cmd in cmds:
if cmd in commands:
completions.append(key)
completions = ' '.join(completions)
msg = '%s: %s' % (msg, completions)
self.status.update(message=msg)
return return
self.clearInputBuffer() self.clearInputBuffer()

@ -250,6 +250,25 @@ class KeyMap(object):
ret.append(FURTHER_INPUT) ret.append(FURTHER_INPUT)
return ret return ret
def getFurtherCommands(self, keys):
if not keys:
return []
tree = self.keytree
for key in keys:
tree = tree.keys.get(key)
if not tree:
return []
return self._getFurtherCommands('', tree)
def _getFurtherCommands(self, keys, tree):
if keys:
ret = [(formatKey(keys), tree.commands[:])]
else:
ret = []
for subtree in tree.keys.values():
ret.extend(self._getFurtherCommands(keys + subtree.key, subtree))
return ret
def getKeys(self, command): def getKeys(self, command):
return self.commandmap.get(command, []) return self.commandmap.get(command, [])

@ -385,55 +385,57 @@ class CommitMessageBox(mywid.HyperText):
@mouse_scroll_decorator.ScrollByWheel @mouse_scroll_decorator.ScrollByWheel
class ChangeView(urwid.WidgetWrap): class ChangeView(urwid.WidgetWrap):
def help(self): def getCommands(self):
key = self.app.config.keymap.formatKeys return [
ret = [ (keymap.LOCAL_CHECKOUT,
(key(keymap.LOCAL_CHECKOUT),
"Checkout the most recent revision into the local repo"), "Checkout the most recent revision into the local repo"),
(key(keymap.DIFF), (keymap.DIFF,
"Show the diff of the most recent revision"), "Show the diff of the most recent revision"),
(key(keymap.TOGGLE_HIDDEN), (keymap.TOGGLE_HIDDEN,
"Toggle the hidden flag for the current change"), "Toggle the hidden flag for the current change"),
(key(keymap.NEXT_CHANGE), (keymap.NEXT_CHANGE,
"Go to the next change in the list"), "Go to the next change in the list"),
(key(keymap.PREV_CHANGE), (keymap.PREV_CHANGE,
"Go to the previous change in the list"), "Go to the previous change in the list"),
(key(keymap.REVIEW), (keymap.REVIEW,
"Leave a review for the most recent revision"), "Leave a review for the most recent revision"),
(key(keymap.TOGGLE_HELD), (keymap.TOGGLE_HELD,
"Toggle the held flag for the current change"), "Toggle the held flag for the current change"),
(key(keymap.TOGGLE_HIDDEN_COMMENTS), (keymap.TOGGLE_HIDDEN_COMMENTS,
"Toggle display of hidden comments"), "Toggle display of hidden comments"),
(key(keymap.SEARCH_RESULTS), (keymap.SEARCH_RESULTS,
"Back to the list of changes"), "Back to the list of changes"),
(key(keymap.TOGGLE_REVIEWED), (keymap.TOGGLE_REVIEWED,
"Toggle the reviewed flag for the current change"), "Toggle the reviewed flag for the current change"),
(key(keymap.TOGGLE_STARRED), (keymap.TOGGLE_STARRED,
"Toggle the starred flag for the current change"), "Toggle the starred flag for the current change"),
(key(keymap.LOCAL_CHERRY_PICK), (keymap.LOCAL_CHERRY_PICK,
"Cherry-pick the most recent revision onto the local repo"), "Cherry-pick the most recent revision onto the local repo"),
(key(keymap.ABANDON_CHANGE), (keymap.ABANDON_CHANGE,
"Abandon this change"), "Abandon this change"),
(key(keymap.EDIT_COMMIT_MESSAGE), (keymap.EDIT_COMMIT_MESSAGE,
"Edit the commit message of this change"), "Edit the commit message of this change"),
(key(keymap.REBASE_CHANGE), (keymap.REBASE_CHANGE,
"Rebase this change (remotely)"), "Rebase this change (remotely)"),
(key(keymap.RESTORE_CHANGE), (keymap.RESTORE_CHANGE,
"Restore this change"), "Restore this change"),
(key(keymap.REFRESH), (keymap.REFRESH,
"Refresh this change"), "Refresh this change"),
(key(keymap.EDIT_TOPIC), (keymap.EDIT_TOPIC,
"Edit the topic of this change"), "Edit the topic of this change"),
(key(keymap.SUBMIT_CHANGE), (keymap.SUBMIT_CHANGE,
"Submit this change"), "Submit this change"),
(key(keymap.CHERRY_PICK_CHANGE), (keymap.CHERRY_PICK_CHANGE,
"Propose this change to another branch"), "Propose this change to another branch"),
] ]
def help(self):
key = self.app.config.keymap.formatKeys
commands = self.getCommands()
ret = [(c[0], key(c[0]), c[1]) for c in commands]
for k in self.app.config.reviewkeys.values(): for k in self.app.config.reviewkeys.values():
action = ', '.join(['{category}:{value}'.format(**a) for a in k['approvals']]) action = ', '.join(['{category}:{value}'.format(**a) for a in k['approvals']])
ret.append((keymap.formatKey(k['key']), action)) ret.append(('', keymap.formatKey(k['key']), action))
return ret return ret
def __init__(self, app, change_key): def __init__(self, app, change_key):

@ -209,49 +209,53 @@ class ChangeListView(urwid.WidgetWrap):
required_columns = set(['Number', 'Subject', 'Updated']) required_columns = set(['Number', 'Subject', 'Updated'])
optional_columns = set(['Topic', 'Branch']) optional_columns = set(['Topic', 'Branch'])
def help(self): def getCommands(self):
key = self.app.config.keymap.formatKeys
if self.project_key: if self.project_key:
refresh_help = "Sync current project" refresh_help = "Sync current project"
else: else:
refresh_help = "Sync subscribed projects" refresh_help = "Sync subscribed projects"
return [ return [
(key(keymap.TOGGLE_HELD), (keymap.TOGGLE_HELD,
"Toggle the held flag for the currently selected change"), "Toggle the held flag for the currently selected change"),
(key(keymap.LOCAL_CHECKOUT), (keymap.LOCAL_CHECKOUT,
"Checkout the most recent revision of the selected change into the local repo"), "Checkout the most recent revision of the selected change into the local repo"),
(key(keymap.TOGGLE_HIDDEN), (keymap.TOGGLE_HIDDEN,
"Toggle the hidden flag for the currently selected change"), "Toggle the hidden flag for the currently selected change"),
(key(keymap.TOGGLE_LIST_REVIEWED), (keymap.TOGGLE_LIST_REVIEWED,
"Toggle whether only unreviewed or all changes are displayed"), "Toggle whether only unreviewed or all changes are displayed"),
(key(keymap.TOGGLE_REVIEWED), (keymap.TOGGLE_REVIEWED,
"Toggle the reviewed flag for the currently selected change"), "Toggle the reviewed flag for the currently selected change"),
(key(keymap.TOGGLE_STARRED), (keymap.TOGGLE_STARRED,
"Toggle the starred flag for the currently selected change"), "Toggle the starred flag for the currently selected change"),
(key(keymap.TOGGLE_MARK), (keymap.TOGGLE_MARK,
"Toggle the process mark for the currently selected change"), "Toggle the process mark for the currently selected change"),
(key(keymap.REFINE_CHANGE_SEARCH), (keymap.REFINE_CHANGE_SEARCH,
"Refine the current search query"), "Refine the current search query"),
(key(keymap.ABANDON_CHANGE), (keymap.ABANDON_CHANGE,
"Abandon the marked changes"), "Abandon the marked changes"),
(key(keymap.EDIT_TOPIC), (keymap.EDIT_TOPIC,
"Set the topic of the marked changes"), "Set the topic of the marked changes"),
(key(keymap.RESTORE_CHANGE), (keymap.RESTORE_CHANGE,
"Restore the marked changes"), "Restore the marked changes"),
(key(keymap.REFRESH), (keymap.REFRESH,
refresh_help), refresh_help),
(key(keymap.REVIEW), (keymap.REVIEW,
"Leave reviews for the marked changes"), "Leave reviews for the marked changes"),
(key(keymap.SORT_BY_NUMBER), (keymap.SORT_BY_NUMBER,
"Sort changes by number"), "Sort changes by number"),
(key(keymap.SORT_BY_UPDATED), (keymap.SORT_BY_UPDATED,
"Sort changes by how recently the change was updated"), "Sort changes by how recently the change was updated"),
(key(keymap.SORT_BY_REVERSE), (keymap.SORT_BY_REVERSE,
"Reverse the sort"), "Reverse the sort"),
(key(keymap.LOCAL_CHERRY_PICK), (keymap.LOCAL_CHERRY_PICK,
"Cherry-pick the most recent revision of the selected change onto the local repo"), "Cherry-pick the most recent revision of the selected change onto the local repo"),
] ]
def help(self):
key = self.app.config.keymap.formatKeys
commands = self.getCommands()
return [(c[0], key(c[0]), c[1]) for c in commands]
def __init__(self, app, query, query_desc=None, project_key=None, def __init__(self, app, query, query_desc=None, project_key=None,
unreviewed=False, sort_by=None, reverse=None): unreviewed=False, sort_by=None, reverse=None):
super(ChangeListView, self).__init__(urwid.Pile([])) super(ChangeListView, self).__init__(urwid.Pile([]))

@ -157,17 +157,21 @@ class DiffContextButton(urwid.WidgetWrap):
@mouse_scroll_decorator.ScrollByWheel @mouse_scroll_decorator.ScrollByWheel
class BaseDiffView(urwid.WidgetWrap): class BaseDiffView(urwid.WidgetWrap):
def help(self): def getCommands(self):
key = self.app.config.keymap.formatKeys
return [ return [
(key(keymap.ACTIVATE), (keymap.ACTIVATE,
"Add an inline comment"), "Add an inline comment"),
(key(keymap.SELECT_PATCHSETS), (keymap.SELECT_PATCHSETS,
"Select old/new patchsets to diff"), "Select old/new patchsets to diff"),
(key(keymap.INTERACTIVE_SEARCH), (keymap.INTERACTIVE_SEARCH,
"Interactive search"), "Interactive search"),
] ]
def help(self):
key = self.app.config.keymap.formatKeys
commands = self.getCommands()
return [(c[0], key(c[0]), c[1]) for c in commands]
def __init__(self, app, new_revision_key): def __init__(self, app, new_revision_key):
super(BaseDiffView, self).__init__(urwid.Pile([])) super(BaseDiffView, self).__init__(urwid.Pile([]))
self.log = logging.getLogger('gertty.view.diff') self.log = logging.getLogger('gertty.view.diff')

@ -199,33 +199,37 @@ class ProjectListHeader(urwid.WidgetWrap):
@mouse_scroll_decorator.ScrollByWheel @mouse_scroll_decorator.ScrollByWheel
class ProjectListView(urwid.WidgetWrap): class ProjectListView(urwid.WidgetWrap):
def help(self): def getCommands(self):
key = self.app.config.keymap.formatKeys
return [ return [
(key(keymap.TOGGLE_LIST_SUBSCRIBED), (keymap.TOGGLE_LIST_SUBSCRIBED,
"Toggle whether only subscribed projects or all projects are listed"), "Toggle whether only subscribed projects or all projects are listed"),
(key(keymap.TOGGLE_LIST_REVIEWED), (keymap.TOGGLE_LIST_REVIEWED,
"Toggle listing of projects with unreviewed changes"), "Toggle listing of projects with unreviewed changes"),
(key(keymap.TOGGLE_SUBSCRIBED), (keymap.TOGGLE_SUBSCRIBED,
"Toggle the subscription flag for the selected project"), "Toggle the subscription flag for the selected project"),
(key(keymap.REFRESH), (keymap.REFRESH,
"Sync subscribed projects"), "Sync subscribed projects"),
(key(keymap.TOGGLE_MARK), (keymap.TOGGLE_MARK,
"Toggle the process mark for the selected project"), "Toggle the process mark for the selected project"),
(key(keymap.NEW_PROJECT_TOPIC), (keymap.NEW_PROJECT_TOPIC,
"Create project topic"), "Create project topic"),
(key(keymap.DELETE_PROJECT_TOPIC), (keymap.DELETE_PROJECT_TOPIC,
"Delete selected project topic"), "Delete selected project topic"),
(key(keymap.MOVE_PROJECT_TOPIC), (keymap.MOVE_PROJECT_TOPIC,
"Move selected project to topic"), "Move selected project to topic"),
(key(keymap.COPY_PROJECT_TOPIC), (keymap.COPY_PROJECT_TOPIC,
"Copy selected project to topic"), "Copy selected project to topic"),
(key(keymap.REMOVE_PROJECT_TOPIC), (keymap.REMOVE_PROJECT_TOPIC,
"Remove selected project from topic"), "Remove selected project from topic"),
(key(keymap.RENAME_PROJECT_TOPIC), (keymap.RENAME_PROJECT_TOPIC,
"Rename selected project topic"), "Rename selected project topic"),
] ]
def help(self):
key = self.app.config.keymap.formatKeys
commands = self.getCommands()
return [(c[0], key(c[0]), c[1]) for c in commands]
def __init__(self, app): def __init__(self, app):
super(ProjectListView, self).__init__(urwid.Pile([])) super(ProjectListView, self).__init__(urwid.Pile([]))
self.log = logging.getLogger('gertty.view.project_list') self.log = logging.getLogger('gertty.view.project_list')