From 2e3004cd1322b8770e1883090b30a53a1e3a16e1 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 13 Feb 2022 09:09:10 +1000 Subject: [PATCH] fill3: Separate keypresses in input - Stdin can contain more than one keypress when inputted quickly. --- fill3/fill3/__init__.py | 31 +++++++++++++++++++++++-------- fill3/tests/fill3_test.py | 13 +++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/fill3/fill3/__init__.py b/fill3/fill3/__init__.py index 32f8036..6531328 100755 --- a/fill3/fill3/__init__.py +++ b/fill3/fill3/__init__.py @@ -494,14 +494,29 @@ async def update_screen(screen_widget): APPEARANCE_CHANGED_EVENT.clear() +def digest_terminal_input(input_str): + cursor = 0 + result = [] + for index, char in enumerate(input_str): + if cursor != index: + if char == terminal.ESC: + result.append(input_str[cursor:index]) + cursor = index + elif ord(char) < 32: + result.extend([input_str[cursor:index], char]) + cursor = index + 1 + if cursor != index + 1: + result.append(input_str[cursor:index+1]) + return result + + @handle_exception def on_terminal_input(screen_widget): - term_code = sys.stdin.read() - if term_code.startswith(terminal.MOUSE): - for part in term_code.split(terminal.MOUSE)[1:]: - screen_widget.on_mouse_input(part) - else: - return screen_widget.on_keyboard_input(term_code) + for part in digest_terminal_input(sys.stdin.read()): + if part.startswith(terminal.MOUSE): + screen_widget.on_mouse_input(part[3:]) + else: + screen_widget.on_keyboard_input(part) @contextlib.contextmanager @@ -513,7 +528,7 @@ def signal_handler(loop, signal_, func): loop.remove_signal_handler(signal_) -async def tui(title, screen_widget): +async def tui(title, screen_widget, input_handler=on_terminal_input): global APPEARANCE_CHANGED_EVENT global SHUTDOWN_EVENT APPEARANCE_CHANGED_EVENT = asyncio.Event() @@ -525,7 +540,7 @@ async def tui(title, screen_widget): terminal.alternate_buffer(), terminal.raw(), terminal.mouse_tracking()): update_task = asyncio.create_task(update_screen(screen_widget)) try: - loop.add_reader(sys.stdin, on_terminal_input, screen_widget) + loop.add_reader(sys.stdin, input_handler, screen_widget) try: await SHUTDOWN_EVENT.wait() finally: diff --git a/fill3/tests/fill3_test.py b/fill3/tests/fill3_test.py index 1bfcaca..9d77a87 100755 --- a/fill3/tests/fill3_test.py +++ b/fill3/tests/fill3_test.py @@ -5,6 +5,7 @@ import unittest import fill3 +import fill3.terminal as terminal class WidgetTests(unittest.TestCase): @@ -139,5 +140,17 @@ class WidgetTests(unittest.TestCase): "B A") +class DigestTerminalInputTestCase(unittest.TestCase): + + def test_digest_terminal_input(self): + self.assertRaises(UnboundLocalError, fill3.digest_terminal_input, "") + self.assertEqual(fill3.digest_terminal_input("a"), ["a"]) + self.assertEqual(fill3.digest_terminal_input("ab"), ["ab"]) + self.assertEqual(fill3.digest_terminal_input("a\nb"), ["a", "\n", "b"]) + self.assertEqual(fill3.digest_terminal_input("a\tb"), ["a", "\t", "b"]) + self.assertEqual(fill3.digest_terminal_input(terminal.UP * 2), [terminal.UP] * 2) + self.assertEqual(fill3.digest_terminal_input(terminal.CTRL_C * 2), [terminal.CTRL_C] * 2) + + if __name__ == "__main__": unittest.main()