Compare commits

...

28 Commits

Author SHA1 Message Date
F.B. 5f212fcbbf
Merge 5359bbf0e7 into 7dd26c5d5c 2026-01-26 08:22:47 -06:00
Shohei YOSHIDA 7dd26c5d5c
Merge pull request #1219 from SyrupyTasty/master
Fix Decrypt completion
2026-01-26 00:31:32 +09:00
Syrupy 8794fdca69
Fix Decrypt completion
Fix 'decrypt the input data' option
2026-01-25 12:18:42 +08:00
Shohei YOSHIDA d41e237045
Merge pull request #1218 from zsh-users/refactor-jrnl
Refactoring jrnl completion
2026-01-24 08:46:30 +09:00
Shohei YOSHIDA 93dc422070
Refactoring jrnl completion
- Apply format
- Update URL
- Update flags to latest version
2026-01-24 08:45:05 +09:00
Shohei YOSHIDA 6370f5a58f
Merge pull request #1217 from Tillerino/master
jrnl: Completion for tags
2026-01-23 18:31:53 +09:00
Tillmann Gaida 8ecee23cab jrnl: Add completion for tags 2026-01-23 08:49:57 +01:00
Shohei YOSHIDA 8a994c20d7
Merge pull request #1215 from zsh-users/update-qmk
Update qmk completion to version 1.2.0
2026-01-21 18:23:28 +09:00
Shohei YOSHIDA 672ae69366
Merge pull request #1216 from zsh-users/update-node-js
Update node.js completion to version 25.4.0
2026-01-21 15:32:50 +09:00
Shohei YOSHIDA 7288768da6
Update node.js completion to version 25.4.0 2026-01-20 12:29:26 +09:00
Shohei YOSHIDA 1ba992dcde
Update qmk completion to version 1.2.0 2026-01-19 17:40:18 +09:00
Shohei YOSHIDA c31ad08eff
Merge pull request #1213 from Magniquick/master
Add gio completion
2026-01-14 11:32:08 +09:00
Magniquick 05a1cf8a8a fix gio completions 2026-01-14 06:23:07 +05:30
Magniquick 9f8413696d Add gio completion 2025-12-31 18:32:53 +05:30
fishBone000 5359bbf0e7
Forgot to change ToC 2024-09-18 06:07:34 +00:00
fishBone000 a3e89f7c56
Reread to make the doc more fluent 2024-09-18 06:06:48 +00:00
fishBone000 cf57a3266c
Try to fix ToC 2024-09-18 04:52:48 +00:00
fishBone000 41896a6013
Finish most TODOs 2024-09-18 04:43:01 +00:00
fishBone000 0d8667bb6a
Fix Table of Contents
I forgot to change it
2024-09-18 04:17:10 +00:00
fishBone000 79c6d1715f
Finish Other resources 2024-09-18 04:15:31 +00:00
fishBone000 f5e58a5ca7
Finish Tips 2024-09-18 04:12:15 +00:00
fishBone000 6c8cd1b111
Finish Gotchas (things to look out for) 2024-09-18 04:01:09 +00:00
fishBone000 eb711299c5
Finish Testing & debugging 2024-09-18 03:53:13 +00:00
fishBone000 d71c3c84cc
Finish Writing your own completion code 2024-09-18 03:43:52 +00:00
fishBone000 c3f1f7748d
Finish Getting started 2024-09-17 07:40:59 +00:00
fishBone000 45fd6f398a
Fix document file extension 2024-09-17 07:15:24 +00:00
fishBone000 26b538f012
Finish Intro 2024-09-17 07:13:14 +00:00
fishBone000 fd43209f3a
Beginning of zh_cn translation 2024-09-17 06:37:21 +00:00
6 changed files with 1005 additions and 28 deletions

307
src/_gio Normal file
View File

