You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Input and TextArea widgets use _on_key to handle processing printable character key events; however, for keys like backspace or left arrow, they are handled by BINDINGS. This can create a problem in the order in which they are processed. The bindings are processed separately from printable characters, so if the user types multiple characters very quickly with a backspace in the middle, the backspace could end up occurring anywhere in the input. This is nearly impossible to occur with a normal user typing (unless they can type several hundred WPM or perhaps if their system is under load, slowing the program down). However, it can occur frequently if a user is using a device like a CharaChorder to type faster than normal (which is how I originally found the bug). If a user binds macros to type any text for them, it could have the same problems if it incorporates a backspace or a left arrow.
While I highlighted individual keys like backspace, the problem is true for all keybindings like paste.
demo
inj.py is a quick script I vibe coded for Unix-based systems to allow running a program with it and intercepting stdin and allows overwriting the data that gets sent to the program it is running.
in this script:
if I press f it becomes "ba\x7fckspace" or [b, a, <backspace>, c, k, s, p, a, c, e] to demonstrate the synchronization problem using a backspace
if I press j it will press \x15\x0b or [ctrl+k, ctrl+u] to clear the input
if I press k it will press left\x1b[Dic\x1b[Car or [l,e,f,t,<left arrow>,i,c,<right arrow>,a,r]
in the demo video I ran python inj.py example.py; python inj.py nvim, so I could demonstrate
what the output of f and k are in textual vs what it is in neovim.
force the bindings to run synchronously in the _on_key. Add something like this: you would need to convert the bindings into a map of
their string value and run something like
This would break if a user overridden bindings since normally everything in SYNC_BINDINGS would be run.
This could be solved by checking if the bindings are empty, but that could lead to the user reintroducing the synchronization issue.
It may be possible to use some sort of metaclass to change BINDINGS to SYNC_BINDINGS. This would force everything to run in _on_key and theoretically shouldn't break if a user overrode BINDINGS unless they depended on the binding running separately
to be nonblocking for typing or if they were referencing BINDINGS directly in a function.
There could be another simple approach that I missed I'm not super familiar with textual's internals. I could open a PR with this approach if it is desired.
The Bug
The Input and TextArea widgets use
_on_keyto handle processing printable character key events; however, for keys like backspace or left arrow, they are handled by BINDINGS. This can create a problem in the order in which they are processed. The bindings are processed separately from printable characters, so if the user types multiple characters very quickly with a backspace in the middle, the backspace could end up occurring anywhere in the input. This is nearly impossible to occur with a normal user typing (unless they can type several hundred WPM or perhaps if their system is under load, slowing the program down). However, it can occur frequently if a user is using a device like a CharaChorder to type faster than normal (which is how I originally found the bug). If a user binds macros to type any text for them, it could have the same problems if it incorporates a backspace or a left arrow.While I highlighted individual keys like backspace, the problem is true for all keybindings like paste.
demo
inj.py is a quick script I vibe coded for Unix-based systems to allow running a program with it and intercepting stdin and allows overwriting the data that gets sent to the program it is running.
in this script:
if I press f it becomes "ba\x7fckspace" or
[b, a, <backspace>, c, k, s, p, a, c, e]to demonstrate the synchronization problem using a backspaceif I press j it will press \x15\x0b or [ctrl+k, ctrl+u] to clear the input
if I press k it will press left\x1b[Dic\x1b[Car or
[l,e,f,t,<left arrow>,i,c,<right arrow>,a,r]in the demo video I ran
python inj.py example.py; python inj.py nvim, so I could demonstratewhat the output of f and k are in textual vs what it is in neovim.
textual_demo.mp4
Here's example.py's code:
here's the code for inj.py
possible fix
force the bindings to run synchronously in the _on_key. Add something like this: you would need to convert the bindings into a map of
their string value and run something like
This would break if a user overridden bindings since normally everything in SYNC_BINDINGS would be run.
This could be solved by checking if the bindings are empty, but that could lead to the user reintroducing the synchronization issue.
It may be possible to use some sort of metaclass to change BINDINGS to SYNC_BINDINGS. This would force everything to run in _on_key and theoretically shouldn't break if a user overrode BINDINGS unless they depended on the binding running separately
to be nonblocking for typing or if they were referencing BINDINGS directly in a function.
There could be another simple approach that I missed I'm not super familiar with textual's internals. I could open a PR with this approach if it is desired.