Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Completing e-mail addresses

Below is a start at a function for completing e-mail addresses.

_email-xxxx() functions are used as plugins for different sources of
e-mail address. I've included functions for mail,mutt,mush,MH,local users
and ldap (though ldap will need a lot more work).

It will complete aliases/nicknames if given a -n option specifying the
plugin. -s sep will allow separated lists of addresses and this copes with
the separator being quoted in an e-mail address.

I'm not too sure about how it should determine the plugins to use by
default.  It'd be nice if I could use ${(k)functions[(I)_email-*]} but
with the ldap plugin, that isn't ideal. If it was possible to specify a
default for the tag-order style, the plugins style would not be necessary.

The hard bit which I have yet to attempt is to turn addresses which
include comments (display names etc) into something of a simple user@host
form. This would be necessary for completing something like a mailto:
url properly and I suspect that some people would want to do it for
all addresses (configured with a style). Can this be done using Bart's
RFC822 gramma in 14050 by using back references? Or should I just try
something fairly simple. Thanks for that grammar by the way Bart, it was
interesting and helped with the quoting stuff. zregexparse seems to be
useful only when completing separate words so I doubt it could be used
with that.

So does anyone have any comments or thoughts on this?


# options:
# -n plugin - can complete nicknames from specified plugin
# -s sep    - complete a list of addresses separated by specified character
# -..       - e-mail address must be of form user@host
# plugins are written as separate functions with _email- prefixing their names
# they should either do their own completion or return the addresses in
# the ali array in the form 'alias:address' and return 300

# plugins
_email-mail() {
  ali=( ${${${(M)${(f)"$(<$files[$plugin])"}:#alias*}##alias[[:blank:]]##}/[[:blank:]]##/:} )
  return 300
_email-mutt() { _email-mail }
_email-mush() { _email-mail }

_email-MH() {
  ali=( ${${(f)"$(_call_program aliases ali)"}/: /:} )
  return 300

_email-ldap() {
  local -a ali res
  local dn cn mail

  res=( ${(f)"$(ldapsearch -x -LLL "(sn=$PREFIX*$SUFFIX)" cn mail)"} )
  for dn cn mail in "${res[@]}"; do
    ali+=( "\"${cn#*: }\" <${mail#*: }>" )
  _wanted addresses expl 'matching names' compadd -U "$@" -a ali

_email-local() {
  local suf

  if compset -P '*@'; then
    _hosts "$@"
    compset -S '@*' || suf=( -qS @ )
    _users $suf "$@"

local nm=$compstate[nmatches]
local -a plugins ali list
local -A opts files
local plugin rcfile expl ret

local __qtext='[^"\\]'
local __qpair='\\?'
local __beginq='"'
local __endq='(|[^\\])"'
local __addresses="($__qtext|$__beginq($__qtext|$__qpair)#$__endq)##"

zparseopts -D -E -A opts n: s:

if [[ -n $opts[-s] ]]; then
  # remove up to the last unquoted separator
  if [[ ${(Q)PREFIX} = (#b)($~__addresses$opts[-s])* ]]; then
    compset -P $(( ${#${(s:,:)${:-x${match[1]}x}}} - 1 )) "*${opts[-s]}"
  # for the suffix, I'm too lazy to work out how to preserve quoted separators
  compset -S ',*' || set -- -qS "$opts[-s]" "$@"

files=( mutt ~/.muttrc mush ~/.mushrc mail ${MAILRC:-.mailrc} )
zstyle -a ":completion:${curcontext}:email-addresses" plugins plugins ||
  plugins=( local $opts[-n] $files(Ne:'REPLY=( ${(k)files[(r)$REPLY]} ):') )

_tags email-$plugins
while _tags; do
  for plugin in $plugins; do
    if _requested email-$plugin; then

      if (( ! $+functions[_email-$plugin] )); then
	_message "$plugin: unknown plugin not found"

      _email-$plugin "$@" && ret=0
      if (( $? == 300 )); then

	if [[ $opts[-n] = $plugin ]]; then
	  zformat -a list ' -- ' "${ali[@]}"
	  _wanted mail-aliases expl 'alias' compadd "$@" \
	      -d list - ${ali%%:*} && ret=0
	  # remove lines not containing `@' as they probably aren't addresses
	  _wanted addresses expl 'address' compadd "$@" - \
	      "${(@)${(M@)ali:#*@*}#*:}" && ret=0
  (( ret )) || return 0

[[ nm -ne compstate[nmatches] ]]

This e-mail and any attachment is for authorised use by the intended recipient(s) only.  It may contain proprietary material, confidential information and/or be subject to legal privilege.  It should not be copied, disclosed to, retained or used by, any other party.  If you are not an intended recipient then please promptly delete this e-mail and any attachment and all copies and inform the sender.  Thank you.

Messages sorted by: Reverse Date, Date, Thread, Author