From b2904998b8a7c32c7ab8a087cce4852f72be1ec1 Mon Sep 17 00:00:00 2001
From: arcan1s Рассмотрим на примере моего же приложения, часть справки к которому выглядит таким образом: Список флагов:
@@ -35,7 +35,7 @@ netctl-gui [ -h | --help ] [ -e ESSID | --essid ESSID ] [ -с FILE | --config FI
В заголовке должно быть обязательно указано, что это файл дополнений и для каких приложений он служит (можно строкой, если в файле будет содержаться дополнение для нескольких команд):
{% highlight bash %}
-#compdef netctl-gui
+#compdef netctl-gui
{% endhighlight %}
Дальше идет описание флагов, вспомогательные функции и переменные. Замечу, что функции и переменные, которые будут использоваться для дополнения должны возвращать массивы, а не строки. В моем случае схема выглядит примерно так (все функции и переменные в этой главе умышленно оставлены пустыми):
@@ -45,14 +45,14 @@ netctl-gui [ -h | --help ] [ -e ESSID | --essid ESSID ] [ -с FILE | --config FI
_netctl_gui_arglist=()
_netctl_gui_settings=()
_netctl_gui_tabs=()
-_netctl_profiles() {}
+_netctl_profiles() {}
{% endhighlight %}
Затем идут основные функции, которые будут вызываться для дополнения для определенной команды. В моем случае команда одна, и функция одна:
{% highlight bash %}
# work block
-_netctl-gui() {}
+_netctl-gui() {}
{% endhighlight %}
Далее без выделения в отдельную функцию идет небольшое шаманство, связанное с соотнесением приложения, которое было декларировано в первой строке, с функцией в теле скрипта:
@@ -62,7 +62,7 @@ case "$service" in
netctl-gui)
_netctl-gui "$@" && return 0
;;
-esac
+esac
{% endhighlight %}
Файл хранится в директории /usr/share/zsh/site-functions
с произвольным в общем-то именем с префиксом _
. Файл примера полностью может быть найден в моем репозитории.
Файл хранится в директории /usr/share/zsh/site-functions/
с произвольным, в общем-то, именем с префиксом _
. Файл примера полностью может быть найден в моем репозитории.
Дополнительная информация может быть найдена в репозитории zsh-completions. Например, там есть такой How-To. А еще там есть много примеров.
diff --git a/ru/_posts/2014-07-17-writting-own-completions-p2.html b/ru/_posts/2014-07-17-writting-own-completions-p2.html new file mode 100644 index 0000000..3ca8c6b --- /dev/null +++ b/ru/_posts/2014-07-17-writting-own-completions-p2.html @@ -0,0 +1,139 @@ +--- +category: ru +type: paper +hasTr: true +layout: paper +tags: linux, разработка +title: Написание собственных дополнений для Shell. Bash +short: writting-own-completions-p2 +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 ] +{% endhighlight %} + +Список флагов: +
-h
и --help
не требуют аргументов;-e
и --essid
требуют аргумента в виде строки, без дополнения;-c
и --config
требуют аргумента в виде строки, файл с произвольной локацией;-o
и --open
требуют аргумента в виде строки, дополнение по файлам из определенной директории;-t
и --tab
требуют аргумента в виде строки, дополнение из указанного массива;--set-opts
требует аргумента в виде строки, дополнение из указанного массива, разделены запятыми;Здесь все переменные должны возвращать массив. Каких-либо особых форматов тут уже нет. Сначала опишем флаги, потом уже все остальные переменные. Я напомню (так как ниже я уже не буду приводить функции более подробно), что _netctl_profiles()
, в отличие от других переменных, должна возвращать актуальный на данный момент массив:
+
+{% highlight bash %}
+# variables
+_netctl_gui_arglist=()
+_netctl_gui_settings=()
+_netctl_gui_tabs=()
+_netctl_profiles() {}
+{% endhighlight %}
+
+Затем идут основные функции, которые будут вызываться для дополнения для определенной команды. В моем случае команда одна, и функция одна:
+
+{% highlight bash %}
+# work block
+_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 %} +
+ +Как было сказано выше, особого формата тут нет, доступные флаги располагаются просто массивом: + +{% highlight bash %} +_netctl_gui_arglist=( + '-h' + '--help' + '-e' + '--essid' + '-c' + '--config' + '-o' + '--open' + '-t' + '--tab' + '--set-opts' +) +{% endhighlight %} +
+ +За дополнение в bash отвечает переменная COMPREPLY
. Для отслеживания текущего состояния нужно вызвать функцию _get_comp_words_by_ref
с параметрами cur
(текущая опция) и prev
(предыдущая, собственно состояние). Ну и нужно несколько точек, на которых сворачивать в определенную часть case (переменные want*
). Для генерации дополнения используется compgen
. После флага -W
ему подается список слов. (Есть еще флаг -F
, который вызывает функцию, но у меня он помимо этого еще и ворнинг выдает.) Последним аргументом идет текущая строка, к которой и нужно генерировать дополнение.
Таким образом, наша функция выглядит так: + +{% 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 + # не делать дополнения, ждать введенной строки + COMPREPLY=() + elif [[ $prev = $wantfiles ]]; then + # дополнение по существующим файлам + _filedir + elif [[ $prev = $wantprofiles ]]; then + # дополнение из функции + COMPREPLY=($(compgen -W '${_netctl_profiles[@]}' -- "$cur")) + elif [[ $prev = $wanttabs ]]; then + # дополнение из массива + COMPREPLY=($(compgen -W '${_netctl_gui_tabs[@]}' -- "$cur")) + elif [[ $prev = $wantsettings ]]; then + # дополнение из массива + # -S вставит запятую после, но вот мультивыбор не включил =( + COMPREPLY=($(compgen -S ',' -W '${_netctl_gui_settings[@]}' -- "$cur")) + else + # вывести доступные аргументы + COMPREPLY=($(compgen -W '${_netctl_gui_arglist[@]}' -- "$cur")) + fi + + true +} +{% endhighlight %} +
+ +Файл хранится в директории /usr/share/bash-completion/completions/
с произвольным именем. Файл примера полностью может быть найден в моем репозитории.