📜 ⬆️ ⬇️

Writing your autocompletions for Shell. Part 1: zsh


These articles (I decided that thematically it is better to break into two parts) describe some of the basics of creating auto-completion files for your own program.


Preamble

In the process of developing one of your projects, there was a desire to add also auto-completion files (just don’t ask why). Fortunately, I somehow took it upon myself to write such things, but then I was too lazy to read anything, and did not master it.

Introduction

There are several options for writing an autocomplete file for zsh. In the case of this article, I will focus only on one of them, which provides great opportunities and does not require large expenditures (for example, working with regular expressions).
')


Consider the example of my own application, part of the help for which looks like this:

proga [ -h | --help ] [ -e ESSID | --essid ESSID ] [ - FILE | --config FILE ] [ -o PROFILE | --open PROFILE ] [ -t NUM | --tab NUM ] [ --set-opts OPTIONS ] 

List of flags:


File structure

The header should indicate that it is an auto-completion file and for which applications it serves (as a string, if the file contains auto-completion for several commands):

 #compdef proga 

Next comes a description of the flags, auxiliary functions and variables. Note that the functions and variables that will be used for autocompletion should return arrays , not strings. In my case, the scheme looks like this (all functions and variables in this chapter are intentionally left empty):

 # variables _proga_arglist=() _proga_settings=() _proga_tabs=() _proga_profiles() {} 

Then come the main functions that will be called for autocompletion for a particular command. In my case, the command is one, and the function is one:

 # work block _proga() {} 

Then, without allocating to a separate function, there is a slight shamanism associated with the correlation of the application, which was declared in the first line, with the function in the script body:

 case "$service" in proga) _proga "$@" && return 0 ;; esac 


Flags

As I said in the introduction, there are several ways to create such files. In particular, they differ in the declaration of flags and their further processing. In this case, I will use the _arguments command, which requires a specific variable format. It looks like this:

 []:: 

The last two fields are optional and, as you will see below, are not needed at all in some places. If you envisage two flags (short and long format) for one action, then the format is slightly more complicated:

 {(_2)_1,(_1)_2}[]:: 

I note that if you want to autocomplete for two types of flags, but some flags do not have a second entry, then you need to duplicate it this way:

 {,}[]:: 

- the message to be shown, - the action to be performed after this flag. In the case of this tutorial, will look like -> .

So, according to our requirements, this declaration of arguments is obtained:

 _proga_arglist=( {'(--help)-h','(-h)--help'}'[show help and exit]' {'(--essid)-e','(-e)--essid'}'[select ESSID]:type ESSID:->essid' {'(--config)-c','(-c)--config'}'[read configuration from this file]:select file:->files' {'(--open)-o','(-o)--open'}'[open profile]:select profile:->profiles' {'(--tab)-t','(-t)--tab'}'[open a tab with specified number]:select tab:->tab' {'--set-opts','--set-opts'}'[set options for this run, comma separated]:comma separated:->settings' ) 


Variable arrays

In our case there are two static arrays (they will not change either now or in five minutes) (the arrays are intentionally reduced):

 _proga_settings=( 'CTRL_DIR' 'CTRL_GROUP' ) _proga_tabs=( '1' '2' ) 

And there is a dynamic array that should be generated each time. It contains, in this case, files in the specified directory (this can be done using zsh, by the way):

 _proga_profiles() { print $(find /some/path -maxdepth 1 -type f -printf "%f\n") } 


Body function


Remember, there was something higher about the condition? It is stored in the $state variable, and a check is made in the function body for what it is equal to in order to select the appropriate actions. At the beginning also need not forget to call _arguments with our flags.

 _proga() { _arguments $_proga_gui_arglist case "$state" in essid) #   ,    ;; files) #     _files ;; profiles) #    #    #     _values 'profiles' $(_proga_profiles) ;; tab) #    _values 'tab' $_proga_tabs ;; settings) #    #  -s      _values -s ',' 'settings' $_proga_settings ;; esac } 


Conclusion


The file is stored in the /usr/share/zsh/site-functions/ directory with an arbitrary, in general, name with the prefix _ . The example file can be fully found in my repository (do not count for advertising, I specifically raised all the names).

Additional information can be found in the zsh-completions repository. For example, there is such a How-To . And there are many examples there.

Source: https://habr.com/ru/post/230421/


All Articles