Zsh autocompletion: Difference between revisions

From wikinotes
 
(16 intermediate revisions by the same user not shown)
Line 30: Line 30:
</blockquote><!-- Locations -->
</blockquote><!-- Locations -->


= Loading Completions =
= Notes =
<blockquote>
<blockquote>
<source lang="bash">
{|
fpath=(~/.zsh/completion $fpath) # add completion dir to $fpath
| [[zsh completion configuration]]
autoload -U compinit            # init autocompletion
|}
compinit
</blockquote><!-- Configuration -->
</source>
 
The cleanest way to reload your autocompletion script is:
 
* open a new terminal
* <code>autoload -U ~/.zsh/completion/_yourcompleter:t</code>
</blockquote><!-- Loading Completions -->


= Syntax =
= Syntax =
<blockquote>
<blockquote>
== Basics ==
{|
<blockquote>
| [[zsh completion basics]]
Now that we have created <code>~/.zsh/completion</code>, and have added it to <code>$fpath</code>,
|-
you can start creating autocompletion files. First, some things to know:
| [[zsh completion testing]]
 
|-
* zsh autocompletion scripts are written in SHELL. <code>_arguments</code> is a function written in shellscript, and each argument you add to it is an argument against the shell script. That should give you some hints about the expected syntax, and how it is expanded.
| [[zsh completion examples]]
* zsh autocompletions do not need to be paired with any actual function. It compares the word typed on the shell against the list.
|-
* zsh autocompletion scripts are sourced once, when the shell is first entered. You can reload them using a custom function (see above)
!colspan=1| Completer Functions
 
|-
I highly recommend reading Completion/Unix/_zpool. It is fairly short, and very understandable.
| [[zsh completion _arguments]]
</blockquote><!-- basics -->
|-
 
| [[zsh completion _describe]]
== Filesystem and Setup ==
|-
<blockquote>
| [[zsh completion _alternative]]
Each program has it's own autocompletion script. It is named after the program with a prefix of '_'.
|-
The first line in the program should be '#compdef <programName>'
|}
 
</blockquote><!-- Syntax -->
<source lang="bash">
touch ~/.zsh/completion/_hello
 
#### ~/.zsh/completion/_hello
#compdef hello
 
...
####
</source>
</blockquote><!-- filesystem and setup -->
 
== Special Filetypes ==
<blockquote>
Standard executables are recognized on for autocompletion ('git' uses '_git').
This gets complicated with aliases and scripts with extensions (ex: script.py).
You can manually tell compinit to be used for certain files with the compinit
command.
 
<source lang="bash">
compdef _wsearch wsearch.py
 
#### _wsearch
#compdef wsearch wsearch.py -p *.py                            ## the '-p' flag to compinit makes it a regex match
 
</source>
</blockquote><!-- Special Filetypes -->
 
== Actions ==
<blockquote>
<source lang="bash">
_normal                --> A normal argument. Expects a word (this seems to be the default)
_files                --> Complete filepaths
_net_interfaces        --> Net Interfaces
_users                --> users
_groups                --> groups
_parameters            --> expect a word argument to follow (can be file, word... pretty much anything. Can be restricted.)
</source>
</blockquote><!-- Actions -->
 
== _arguments ==
<blockquote>
<code>_arguments</code> expects a bash array of arguments. Each argument is a string following
the format laid out below.
 
<source lang="bash">
"(exclude  )--opt  [description  ]:message:action"
 
(exclude)      --> If any flag named within exclude has been used, don't autocomplete with this option.
--opt          --> The commandline flag (you may have arguments without flags)
[description] --> The Description displayed next to the flag on autocompletion
message        --> Displayed Text? Unsure what this does...
action        --> One of ZSH's custom autocompletion actions like _files (which autocompletes to filepaths)
</source>
 
'''Argument Examples'''
<syntaxhighlight lang="bash">
'-o[output file]'        # `foo -o`
{-h,--help}'[show help]'  # `foo -h`  OR  `foo --help`
'1:foo:_normal'          # positional arg $1 completes to anything (usually defaults to files)
'2:foo:_directories'      # positional arg $2 completes to directories
'*:foo:(one two three)'  # completes 1x optional un-named param (rest arg) to 'one', 'two' or 'three'
'*::foo:(one two three)'  # completes unlimited optional un-named params (rest args) to 'one', 'two' or 'tree'
</syntaxhighlight>
 
 
''' More Clear Way of Defining Args '''
<source lang="bash">
#compdef wsearch wsearch.py
 
local arguments
 
arguments=(
    {-b,-m}'[Few words of description.]'
    {-B,--always-make}'[Short and long variant.]'
    {-C,--directory}'[This description is on the next line.]'
    )
 
_arguments -s $arguments
</source>
 
<source lang="bash">
echo {-b,-m}'[abd def hij]'
>> -b[abd def hij] -m[abd def hij]
 
echo {-b,\
-m}'[abd def hij]'
 
</source>
 
