mu: add zsh completion

Remove the ancient zsh completion, and replace with a new, more complete
one. Automatically install (but configuration through meson options)
This commit is contained in:
Dirk-Jan C. Binnema
2026-08-04 06:46:11 +03:00
committed by Seth Ladygo
parent e26fc581df
commit 756f795cea
6 changed files with 451 additions and 125 deletions

View File

@ -1,124 +0,0 @@
#compdef mu
## Copyright (C) 2011-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software Foundation,
## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# zsh completion for mu. Install this by copying/linking to this file somewhere in
# your $fpath; the link/copy must have a name starting with an underscore "_"
# main dispatcher function
_mu() {
if (( CURRENT > 2 )) ; then
local cmd=${words[2]}
curcontext="${curcontext%:*:*}:mu-$cmd"
(( CURRENT-- ))
shift words
_call_function ret _mu_$cmd
return ret
else
_mu_commands
fi
}
_mu_commands() {
local -a mu_commands
mu_commands=(
'index:scan your maildirs and import their metadata in the database'
'find:search for messages in the database'
'view:display specific messages'
'cfind:search for contacts (name + email) in the database'
'extract:extract message-parts (attachments) and save or open them'
'mkdir:create maildirs'
# below are not generally very useful, so let's not auto-complete them
# 'add: add a message to the database.'
# 'remove:remove a message from the database.'
# 'server:sart the mu server'
)
_describe -t command 'command' mu_commands
}
_mu_common_options=(
'--debug[output information useful for debugging mu]'
'--quiet[do not give any non-critical information]'
'--nocolor[do not use colors in some of the output]'
'--version[display mu version and copyright information]'
'--log-stderr[log to standard error]'
)
_mu_db_options=(
'--muhome[use some non-default location for the mu database]:directory:_files'
)
_mu_find_options=(
'--fields[fields to display in the output]'
'--sortfield[field to sort the output by]'
'--descending[sort in descending order]'
'--summary[include a summary of the message]'
'--summary-len[number of lines to use for the summary]'
'--bookmark[use a named bookmark]'
'--output[set the kind of output for the query]'
)
_mu_view_options=(
'--summary[only show a summary of the message]'
'--summary-len[number of lines to use for the summary]'
)
_mu_view() {
_arguments -s : \
$_mu_common_options \
$_mu_view_options
}
_mu_extract() {
_files
}
_mu_find() {
_arguments -s : \
$_mu_common_options \
$_mu_db_options \
$_mu_find_options
}
_mu_index() {
_arguments -s : \
$_mu_db_options \
$_mu_common_options
}mu
_mu_cleanup() {
_arguments -s : \
$_mu_db_options \
$_mu_common_options
}
_mu_mkdir() {
_arguments -s : \
'--mode=[file mode for the new Maildir]:file mode: ' \
$_mu_common_options
}
_mu "$@"
# Local variables:
# mode: sh
# End:

View File

@ -415,6 +415,9 @@ if emacs.found()
section: 'Mu4e')
endif
summary({'zsh' : get_option('zsh-completion').allowed()},
section: 'Completion', bool_yn: true)
# Local Variables:
# indent-tabs-mode: nil
# End:

View File

@ -75,6 +75,18 @@ option('guile-extension-dir',
type: 'string',
description: 'custom install path for the guile extension module')
#
# completion
#
option('zsh-completion',
type : 'feature',
value: 'auto',
description: 'whether to install zsh completion for mu')
option('zsh-completion-dir',
type: 'string',
description: 'custom install path for zsh completion')
#
# misc
#

29
mu/completion/meson.build Normal file
View File

@ -0,0 +1,29 @@
## Copyright (C) 2026 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software Foundation,
## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
if get_option('zsh-completion').allowed()
if get_option('zsh-completion-dir') == ''
zcdir = datadir / 'zsh' / 'site-functions'
else
zcdir = get_option('zsh-completion-dir')
endif
install_data('mu-completion.zsh',
rename: '_mu',
install_dir: zcdir)
endif

View File

