Interpret zsh bindkey escaped sequences - zsh

I usually find interesting zsh keybinding settings (through bindkey command) around the web. My question is how do I interpret what these escaped sequences mapped to? For instance, here is a snippet from oh-my-zsh's key-bindings.zsh
bindkey "^[[H" beginning-of-line
bindkey "^[[1~" beginning-of-line
bindkey "^[[F" end-of-line
bindkey "^[[4~" end-of-line
Is there a reference on how do these keymaps represented? Also, is it zsh-specific or platform specific at all?
I am aware that I can use either cat or Ctrl-V to find the corresponding escaped sequence for certain keys. Given that I could brute force to find the reverse match, but this would not work for the keys that do not exist on my keyboard (e.g. Home/End on Mac laptops). Thus, I'd prefer methods that could determine the keys regardless of the physical keyboard.

If speaking of a typical unix/linux flow of events the picture is roughly the following.
The terminal emulator program recieves the X events such as so and so button pressed, another button is released. Those events can be tracked with xev utility, for example. The terminal emulator then translates those events into escape sequences.
This translation is not set in stone. It can be configured. Different terminal emulators are configured differently. For example xterm translation can be set up in .Xdefaults like that:
XTerm*VT100*Translations:#override \
Ctrl<Key>Left: string(0x1B) string(OD) \n\
Ctrl<Key>Right: string(0x1B) string(OC) \n\
Note 0x1B which is ESC. ESC is also printed as ^[.
Now, zsh uses zle (and bash uses readline library for the same purpose)
which interprets some of the sequences to move around the input line and perform editing actions.
The following texts should provide more additional details.
Zsh Line editor description
Wikipedia article on escape sequences
and
Xterm Control Sequences

My answer is for modern readers in 2021 using MacOSX with default zsh Termnial:
Run your Terminal, press ⌘ + , to open Preferences.
Select Profiles > Keyboard tab, then here you are, there are all your mappings.

Related

How to setup Vi Editing Mode for zsh

I want to set vi in editing mode in zsh (I am using oh-my-zsh) at start automatically when I open my shell, so at the beginning of my .zshrc I have tried the following code:
set -o vi
or
bindkey -v
but when pressing enter in the shell I cannot enter the vi mode.
If I tried one of the two commands in the shell, it works.
Basically I want zsh to start in vi edit mode.
Any ideas how to solve this problem?
bindkey -v is enough to enable vi mode in ZSH. If you are worried the setting will be overwritten by another plugin, put the setting at the bottom of your ~/.zshrc.
After vi mode is enabled, you enter the "insert" mode by default. To enter "normal" mode, use Esc. And i or a to switch back to "insert" mode.
BTW, softmoth/zsh-vim-mode is the most powerful vim mode plugin I've ever used in ZSH.
Using bindkey -v may take over functionality such as history search with control+R and control+S. To restore that particular behavior, add the following lines after bindkey -v:
bindkey ^R history-incremental-search-backward
bindkey ^S history-incremental-search-forward
Other bindings can be found in the ZSH manual Standard Widgets section.
If you are using https://ohmyz.sh/ you can add vi-mode to the list of plugins in ~/.zshrc:
plugins=(git vi-mode)
If you don't mind to use a plugin for vi mode in zsh, here is a better choice for you to quickly reach it.
zsh-vi-mode: A better and friendly vi(vim) mode plugin for ZSH.
After adding this plugin, then you can input with vi-mode like this:
Features
Cursor movement (Navigation).
Insert & Replace (Insert mode).
Text Objects.
Searching text.
Undo, Redo, Cut, Copy, Paste, and Delete.
Surrounds (Add, Replace, Delete, and Move Around).
Switch keywords (Increase/Decrease Number, Boolean, etc. In progress).
...

Is it possible to bindkey Shift+Enter in zsh?

I'm starting to use zsh on macOS Sierra. I would like to have the following key mappings:
Enter => accept-line
Shift-Enter => accept-and-hold
However, I can't seem to differentiate between the two. I'm only able to get Enter, and Esc-Enter, but not Shift-Enter:
bindkey "^M" accept-line # Enter
bindkey "^[^M" accept-and-hold # Esc-Enter
bindkey "????" accept-and-hold # Shift-Enter
Is it possible to detect and handle Shift-Enter?
zsh (as well as other shells) do not act on key bindings but rather on key sequences received from the terminal. Converting key presses and combinations into key sequences is the responsibility of the terminal. You can retrieve the key sequence for a key combination by pressing Ctr+v followed by the key combination, e.g. Shift+Enter.
By default Enter and Shift+Enter (as well as Ctrl+v and Ctrl+Shift+m) all generate the identical key sequence ^M (at least in most common terminal emulators).
Fortunately, some terminal emulators allow to configure the key sequences sent. For example iTerm2 allows you to set customized key bindings that send escape sequences (in Profile > Keys), you should be able to define a sequence for Shift+Enter there, e.g. [[SE and can then make the appropriate settings in zsh: bindkey '^[[[SE' 'accept-and-hold'. (Unfortunately I do not have access to a Mac at the moment, so I could not test this).
This might answer your problem (can't put it into comment, don't have 50 rep). You might try # showkey --scancodes which gives you key codes and take a look into the manual-pages e.g. man zshzle and search for "code". I have tried to map the shift key without success. Might be it's not possible. Also have a look into bindkey -l wich gives you the keymaps and bindkey -M emacs for emacs keymap

List current existing or applied key bindings made via bindkey

My ZSH has at some point been told to bind Delete to some complicated function, key sequence, or macro, and I want to remove this binding from my configuration. In order to better find where this binding is being set, I'd like to see what Zsh is actually doing when I hit Delete.
How can I see a list of all the currently existing key bindings present in my Zsh environment?
Just run bindkey with no arguments:
$ bindkey
"^A"-"^C" self-insert
"^D" list-choices
"^E"-"^F" self-insert
"^G" list-expand
"^H" vi-backward-delete-char
"^I" expand-or-complete
"^J" history-substring-search-down
"^K" self-insert
"^L" clear-screen
...
However, the particular behavior you are describing with regards to Delete can be resolved by adding this to your .zshrc:
bindkey "^[[3~" delete-char
bindkey "^[3;5~" delete-char
Explanation
Depending on the terminal, Delete generates one of the following character sequences:
^[[3~
^[3;5~
You can see which sequence your terminal uses via sed -n l as explained here.
zsh tries to evaluate the longest match. In both cases, zsh matches ^[ first, which matches Esc. If you have vi mode enabled, this tells zsh to turn it on.
After this, vi mode reads the remaining characters, which are one of the following:
[3~ toggle the case of the next 3 characters
3;5~ repeat the last find operation 3 times then toggle the case of the next 5 characters
So if you haven't explicitly used bindkey on this character sequence, every time you press Delete with vi mode enabled, you will enter vi mode and the last character you typed will be uppercased.
Thanks to Adaephon in the comments below for help with this explanation.

Zsh Line Editor History Filtering

One of the killer features of the readline line editor is the ability to type the first few characters of a command in one's history and then up-arrow to get to it. For instance, if I have 'grep "te' in the zle buffer, the up-arrow key iterates through grep commands whose first two search characters are 't' and 'e'. In my current zsh configuration, the up arrow key does not do such filtering. Are there zle commands/widgets that would give the type of filtering I want?
The widget you are looking for is history-beginning-search-backward. You can bind it to up arrow using
bindkey "^[OA" history-beginning-search-backward
or
bindkey "^[[A" history-beginning-search-backward
depending on which escape sequence your up-arrow key sends (you can just use both, to be safe).

In zsh how do you bind Ctrl+backspace to delete the previous word?

I'm trying to bind the command usualy binded to ^W with ctrl+backspace.
I have two problem here, one for each parameter of the bindkey command:
what is string to mean the ctrl+backspace
what is the command to delete the previous word
One may use bindkey '^H' backward-kill-word.
Note that, on old versions of GNOME terminal, it won't work; see How do I get Ctrl-Backspace to delete a word in vim within gnome-terminal? and Bug 420039 - VTE doesn't distinguish between backspace and control-backspace.
As reported by thorbjornwolf in his comment, commit 23c7cd0f fixed it.
As I pointed out here there is a chance that the keystrokes are different in some systems.
If the output of showkey -a is:
Ctrl+Backspace is ^?
then you should add the following line in your ~/.zshrc file:
bindkey '^?' backward-kill-word

Resources