Re: [PATCH] cli: add a tool for starting new message in the emacs ui

Subject: Re: [PATCH] cli: add a tool for starting new message in the emacs ui

Date: Wed, 19 Mar 2014 23:54:15 +0200

To: Tomi Ollila, notmuch@notmuchmail.org

Cc:

From: Jani Nikula


On Wed, 19 Mar 2014, Tomi Ollila <tomi.ollila@iki.fi> wrote:
> On Wed, Mar 19 2014, Jani Nikula <jani@nikula.org> wrote:
>
>> Add a tool to start composing an email in the Notmuch Emacs UI with
>> the specified subject, recipients, and message body.
>>
>> ---
>>
>> I need something like this to script some mails, particularly with the
>> mutt compatible options, but I also think notmuch must have long
>> options. I then got a little carried away with figuring out how to
>> support both. I think it turned out pretty neat, except due to some
>> subtlety it only works with bash.
>>
>> I didn't integrate this in the man build or install or anything,
>> because I wanted to get feedback first on whether we want to have this
>> at all. Or if it should live in contrib or something.
>>
>> BR,
>> Jani.
>> ---
>
> Quick glance to the code and some thoughts.

Thanks!

>
>>  doc/man1/notmuch-emacs-mua.rst |  50 ++++++++++++++++++
>>  notmuch-emacs-mua              | 113 +++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 163 insertions(+)
>>  create mode 100644 doc/man1/notmuch-emacs-mua.rst
>>  create mode 100755 notmuch-emacs-mua
>>
>> diff --git a/doc/man1/notmuch-emacs-mua.rst b/doc/man1/notmuch-emacs-mua.rst
>> new file mode 100644
>> index 000000000000..6e63818492fb
>> --- /dev/null
>> +++ b/doc/man1/notmuch-emacs-mua.rst
>> @@ -0,0 +1,50 @@
>> +=================
>> +notmuch-emacs-mua
>> +=================
>> +
>> +SYNOPSIS
>> +========
>> +
>> +**notmuch-emacs-mua** [options ...] [<to-address> ...]
>> +
>> +DESCRIPTION
>> +===========
>> +
>> +Start composing an email in the Notmuch Emacs UI with the specified
>> +subject, recipients, and message body.
>> +
>> +For **notmuch-emacs-mua** to work, you need **emacsclient** and an
>> +already running Emacs with a server.
>> +
>> +Supported options for **notmuch-emacs-mua** include
>> +
>> +    ``-h, --help``
>> +        Display help.
>> +
>> +    ``-s, --subject=``\ <subject>
>> +        Specify the subject of the message.
>> +
>> +    ``--to=``\ <to-address>
>> +        Specify a recipient (To).
>> +
>> +    ``-c, --cc=``\ <cc-address>
>> +        Specify a carbon-copy (Cc) recipient.
>> +
>> +    ``-b, --bcc=``\ <bcc-address>
>> +        Specify a blind-carbon-copy (Bcc) recipient.
>> +
>> +    ``-i, --body=``\ <file>
>> +        Specify a file to include into the body of the message.
>> +
>> +    ``--print``
>> +        Output the resulting elisp to stdout instead of evaluating it.
>> +
>> +The supported positional parameters and short options are a compatible
>> +subset of the **mutt** MUA command-line options.
>> +
>> +Options may be specified multiple times.
>> +
>> +SEE ALSO
>> +========
>> +
>> +**notmuch(1)**, **emacsclient(1)**, **mutt(1)**
>
> It would be convenient to the user to have the manual embedded in
> the script in case no args are given or so (or that is convenient
> to me ;) and no namual page at all...

If this is to become part of notmuch, I think a separate man page is in
order. Otherwise, agreed (and even had that in an earlier version).

>
>> diff --git a/notmuch-emacs-mua b/notmuch-emacs-mua
>> new file mode 100755
>> index 000000000000..a482fe1a8eca
>> --- /dev/null
>> +++ b/notmuch-emacs-mua
>> @@ -0,0 +1,113 @@
>> +#!/bin/bash
>
> #!/usr/bin/env bash

Agreed.

>
>> +#
>> +# notmuch-emacs-mua - start composing a mail on the command line
>> +#
>> +# Copyright © 2014 Jani Nikula
>> +#
>> +# This program is free software: you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation, either version 3 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program.  If not, see http://www.gnu.org/licenses/ .
>> +#
>> +# Authors: Jani Nikula <jani@nikula.org>
>> +#
>> +
>> +set -e
>
> set -eu would be nice IMO...
>
> ... then initialize all vars to empty strings, like PRINT_ONLY=
> ... and in cases not possible syntax ${1-} can be used.