@ -0,0 +1,404 @@
#compdef mu
# completions for zsh; place in a file called _mu in a directory
# in ${fpath}
_mu_add() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'*:files:_files'
}
_mu_cfind() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-o --format)'{-o+,--format=}'[Output format]:format:(bbdb csv json mutt-ab mutt-alias org-contact plain wl)' \
'(-p --personal)'{-p,--personal}'[Only show '\''personal'\'' contacts]' \
'--after=[Only show results after some timestamps]:time_t:' \
'(-n --maxnum)'{-n+,--maxnum=}'[Maximum number of results]:number:' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1::pattern:'
}
_mu_extract() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-r --auto-retrieve)'{-r,--auto-retrieve}'[Attempt to automatically retrieve online keys]' \
'--decrypt[Attempt to decrypt]' \
'(-a --save-attachments)'{-a,--save-attachments}'[Save all attachments]' \
'--save-all[Save all MIME parts]' \
'--overwrite[Overwrite existing files]' \
'--play[Attempt to open the extracted parts]' \
'*''--parts=[Save specific parts (comma-sep'\''d list)]:parts:' \
'--target-dir=[Target directory for saving]:dir:_files -/' \
'(-u --uncooked)'{-u,--uncooked}'[Avoid massaging extracted file-names]' \
'--matches=[Regular expression for files to save]:filename-rx:' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1::message-path:_files' \
'2::filename-rx:'
}
_mu_fields() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]'
}
_mu_find() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-t --threads)'{-t,--threads}'[Show message threads]' \
'(-u --skip-dups)'{-u,--skip-dups}'[Show only one of messages with same message-id]' \
'(-r --include-related)'{-r,--include-related}'[Include related messages in results]' \
'(-a --analyze)'{-a,--analyze}'[Analyze the query]' \
'(-o --format)'{-o+,--format=}'[Output format]:format:(json json2 links plain sexp xml)' \
'(-n --maxnum)'{-n+,--maxnum=}'[Maximum number of results]:number:' \
'(-f --fields)'{-f+,--fields=}'[Fields to display]:value:' \
'(-s --sortfield)'{-s+,--sortfield=}'[Field to sort the results by]:field:(a bcc c cc changed d date f flags from g h i k l labels language list m maildir message-id p path priority q r references s size subject t tags thread to u utc-offset v w x z)' \
'(-z --reverse)'{-z,--reverse}'[Sort in descending order]' \
'(-b --bookmark)'{-b+,--bookmark=}'[Use bookmarked query]:bookmark:' \
'--clearlinks[Clear old links first]' \
'--linksdir=[Target directory for symlinks]:dir:_files -/' \
'--after=[Only show messages whose message file was changed after some timestamp]:time_t:' \
'--summary-len=[Use up to so many lines for the summary]:lines:' \
'--exec=[Command to execute on message file]:command:' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'*:query:'
}
_mu_help() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1::command:'
}
_mu_index() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--lazy-check[Skip based on dir-timestamps]' \
'--nocleanup[Don'\''t clean up database after indexing]' \
'--reindex[Perform a complete reindexing]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]'
}
_mu_info() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1::topic:'
}
_mu_init() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-m --maildir)'{-m+,--maildir=}'[Root maildir]:maildir:_files -/' \
'*'{--personal-address=,--my-address=}'[Personal e-mail address or regexp (can be used multiple times)]:address:' \
'*''--ignored-address=[Ignored e-mail address or regexp]:address:' \
'--max-message-size=[Maximum allowed message size in bytes]:value:' \
'--batch-size=[Maximum size of database transaction]:value:' \
'--support-ngrams[Support CJK n-grams for querying/indexing]' \
'--reinit[Re-initialize database with current settings]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]'
}
_mu_labels_update() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'*''--labels=[One or more comma-separated +label,-label]:delta-label:' \
'(-n --dry-run)'{-n,--dry-run}'[Output what would change without changing anything]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'1:query:'
}
_mu_labels_clear() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-n --dry-run)'{-n,--dry-run}'[Output what would change without changing anything]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'1:query:'
}
_mu_labels_list() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/'
}
_mu_labels_restore_list() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/'
}
_mu_labels_export() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'1::output:_files'
}
_mu_labels_import() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-n --dry-run)'{-n,--dry-run}'[Output what would change without changing anything]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'1:input:_files'
}
_mu_labels_commands() {
local -a commands
commands=(
'update:update labels'
'clear:clear all labels from matched messages'
'list:list labels in the store'
'restore-list:restore the labels cache'
'export:export labels to a file'
'import:import labels from a file'
)
_describe -t commands 'command' commands
}
_mu_labels() {
local curcontext="$curcontext" state line ret=1
_arguments -S -C \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1: :_mu_labels_commands' \
'*::arg:->args' && ret=0
case $state in
(args)
curcontext="${curcontext%:*:*}:mu-labels-${words[1]}:"
case ${words[1]} in
(update) _mu_labels_update && ret=0;;
(clear) _mu_labels_clear && ret=0;;
(list) _mu_labels_list && ret=0;;
(restore-list) _mu_labels_restore_list && ret=0;;
(export) _mu_labels_export && ret=0;;
(import) _mu_labels_import && ret=0;;
esac
;;
esac
return ret
}
_mu_mkdir() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--mode=[Set the access mode (octal)]:mode:' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'*:dirs:_files -/'
}
_mu_move() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--change-name[Change name of target file]' \
'--update-dups[Update duplicate messages too]' \
'(-n --dry-run)'{-n,--dry-run}'[Print target name, but do not change anything]' \
'--flags=[Target flags]:flags:' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1:source:_files' \
'2::destination:_files -/'
}
_mu_remove() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'*:files:_files'
}
_mu_scm() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--listen[Start SCM REPL on a domain socket]' \
'--eval=[Expression to evaluate]:value:' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1::script-path:_files' \
'*:script-args:'
}
_mu_server() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'--commands[List available commands]' \
'--eval=[Evaluate mu server expression]:value:' \
'--allow-temp-file[Allow for the temp-file optimization]' \
'--listen[Start SCM REPL on a domain socket]' \
'--muhome=[Specify alternative mu directory]:dir:_files -/' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]'
}
_mu_verify() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-r --auto-retrieve)'{-r,--auto-retrieve}'[Attempt to automatically retrieve online keys]' \
'--decrypt[Attempt to decrypt]' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'*:message-paths:_files'
}
_mu_view() {
_arguments -S \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-o --format)'{-o+,--format=}'[Output format]:format:(html plain sexp)' \
'(-r --auto-retrieve)'{-r,--auto-retrieve}'[Attempt to automatically retrieve online keys]' \
'--decrypt[Attempt to decrypt]' \
'--summary-len=[Use up to so many lines for the summary]:lines:' \
'--terminate[Insert form-feed after each message]' \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'*:message-paths:_files'
}
_mu_commands() {
local -a commands
commands=(
'add:Add messages to the database'
'cfind:Find contacts matching some pattern'
'extract:Extract attachments and other MIME-parts'
'fields:Superseded by '\''mu info'\'''
'find:Find messages matching some query'
'help:Show help information'
'index:Scan maildirs and store information'
'info:Show information about mu'
'init:Initialize the mu database'
'labels:Manage message labels'
'mkdir:Create a new Maildir'
'move:Move a message or change its flags'
'remove:Remove message from file-system and database'
'scm:Start Guile/Scheme shell or run script'
'server:Start a mu server (for mu4e)'
'verify:Verify cryptographic signatures'
'view:View specific messages'
)
_describe -t commands 'command' commands
}
_mu() {
local curcontext="$curcontext" state line ret=1
_arguments -S -C \
'(-V --version)'{-V,--version}'[Display program version information and exit]' \
'(-h --help)'{-h,--help}'[Show help information]' \
'--help-all[Show help for all commands]' \
'(-q --quiet)'{-q,--quiet}'[Hide non-essential output]' \
'(-v --verbose)'{-v,--verbose}'[Show verbose output]' \
'--nocolor[Don'\''t show ANSI colors]' \
'1: :_mu_commands' \
'*::arg:->args' && ret=0
case $state in
(args)
curcontext="${curcontext%:*:*}:mu-${words[1]}:"
case ${words[1]} in
(add) _mu_add && ret=0;;
(cfind) _mu_cfind && ret=0;;
(extract) _mu_extract && ret=0;;
(fields) _mu_fields && ret=0;;
(find) _mu_find && ret=0;;
(help) _mu_help && ret=0;;
(index) _mu_index && ret=0;;
(info) _mu_info && ret=0;;
(init) _mu_init && ret=0;;
(labels) _mu_labels && ret=0;;
(mkdir) _mu_mkdir && ret=0;;
(move) _mu_move && ret=0;;
(remove) _mu_remove && ret=0;;
(scm) _mu_scm && ret=0;;
(server) _mu_server && ret=0;;
(verify) _mu_verify && ret=0;;
(view) _mu_view && ret=0;;
esac
;;
esac
return ret
}
_mu "$@"

View File

@ -39,7 +39,9 @@ mu = executable(
lib_mu_dep, mu_scm_dep, thread_dep, config_h_dep ],
cpp_args: ['-DMU_SCRIPTS_DIR="' + datadir / 'mu' / 'scripts' + '"'],
install: true)
#
subdir('completion')
if get_option('tests').allowed()
subdir('tests')
endif