Jonathan
Jonathan

Reputation: 539

In zsh how do I bind a keyboard shortcut to run the last command?

I frequently find myself wanting to repeat a command, and while !! is useful, I'd like to bind that to ctrl-w or something like that. Is there a way to do that?

EDIT: I'm aware that the up arrow does what I want, however I would rather not have to leave the home row. Being an avid Vim user has taught me the value of staying on the home keys.

I looked at this post about adding a shortcut to access the info command and tried to extrapolate something out of it, but had no success. Zsh yelled at me about zle not being active or something.

I know this will rely on knowledge of how my shell is configured, so below I've pasted some relevant code, as well as a link to my entire .zshrc and dotfiles in general.

# oh-my-zsh plugins. zsh-aliases and drush are custom plugins.
plugins=( git z tmux web-search colored-man zsh-aliases drush)
ZSH_TMUX_AUTOSTART=true
#... $PATH, start background process (clipboard integration for tmux, 
# glances system monitor), history options, editor, all truncated for brevity.
# use vim mode
bindkey -v
#show insert/normal mode in prompt
function zle-line-init zle-keymap-select {
    RPS1="${${KEYMAP/vicmd/NORMAL}/(main|viins)/INSERT}"
    RPS2=$RPS1
zle reset-prompt
}
zle -N zle-line-init
zle -N zle-keymap-select
# rebind ctrl-r
bindkey -M vicmd '^R' history-incremental-search-backward
bindkey -M viins '^R' history-incremental-search-backward

Full configs: https://github.com/yramagicman/dotfiles

Just the .zshrc: https://github.com/yramagicman/dotfiles/blob/master/.zshrc

custom plugins:

Upvotes: 1

Views: 4517

Answers (2)

gib
gib

Reputation: 2131

EDIT: Alternative that doesn't break Esc/ search:

accept-line() { [ -z "$BUFFER" ] && zle up-history; zle ".$WIDGET"; }
zle -N zle-line-init

Redefine default Enter command so it inserts last command if the buffer is empty. Inspired by this answer.


ORIGINAL:

I have this in my .zshrc:

last_if_empty() {
  [ -z "$BUFFER" ] && zle up-history
  zle accept-line
}
zle -N last_if_empty
bindkey -M viins '^M' last_if_empty

It remaps Enter to Run last command if nothing has been typed on the screen.

Unfortunately it seems to break Esc/ search (Enter key doesn't work). I use Ctrl+R so it doesn't bother me, but might be a dealbreaker.

Upvotes: 0

Adaephon
Adaephon

Reputation: 18429

To get the last command from the history you can use the up-history widget. This is by default bound to Ctrl+P in vicmd mode, so pressing Esc followed by Ctrl+P and then Enter (which invokes the widget accept-line) would do the trick.

If you want to bind it to a single shortcut, you have to write your own widget. You can add this to your ~/.zshrc:

# define function that retrieves and runs last command
function run-again {
    # get previous history item
    zle up-history
    # confirm command
    zle accept-line
}

# define run-again widget from function of the same name
zle -N run-again

# bind widget to Ctrl+X in viins mode
bindkey -M viins '^X' run-again 
# bind widget to Ctrl+X in vicmd mode
bindkey -M vicmd '^X' run-again

For the example I chose Ctrl+X as shortcut because by default it is unbound in vicmd mode and self-inserts in in viins mode, whereas Ctrl+W is already bound to vi-backward-kill-word in viins. Of course you could overwrite the default binding if you do not use it anyway or bind the widget only in mode.

Upvotes: 5

Related Questions