test_key_binding.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. from __future__ import annotations
  2. from contextlib import contextmanager
  3. import pytest
  4. from prompt_toolkit.application import Application
  5. from prompt_toolkit.application.current import set_app
  6. from prompt_toolkit.input.defaults import create_pipe_input
  7. from prompt_toolkit.key_binding.key_bindings import KeyBindings
  8. from prompt_toolkit.key_binding.key_processor import KeyPress, KeyProcessor
  9. from prompt_toolkit.keys import Keys
  10. from prompt_toolkit.layout import Layout, Window
  11. from prompt_toolkit.output import DummyOutput
  12. class Handlers:
  13. def __init__(self):
  14. self.called = []
  15. def __getattr__(self, name):
  16. def func(event):
  17. self.called.append(name)
  18. return func
  19. @contextmanager
  20. def set_dummy_app():
  21. """
  22. Return a context manager that makes sure that this dummy application is
  23. active. This is important, because we need an `Application` with
  24. `is_done=False` flag, otherwise no keys will be processed.
  25. """
  26. with create_pipe_input() as pipe_input:
  27. app = Application(
  28. layout=Layout(Window()),
  29. output=DummyOutput(),
  30. input=pipe_input,
  31. )
  32. # Don't start background tasks for these tests. The `KeyProcessor`
  33. # wants to create a background task for flushing keys. We can ignore it
  34. # here for these tests.
  35. # This patch is not clean. In the future, when we can use Taskgroups,
  36. # the `Application` should pass its task group to the constructor of
  37. # `KeyProcessor`. That way, it doesn't have to do a lookup using
  38. # `get_app()`.
  39. app.create_background_task = lambda *_, **kw: None
  40. with set_app(app):
  41. yield
  42. @pytest.fixture
  43. def handlers():
  44. return Handlers()
  45. @pytest.fixture
  46. def bindings(handlers):
  47. bindings = KeyBindings()
  48. bindings.add(Keys.ControlX, Keys.ControlC)(handlers.controlx_controlc)
  49. bindings.add(Keys.ControlX)(handlers.control_x)
  50. bindings.add(Keys.ControlD)(handlers.control_d)
  51. bindings.add(Keys.ControlSquareClose, Keys.Any)(handlers.control_square_close_any)
  52. return bindings
  53. @pytest.fixture
  54. def processor(bindings):
  55. return KeyProcessor(bindings)
  56. def test_remove_bindings(handlers):
  57. with set_dummy_app():
  58. h = handlers.controlx_controlc
  59. h2 = handlers.controld
  60. # Test passing a handler to the remove() function.
  61. bindings = KeyBindings()
  62. bindings.add(Keys.ControlX, Keys.ControlC)(h)
  63. bindings.add(Keys.ControlD)(h2)
  64. assert len(bindings.bindings) == 2
  65. bindings.remove(h)
  66. assert len(bindings.bindings) == 1
  67. # Test passing a key sequence to the remove() function.
  68. bindings = KeyBindings()
  69. bindings.add(Keys.ControlX, Keys.ControlC)(h)
  70. bindings.add(Keys.ControlD)(h2)
  71. assert len(bindings.bindings) == 2
  72. bindings.remove(Keys.ControlX, Keys.ControlC)
  73. assert len(bindings.bindings) == 1
  74. def test_feed_simple(processor, handlers):
  75. with set_dummy_app():
  76. processor.feed(KeyPress(Keys.ControlX, "\x18"))
  77. processor.feed(KeyPress(Keys.ControlC, "\x03"))
  78. processor.process_keys()
  79. assert handlers.called == ["controlx_controlc"]
  80. def test_feed_several(processor, handlers):
  81. with set_dummy_app():
  82. # First an unknown key first.
  83. processor.feed(KeyPress(Keys.ControlQ, ""))
  84. processor.process_keys()
  85. assert handlers.called == []
  86. # Followed by a know key sequence.
  87. processor.feed(KeyPress(Keys.ControlX, ""))
  88. processor.feed(KeyPress(Keys.ControlC, ""))
  89. processor.process_keys()
  90. assert handlers.called == ["controlx_controlc"]
  91. # Followed by another unknown sequence.
  92. processor.feed(KeyPress(Keys.ControlR, ""))
  93. processor.feed(KeyPress(Keys.ControlS, ""))
  94. # Followed again by a know key sequence.
  95. processor.feed(KeyPress(Keys.ControlD, ""))
  96. processor.process_keys()
  97. assert handlers.called == ["controlx_controlc", "control_d"]
  98. def test_control_square_closed_any(processor, handlers):
  99. with set_dummy_app():
  100. processor.feed(KeyPress(Keys.ControlSquareClose, ""))
  101. processor.feed(KeyPress("C", "C"))
  102. processor.process_keys()
  103. assert handlers.called == ["control_square_close_any"]
  104. def test_common_prefix(processor, handlers):
  105. with set_dummy_app():
  106. # Sending Control_X should not yet do anything, because there is
  107. # another sequence starting with that as well.
  108. processor.feed(KeyPress(Keys.ControlX, ""))
  109. processor.process_keys()
  110. assert handlers.called == []
  111. # When another key is pressed, we know that we did not meant the longer
  112. # "ControlX ControlC" sequence and the callbacks are called.
  113. processor.feed(KeyPress(Keys.ControlD, ""))
  114. processor.process_keys()
  115. assert handlers.called == ["control_x", "control_d"]
  116. def test_previous_key_sequence(processor):
  117. """
  118. test whether we receive the correct previous_key_sequence.
  119. """
  120. with set_dummy_app():
  121. events = []
  122. def handler(event):
  123. events.append(event)
  124. # Build registry.
  125. registry = KeyBindings()
  126. registry.add("a", "a")(handler)
  127. registry.add("b", "b")(handler)
  128. processor = KeyProcessor(registry)
  129. # Create processor and feed keys.
  130. processor.feed(KeyPress("a", "a"))
  131. processor.feed(KeyPress("a", "a"))
  132. processor.feed(KeyPress("b", "b"))
  133. processor.feed(KeyPress("b", "b"))
  134. processor.process_keys()
  135. # Test.
  136. assert len(events) == 2
  137. assert len(events[0].key_sequence) == 2
  138. assert events[0].key_sequence[0].key == "a"
  139. assert events[0].key_sequence[0].data == "a"
  140. assert events[0].key_sequence[1].key == "a"
  141. assert events[0].key_sequence[1].data == "a"
  142. assert events[0].previous_key_sequence == []
  143. assert len(events[1].key_sequence) == 2
  144. assert events[1].key_sequence[0].key == "b"
  145. assert events[1].key_sequence[0].data == "b"
  146. assert events[1].key_sequence[1].key == "b"
  147. assert events[1].key_sequence[1].data == "b"
  148. assert len(events[1].previous_key_sequence) == 2
  149. assert events[1].previous_key_sequence[0].key == "a"
  150. assert events[1].previous_key_sequence[0].data == "a"
  151. assert events[1].previous_key_sequence[1].key == "a"
  152. assert events[1].previous_key_sequence[1].data == "a"