25

In zsh (as well as bash) you can use some history word expansions to denote arguments from previous commands.

This example shows getting the 2nd parameter from the previous command in history with !:# expansion:

% echo foo bar baz
foo bar baz
% echo !:2
echo bar
bar

I often forget exactly what # parameter a particular argument is and typing !:# isn't always that quick when I do remember which arg it is. I know about meta-. to replace the last argument, but sometimes it isn't the last arg that I want.

I'd like to add the arguments from the previous command as suggestions to complete any command that I'm typing in zsh.

I was able to figure out how to create a shell function that can create an array of arguments (0..N) from the last command and bind it to a particular command.

_last_command_args() {
    last_command=$history[$[HISTCMD-1]]
    last_command_array=("${(s/ /)last_command}") 
    _sep_parts last_command_array
}

# trying to get last_command_args to be suggested for any command, this just works for foo
compdef _last_command_args foo

This is what it looks like for completing just foo where I hit the tab key at <TAB>:

% echo bar baz qux
bar baz qux
% foo <TAB>
bar   baz   echo  qux 

This works great for completing the command "foo", but I'd like these to be options on any zsh expansion that I do. I think it's got something to do with the zstyle completer stuff, but after a few hours of hacking around I realized I'm out of my depth.

How can I get the arguments from my previous command as suggested completions for any command in zsh?

I've got my full zshrc compinstall file shared out on bitbucket if that helps. Lots of it is cribbed from a variety of sources and some of it I've hacked together myself.

UPDATE:

@Julien Nicoulaud's answer got me close, I'm marking it as accepted as it got me where I needed to get.

With my particular config, using the suggested:

zstyle ':completion:*' completer _last_command_args _complete

Didn't quite work for me as it was causing tab completion to only display the list of arguments from the last command (though it'd actually complete with filenames as well, just not display them). Changing the order to _complete _last_command_args did the reverse. It'd display the normal filenames, but not last_command_args

I'm guessing this has something to do with the way completer works. I think it only displays the output from the first method that returns successfully, but I'm having trouble parsing the zsh source to understand fully what's going on. I was able to tweak my method to include a call to _complete so that it showed both last argument commands as well as the regular autocomplete stuff. Not quite as separated, but works well enough for me.

Here's the full function I used along with the other zstyle stuff I have:

# adds the arguments from the last commadn to the autocomplete list
# I wasn't able to get this to work standalone and still print out both regular
# completion plus the last args, but this works well enough.
_complete_plus_last_command_args() {
    last_command=$history[$[HISTCMD-1]]
    last_command_array=("${(s/ /)last_command}") 
    _sep_parts last_command_array
    _complete 
}


_force_rehash() {
  (( CURRENT == 1 )) && rehash
  return 1  # Because we didn't really complete anything
}

zstyle ':completion:::::' completer _force_rehash _complete_plus_last_command_args _approximate 

Other zstyle lines I have, not necessary for this to work, but could affect why this works for me:

zstyle -e ':completion:*:approximate:*' max-errors 'reply=( $(( ($#PREFIX + $#SUFFIX) / 3 )) )'
zstyle ':completion:*:descriptions' format "- %d -"
zstyle ':completion:*:corrections' format "- %d - (errors %e})"
zstyle ':completion:*:default' list-prompt '%S%M matches%s'
zstyle ':completion:*' group-name ''
zstyle ':completion:*:manuals' separate-sections true
zstyle ':completion:*' menu select
zstyle ':completion:*' verbose yes

Now, if I'm in a directory with file1.txt and file2.txt, and my last command was echo foo bar baz, I get this for autocomplete which is just what I wanted:

% ls
bar   baz   echo  foo 
- files -
file1.txt   file2.txt 
3
  • 3
    This is a spectacular idea Commented Dec 29, 2010 at 21:14
  • 2
    To supplement ^[ . (insert-last-word), I like copy-earlier-word, which cycles between words of the line reached by insert-last-word. For your .zshrc: autoload copy-earlier-word && zle -N copy-earlier-word && bindkey '^[,' copy-earlier-word Commented Dec 29, 2010 at 21:30
  • @Gilles thanks! That gets me closer to the functionality if I'm not able to get full autocompletion. ~80% of the time, if it's not the last argument, it's the 2nd to last argument that I want and C-. + C-, is pretty good.
    – Ted Naleid
    Commented Dec 29, 2010 at 22:26

1 Answer 1

6

You can add your completer to the list of completers used by default:

zstyle ':completion:*' completer _last_command_args _complete
3
  • 1
    If there is any _last_command_args completions, the others aren't shown. How do you get them in addition to the usual ones? Commented Jan 20, 2011 at 21:06
  • 1
    Thanks for the answer, this got me close enough to get something working for me, so I'm accepting it. What I did differently is noted as an update to the question above. Your solution didn't print out both the arguments from the last command as well as files, etc from the normal completer. I composed _complete inside the function I created and it works just like I wanted it to.
    – Ted Naleid
    Commented Jan 21, 2011 at 5:02
  • You are right, my answer is not correct.
    – nicoulaj
    Commented Jan 22, 2011 at 15:31

You must log in to answer this question.

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