[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

completion (was Re: [Nmh-workers] What is MH ?)

From: Oliver Kiddle
Subject: completion (was Re: [Nmh-workers] What is MH ?)
Date: Tue, 10 Jan 2006 12:32:59 +0100

Nathan Bailey wrote:
> PS: Another couple of things I'd like to see on the MH wiki would be:
> i) How do you do 'To/Cc/Dcc/Bcc' field completion? (i.e. in the same way 
> the GUI clients do, or web browsers do for URLs).  Special bonus if your 
> solution supports an LDAP directory (i.e. not just aliases in lib/alias :-)

I use a Zsh script named zprompter to replace prompter. Zsh already has
powerful completion capabilities so it handles the job well. Even stuff
like LDAP is straightforward.

I've attached the script but you may need to tweak it for your own
purposes. You'll need a relatively recent version of zsh installed. I
use the script to quote repl replies (using mhshow -part 1) which works
most of the time so you need to remove the message quoting from
replcomps or modify the script. I use 
comp: -editor zprompter
to enable it.

Pressing tab does completion of aliases (depending on the header). Other
keybindings should be configurable in the bindkey settings. In addition
to normal editing operations are the following:
  Ctrl-F   - add a new header field
  Ctrl-J   - attach a file
  Ctrl-K   - LDAP lookup
  Ctrl-X A - expand alias inline

For LDAP, you may need to change ldapsearch's options and create a
.ldaprc to point to the LDAP server. If you've got better ideas for the
search than the current surname based one then let me know.

If you want completion to work for the From header, you'll need to add
aliases for your own addresses.

There are various problems with it and areas for improvement but I find
it useful.


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.

trap 'exit 1' INT
setopt autolist extendedglob completeinword

# provide function for getent if it doesn't exist
which getent >/dev/null || getent() {
  cat /etc/passwd

_complete() {
  [[ "$compstate[insert]" = tab* ]] &&
      compstate[insert]="${compstate[insert]//tab /}"

  case $hdr in
    From|Reply-To) _comp_from;;
    Filename) _comp_files;;
    Header) _comp_headers;;
    *) _comp_aliases;;

_comp_from() {
  compadd -X '%Balias%b' -S '' \
      ${${(f)"$(ali -user $(mhparam Alternate-Mailboxes))"}:address@hidden

_comp_aliases() {
  local nick list suf

  compset -P '*,'
  compset -S ',*' || suf=( -qS, )
  nick=( ${(f)"$(ali)"} )
  zformat -a list ' --' "address@hidden"
  nick=( ${nick%%:*} )
  compadd -Q -X '%Balias%b' -d list "address@hidden" -a nick

_comp_lookup() {
  local -a res all suf
  local dn cn
  local -l mail

  compset -P '*,'
  compset -S ',*' || suf=( -qS, )
  res=( ${(f)"$(ldapsearch -x -LLL "(sn=$PREFIX*$SUFFIX)" cn mail)"} )
  for dn cn mail in "address@hidden"; do
    all+=( "\"${cn#*: }\" <${mail#*: }>" )
  compadd -X '%Bmatching names%b' $suf -i "$IPREFIX" -I "$ISUFFIX" -UQ -a all

_comp_files() {
  local glob suf='-S/'
  compset -S '/*' && suf=( -S '' )

  if [[ $PREFIX = \~[^/]# ]]; then
    compset -P \~
    compadd -X '%Buser%b' $suf ${${(f)"$(getent passwd)"}%%:*}
    compset -P '*/'
    [[ $PREFIX = .* ]] && glob=D
    compadd -X '%Bfile%b' -f -W ${~IPREFIX:-$PWD/} ${~IPREFIX:-./}*(${glob}N:t)

_comp_headers() {
  compadd -X '%Badditional header%b' -M 'M:{A-Za-z}={a-zA-Z}' -S '' \
      Bcc cc Dcc Mail-Followup-To Reply-To

_expand_alias() {
  local suf

  compset -P '*,'
  compset -S ',*' || suf=( -qS, )
  [[ -n $PREFIX$SUFFIX ]] && compadd $suf -i "$IPREFIX" -I "$ISUFFIX" -UQ \
      "$(ali $PREFIX$SUFFIX)"

attachment() {
  local hdr=Filename
  read-from-minibuffer 'Filename: '
  attachments+=( ${(Q)${(z)REPLY}} )

new-header() {
  local hdr=Header
  read-from-minibuffer 'Header: '
  read-from-minibuffer "$hdr: "

zmodload zsh/complist
zle -C complete-word complete-word _complete
zle -C complete-lookup menu-complete _comp_lookup
zle -C expand-alias menu-complete _expand_alias
zle -N attachment
zle -N new-header
autoload -U read-from-minibuffer

bindkey -v
bindkey '^A'      beginning-of-line
bindkey '^E'      end-of-line
bindkey '^F'      new-header
bindkey '^I'      complete-word
bindkey '^J'      attachment
bindkey '^K'      complete-lookup
bindkey '^Z'      undo
bindkey '^Xa'     expand-alias
bindkey '^[[P'    delete-char
bindkey '\e[3~'   delete-char
bindkey '^[[139q' overwrite-mode
bindkey '^[[2~'   overwrite-mode
bindkey '^[^[[D'  backward-kill-word
bindkey '^[[160q' backward-kill-word
bindkey '^[[3D'   backward-kill-word
bindkey '^[^[[C'  kill-word
bindkey '^[[3C'   kill-word
bindkey '^[[169q' kill-word
bindkey '^[OD'    backward-word
bindkey '^[[159q' backward-word
bindkey '^[[1;5D' backward-word
bindkey '^[OC'    forward-word
bindkey '^[[168q' forward-word
bindkey '^[[1;5C' forward-word

typeset -A value from

edit=( Resent-To To cc From Subject )
aliexp=( Resent-To Reply-To Bcc Dcc To cc From )

exec 4<$1
while IFS= read -r line && [[ $line != "--------" ]]; do
  if [[ "$line" = [[:blank:]]* ]]; then
    headers+=( $hdr )
done <&4

# if we have a cc header but no To, swap it
if (( ${headers[(I)cc]} && ! ${headers[(I)To]} )); then
  unset 'value[cc]'

for hdr in ${(M)headers:#(${(j.|.)~edit})}; do
  vared -c -p "$hdr: " "value[$hdr]"

# expand aliases in relevant headers
for exp in ${(Mk)value:#(${(j.|.)~aliexp})}; do
  for r in ${(s:,:)value[$exp]}; do
    if [[ $r = *[\"@\\]* ]]; then
      new+=( "${r##[[:blank:]]#}" )
      new+=( "$(ali ${r##[[:blank:]]#})" )
  value[$exp]="${(j:, :)new}"

exec 3>$1

for h in ${(k)value}; do
  print -r "$h: $value[$h]" >&3

# exit now for the dist command
[[ $mhdist = 1 ]] && exit

echo "--------" >&3
print -r "$quoting" >&3
if [[ -n $editalt ]]; then
  mhshow -part 1 -form mhl.null -file $editalt | sed -e '1 d' -e 's/^/> /' >&3

# get possible mime types
types=( ${${(f)"$(cat /etc/mime.types(N) ~/.mime.types(N))"}:#\#*} )

for file in $attachments; do
  [[ -z $type && $(file $~file) = *\ text ]] && type="text/plain"
  print "#${type:-application/octet-stream}; name="'"'${file:t}'"' $~file >&3

NEXINIT='set wrapmargin=8' nvi "+$(($#value + 2))" $1

reply via email to

[Prev in Thread] Current Thread [Next in Thread]