=== Flag Based Arguments ===
<blockquote>
Each Flag based argument is made of a single SHELL argument.
Just like shell you can break up the arguments onto separate lines,
by using the '\' character.<br>
 
Each argument consists of 3 parts:
* <code>( 'right' 'center' )</code> Do not autocomplete current entry if one of these flags has been used.
* <code>-left</code> The name of your argument
* <code>[ this is my description ]</code>  Description that appears when flag is suggested
 
 
<source lang="bash">
#compdef hello
 
_arguments \
    "1::filename:_files" \                                                                ## Positional and Flag based arguments can be mixed
  "('--right' '--center')--left    [ align text to the left ]"\
  "(                    )--stuff  [ do something else neat ]"
 
</source>
</blockquote><!-- Flag Based Arguments -->
 
=== Positional Arguments ===
<blockquote>
Positional arguments seem to replace keyword arguments in use.
 
* <code>1</code> The argument position
* <code>:heroes:</code> A label for the arguments
* <code>('batman' 'cat woman' 'robin')</code> Every option that is allowed to be presented on autocompletion
 
<source lang="bash">
_arguments \
  "1:heroes:('batman' 'bat girl' 'robin')"\                ## First Argument Autocompletions
  "2:villains:('joker' 'bane')"\                            ## Second Argument Autocompletions
    "*:ambiguous:('catwoman' 'lex luthor')"                ## Every Remaining Argument will be one of these
</source>
 
 
</blockquote><!-- positional arguments -->
 
=== Optional Arguments ===
<blockquote>
Optional Arguments are marked by a double <code>::</code>. Optional arguments are anchored
to positional arguments. In the following case, after 'interface', you have the option to add
a file before the second positional argument 'blah'.
 
<source lang="bash">
_arguments \
    "1:interface:_net_interfaces" \
    "::optional:_files" \
    "2:blah[testing second var]"
 
 
### So for instance if you were completing an argparse list:
_arguments \
    "1:first:_parameters" \                ## This will allow you to complete as many parameters as you'd like
    "::next:_parameters" \                ## and as soon as you enter a '-' it will start completing the next flags.
    "-s[search]" \
    "-m[match]"
 
</source>
</blockquote><!-- Optional Arguments -->
 
=== Subparsers ===
<blockquote>
Sometimes, the use of a particular flag opens up a chain of
possible sub-commands. (ex: git add, git commit, ...). This
can be handled in ZSH as follows:
 
<source lang="bash">
#compdef wtest
 
_wtest() {
    local context state line expl implementation
    local -a subcmds
 
    ## list of arguments with their own parser-options
    subcmds=(
        archive
        display
        data
    )
 
    ## Determine completer for subcommand
    ## (based on 1st positional argument)
    _arguments -C \
        {-h,--help}'[show help information]' \
        '1:subcommand:compadd -a subcmds' \
        '*:: :->subcmd' && return
 
    service="$words[1]"
    curcontext="${curcontext%:*}-$service:"
 
    ## completers for each subcommand of
    ## 1st argument.
    case $service in
    (archive)
        _arguments -A "-*" \
            '-a[append files to archive]' \
            '-e[extract files to archive]'
        ;;
 
    (display)
        _arguments -A "-*" \
            '-n[next file in archive]' \
            '-p[previous file in archive]'
        ;;
 
    (data)
        _arguments -A "-*" \
            '-s[shuffle order of wallpapers]'
        ;;
 
 
    (*)
        _message "unknown sub-command: $service"
        ;;
    esac
 
}
 
_wtest "$@"
 
</source>
 
 
</blockquote><!-- subparsers -->
 
=== Dynamic Arguments ===
<blockquote>
Generate autocomplete options based on the options that were entered
previously. This can do just about anything you need it to, it's really cool.
This isn't my example, and I haven't yet needed this level of flexibility yet.
This example is just so clear, I had to add it.<br>
 
Some Background Info:
* <code>$words[]</code> is an array of all arguments used so far
* <code>$state</code> is set in <code>_arguments</code>. In the below example. If completion is used for '1', the value of $state will be 'country'
 
<source lang="bash">
#compdef hello
_hello() {
    local curcontext="$curcontext" state line                ## These two lines are required,
    typeset -A opt_args                                                ## they initialize some internal variables
                                                                            ## for your function
 
    _arguments \                                                        ## Initial Arguments
        '1: :->country'\
        '*: :->city'
    case $state in                                                    ## Expand Arguments based on their category
    country)
        _arguments '1:Countries:(France Germany Italy)'
    ;;
    *)
        case $words[2] in
        France)
            compadd "$@" Paris Lyon Marseille
        ;;
        Germany)
            compadd "$@" Berlin Munich Dresden
        ;;
        Italy)
            compadd "$@" Rome Napoli Palermo
        ;;
        *)
            _files
        esac
    esac
}
_hello "$@"                                                            ## Passes _hello() function all arguments that was passed to the script.
 
</source>
</blockquote><!-- Dynamic Arguments -->
</blockquote><!-- _arguments -->
</blockquote><!-- Syntax -->

Latest revision as of 22:58, 24 July 2021