Make change list searchable

Change-Id: I5edc1a9082dc81dfd06dc00f42abf05f0b03dc26
This commit is contained in:
James E. Blair 2016-04-29 19:55:18 -05:00
parent 7a149db6cf
commit 1f25c9b450
3 changed files with 95 additions and 58 deletions

View File

@ -290,6 +290,62 @@ class SearchableText(urwid.Text):
self._invalidate()
return found
class Searchable(object):
def searchInit(self):
self.search = None
self.results = []
self.current_result = 0
def searchValidChar(self, ch):
return urwid.util.is_wide_char(ch, 0) or (len(ch) == 1 and ord(ch) >= 32)
def searchKeypress(self, size, key):
if self.search is not None:
if self.searchValidChar(key) or key == 'backspace':
if key == 'backspace':
self.search = self.search[:-1]
else:
self.search += key
self.interactiveSearch(self.search)
return True
else:
commands = self.app.config.keymap.getCommands([key])
if keymap.INTERACTIVE_SEARCH in commands:
self.nextSearchResult()
return True
else:
self.app.status.update(title=self.title)
if not self.search:
self.interactiveSearch(None)
self.search = None
if key in ['enter', 'esc']:
return True
return False
def searchStart(self):
self.search = ''
self.app.status.update(title=("Search: "))
def interactiveSearch(self, search):
if search is not None:
self.app.status.update(title=("Search: " + search))
self.results = []
self.current_result = 0
for i, line in enumerate(self.listbox.body):
if hasattr(line, 'search'):
if line.search(search, 'search-result'):
self.results.append(i)
def nextSearchResult(self):
if not self.results:
return
dest = self.results[self.current_result]
self.listbox.set_focus(dest)
self.listbox._invalidate()
self.current_result += 1
if self.current_result >= len(self.results):
self.current_result = 0
class HyperText(urwid.Text):
_selectable = True

View File

@ -111,13 +111,13 @@ class ChangeRow(urwid.Button, ChangeListColumns):
self.change_key = change.key
self.prefix = prefix
self.enabled_columns = enabled_columns
self.subject = urwid.Text(u'', wrap='clip')
self.number = urwid.Text(u'')
self.updated = urwid.Text(u'')
self.project = urwid.Text(u'', wrap='clip')
self.owner = urwid.Text(u'', wrap='clip')
self.branch = urwid.Text(u'', wrap='clip')
self.topic = urwid.Text(u'', wrap='clip')
self.subject = mywid.SearchableText(u'', wrap='clip')
self.number = mywid.SearchableText(u'')
self.updated = mywid.SearchableText(u'')
self.project = mywid.SearchableText(u'', wrap='clip')
self.owner = mywid.SearchableText(u'', wrap='clip')
self.branch = mywid.SearchableText(u'', wrap='clip')
self.topic = mywid.SearchableText(u'', wrap='clip')
self.mark = False
self.columns = urwid.Columns([], dividechars=1)
self.row_style = urwid.AttrMap(self.columns, '')
@ -125,6 +125,23 @@ class ChangeRow(urwid.Button, ChangeListColumns):
self.category_columns = []
self.update(change, categories)
def search(self, search, attribute):
if self.subject.search(search, attribute):
return True
if self.number.search(search, attribute):
return True
if self.project.search(search, attribute):
return True
if self.branch.search(search, attribute):
return True
if self.owner.search(search, attribute):
return True
if self.topic.search(search, attribute):
return True
if self.updated.search(search, attribute):
return True
return False
def update(self, change, categories):
if change.reviewed or change.hidden:
style = 'reviewed-change'
@ -181,7 +198,6 @@ class ChangeRow(urwid.Button, ChangeListColumns):
self.columns.options('given', 2)))
self.updateColumns()
class ChangeListHeader(urwid.WidgetWrap, ChangeListColumns):
def __init__(self, enabled_columns):
self.enabled_columns = enabled_columns
@ -205,7 +221,7 @@ class ChangeListHeader(urwid.WidgetWrap, ChangeListColumns):
@mouse_scroll_decorator.ScrollByWheel
class ChangeListView(urwid.WidgetWrap):
class ChangeListView(urwid.WidgetWrap, mywid.Searchable):
required_columns = set(['Number', 'Subject', 'Updated'])
optional_columns = set(['Topic', 'Branch'])
@ -249,6 +265,8 @@ class ChangeListView(urwid.WidgetWrap):
"Reverse the sort"),
(keymap.LOCAL_CHERRY_PICK,
"Cherry-pick the most recent revision of the selected change onto the local repo"),
(keymap.INTERACTIVE_SEARCH,
"Interactive search"),
]
def help(self):
@ -260,6 +278,7 @@ class ChangeListView(urwid.WidgetWrap):
unreviewed=False, sort_by=None, reverse=None):
super(ChangeListView, self).__init__(urwid.Pile([]))
self.log = logging.getLogger('gertty.view.change_list')
self.searchInit()
self.app = app
self.query = query
self.query_desc = query_desc or query
@ -521,6 +540,9 @@ class ChangeListView(urwid.WidgetWrap):
self.listbox.focus_position = pos
def keypress(self, size, key):
if self.searchKeypress(size, key):
return None
if not self.app.input_buffer:
key = super(ChangeListView, self).keypress(size, key)
keys = self.app.input_buffer + [key]
@ -681,6 +703,9 @@ class ChangeListView(urwid.WidgetWrap):
if keymap.RESTORE_CHANGE in commands:
self.restoreChange()
return True
if keymap.INTERACTIVE_SEARCH in commands:
self.searchStart()
return True
return False
def onSelect(self, button, change_key):

