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))