123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- from __future__ import annotations
- from contextlib import contextmanager
- import pytest
- from prompt_toolkit.application import Application
- from prompt_toolkit.application.current import set_app
- from prompt_toolkit.input.defaults import create_pipe_input
- from prompt_toolkit.key_binding.key_bindings import KeyBindings
- from prompt_toolkit.key_binding.key_processor import KeyPress, KeyProcessor
- from prompt_toolkit.keys import Keys
- from prompt_toolkit.layout import Layout, Window
- from prompt_toolkit.output import DummyOutput
- class Handlers:
- def __init__(self):
- self.called = []
- def __getattr__(self, name):
- def func(event):
- self.called.append(name)
- return func
- @contextmanager
- def set_dummy_app():
- """
- Return a context manager that makes sure that this dummy application is
- active. This is important, because we need an `Application` with
- `is_done=False` flag, otherwise no keys will be processed.
- """
- with create_pipe_input() as pipe_input:
- app = Application(
- layout=Layout(Window()),
- output=DummyOutput(),
- input=pipe_input,
- )
- # Don't start background tasks for these tests. The `KeyProcessor`
- # wants to create a background task for flushing keys. We can ignore it
- # here for these tests.
- # This patch is not clean. In the future, when we can use Taskgroups,
- # the `Application` should pass its task group to the constructor of
- # `KeyProcessor`. That way, it doesn't have to do a lookup using
- # `get_app()`.
- app.create_background_task = lambda *_, **kw: None
- with set_app(app):
- yield
- @pytest.fixture
- def handlers():
- return Handlers()
- @pytest.fixture
- def bindings(handlers):
- bindings = KeyBindings()
- bindings.add(Keys.ControlX, Keys.ControlC)(handlers.controlx_controlc)
- bindings.add(Keys.ControlX)(handlers.control_x)
- bindings.add(Keys.ControlD)(handlers.control_d)
- bindings.add(Keys.ControlSquareClose, Keys.Any)(handlers.control_square_close_any)
- return bindings
- @pytest.fixture
- def processor(bindings):
- return KeyProcessor(bindings)
- def test_remove_bindings(handlers):
- with set_dummy_app():
- h = handlers.controlx_controlc
- h2 = handlers.controld
- # Test passing a handler to the remove() function.
- bindings = KeyBindings()
- bindings.add(Keys.ControlX, Keys.ControlC)(h)
- bindings.add(Keys.ControlD)(h2)
- assert len(bindings.bindings) == 2
- bindings.remove(h)
- assert len(bindings.bindings) == 1
- # Test passing a key sequence to the remove() function.
- bindings = KeyBindings()
- bindings.add(Keys.ControlX, Keys.ControlC)(h)
- bindings.add(Keys.ControlD)(h2)
- assert len(bindings.bindings) == 2
- bindings.remove(Keys.ControlX, Keys.ControlC)
- assert len(bindings.bindings) == 1
- def test_feed_simple(processor, handlers):
- with set_dummy_app():
- processor.feed(KeyPress(Keys.ControlX, "\x18"))
- processor.feed(KeyPress(Keys.ControlC, "\x03"))
- processor.process_keys()
- assert handlers.called == ["controlx_controlc"]
- def test_feed_several(processor, handlers):
- with set_dummy_app():
- # First an unknown key first.
- processor.feed(KeyPress(Keys.ControlQ, ""))
- processor.process_keys()
- assert handlers.called == []
- # Followed by a know key sequence.
- processor.feed(KeyPress(Keys.ControlX, ""))
- processor.feed(KeyPress(Keys.ControlC, ""))
- processor.process_keys()
- assert handlers.called == ["controlx_controlc"]
- # Followed by another unknown sequence.
- processor.feed(KeyPress(Keys.ControlR, ""))
- processor.feed(KeyPress(Keys.ControlS, ""))
- # Followed again by a know key sequence.
- processor.feed(KeyPress(Keys.ControlD, ""))
- processor.process_keys()
- assert handlers.called == ["controlx_controlc", "control_d"]
- def test_control_square_closed_any(processor, handlers):
- with set_dummy_app():
- processor.feed(KeyPress(Keys.ControlSquareClose, ""))
- processor.feed(KeyPress("C", "C"))
- processor.process_keys()
- assert handlers.called == ["control_square_close_any"]
- def test_common_prefix(processor, handlers):
- with set_dummy_app():
- # Sending Control_X should not yet do anything, because there is
- # another sequence starting with that as well.
- processor.feed(KeyPress(Keys.ControlX, ""))
- processor.process_keys()
- assert handlers.called == []
- # When another key is pressed, we know that we did not meant the longer
- # "ControlX ControlC" sequence and the callbacks are called.
- processor.feed(KeyPress(Keys.ControlD, ""))
- processor.process_keys()
- assert handlers.called == ["control_x", "control_d"]
- def test_previous_key_sequence(processor):
- """
- test whether we receive the correct previous_key_sequence.
- """
- with set_dummy_app():
- events = []
- def handler(event):
- events.append(event)
- # Build registry.
- registry = KeyBindings()
- registry.add("a", "a")(handler)
- registry.add("b", "b")(handler)
- processor = KeyProcessor(registry)
- # Create processor and feed keys.
- processor.feed(KeyPress("a", "a"))
- processor.feed(KeyPress("a", "a"))
- processor.feed(KeyPress("b", "b"))
- processor.feed(KeyPress("b", "b"))
- processor.process_keys()
- # Test.
- assert len(events) == 2
- assert len(events[0].key_sequence) == 2
- assert events[0].key_sequence[0].key == "a"
- assert events[0].key_sequence[0].data == "a"
- assert events[0].key_sequence[1].key == "a"
- assert events[0].key_sequence[1].data == "a"
- assert events[0].previous_key_sequence == []
- assert len(events[1].key_sequence) == 2
- assert events[1].key_sequence[0].key == "b"
- assert events[1].key_sequence[0].data == "b"
- assert events[1].key_sequence[1].key == "b"
- assert events[1].key_sequence[1].data == "b"
- assert len(events[1].previous_key_sequence) == 2
- assert events[1].previous_key_sequence[0].key == "a"
- assert events[1].previous_key_sequence[0].data == "a"
- assert events[1].previous_key_sequence[1].key == "a"
- assert events[1].previous_key_sequence[1].data == "a"
|