Discussion:
[PATCH] More complete completion scripts for zsh.
Vincent Breitmoser
2018-09-15 22:34:28 UTC
Permalink
This adds completion files for zsh that cover most of notmuch's cli.

The files in completion/zsh are formatted so that they can be found by
zsh's completion system if put $fpath. They are also registered to the
notmuch-* pattern, so they can be called externally using _dispatch.
---
completion/README | 4 +-
completion/notmuch-completion.zsh | 88 ---------
completion/zsh/_email-notmuch | 10 ++
completion/zsh/_notmuch | 289 ++++++++++++++++++++++++++++++
4 files changed, 301 insertions(+), 90 deletions(-)
delete mode 100644 completion/notmuch-completion.zsh
create mode 100644 completion/zsh/_email-notmuch
create mode 100644 completion/zsh/_notmuch

diff --git a/completion/README b/completion/README
index 89805c72..900e1c98 100644
--- a/completion/README
+++ b/completion/README
@@ -11,6 +11,6 @@ notmuch-completion.bash

[1] https://github.com/scop/bash-completion

-notmuch-completion.zsh
+zsh

- Command-line completion for the zsh shell.
+ Command-line completions for the zsh shell.
diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh
deleted file mode 100644
index 208a5503..00000000
--- a/completion/notmuch-completion.zsh
+++ /dev/null
@@ -1,88 +0,0 @@
-#compdef notmuch
-
-# ZSH completion for `notmuch`
-# Copyright © 2009 Ingmar Vanhassel <***@exherbo.org>
-
-_notmuch_commands()
-{
- local -a notmuch_commands
- notmuch_commands=(
- 'help:display documentation for a subcommand'
- 'setup:interactively configure notmuch'
-
- 'address:output addresses from matching messages'
- 'compact:compact the notmuch database'
- 'config:access notmuch configuration file'
- 'count:count messages matching the given search terms'
- 'dump:creates a plain-text dump of the tags of each message'
- 'insert:add a message to the maildir and notmuch database'
- 'new:incorporate new mail into the notmuch database'
- 'reply:constructs a reply template for a set of messages'
- 'restore:restores the tags from the given file (see notmuch dump)'
- 'search:search for messages matching the given search terms'
- 'show:show messages matching the given search terms'
- 'tag:add/remove tags for all messages matching the search terms'
- )
-
- _describe -t command 'command' notmuch_commands
-}
-
-_notmuch_dump()
-{
- _files
-}
-
-_notmuch_help_topics()
-{
- local -a notmuch_help_topics
- notmuch_help_topics=(
- 'search-terms:show common search-terms syntax'
- )
- _describe -t notmuch-help-topics 'topic' notmuch_help_topics
-}
-
-_notmuch_help()
-{
- _alternative \
- _notmuch_commands \
- _notmuch_help_topics
-}
-
-_notmuch_restore()
-{
- _files
-}
-
-_notmuch_search()
-{
- _arguments -s : \
- '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \
- '--first=[omit the first x threads from the search results]:number of threads to omit: ' \
- '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
- '--output=[select what to output]:output:((summary threads messages files tags))'
-}
-
-_notmuch_address()
-{
- _arguments -s : \
- '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
- '--output=[select what to output]:output:((sender recipients count))'
-}
-
-_notmuch()
-{
- if (( CURRENT > 2 )) ; then
- local cmd=${words[2]}
- curcontext="${curcontext%:*:*}:notmuch-$cmd"
- (( CURRENT-- ))
- shift words
- _call_function ret _notmuch_$cmd
- return ret
- else
- _notmuch_commands
- fi
-}
-
-_notmuch "$@"
-
-# vim: set sw=2 sts=2 ts=2 et ft=zsh :
diff --git a/completion/zsh/_email-notmuch b/completion/zsh/_email-notmuch
new file mode 100644
index 00000000..3e157dd1
--- /dev/null
+++ b/completion/zsh/_email-notmuch
@@ -0,0 +1,10 @@
+#autoload
+
+local expl
+local -a notmuch_addr
+
+notmuch_addr=( ${(f)"$(notmuch address -- $PREFIX'*')"} )
+notmuch_addr=( ${${notmuch_addr##*<}%%>*} )
+
+_description notmuch-addr expl 'email address (notmuch)'
+compadd "$expl[@]" -a notmuch_addr
diff --git a/completion/zsh/_notmuch b/completion/zsh/_notmuch
new file mode 100644
index 00000000..ae071081
--- /dev/null
+++ b/completion/zsh/_notmuch
@@ -0,0 +1,289 @@
+#compdef notmuch -p notmuch-*
+
+_notmuch_command() {
+ local -a notmuch_commands
+ notmuch_commands=(
+ 'help:display documentation for a subcommand'
+ 'setup:interactively configure notmuch'
+
+ 'address:output addresses from matching messages'
+ 'compact:compact the notmuch database'
+ 'config:access notmuch configuration file'
+ 'count:count messages matching the given search terms'
+ 'dump:creates a plain-text dump of the tags of each message'
+ 'insert:add a message to the maildir and notmuch database'
+ 'new:incorporate new mail into the notmuch database'
+ 'reply:constructs a reply template for a set of messages'
+ 'restore:restores the tags from the given file (see notmuch dump)'
+ 'search:search for messages matching the given search terms'
+ 'show:show messages matching the given search terms'
+ 'tag:add/remove tags for all messages matching the search terms'
+ )
+
+ if ((CURRENT == 1)); then
+ _describe -t commands 'notmuch command' notmuch_commands
+ else
+ local curcontext="$curcontext"
+ cmd=$words[1]
+ if (( $+functions[_notmuch_$cmd] )); then
+ _notmuch_$cmd
+ else
+ _message -e "unknown command $cmd"
+ fi
+ fi
+}
+
+_notmuch_term_tag() {
+ local ret=1 expl
+ local -a notmuch_tags
+
+ notmuch_tags=( ${(f)"$(notmuch search --output=tags '*')"} )
+
+ _description notmuch-tag expl 'tag'
+ compadd "$expl[@]" -a notmuch_tags && ret=0
+ return $ret
+}
+
+_notmuch_term_to _notmuch_term_from() {
+ _email_addresses -c
+}
+
+_notmuch_term_mimetype() {
+ local ret=1 expl
+ local -a commontypes
+ commontypes=(
+ 'text/plain'
+ 'text/html'
+ 'application/pdf'
+ )
+ _description typical-mimetypes expl 'common types'
+ compadd "$expl[@]" -a commontypes && ret=0
+
+ _mime_types && ret=0
+ return $ret
+}
+
+_notmuch_term_path() {
+ local ret=1 expl
+ local maildir="$(notmuch config get database.path)"
+ [[ -d $maildir ]] || { _message -e "database.path not found" ; return $ret }
+
+ _description notmuch-folder expl 'maildir folder'
+ _files "$expl[@]" -W $maildir -/ && ret=0
+ return $ret
+}
+
+_notmuch_term_folder() {
+ local ret=1 expl
+ local maildir="$(notmuch config get database.path)"
+ [[ -d $maildir ]] || { _message -e "database.path not found" ; return $ret }
+
+ _description notmuch-folder expl 'maildir folder'
+ local ignoredfolders=( '*/(cur|new|tmp)' )
+ _files "$expl[@]" -W $maildir -F ignoredfolders -/ && ret=0
+ return $ret
+}
+
+_notmuch_term_query() {
+ local ret=1
+ local line query_name
+ local -a query_names query_content
+ for line in ${(f)"$(notmuch config list | grep '^query.')"}; do
+ query_name=${${line%%=*}#query.}
+ query_names+=( $query_name )
+ query_content+=( "$query_name = ${line#*=}" )
+ done
+
+ _description notmuch-named-query expl 'named query'
+ compadd "$expl[@]" -d query_content -a query_names && ret=0
+ return $ret
+}
+
+_notmuch_search_term() {
+ local ret=1 expl match
+ setopt localoptions extendedglob
+
+ typeset -a notmuch_search_terms
+ notmuch_search_terms=(
+ 'from' 'to' 'subject' 'attachment' 'mimetype' 'tag' 'id' 'thread' 'path' 'folder' 'date' 'lastmod' 'query' 'property'
+ )
+
+ if compset -P '(#b)(*):'; then
+ if (( $+functions[_notmuch_term_$match[1]] )); then
+ _notmuch_term_$match[1] && ret=0
+ return $ret
+ elif (( $+notmuch_search_terms[(r)$match[1]] )); then
+ _message "search term '$match[1]'" && ret=0
+ return $ret
+ else
+ _message -e "unknown search term '$match[1]'"
+ return $ret
+ fi
+ fi
+
+ _description notmuch-term expl 'search term'
+ compadd "$expl[@]" -S ':' -a notmuch_search_terms && ret=0
+
+ if [[ $CURRENT -gt 1 && $words[CURRENT-1] != '--' ]]; then
+ _description notmuch-op expl 'boolean operator'
+ compadd "$expl[@]" -- and or not xor && ret=0
+ fi
+
+ return $ret
+}
+
+_notmuch_tagging_or_search() {
+ setopt localoptions extendedglob
+ local ret=1 expl
+ local -a notmuch_tags
+
+ # first arg that is a search term, or $#words+1
+ integer searchtermarg=$(( $words[(I)--] != 0 ? $words[(i)--] : $words[(i)^(-|+)*] ))
+
+ if (( CURRENT > 1 )); then
+ () {
+ local -a words=( $argv )
+ local CURRENT=$(( CURRENT - searchtermarg + 1 ))
+ _notmuch_search_term && ret=0
+ } $words[searchtermarg,$]
+ fi
+
+ # only complete +/- tags if we're before the first search term
+ if (( searchtermarg >= CURRENT )); then
+ if compset -P '+'; then
+ notmuch_tags=( ${(f)"$(notmuch search --output=tags '*')"} )
+ _description notmuch-tag expl 'add tag'
+ compadd "$expl[@]" -a notmuch_tags
+ return 0
+ elif compset -P '-'; then
+ notmuch_tags=( ${(f)"$(notmuch search --output=tags '*')"} )
+ _description notmuch-tag expl 'remove tag'
+ compadd "$expl[@]" -a notmuch_tags
+ return 0
+ else
+ _description notmuch-tag expl 'add or remove tags'
+ compadd "$expl[@]" -S '' -- '+' '-' && ret=0
+ fi
+ fi
+
+ return $ret
+}
+
+_notmuch_address() {
+ _arguments -S \
+ '--format=[set output format]:output format:(json sexp text text0)' \
+ '--format-version=[set output format version]:format version: ' \
+ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
+ '--output=[select output format]:output format:(sender recipients count address)' \
+ '--deduplicate=[deduplicate results]:deduplication mode:(no mailbox address)' \
+ '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_compact() {
+ _arguments \
+ '--backup=[save a backup before compacting]:backup directory:_files -/' \
+ '--quiet[do not print progress or results]'
+}
+
+_notmuch_count() {
+ _arguments \
+ - normal \
+ '--lastmod[append lastmod and uuid to output]' \
+ '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
+ '--output=[select what to count]:output format:(messages threads files)' \
+ '*::search term:_notmuch_search_term' \
+ - batch \
+ '--batch[operate in batch mode]' \
+ '(--batch)--input=[read batch operations from file]:batch file:_files'
+}
+
+_notmuch_dump() {
+ _arguments -S \
+ '--gzip[compress output with gzip]' \
+ '--format=[specify output format]:output format:(batch-tag sup)' \
+ '*--include=[configure metadata to output (default all)]:metadata type:(config properties tags)' \
+ '--output=[write output to file]:output file:_files' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_new() {
+ _arguments \
+ '--no-hooks[prevent hooks from being run]' \
+ '--quiet[do not print progress or results]' \
+ '--full-scan[don''t rely on directory modification times for scan]' \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys" stash\:"decrypt, and store session keys"))'
+}
+
+_notmuch_reindex() {
+ _arguments \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys" stash\:"decrypt, and store session keys"))'
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_search() {
+ _arguments -S \
+ '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \
+ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \
+ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
+ '--output=[select what to output]:output:(summary threads messages files tags)' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_show() {
+ _arguments -S \
+ '--entire-thread=[output entire threads]:show thread:(true false)' \
+ '--format=[set output format]:output format:(text json sexp mbox raw)' \
+ '--format-version=[set output format version]:format version: ' \
+ '--part=[output a single decoded mime part]:part number: ' \
+ '--verify[verify signed MIME parts]' \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys" stash\:"decrypt, and store session keys"))' \
+ '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
+ '--body=[output body]:output body content:(true false)' \
+ '--include-html[include text/html parts in the output]' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_reply() {
+ _arguments \
+ '--format=[set output format]:output format:(default json sexp headers-only)' \
+ '--format-version=[set output format version]:output format version: ' \
+ '--reply-to=[specify recipient types]:recipient types:(all sender)' \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys"))' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_restore() {
+ _arguments \
+ '--acumulate[add data to db instead of replacing]' \
+ '--format=[specify input format]:input format:(auto batch-tag sup)' \
+ '*--include=[configure metadata to import (default all)]:metadata type:(config properties tags)' \
+ '--input=[read from file]:notmuch dump file:_files'
+}
+
+_notmuch_tag() {
+ _arguments \
+ - normal \
+ '--remove-all[remove all tags from matching messages]:*:search term:_notmuch_search_term' \
+ '*::tag or search:_notmuch_tagging_or_search' \
+ - batch \
+ '--batch[operate in batch mode]' \
+ '(--batch)--input=[read batch operations from file]:batch file:_files'
+}
+
+_notmuch() {
+ if [[ $service == notmuch-* ]]; then
+ local compfunc=_${service//-/_}
+ (( $+functions[$compfunc] )) || return 1
+ $compfunc "$@"
+ else
+ _arguments \
+ '(* -)--help[show help]' \
+ '(* -)--version[show version]' \
+ '--config=-[specify config file]:config file:_files' \
+ '--uuid=-[check against database uuid or exit]:uuid: ' \
+ '*::notmuch commands:_notmuch_command'
+ fi
+}
+
+_notmuch "$@"
--
2.18.0
Chris Coutinho
2018-09-16 11:26:51 UTC
Permalink
Thanks for the patch Vincent!

The Makefile.local should also be updated to successfully install
notmuch using this patch.

Regards,
Chris

---
completion/Makefile.local | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/completion/Makefile.local b/completion/Makefile.local
index dfc12713..74c89760 100644
--- a/completion/Makefile.local
+++ b/completion/Makefile.local
@@ -6,7 +6,8 @@ dir := completion
# directly in any shell commands. Instead we save its value in other,
# private variables that we can use in the commands.
bash_script := $(srcdir)/$(dir)/notmuch-completion.bash
-zsh_script := $(srcdir)/$(dir)/notmuch-completion.zsh
+zsh_script1 := $(srcdir)/$(dir)/zsh/_notmuch
+zsh_script2 := $(srcdir)/$(dir)/zsh/_email-notmuch

install: install-$(dir)

@@ -18,5 +19,6 @@ ifeq ($(WITH_BASH),1)
endif
ifeq ($(WITH_ZSH),1)
mkdir -p "$(DESTDIR)$(zsh_completion_dir)"
- install -m0644 $(zsh_script) "$(DESTDIR)$(zsh_completion_dir)/_notmuch"
+ install -m0644 $(zsh_script1) "$(DESTDIR)$(zsh_completion_dir)/_notmuch"
+ install -m0644 $(zsh_script2) "$(DESTDIR)$(zsh_completion_dir)/_email-notmuch"
endif
--
2.18.0
David Bremner
2018-09-16 12:44:18 UTC
Permalink
Post by Chris Coutinho
Thanks for the patch Vincent!
The Makefile.local should also be updated to successfully install
notmuch using this patch.
Hah, our patches passed in the mail!
David Bremner
2018-09-16 12:41:39 UTC
Permalink
Hi Vincent;

Thanks for this patch, it's definitely a big improvement. I had to
make some small adjustments to the install recipes to accomodate the
renaming / removal of notmuch-completion.zsh. A revised version of
your patch with that squashed in follows.

I also added a patch to install the completion files properly on
Debian. That could be squashed in, or left as a seperate patch.

I do have one problem with the completion of tags. It seems the
treatment of tags with :: in them is not consistent.

If I run

% notmuch tag +foo::bar tag:foo::bar

for some existing tag foo::bar, the second one fails to complete

C-x h yields something like the following

╭─ convex:~
╰─% notmuch tag +inbox::notmuch tag:inbox:
tags in context :completion::complete:notmuch::
argument-rest (_arguments _notmuch)
tags in context :completion::complete:notmuch:argument-rest:
normal-argument-rest (_arguments _notmuch_tag _notmuch_command _arguments _notmuch)
tags in context :completion::complete:notmuch:normal-argument-rest:
normal-argument-rest (_message _notmuch_search_term _notmuch_tagging_or_search _arguments _notmuch_tag _notmuch_command _arguments _notmuch)
David Bremner
2018-09-16 12:41:41 UTC
Permalink
This ${prefix}/share/vendor-completion convention seems to be debian
specific, so leave the global default alone for now.
---
debian/notmuch.install | 1 +
debian/rules | 1 +
2 files changed, 2 insertions(+)

diff --git a/debian/notmuch.install b/debian/notmuch.install
index 31b9a37e..5067a441 100644
--- a/debian/notmuch.install
+++ b/debian/notmuch.install
@@ -1,3 +1,4 @@
usr/bin
usr/share/man
usr/share/bash-completion
+usr/share/zsh/vendor-completions
diff --git a/debian/rules b/debian/rules
index 1ac6498c..b2cba0e9 100755
--- a/debian/rules
+++ b/debian/rules
@@ -12,6 +12,7 @@ override_dh_auto_configure:
--mandir=/usr/share/man \
--infodir=/usr/share/info \
--sysconfdir=/etc \
+ --zshcompletiondir=/usr/share/zsh/vendor-completions \
--localstatedir=/var

override_dh_auto_build:
--
2.18.0
David Bremner
2018-09-16 12:41:40 UTC
Permalink
From: Vincent Breitmoser <***@my.amazin.horse>

This adds completion files for zsh that cover most of notmuch's cli.

The files in completion/zsh are formatted so that they can be found by
zsh's completion system if put $fpath. They are also registered to the
notmuch-* pattern, so they can be called externally using _dispatch.

Update installation recipe and drop debian/notmuch.examples to avoid
breakage. This means zsh completion is not installed for debian, to be
fixed in a future commit.
---
completion/Makefile.local | 4 +-
completion/README | 4 +-
completion/notmuch-completion.zsh | 88 ---------
completion/zsh/_email-notmuch | 10 ++
completion/zsh/_notmuch | 289 ++++++++++++++++++++++++++++++
debian/notmuch.examples | 1 -
6 files changed, 303 insertions(+), 93 deletions(-)
delete mode 100644 completion/notmuch-completion.zsh
create mode 100644 completion/zsh/_email-notmuch
create mode 100644 completion/zsh/_notmuch
delete mode 100644 debian/notmuch.examples

diff --git a/completion/Makefile.local b/completion/Makefile.local
index dfc12713..8e86c9d2 100644
--- a/completion/Makefile.local
+++ b/completion/Makefile.local
@@ -6,7 +6,7 @@ dir := completion
# directly in any shell commands. Instead we save its value in other,
# private variables that we can use in the commands.
bash_script := $(srcdir)/$(dir)/notmuch-completion.bash
-zsh_script := $(srcdir)/$(dir)/notmuch-completion.zsh
+zsh_scripts := $(srcdir)/$(dir)/zsh/_notmuch $(srcdir)/$(dir)/zsh/_email-notmuch

install: install-$(dir)

@@ -18,5 +18,5 @@ ifeq ($(WITH_BASH),1)
endif
ifeq ($(WITH_ZSH),1)
mkdir -p "$(DESTDIR)$(zsh_completion_dir)"
- install -m0644 $(zsh_script) "$(DESTDIR)$(zsh_completion_dir)/_notmuch"
+ install -m0644 $(zsh_scripts) "$(DESTDIR)$(zsh_completion_dir)"
endif
diff --git a/completion/README b/completion/README
index 89805c72..900e1c98 100644
--- a/completion/README
+++ b/completion/README
@@ -11,6 +11,6 @@ notmuch-completion.bash

[1] https://github.com/scop/bash-completion

-notmuch-completion.zsh
+zsh

- Command-line completion for the zsh shell.
+ Command-line completions for the zsh shell.
diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh
deleted file mode 100644
index 208a5503..00000000
--- a/completion/notmuch-completion.zsh
+++ /dev/null
@@ -1,88 +0,0 @@
-#compdef notmuch
-
-# ZSH completion for `notmuch`
-# Copyright © 2009 Ingmar Vanhassel <***@exherbo.org>
-
-_notmuch_commands()
-{
- local -a notmuch_commands
- notmuch_commands=(
- 'help:display documentation for a subcommand'
- 'setup:interactively configure notmuch'
-
- 'address:output addresses from matching messages'
- 'compact:compact the notmuch database'
- 'config:access notmuch configuration file'
- 'count:count messages matching the given search terms'
- 'dump:creates a plain-text dump of the tags of each message'
- 'insert:add a message to the maildir and notmuch database'
- 'new:incorporate new mail into the notmuch database'
- 'reply:constructs a reply template for a set of messages'
- 'restore:restores the tags from the given file (see notmuch dump)'
- 'search:search for messages matching the given search terms'
- 'show:show messages matching the given search terms'
- 'tag:add/remove tags for all messages matching the search terms'
- )
-
- _describe -t command 'command' notmuch_commands
-}
-
-_notmuch_dump()
-{
- _files
-}
-
-_notmuch_help_topics()
-{
- local -a notmuch_help_topics
- notmuch_help_topics=(
- 'search-terms:show common search-terms syntax'
- )
- _describe -t notmuch-help-topics 'topic' notmuch_help_topics
-}
-
-_notmuch_help()
-{
- _alternative \
- _notmuch_commands \
- _notmuch_help_topics
-}
-
-_notmuch_restore()
-{
- _files
-}
-
-_notmuch_search()
-{
- _arguments -s : \
- '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \
- '--first=[omit the first x threads from the search results]:number of threads to omit: ' \
- '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
- '--output=[select what to output]:output:((summary threads messages files tags))'
-}
-
-_notmuch_address()
-{
- _arguments -s : \
- '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
- '--output=[select what to output]:output:((sender recipients count))'
-}
-
-_notmuch()
-{
- if (( CURRENT > 2 )) ; then
- local cmd=${words[2]}
- curcontext="${curcontext%:*:*}:notmuch-$cmd"
- (( CURRENT-- ))
- shift words
- _call_function ret _notmuch_$cmd
- return ret
- else
- _notmuch_commands
- fi
-}
-
-_notmuch "$@"
-
-# vim: set sw=2 sts=2 ts=2 et ft=zsh :
diff --git a/completion/zsh/_email-notmuch b/completion/zsh/_email-notmuch
new file mode 100644
index 00000000..3e157dd1
--- /dev/null
+++ b/completion/zsh/_email-notmuch
@@ -0,0 +1,10 @@
+#autoload
+
+local expl
+local -a notmuch_addr
+
+notmuch_addr=( ${(f)"$(notmuch address -- $PREFIX'*')"} )
+notmuch_addr=( ${${notmuch_addr##*<}%%>*} )
+
+_description notmuch-addr expl 'email address (notmuch)'
+compadd "$expl[@]" -a notmuch_addr
diff --git a/completion/zsh/_notmuch b/completion/zsh/_notmuch
new file mode 100644
index 00000000..ae071081
--- /dev/null
+++ b/completion/zsh/_notmuch
@@ -0,0 +1,289 @@
+#compdef notmuch -p notmuch-*
+
+_notmuch_command() {
+ local -a notmuch_commands
+ notmuch_commands=(
+ 'help:display documentation for a subcommand'
+ 'setup:interactively configure notmuch'
+
+ 'address:output addresses from matching messages'
+ 'compact:compact the notmuch database'
+ 'config:access notmuch configuration file'
+ 'count:count messages matching the given search terms'
+ 'dump:creates a plain-text dump of the tags of each message'
+ 'insert:add a message to the maildir and notmuch database'
+ 'new:incorporate new mail into the notmuch database'
+ 'reply:constructs a reply template for a set of messages'
+ 'restore:restores the tags from the given file (see notmuch dump)'
+ 'search:search for messages matching the given search terms'
+ 'show:show messages matching the given search terms'
+ 'tag:add/remove tags for all messages matching the search terms'
+ )
+
+ if ((CURRENT == 1)); then
+ _describe -t commands 'notmuch command' notmuch_commands
+ else
+ local curcontext="$curcontext"
+ cmd=$words[1]
+ if (( $+functions[_notmuch_$cmd] )); then
+ _notmuch_$cmd
+ else
+ _message -e "unknown command $cmd"
+ fi
+ fi
+}
+
+_notmuch_term_tag() {
+ local ret=1 expl
+ local -a notmuch_tags
+
+ notmuch_tags=( ${(f)"$(notmuch search --output=tags '*')"} )
+
+ _description notmuch-tag expl 'tag'
+ compadd "$expl[@]" -a notmuch_tags && ret=0
+ return $ret
+}
+
+_notmuch_term_to _notmuch_term_from() {
+ _email_addresses -c
+}
+
+_notmuch_term_mimetype() {
+ local ret=1 expl
+ local -a commontypes
+ commontypes=(
+ 'text/plain'
+ 'text/html'
+ 'application/pdf'
+ )
+ _description typical-mimetypes expl 'common types'
+ compadd "$expl[@]" -a commontypes && ret=0
+
+ _mime_types && ret=0
+ return $ret
+}
+
+_notmuch_term_path() {
+ local ret=1 expl
+ local maildir="$(notmuch config get database.path)"
+ [[ -d $maildir ]] || { _message -e "database.path not found" ; return $ret }
+
+ _description notmuch-folder expl 'maildir folder'
+ _files "$expl[@]" -W $maildir -/ && ret=0
+ return $ret
+}
+
+_notmuch_term_folder() {
+ local ret=1 expl
+ local maildir="$(notmuch config get database.path)"
+ [[ -d $maildir ]] || { _message -e "database.path not found" ; return $ret }
+
+ _description notmuch-folder expl 'maildir folder'
+ local ignoredfolders=( '*/(cur|new|tmp)' )
+ _files "$expl[@]" -W $maildir -F ignoredfolders -/ && ret=0
+ return $ret
+}
+
+_notmuch_term_query() {
+ local ret=1
+ local line query_name
+ local -a query_names query_content
+ for line in ${(f)"$(notmuch config list | grep '^query.')"}; do
+ query_name=${${line%%=*}#query.}
+ query_names+=( $query_name )
+ query_content+=( "$query_name = ${line#*=}" )
+ done
+
+ _description notmuch-named-query expl 'named query'
+ compadd "$expl[@]" -d query_content -a query_names && ret=0
+ return $ret
+}
+
+_notmuch_search_term() {
+ local ret=1 expl match
+ setopt localoptions extendedglob
+
+ typeset -a notmuch_search_terms
+ notmuch_search_terms=(
+ 'from' 'to' 'subject' 'attachment' 'mimetype' 'tag' 'id' 'thread' 'path' 'folder' 'date' 'lastmod' 'query' 'property'
+ )
+
+ if compset -P '(#b)(*):'; then
+ if (( $+functions[_notmuch_term_$match[1]] )); then
+ _notmuch_term_$match[1] && ret=0
+ return $ret
+ elif (( $+notmuch_search_terms[(r)$match[1]] )); then
+ _message "search term '$match[1]'" && ret=0
+ return $ret
+ else
+ _message -e "unknown search term '$match[1]'"
+ return $ret
+ fi
+ fi
+
+ _description notmuch-term expl 'search term'
+ compadd "$expl[@]" -S ':' -a notmuch_search_terms && ret=0
+
+ if [[ $CURRENT -gt 1 && $words[CURRENT-1] != '--' ]]; then
+ _description notmuch-op expl 'boolean operator'
+ compadd "$expl[@]" -- and or not xor && ret=0
+ fi
+
+ return $ret
+}
+
+_notmuch_tagging_or_search() {
+ setopt localoptions extendedglob
+ local ret=1 expl
+ local -a notmuch_tags
+
+ # first arg that is a search term, or $#words+1
+ integer searchtermarg=$(( $words[(I)--] != 0 ? $words[(i)--] : $words[(i)^(-|+)*] ))
+
+ if (( CURRENT > 1 )); then
+ () {
+ local -a words=( $argv )
+ local CURRENT=$(( CURRENT - searchtermarg + 1 ))
+ _notmuch_search_term && ret=0
+ } $words[searchtermarg,$]
+ fi
+
+ # only complete +/- tags if we're before the first search term
+ if (( searchtermarg >= CURRENT )); then
+ if compset -P '+'; then
+ notmuch_tags=( ${(f)"$(notmuch search --output=tags '*')"} )
+ _description notmuch-tag expl 'add tag'
+ compadd "$expl[@]" -a notmuch_tags
+ return 0
+ elif compset -P '-'; then
+ notmuch_tags=( ${(f)"$(notmuch search --output=tags '*')"} )
+ _description notmuch-tag expl 'remove tag'
+ compadd "$expl[@]" -a notmuch_tags
+ return 0
+ else
+ _description notmuch-tag expl 'add or remove tags'
+ compadd "$expl[@]" -S '' -- '+' '-' && ret=0
+ fi
+ fi
+
+ return $ret
+}
+
+_notmuch_address() {
+ _arguments -S \
+ '--format=[set output format]:output format:(json sexp text text0)' \
+ '--format-version=[set output format version]:format version: ' \
+ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
+ '--output=[select output format]:output format:(sender recipients count address)' \
+ '--deduplicate=[deduplicate results]:deduplication mode:(no mailbox address)' \
+ '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_compact() {
+ _arguments \
+ '--backup=[save a backup before compacting]:backup directory:_files -/' \
+ '--quiet[do not print progress or results]'
+}
+
+_notmuch_count() {
+ _arguments \
+ - normal \
+ '--lastmod[append lastmod and uuid to output]' \
+ '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
+ '--output=[select what to count]:output format:(messages threads files)' \
+ '*::search term:_notmuch_search_term' \
+ - batch \
+ '--batch[operate in batch mode]' \
+ '(--batch)--input=[read batch operations from file]:batch file:_files'
+}
+
+_notmuch_dump() {
+ _arguments -S \
+ '--gzip[compress output with gzip]' \
+ '--format=[specify output format]:output format:(batch-tag sup)' \
+ '*--include=[configure metadata to output (default all)]:metadata type:(config properties tags)' \
+ '--output=[write output to file]:output file:_files' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_new() {
+ _arguments \
+ '--no-hooks[prevent hooks from being run]' \
+ '--quiet[do not print progress or results]' \
+ '--full-scan[don''t rely on directory modification times for scan]' \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys" stash\:"decrypt, and store session keys"))'
+}
+
+_notmuch_reindex() {
+ _arguments \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys" stash\:"decrypt, and store session keys"))'
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_search() {
+ _arguments -S \
+ '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \
+ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \
+ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
+ '--output=[select what to output]:output:(summary threads messages files tags)' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_show() {
+ _arguments -S \
+ '--entire-thread=[output entire threads]:show thread:(true false)' \
+ '--format=[set output format]:output format:(text json sexp mbox raw)' \
+ '--format-version=[set output format version]:format version: ' \
+ '--part=[output a single decoded mime part]:part number: ' \
+ '--verify[verify signed MIME parts]' \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys" stash\:"decrypt, and store session keys"))' \
+ '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
+ '--body=[output body]:output body content:(true false)' \
+ '--include-html[include text/html parts in the output]' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_reply() {
+ _arguments \
+ '--format=[set output format]:output format:(default json sexp headers-only)' \
+ '--format-version=[set output format version]:output format version: ' \
+ '--reply-to=[specify recipient types]:recipient types:(all sender)' \
+ '--decrypt=[decrypt messages]:decryption setting:((false\:"never decrypt" auto\:"decrypt if session key is known (default)" true\:"decrypt using secret keys"))' \
+ '*::search term:_notmuch_search_term'
+}
+
+_notmuch_restore() {
+ _arguments \
+ '--acumulate[add data to db instead of replacing]' \
+ '--format=[specify input format]:input format:(auto batch-tag sup)' \
+ '*--include=[configure metadata to import (default all)]:metadata type:(config properties tags)' \
+ '--input=[read from file]:notmuch dump file:_files'
+}
+
+_notmuch_tag() {
+ _arguments \
+ - normal \
+ '--remove-all[remove all tags from matching messages]:*:search term:_notmuch_search_term' \
+ '*::tag or search:_notmuch_tagging_or_search' \
+ - batch \
+ '--batch[operate in batch mode]' \
+ '(--batch)--input=[read batch operations from file]:batch file:_files'
+}
+
+_notmuch() {
+ if [[ $service == notmuch-* ]]; then
+ local compfunc=_${service//-/_}
+ (( $+functions[$compfunc] )) || return 1
+ $compfunc "$@"
+ else
+ _arguments \
+ '(* -)--help[show help]' \
+ '(* -)--version[show version]' \
+ '--config=-[specify config file]:config file:_files' \
+ '--uuid=-[check against database uuid or exit]:uuid: ' \
+ '*::notmuch commands:_notmuch_command'
+ fi
+}
+
+_notmuch "$@"
diff --git a/debian/notmuch.examples b/debian/notmuch.examples
deleted file mode 100644
index 524e0f4b..00000000
--- a/debian/notmuch.examples
+++ /dev/null
@@ -1 +0,0 @@
-completion/notmuch-completion.zsh
--
2.18.0
Vincent Breitmoser
2018-09-16 13:22:02 UTC
Permalink
Hey David,
Post by David Bremner
I do have one problem with the completion of tags. It seems the
treatment of tags with :: in them is not consistent.
Thanks for the hint. I didn't expect :: to be special so I didn't test that
case. I'll check it out later today or possibly tomorrow.

- V
David Bremner
2018-09-16 14:38:25 UTC
Permalink
Post by Vincent Breitmoser
Hey David,
Post by David Bremner
I do have one problem with the completion of tags. It seems the
treatment of tags with :: in them is not consistent.
Thanks for the hint. I didn't expect :: to be special so I didn't test that
case. I'll check it out later today or possibly tomorrow.
It is just a convention, but it's used by nmbug, so I guess some people
other than me would hit it.

Loading...