4.4 KiB
title | date |
---|---|
Edit Command Line in Zsh | 2022-07-10T00:25:21-07:00 |
While reading through my dotfiles, I found some configuration1 that didn't seem to be working - it claimed that <ESC>,v
would allow editing of the current line in vim, but that didn't seem to work. I guess I'd copied that from some other configuration and lost patience with trying to get it working, or that it relied on some other configuration option which had been broken2. I dug in to find out more. (This article was invaluable!)
Intention
First, let's understand what that snippet is trying to do. The ZLE (Zsh Line Editor) is a tool3 that allows for:
- the definition of various commands (called "widgets") for editing text.
- the binding of those widgets (or built-in or imported ones) to keys within named "keymaps". Commands can switch between keymaps.
Three standard keymaps in ZLE are emacs
, vicmd
, and viins
. In the viins
(vi-insert) keymap, the <ESC>
key is associated with a widget that switches to the vicmd
keymap:
$ bindkey -M viins '^['
"^[" vi-cmd-mode
(The character string '^['
represents the single keypress <ESC>
)
The line bindkey -M vicmd v edit-command-line
means "Within the keymap vicmd
, bind the widget edit-command-line
to the key v
". The earlier commands (autoload edit-command-line; zle -N edit-command-line
) deal with making that widget available to ZLE - I guess it's not available by default?
So, taken as a whole, this configuration should make the key-sequence <ESC>,v
translate to "enter vicmd
mode, then execute edit-command-line
". But that didn't seem to be the case.
Investigation and resolution
I quickly noticed that my default keymap was not viins
but emacs
:
$ bindkey -lL main
bindkey -A emacs main
Quick fix, right? Add a line bindkey -v
4 to my dotfile, and done?
Well, sort-of. This change did allow the edit-command-line
to fire as expected, allowing the current line to be edited in vi
; but, after "writing" the command and returning to the command line, I was not able to delete any character of the written command. Initially I thought this was because I was still in vicmd
mode (and created a workaround custom widget here), but that turned out to be an incorrect assumption. After looking around a little more, I found this SO answer which suggested changing the binding for '^?'
(that is - <BACKSPACE>
) from vi-backward-delete-char
to backward-delete-char
. I'm not sure why, though - backspaces still work fine on the terminal before entering vicmd
mode, which lends further credence to my suspicion that the ZLE is not the entirety of the terminal, but merely a tool within it.
I've been making a lot of use of some Emacs-mode shortcuts:
Ctrl-A
=> beginning of lineCtrl-E
=> end of lineCtrl-U
=> clear everything
So I added these to my setup with the following commands:
bindkey -M viins '^A' beginning-of-line
bindkey -M viins '^E' end-of-line
bindkey -M viins '^U' kill-whole-line
-
Still in Github until I fully migrate to my self-hosted Gitea instance. I'm cautious of a circular dependency here - Gitea would need to be up-and-available to source dotfiles, but dotfiles would be referenced as part of the setup process for hosts (including the one that runs the Gitea instance). ↩︎
-
An idea - regression testing for dotfiles? Don't tempt me... ↩︎
-
The article says that the ZLE is the command prompt, which...seems unlikely to me? I would think that the ZLE is a part of the command prompt, but not all of it? Although the article contains a lot of useful information and insight, it also has some rather loose and imprecise statements, so I'm not sure how much to trust this. ↩︎
-
bindkey -v
is an alias forbindkey -A viins main
- in ZLE, you don't set a keymap as active, instead you set a keymap as an alias formain
, and I think that's beautiful. ↩︎