diff --git a/_posts/2014-07-17-writting-own-completions-p1.html b/_posts/2014-07-17-writting-own-completions-p1.html new file mode 100644 index 0000000..bc2a6c4 --- /dev/null +++ b/_posts/2014-07-17-writting-own-completions-p1.html @@ -0,0 +1,148 @@ +--- +category: en +type: paper +hasTr: true +layout: paper +tags: linux, development +title: Writting own Shell completions. Zsh +short: writting-own-completions-p1 +description: Some basics of creating a completion files for own application are described in these articles. +--- +
While developing one of my projects I have wanted to add completion files. I have already tried to create these files, but I was too lazy to read some manuals about it.
+ +There are some possible ways to create zsh completion file. In this article I will describe only one of them, which provides a lot of opportunities, but does not require a lot of costs (such as regular expressions).
+ +Lets consider the example of my application, which has a part of help message that looks like this:
+ +{% highlight bash %} +netctl-gui [ -h | --help ] [ -e ESSID | --essid ESSID ] [ -с FILE | --config FILE ] + [ -o PROFILE | --open PROFILE ] [ -t NUM | --tab NUM ] [ --set-opts OPTIONS ] +{% endhighlight %} + +Here is a flag list: +
-h
and --help
do not require any arguments;-e
and --essid
require a string argument without completion;-c
and --config
require a string argument, which is a file;-o
and --open
require a string argument, there is a completion from files in the specified directory;-t
and --tab
require a string argument, there is a completion from the specified array;--set-opts
requires a string argument, there is a completion from the specified array comma separated;It must be specified in the header that it is a completion file and application for which it will complete (may be string if this file provides completions for several applications): + +{% highlight bash %} +#compdef netctl-gui +{% endhighlight %} + +Next there is flags, additional functions and variables declarations. It should be noted that all functions and variables, which will be used for completions, should return arrays. In my case this scheme looks like this (I left empty these functions in this chapter): + +{% highlight bash %} +# variables +_netctl_gui_arglist=() +_netctl_gui_settings=() +_netctl_gui_tabs=() +_netctl_profiles() {} +{% endhighlight %} + +Then there are main functions, which will be called for completion of specific application. In my case this there is only one applications, so there is only one function: + +{% highlight bash %} +# work block +_netctl-gui() {} +{% endhighlight %} + +And finally without isolation in a separate function there is a small shamanism, which declares a dependence "application-function": + +{% highlight bash %} +case "$service" in + netctl-gui) + _netctl-gui "$@" && return 0 + ;; +esac +{% endhighlight %} +
+ +As it was said above, there are some different ways to create these files. In particular they differ in the flag declaration and their further processing. In my case I will use _arguments
command, which require a specific format of variables: FLAG[description]:MESSAGE:ACTION
. The last two fields are not required and, as you will see below, are not needed in some cases. If you want to add two flags for an action (short and long format), then the format is a little bit complicated: {(FLAG_2)FLAG_1,(FLAG_1)FLAG_2}[description]:MESSAGE:ACTION
. It should be noted that if you want to create completions for two flags but some flags have not a second format. you will should to add following line: {FLAG,FLAG}[description]:MESSAGE:ACTION
. MESSAGE
is a message which will be shown, ACTION
is an action whichh will be performed after this flag. In this tutorial ACTION
will be following: ->STATE
.
So, according to our requirements, flags declaration will be following: + +{% highlight bash %} +_netctl_gui_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' +) +{% endhighlight %} +
+ +In my case there are two static arrays (which will not be changed): + +{% highlight bash %} +_netctl_gui_settings=( + 'CTRL_DIR' + 'CTRL_GROUP' +) + +_netctl_gui_tabs=( + '1' + '2' +) +{% endhighlight %} + +And there is a dynamic array, which should be generated each time. In my case it is a list of files in the specified directory (by the way it may be done by means of zsh): + +{% highlight bash %} +_netctl_profiles() { + print $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") +} +{% endhighlight %} +
+ +Remember, there was something about a state above? It is stored in the variable $state
and in this function we will check what it is to choose the appropriate action. At the beginning of the function we should call _arguments
with our flags.
+
+{% highlight bash %}
+_netctl-gui() {
+ _arguments $_netctl_gui_arglist
+ case "$state" in
+ essid)
+ # do not completion, wait for string
+ ;;
+ files)
+ # completion from files in this directory
+ _files
+ ;;
+ profiles)
+ # completion from function
+ # first variable is a description
+ # second variable is a completion array
+ _values 'profiles' $(_netctl_profiles)
+ ;;
+ tab)
+ # completion from array
+ _values 'tab' $_netctl_gui_tabs
+ ;;
+ settings)
+ # completion from array
+ # flag -s sets separator and enables multi-selection
+ _values -s ',' 'settings' $_netctl_gui_settings
+ ;;
+ esac
+}
+{% endhighlight %}
+
File should be places to /usr/share/zsh/site-functions/
with any name (it is recommended to set prefix to _
). You may found the example in my repository.
The additional information may be found in zsh-completions repository. For example there is this How-To. And also there are a lot of examples.
diff --git a/_posts/2014-07-17-writting-own-completions-p2.html b/_posts/2014-07-17-writting-own-completions-p2.html new file mode 100644 index 0000000..9ff017a --- /dev/null +++ b/_posts/2014-07-17-writting-own-completions-p2.html @@ -0,0 +1,140 @@ +--- +category: en +type: paper +hasTr: true +layout: paper +tags: linux, development +title: Writting own Shell completions. Bash +short: writting-own-completions-p2 +description: Some basics of creating a completion files for own application are described in these articles. +--- +While developing one of my projects I have wanted to add completion files. I have already tried to create these files, but I was too lazy to read some manuals about it.
+ +Bash, unlike zsh, demands some dirty workarounds for completions. Cursory have Googled, I have not found a more or less normal tutorials, so it is based on the available pacman
completion files in my system.
Lets consider the example of the same my application. I remind you that a part of help message is as follows:
+ +{% highlight bash %} +netctl-gui [ -h | --help ] [ -e ESSID | --essid ESSID ] [ -с FILE | --config FILE ] + [ -o PROFILE | --open PROFILE ] [ -t NUM | --tab NUM ] [ --set-opts OPTIONS ] +{% endhighlight %} + +Here is a flag list: +
-h
and --help
do not require any arguments;-e
and --essid
require a string argument without completion;-c
and --config
require a string argument, which is a file;-o
and --open
require a string argument, there is a completion from files in the specified directory;-t
and --tab
require a string argument, there is a completion from the specified array;--set-opts
requires a string argument, there is a completion from the specified array comma separated;Here all variables must return an array. And there no specific formats. First we declare the flags and then we describe all other variables. As I am not going to describe the functions in more detail below I remind you that _netctl_profiles()
should be generated each time:
+
+{% highlight bash %}
+# variables
+_netctl_gui_arglist=()
+_netctl_gui_settings=()
+_netctl_gui_tabs=()
+_netctl_profiles() {}
+{% endhighlight %}
+
+Then there are main functions, which will be called for completion of specific application. In my case this there is only one applications, so there is only one function:
+
+{% highlight bash %}
+# work block
+_netctl-gui() {}
+{% endhighlight %}
+
+And finally again without isolation in a separate function we create a dependence "function-application":
+
+{% highlight bash %}
+complete -F _netctl_gui netctl-gui
+{% endhighlight %}
+
As it was said above there is no specific format, so all available flags declare by array: + +{% highlight bash %} +_netctl_gui_arglist=( + '-h' + '--help' + '-e' + '--essid' + '-c' + '--config' + '-o' + '--open' + '-t' + '--tab' + '--set-opts' +) +{% endhighlight %} +
+ +I just give a function that looked like this in zsh: + +{% highlight bash %} +_netctl_profiles() { + print $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") +} +{% endhighlight %} + +Bash does not allow to do so, so this function should be a little changed: + +{% highlight bash %} +_netctl_profiles() { + echo $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") +} +{% endhighlight %} +
+ +The variable COMPREPLY
responds for completion in Bash. To keep track of the current state function _get_comp_words_by_ref
must be called with parameters cur
(current flag) and prev
(previous flag, it is the state). Also some point for case are needed (variables want*
). Function compgen
is used for completion generation. A list of words is given after flag -W
. (Also there is flag -F
which requires a function as argument, but it gives warning for me.) The last argument is a current string to which you want to generate completion.
So, here is our function: + +{% highlight bash %} +_netctl_gui() { + COMPREPLY=() + wantfiles='-@(c|-config)' + wantprofiles='-@(o|-open|s|-select)' + wantsettings='-@(-set-opts)' + + wanttabs='-@(t|-tab)' + _get_comp_words_by_ref cur prev + + if [[ $prev = $wantstring ]]; then + # do not completion, wait for string + COMPREPLY=() + elif [[ $prev = $wantfiles ]]; then + # completion from files in this directory + _filedir + elif [[ $prev = $wantprofiles ]]; then + # completion from function + COMPREPLY=($(compgen -W '${_netctl_profiles[@]}' -- "$cur")) + elif [[ $prev = $wanttabs ]]; then + # completion from array + COMPREPLY=($(compgen -W '${_netctl_gui_tabs[@]}' -- "$cur")) + elif [[ $prev = $wantsettings ]]; then + # completion from array + # flag -S add a comma after completion, but does not enable multi-selection =( + COMPREPLY=($(compgen -S ',' -W '${_netctl_gui_settings[@]}' -- "$cur")) + else + # show all available flags + COMPREPLY=($(compgen -W '${_netctl_gui_arglist[@]}' -- "$cur")) + fi + + true +} +{% endhighlight %} +
+ +File should be places to /usr/share/bash-completion/completions/
with any name. You may found the example in my repository.
Существует несколько возможных вариантов написания файла автодополнения для zsh. В случае данной статьи я остановлюсь только на одном из них, который предоставляет большие возможности и не требует больших затрат (например, работы с регулярными выражениями).
Рассмотрим на примере моего же приложения, часть справки к которому выглядит таким образом:
+ {% highlight bash %} netctl-gui [ -h | --help ] [ -e ESSID | --essid ESSID ] [ -с FILE | --config FILE ] [ -o PROFILE | --open PROFILE ] [ -t NUM | --tab NUM ] [ --set-opts OPTIONS ] @@ -85,6 +86,7 @@ _netctl_gui_arglist=(В нашем случае есть два статических массива (не изменятся ни сейчас, ни через пять минут) (массивы умышленно уменьшены): + {% highlight bash %} _netctl_gui_settings=( 'CTRL_DIR' diff --git a/ru/_posts/2014-07-17-writting-own-completions-p2.html b/ru/_posts/2014-07-17-writting-own-completions-p2.html index 0cc5942..e7525b2 100644 --- a/ru/_posts/2014-07-17-writting-own-completions-p2.html +++ b/ru/_posts/2014-07-17-writting-own-completions-p2.html @@ -15,6 +15,7 @@ description: В данных статьях описываются некото
Bash, в отличие от zsh, требует к себе некоторого велосипедостроения в отношении дополнений. Бегло погуглив, я не нашел более-менее нормальных туториалов, потому за основу были взяты имеющиеся в системе файлы дополнений для pacman
.
Рассмотрим на примере все того же моего приложения. Я напомню, что часть справки к которому выглядит таким образом:
+ {% highlight bash %} netctl-gui [ -h | --help ] [ -e ESSID | --essid ESSID ] [ -с FILE | --config FILE ] [ -o PROFILE | --open PROFILE ] [ -t NUM | --tab NUM ] [ --set-opts OPTIONS ] @@ -49,31 +50,13 @@ _netctl_profiles() {} _netctl-gui() {} {% endhighlight %} -Далее, опять, без выделения в отдельную функцию делаем соответствие функция-команда: +Далее, опять, без выделения в отдельную функцию делаем соответствие "функция-команда": {% highlight bash %} complete -F _netctl_gui netctl-gui {% endhighlight %} -Приведу только функцию, которая в zsh выглядела таким образом: - -{% highlight bash %} -_netctl_profiles() { - print $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") -} -{% endhighlight %} - -В bash так не получится, пришлось чуть-чуть изменить: - -{% highlight bash %} -_netctl_profiles() { - echo $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") -} -{% endhighlight %} -
-Как было сказано выше, особого формата тут нет, доступные флаги располагаются просто массивом: @@ -94,6 +77,24 @@ _netctl_gui_arglist=( {% endhighlight %}
+Приведу только функцию, которая в zsh выглядела таким образом: + +{% highlight bash %} +_netctl_profiles() { + print $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") +} +{% endhighlight %} + +В bash так не получится, пришлось чуть-чуть изменить: + +{% highlight bash %} +_netctl_profiles() { + echo $(find /etc/netctl -maxdepth 1 -type f -printf "%f\n") +} +{% endhighlight %} +
+За дополнение в bash отвечает переменная COMPREPLY
. Для отслеживания текущего состояния нужно вызвать функцию _get_comp_words_by_ref
с параметрами cur
(текущая опция) и prev
(предыдущая, собственно состояние). Ну и нужно несколько точек, на которых сворачивать в определенную часть case (переменные want*
). Для генерации дополнения используется compgen
. После флага -W
ему подается список слов. (Есть еще флаг -F
, который вызывает функцию, но у меня он помимо этого еще и ворнинг выдает.) Последним аргументом идет текущая строка, к которой и нужно генерировать дополнение.