diary at Telent Netowrks

[ updated for elisp syntax error, 13:01:52 GMT ]#

Tue, 14 Jan 2003 07:55:15 +0000

[ updated for elisp syntax error, 13:01:52 GMT ]

This is what I mean by a scriptable user interface...

So, it's not completely unheard of for me to write "Please find attached ..." in an email message, then forget to actually attach the file. Apparently I'm not the only person this happens to, either. SO, the other day I decided to sit down and sort it out.

I use Gnus for my email and news reading. Gnus, I happen to know, uses the default message composer in Emacs, which is known as Message. What I want is for something to run when I attempt to send mail, that warns me when I send a mail using the word "attach" if I haven't actually added an attachment.

  1. Enter The Dragon^Wscratch buffer

  2. Emacs has hooks everywhere. Probability is that there's a hook that runs just before a message is sent. We can find out with apropos:

    (apropos "message.+hook") C-j
      Variable: Hook run before sending messages.
      Plist: standard-value variable-documentation custom-options custom-type custom-requests

    Looks good to me

  3. Middle-clicking on Variable tells me I can customize this. Middle-clicking on that gets me the Customize buffer. There's nothing on the hook at the moment, but apparently I could add ispell-message if I wanted to. Putting the cursor on ispell-message and pressing C-h f (describe function) tells me

    Check the spelling of a mail message or news post.
    Don't check spelling of message headers except the Subject field.
    Don't check included messages.

    To abort spell checking of a message region and send the message anyway, use the `x' command. (Any subsequent regions will be checked.) The `X' command aborts the message send so that you can edit the buffer.

  4. Not exactly what I want to do, but close enough that I can be pretty sure that what I do want to do (check for the string "attach", check for an attachment, query whether to send if a but not b found, abort sending if user says no) is implementable.

  5. Following the link to "ispell" takes me directly to the function's source code. Now, let's play with it. Copy and paste the entire ispell-message function into scratch, change its name to, say, check-attachments-attached, and start hacking it about a bit. Here's what I ended up with

    (defun check-attachments-attached ()
        (goto-char (point-min))
        (let* (
               ;; Nil when message came from outside (eg calling emacs as editor)
               ;; Non-nil marker of end of headers.
                 (concat "^" (regexp-quote mail-header-separator) "$") nil t))
               (end-of-headers              ; Start of body.
                 (or internal-messagep
                     (re-search-forward "^$" nil t)
                (or (re-search-forward "^-- $" nil t) 
               (old-case-fold-search case-fold-search))
                (goto-char end-of-headers)
                (when (search-forward "attach" limit t)
                  (goto-char end-of-headers)
                  ;; the word 'attach' has been used, can we find an
                  ;; attachment?
                      (or (re-search-forward "^<#/" limit t)
                           "Found the word `attach' but no MIME attachment: send anyway?"
                         (error "Aborted send")))))
            (set-marker end-of-headers nil)))))

  6. and add it to the hook:
    (add-hook 'message-send-hook 'check-attachments-attached)

Points of note -

First off, we didn't need to restart emacs. In fact, we didn't even need to load any files. All of this was done on the fly. Second, we didn't at any point have to read an info manual, fiddle around in the filesystem, or search a web site to get to the documentation for anything. We had a little help from customize to tell us that ispell-message was a suitable function for this hook, but aside from that, all we needed to know was in function and variable docstrings. And when we went to look for the source code to ispell-message, there it was, one mouse-click away. No cvs trees to check out, no source packages to download and puzzle over the autobreak incantations for.

The function is a hack. That's the difference between scripting and programming. If I were doing it properly, then instead of looking for that funny <#/ string I'd read the appropriate bits of whatever MIME attachment stuff I'm using (that's right, I don't actually know) and find out what it does to detect the presence of attachments.

The whole exercise took under an hour. Although I consider myself a competent Lisp programmer, that's Common Lisp, not elisp. I touch emacs customization about once a year on average - I may have a head start over someone whose programming experience is entirely with, say, C or Perl, but I'm by no means a guru.

This is what I mean by a scriptable user interface. This is the opportunity that GNOME (as far as I can determine) missed. This is the opportunity that Mozilla, as far as I can see, missed. (This is the opportunity that presently-mythical-environment-based-on-McCLIM had damned well better not miss).

People believe they're being funny when they compare Emacs to an operating system. Frankly, they're nearer the truth than they think: it may not have a pre-emptive scheduler, but it's still a better desktop environment than anything else I've used.

:; cu -l /dev/ttyUSB0
+CLIP: 0,1



+CLIP: "01865423320",129


+CLIP: "01865423320",129

Next up, maybe, mobile phone book/BBDB integration, with caller id on incoming calls.