How can I make zsh's vi mode behave more like bash's vi mode?
I am really liking the general speed of zsh, but two things are annoying the heck out of me.
- I have to hit wait a moment between hitting escape and hitting slash to get to the history search (if it hit slash too quickly it says
zsh: do you wish to see all 514 possibilities (172 lines)
) - After entering insert mode because of hitting
a
orA
, I can't backspace past the point where I entered insert mode.
I know that 2 is like classic vi, but I like the vim style better.
command-line zsh
add a comment |
I am really liking the general speed of zsh, but two things are annoying the heck out of me.
- I have to hit wait a moment between hitting escape and hitting slash to get to the history search (if it hit slash too quickly it says
zsh: do you wish to see all 514 possibilities (172 lines)
) - After entering insert mode because of hitting
a
orA
, I can't backspace past the point where I entered insert mode.
I know that 2 is like classic vi, but I like the vim style better.
command-line zsh
If anyone is running into the very annoying issue of double escaping causing you to have to hiti
twice to get back to insert mode, I would highly recommend this fix!
– cchamberlain
Jul 24 '15 at 3:01
add a comment |
I am really liking the general speed of zsh, but two things are annoying the heck out of me.
- I have to hit wait a moment between hitting escape and hitting slash to get to the history search (if it hit slash too quickly it says
zsh: do you wish to see all 514 possibilities (172 lines)
) - After entering insert mode because of hitting
a
orA
, I can't backspace past the point where I entered insert mode.
I know that 2 is like classic vi, but I like the vim style better.
command-line zsh
I am really liking the general speed of zsh, but two things are annoying the heck out of me.
- I have to hit wait a moment between hitting escape and hitting slash to get to the history search (if it hit slash too quickly it says
zsh: do you wish to see all 514 possibilities (172 lines)
) - After entering insert mode because of hitting
a
orA
, I can't backspace past the point where I entered insert mode.
I know that 2 is like classic vi, but I like the vim style better.
command-line zsh
command-line zsh
asked Sep 18 '12 at 11:20
Chas. OwensChas. Owens
1,01711122
1,01711122
If anyone is running into the very annoying issue of double escaping causing you to have to hiti
twice to get back to insert mode, I would highly recommend this fix!
– cchamberlain
Jul 24 '15 at 3:01
add a comment |
If anyone is running into the very annoying issue of double escaping causing you to have to hiti
twice to get back to insert mode, I would highly recommend this fix!
– cchamberlain
Jul 24 '15 at 3:01
If anyone is running into the very annoying issue of double escaping causing you to have to hit
i
twice to get back to insert mode, I would highly recommend this fix!– cchamberlain
Jul 24 '15 at 3:01
If anyone is running into the very annoying issue of double escaping causing you to have to hit
i
twice to get back to insert mode, I would highly recommend this fix!– cchamberlain
Jul 24 '15 at 3:01
add a comment |
2 Answers
2
active
oldest
votes
(1). For some reason, bindkey behaves oddly when it comes to "/": <esc>
followed quickly by /
is interpreted as <esc-/>
. (I observed this behavior the other day; not quite sure what causes it.) I don't know if this is a bug or a feature, and if it's a feature if it can be disabled, but you can work around it fairly easily.
This key combo is probably bound to _history-complete-older
, which is generating the undesired result – you can use bindkey -L
to see the if this is the case.
At any rate, if you don't mind sacrificing the actual <esc-/>
(pressed together, as a chord) binding, you can re-bind it to the vi-mode history search command, so that typing <esc>
followed by /
does the same thing at any typing speed. =)
Since this will be treated as a chord, it won't have the effect of first entering vi command mode, so we'll have to make sure that happens first. First, you need to define a function; put it somewhere in your fpath
if you use that, or put it in your .zshrc otherwise:
vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}
The rest goes in your .zshrc either way:
autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins 'e/' vi-search-fix
Should be good to go.
(2). You can fix the backspace key as follows:
`bindkey "^?" backward-delete-char`
Also, if you want similar behavior for other vi style commands:
bindkey "^W" backward-kill-word
bindkey "^H" backward-delete-char # Control-h also deletes the previous char
bindkey "^U" backward-kill-line
It was under^[/
note/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.
– Chas. Owens
Jan 14 '13 at 12:36
1
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes.vi-history-search-backward
. I'll write it and edit my answer – check back later today.
– Marshall Eubanks
Jan 14 '13 at 13:14
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
With regards to (2), when I dobindkey | grep <searchterm>
for any of the terms, they're all prefixed byvi-
. Do I need to set upbindkey
commands that aren't prefixed byvi-
?
– adam_0
Feb 13 '13 at 18:05
1
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
|
show 7 more comments
I'm only going to address question (1).
Your problem is KEYTIMEOUT. I quote from zshzle(1):
When ZLE is reading a command from the terminal, it may read a
sequence that is bound to some command and is also a prefix of a
longer bound string. In this case ZLE will wait a certain time to see
if more characters are typed, and if not (or they don't match any
longer string) it will execute the binding. This timeout is defined
by the KEYTIMEOUT parameter; its default is 0.4 sec. There is no
timeout if the prefix string is not itself bound to a command.
That 0.4s is the delay you're experiencing after hitting ESC. The fix is to set KEYTIMEOUT right down to 0.01s in one of the shell startup files:
export KEYTIMEOUT=1
Unfortunately this has a knock-on effect: Other things start going wrong…
Firstly, there is now a problem in vi command mode: Typing ESC causes the cursor to hang, and then whichever character you type next gets swallowed. This is because ESC is not bound to anything by default in vi command mode, yet there are multi-character widgets that start with ESC (cursor keys!). So when you hit ESC, ZLE waits for the next character… and then consumes it.
The fix is to bind ESC to something in command mode, thus ensuring that the something gets passed to ZLE after $KEYTIMEOUT centiseconds. Now we can keep bindings starting with ESC in command mode without these ill effects. I bind ESC to the bell character, which I find to be even less intrusive than self-insert (and my shell is silenced):
bindkey -sM vicmd '^[' '^G'
Update 2017:
I have since found an even better solution for binding ESC — the
undefined-key
widget. I’m not sure whether this widget was available in zsh when I originally wrote this answer.
bindkey -M vicmd '^[' undefined-key
Next problem: There are by default some two-key widgets starting in ^X in vi insert mode; these become unusable if $KEYTIMEOUT is set all the way down. What I do is unbind ^X in vi insert mode (it's self-insert by default); this allows those two-key widgets to continue working.
bindkey -rM viins '^X'
You lose the binding for self-insert, but you can bind it to something else of course. (I don't, since I have no use for it.)
The last problem (I've found so far): There are some remaining default keybindings that we "lose" due to setting $KEYTIMEOUT right down, to wit: those starting with ESC in vi insert mode which are not cursor keys. I personally rebind them to start with ^X instead:
bindkey -M viins '^X,' _history-complete-newer
'^X/' _history-complete-older
'^X`' _bash_complete-word
Update 2018:
It turns out the entire section above (after “Update 2017”) is not necessarily required. It’s possible to set the META key to be equivalent to ESC in keyboard mappings using:
bindkey -mv
It is therefore possible not to unbind ^X, and to access the keybindings that start in ESC by pressing META as a leader instead (ALT or OPT on modern keyboards).
If you have access to the book From Bash to Z Shell by Kiddle et al., the equivalence of ESC and META in keybindings is discussed in the Chapter 4 sidebar on pp. 78–79.
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
1
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "3"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f476532%2fhow-can-i-make-zshs-vi-mode-behave-more-like-bashs-vi-mode%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
(1). For some reason, bindkey behaves oddly when it comes to "/": <esc>
followed quickly by /
is interpreted as <esc-/>
. (I observed this behavior the other day; not quite sure what causes it.) I don't know if this is a bug or a feature, and if it's a feature if it can be disabled, but you can work around it fairly easily.
This key combo is probably bound to _history-complete-older
, which is generating the undesired result – you can use bindkey -L
to see the if this is the case.
At any rate, if you don't mind sacrificing the actual <esc-/>
(pressed together, as a chord) binding, you can re-bind it to the vi-mode history search command, so that typing <esc>
followed by /
does the same thing at any typing speed. =)
Since this will be treated as a chord, it won't have the effect of first entering vi command mode, so we'll have to make sure that happens first. First, you need to define a function; put it somewhere in your fpath
if you use that, or put it in your .zshrc otherwise:
vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}
The rest goes in your .zshrc either way:
autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins 'e/' vi-search-fix
Should be good to go.
(2). You can fix the backspace key as follows:
`bindkey "^?" backward-delete-char`
Also, if you want similar behavior for other vi style commands:
bindkey "^W" backward-kill-word
bindkey "^H" backward-delete-char # Control-h also deletes the previous char
bindkey "^U" backward-kill-line
It was under^[/
note/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.
– Chas. Owens
Jan 14 '13 at 12:36
1
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes.vi-history-search-backward
. I'll write it and edit my answer – check back later today.
– Marshall Eubanks
Jan 14 '13 at 13:14
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
With regards to (2), when I dobindkey | grep <searchterm>
for any of the terms, they're all prefixed byvi-
. Do I need to set upbindkey
commands that aren't prefixed byvi-
?
– adam_0
Feb 13 '13 at 18:05
1
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
|
show 7 more comments
(1). For some reason, bindkey behaves oddly when it comes to "/": <esc>
followed quickly by /
is interpreted as <esc-/>
. (I observed this behavior the other day; not quite sure what causes it.) I don't know if this is a bug or a feature, and if it's a feature if it can be disabled, but you can work around it fairly easily.
This key combo is probably bound to _history-complete-older
, which is generating the undesired result – you can use bindkey -L
to see the if this is the case.
At any rate, if you don't mind sacrificing the actual <esc-/>
(pressed together, as a chord) binding, you can re-bind it to the vi-mode history search command, so that typing <esc>
followed by /
does the same thing at any typing speed. =)
Since this will be treated as a chord, it won't have the effect of first entering vi command mode, so we'll have to make sure that happens first. First, you need to define a function; put it somewhere in your fpath
if you use that, or put it in your .zshrc otherwise:
vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}
The rest goes in your .zshrc either way:
autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins 'e/' vi-search-fix
Should be good to go.
(2). You can fix the backspace key as follows:
`bindkey "^?" backward-delete-char`
Also, if you want similar behavior for other vi style commands:
bindkey "^W" backward-kill-word
bindkey "^H" backward-delete-char # Control-h also deletes the previous char
bindkey "^U" backward-kill-line
It was under^[/
note/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.
– Chas. Owens
Jan 14 '13 at 12:36
1
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes.vi-history-search-backward
. I'll write it and edit my answer – check back later today.
– Marshall Eubanks
Jan 14 '13 at 13:14
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
With regards to (2), when I dobindkey | grep <searchterm>
for any of the terms, they're all prefixed byvi-
. Do I need to set upbindkey
commands that aren't prefixed byvi-
?
– adam_0
Feb 13 '13 at 18:05
1
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
|
show 7 more comments
(1). For some reason, bindkey behaves oddly when it comes to "/": <esc>
followed quickly by /
is interpreted as <esc-/>
. (I observed this behavior the other day; not quite sure what causes it.) I don't know if this is a bug or a feature, and if it's a feature if it can be disabled, but you can work around it fairly easily.
This key combo is probably bound to _history-complete-older
, which is generating the undesired result – you can use bindkey -L
to see the if this is the case.
At any rate, if you don't mind sacrificing the actual <esc-/>
(pressed together, as a chord) binding, you can re-bind it to the vi-mode history search command, so that typing <esc>
followed by /
does the same thing at any typing speed. =)
Since this will be treated as a chord, it won't have the effect of first entering vi command mode, so we'll have to make sure that happens first. First, you need to define a function; put it somewhere in your fpath
if you use that, or put it in your .zshrc otherwise:
vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}
The rest goes in your .zshrc either way:
autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins 'e/' vi-search-fix
Should be good to go.
(2). You can fix the backspace key as follows:
`bindkey "^?" backward-delete-char`
Also, if you want similar behavior for other vi style commands:
bindkey "^W" backward-kill-word
bindkey "^H" backward-delete-char # Control-h also deletes the previous char
bindkey "^U" backward-kill-line
(1). For some reason, bindkey behaves oddly when it comes to "/": <esc>
followed quickly by /
is interpreted as <esc-/>
. (I observed this behavior the other day; not quite sure what causes it.) I don't know if this is a bug or a feature, and if it's a feature if it can be disabled, but you can work around it fairly easily.
This key combo is probably bound to _history-complete-older
, which is generating the undesired result – you can use bindkey -L
to see the if this is the case.
At any rate, if you don't mind sacrificing the actual <esc-/>
(pressed together, as a chord) binding, you can re-bind it to the vi-mode history search command, so that typing <esc>
followed by /
does the same thing at any typing speed. =)
Since this will be treated as a chord, it won't have the effect of first entering vi command mode, so we'll have to make sure that happens first. First, you need to define a function; put it somewhere in your fpath
if you use that, or put it in your .zshrc otherwise:
vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}
The rest goes in your .zshrc either way:
autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins 'e/' vi-search-fix
Should be good to go.
(2). You can fix the backspace key as follows:
`bindkey "^?" backward-delete-char`
Also, if you want similar behavior for other vi style commands:
bindkey "^W" backward-kill-word
bindkey "^H" backward-delete-char # Control-h also deletes the previous char
bindkey "^U" backward-kill-line
edited Jul 23 '14 at 7:09
Community♦
1
1
answered Jan 13 '13 at 21:34
Marshall EubanksMarshall Eubanks
549413
549413
It was under^[/
note/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.
– Chas. Owens
Jan 14 '13 at 12:36
1
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes.vi-history-search-backward
. I'll write it and edit my answer – check back later today.
– Marshall Eubanks
Jan 14 '13 at 13:14
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
With regards to (2), when I dobindkey | grep <searchterm>
for any of the terms, they're all prefixed byvi-
. Do I need to set upbindkey
commands that aren't prefixed byvi-
?
– adam_0
Feb 13 '13 at 18:05
1
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
|
show 7 more comments
It was under^[/
note/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.
– Chas. Owens
Jan 14 '13 at 12:36
1
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes.vi-history-search-backward
. I'll write it and edit my answer – check back later today.
– Marshall Eubanks
Jan 14 '13 at 13:14
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
With regards to (2), when I dobindkey | grep <searchterm>
for any of the terms, they're all prefixed byvi-
. Do I need to set upbindkey
commands that aren't prefixed byvi-
?
– adam_0
Feb 13 '13 at 18:05
1
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
It was under
^[/
not e/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.– Chas. Owens
Jan 14 '13 at 12:36
It was under
^[/
not e/
, but those are both valid ways of saying escape. The change works perfectly. Now that I am playing with it more completely, it looks like zsh's vi mode sucks in comparison to bash's (or at least is not fully configured by default). One example of this is the fact that it drops you into insert mode after the searching history. I have to go back to command mode to hit n to find the next search item.– Chas. Owens
Jan 14 '13 at 12:36
1
1
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes
.vi-history-search-backward
. I'll write it and edit my answer – check back later today.– Marshall Eubanks
Jan 14 '13 at 13:14
Well, I don't know if you have any other examples, but the one you mention is my fault, not zsh's. =) What's happened is I've bound a vi-cmd mode editor command in vi insert mode – the command expects the shell to already by in cmd mode and behaves accordingly. We need to write an editor command which first calls the "enter cmd mode" command, and then executes
.vi-history-search-backward
. I'll write it and edit my answer – check back later today.– Marshall Eubanks
Jan 14 '13 at 13:14
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
OK, I updated my answer. Try it out.
– Marshall Eubanks
Jan 15 '13 at 0:26
With regards to (2), when I do
bindkey | grep <searchterm>
for any of the terms, they're all prefixed by vi-
. Do I need to set up bindkey
commands that aren't prefixed by vi-
?– adam_0
Feb 13 '13 at 18:05
With regards to (2), when I do
bindkey | grep <searchterm>
for any of the terms, they're all prefixed by vi-
. Do I need to set up bindkey
commands that aren't prefixed by vi-
?– adam_0
Feb 13 '13 at 18:05
1
1
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
Thank you. These hacks (and those of wjv below, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
|
show 7 more comments
I'm only going to address question (1).
Your problem is KEYTIMEOUT. I quote from zshzle(1):
When ZLE is reading a command from the terminal, it may read a
sequence that is bound to some command and is also a prefix of a
longer bound string. In this case ZLE will wait a certain time to see
if more characters are typed, and if not (or they don't match any
longer string) it will execute the binding. This timeout is defined
by the KEYTIMEOUT parameter; its default is 0.4 sec. There is no
timeout if the prefix string is not itself bound to a command.
That 0.4s is the delay you're experiencing after hitting ESC. The fix is to set KEYTIMEOUT right down to 0.01s in one of the shell startup files:
export KEYTIMEOUT=1
Unfortunately this has a knock-on effect: Other things start going wrong…
Firstly, there is now a problem in vi command mode: Typing ESC causes the cursor to hang, and then whichever character you type next gets swallowed. This is because ESC is not bound to anything by default in vi command mode, yet there are multi-character widgets that start with ESC (cursor keys!). So when you hit ESC, ZLE waits for the next character… and then consumes it.
The fix is to bind ESC to something in command mode, thus ensuring that the something gets passed to ZLE after $KEYTIMEOUT centiseconds. Now we can keep bindings starting with ESC in command mode without these ill effects. I bind ESC to the bell character, which I find to be even less intrusive than self-insert (and my shell is silenced):
bindkey -sM vicmd '^[' '^G'
Update 2017:
I have since found an even better solution for binding ESC — the
undefined-key
widget. I’m not sure whether this widget was available in zsh when I originally wrote this answer.
bindkey -M vicmd '^[' undefined-key
Next problem: There are by default some two-key widgets starting in ^X in vi insert mode; these become unusable if $KEYTIMEOUT is set all the way down. What I do is unbind ^X in vi insert mode (it's self-insert by default); this allows those two-key widgets to continue working.
bindkey -rM viins '^X'
You lose the binding for self-insert, but you can bind it to something else of course. (I don't, since I have no use for it.)
The last problem (I've found so far): There are some remaining default keybindings that we "lose" due to setting $KEYTIMEOUT right down, to wit: those starting with ESC in vi insert mode which are not cursor keys. I personally rebind them to start with ^X instead:
bindkey -M viins '^X,' _history-complete-newer
'^X/' _history-complete-older
'^X`' _bash_complete-word
Update 2018:
It turns out the entire section above (after “Update 2017”) is not necessarily required. It’s possible to set the META key to be equivalent to ESC in keyboard mappings using:
bindkey -mv
It is therefore possible not to unbind ^X, and to access the keybindings that start in ESC by pressing META as a leader instead (ALT or OPT on modern keyboards).
If you have access to the book From Bash to Z Shell by Kiddle et al., the equivalence of ESC and META in keybindings is discussed in the Chapter 4 sidebar on pp. 78–79.
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
1
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
add a comment |
I'm only going to address question (1).
Your problem is KEYTIMEOUT. I quote from zshzle(1):
When ZLE is reading a command from the terminal, it may read a
sequence that is bound to some command and is also a prefix of a
longer bound string. In this case ZLE will wait a certain time to see
if more characters are typed, and if not (or they don't match any
longer string) it will execute the binding. This timeout is defined
by the KEYTIMEOUT parameter; its default is 0.4 sec. There is no
timeout if the prefix string is not itself bound to a command.
That 0.4s is the delay you're experiencing after hitting ESC. The fix is to set KEYTIMEOUT right down to 0.01s in one of the shell startup files:
export KEYTIMEOUT=1
Unfortunately this has a knock-on effect: Other things start going wrong…
Firstly, there is now a problem in vi command mode: Typing ESC causes the cursor to hang, and then whichever character you type next gets swallowed. This is because ESC is not bound to anything by default in vi command mode, yet there are multi-character widgets that start with ESC (cursor keys!). So when you hit ESC, ZLE waits for the next character… and then consumes it.
The fix is to bind ESC to something in command mode, thus ensuring that the something gets passed to ZLE after $KEYTIMEOUT centiseconds. Now we can keep bindings starting with ESC in command mode without these ill effects. I bind ESC to the bell character, which I find to be even less intrusive than self-insert (and my shell is silenced):
bindkey -sM vicmd '^[' '^G'
Update 2017:
I have since found an even better solution for binding ESC — the
undefined-key
widget. I’m not sure whether this widget was available in zsh when I originally wrote this answer.
bindkey -M vicmd '^[' undefined-key
Next problem: There are by default some two-key widgets starting in ^X in vi insert mode; these become unusable if $KEYTIMEOUT is set all the way down. What I do is unbind ^X in vi insert mode (it's self-insert by default); this allows those two-key widgets to continue working.
bindkey -rM viins '^X'
You lose the binding for self-insert, but you can bind it to something else of course. (I don't, since I have no use for it.)
The last problem (I've found so far): There are some remaining default keybindings that we "lose" due to setting $KEYTIMEOUT right down, to wit: those starting with ESC in vi insert mode which are not cursor keys. I personally rebind them to start with ^X instead:
bindkey -M viins '^X,' _history-complete-newer
'^X/' _history-complete-older
'^X`' _bash_complete-word
Update 2018:
It turns out the entire section above (after “Update 2017”) is not necessarily required. It’s possible to set the META key to be equivalent to ESC in keyboard mappings using:
bindkey -mv
It is therefore possible not to unbind ^X, and to access the keybindings that start in ESC by pressing META as a leader instead (ALT or OPT on modern keyboards).
If you have access to the book From Bash to Z Shell by Kiddle et al., the equivalence of ESC and META in keybindings is discussed in the Chapter 4 sidebar on pp. 78–79.
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
1
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
add a comment |
I'm only going to address question (1).
Your problem is KEYTIMEOUT. I quote from zshzle(1):
When ZLE is reading a command from the terminal, it may read a
sequence that is bound to some command and is also a prefix of a
longer bound string. In this case ZLE will wait a certain time to see
if more characters are typed, and if not (or they don't match any
longer string) it will execute the binding. This timeout is defined
by the KEYTIMEOUT parameter; its default is 0.4 sec. There is no
timeout if the prefix string is not itself bound to a command.
That 0.4s is the delay you're experiencing after hitting ESC. The fix is to set KEYTIMEOUT right down to 0.01s in one of the shell startup files:
export KEYTIMEOUT=1
Unfortunately this has a knock-on effect: Other things start going wrong…
Firstly, there is now a problem in vi command mode: Typing ESC causes the cursor to hang, and then whichever character you type next gets swallowed. This is because ESC is not bound to anything by default in vi command mode, yet there are multi-character widgets that start with ESC (cursor keys!). So when you hit ESC, ZLE waits for the next character… and then consumes it.
The fix is to bind ESC to something in command mode, thus ensuring that the something gets passed to ZLE after $KEYTIMEOUT centiseconds. Now we can keep bindings starting with ESC in command mode without these ill effects. I bind ESC to the bell character, which I find to be even less intrusive than self-insert (and my shell is silenced):
bindkey -sM vicmd '^[' '^G'
Update 2017:
I have since found an even better solution for binding ESC — the
undefined-key
widget. I’m not sure whether this widget was available in zsh when I originally wrote this answer.
bindkey -M vicmd '^[' undefined-key
Next problem: There are by default some two-key widgets starting in ^X in vi insert mode; these become unusable if $KEYTIMEOUT is set all the way down. What I do is unbind ^X in vi insert mode (it's self-insert by default); this allows those two-key widgets to continue working.
bindkey -rM viins '^X'
You lose the binding for self-insert, but you can bind it to something else of course. (I don't, since I have no use for it.)
The last problem (I've found so far): There are some remaining default keybindings that we "lose" due to setting $KEYTIMEOUT right down, to wit: those starting with ESC in vi insert mode which are not cursor keys. I personally rebind them to start with ^X instead:
bindkey -M viins '^X,' _history-complete-newer
'^X/' _history-complete-older
'^X`' _bash_complete-word
Update 2018:
It turns out the entire section above (after “Update 2017”) is not necessarily required. It’s possible to set the META key to be equivalent to ESC in keyboard mappings using:
bindkey -mv
It is therefore possible not to unbind ^X, and to access the keybindings that start in ESC by pressing META as a leader instead (ALT or OPT on modern keyboards).
If you have access to the book From Bash to Z Shell by Kiddle et al., the equivalence of ESC and META in keybindings is discussed in the Chapter 4 sidebar on pp. 78–79.
I'm only going to address question (1).
Your problem is KEYTIMEOUT. I quote from zshzle(1):
When ZLE is reading a command from the terminal, it may read a
sequence that is bound to some command and is also a prefix of a
longer bound string. In this case ZLE will wait a certain time to see
if more characters are typed, and if not (or they don't match any
longer string) it will execute the binding. This timeout is defined
by the KEYTIMEOUT parameter; its default is 0.4 sec. There is no
timeout if the prefix string is not itself bound to a command.
That 0.4s is the delay you're experiencing after hitting ESC. The fix is to set KEYTIMEOUT right down to 0.01s in one of the shell startup files:
export KEYTIMEOUT=1
Unfortunately this has a knock-on effect: Other things start going wrong…
Firstly, there is now a problem in vi command mode: Typing ESC causes the cursor to hang, and then whichever character you type next gets swallowed. This is because ESC is not bound to anything by default in vi command mode, yet there are multi-character widgets that start with ESC (cursor keys!). So when you hit ESC, ZLE waits for the next character… and then consumes it.
The fix is to bind ESC to something in command mode, thus ensuring that the something gets passed to ZLE after $KEYTIMEOUT centiseconds. Now we can keep bindings starting with ESC in command mode without these ill effects. I bind ESC to the bell character, which I find to be even less intrusive than self-insert (and my shell is silenced):
bindkey -sM vicmd '^[' '^G'
Update 2017:
I have since found an even better solution for binding ESC — the
undefined-key
widget. I’m not sure whether this widget was available in zsh when I originally wrote this answer.
bindkey -M vicmd '^[' undefined-key
Next problem: There are by default some two-key widgets starting in ^X in vi insert mode; these become unusable if $KEYTIMEOUT is set all the way down. What I do is unbind ^X in vi insert mode (it's self-insert by default); this allows those two-key widgets to continue working.
bindkey -rM viins '^X'
You lose the binding for self-insert, but you can bind it to something else of course. (I don't, since I have no use for it.)
The last problem (I've found so far): There are some remaining default keybindings that we "lose" due to setting $KEYTIMEOUT right down, to wit: those starting with ESC in vi insert mode which are not cursor keys. I personally rebind them to start with ^X instead:
bindkey -M viins '^X,' _history-complete-newer
'^X/' _history-complete-older
'^X`' _bash_complete-word
Update 2018:
It turns out the entire section above (after “Update 2017”) is not necessarily required. It’s possible to set the META key to be equivalent to ESC in keyboard mappings using:
bindkey -mv
It is therefore possible not to unbind ^X, and to access the keybindings that start in ESC by pressing META as a leader instead (ALT or OPT on modern keyboards).
If you have access to the book From Bash to Z Shell by Kiddle et al., the equivalence of ESC and META in keybindings is discussed in the Chapter 4 sidebar on pp. 78–79.
edited Dec 17 '18 at 9:50
answered Sep 20 '13 at 13:59
wjvwjv
621610
621610
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
1
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
add a comment |
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
1
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
Thank you. These hacks (and those of marshaul above, too) make zsh's vi mode go from nigh-unusable to excellent. I created a superuser account so that I could vote you up. :-)
– ctrueden
Jan 26 '15 at 13:28
1
1
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
Thanks! I find it a little worrying that, after all this time, we still need what is essentially a hack and a workaround to make a core bit of zsh functionality usable!
– wjv
Feb 6 '15 at 9:26
add a comment |
Thanks for contributing an answer to Super User!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f476532%2fhow-can-i-make-zshs-vi-mode-behave-more-like-bashs-vi-mode%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
If anyone is running into the very annoying issue of double escaping causing you to have to hit
i
twice to get back to insert mode, I would highly recommend this fix!– cchamberlain
Jul 24 '15 at 3:01