View File

@ -156,7 +156,7 @@ class DiffContextButton(urwid.WidgetWrap):
self.view.expandChunk(self.diff, self.chunk, from_end=-10)
@mouse_scroll_decorator.ScrollByWheel
class BaseDiffView(urwid.WidgetWrap):
class BaseDiffView(urwid.WidgetWrap, mywid.Searchable):
def getCommands(self):
return [
(keymap.ACTIVATE,
@ -182,9 +182,7 @@ class BaseDiffView(urwid.WidgetWrap):
def _init(self):
del self._w.contents[:]
self.search = None
self.results = []
self.current_result = None
self.searchInit()
with self.app.db.getSession() as session:
new_revision = session.getRevision(self.new_revision_key)
old_comments = []
@ -443,30 +441,9 @@ class BaseDiffView(urwid.WidgetWrap):
context = item.context
return context
def search_valid_char(self, ch):
return urwid.util.is_wide_char(ch, 0) or (len(ch) == 1 and ord(ch) >= 32)
def keypress(self, size, key):
if self.search is not None:
if self.search_valid_char(key) or key == 'backspace':
if key == 'backspace':
self.search = self.search[:-1]
else:
self.search += key
self.interactiveSearch(self.search)
return None
else:
commands = self.app.config.keymap.getCommands([key])
if keymap.INTERACTIVE_SEARCH in commands:
self.nextSearchResult()
return None
else:
self.app.status.update(title=self.title)
if not self.search:
self.interactiveSearch(None)
self.search = None
if key in ['enter', 'esc']:
return None
if self.searchKeypress(size, key):
return None
old_focus = self.listbox.focus
if not self.app.input_buffer:
@ -489,8 +466,7 @@ class BaseDiffView(urwid.WidgetWrap):
self.openPatchsetDialog()
return None
if keymap.INTERACTIVE_SEARCH in commands:
self.search = ''
self.app.status.update(title=("Search: "))
self.searchStart()
return None
return key
@ -561,23 +537,3 @@ class BaseDiffView(urwid.WidgetWrap):
self.app.backScreen()
self.old_revision_key, self.new_revision_key = dialog.getSelected()
self._init()
def interactiveSearch(self, search):
if search is not None:
self.app.status.update(title=("Search: " + search))
self.results = []
self.current_result = 0
for i, line in enumerate(self.listbox.body):
if hasattr(line, 'search'):
if line.search(search, 'search-result'):
self.results.append(i)
def nextSearchResult(self):
if not self.results:
return
dest = self.results[self.current_result]
self.listbox.set_focus(dest)
self.listbox._invalidate()
self.current_result += 1
if self.current_result >= len(self.results):
self.current_result = 0