Cursor keys are fun.
Albeit that they are not quite as fun as editing keys, which are really fun.
You have two sets of cursor keys on your keyboard, ones on the cursor keypad and ones on the calculator keypad.
Most terminal emulators try (sometimes quite poorly) to employ the model of DEC VTs, where each set of keys is individually switchable between application mode and normal mode using the private mode settings DECCKM
(Cursor Keypad Mode) and DECNKM
(Numeric Keypad Mode), respectively. The idea of application mode is essentially that the keys on the relevant keypad turn into extra application function keys.
-
⇐ This is the cursor keypad.
-
- In normal mode, the arrow keys send the ECMA-48
CUB
, CUF
, CUU
, and CUD
control sequences, unless the ⎇ Alt modifier is in effect in which case they send DECFNK
control sequences. - In application mode, the arrow keys send
SS3
single-shift 3 sequences.
-
⇐ This is the calculator keypad.
-
- In normal mode, the arrow keys send the ECMA-48
CUB
, CUF
, CUU
, and CUD
control sequences, unless the ⎇ Alt modifier is in effect in which case they send DECFNK
control sequences, or unless the combination of numeric lock and shift causes them to send digits. - In application mode, the arrow keys send a different set of
SS3
single-shift 3 sequences (unless, again, the combination of numeric lock and shift causes them to send digits).
The ␛
[
A
sequence that you have told ZLE to bind to a widget is an ECMA-48 7-bit alias for the CSI
A
control sequence, which is the CUP
("Cursor UP") control sequence. That control sequence is only generated by DEC VTs and their imitator terminal emulators when a keypad is in normal mode and the ⎇ Alt modifier is not in effect. It won't match the shift sequences sent when the relevant keypads are in application mode.
The terminfo database muddies the waters, and causes extra fun here, because it does not employ this model for terminal I/O. Rather, it employs its own different model that embodies notions of "local" and "remote" keys, which is not what DEC VT application/normal mode switching actually involves at all; and it has a single local/remote switch mechanism, that ends up switching both keypads between application/normal modes indivisibly.
terminfo is the way not to hardwire to one specific terminal type how you are configuring ZLE, just in case you find yourself with a terminal or terminal emulator that does not imitate a DEC VT. And the Z shell provides you with ways of accessing the necessary capability entries from the database record. So you could read from terminfo what control sequences terminfo expects the up/down/left/right cursor keys to produce, and issue appropriate bindkey
commands that map those control sequences to widgets.
The problem is that terminfo is inadequate for this job. It only has a way to record one control sequence per key, whereas as you can see keys can send at least three different sequences, dependent from mode and modifiers pressed. (Modifiers can influence sent control sequences quite significantly in the DEC VT model.) So you need to switch the terminal into the mode that generates what terminfo tells you to expect.
But it gets worse: terminfo isn't consistent. The single control sequence is sometimes the DEC VT application mode sequence, as terminfo records for the putty
terminal type, sometimes the DEC VT normal mode sequence, as terminfo records for the rxvt
terminal type, but never the DECFNK
sequence. So you have no way to know whether you should switch to application or to normal mode with any given terminal or terminal emulator. What will work right for one will go wrong for another.
The other approach, therefore, is to ignore terminfo and realize that you are already and quite happily assuming that your terminal is always going to be like a DEC VT with your original bindkey
command. You just need two of them, to make sure that whether your terminal is in application or normal mode the control sequence that it sends will be matched:
bindkey "^[OA" history-beginning-search-backward
However, this will not cope with modifier keys being pressed, which adds extra parameters to the CUP
control sequence that causes the simplistic string matching that ZLE uses to fail when all that it is looking for is plain old parameterless CUP
. You have to manually issue an additional bindkey
command for each possible CUP
control sequence resulting from each possible combination of modifiers.
seq 1 8 |
while read -r i
do
bindkey "^[[1;${i}A" history-beginning-search-backward
done
ZLE is not alone, here. Other terminfo-based programs, such as the fish
shell, suffer in the same way. (The fish
shell people, too, found out that what choice of application/normal mode works right for one terminal emulator will go wrong for another.) Rearchitecting this (compare libtermkey
which has an actual ECMA-48 control sequence parser for input) in these programs is long overdue. But no-one has tackled it yet.
Further reading
zsh
you're running on each system. Then I'd make sure that the key is correct because it's not uncommon that keyboards are mapped different in different distributions. Also try to verify that the.zshrc
is really executed in the Ubuntu environment (e.g. echo something to terminal or touch a file). – Mikko Rantalainen Aug 28 '18 at 10:09^[[A
and^[OA
(or$terminfo[kcuu1]
or$key[Up]
) – Stéphane Chazelas Aug 28 '18 at 11:17Ctrl+V
tip, it made solving the problem a triviality! – Ul Tome Aug 28 '18 at 13:34