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.

1 Answer 1

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.

5
  • 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, 2018 at 13:02
  • 1
    @Lery figured it out, compset can look for the / and based on that disable completion
    – thrig
    Nov 19, 2018 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, 2018 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, 2018 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, 2019 at 13:23

You must log in to answer this question.

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