6

In zsh, the key combination Alt+. is bound to insert-last-word, and it will insert the last argument from previous command on the current command line.

I am looking for a keybinding / shortcut that will act similar to Alt+., except that it will paste all the arguments from previous command on the command line .

I know I can type !* and zsh will interpret it as "reuse all arguments from previous command". But this is not quite what I am looking for. Also, it does not actually paste the arguments so that I can see them, zsh only interprets !* as such. I could hit Tab to have it expanded, but that is yet another command necessary.

I would prefer to have this as a key combination such as Alt+something, instead of having to type '!*' and hitting tab

How can I do this ?

UPDATE

After using the widget extensively for few years, I found few things that bother me: (I have the widget bound to Alt + /)

  1. it works OK once, but when I repeat again, it cycles through my zsh history from the beginning of my .zsh_history file.

Instead, I would like the widget to go backwards, from most recent history to the beginning.

  1. again, when it cycles from the beginning of my zsh history, the space between the command, and the arguments-to-be-completed is removed.

ie:

type some command with arguments:

echo 111 222 333

use widget to complete arguments from previous command:

printf <WIDGET>
printf 111 222 333

above works as expected. But when I press WIDGET again, it suddenly behaves like this:

printf <WIDGET>
printf111 222 333

ie the space between command and argumens was removed

  1. lastly, I would like to bind another key, for instance Alt+\ to do the reverse, so that when I press Alt + / one too many times, I can go back one step.
1
  • Note that !* expands to all but the first token from the previous history entry, not all arguments from previous command. If the previous entry was (echo foo; echo bar), !* expands to echo foo; echo bar) not bar. Is that what you want? Aug 26, 2019 at 15:08

1 Answer 1

5
+250

To insert all but the first word of the previous history entry, you could define a custom widget like:

insert-last-words() {
  emulate -L zsh
  set -o extendedglob # for the # wildcard operator

  local direction
  case $WIDGET in
    (*-reverse) direction=-1;;
    (*) direction=1;;
  esac

  if [[ ${WIDGET%-reverse} = ${LASTWIDGET%-reverse} ]]; then
    # subsequent invocations go further back in history like
    # insert-last-word

    (($+INSERT_LAST_WORDS_SKIP_UNDO)) ||
      NUMERIC=1 zle undo # previous invocation

    # we honour $NUMERIC for Alt+4 Alt+/ to go back 4 in history
    ((INSERT_LAST_WORDS_INDEX += ${NUMERIC-1} * direction))
  else
    # head of history upon first invocation
    INSERT_LAST_WORDS_INDEX=0
  fi
  unset INSERT_LAST_WORDS_SKIP_UNDO

  local lastwords
  local cmd=${history:$INSERT_LAST_WORDS_INDEX:1}

  # cmdline split according to zsh tokenisation rules
  lastwords=(${(z)cmd})

  if (($#lastwords <= 1)); then
    # return failure (causing beep or visual indication) when the
    # corresponding cmd had no arguments. We also need to remember
    # not to undo next time.
    INSERT_LAST_WORDS_SKIP_UNDO=1
    return 1
  fi

  # remove first word (preserving spacing between the remaining
  # words).
  cmd=${cmd##[[:space:]]#$lastwords[1][[:space:]]#}
  LBUFFER+=$cmd
}

zle -N insert-last-words
zle -N insert-last-words-reverse insert-last-words
bindkey '\e/' insert-last-words
bindkey '\e\\' insert-last-words-reverse
4
  • the widget works perfectly Aug 26, 2019 at 17:53
  • I have updated the original question with description of the issues I am having, and created a bounty. Sep 14, 2020 at 6:58
  • @MartinVegter, see edit. Sep 14, 2020 at 7:59
  • works perfectly now. Thank you very much. Sep 14, 2020 at 15:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .