completion: handle notmuch address --deduplicate= option
[notmuch] / completion / notmuch-completion.bash
1 # bash completion for notmuch                              -*- shell-script -*-
2 #
3 # Copyright © 2013 Jani Nikula
4 #
5 # Based on the bash-completion package:
6 # http://bash-completion.alioth.debian.org/
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see http://www.gnu.org/licenses/ .
20 #
21 # Author: Jani Nikula <jani@nikula.org>
22 #
23 #
24 # BUGS:
25 #
26 # Add space after an --option without parameter (e.g. reply --decrypt)
27 # on completion.
28 #
29
30 # $1: current input of the form prefix:partialinput, where prefix is
31 # to or from.
32 _notmuch_email()
33 {
34     local output prefix cur
35
36     prefix="${1%%:*}"
37     cur="${1#*:}"
38
39     # Cut the input to be completed at punctuation because
40     # (apparently) Xapian does not support the trailing wildcard '*'
41     # operator for input with punctuation. We let compgen handle the
42     # extra filtering required.
43     cur="${cur%%[^a-zA-Z0-9]*}"
44
45     case "$prefix" in
46         # Note: It would be more accurate and less surprising to have
47         # output=recipients here for to: addresses, but as gathering
48         # the recipient addresses requires disk access for each
49         # matching message, this becomes prohibitively slow.
50         to|from) output=sender;;
51         *) return;;
52     esac
53
54     # Only emit plain, lower case, unique addresses.
55     notmuch address --output=$output $prefix:"${cur}*" | \
56         sed 's/[^<]*<\([^>]*\)>/\1/' | tr "[:upper:]" "[:lower:]" | sort -u
57 }
58
59 _notmuch_search_terms()
60 {
61     local cur prev words cword split
62     # handle search prefixes and tags with colons and equal signs
63     _init_completion -n := || return
64
65     case "${cur}" in
66         tag:*)
67             COMPREPLY=( $(compgen -P "tag:" -W "`notmuch search --output=tags \*`" -- ${cur##tag:}) )
68             ;;
69         to:*)
70             COMPREPLY=( $(compgen -P "to:" -W "`_notmuch_email ${cur}`" -- ${cur##to:}) )
71             ;;
72         from:*)
73             COMPREPLY=( $(compgen -P "from:" -W "`_notmuch_email ${cur}`" -- ${cur##from:}) )
74             ;;
75         path:*)
76             local path=`notmuch config get database.path`
77             compopt -o nospace
78             COMPREPLY=( $(compgen -d "$path/${cur##path:}" | sed "s|^$path/||" ) )
79             ;;
80         folder:*)
81             local path=`notmuch config get database.path`
82             compopt -o nospace
83             COMPREPLY=( $(compgen -d "$path/${cur##folder:}" | \
84                 sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
85             ;;
86         *)
87             local search_terms="from: to: subject: attachment: mimetype: tag: id: thread: folder: path: date:"
88             compopt -o nospace
89             COMPREPLY=( $(compgen -W "${search_terms}" -- ${cur}) )
90             ;;
91     esac
92     # handle search prefixes and tags with colons
93     __ltrim_colon_completions "${cur}"
94 }
95
96 _notmuch_compact()
97 {
98     local cur prev words cword split
99     _init_completion -s || return
100
101     $split &&
102     case "${prev}" in
103         --backup)
104             _filedir -d
105             return
106             ;;
107     esac
108
109     ! $split &&
110     case "${cur}" in
111         -*)
112             local options="--backup= --quiet"
113             compopt -o nospace
114             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
115             ;;
116     esac
117 }
118
119 _notmuch_config()
120 {
121     local cur prev words cword split
122     _init_completion || return
123
124     case "${prev}" in
125         config)
126             COMPREPLY=( $(compgen -W "get set list" -- ${cur}) )
127             ;;
128         get|set)
129             COMPREPLY=( $(compgen -W "`notmuch config list | sed 's/=.*\$//'`" -- ${cur}) )
130             ;;
131         # these will also complete on config get, but we don't care
132         database.path)
133             _filedir -d
134             ;;
135         maildir.synchronize_flags)
136             COMPREPLY=( $(compgen -W "true false" -- ${cur}) )
137             ;;
138     esac
139 }
140
141 _notmuch_count()
142 {
143     local cur prev words cword split
144     _init_completion -s || return
145
146     $split &&
147     case "${prev}" in
148         --output)
149             COMPREPLY=( $( compgen -W "messages threads files" -- "${cur}" ) )
150             return
151             ;;
152         --exclude)
153             COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
154             return
155             ;;
156         --input)
157             _filedir
158             return
159             ;;
160     esac
161
162     ! $split &&
163     case "${cur}" in
164         -*)
165             local options="--output= --exclude= --batch --input="
166             compopt -o nospace
167             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
168             ;;
169         *)
170             _notmuch_search_terms
171             ;;
172     esac
173 }
174
175 _notmuch_dump()
176 {
177     local cur prev words cword split
178     _init_completion -s || return
179
180     $split &&
181     case "${prev}" in
182         --format)
183             COMPREPLY=( $( compgen -W "sup batch-tag" -- "${cur}" ) )
184             return
185             ;;
186         --output)
187             _filedir
188             return
189             ;;
190     esac
191
192     ! $split &&
193     case "${cur}" in
194         -*)
195             local options="--gzip --format= --output="
196             compopt -o nospace
197             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
198             ;;
199         *)
200             _notmuch_search_terms
201             ;;
202     esac
203 }
204
205 _notmuch_insert()
206 {
207     local cur prev words cword split
208     # handle tags with colons and equal signs
209     _init_completion -s -n := || return
210
211     $split &&
212     case "${prev}" in
213         --folder)
214             local path=`notmuch config get database.path`
215             compopt -o nospace
216             COMPREPLY=( $(compgen -d "$path/${cur}" | \
217                 sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
218             return
219             ;;
220     esac
221
222     ! $split &&
223     case "${cur}" in
224         --*)
225             local options="--create-folder --folder= --keep --no-hooks"
226             compopt -o nospace
227             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
228             return
229             ;;
230         +*)
231             COMPREPLY=( $(compgen -P "+" -W "`notmuch search --output=tags \*`" -- ${cur##+}) )
232             ;;
233         -*)
234             COMPREPLY=( $(compgen -P "-" -W "`notmuch search --output=tags \*`" -- ${cur##-}) )
235             ;;
236     esac
237     # handle tags with colons
238     __ltrim_colon_completions "${cur}"
239 }
240
241 _notmuch_new()
242 {
243     local cur prev words cword split
244     _init_completion || return
245
246     case "${cur}" in
247         -*)
248             local options="--no-hooks --quiet"
249             COMPREPLY=( $(compgen -W "${options}" -- ${cur}) )
250             ;;
251     esac
252 }
253
254 _notmuch_reply()
255 {
256     local cur prev words cword split
257     _init_completion -s || return
258
259     $split &&
260     case "${prev}" in
261         --format)
262             COMPREPLY=( $( compgen -W "default json sexp headers-only" -- "${cur}" ) )
263             return
264             ;;
265         --reply-to)
266             COMPREPLY=( $( compgen -W "all sender" -- "${cur}" ) )
267             return
268             ;;
269     esac
270
271     ! $split &&
272     case "${cur}" in
273         -*)
274             local options="--format= --format-version= --reply-to= --decrypt"
275             compopt -o nospace
276             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
277             ;;
278         *)
279             _notmuch_search_terms
280             ;;
281     esac
282 }
283
284 _notmuch_restore()
285 {
286     local cur prev words cword split
287     _init_completion -s || return
288
289     $split &&
290     case "${prev}" in
291         --format)
292             COMPREPLY=( $( compgen -W "sup batch-tag auto" -- "${cur}" ) )
293             return
294             ;;
295         --input)
296             _filedir
297             return
298             ;;
299     esac
300
301     ! $split &&
302     case "${cur}" in
303         -*)
304             local options="--format= --accumulate --input="
305             compopt -o nospace
306             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
307             ;;
308     esac
309 }
310
311 _notmuch_search()
312 {
313     local cur prev words cword split
314     _init_completion -s || return
315
316     $split &&
317     case "${prev}" in
318         --format)
319             COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
320             return
321             ;;
322         --output)
323             COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) )
324             return
325             ;;
326         --sort)
327             COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) )
328             return
329             ;;
330         --exclude)
331             COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
332             return
333             ;;
334     esac
335
336     ! $split &&
337     case "${cur}" in
338         -*)
339             local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate="
340             compopt -o nospace
341             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
342             ;;
343         *)
344             _notmuch_search_terms
345             ;;
346     esac
347 }
348
349 _notmuch_address()
350 {
351     local cur prev words cword split
352     _init_completion -s || return
353
354     $split &&
355     case "${prev}" in
356         --format)
357             COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
358             return
359             ;;
360         --output)
361             COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) )
362             return
363             ;;
364         --sort)
365             COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) )
366             return
367             ;;
368         --exclude)
369             COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
370             return
371             ;;
372         --deduplicate)
373             COMPREPLY=( $( compgen -W "no mailbox address" -- "${cur}" ) )
374             return
375             ;;
376     esac
377
378     ! $split &&
379     case "${cur}" in
380         -*)
381             local options="--format= --output= --sort= --exclude= --deduplicate="
382             compopt -o nospace
383             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
384             ;;
385         *)
386             _notmuch_search_terms
387             ;;
388     esac
389 }
390
391 _notmuch_show()
392 {
393     local cur prev words cword split
394     _init_completion -s || return
395
396     $split &&
397     case "${prev}" in
398         --entire-thread)
399             COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
400             return
401             ;;
402         --format)
403             COMPREPLY=( $( compgen -W "text json sexp mbox raw" -- "${cur}" ) )
404             return
405             ;;
406         --exclude|--body)
407             COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
408             return
409             ;;
410     esac
411
412     ! $split &&
413     case "${cur}" in
414         -*)
415             local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt --include-html"
416             compopt -o nospace
417             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
418             ;;
419         *)
420             _notmuch_search_terms
421             ;;
422     esac
423 }
424
425 _notmuch_tag()
426 {
427     local cur prev words cword split
428     # handle tags with colons and equal signs
429     _init_completion -s -n := || return
430
431     $split &&
432     case "${prev}" in
433         --input)
434             _filedir
435             return
436             ;;
437     esac
438
439     ! $split &&
440     case "${cur}" in
441         --*)
442             local options="--batch --input= --remove-all"
443             compopt -o nospace
444             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
445             return
446             ;;
447         +*)
448             COMPREPLY=( $(compgen -P "+" -W "`notmuch search --output=tags \*`" -- ${cur##+}) )
449             ;;
450         -*)
451             COMPREPLY=( $(compgen -P "-" -W "`notmuch search --output=tags \*`" -- ${cur##-}) )
452             ;;
453         *)
454             _notmuch_search_terms
455             return
456             ;;
457     esac
458     # handle tags with colons
459     __ltrim_colon_completions "${cur}"
460 }
461
462 _notmuch()
463 {
464     local _notmuch_commands="compact config count dump help insert new reply restore search address setup show tag"
465     local arg cur prev words cword split
466
467     # require bash-completion with _init_completion
468     type -t _init_completion >/dev/null 2>&1 || return
469
470     _init_completion || return
471
472     COMPREPLY=()
473
474     # subcommand
475     _get_first_arg
476
477     # complete --help option like the subcommand
478     if [ -z "${arg}" -a "${prev}" = "--help" ]; then
479         arg="help"
480     fi
481
482     if [ -z "${arg}" ]; then
483         # top level completion
484         local top_options="--help --version"
485         case "${cur}" in
486             -*) COMPREPLY=( $(compgen -W "${top_options}" -- ${cur}) ) ;;
487             *) COMPREPLY=( $(compgen -W "${_notmuch_commands}" -- ${cur}) ) ;;
488         esac
489     elif [ "${arg}" = "help" ]; then
490         # handle help command specially due to _notmuch_commands usage
491         local help_topics="$_notmuch_commands hooks search-terms"
492         COMPREPLY=( $(compgen -W "${help_topics}" -- ${cur}) )
493     else
494         # complete using _notmuch_subcommand if one exist
495         local completion_func="_notmuch_${arg//-/_}"
496         declare -f $completion_func >/dev/null && $completion_func
497     fi
498 } &&
499 complete -F _notmuch notmuch