Extended keyboard input handling#10359
Conversation
We support two delays. One is, as you said, a historical bogosity to distinguish alt+x from escape, wait, x. The other is a delay to decide if we should use what you already pressed or wait for more - this is not just historical cruft, it's needed if you want to bind anything to longer sequences. E.g. you can bind escape to one thing and escape+x to another, or a reasonably popular idea in vi-mode: Bind "jk" in insert mode to switch to normal mode. The former could only go if we decided to only support terminals with CSI u (given my faith in the overall terminal ecosystem I do not believe that's likely to happen anytime soon). The latter has to stay.
Anyway: Honestly this is fine. We already have
Yeah, that's fine.
The readme does not fill me with confidence that it fits our requirements. The "Tested Terminals" list is much too short for my taste, especially considering Console Host is, as I understand WSL, not relevant to us.
I would like to have this one step deeper and translate the xterm sequences to keys internally. I don't want to ask people to bind Alright, now for the obvious bikeshedding about key names:
Agreed, and tbh both are pretty jargony (why does "M" stand for alt? What on earth is a "meta" key?).
I'm not enamored with these either. The bracket notation is clever - the alternative I proposed would require But I don't like the abbreviations here - "lbrack" and "ret" in particular. I would appreciate "LeftBracket" and "Return". I would also spell out the modifiers. So this would be bind x[Ctrl-y][Alt-Up][Shift-PageUp][LeftBracket]I would also probably make the names case-insensitive. Of course that might conflict with allowing you do use uppercase letters instead of "Shift-" - is Bindings are read much more often than they are written, so it's fine if something is a bit longer. As a weird sidenote: I probably have a different idea of how often people would bind the |
|
The other is a delay to decide if we should use what you already pressed or wait for more
Ok sure. I just think that overloading bindings like that is overly complex. When I use bindings that share prefixes I make sure that they are not a prefix of one another.
I guess we should keep aliasing `Ctrl-[` to Escape. Not sure.
> This list currently includes some control characters, meaning they can be specified in two ways, for example bind [ret] means the same as bind \n
`\n` or `\r`?
I'd need to check but with CSI u the distinction is no longer relevant. It's just `Return`.
I also mapped the newly-independent `Control+j` and `Control+m` to ease the transition.
> I recently realized that there is a Rust crate called [crossterm](https://crates.io/crates/crossterm) that might cover some of the terminal-specific parts.
The readme does not fill me with confidence that it fits our requirements. The "Tested Terminals" list is much too short for my taste, especially considering Console Host is, as I understand WSL, not relevant to us.
It's used by things like Helix and Nushell so I'm sure it runs on many more things.
I haven't looked at the library; probably won't until after this is done.
So this would be
```fish
bind x[Ctrl-y][Alt-Up][Shift-PageUp][LeftBracket]
```
yeah I think I'm fine with long names. We don't have CamelCase anywhere else but seems like the best option.
I would also probably make the names case-insensitive. Of course that might conflict with allowing you do use uppercase letters instead of "Shift-" - is `[Ctrl-Y]` ctrl-shift-y or just ctrl-y? If it is the latter, is `Y` shift-y or y?
Perhaps only named keys (so not the single-char ones) should be case-insensitive.
|
It is a feature we have, that had people asking for it. It's pretty new, but I do not believe it is unreasonable to have.
Not sure if you're running the german qwertz layout, on that Ctrl-[ is basically unusable. So I would need the opinion of people with other layouts to see if that is a thing that people do.
That makes sense. |
|
Is this meant to be working now? |
|
works for me, do you have steps to reproduce? |
|
It now works for me too, I think the issue was running fish_user_key_bindings.fishfunction fish_user_key_bindings
# wipe out the default keybindings
bind --erase --all
bind --erase --all --preset
# add the trivial ones
bind '' self-insert
bind -M paste '' self-insert
bind -M paste ' ' self-insert-notfirst
bind -m paste \e\[200\~ __fish_start_bracketed_paste
bind -M paste \e\[201\~ __fish_stop_bracketed_paste
bind -M paste \' __fish_commandline_insert_escaped\ \\\'\ \$__fish_paste_quoted
bind -M paste \\ __fish_commandline_insert_escaped\ \\\\\ \$__fish_paste_quoted
# add all the other ones
bind \b "backward-kill-line"
bind \cd "exit"
bind \cz "fg 2>/dev/null; commandline -f repaint"
bind \e, "history-token-search-backward"
bind \e. "history-token-search-forward"
bind \eC "cancel-commandline"
bind \eO "insert-line-over"
bind \eU "redo"
bind \eY "commandline | wl-copy -n"
bind \e\# "__fish_toggle_comment_commandline"
bind \e\[1\;2C "forward-word"
bind \e\[1\;2D "backward-word"
bind \e\[1\;3A "cd_up"
bind \e\[1\;3B "cd_into_child_directory"
bind \e\[1\;3C "forward-bigword"
bind \e\[1\;3D "backward-bigword"
bind \e\[1\;4C "end-of-line"
bind \e\[1\;4D "beginning-of-line"
bind \e\[1\;5C "end-of-buffer"
bind \e\[1\;5D "beginning-of-buffer"
bind \e\[3\;2~ "kill-word"
bind \e\[3\;3~ "kill-bigword"
bind \e\[3\;4~ "kill-line"
bind \e\[3~ "history-pager-delete or delete-char"
bind \e\[A "up-or-search"
bind \e\[B "down-or-search"
bind \e\[C "forward-char"
bind \e\[D "backward-char"
bind \e\b "backward-kill-path-component"
bind \e\x7F "backward-kill-word"
bind \e` "togglecase-char"
bind \ec "cancel"
bind \ed "kill-whole-line"
bind \ee "edit_command_buffer"
bind \eg "__fish_paginate"
bind \eh "history-pager"
bind \el "__fish_list_current_token"
bind \em "__fish_man_page"
bind \eo "insert-line-under"
bind \ep "__fish_preview_current_file"
bind \er "clear-screen"
bind \es "fish_commandline_prepend sudo"
bind \eu "undo"
bind \ey "commandline | wl-copy -p -n"
bind \r "execute"
bind \x7F "backward-delete-char"
end
|
|
yeah |
|
I don't think I think I'd like to stick to lowercase key names, not sure the details yet. |
|
From the OP it's not clear to me whether it's planned to deprecate current, escapes-based syntax. I would argue that this is desirable. Consider the scenario which I run into recently:
While this is, technically, no different than simply erasing all keybindings, and thus a user's failure, still, there are programs which handle this fine (among them Kakoune). So, would it be desirable to automatically map new syntax to old syntax? |
Not for quite a while.
Those are two separate questions. We should probably map most keys also to the typical xterm sequences, yes. And again "We can't cover all cases but most of the relevant ones". This should probably cover:
It's fine if windows/option etc, "hyper", the F1 etc function keys, ctrl+alt+shift+left and the numpad aren't mapped. It's fine if some of these use the xterm sequence and don't work in some terminals, as long as the basic "enter text and press return to execute" works. |
|
One option would be to use the names from /usr/include/xkbcommon/xkbcommon-names.h but unfortunately Probably kitty key names (like To optimize for the common case we could make as a bonus this frees us from needing to find a name for
EDIT: actually I think comma-separation is better than space-separation. Of course it's possible to don't invent new names for control and alt but keep using
Yes I use qwerty (alternative layouts are not great with Vim and I need |
|
I've compiled a list of changelog additions (same in PR description and in the last commit), we can use that as guidance for deciding about the user-visible changes. This is ready as far as I'm concerned (of course there's a bunch more things I could document or improve, LMK) |
faho
left a comment
There was a problem hiding this comment.
- Put escape-delay back
- Remove $fish_legacy_bindings
| Terminal Limitations | ||
| -------------------- | ||
|
|
||
| Unix terminals, like the ones fish operates in, are at heart 70s technology. They have some limitations that applications running inside them can't workaround. |
There was a problem hiding this comment.
This would need something on CSI u and then still something on the limitation of the legacy system.
I do not believe that CSI u is that well-supported that we can ignore the older system almost entirely.
There was a problem hiding this comment.
I guess you're right but I don't want to spend too much time on this.
Given that, I might not even mention CSI u - it's an implementation detail and it should just work, the same as in Vim and elsewhere.
So I'd prefer to let someone else write the documentation if they feel like it's useful.
I guess I can avoid touching the existing docs.
I plan to switch to a different flavor of the protocol in future.
There was a problem hiding this comment.
How about something like:
fish works best in terminal emulators that support the modern CSI u key sequence protocol.
It will try to enable this automatically, and if your terminal supports it everything should just work.
If your terminal does not support CSI u, key bindings will have some limitations:
- being incapable of telling many characters + control from other keys, like Control+I from tab or Control+J from newline.
- Control and Shift can't be used simultaneously.
- not knowing some modifier+key sequences
These limitations are caused by the old encoding simply not giving the application more information, which is why the CSI u protocol was created.
There was a problem hiding this comment.
Yeah those are good things to mention.
It's not exactly CSI u but a mix of two protocols with kitty extensions. It's subject to change in future, so I'm not sure if I want to document the specifics. But I think linking to the kitty page is a good idea.
There was a problem hiding this comment.
It's not exactly CSI u but a mix of two protocols with kitty extensions.
Okay, so which things do we need, what terminals does this work properly on? What can we mention here?
I don't really want to link to kitty documentation because that makes it look like a single-terminal thing.
There was a problem hiding this comment.
Features like disambiguation of keys ctrl-j or shift-enter work only on terminals that implement kitty extensions.
(But users of all terminals benefit from having a human-friendly, (terminal/protocol)-agnostic key binding syntax)
I don't really want to link to kitty documentation because that makes it look like a single-terminal thing.
I think this is very easy to avoid, like "if your terminal implements the kitty keyboard protocol ...".
I don't think it's very important to document the specifics because it's always easy to get the ground truth by trying fish on some terminal.
It should be obvious that "if the terminal disambiguates a key, then you can map it" because I don't think there is any other cross-terminal protocol around (not counting terminfo).
When a new protocol comes along we can implement that etc.
There was a problem hiding this comment.
XTerm users also get some benefits from modifyOtherKeys. I dropped this doc change for now
There was a problem hiding this comment.
It should be obvious that "if the terminal disambiguates a key, then you can map it" because I don't think there is any other cross-terminal protocol around (not counting terminfo).
What I'm looking for here is some feature we can tell people to look for in a terminal without having to try it out and see.
Ideally this would just be a bullet point like "Keyboard Input Protocol 2024". I don't love calling it the "kitty keyboard protocol" because that pushes people towards kitty specifically, but if that's the best description we have I guess that's it.
I dropped this doc change for now
That's fine.
|
Okay, running EDIT: Oh god, starting On every prompt. |
|
I'm getting this branch' rust backtrace |
This fixes a problem with the rio terminal as reported in fish-shell/fish-shell#10359 (comment)
This fixes a problem with the rio terminal as reported in fish-shell/fish-shell#10359 (comment)
We need to give fish time to render I think.
Terminal titles are set with an OSC 0 sequence. I don't think we want to support terminals that react badly to unknown OSC (or CSI) sequences. So let's remove our feature detection. This will fix future false negatives along the lines of fish-shell#10037
Use generic shell commands instead. This keeps us honest. No functional change expected.
To do so add an ad-hoc "commandline --search-field" to operate on pager search field. This is primarily motivated because a following commit reuses the fish_clipboard_paste logic for bracketed paste. This avoids a regression.
I don't know why we're inconsistent about this, and at least asan fails frequently due to timeouts.
|
that's unrelated but I've pushed a fix (to master and here). |
|
Thank you @krobelus, I can confirm that the crash issue is solved now. That said, keys detection seems to be inconsistent across different terminals. Those that can accurately report
Those that cannot report it accurately and instead report
With regards to |
|
yeah the plan for the future is to also use the other progressive enhancements |
See the changelog additions for user-visible changes. Since we enable/disable terminal protocols whenever we pass terminal ownership, tests can no longer run in parallel on the same terminal. Since fish asks the terminal to speak CSI u now, using gdb may be less ergonomic. As a remedy, use gdbserver, or lobby for CSI u support in libreadline[1]. [1]: https://lists.gnu.org/archive/html/bug-readline/2022-04/msg00010.html
Not a fan of the harsh tone, I don't think yelling at some inanimate thing is helpful |
Sorry, yeah, I was just surprised by things jumping around and immediately typed it in here. It wasn't my intention for it to sound harsh or like I'm yelling, it was the equivalent of a jumpscare. |
|
ASan |
This fixes a problem with the rio terminal as reported in fish-shell/fish-shell#10359 (comment)
Improve keyboard handling using the CSI u protocol with fixes/extensions from the kitty keyboard protocol.
This allows to
\e\[1\;5C)Changelog additions:
Fish now asks the terminal to speak the keyboard input protocol CSI
u in combination with XTerm's
modifyOtherKeysand Kitty'sprogressive enhancements. This allows to bind almost any key,
including arbitrary combinations of modifiers ctrl, alt and shift.
There is a new syntax for specifying keys for builtin
bind. Thenew syntax introduces modifier names and names for some keys that
don't have an obvious and printable Unicode code point. The old
syntax remains mostly supported but the new one is preferred.
meaning now. For example
bind up 'do something'maps the up arrow instead of thetwo keys
uandp.bind ctrl-x,alt-c 'do something'maps a sequence of twokey chords.
- To minimize breakage,
KEYSargument tobindis parsed in the old syntax in two cases:KEYSstarts with a raw escape character(
\e) or a raw ASCII controlcharacter (
\c).,or-and it is not a named key.Since
,and-act as separators, they need to be given ascommaandminusin ambiguous cases.Specifying key names as terminfo name (
bind -k) is deprecated andmay be removed in a future version.
Legacy bindings that use raw escape sequences instead of named
modifiers/keys continue to be included in the default configuration.
If your terminal supports CSI u, you can remove legacy bindings
(including terminfo bindings) by setting the
fish_legacy_bindings variable to
false(on shell startup or before switching key bindings).The
fish_escape_delay_msconfiguration knob has been obsolete (fora while I think) and has been removed.
fish_key_reader --verboseis now ignored, so it no longer showscodepoint values or timing information (though the latter might be
useful for users of
fish_sequence_key_delay_ms).When a terminal pastes text into fish using bracketed paste, fish
used to switch to a special
pastebind mode. This bind mode hasbeen removed. The behavior on paste is not intended to be
configurable.
Focus reporting is enabled unconditionally, not just for tmux. To
use it, define functions that handle events
for
fish_focus_in` andfish_focus_out``.When fish is stopped or terminated by a signal that cannot be caught
(SIGSTOP or SIGKILL), it may leave the terminal in a state where
keypresses with modifiers send CSI u sequences instead of
traditional ones (that are recognized by bash). If this happens, you
can use the
resetcommand fromncursesto restore the terminalstate.
If the terminal supports CSI u,
shift-enternow inserts a newlineinstead of executing the command line..
Original description, somewhat outdated:
Details
This is a WIP implementation of better keyboard handling, using the CSI u protocol with fixes/extensions from the kitty keyboard protocol.
It allows to
\e\[1\;5C)The implementation works for me but has lots of glaring issues; I'm sharing in case there is early feedback on UI/design.
UI Changes
Today, builtin
bindsupports binding either raw sequences (like\e\[1\;5C), or singular named keys (like--key up) as provided byterminfo.With extended keys, we probably want to support binding sequences of (named) keys where each key might have a number of modifiers. This is not possible with today's syntax. Raw sequences do not support named keys. Terminfo keys don't support sequences or modifiers.
Syntax precedents are the Emacs family (
C-M-xand<next>) and Vim (<C-M-x>and<PageDown>). Unfortunately neither are very natural for ourbindusage.Use
[c-a-x]and[pagedown].Key names
Introduce names for keys that don't map to a standard/unique Unicode code point (see
src/key.rs).I don't know if there is a standard set of names, for now I copied them from Kakoune.
This list currently includes some control characters, meaning they can be specified in two ways, for example
bind [ret]means the same asbind \n. Not sure about this - the redundancy might be confusing.Sequencing
Binding sequences of extended keys is somewhat rare. Concatenation seems free:
One alternative is to separate by spaces but that's ambiguous.
Backwards compat
If the terminal agrees to talk
CSI u, the user's customized bindings of raw non-CSI-u escape sequences will no longer work.We can probably remedy this by translating the escape sequences that are used in practice.
For example, hard-code
\e\[1\;5Cas alias for[c-right](by keeping the existing binding).Additionally the syntax change affects bindings that contain both
[and].This is completely avoidable but actual breakage seems very unlikely.
Most still work as-is
bind [ 'echo foo'bind [x 'echo foo'bind \cX 'echo foo'# note this is[c-x], not[c-X]!bind \ex 'echo foo'# aka[a-x]Some throw an error:
bind [no-such-named-key] 'echo foo'Some silently do something different:
bind [x] 'echo foo'# binds a single key (x), not a sequence of three keys. Could also make it an error.bind [ret] 'echo foo'# binds Return, not a sequenceFuture direction
The implementation is in large parts copied from here (and terminal-agnostic parts from here).
I recently realized that there is a Rust crate called crossterm that might cover some of the terminal-specific parts.