@ -0,0 +1,307 @@
#compdef gio
# ------------------------------------------------------------------------------
# Copyright (c) 2025 Github zsh-users - https://github.com/zsh-users
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the zsh-users nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ZSH-USERS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ------------------------------------------------------------------------------
# Description
# -----------
#
# Completion script for gio 2.86.3 (https://docs.gtk.org/gio/).
#
# Status: Basic (subcommands and common options).
# Origin: Original script.
#
# ------------------------------------------------------------------------------
# Authors
# -------
#
# * Magniquick (https://github.com/Magniquick)
#
# ------------------------------------------------------------------------------
_gio() {
local context state line
typeset -A opt_args
local ret=1
_arguments -C \
'(- : *)'{-h,--help}'[show help information]' \
'(- : *)'--version'[display version information]' \
'1:command:->command' \
'*::arg:->args' \
&& ret=0
case $state in
(command)
_gio_commands
;;
(args)
local subcmd=$words[1]
if [[ $subcmd == gio ]]; then
subcmd=$words[2]
fi
case $subcmd in
(help)
_arguments \
'1:command:_gio_commands' \
&& ret=0
;;
(version)
_arguments && ret=0
;;
(cat)
_arguments \
'*:location:_gio_location' \
&& ret=0
;;
(copy)
_arguments \
'(-T --no-target-directory)'{-T,--no-target-directory}'[no target directory]' \
'(-p --progress)'{-p,--progress}'[show progress]' \
'(-i --interactive)'{-i,--interactive}'[prompt before overwrite]' \
'--preserve[preserve all attributes]' \
'(-b --backup)'{-b,--backup}'[backup existing destination files]' \
'(-P --no-dereference)'{-P,--no-dereference}'[never follow symbolic links]' \
'--default-permissions[use default permissions for the destination]' \
'--default-modified-time[use default file modification timestamps for the destination]' \
'*:location:_gio_location' \
&& ret=0
;;
(info)
_arguments \
'(-w --query-writable)'{-w,--query-writable}'[list writable attributes]' \
'(-f --filesystem)'{-f,--filesystem}'[get file system info]' \
'(-a --attributes)'{-a,--attributes}'[attributes to get]:attributes' \
'(-n --nofollow-symlinks)'{-n,--nofollow-symlinks}'[do not follow symbolic links]' \
'*:location:_gio_location' \
&& ret=0
;;
(launch)
_arguments \
'1:desktop file:_gio_desktop_files' \
'*::file argument:_gio_location' \
&& ret=0
;;
(list)
_arguments \
'(-a --attributes)'{-a,--attributes}'[attributes to get]:attributes' \
'(-h --hidden)'{-h,--hidden}'[show hidden files]' \
'(-l --long)'{-l,--long}'[use a long listing format]' \
'(-n --nofollow-symlinks)'{-n,--nofollow-symlinks}'[do not follow symbolic links]' \
'(-d --print-display-names)'{-d,--print-display-names}'[print display names]' \
'(-u --print-uris)'{-u,--print-uris}'[print full URIs]' \
'*:location:_gio_location' \
&& ret=0
;;
(mime)
_arguments \
'1:mimetype:_gio_mime_types' \
'2:handler' \
&& ret=0
;;
(mkdir)
_arguments \
'(-p --parent)'{-p,--parent}'[create parent directories]' \
'*:location:_gio_location' \
&& ret=0
;;
(monitor)
_arguments \
'(-d --dir)'{-d,--dir}'[monitor a directory]:location:_gio_location' \
'(-f --file)'{-f,--file}'[monitor a file]:location:_gio_location' \
'(-D --direct)'{-D,--direct}'[monitor a file directly]:location:_gio_location' \
'(-s --silent)'{-s,--silent}'[monitor a file directly without reporting changes]:location:_gio_location' \
'(-n --no-moves)'{-n,--no-moves}'[report moves and renames as simple deleted/created events]' \
'(-m --mounts)'{-m,--mounts}'[watch for mount events]' \
'*:location:_gio_location' \
&& ret=0
;;
(mount)
_arguments \
'(-m --mountable)'{-m,--mountable}'[mount as mountable]' \
'(-d --device)'{-d,--device}'[mount volume with device file or identifier]:device' \
'(-u --unmount)'{-u,--unmount}'[unmount]' \
'(-e --eject)'{-e,--eject}'[eject]' \
'(-t --stop)'{-t,--stop}'[stop drive with device file]:device' \
'(-s --unmount-scheme)'{-s,--unmount-scheme}'[unmount all mounts with scheme]:scheme' \
'(-f --force)'{-f,--force}'[ignore outstanding file operations when unmounting or ejecting]' \
'(-a --anonymous)'{-a,--anonymous}'[use an anonymous user when authenticating]' \
'(-l --list)'{-l,--list}'[list volumes, drives and mounts]' \
'(-o --monitor)'{-o,--monitor}'[monitor volume, drive and mount events]' \
'(-i --detail)'{-i,--detail}'[show extra information]' \
'--tcrypt-pim[unlock VeraCrypt volume with PIM]:pim' \
'--tcrypt-hidden[mount a TCRYPT hidden volume]' \
'--tcrypt-system[mount a TCRYPT system volume]' \
'*:location:_gio_location' \
&& ret=0
;;
(move)
_arguments \
'(-T --no-target-directory)'{-T,--no-target-directory}'[no target directory]' \
'(-p --progress)'{-p,--progress}'[show progress]' \
'(-i --interactive)'{-i,--interactive}'[prompt before overwrite]' \
'(-b --backup)'{-b,--backup}'[backup existing destination files]' \
'(-C --no-copy-fallback)'{-C,--no-copy-fallback}'[do not use copy and delete fallback]' \
'*:location:_gio_location' \
&& ret=0
;;
(open)
_arguments \
'*:location:_gio_location' \
&& ret=0
;;
(rename)
_arguments \
'1:location:_gio_location' \
'2:new name' \
&& ret=0
;;
(remove)
_arguments \
'(-f --force)'{-f,--force}'[ignore nonexistent files, never prompt]' \
'*:location:_gio_location' \
&& ret=0
;;
(save)
_arguments \
'(-b --backup)'{-b,--backup}'[backup existing destination files]' \
'(-c --create)'{-c,--create}'[only create if not existing]' \
'(-a --append)'{-a,--append}'[append to end of file]' \
'(-p --private)'{-p,--private}'[restrict access to current user]' \
'(-u --unlink)'{-u,--unlink}'[replace as if the destination did not exist]' \
'(-v --print-etag)'{-v,--print-etag}'[print new etag at end]' \
'(-e --etag)'{-e,--etag}'[etag of the file being overwritten]:etag' \
'1:destination:_gio_location' \
&& ret=0
;;
(set)
_arguments \
'(-t --type)'{-t,--type}'[type of the attribute]:type' \
'(-n --nofollow-symlinks)'{-n,--nofollow-symlinks}'[do not follow symbolic links]' \
'(-d --delete)'{-d,--delete}'[unset given attribute]' \
'1:location:_gio_location' \
'2:attribute' \
'*::value' \
&& ret=0
;;
(trash)
_arguments \
'(-f --force)'{-f,--force}'[ignore nonexistent files, never prompt]' \
'--empty[empty the trash]' \
'--list[list files in the trash with their original locations]' \
'--restore[restore a file from trash to its original location]' \
'*:location:_gio_location' \
&& ret=0
;;
(tree)
_arguments \
'(-h --hidden)'{-h,--hidden}'[show hidden files]' \
'(-l --follow-symlinks)'{-l,--follow-symlinks}'[follow symbolic links, mounts and shortcuts]' \
'*:location:_gio_location' \
&& ret=0
;;
esac
;;
esac
return ret
}
(( $+functions[_gio_commands] )) ||
_gio_commands() {
local -a commands=(
'help:print help'
'version:print version'
'cat:concatenate files to standard output'
'copy:copy one or more files'
'info:show information about locations'
'launch:launch an application from a desktop file'
'list:list the contents of locations'
'mime:get or set the handler for a mimetype'
'mkdir:create directories'
'monitor:monitor files and directories for changes'
'mount:mount or unmount the locations'
'move:move one or more files'
'open:open files with the default application'
'rename:rename a file'
'remove:delete one or more files'
'save:read from standard input and save'
'set:set a file attribute'
'trash:move or restore files to the trash'
'tree:list the contents of locations in a tree'
)
_describe -t commands 'gio commands' commands
}
(( $+functions[_gio_location] )) ||
_gio_location() {
if (( $+functions[_urls] )); then
_alternative \
'files:files:_files' \
'urls:URL:_urls'
else
_files
fi
}
(( $+functions[_gio_desktop_files] )) ||
_gio_desktop_files() {
local -a search_path data_dirs desktop_files
data_dirs=(${(@s/:/)XDG_DATA_DIRS:-/usr/local/share:/usr/share})
search_path=(
"${XDG_DATA_HOME:-$HOME/.local/share}/applications"
${data_dirs[@]/%/\/applications}
)
desktop_files=()
local dir
for dir in $search_path; do
[[ -d $dir ]] || continue
desktop_files+=($dir/*.desktop(N))
done
compadd -a -- desktop_files
}
(( $+functions[_gio_mime_types] )) ||
_gio_mime_types() {
if (( $+functions[_mime_types] )); then
_mime_types
else
_message 'mime type'
fi
}
_gio "$@"
# Local Variables:
# mode: Shell-Script
# sh-indentation: 2
# indent-tabs-mode: nil
# sh-basic-offset: 2
# End:
# vim: ft=zsh sw=2 ts=2 et

View File

@ -28,7 +28,7 @@
# Description
# -----------
#
# Completion script for jrnl a simple journal application for your command line. (https://maebert.github.io/jrnl/).
# Completion script for jrnl v4.2.1 (https://github.com/jrnl-org/jrnl).
#
# ------------------------------------------------------------------------------
# Authors
@ -38,29 +38,77 @@
#
# ------------------------------------------------------------------------------
_jrnl() {
typeset -A opt_args
local context state line
local curcontext="$curcontext"
local ret=1
_arguments -C \
'(- 1 *)'-h"[Show help and exit]" \
'(- 1 *)'-v"[Prints version information and exits]" \
'(- 1 *)'-ls"[Displays accessible journals]" \
'(- 1 *)'-d"[Execute in debug mode]" \
'(- 1 *)'--tags"[Returns a list of all tags and number of occurrences]" \
"--short[Show only titles or line containing the search]" \
"-from[View entries after this date]:date:" \
"-until[View entries before this date]:date:" \
"-to[View entries before this date]:date:" \
"-on[View entries on this date]:date:" \
"-and[Filter by tags using AND (default: OR)]" \
"-starred[Show only starred entries]" \
"-n[Shows the last n entries matching the filter. And '-3' have the same effect.]":number: \
"--export[Export your journal. TYPE can be json, markdown text.]:format:(json markdown text)" \
"-o[Optionally specifies output file when using --export If OUTPUT is a directory, exports each entry in individual file instead.]:output file:" \
"--encrypt[Encrypts your existing journal with a new pass]" \
"--decrypt[Decrypts your journal and stores it in plain text]" \
"--edit[Opens your editor to edit the selected entries.]" \
_arguments -C \
'--debug[Print information useful for troubleshooting]' \
'(- *)--help[Show help message]' \
'(- *)--version[print version information]' \
'--list[List all configured journals]' \
'--encrypt[Encrypt selected journal with a password]' \
'--decrypt[Decrypt selected journal and store it in plain text]' \
'--import[Import entries from another journal]' \
'-on[Show entries on this date]:date' \
'-today-in-history[Show entries of today over the years]' \
'-month[Show entries on this month of any year]:date' \
'-day[Show entries on this day of any month]:date' \
'-year[Show entries of a specific year]:date' \
'-from[Show entries after, or on, this date]:date' \
'(-to -until)'{-to,-until}'[Show entries before, or on, this date]' \
'-contains[Show entries containing specific text]:text' \
'-and[Show only entries that match all conditions(default: OR)]' \
'-starred[Show only starred entries (marked with *)]' \
'-tagged[Show only entries that have at least one tag]' \
'-n[Show a maximum of NUMBER entries. And "-3" has the same effect]:number' \
'-not[exclude entries with that tag]:tag_or_flag' \
'--edit[Opens the selected entries in your configured editor]' \
'--delete[Interactively deletes selected entries]' \
'--change-time[Change timestamp for selected entries(default: now)]:date' \
'--format[Display selected entries in an alternate format]:format:_jrnl_formats' \
'--tags[Alias for "--format tags"]' \
'--short[Show only titles or line containing the search tags]' \
'--config-override[Override configured key-value pair]:key_value' \
'--config-file[Override default config file]:path:_files' \
'*:: :->args' \
&& ret=0
case $state in
(args)
if [[ $PREFIX = @* ]]; then
local -a tags=($(jrnl --tags 2>/dev/null | command grep -oE '@\S+'))
_describe -t tags 'tags' tags && ret=0
fi
;;
esac
return ret
}
_jrnl
(( $+functions[_jrnl_formats] )) ||
_jrnl_formats() {
local -a formats
if (( $+opt_args[--list] )); then
formats=(json yaml)
elif (( $+opt_args[--import] )); then
formats=(jrnl)
else
formats=(boxed calendar dates fancy heatmap json markdown md pretty short tags text txt xml yaml)
fi
_values 'format' $formats
}
_jrnl "$@"
# Local Variables:
# mode: Shell-Script
# sh-indentation: 2
# indent-tabs-mode: nil
# sh-basic-offset: 2
# End:
# vim: ft=zsh sw=2 ts=2 et

View File

@ -28,7 +28,7 @@
# Description
# -----------
#
# Completion script for Node.js v25.0.0 (https://nodejs.org)
# Completion script for Node.js v25.4.0 (https://nodejs.org)
#
# ------------------------------------------------------------------------------
# Authors
@ -156,7 +156,6 @@ _node() {
'--no-experimental-detect-module[when ambiguous modules fail to evaluate, try again to evaluate them as ES modules]' \
'--no-experimental-global-navigator[expose experimental Navigator API on the global scope]' \
'--no-experimental-repl-await[disable experimental await keyword support in REPL]' \
'--no-experimental-require-module[allow loading synchronous ES Modules in require()]' \
'--no-experimental-sqlite[disable experimental node sqlite module]' \
'--no-experimental-strip-types[disable experimental type-stripping for TypeScript files]' \
'--no-experimental-websocket[experimental WebSocket API (currently set)]' \
@ -164,6 +163,7 @@ _node() {
'--no-force-async-hooks-checks[disable checks for async_hooks]' \
'--no-global-search-paths[disable global module search paths]' \
'--no-network-family-autoselection[disable network address family autodetection algorithm]' \
'(--require-module --no-require-module)--no-require-module[disable support for loading a synchronous ES module graph in require()]' \
'--no-warnings[silence all process warnings]' \
'--no-webstorage[disable Web Storage API]' \
'--node-memory-debug[run with extra debug checks for memory leaks in Node.js itself]' \
@ -185,6 +185,7 @@ _node() {
'--report-on-signal=[generate diagnostic report upon receiving signals]' \
'--report-signal=[causes diagnostic report to be produced on provided signal]:signals:_signals -s' \
'--report-uncaught-exception[generate diagnostic report on uncaught exceptions]' \
'(--require-module --no-require-module)--require-module[support for loading a synchronous ES module graph in require()]' \
'*'{-r,--require}'[module to preload (option can be repeated)]: :_node_files' \
'--run=[run a script specified in package.json]:script:_node_scripts' \
'--secure-heap=[total size of the OpenSSL secure heap]: :number' \

View File

@ -990,7 +990,7 @@ _openssl_enc() {
'-out[output file name]:file:_files' \
'-pass[password source]:source:_openssl_pass_phrase_options' \
'-e[encrypt the input data(default behavior)]' \
'-e[decrypt the input data]' \
'-d[decrypt the input data]' \
'(-a -base64)'{-a,-base64}'[Base64 proess the data]' \
'-A[base64 encoding produces output without any newline character]' \
'-k[password to derive the key from]:password' \

173
src/_qmk
View File

@ -29,7 +29,7 @@
# -----------
#
# Completion script for QMK CLI (https://qmk.fm/).
# version: 1.1.1
# version: 1.2.0
#
# ------------------------------------------------------------------------------
# Authors
@ -52,7 +52,7 @@ _qmk() {
'--datetime-fmt[Format string for datetimes]:DATETIME_FMT' \
'--log-fmt[Format string for printed log output]:LOG_FMT' \
'--log-file-fmt[Format string for log file.]:LOG_FILE_FMT' \
'--log-file-level[Logging level for log file]:(debug,info,warning,error,critical)' \
'--log-file-level[Logging level for log file]:level:(debug info warning error critical)' \
'--log-file[File to write log messages to]:filename:_files' \
'(--no-color --color)--color[Enable color in output]' \
'(--no-color --color)--no-color[Disable color in output]' \
@ -73,7 +73,7 @@ _qmk() {
;;
esac
return $ret
return ret
}
(( $+functions[_qmk_commands] )) ||
@ -91,8 +91,18 @@ _qmk_commands() {
'compile:Compile a QMK Firmware'
'doctor:Basic QMK environment checks'
'flash:QMK Flash'
'generate-autocorrect-data:Generate the autocorrection data file from a dictionary file'
'generate-compilation-database:Create a compilation database'
'generate-community-modules-rules-mk:Creates a community_modules_rules_mk from a keymap.json file'
'generate-community-modules-h:Creates a community_modules.h from a keymap.json file'
'generate-community-modules-c:Creates a community_modules.c from a keymap.json file'
'generate-community-modules-introspection-h:Creates a community_modules_introspection.h from a keymap.json file'
'generate-community-modules-introspection-c:Creates a community_modules_introspection.c from a keymap.json file'
'generate-led-matrix-community-modules-inc:Creates an led_matrix_community_modules.inc from a keymap.json file'
'generate-rgb-matrix-community-modules-inc:Creates an rgb_matrix_community_modules.inc from a keymap.json file'
'generate-keymap-h:Creates a keymap.h from a QMK Configurator export.'
'generate-rgb-breathe-table:Generates an RGB Light breathing table header'
'git-submodule:Git Submodule actions'
'import-kbfirmware:Import kbfirmware json export'
'import-keyboard:Import data driven keyboard'
'import-keymap:Import data-driven keymap'
@ -107,6 +117,12 @@ _qmk_commands() {
'painter-convert-graphics:Converts an input image to something QMK understands'
'painter-make-font-image:Converts an input font to something QMK understands'
'painter-convert-font-image:Converts an input font image to something QMK firmware understands'
'resolve-alias:Resolve any keyboard_aliases for provided rule'
'userspace-doctor:Checks userspace configuration'
'userspace-add:Adds a build target to userspace "qmk.json"'
'userspace-remove:Removes a build target from userspace "qmk.json"'
'userspace-list:Lists the build targets specified in userspace "qmk.json"'
'userspace-compile:Compiles the build targets specified in userspace "qmk.json"'
'via2json:Convert a VIA backup json to keymap.json format'
)
@ -235,6 +251,17 @@ _qmk_flash() {
'*: :_files'
}
(( $+functions[_qmk_generate-autocorrect-data] )) ||
_qmk_generate-autocorrect-data() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'(-km --keymap)'{-km,--keymap}'[The keymap to build a firmware for]:keymap' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'*: :_files'
}
(( $+functions[_qmk_generate-compilation-database] )) ||
_qmk_generate-compilation-database() {
_arguments \
@ -243,6 +270,86 @@ _qmk_generate-compilation-database() {
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard'
}
(( $+functions[_qmk_generate-community-modules-rules-mk] )) ||
_qmk_generate-community-modules-rules-mk() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-e --escape)'{-e,--escape}'[Escape spaces in quiet mode]' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-community-modules-h] )) ||
_qmk_generate-community-modules-h() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-community-modules-c] )) ||
_qmk_generate-community-modules-c() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-community-modules-introspection-h] )) ||
_qmk_generate-community-modules-introspection-h() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-community-modules-introspection-c] )) ||
_qmk_generate-community-modules-introspection-c() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-led-matrix-community-modules-inc] )) ||
_qmk_generate-led-matrix-community-modules-inc() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-rgb-matrix-community-modules-inc] )) ||
_qmk_generate-rgb-matrix-community-modules-inc() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-kb --keyboard)'{-kb,--keyboard}'[The keyboard to build a firmware for]:keyboard' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-keymap-h] )) ||
_qmk_generate-keymap-h() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-q --quiet)'{-q,--quiet}'[Quiet mode, only output error message]' \
'(-o --output)'{-o,--output}'[File to write to]:file:_files' \
'*: :_files'
}
(( $+functions[_qmk_generate-rgb-breathe-table] )) ||
_qmk_generate-rgb-breathe-table(){
_arguments \
@ -253,6 +360,16 @@ _qmk_generate-rgb-breathe-table(){
'(-c --center)'{-c,--center}'[The breathing center value, from 1 to 2.7]:num'
}
(( $+functions[_qmk_git-submodule] )) ||
_qmk_git-submodule() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-f --force)'{-f,--force}'[Flag to remove unexpected directories]' \
'--sync[Shallow clone any missing submodules]' \
'--check[Check if the submodules are dirty, and display a warning if they are]' \
'*: :_files'
}
(( $+functions[_qmk_import-kbfirmware] )) ||
_qmk_import-kbfirmware() {
_arguments \
@ -382,6 +499,56 @@ _qmk_painter-convert-font-image() {
'(-i --input)'{-i,--input}'[Specify input graphic file]: :_files'
}
(( $+functions[_qmk_resolve-alias] )) ||
_qmk_resolve-alias() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'--allow-unknown[Return original if rule is not a valid keyboard]'
}
(( $+functions[_qmk_userspace-doctor] )) ||
_qmk_userspace-doctor() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]'
}
(( $+functions[_qmk_userspace-add] )) ||
_qmk_userspace-add() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
\*{-e,--env}'[Extra variables to set during build]:env' \
'(-km --keymap)'{-km,--keymap}"[The keymap's name]" \
'(-kb --keyboard)'{-kb,--keyboard}"[The keyboard's name]"
}
(( $+functions[_qmk_userspace-remove] )) ||
_qmk_userspace-remove() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
\*{-e,--env}'[Extra variables to set during build]:env' \
'(-km --keymap)'{-km,--keymap}"[The keymap's name]" \
'(-kb --keyboard)'{-kb,--keyboard}"[The keyboard's name]"
}
(( $+functions[_qmk_userspace-list] )) ||
_qmk_userspace-list() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
'(-e --expand)'{-e,--expand}'[Expands any use of "all" for either keyboard or keymap]'
}
(( $+functions[_qmk_userspace-compile] )) ||
_qmk_userspace-compile() {
_arguments \
'(- *)'{-h,--help}'[show help message and exit]' \
\*{-e,--env}'[Extra variables to set during build]:env' \
'(-p --print-failures)'{-p,--print-failures}'[Print failed builds]' \
'(-n --dry-run)'{-n,--dry-run}'[Do not actually build, just show the commands to be run]' \
'(-c --clean)'{-c,--clean}'[Remove object files before compiling]' \
'(-j --parallel)'{-j,--parallel}'[Set the number of parallel make jobs, 0 means unlimited]:parallel' \
'(-t --no-temp)'{-t,--no-temp}'[Remove temporary files during build]'
}
(( $+functions[_qmk_via2json] )) ||
_qmk_via2json() {
_arguments \

View File

@ -0,0 +1,454 @@
* 目录
- [[#介绍][介绍]]
- [[#开始][开始]]
- [[#让zsh知道用哪个函数补全命令][让zsh知道用哪个函数补全命令]]
- [[#补全gnu格式命令][补全gnu格式命令]]
- [[#从其它命令复制补全][从其它命令复制补全]]
- [[#编写你自己的补全代码][编写你自己的补全代码]]
- [[#工具函数][工具函数]]
- [[#用_describe编写简单的补全函数][用_describe编写简单的补全函数]]
- [[#用_alternative编写补全函数][用_alternative编写补全函数]]
- [[#用_arguments编写补全函数][用_arguments编写补全函数]]
- [[#用_regex_arguments和_regex_words编写补全函数][用_regex_arguments和_regex_words编写补全函数]]
- [[#用_values_sep_parts和_multi_parts实现复杂补全][用_values、_sep_parts和_multi_parts实现复杂补全]]
- [[#用compadd直接添加补全词][用compadd直接添加补全词]]
- [[#测试与debug][测试与debug]]
- [[#踩坑了吧-需要小心的东西][踩坑了吧 (需要小心的东西)]]
- [[#小贴士][小贴士]]
- [[#其它资源][其它资源]]
> 译注:本文可能有不通顺的地方,或者采用的词汇不是很正确。
> 如果你有更好的主意欢迎提交PR。
* 介绍
Zsh官方的补全函数文档令人费解而且也没提供多少示例。
写这份文档的当下我已经在网上找到了其它几份教程,但是那些教程只涉及了补全系统的一小部分。
这份文档目的在于补全网上其它地方没涵盖的部分,同时附带示例,这样读者就可以学会如何写更高级的补全函数。
我不会展开每一细节,但给你提供的内容和示例足以从零开始。
如果你需要了解更多细节,你可以自行查询[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-System][官方文档]]。
还请公开你所创作的任何脚本比如fork这个仓库然后[[id:64bcd501-b0f0-48c7-b8e2-07af708b95ec][pr]])。
此外如果你有任何更多补充内容或对此教程的改进,欢迎做贡献。
* 开始
** 让zsh知道用哪个函数补全命令
补全命令用的补全函数储存于名字以下划线“_”起始的文件这些文件应存于$fpath变量所列出的某目录中。
你可以将下面的代码写入你的~/.zshrc以在$fpath中新增目录
#+BEGIN_SRC sh
fpath=(~/newdir $fpath)
#+END_SRC
一个补全函数文件的第一行长这个样:
#+BEGIN_SRC sh
#compdef foobar
#+END_SRC
这行代码表示这个文件含有补全foobar命令的代码。
多数情况下第一行都采用这个格式,但你也可以用同一个文件补全多个不同的函数。
查阅[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Autoloaded-files][官方文档]]以了解更多细节。
你也可以直接使用compdef命令(比如在你的~/.zshrc文件里)来告诉zsh用哪个函数补全命令
#+BEGIN_SRC sh
> compdef _function foobar
#+END_SRC
或者对多个命令使用同一种补全:
#+BEGIN_SRC sh
> compdef _function foobar goocar hoodar
#+END_SRC
如果你想提供参数的话:
#+BEGIN_SRC sh
> compdef '_function arg1 arg2' foobar
#+END_SRC
查阅[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Functions-4][官方文档]]以了解更多细节。
** 补全gnu格式命令
很多[[https://www.gnu.org/][gnu]]命令以标准化的方式列出选项描述(使用--help选项时
对于这些命令你可以使用_gnu_generic函数自动创建补全比如这样
#+BEGIN_SRC sh
> compdef _gnu_generic foobar
#+END_SRC
或者对多个不同命令使用_gnu_generic
#+BEGIN_SRC sh
> compdef _gnu_generic foobar goocar hoodar
#+END_SRC
你可以把这行代码放进~/.zshrc文件里。
** 从其它命令复制补全
如果你想要让一个命令比如cmd1和另一个已有补全的命令比如cmd2)拥有相同的补全,你可以:
#+BEGIN_SRC sh
> compdef cmd1=cmd2
#+END_SRC
比如当你给一个命令创建了一个助记alias的时候会很有帮助。
* 编写你自己的补全代码
你可以通过阅读已有的补全函数来开始入门。
在我的Linux系统上这些补全函数在/usr/share/zsh/functions/Completion/Unix、
/usr/share/zsh/functions/Completion/Linux和一些其它子目录下。
你会注意到这些文件频繁使用_arguments函数。
该函数是一个工具函数,可用于编写简单的补全函数。
_arguments函数是一个compadd内置函数的包装函数。
compadd内置函数是一个核心函数用于向命令行加入补全词并控制其行为。
不过多数情况下你不需要使用compadd因为有很多更易于使用的工具函数如_arguments和_describe。
对于非常基础的补全_describe函数已经够用了。
** 工具函数
下面是一个工具函数列表,你或许会用到它们。
工具函数的完整列表及使用方法在[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions][]]可供查阅。
这些函数的使用示例在下一节给出。
> 译注从这里开始会出现一些术语如定义specification、描述description、动作action、词汇word等。
> 初次阅读可能会觉得比较困难,尤其是下面的工具函数表格,稍后教程开始一一讲解并给出示例的时候就好懂多了。
> 定义指的是对补全规则的定义,如`_describe 定义`。
> 描述当然指的是对命令行选项和参数的用户友好描述,补全时会显示在屏幕上,如:'-o 输出文件'
> 词汇指的是如同'word'、'-o'、'--help'、'start'等连续的一小段字符串
> 动作指的是匹配成功后执行的操作,比如执行某函数生成补全,或直接补全等等。
> 选项option和参数指给命令提供的选项和参数如`cp -r src dst`中,`-r`为选项,`src`和`dst`为参数。有些选项带参,如`tar -f file ...`中`-f`选项带`file`参数。
> 候选candidate指可能的补全结果如补全`tar -f`时目录下所有文件会被作为候选,补全`systemctl`时`start`、`stop`、`list-units`等指令会被作为候选。
> 还请不要感到灰心,读下去就是了。
*** 用于大部分补全的主要工具函数
| _alternative | 从其它工具函数或shell代码生成补全候选。 |
| _arguments | 指定如何补全一命令的各选项和参数命令选项风格为unix风格。 |
| _describe | 创建由带描述的词汇但不包含动作组成的简单补全。比_arguments更简单。 |
| _gnu_generic | 为带有“--help”选项的命令补全选项。 |
| _regex_arguments | 创建一先用regex表达式匹配命令行参数再执行动作/补全的函数。 |
*** 对单个词汇进行复杂补全的工具函数
| _values | 补全任意词汇(值)及其参数,或逗号分隔的词汇与参数列表。 |
| _combination | 补全值的组合,比如域名和用户名的二元组。 |
| _multi_parts | 对词汇的由符号分隔的多个部分分别补全,比如补全路径:/u/i/sy -> /usr/include/sys |
| _sep_parts | 类似_multi_parts但在补全的不同部分中允许不同的分隔符。 |
| _sequence | 包装另一补全函数,并补全由该函数生成的匹配列表。 |
*** 用于补全特定对象种类的函数
| _path_files | 补全文件目录。用多个选项控制行为。 |
| _files | 使用所有选项调用_path_files除了-g和-/。这些选项取决于file-patterns风格设置。 |
| _net_interfaces | 补全网络接口名称。 |
| _users | 补全用户名 |
| _groups | 补全组名 |
| _options | 补全shell选项名。 |
| _parameters | 补全shell参数/变量名(可用模式匹配限制要补全的参数/变量名)。 |
*** 处理已缓存的补全的函数
如果你有大量的补全,你可以将补全保存于一个缓存文件以快速加载。
| _cache_invalid | 指明补全缓存是否需要重新构建rebuild缓存由标识符指定 |
| _retrieve_cache | 从缓存文件获取补全信息 |
| _store_cache | 储存缓存于缓存文件,缓存由标识符指定 |
*** 其它函数
| _message | 当无补全可生成时显示帮助信息。 |
| _regex_words | 为_regex_arguments命令生成参数。比手写参数更简单。 |
| _guard | 检查被补全的词汇用于_arguments和类似函数的定义的ACTION中。 |
*** 动作Actions
许多工具函数如_arguments、_regex_arguments、_alternative和_values在选项/参数末尾有一个action。
这个action指定如何补全对应的参数。
这些action可以是如下形式之一
| ( ) | 需要提供参数但未生成任何匹配 |
| (ITEM1 ITEM2) | 匹配列表 |
| ((ITEM1\:'DESC1' ITEM2\:'DESC2')) | 匹配列表,带有描述。引号必须和整个定义所使用的引号不同。 |
| ->STRING | 将$state设为STRING然后继续可在调用工具函数后用case语句检查$state的值 |
| FUNCTION | 生成匹配或完成其它操作的函数的函数名比如_files或_message |
| {EVAL-STRING} | 把字符串当作shell代码执行evaluate。可用于带参调用工具函数如_values或_describe |
| =ACTION | 在补全命令行中插入占位词汇不改变补全点。 |
并非所有的action种类都可用于使用action的工具函数。比如->STRING类不可用于_regex_arguments或_alternative函数。
** 用_describe编写简单的补全函数
_describe函数可以用于简单的补全此类补全的选项/参数的位置与顺序无关紧要。
你只需用一个数组参数储存这些选项和其描述然后将数组参数的名作为参数传入_describe。
下面的示例创建补全候选c和d注意代码文件名应为_cmd并且文件存于$fpath所列出的目录之下
#+BEGIN_SRC sh
#compdef cmd
local -a subcmds
subcmds=('c:c命令描述' 'd:d命令描述')
_describe 'command' subcmds
#+END_SRC
你可以像下面一样使用由双横杠分隔的列表,但注意实操时会混合匹配结果,所以不应该用于不同种类的补全候选:
#+BEGIN_SRC sh
local -a subcmds topics
subcmds=('c:c命令描述' 'd:d命令的描述')
topics=('e:e帮助主题的描述' 'f:f帮助主题的描述')
_describe 'command' subcmds -- topics
#+END_SRC
如果两个候选有相同的描述_describe把它们集于一行并确保描述严格按列对齐。
_describe函数可用在_alternative、_arguments或_regex_arguments的ACTION中。
在这种情况下你需要用括号将_describe和参数包起来比如'TAG:DESCRIPTION:{_describe 'values' options}'
** 用_alternative编写补全函数
如同_describe该函数进行简单补全其选项/参数的顺序和位置并不重要。
然而与_describe的固定匹配不同_alternative可进一步调用函数生成补全候选。
此外_alternative允许混合不同种类的补全候选。
关于参数该函数接受一列定义specification每项定义的形式为“TAG:DESCRIPTION:ACTION”即“标签:描述:动作”其中TAG是一个标识补全匹配种类的特殊标签。
DESCRIPTION以标题heading的形式描述补全候选组而ACTION是先前列出的动作种类之一除了->STRING和=ACTION之外
例如:
#+BEGIN_SRC sh
_alternative 'arguments:自定义参数:(a b c)' 'files:文件名:_files'
#+END_SRC
第一个定义增加了补全候选a、b和c而第二个定义调用_files函数以补全文件目录。
我们可以用反斜杠 \ 将不同定义分成几行并给每个自定义参数加入描述文字:
#+BEGIN_SRC sh
_alternative \
'args:自定义参数:((a\:"描述a" b\:"描述b" c\:"描述c"))' \
'files:文件名:_files'
#+END_SRC
如果我们想向_files传递参数我们可以直接写在_files后面
#+BEGIN_SRC sh
_alternative \
'args:自定义参数:((a\:"描述a" b\:"描述b" c\:"描述c"))' \
'files:文件名:_files -/'
#+END_SRC
如要用变量展开创建补全列表,必须用双引号将定义括起来,
如:
#+BEGIN_SRC sh
_alternative \
"dirs:用户目录:($userdirs)" \
"pids:进程ID:($(ps -A o pid=))"
#+END_SRC
在此例子中第一个定义加入$userdirs变量中的词汇第二个定义执行'ps -A o pid='并获取pid表pid表用作补全候选。
实操中我们使用已有的_pids函数而不是像上面那样手写。
我们可以在ACTION中使用_values等其它工具函数以完成更复杂的补全
#+BEGIN_SRC sh
_alternative \
"directories:用户目录:($userdirs)" \
'options:逗号分隔选项: _values -s , letter a b c'
#+END_SRC
该示例补全$userdirs里的项目以及用逗号分隔的、含有a、b和/或c的列表。
注意_values前面的空格。空格不可省略因为_values不能接受标准compadd描述选项。
和_describe一样_alternative可用作ACTION并作为_arguments或_regex_arguments的一部分。
** 用_arguments编写补全函数
只需要调用_arguments函数一次就可以创造出非常智能的补全函数。该函数本身就是用于处理这种带有带参选项的命令的。
如同_alternative函数_arguments接受一列定义字符串参数。
这些定义字符串指定选项和任何对应的选项参数(如:-f 文件名),或命令参数。
简单的选项定义用'-OPT[DESCRIPTION]'(即'-选项[描述]'),比如:
#+BEGIN_SRC sh
_arguments '-s[排序后输出]' '--l[更多输出]' '-l[更多输出]'
#+END_SRC
选项参数可在选项描述后指定,形式用'-OPT[DESCRIPTION]:MESSAGE:ACTION'(即'-选项[描述]:消息:动作',
其中MESSAGE是待显示的信息而ACTION可以是前面的动作Actions章节提到的任何形式。
比如:
#+BEGIN_SRC sh
_arguments '-f[输入文件]:文件名:_files'
#+END_SRC
命令参数定义用'N:MESSAGE:ACTION'(即'N:消息:动作'其中N指定这是第N个命令参数而MESSAGE和ACTION都和前面的一样。
如果N被省略则其仅表示这是在所有已定义的参数之后的下一个参数。如果开头在N后面用的是双冒号则参数非必需。
比如:
#+BEGIN_SRC sh
_arguments '-s[排序后输出]' '1:第一个参数:_net_interfaces' '::可选参数:_files' ':下一个参数:(a b c)'
#+END_SRC
这里第一个参数是网络接口下一个可选参数是一个文件名最后一个参数可以是a、b或c而-s选项可以在任何位置被补全。
_arguments函数允许所有ACTION形式在前面的动作(Actions)章节列出)。
这表示你可以用动作来选择case语句分支
#+BEGIN_SRC sh
_arguments '-m[音乐文件]:文件名:->files' '-f[flags]:flag:->flags'
case "$state" in
files)
local -a music_files
music_files=( Music/**/*.{mp3,wav,flac,ogg} )
_multi_parts / music_files
;;
flags)
_values -s , 'flags' a b c d e
;;
esac
#+END_SRC
在此例子中指向音乐文件的路径被_multi_parts一步步地沿目录下降补全
而flags被_values函数按照逗号分隔列表补全。
我已经介绍了_arguments定义的基础部分你还可以定义互斥选项、重复选项和参数、以+开头的选项等。有关更多细节,查阅[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-System][官方文档]]。
同时你也可以看看本文末尾列出的教程,以及[[https://github.com/vapniks/zsh-completions/tree/master/src][src目录]]下的补全函数。
** 用_regex_arguments和_regex_words编写补全函数
如果你的命令行定义比较复杂有多个可能的参数序列那你可能需要_regex_arguments函数。
该函数也适用于有一系列跟着多个参数的关键词的情况。
_regex_arguments创建名字由第一个参数给出的补全函数。
因此你需要先调用_regex_arguments来创建补全函数然后再调用该函数比如
#+BEGIN_SRC sh
_regex_arguments _cmd OTHER_ARGS..
_cmd "$@"
#+END_SRC
OTHER_ARGS即“其它参数”是一序列用于在命令行上匹配和补全词汇的定义。
这些序列可被'|'分隔来表示备选词汇序列。
你可以用任意嵌套深度的括号来指定备选序列,但括号必须带反斜杠前缀,如\( \),或用引号括起来,如'(' ')'。
比如:
#+BEGIN_SRC sh
_regex_arguments _cmd 序列1 '|' 序列2 \( 序列2a '|' 序列2b \)
_cmd "$@"
#+END_SRC
该示例定义一个匹配序列1或序列2后跟着序列2a或序列2b的命令行。这种方式和正则表达式语法类似。
一个序列中的每个定义必须在开头包含一个/ PATTERN/ (即/ 模式/)部分,后跟着可选的':TAG:DESCRIPTION:ACTION'(即':标签:描述:动作')部分。
每个PATTERN是一个匹配一命令行词汇的正则表达式。这些模式按顺序匹配直到某个模式不匹配不匹配的模式将执行对应的ACTION动作以进行补全。
注意,一定要有一个匹配命令自身的模式。
下面有对PATTERN模式更详细的解释。
':TAG:DESCRIPTION:ACTION'的使用方法和_alternative相同只是开头多了个冒号“:”并且前面列出的所有ACTION格式都可用。
例如:
#+BEGIN_SRC sh
_regex_arguments _cmd /$'[^\0]##\0'/ \( /$'word1(a|b|c)\0'/ ':word:first word:(word1a word1b word1c)' '|'\
/$'word11(a|b|c)\0'/ ':word:first word:(word11a word11b word11c)' \( /$'word2(a|b|c)\0'/ ':word:second word:(word2a word2b word2c)'\
'|' /$'word22(a|b|c)\0'/ ':word:second word:(word22a word22b word22c)' \) \)
_cmd "$@"
#+END_SRC
TODO 英文原文和例子有出入
在这个例子中第一个词可以是word1即“词1”下同或者word11后紧跟a、b或c并且如果第一个词含有11则第二个词可以是word2后紧跟a、b或c或一个文件名。
如果感觉太复杂你也可以用更简单的_regex_words函数达到相同效果。
*** 模式
你可能注意到了上个例子中的/ PATTERN/和普通的正则表达式不太一样。
通常使用的是形如$'foo\0'的字符串参数。这是为了让\0表示成null字符而zsh内部用来分隔词汇的也是null字符。
如果不在末尾包含\0的话可能会无法匹配下一个词。如果你要把一个变量的值作为模式的一部分你可以用双括号包起来
这样变量就会展开然后再在后面加个包含null字符的字符串参数比如"$somevar"$'\0'somevar即“某变量”
表示模式用的正则表达式语法和正常的正则表达式不太一样,但我也找不到有关的文档。
不过我还是试图搞清楚了这些特殊字符的意义:
| * | 通配符 - 任何数量的字符 |
| ? | 通配符 - 单个字符 |
| # | 零个或更多的上一个字符(和一般正则表达式里的*一样) |
| ## | 一个或更多的上一个字符(和一般正则表达式里的+一样) |
*** _regex_words
_regex_words函数比_regex_arguments更简单易用。
调用_regex_words后的结果可以存在变量里。
要用_regex_words创建一个定义specification你需要提供一个标签后跟一段描述后跟一个定义不同词汇的列表。
这些定义采用'WORD:DESCRIPTION:SPEC'(即'词汇:描述:定义'的格式WORD即待补全的词DESCRIPTION是对应的描述
SPEC可以是由_regex_words创建的另一个变量以指定当前词后的下一个词汇也可以留空以表示没有更多的词。
比如:
#+BEGIN_SRC sh
_regex_words firstword '第一个词' 'word1a:词a:' 'word1b:词b:' 'word1c:词c'
#+END_SRC
该函数的返回结果将被存入$replyreply即“回复”、“回应”数组里所以我们需要在$reply变化前将结果存进另一个数组里
#+BEGIN_SRC sh
local -a firstword
_regex_words word 'The first word' 'word1a:a word:' 'word1b:b word:' 'word1c:c word'
firstword="$reply[@]"
#+END_SRC
firstword即“第一个词”。
然后我们可以把结果用在_regex_arguments里
#+BEGIN_SRC sh
_regex_arguments _cmd /$'[^\0]##\0'/ "$firstword[@]"
_cmd "$@"
#+END_SRC
注意到我给命令自身也加了模式。
这里还有个更复杂的词汇我们调用_regex_words以匹配不同词汇
#+BEGIN_SRC sh
local -a firstword firstword2 secondword secondword2
_regex_words word1 '第二个词' 'woo:鄧族' 'hoo:不关我事'
secondword=("$reply[@]")
_regex_words word2 '另一个第二个词' 'yee:汝' 'haa:很搞笑!'
secondword2=("$reply[@]")
_regex_words commands '第一个词' 'foo:做foo' 'man:yeah man' 'chu:at chu' # 译注作者在自嗨at chu除了比较像at you外没什么特殊意义
firstword=("$reply[@]")
_regex_words word4 '另一个第一个词' 'boo:吓死某人:$secondword' 'ga:嘤嘤嘤:$secondword'\
'loo:上厕所:$secondword2'
firstword2=("$reply[@]")
_regex_arguments _hello /$'[^\0]##\0'/ "${firstword[@]}" "${firstword2[@]}"
_hello "$@"
#+END_SRC
在这个例子中第一个词可以是"foo"、"man"、"chu"、"boo"、"ga"或"loo"。
如果第一个词是"boo"或"ga",那下一个词可以是"woo"或"hoo"
而如果第一个词是"loo"则第二个词可以是"yee"或"haa",其它情况下没有第二个词。
_ip函数是_regex_words的一个好用例。
** 用_values、_sep_parts和_multi_parts实现复杂补全
_values、_sep_parts和_multi_parts可以单独使用也可以作为_alternative、_arguments或_regex_arguments定义里的ACTION。可以看看下面的例子。
查阅[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-System][官方文档]]以了解更多信息。
空格分隔的mp3文件列表
#+BEGIN_SRC sh
_values 'mp3文件' ~/*.mp3
#+END_SRC
逗号分隔的会话id列表
#+BEGIN_SRC sh
_values -s , '会话id' "${(uonzf)$(ps -A o sid=)}"
#+END_SRC
补全foo@news:woo、foo@news:laa或bar@news:woo等
#+BEGIN_SRC sh
_sep_parts '(foo bar)' @ '(news ftp)' : '(woo laa)'
#+END_SRC
补全MAC地址一次补全一个字节
#+BEGIN_SRC sh
_multi_parts : '(00:11:22:33:44:55 00:23:34:45:56:67 00:23:45:56:67:78)'
#+END_SRC
** 用compadd直接添加补全词
你可以使用内置的compadd函数增加补全词以获得更细致的控制。
这个函数有各种选项控制如何显示补全以及补全时如何替换命令行上的文字。
阅读[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-System][官方文档]]以获得更多细节。
这里我只给出几个简单的示例。
向补全列表里加入若干词汇:
#+BEGIN_SRC sh
compadd foo bar blah
#+END_SRC
同上但显示注释:
#+BEGIN_SRC sh
compadd -X '一些注释' foo bar blah
#+END_SRC
同上但在补全前自动插入"what_"前缀
#+BEGIN_SRC sh
compadd -P what_ foo bar blah
#+END_SRC
同上但补全后自动插入"_todo"后缀:
#+BEGIN_SRC sh
compadd -S _todo foo bar blah
#+END_SRC
同上但在后缀后打空格时自动移除"_todo"后缀:
#+BEGIN_SRC sh
compadd -P _todo -q foo bar blah
#+END_SRC
向补全数组$wordsarraywordsarray即“词数组”加入词汇
#+BEGIN_SRC sh
compadd -a wordsarray
#+END_SRC
* 测试与debug
重新加载补全函数:
#+BEGIN_SRC sh
> unfunction _func
> autoload -U _func
#+END_SRC
这些函数会提供有用的信息。
如果默认按键没有用你可以尝试Alt+x然后再输入命令名。
| 函数 | 默认按键 | 作用 |
|-----------------+--------------------+----------------------------------------------------------------|
| _complete_help | Ctrl+x h | 在当前光标位置补全时显示有关上下文名称、标签和补全函数的信息 |
| _complete_help | Alt+2 Ctrl+x h | 同上但显示更多信息 |
| _complete_debug | Ctrl+x ? | 执行正常补全但跟踪补全系统执行的shell命令并存入一个临时文件 |
* 踩坑了吧 (需要小心的东西)
记得在补全函数的文件开头加那行#compdef
_arguments或_regex_arguments的定义中要使用正确的引号
如果定义中有变量要展开,用双引号,其它情况用单引号,
并且记得在ITEM项目描述处用不同的引号。译注见[[#动作Actions][动作Actions]]章节)
_arguments、_alternative、_regex_arguments等的定义处要在正确的地方使用正确数量的冒号“:”。
使用_regex_arguments时要记得在开头写匹配命令的模式不需要加入匹配动作action
记得在_regex_arguments的任何PATTERN模式参数后加上null字符$'\0'
* 小贴士
有时一个子命令后只会跟一个选项这时zsh会在tab在子命令后按下时自动补全。如果你想要在补全前先列出选项和描述
你可以向ACTION动作加入另一个空选项比如\:),如':TAG:DESCRIPTION:((opt1\:"opt1描述" \:))'
注意这只对在定义参数中使用ACTION的工具函数_arguments、_regex_arguments等有效。
* 其它资源
[[https://wikimatze.de/writing-zsh-completion-for-padrino/][]]是个展示_arguments函数的基本使用方法的教程短小精悍
而[[https://web.archive.org/web/20190411104837/http://www.linux-mag.com/id/1106/][]]是_arguments函数的稍稍更进阶的教程。
[[https://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-System][]]是zshcompsys手册页man page