Skip to content

Commit 296b9e4

Browse files
committed
Add pending() and flush() to BaseEventQueue
1 parent 46acb79 commit 296b9e4

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

Lib/_pyrepl/base_eventqueue.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,51 @@ def empty(self) -> bool:
5454
"""
5555
return not self.events
5656

57+
def pending(self) -> bool:
58+
"""
59+
True when we have received the start of an escape/key sequence but
60+
are still waiting for further bytes to disambiguate it.
61+
62+
This is the case right after a byte (such as ESC) that our keymap
63+
knows only as a prefix of a longer sequence has been pushed, but
64+
before the rest of that sequence arrives. Consoles use this to
65+
decide whether the next read should block indefinitely or only for
66+
the escape timeout (see ``flush``).
67+
"""
68+
return self.keymap is not self.compiled_keymap
69+
70+
def flush(self) -> None:
71+
"""
72+
Finalize any pending, incomplete input as discrete events.
73+
74+
This implements the classic "escape-time" disambiguation used by
75+
TUIs such as ncurses, Vim and Tmux: when we have received the start
76+
of an escape sequence (e.g. a lone ESC) but no further bytes arrive
77+
within the escape timeout, we stop waiting and emit the bytes
78+
accumulated so far as a complete key instead.
79+
80+
The behavior mirrors that of an unrecognized escape sequence: the
81+
leading ESC is emitted as a key and any remaining bytes are fed
82+
back through ``push`` so they can start another sequence or be
83+
treated as ordinary text. Calling this when there is nothing
84+
pending is a no-op.
85+
"""
86+
if self.keymap is self.compiled_keymap:
87+
# Nothing pending: either idle, or a complete key was already
88+
# emitted by ``push``.
89+
return
90+
buf = self.buf.take_bytes()
91+
self.keymap = self.compiled_keymap
92+
if buf and buf[0] == 27: # escape
93+
self.insert(Event('key', '\033', b'\033'))
94+
for _c in buf[1:]:
95+
self.push(_c)
96+
else:
97+
# Defensive: a pending prefix that does not start with ESC
98+
# (e.g. a partial multi-byte sequence). Emit it as text.
99+
data = bytes(buf).decode(self.encoding, 'replace')
100+
self.insert(Event('key', data, buf))
101+
57102
def insert(self, event: Event) -> None:
58103
"""
59104
Inserts an event into the queue.

0 commit comments

Comments
 (0)