Agreed.

>
>> +
>> +# The crux of it all: construct an elisp progn and eval it.
>> +ELISP="(progn (notmuch-mua-new-mail)"
>
> ELISP="(progn (require 'notmuch) (notmuch-mua-new-mail)"

Right.

>
>> +
>> +while getopts :s:c:b:i:h opt; do
>> +    # Handle errors and long options.
>> +    case "${opt}" in
>> +	:)
>> +	    echo "$0: short option -${OPTARG} requires an argument." >&2
>> +	    exit 1
>> +	    ;;
>> +	\?)
>> +	    opt=$1
>> +	    if [ "${OPTARG}" != "-" ]; then
>> +		echo "$0: unknown short option -${OPTARG}." >&2
>> +		exit 1
>> +	    fi
>> +
>> +	    case "${opt}" in
>> +		# Long options with arguments.
>> +		--subject=*|--to=*|--cc=*|--bcc=*|--body=*)
>> +		    OPTARG=${opt#--*=}
>> +		    opt=${opt%%=*}
>> +		    ;;
>> +		# Long options without arguments.
>> +		--help|--print)
>> +		    ;;
>> +		*)
>> +		    echo "$0: unknown long option ${opt}, or argument mismatch." >&2
>> +		    exit 1
>> +		    ;;
>> +	    esac
>> +	    # getopts does not do this for what it considers errors.
>> +	    OPTIND=$((OPTIND + 1))
>> +	    ;;
>> +    esac
>> +
>> +    case "${opt}" in
>> +	--help|h)
>> +	    exec man notmuch-search
>> +	    ;;
>> +	--subject|s)
>> +	    ELISP="${ELISP} (message-goto-subject) (insert \"${OPTARG}\")"
>> +	    ;;
>> +	--to)
>> +	    ELISP="${ELISP} (message-goto-to) (insert \"${OPTARG}, \")"
>> +	    ;;
>> +	--cc|c)
>> +	    ELISP="${ELISP} (message-goto-cc) (insert \"${OPTARG}, \")"
>> +	    ;;
>> +	--bcc|b)
>> +	    ELISP="${ELISP} (message-goto-bcc) (insert \"${OPTARG}, \")"
>> +	    ;;
>> +	--body|i)
>> +	    ELISP="${ELISP} (message-goto-body) (cd \"${PWD}\") (insert-file \"${OPTARG}\")"
>> +	    ;;
>> +	--print)
>> +	    PRINT_ONLY=1
>> +	    ;;
>> +	*)
>> +	    # We should never end up here.
>> +	    echo "$0: internal error (option ${opt})." >&2
>> +	    exit 1
>> +	    ;;
>> +    esac
>> +
>> +    shift $((OPTIND - 1))
>> +    OPTIND=1
>> +done
>> +
>> +# Positional parameters.
>> +while [ $# -gt 0 ]; do
>> +    ELISP="${ELISP} (message-goto-to) (insert \"${1}, \")"
>> +    shift
>> +done
>
> for arg; do

for arg in $@; ?

>    ELISP="${ELISP} (message-goto-to) (insert \"${arg}, \")"
> done
>
> I tried to address Austin's comment on IRC with 
> printf -v qarg %q "$arg"  -- that has problem if there is whitespace
> in arg. maybe  printf -v qarg '%q ' "$arg" -- then there is always one
> trailing ws (and spaces are always prefixed w/ \ -- (insert "foo\ bar")
> just makes the ' ' disappear (which is not good...).
>
> maybe arg=${arg//\\/\\\\}; arg=${arg//"/\\"}; 
>
> ${parameter/pattern/string} -- Pattern substition in bash manual.

I'm inclined to go with "don't do that then" ;)

>
>
>> +# End progn.
>> +ELISP="${ELISP})"
>> +
>> +if [ -n "$PRINT_ONLY" ]; then
>> +    echo ${ELISP}
>> +    exit 0
>> +fi
>> +
>> +# Evaluate the progn.
>> +emacsclient --eval "${ELISP}" &>/dev/null
>
> emacsclient --eval "${ELISP}" >/dev/null 2>&1
>
> (why do you want to redirect stdout & stderr to devnull ?)

Okay, maybe it could be just stdout (which will be the eval result, in
this case nil).

>
>> +if [ $? -ne 0 ]; then
>> +    echo "$0: emacsclient failed" >&2
>> +    exit 1
>> +fi
>> -- 
>> 1.9.0

Thread: