3

I'm trying to create an autoloaded completion file for zsh. To do so, I've added a _myprog file in a folder that is in my fpath.

This file goes roughly like this:

#compdef myprog
_myprog () {
    local cmd
    if (( CURRENT > 2)); then
        cmd=${words[2]}
        curcontext="${curcontext%:*:*}:myprog-$cmd"
        (( CURRENT-- ))
        shift words
        case "${cmd}" in
          list|ls)
            _arguments : "--limit[Max depth]" "--folders[Print flat list of folders]"
            _describe -t commands "myprog list" subcommands
          ;;
          insert)
            _arguments : "--force[Overwrite file]" "--append[Append to existing data]"
            _describe -t commands "myprog insert" subcommands
            _myprog_complete_folders
          ;;
        esac
    else
        local -a subcommands
        subcommands=(
          "insert:Insert a new file"
          "list:List existing files"
        )
        _describe -t command 'myprog' subcommands
        _arguments : "--yes[Assume yes]" 
        _myprog_complete_files
    fi
}

_myprog_complete_files () {
    _values 'files' $(myprog ls)
}

_myprog_complete_folders () {
    _values 'folders' $(myprog ls --folders)
}

_myprog

So, my program has two scenarios where I want auto-completion:

  1. When used without subcommand, I'd like to specify a path in a remote location. This is done by the function _myprog_complete_files thanks to the _value function. The file list is provided by running myprog ls, which prints each remote file on a new line.

  2. When inserting a new remote file, I'd like to be able to autocomplete the folders name, which is done in the function _myprog_complete_folders using _value too, but this time the folder list is generated using myprog ls --folder instead, which only prints folders.

So far so good... Excepted that zsh is inserting a space after the folder name when I'm using <tab><tab> to have the value list displayed and I'm trying to type the file name after the folder name.

For example:

$ myprog insert web<tab><tab><tab>
 -- folders --
web/                          web/foo/                web/bar/

is supposed to select web/foo and it effectively does so and autocomplete to myprog insert web/foo/  but notice the trailing space! So if I'm trying to type directly the name of the file I want to insert once I've selected the folder I wanted using <tab>, say I want to call it baz, I'm ending up with myprog insert web/foo/ baz which is not my goal at all.

I've tried browsing through the zsh doc, without success. I've for example tried to set zstyle ':completion::complete:myprog-insert:' add-space false to no avail, both in my .zshrc and in my _myprog file. I couldn't even see a difference when having it set or not.

I'm guessing I must be missing something, since it seems to be a desirable feature to be able to type directly after autocompletion in order to specify a parameter or so.

2

The add-space code in $fpath[-1]/_expand does not appear to be involved with your completion via _values; this can be checked by setting add-space to false everywhere, or by calling _complete_debug and seeing what code the completion went through:

% bindkey -M viins "^t" _complete_debug
% foo 
Trace output left in /tmp/zsh438foo2 (up-history to view)

One solution is to use the -s ... feature of _values, which can optionally insert a separator that is normally used to join multiple values, and then to disable that feature if the separator has been used:

#compdef foo
local curcontext="$curcontext" state line

choices=(aaa bbb ccc)

_arguments '1:dir:->folders' && return 0

case "$state" in
  folders)
    if ! compset -P '*/'; then
      _values -s / folders $choices
    fi
  ;;
esac

Which on a foo atab should append a slash, and then since / now appears the compset test should prevent additional completions.

  • While this is effectively not the ideal way to do it, since it keeps trying to complete after typing stuff, it does effectively solve my problem. I tried using -S (which didn't work) but didn't try using -s, since it refused the empty separator... I should have tried using slashes directly (they were currently in my _values already, so I had to remove the trailing slashes for this method to work.) – Lery Nov 19 '18 at 13:02
  • 1
    @Lery figured it out, compset can look for the / and based on that disable completion – thrig Nov 19 '18 at 15:08
  • I think I've found a bug with this compset solution: suppose I have a path like this: foo/bar/zen; fee/zen/bar; fee/foo/bar; When I type f and try to autocomplete with tab, then press enter to select fee/ and type the first letter of the next subfolder, say z, the autocompletion will show "no matches found". It's even worse if I type manually fee/z then the autocompletion directly does nothing. I'm guessing it's because of the way compset is modifying the iprefix, maybe, but I'm not sure. – Lery Nov 27 '18 at 14:50
  • What I want to do seems very close to what is asked in this question: stackoverflow.com/questions/42526436/… it's a shame it remained unanswered. And it seems _values is not a solution. I've also tried specifying -S / and specifying in my values that they accept an argument, but to no avail. When I'm typing we<tab> to get to web/, if I keep typing web/f<tab>, it won't keep completing, no matter what solution I'm trying. Would you happen to have any idea to solve this? – Lery Dec 19 '18 at 17:40
  • The SO question linked in the comment above now has an answer, which solve a similar issue I was having. :-) – Thomas K Feb 21 at 13:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.