diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 17:35:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 17:35:20 +0000 |
commit | e106bf94eff07d9a59771d9ccc4406421e18ab64 (patch) | |
tree | edb6545500e39df9c67aa918a6125bffc8ec1aee /docs/pages/advanced_topics/key_bindings.rst | |
parent | Initial commit. (diff) | |
download | prompt-toolkit-upstream.tar.xz prompt-toolkit-upstream.zip |
Adding upstream version 3.0.36.upstream/3.0.36upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'docs/pages/advanced_topics/key_bindings.rst')
-rw-r--r-- | docs/pages/advanced_topics/key_bindings.rst | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/docs/pages/advanced_topics/key_bindings.rst b/docs/pages/advanced_topics/key_bindings.rst new file mode 100644 index 0000000..8b334fc --- /dev/null +++ b/docs/pages/advanced_topics/key_bindings.rst @@ -0,0 +1,388 @@ +.. _key_bindings: + +More about key bindings +======================= + +This page contains a few additional notes about key bindings. + + +Key bindings can be defined as follows by creating a +:class:`~prompt_toolkit.key_binding.KeyBindings` instance: + + +.. code:: python + + from prompt_toolkit.key_binding import KeyBindings + + bindings = KeyBindings() + + @bindings.add('a') + def _(event): + " Do something if 'a' has been pressed. " + ... + + + @bindings.add('c-t') + def _(event): + " Do something if Control-T has been pressed. " + ... + +.. note:: + + :kbd:`c-q` (control-q) and :kbd:`c-s` (control-s) are often captured by the + terminal, because they were used traditionally for software flow control. + When this is enabled, the application will automatically freeze when + :kbd:`c-s` is pressed, until :kbd:`c-q` is pressed. It won't be possible to + bind these keys. + + In order to disable this, execute the following command in your shell, or even + add it to your `.bashrc`. + + .. code:: + + stty -ixon + +Key bindings can even consist of a sequence of multiple keys. The binding is +only triggered when all the keys in this sequence are pressed. + +.. code:: python + + @bindings.add('a', 'b') + def _(event): + " Do something if 'a' is pressed and then 'b' is pressed. " + ... + +If the user presses only `a`, then nothing will happen until either a second +key (like `b`) has been pressed or until the timeout expires (see later). + + +List of special keys +-------------------- + +Besides literal characters, any of the following keys can be used in a key +binding: + ++-------------------+-----------------------------------------+ +| Name + Possible keys | ++===================+=========================================+ +| Escape | :kbd:`escape` | +| Shift + escape | :kbd:`s-escape` | ++-------------------+-----------------------------------------+ +| Arrows | :kbd:`left`, | +| | :kbd:`right`, | +| | :kbd:`up`, | +| | :kbd:`down` | ++-------------------+-----------------------------------------+ +| Navigation | :kbd:`home`, | +| | :kbd:`end`, | +| | :kbd:`delete`, | +| | :kbd:`pageup`, | +| | :kbd:`pagedown`, | +| | :kbd:`insert` | ++-------------------+-----------------------------------------+ +| Control+letter | :kbd:`c-a`, :kbd:`c-b`, :kbd:`c-c`, | +| | :kbd:`c-d`, :kbd:`c-e`, :kbd:`c-f`, | +| | :kbd:`c-g`, :kbd:`c-h`, :kbd:`c-i`, | +| | :kbd:`c-j`, :kbd:`c-k`, :kbd:`c-l`, | +| | | +| | :kbd:`c-m`, :kbd:`c-n`, :kbd:`c-o`, | +| | :kbd:`c-p`, :kbd:`c-q`, :kbd:`c-r`, | +| | :kbd:`c-s`, :kbd:`c-t`, :kbd:`c-u`, | +| | :kbd:`c-v`, :kbd:`c-w`, :kbd:`c-x`, | +| | | +| | :kbd:`c-y`, :kbd:`c-z` | ++-------------------+-----------------------------------------+ +| Control + number | :kbd:`c-1`, :kbd:`c-2`, :kbd:`c-3`, | +| | :kbd:`c-4`, :kbd:`c-5`, :kbd:`c-6`, | +| | :kbd:`c-7`, :kbd:`c-8`, :kbd:`c-9`, | +| | :kbd:`c-0` | ++-------------------+-----------------------------------------+ +| Control + arrow | :kbd:`c-left`, | +| | :kbd:`c-right`, | +| | :kbd:`c-up`, | +| | :kbd:`c-down` | ++-------------------+-----------------------------------------+ +| Other control | :kbd:`c-@`, | +| keys | :kbd:`c-\\`, | +| | :kbd:`c-]`, | +| | :kbd:`c-^`, | +| | :kbd:`c-_`, | +| | :kbd:`c-delete` | ++-------------------+-----------------------------------------+ +| Shift + arrow | :kbd:`s-left`, | +| | :kbd:`s-right`, | +| | :kbd:`s-up`, | +| | :kbd:`s-down` | ++-------------------+-----------------------------------------+ +| Control + Shift + | :kbd:`c-s-left`, | +| arrow | :kbd:`c-s-right`, | +| | :kbd:`c-s-up`, | +| | :kbd:`c-s-down` | ++-------------------+-----------------------------------------+ +| Other shift | :kbd:`s-delete`, | +| keys | :kbd:`s-tab` | ++-------------------+-----------------------------------------+ +| F-keys | :kbd:`f1`, :kbd:`f2`, :kbd:`f3`, | +| | :kbd:`f4`, :kbd:`f5`, :kbd:`f6`, | +| | :kbd:`f7`, :kbd:`f8`, :kbd:`f9`, | +| | :kbd:`f10`, :kbd:`f11`, :kbd:`f12`, | +| | | +| | :kbd:`f13`, :kbd:`f14`, :kbd:`f15`, | +| | :kbd:`f16`, :kbd:`f17`, :kbd:`f18`, | +| | :kbd:`f19`, :kbd:`f20`, :kbd:`f21`, | +| | :kbd:`f22`, :kbd:`f23`, :kbd:`f24` | ++-------------------+-----------------------------------------+ + +There are a couple of useful aliases as well: + ++-------------------+-------------------+ +| :kbd:`c-h` | :kbd:`backspace` | ++-------------------+-------------------+ +| :kbd:`c-@` | :kbd:`c-space` | ++-------------------+-------------------+ +| :kbd:`c-m` | :kbd:`enter` | ++-------------------+-------------------+ +| :kbd:`c-i` | :kbd:`tab` | ++-------------------+-------------------+ + +.. note:: + + Note that the supported keys are limited to what typical VT100 terminals + offer. Binding :kbd:`c-7` (control + number 7) for instance is not + supported. + + +Binding alt+something, option+something or meta+something +--------------------------------------------------------- + +Vt100 terminals translate the alt key into a leading :kbd:`escape` key. +For instance, in order to handle :kbd:`alt-f`, we have to handle +:kbd:`escape` + :kbd:`f`. Notice that we receive this as two individual keys. +This means that it's exactly the same as first typing :kbd:`escape` and then +typing :kbd:`f`. Something this alt-key is also known as option or meta. + +In code that looks as follows: + +.. code:: python + + @bindings.add('escape', 'f') + def _(event): + " Do something if alt-f or meta-f have been pressed. " + + +Wildcards +--------- + +Sometimes you want to catch any key that follows after a certain key stroke. +This is possible by binding the '<any>' key: + +.. code:: python + + @bindings.add('a', '<any>') + def _(event): + ... + +This will handle `aa`, `ab`, `ac`, etcetera. The key binding can check the +`event` object for which keys exactly have been pressed. + + +Attaching a filter (condition) +------------------------------ + +In order to enable a key binding according to a certain condition, we have to +pass it a :class:`~prompt_toolkit.filters.Filter`, usually a +:class:`~prompt_toolkit.filters.Condition` instance. (:ref:`Read more about +filters <filters>`.) + +.. code:: python + + from prompt_toolkit.filters import Condition + + @Condition + def is_active(): + " Only activate key binding on the second half of each minute. " + return datetime.datetime.now().second > 30 + + @bindings.add('c-t', filter=is_active) + def _(event): + # ... + pass + +The key binding will be ignored when this condition is not satisfied. + + +ConditionalKeyBindings: Disabling a set of key bindings +------------------------------------------------------- + +Sometimes you want to enable or disable a whole set of key bindings according +to a certain condition. This is possible by wrapping it in a +:class:`~prompt_toolkit.key_binding.ConditionalKeyBindings` object. + +.. code:: python + + from prompt_toolkit.key_binding import ConditionalKeyBindings + + @Condition + def is_active(): + " Only activate key binding on the second half of each minute. " + return datetime.datetime.now().second > 30 + + bindings = ConditionalKeyBindings( + key_bindings=my_bindings, + filter=is_active) + +If the condition is not satisfied, all the key bindings in `my_bindings` above +will be ignored. + + +Merging key bindings +-------------------- + +Sometimes you have different parts of your application generate a collection of +key bindings. It is possible to merge them together through the +:func:`~prompt_toolkit.key_binding.merge_key_bindings` function. This is +preferred above passing a :class:`~prompt_toolkit.key_binding.KeyBindings` +object around and having everyone populate it. + +.. code:: python + + from prompt_toolkit.key_binding import merge_key_bindings + + bindings = merge_key_bindings([ + bindings1, + bindings2, + ]) + + +Eager +----- + +Usually not required, but if ever you have to override an existing key binding, +the `eager` flag can be useful. + +Suppose that there is already an active binding for `ab` and you'd like to add +a second binding that only handles `a`. When the user presses only `a`, +prompt_toolkit has to wait for the next key press in order to know which +handler to call. + +By passing the `eager` flag to this second binding, we are actually saying that +prompt_toolkit shouldn't wait for longer matches when all the keys in this key +binding are matched. So, if `a` has been pressed, this second binding will be +called, even if there's an active `ab` binding. + +.. code:: python + + @bindings.add('a', 'b') + def binding_1(event): + ... + + @bindings.add('a', eager=True) + def binding_2(event): + ... + +This is mainly useful in order to conditionally override another binding. + +Asyncio coroutines +------------------ + +Key binding handlers can be asyncio coroutines. + +.. code:: python + + from prompt_toolkit.application import in_terminal + + @bindings.add('x') + async def print_hello(event): + """ + Pressing 'x' will print 5 times "hello" in the background above the + prompt. + """ + for i in range(5): + # Print hello above the current prompt. + async with in_terminal(): + print('hello') + + # Sleep, but allow further input editing in the meantime. + await asyncio.sleep(1) + +If the user accepts the input on the prompt, while this coroutine is not yet +finished , an `asyncio.CancelledError` exception will be thrown in this +coroutine. + + +Timeouts +-------- + +There are two timeout settings that effect the handling of keys. + +- ``Application.ttimeoutlen``: Like Vim's `ttimeoutlen` option. + When to flush the input (For flushing escape keys.) This is important on + terminals that use vt100 input. We can't distinguish the escape key from for + instance the left-arrow key, if we don't know what follows after "\x1b". This + little timer will consider "\x1b" to be escape if nothing did follow in this + time span. This seems to work like the `ttimeoutlen` option in Vim. + +- ``KeyProcessor.timeoutlen``: like Vim's `timeoutlen` option. + This can be `None` or a float. For instance, suppose that we have a key + binding AB and a second key binding A. If the uses presses A and then waits, + we don't handle this binding yet (unless it was marked 'eager'), because we + don't know what will follow. This timeout is the maximum amount of time that + we wait until we call the handlers anyway. Pass `None` to disable this + timeout. + + +Recording macros +---------------- + +Both Emacs and Vi mode allow macro recording. By default, all key presses are +recorded during a macro, but it is possible to exclude certain keys by setting +the `record_in_macro` parameter to `False`: + +.. code:: python + + @bindings.add('c-t', record_in_macro=False) + def _(event): + # ... + pass + + +Creating new Vi text objects and operators +------------------------------------------ + +We tried very hard to ship prompt_toolkit with as many as possible Vi text +objects and operators, so that text editing feels as natural as possible to Vi +users. + +If you wish to create a new text object or key binding, that is actually +possible. Check the `custom-vi-operator-and-text-object.py` example for more +information. + + +Handling SIGINT +--------------- + +The SIGINT Unix signal can be handled by binding ``<sigint>``. For instance: + +.. code:: python + + @bindings.add('<sigint>') + def _(event): + # ... + pass + +This will handle a SIGINT that was sent by an external application into the +process. Handling control-c should be done by binding ``c-c``. (The terminal +input is set to raw mode, which means that a ``c-c`` won't be translated into a +SIGINT.) + +For a ``PromptSession``, there is a default binding for ``<sigint>`` that +corresponds to ``c-c``: it will exit the prompt, raising a +``KeyboardInterrupt`` exception. + + +Processing `.inputrc` +--------------------- + +GNU readline can be configured using an `.inputrc` configuration file. This file +contains key bindings as well as certain settings. Right now, prompt_toolkit +doesn't support `.inputrc`, but it should be possible in the future. |