summaryrefslogtreecommitdiffstats
path: root/src/prompt_toolkit/input/typeahead.py
blob: a45e7cf50b8eaeae03e3713a98eb09d9c0a98f55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
r"""
Store input key strokes if we did read more than was required.

The input classes `Vt100Input` and `Win32Input` read the input text in chunks
of a few kilobytes. This means that if we read input from stdin, it could be
that we read a couple of lines (with newlines in between) at once.

This creates a problem: potentially, we read too much from stdin. Sometimes
people paste several lines at once because they paste input in a REPL and
expect each input() call to process one line. Or they rely on type ahead
because the application can't keep up with the processing.

However, we need to read input in bigger chunks. We need this mostly to support
pasting of larger chunks of text. We don't want everything to become
unresponsive because we:
  - read one character;
  - parse one character;
  - call the key binding, which does a string operation with one character;
  - and render the user interface.
Doing text operations on single characters is very inefficient in Python, so we
prefer to work on bigger chunks of text. This is why we have to read the input
in bigger chunks.

Further, line buffering is also not an option, because it doesn't work well in
the architecture. We use lower level Posix APIs, that work better with the
event loop and so on. In fact, there is also nothing that defines that only \n
can accept the input, you could create a key binding for any key to accept the
input.

To support type ahead, this module will store all the key strokes that were
read too early, so that they can be feed into to the next `prompt()` call or to
the next prompt_toolkit `Application`.
"""
from __future__ import annotations

from collections import defaultdict

from ..key_binding import KeyPress
from .base import Input

__all__ = [
    "store_typeahead",
    "get_typeahead",
    "clear_typeahead",
]

_buffer: dict[str, list[KeyPress]] = defaultdict(list)


def store_typeahead(input_obj: Input, key_presses: list[KeyPress]) -> None:
    """
    Insert typeahead key presses for the given input.
    """
    global _buffer
    key = input_obj.typeahead_hash()
    _buffer[key].extend(key_presses)


def get_typeahead(input_obj: Input) -> list[KeyPress]:
    """
    Retrieve typeahead and reset the buffer for this input.
    """
    global _buffer

    key = input_obj.typeahead_hash()
    result = _buffer[key]
    _buffer[key] = []
    return result


def clear_typeahead(input_obj: Input) -> None:
    """
    Clear typeahead buffer.
    """
    global _buffer
    key = input_obj.typeahead_hash()
    _buffer[key] = []