wpcarro
wpcarro

Reputation: 1546

zsh bindkey with history

In short I'd like to map Ctrl + 2 to the second most recently logged command in my .zsh_history file. Normally I would type !-2 to achieve this. However, to recreate this behavior in a shell function I wrote the following:

function two_back {
  HISTFILE=~/.zsh_history
  command=$(history | tail -n 2 | head -n 1 | cut -c 8-)

  echo -n "$command"
}

This outputs the value that I would expect, so it seems fine to me. In fact, If I just run the function two_back and press <Enter> it works as expected. The issue seems to reveal itself after I bind the function to some keystrokes.

First I convert the function into a zsh widget, so that I can bind it to some keystrokes...

$ zle -N two_back_widget two_back

Then I use the bindkey command to create the mapping...

$ bindkey '^@' two_back_widget

Now when I type Ctrl + 2 my z-shell line editor zle contains the output that I expect. But when I attempt to run the command by pressing <Enter> nothing happens... Can anyone else reproduce this? And does anyone know why this isn't working?

Upvotes: 2

Views: 673

Answers (1)

Adaephon
Adaephon

Reputation: 18339

The reason this is not working is, that while the command was printed to the terminal (output) it was not written in the terminal (input).

In order to actually enter the command like it has been typed into the shell this should work:

function two_back {
   HISTFILE=~/.zsh_history
   command=$(history | tail -n 2 | head -n 1 | cut -c 8-)

   BUFFER="$command"
   CURSOR=$#BUFFER
}

zle -N two_back_widget two_back
bindkey '^@' two_back_widget

Explanation:

  • Within zle widgets BUFFER contains the current command line. It can also be modified. In this case BUFFER is set to the value of command. Writing to BUFFER effectively edits the current command on the command line.
  • CURSOR contains the cursor position on the command line. By setting it to the lengt of BUFFER the cursor will be placed at the end of the input. This is not strictly necessary, without setting it the cursor will remain in the same place. If the command line was previously empty the cursor will be on the very left of the command.

Upvotes: 3

Related Questions