-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathneomutt.bash
More file actions
309 lines (276 loc) · 10.4 KB
/
neomutt.bash
File metadata and controls
309 lines (276 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# neomutt(1) completion -*- shell-script -*-
#
# Bash completion for NeoMutt - Command Line Email Client
# https://neomutt.org
#
# Copyright (C) 2026 Richard Russon <rich@flatcap.org>
# Complete email addresses from aliases, query command, and system users
# @param $1 (cur) Current word to complete
_comp_cmd_neomutt__addresses()
{
_comp_cmd_neomutt__aliases "$1"
_comp_cmd_neomutt__query "$1"
}
# Find neomutt configuration file to use
# First checks command line for -F/--config options, then searches standard locations
# @var[out] REPLY neomuttrc filename (empty if not found)
_comp_cmd_neomutt__get_neomuttrc()
{
REPLY=
# Search command line for '-F neomuttrc', '-Fneomuttrc', or '--config neomuttrc'
set -- "${words[@]}"
while (($# > 0)); do
if [[ $1 == -F* ]]; then
# Handle -Ffile or -F file
if ((${#1} > 2)); then
_comp_dequote "${1:2}"
else
shift
[[ ${1-} ]] && _comp_dequote "$1"
fi
break
elif [[ $1 == --config ]]; then
# Handle --config file
shift
[[ ${1-} ]] && _comp_dequote "$1"
break
fi
shift
done
# If not specified on command line, search standard NeoMutt config locations
if [[ ! ${REPLY-} ]]; then
local -a config_locations=(
~/.config/neomutt/neomuttrc
~/.config/neomutt/muttrc
~/.config/mutt/neomuttrc
~/.config/mutt/muttrc
~/.neomutt/neomuttrc
~/.neomutt/muttrc
~/.mutt/neomuttrc
~/.mutt/muttrc
~/.neomuttrc
~/.muttrc
)
local loc
for loc in "${config_locations[@]}"; do
if [[ -f $loc ]]; then
REPLY=$loc
break
fi
done
fi
}
# Recursively build list of sourced config files
# @param $1... Config file to process
# @var[out] REPLY List of config files
# @return 0 if any conffiles are generated, 1 if none is generated.
_comp_cmd_neomutt__get_conffiles()
{
local -a conffiles=()
local -A visited=()
local file
for file; do
_comp_dequote "$file"
_comp_cmd_neomutt__get_conffiles__visit "${REPLY-}"
done
((${#conffiles[@]})) || return 1
REPLY=("${conffiles[@]}")
}
# Recursion helper for _comp_cmd_neomutt__get_conffiles
# Visits a config file and recursively follows any 'source' directives
# @param $1 Config file to visit
# @var[ref] conffiles List of config files found so far
# @var[ref] visited Dictionary of config files already visited (prevents cycles)
_comp_cmd_neomutt__get_conffiles__visit()
{
# Skip if file doesn't exist or already visited
[[ -f $1 && ${visited[$1]-} != set ]] || return 0
visited[$1]='set'
conffiles+=("$1")
# Extract sourced config files from 'source <file>' directives
local -a newconffiles
_comp_split newconffiles "$(command sed -n 's|^source[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*$|\1|p' "$1")" ||
return 0
# Get the directory of the current config file for resolving relative paths
local parent_dir
parent_dir=$(dirname "$1")
# Recursively visit each sourced file
local file REPLY
for file in "${newconffiles[@]}"; do
# Dequote the filename (removes quotes from source "file")
_comp_dequote "$file"
file=$REPLY
# Expand tilde if present
_comp_expand_tilde "$file"
file=$REPLY
# If path is relative (doesn't start with / or ~), resolve relative to parent config
if [[ $file != /* && $file != ~* ]]; then
file="$parent_dir/$file"
fi
_comp_cmd_neomutt__get_conffiles__visit "$file"
done
}
# Complete NeoMutt aliases from config file(s)
# Extracts alias names from 'alias <name> ...' directives
# @param $1 (cur) Current word to complete
_comp_cmd_neomutt__aliases()
{
local cur=$1 neomuttrc REPLY
local -a conffiles
_comp_cmd_neomutt__get_neomuttrc
neomuttrc=$REPLY
[[ ! $neomuttrc ]] && return
# Get all config files (main + sourced)
local REPLY
_comp_cmd_neomutt__get_conffiles "$neomuttrc" || return 0
conffiles=("${REPLY[@]}")
# Extract alias names from 'alias <name> ...' directives
_comp_compgen -a split -- "$(command sed -n 's|^alias[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*$|\1|p' \
"${conffiles[@]}")"
}
# Complete addresses using NeoMutt's query_command
# Executes the configured query command and parses email addresses from output
# @param $1 (cur) Current word to complete
_comp_cmd_neomutt__query()
{
local cur=$1
[[ $cur ]] || return 0
# Query NeoMutt for query_command setting and substitute %s with current word
local query_cmd
query_cmd="$(neomutt -Q query_command 2>/dev/null |
command sed -e 's|^set query_command = "\(.*\)"$|\1|' -e 's|%s|'"$cur"'|')"
if [[ $query_cmd ]]; then
local REPLY
_comp_expand_tilde "$query_cmd"
query_cmd=$REPLY
# Execute query command and extract email addresses
# Expected format: first line is header, subsequent lines have email as first field
_comp_compgen -a split -- "$($query_cmd |
command sed -n '2,$s|^\([^[:space:]]\{1,\}\).*|\1|p')"
fi
}
# Complete NeoMutt config option names for -Q/--query
# Extracts option names from 'neomutt -D' output
# @param $1 (cur) Current word to complete
_comp_cmd_neomutt__config_options()
{
local cur=$1
# Get all config options from NeoMutt dump
# Format: 'set option_name = value' or "set option_name = 'value'"
# Extract just the option_name part
_comp_compgen -a split -- "$(neomutt -n -F /dev/null -D 2>/dev/null |
command sed -n 's|^set \([^ ]\{1,\}\) = .*$|\1|p')"
}
# Complete file/directory paths with NeoMutt shortcuts
# Supports: =file (relative to $folder), +file (alias for =), !file (shortcut for $spool_file)
# @param $1 (cur) Current word to complete
_comp_cmd_neomutt__filedir()
{
local cur=$1 folder neomuttrc spool_file REPLY
_comp_cmd_neomutt__get_neomuttrc
neomuttrc=$REPLY
if [[ $cur == [=+]* ]]; then
# Handle = or + shortcuts (folder shortcut)
folder="$(neomutt -F "$neomuttrc" -Q folder 2>/dev/null |
command sed -e 's|^set folder = "\(.*\)"$|\1|')"
[[ $folder ]] || folder=~/Mail
# Complete files in $folder, then strip folder prefix from results
compopt -o filenames
_comp_compgen -c "$folder/${cur:1}" -- -f
COMPREPLY=("${COMPREPLY[@]#"$folder"/}")
return
elif [[ $cur == !* ]]; then
# Handle ! shortcut (spool_file shortcut)
spool_file="$(neomutt -F "$neomuttrc" -Q spool_file 2>/dev/null |
command sed -e 's|^set spool_file = "\(.*\)"$|\1|')"
if [[ $spool_file ]]; then
_comp_dequote "\"$spool_file\"" && spool_file=$REPLY
cur=$spool_file${cur:1}
fi
fi
# Default file/directory completion
_comp_compgen -c "$cur" filedir
}
# Main completion function for neomutt
_comp_cmd_neomutt()
{
local cur prev words cword
_comp_initialize -n =+! -- "$@" || return
: "$cword" # Used by _comp_initialize
local short_opts='-A -a -b -C -c -D -d -E -e -F -f -G -g -H -h -i -l -m -n -O -p -Q -R -S -s -v -y -Z -z'
local long_opts='--alias --attach --bcc --browser --cc --check-any-mail
--check-new-mail --command --config --crypto --debug-file --debug-level
--draft --dump-changed-config --dump-config --edit-message --folder
--help --hide-sensitive --include --license --mbox-type --nntp-browser
--nntp-server --no-system-config --postponed --query --read-only
--subject --version --with-docs'
# Complete based on current word or previous option
case $cur in
--*)
# Complete long options
_comp_compgen -- -W "$long_opts"
return
;;
-*)
# Complete short or long options
_comp_compgen -- -W "$short_opts $long_opts"
return
;;
*)
# Complete based on previous option
case $prev in
# Options that take file/directory arguments
-[aFfHil] | --attach | --config | --debug-file | --draft | --folder | --include)
_comp_cmd_neomutt__filedir "$cur"
return
;;
# Options that take alias arguments
-A | --alias)
_comp_cmd_neomutt__aliases "$cur"
return
;;
# Options that take email address arguments
-[bc] | --bcc | --cc)
_comp_cmd_neomutt__addresses "$cur"
return
;;
# Help mode options
-h | --help)
_comp_compgen -- -W 'shared help info send tui all'
return
;;
# Debug level options (0-5)
-d | --debug-level)
_comp_compgen -- -W '0 1 2 3 4 5'
return
;;
# Mailbox type options
-m | --mbox-type)
_comp_compgen -- -W 'maildir mbox mh mmdf'
return
;;
# Config query options
-Q | --query)
_comp_cmd_neomutt__config_options "$cur"
return
;;
# Options that take arbitrary strings (no completion possible)
-[egs] | --command | --nntp-server | --subject)
return
;;
# Options with no arguments - fall through to default
-[CDEGnOpRSvyZz] | --browser | --check-any-mail | --check-new-mail | \
--crypto | --dump-changed-config | --dump-config | --edit-message | \
--hide-sensitive | --license | --nntp-browser | --no-system-config | \
--postponed | --read-only | --version | --with-docs)
# Fall through to address completion
;;
esac
# Default: complete email addresses
_comp_cmd_neomutt__addresses "$cur"
return
;;
esac
} &&
complete -F _comp_cmd_neomutt -o default neomutt
# ex: filetype=sh