On Mon, Sep 05, 2022 at 08:36:54AM +1000, Cameron Simpson wrote:
But not space-stuffing, right?
I just reread https://www.rfc-editor.org/rfc/rfc3676#section-4.4 to 
refresh my brain. Yeah, I don't think I'd want that when writing a message.
Which I guess is why Mutt space-stuffs the format=flowed that it gets back from the editor.
Aye. I avoid lines commencing with a ">" just because they look quoted 
to my eye anyway, so that aside "live" space stuffing in authoring is 
something I'd find distracting.
Agreed, I wouldn't want space-stuffing maintained on the fly, while 
editing. It could be done at the end, when handing the message back to Mutt.
It seems a little conceptually cleaner to have the editor do the whole 
job, rather than divide it between the editor and Mutt. But another 
complication is that you can edit a message more than once...
I've just written something for Emacs, to use Emacs to compose 
format=flowed for Mutt. Well, I wrote two things, that do it two 
different ways.
The first one does it like vim, maintaining format=flowed on the fly as 
you type, by adding things to the existing "fill" functions (re-wrap). 
I have it working -- I've used it to post a couple things here -- but I 
had to put fingers into existing Emacs mechanisms (by adding "advice" 
to functions), which I'm leery of, and I don't think it can be done in 
a buffer-local way.
The second one works differently. It's a "major mode"; it has commands 
to convert both ways between format=flowed and one-line paragraphs, 
while you edit. The idea is to type with each paragraph being a single 
long line, and then "encode" that as format=flowed when you're done. 
That doesn't mess with existing mechanisms, and doesn't take much code. 
Emacs has something called "visual line mode" to make those long lines 
not ugly. I just got it working...I've tested it, but not 
extensively...I'm using it for this message...
That second one is attached, for anyone who's interested (Cameron, I 
suppose you probably don't use Emacs).
Set it up like this in .muttrc:

set text_flowed = yes           #compose text/plain format=flowed
set editor = 'emacs --load=$HOME/elisp/mutt-flowed-text-mode.el %s 
--funcall=mutt-flowed-text-mode'
set edit_headers = no           #don't want to flow the header section

Fire up Mutt, compose a message, and type C-h m to get help on the Emacs mode.
(defvar mutt-flowed-text-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map text-mode-map)
    (define-key map "\C-cd" 'flowed-text-decode-region)
    (define-key map "\C-ce" 'flowed-text-encode-region)
    (define-key map "\C-cD" 'flowed-text-decode-buffer)
    (define-key map "\C-cE" 'flowed-text-encode-buffer)
    (define-key map "\C-c\C-c" 'flowed-text-done)
    map)
  "Keymap for Mutt flowed text mode.")

(define-derived-mode mutt-flowed-text-mode text-mode "Mutt flowed text"
  "Major mode to compose text/plain format=flowed for mail reader Mutt.

Type and edit text with each paragraph being a single long line.
When you're done, use a mode command to encode it as flowed text;
that will insert soft line breaks.  You can decode it (go back to
one-line paragraphs) and encode it any time, but make sure the
message body is encoded when you exit Emacs and hand the message
back to Mutt.

You could use visual-line-mode to display those long lines
nicely.  Also, you might want to make trailing whitespace visible
by setting the variable show-trailing-whitespace, or by using
whitespace-mode.

This mode sets the buffer-local variable fill-column to a very
large number, because Emacs text fill would damage both
single-line paragraphs and text encoded as format=flowed.

Don't encode/decode the message header section; that could
possibly break things.  It's best to tell Mutt

    set edit_headers = no

to avoid that possibility.

This mode does not space-stuff the message, because Mutt does that.
For that reason, it may not be usable with other mail readers."

  :syntax-table=nil :abbrev-table=nil
  (setq-local delsp nil)
  (setq fill-column most-positive-fixnum))

(defun flowed-text-quote-level ()
  (save-excursion
    (forward-line 0)
    (looking-at ">*")
    (length (match-string-no-properties 0))))

(defun flowed-text-decode-region (begin end)
  (interactive "r")
  (save-excursion
    (let (quote-level endm)
      (setq endm (set-marker (make-marker) end))
      (set-marker-insertion-type endm t)
      (goto-char begin)
      (while (< (point) endm)
        (setq quote-level (flowed-text-quote-level))
        (if (looking-at-p ".* \n")
            (progn
              (forward-line)
              (if (and (< (point) endm)
                       (equal (flowed-text-quote-level) quote-level))
                  (progn
                    (delete-char (if delsp -2 -1))
                    (delete-char quote-level))
                (progn                  ;else: invalid space, remove it
                  (backward-char 1)
                  (delete-char -1)
                  (forward-char))))
          (forward-line))))))

(defun flowed-text-encode-region (begin end)
  (interactive "r")
  (save-excursion
    (let (endm bl el quote-level)
      (setq endm (set-marker (make-marker) end))
      (set-marker-insertion-type endm t)
      (goto-char begin)
      (while (< (point) endm)
        ;;line-end-position stays within a field, forward-line doesn't
        (setq bl (point))               ;beginning of line
        (setq el (line-end-position))   ;end of line
        (setq quote-level (flowed-text-quote-level))
        (when (> el (+ bl 78))
          (goto-char (+ bl 72))
          (search-backward " " bl t)
          (if (re-search-forward " +" el t)
              (progn
                (if delsp
                    (insert ?\s))
                (insert ?\n)
                (insert (make-string quote-level ?>))
                (forward-line -1))))
        (forward-line)))))

(defun flowed-text-decode-buffer ()
  (interactive)
  (flowed-text-decode-region (point-min) (point-max)))

(defun flowed-text-encode-buffer ()
  (interactive)
  (flowed-text-encode-region (point-min) (point-max)))

(defun flowed-text-done ()
  (interactive)
  (flowed-text-encode-buffer)
  (save-buffer)
  (bury-buffer))

Reply via email to