Make change list searchable
Change-Id: I5edc1a9082dc81dfd06dc00f42abf05f0b03dc26
This commit is contained in:
parent
7a149db6cf
commit
1f25c9b450
@ -290,6 +290,62 @@ class SearchableText(urwid.Text):
|
|||||||
self._invalidate()
|
self._invalidate()
|
||||||
return found
|
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):
|
class HyperText(urwid.Text):
|
||||||
_selectable = True
|
_selectable = True
|
||||||
|
|
||||||
|
@ -111,13 +111,13 @@ class ChangeRow(urwid.Button, ChangeListColumns):
|
|||||||
self.change_key = change.key
|
self.change_key = change.key
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.enabled_columns = enabled_columns
|
self.enabled_columns = enabled_columns
|
||||||
self.subject = urwid.Text(u'', wrap='clip')
|
self.subject = mywid.SearchableText(u'', wrap='clip')
|
||||||
self.number = urwid.Text(u'')
|
self.number = mywid.SearchableText(u'')
|
||||||
self.updated = urwid.Text(u'')
|
self.updated = mywid.SearchableText(u'')
|
||||||
self.project = urwid.Text(u'', wrap='clip')
|
self.project = mywid.SearchableText(u'', wrap='clip')
|
||||||
self.owner = urwid.Text(u'', wrap='clip')
|
self.owner = mywid.SearchableText(u'', wrap='clip')
|
||||||
self.branch = urwid.Text(u'', wrap='clip')
|
self.branch = mywid.SearchableText(u'', wrap='clip')
|
||||||
self.topic = urwid.Text(u'', wrap='clip')
|
self.topic = mywid.SearchableText(u'', wrap='clip')
|
||||||
self.mark = False
|
self.mark = False
|
||||||
self.columns = urwid.Columns([], dividechars=1)
|
self.columns = urwid.Columns([], dividechars=1)
|
||||||
self.row_style = urwid.AttrMap(self.columns, '')
|
self.row_style = urwid.AttrMap(self.columns, '')
|
||||||
@ -125,6 +125,23 @@ class ChangeRow(urwid.Button, ChangeListColumns):
|
|||||||
self.category_columns = []
|
self.category_columns = []
|
||||||
self.update(change, categories)
|
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):
|
def update(self, change, categories):
|
||||||
if change.reviewed or change.hidden:
|
if change.reviewed or change.hidden:
|
||||||
style = 'reviewed-change'
|
style = 'reviewed-change'
|
||||||
@ -181,7 +198,6 @@ class ChangeRow(urwid.Button, ChangeListColumns):
|
|||||||
self.columns.options('given', 2)))
|
self.columns.options('given', 2)))
|
||||||
self.updateColumns()
|
self.updateColumns()
|
||||||
|
|
||||||
|
|
||||||
class ChangeListHeader(urwid.WidgetWrap, ChangeListColumns):
|
class ChangeListHeader(urwid.WidgetWrap, ChangeListColumns):
|
||||||
def __init__(self, enabled_columns):
|
def __init__(self, enabled_columns):
|
||||||
self.enabled_columns = enabled_columns
|
self.enabled_columns = enabled_columns
|
||||||
@ -205,7 +221,7 @@ class ChangeListHeader(urwid.WidgetWrap, ChangeListColumns):
|
|||||||
|
|
||||||
|
|
||||||
@mouse_scroll_decorator.ScrollByWheel
|
@mouse_scroll_decorator.ScrollByWheel
|
||||||
class ChangeListView(urwid.WidgetWrap):
|
class ChangeListView(urwid.WidgetWrap, mywid.Searchable):
|
||||||
required_columns = set(['Number', 'Subject', 'Updated'])
|
required_columns = set(['Number', 'Subject', 'Updated'])
|
||||||
optional_columns = set(['Topic', 'Branch'])
|
optional_columns = set(['Topic', 'Branch'])
|
||||||
|
|
||||||
@ -249,6 +265,8 @@ class ChangeListView(urwid.WidgetWrap):
|
|||||||
"Reverse the sort"),
|
"Reverse the sort"),
|
||||||
(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"),
|
||||||
|
(keymap.INTERACTIVE_SEARCH,
|
||||||
|
"Interactive search"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
@ -260,6 +278,7 @@ class ChangeListView(urwid.WidgetWrap):
|
|||||||
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([]))
|
||||||
self.log = logging.getLogger('gertty.view.change_list')
|
self.log = logging.getLogger('gertty.view.change_list')
|
||||||
|
self.searchInit()
|
||||||
self.app = app
|
self.app = app
|
||||||
self.query = query
|
self.query = query
|
||||||
self.query_desc = query_desc or query
|
self.query_desc = query_desc or query
|
||||||
@ -521,6 +540,9 @@ class ChangeListView(urwid.WidgetWrap):
|
|||||||
self.listbox.focus_position = pos
|
self.listbox.focus_position = pos
|
||||||
|
|
||||||
def keypress(self, size, key):
|
def keypress(self, size, key):
|
||||||
|
if self.searchKeypress(size, key):
|
||||||
|
return None
|
||||||
|
|
||||||
if not self.app.input_buffer:
|
if not self.app.input_buffer:
|
||||||
key = super(ChangeListView, self).keypress(size, key)
|
key = super(ChangeListView, self).keypress(size, key)
|
||||||
keys = self.app.input_buffer + [key]
|
keys = self.app.input_buffer + [key]
|
||||||
@ -681,6 +703,9 @@ class ChangeListView(urwid.WidgetWrap):
|
|||||||
if keymap.RESTORE_CHANGE in commands:
|
if keymap.RESTORE_CHANGE in commands:
|
||||||
self.restoreChange()
|
self.restoreChange()
|
||||||
return True
|
return True
|
||||||
|
if keymap.INTERACTIVE_SEARCH in commands:
|
||||||
|
self.searchStart()
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def onSelect(self, button, change_key):
|
def onSelect(self, button, change_key):
|
||||||
|
@ -156,7 +156,7 @@ class DiffContextButton(urwid.WidgetWrap):
|
|||||||
self.view.expandChunk(self.diff, self.chunk, from_end=-10)
|
self.view.expandChunk(self.diff, self.chunk, from_end=-10)
|
||||||
|
|
||||||
@mouse_scroll_decorator.ScrollByWheel
|
@mouse_scroll_decorator.ScrollByWheel
|
||||||
class BaseDiffView(urwid.WidgetWrap):
|
class BaseDiffView(urwid.WidgetWrap, mywid.Searchable):
|
||||||
def getCommands(self):
|
def getCommands(self):
|
||||||
return [
|
return [
|
||||||
(keymap.ACTIVATE,
|
(keymap.ACTIVATE,
|
||||||
@ -182,9 +182,7 @@ class BaseDiffView(urwid.WidgetWrap):
|
|||||||
|
|
||||||
def _init(self):
|
def _init(self):
|
||||||
del self._w.contents[:]
|
del self._w.contents[:]
|
||||||
self.search = None
|
self.searchInit()
|
||||||
self.results = []
|
|
||||||
self.current_result = None
|
|
||||||
with self.app.db.getSession() as session:
|
with self.app.db.getSession() as session:
|
||||||
new_revision = session.getRevision(self.new_revision_key)
|
new_revision = session.getRevision(self.new_revision_key)
|
||||||
old_comments = []
|
old_comments = []
|
||||||
@ -443,30 +441,9 @@ class BaseDiffView(urwid.WidgetWrap):
|
|||||||
context = item.context
|
context = item.context
|
||||||
return 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):
|
def keypress(self, size, key):
|
||||||
if self.search is not None:
|
if self.searchKeypress(size, key):
|
||||||
if self.search_valid_char(key) or key == 'backspace':
|
return None
|
||||||
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
|
|
||||||
|
|
||||||
old_focus = self.listbox.focus
|
old_focus = self.listbox.focus
|
||||||
if not self.app.input_buffer:
|
if not self.app.input_buffer:
|
||||||
@ -489,8 +466,7 @@ class BaseDiffView(urwid.WidgetWrap):
|
|||||||
self.openPatchsetDialog()
|
self.openPatchsetDialog()
|
||||||
return None
|
return None
|
||||||
if keymap.INTERACTIVE_SEARCH in commands:
|
if keymap.INTERACTIVE_SEARCH in commands:
|
||||||
self.search = ''
|
self.searchStart()
|
||||||
self.app.status.update(title=("Search: "))
|
|
||||||
return None
|
return None
|
||||||
return key
|
return key
|
||||||
|
|
||||||
@ -561,23 +537,3 @@ class BaseDiffView(urwid.WidgetWrap):
|
|||||||
self.app.backScreen()
|
self.app.backScreen()
|
||||||
self.old_revision_key, self.new_revision_key = dialog.getSelected()
|
self.old_revision_key, self.new_revision_key = dialog.getSelected()
|
||||||
self._init()
|
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
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user