[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [nyx/master] Rewrite backlog handling
commit b6908b1fa5092eab691b261e06be34df1e1d3eb7
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sat Jul 23 10:50:07 2016 -0700
Rewrite backlog handling
Mutable function globals are code stink. They break thread safety and are just
about never the right thing to do. The arm 1.4.5 HistoryValidator class was a
lot closer to what we want...
https://gitweb.torproject.org/nyx.git/tree/src/util/textInput.py?h=release#n103
Cleaning that up and replacing our _handle_history_key() with it. This is
invisible to callers (they're both private helpers). Improving our tests along
the way too.
---
nyx/curses.py | 86 +++++++++++++++++++++++++------------------------------
test/subwindow.py | 48 +++++++++++++++----------------
2 files changed, 63 insertions(+), 71 deletions(-)
diff --git a/nyx/curses.py b/nyx/curses.py
index ecc2644..b33abd3 100644
--- a/nyx/curses.py
+++ b/nyx/curses.py
@@ -150,8 +150,6 @@ SPECIAL_KEYS = {
Dimensions = collections.namedtuple('Dimensions', ['width', 'height'])
-HISTORY_DICT = {'selection_index': -1, 'custom_input': ''}
-
def conf_handler(key, value):
if key == 'features.colorOverride':
@@ -286,7 +284,7 @@ def str_input(x, y, initial_text = '', backlog = None, tab_completion = None):
handler = _handle_key
if backlog:
- handler = functools.partial(_handle_history_key, handler, backlog)
+ handler = functools.partial(_TextBacklog(backlog)._handler, handler)
if tab_completion:
handler = functools.partial(_handle_tab_completion, handler, tab_completion)
@@ -335,50 +333,6 @@ def _handle_key(textbox, key):
return key
-def _handle_history_key(next_handler, backlog, textbox, key):
- """
- Allows user to select previous inputs when pressing up/down.
-
- :param func next_handler: handler to invoke after this
- :param list backlog: backlog of all previous commands
- :param Textbox textbox: current textbox context
- :param int key: key pressed
-
- :returns: **None** if up/down is pressed, otherwise invokes next handler
- """
-
- global HISTORY_DICT
-
- if key in (curses.KEY_UP, curses.KEY_DOWN):
- offset = 1 if key == curses.KEY_UP else -1
- new_selection = HISTORY_DICT['selection_index'] + offset
-
- new_selection = max(-1, new_selection)
- new_selection = min(len(backlog) - 1, new_selection)
-
- if HISTORY_DICT['selection_index'] == new_selection:
- return None
-
- if HISTORY_DICT['selection_index'] == -1:
- HISTORY_DICT['custom_input'] = textbox.gather().strip()
-
- if new_selection == -1:
- new_input = HISTORY_DICT['custom_input']
- else:
- new_input = backlog[new_selection]
-
- y, _ = textbox.win.getyx()
- _, max_x = textbox.win.getmaxyx()
- textbox.win.clear()
- textbox.win.addstr(y, 0, new_input[:max_x - 1])
- textbox.win.move(y, min(len(new_input), max_x - 1))
-
- HISTORY_DICT['selection_index'] = new_selection
- return None
-
- return next_handler(textbox, key)
-
-
def _handle_tab_completion(next_handler, tab_completion, textbox, key):
"""
Allows user to tab complete commands if sufficient context is provided to
@@ -419,6 +373,44 @@ def _handle_tab_completion(next_handler, tab_completion, textbox, key):
return next_handler(textbox, key)
+class _TextBacklog(object):
+ """
+ History backlog that allows the :func:`~nyx.curses.str_input` function to
+ scroll through prior inputs when pressing the up and down arrow keys.
+ """
+
+ def __init__(self, backlog = []):
+ self._backlog = backlog # backlog contents, newest to oldest
+ self._selection = -1 # selected item, -1 if we're not on the backlog
+ self._custom_input = '' # field's input prior to selecting a backlog item
+
+ def _handler(self, next_handler, textbox, key):
+ if key in (curses.KEY_UP, curses.KEY_DOWN):
+ if key == curses.KEY_UP:
+ new_selection = min(len(self._backlog) - 1, self._selection + 1)
+ else:
+ new_selection = max(-1, self._selection - 1)
+
+ if self._selection == new_selection:
+ return None
+
+ if self._selection == -1:
+ self._custom_input = textbox.gather().strip() # save custom input
+
+ new_input = self._custom_input if new_selection == -1 else self._backlog[new_selection]
+
+ y, _ = textbox.win.getyx()
+ _, max_x = textbox.win.getmaxyx()
+ textbox.win.clear()
+ textbox.win.addstr(y, 0, new_input[:max_x - 1])
+ textbox.win.move(y, min(len(new_input), max_x - 1))
+ self._selection = new_selection
+
+ return None
+
+ return next_handler(textbox, key)
+
+
def curses_attr(*attributes):
"""
Provides encoding for the given curses text attributes.
diff --git a/test/subwindow.py b/test/subwindow.py
index b72aeb7..2df946e 100644
--- a/test/subwindow.py
+++ b/test/subwindow.py
@@ -174,30 +174,6 @@ class TestCurses(unittest.TestCase):
def test_handle_key_when_resized(self):
self.assertEqual(curses.ascii.BEL, nyx.curses._handle_key(_textbox(), 410))
- def test_handle_history_key(self):
- backlog = ['GETINFO version']
-
- textbox = Mock()
- textbox.win.getyx.return_value = DIMENSIONS
- self.assertIsNone(nyx.curses._handle_history_key(NO_OP_HANDLER, [], textbox, curses.KEY_UP))
-
- textbox = Mock()
- textbox.win.getyx.return_value = DIMENSIONS
- textbox.win.getmaxyx.return_value = DIMENSIONS
- textbox.win.addstr = Mock()
- textbox.win.move = Mock()
- nyx.curses._handle_history_key(NO_OP_HANDLER, backlog, textbox, curses.KEY_UP)
- self.assertTrue(textbox.win.clear.called)
- expected_addstr_call = call(DIMENSIONS[0], 0, backlog[0])
- self.assertEqual(expected_addstr_call, textbox.win.addstr.call_args)
- expected_move_call = call(DIMENSIONS[0], len(backlog[0]))
- self.assertEqual(expected_move_call, textbox.win.move.call_args)
-
- textbox = Mock()
- mock_handle_key = Mock()
- nyx.curses._handle_history_key(mock_handle_key, [], textbox, curses.KEY_LEFT)
- self.assertTrue(mock_handle_key.called)
-
def test_handle_tab_completion_no_op(self):
tab_completion = lambda txt_input: ['GETINFO version']
result = nyx.curses._handle_tab_completion(NO_OP_HANDLER, tab_completion, _textbox(), ord('a'))
@@ -228,3 +204,27 @@ class TestCurses(unittest.TestCase):
self.assertEqual(None, result) # consumes input
self.assertEquals(call(0, 8), textbox.win.move.call_args) # move cursor to end
self.assertEqual(call(0, 0, 'GETINFO '), textbox.win.addstr.call_args)
+
+ def test_text_backlog_no_op(self):
+ backlog = nyx.curses._TextBacklog(['GETINFO version'])
+ textbox = _textbox()
+
+ self.assertEqual(ord('a'), backlog._handler(NO_OP_HANDLER, textbox, ord('a')))
+ self.assertFalse(textbox.win.addstr.called)
+
+ def test_text_backlog_fills_history(self):
+ backlog = nyx.curses._TextBacklog(['GETINFO version'])
+ textbox = _textbox()
+
+ self.assertEqual(None, backlog._handler(NO_OP_HANDLER, textbox, curses.KEY_UP))
+ self.assertEqual(call(0, 0, 'GETINFO version'), textbox.win.addstr.call_args)
+
+ def test_text_backlog_remembers_custom_input(self):
+ backlog = nyx.curses._TextBacklog(['GETINFO version'])
+ textbox = _textbox(text = 'hello')
+
+ self.assertEqual(None, backlog._handler(NO_OP_HANDLER, textbox, curses.KEY_UP))
+ self.assertEqual(call(0, 0, 'GETINFO version'), textbox.win.addstr.call_args)
+
+ self.assertEqual(None, backlog._handler(NO_OP_HANDLER, textbox, curses.KEY_DOWN))
+ self.assertEqual(call(0, 0, 'hello'), textbox.win.addstr.call_args)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits