Gnus development mailing list
 help / color / mirror / Atom feed
* MML composing snippet
@ 1999-05-03 14:31 François Pinard
  1999-06-12  6:21 ` Lars Magne Ingebrigtsen
  0 siblings, 1 reply; 2+ messages in thread
From: François Pinard @ 1999-05-03 14:31 UTC (permalink / raw)


[-- Attachment #1: Type: text/plain, Size: 229 bytes --]

Hi, people.  I worked a tiny bit on the MML composing snippet which original
purpose was to help writing enriched text.  Here is a description of the
change and current concerns.  Of course, please share me your good ideas! :-)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/enriched, Size: 427 bytes --]

First, I found inappropriate to always start with an empty buffer.
Sometimes, in the process of composing an email, we decide <bold><italic><x-bg-color><param>cyan</param>after the
fact</x-bg-color></italic></bold> to turn some already composed text into enriched.  So, commands
now allow to set a region in a message, and then turn that region into
an enriched part, or something else.  Let me try it with this
paragraph ;-).

[-- Attachment #3: Type: text/plain, Size: 1722 bytes --]


So, some commands insert a part at point, other commands turn a region into
a part.  I guess these would be useful.  However, this raises some questions.
What should we do, for example, when inserting a part within an already
existing MML part?  For now, I took the approach of splitting the intruded
part in two pieces, one before the new part and another after the new part.
This sounds OK for simple cases, but it should also address (later) the case
of hierarchical multiparts.  When a region is turned into a part, I added
the restriction that the region should be fully contained within a single
MML part, or else, fully sandwiched between two neighbouring parts (if any).

Let me add a note about multipart hierarchies.  I've seen some
very complex MIME messages in which the document hierarchy (chapters,
sections, subsections, etc.) was conveyed directly into a MIME hierarchy,
all multipart/mixed containing mainly text/plain, say.  For me, this is
not far from abusing MIME, as it then invites readers to give much more
importance to the MIME intimate organization and structure than it should
normally have in most messages, anyway.  Maybe this scheme is only used
out of laziness by document converters, and I then find some ugliness in
all the internal MIME noise which is then implied.  All this to say why
I tend favor inserting parts flat at the same level of the parts they are
intruding in, instead of creating a part hierarchy if it can be avoided.
My intuition tells me it should cut down a bit on the MIME conceptual noise,
when such noise could be avoided.

Another problem I had was to use another major mode to edit a part, while
wanting to retain Emacs highlighting in the message.  This text:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: Type: text/enriched, Size: 1216 bytes --]

<nofill>
;; Editing parts generics.
<x-color><param>Firebrick</param>;; [...]
</x-color>
(<x-color><param>Purple</param>defun</x-color> <x-color><param>Blue</param>fp-mml-replace-current-part-type</x-color> (type)
  <x-color><param>RosyBrown</param>"Force TYPE as the type/subtype for the current part."</x-color>
  (interactive (list (mml-minibuffer-read-type nil <x-color><param>RosyBrown</param>"text/enriched"</x-color>)))
  (<x-color><param>Purple</param>let</x-color> ((extent (fp-mml-find-current-part-extent)))
    (<x-color><param>Purple</param>unless</x-color> extent
      (<x-color><param>Red</param>error</x-color> <x-color><param>RosyBrown</param>"Not within an MML part"</x-color>))
    (<x-color><param>Purple</param>save-excursion</x-color>
      (goto-char (nth 1 extent))
      (<x-color><param>Purple</param>unless</x-color> (re-search-backward <x-color><param>RosyBrown</param>"type=\"[a-z]+/[a-z]+\""</x-color> (car extent) t)
	(<x-color><param>Red</param>error</x-color> <x-color><param>RosyBrown</param>"MML part had no type"</x-color>))
      (replace-match (concat <x-color><param>RosyBrown</param>"type=\""</x-color> type <x-color><param>RosyBrown</param>"\""</x-color>) t t))))
</nofill>

[-- Attachment #5: Type: text/plain, Size: 2358 bytes --]


was made by editing a new text/enriched part (with `M-m r'), forcing
the mode (with `M-x emacs-lisp-mode'), inserting the appropriate text,
and getting out (with `M-x fp-mml-stop-edit-part', as `C-c C-c' is not
available, waiting to implement suggestions I received about that one).
But I wanted that users could instead begin a text/plain part and, while
editing goes on, change their mind about it and be able to edit the type
in the MML tag from text/plain to text/enriched, before getting out of
editing the part.  Maybe some other commands could allow for doing such
a MML type change directly from the editing buffer, while the edit goes,
or maybe as an option while leaving edit.

An annoyance is that I had to explicitly nest the generated enriched part
within <nofill> and </nofill>, as filling is the default.  There should
be some way to automate this as well.

Another irritating thing is that MML tags should be quoted, getting out
of edit.  I'm not fully sure about this one, depending on if it makes sense
or note to later hope editing more complex hierarchical structures, having
real MML tags in them.  Some might hope to never edit more than one part at
a time (given Emacs limitations of only one mode per buffer), in which case,
requoting (and unquoting) would make a lot of sense.  On the other hand,
if you consider the base message buffer, it does have real MML tags, so we
_do_ edit more than one part at a time already, and this invites to have
other indirect buffers allowing for real MML as well.  So, there are two
opposing trends for edition, each with merits and flaws.  Food for thought!

I'm not very happy with the keymap, and need good ideas there as well.
For now, I moved `M-m e' (mml-attach-external) out of the way on `M-m x', so
`M-m e' can be used for editing the already existing part where the point is.
Command `M-m t' changes, with completion, the type/subtype of the existing
part where the point is (this might not be that useful, I do not know).
Commands `M-m p' and `M-m r' create a new part, either at point or from
the region, both interactively asking for its type/subtype with completion.
`M-m P' and `M-m R' do the same, yet text/enriched is then implied.

Here is the state of the code I'm playing with.  Since I cannot easily
preview this message, I hope to be lucky enough so it goes out nicely! :-)


[-- Attachment #6: snippet --]
[-- Type: text/plain, Size: 6775 bytes --]


;;; Start of editing parts.

;; Editing parts generics.

(defvar message-edit-part-counter 0
  "Counter so all indirect buffers for editing MML parts are different.")

(defvar fp-mml-edit-part-map nil
  "Keymap while editing an MML part in an indirect buffer.")
(unless fp-mml-edit-part-map
  (setq fp-mml-edit-part-map (make-sparse-keymap))
  (define-key fp-mml-edit-part-map "\C-c\C-c" 'fp-mml-stop-edit-part))

(defun fp-mml-find-current-part-extent ()
  ;; Return the current MML part extent, or nil if not within a part.
  ;; The result is a list of three positions: the start of the starting MML tag,
  ;; the end of the starting MML tag and the start of the ending MML tag.
  (save-excursion
    (let ((here (point))
	  start end)
      (when (search-backward "<#part" nil t)
	(setq start (point))
	(when (search-forward ">" nil)
	  (setq end (point))
	  (when (and (search-forward "<#/part>" nil t)
		     (> (point) here))
	    (list start end (match-beginning 0))))))))

(defun fp-mml-find-current-part-type ()
  ;; Return the type/subtype for the current part, or nil if not within a part.
  (let ((extent (fp-mml-find-current-part-extent)))
    (when extent
      (save-excursion
	(goto-char (nth 1 extent))
	(when (re-search-backward "type=\"\\([a-z]+/[a-z]+\\)\"" (car extent) t)
	  (match-string 1))))))

(defun fp-mml-replace-current-part-type (type)
  "Force TYPE as the type/subtype for the current part."
  (interactive (list (mml-minibuffer-read-type nil "text/enriched")))
  (let ((extent (fp-mml-find-current-part-extent)))
    (unless extent
      (error "Not within an MML part"))
    (save-excursion
      (goto-char (nth 1 extent))
      (unless (re-search-backward "type=\"[a-z]+/[a-z]+\"" (car extent) t)
	(error "MML part had no type"))
      (replace-match (concat "type=\"" type "\"") t t))))

(defun fp-mml-edit-current-part ()
  "Setup an indirect buffer to edit the MML part containing point."
  (interactive)
  (let ((part-data (fp-mml-find-current-part-extent)))
    (unless part-data
      (error "Not within an MML part"))
    (let ((start (nth 1 part-data))
	  (end (nth 2 part-data))
	  (type (fp-mml-find-current-part-type)))
      (let ((name (format "*edit part <%d>*" (incf message-edit-part-counter)))
	    (action (intern-soft (concat "fp-mml-" type "-start-edit"))))
	(switch-to-buffer (make-indirect-buffer (current-buffer) name))
	(narrow-to-region start end)
	(when action
	  (apply action nil))
	(use-local-map fp-mml-edit-part-map)
	(message "Type `C-c C-c' once done")))))

(defun fp-mml-stop-edit-part (prefix)
  "Get rid of the current indirect buffer for editing an MML part."
  (interactive "P")
  (let* ((type (save-restriction
		 (widen)
		 (fp-mml-find-current-part-type)))
	 (action (intern-soft (concat "fp-mml-" type "-stop-edit"))))
    (when action
      (apply action nil)))
  (kill-buffer (current-buffer)))

(autoload 'mml-minibuffer-read-type "mml")

(defun fp-mml-new-part-at-point (type)
  "Make a new MIME part and select an indirect buffer for editing its contents.
TYPE is a string giving the MIME type and subtype, separated by a slash.
The editing buffer starts empty and is aimed for the current position."
  (interactive (list (mml-minibuffer-read-type nil "text/plain")))
  (fp-mml-new-part-from-region type (point) (point)))

(defun fp-mml-new-part-from-region (type start end)
  "Make a new MIME part and select an indirect buffer for editing its contents.
TYPE is a string giving the MIME type and subtype, separated by a slash.
The editing buffer is initialized with the text going from START to END."
  (interactive (list (mml-minibuffer-read-type nil "text/plain")
		     (region-beginning)
		     (region-end)))
  (let ((start-extent (save-excursion
			(goto-char start)
			(fp-mml-find-current-part-extent)))
	(end-extent (save-excursion
		      (goto-char end)
		      (fp-mml-find-current-part-extent))))
    (if (or start-extent end-extent)
	(unless (equal start-extent end-extent)
	  (error "Requested part would partially overlap with another"))
      (save-excursion
	(goto-char start)
	(when (re-search-forward "<#/?part" end t)
	  (error "Requested part would overlap with a subpart"))))
    (goto-char end)
    (insert "<#/part>")
    (when end-extent
      (insert (buffer-substring (car end-extent) (nth 1 end-extent))))
    (goto-char start)
    (when start-extent
      (insert "<#/part>"))
    (insert "<#part type=\"" type "\" disposition=inline>")
    (fp-mml-edit-current-part)))

(unless (fboundp 'buffer-indirect-children)
  (defun buffer-indirect-children (main-buffer)
    (and (bufferp main-buffer)
	 (let ((buffers (buffer-list))
	       result)
	   (while buffers
	     (let ((buffer (pop buffers)))
	       (when (eq (buffer-base-buffer buffer) main-buffer)
		 (push buffer result))))
	   result))))

(defun fp-mml-stop-edit-all-parts ()
  (let ((buffers (buffer-indirect-children (current-buffer))))
    (save-excursion
      (while buffers
	(set-buffer (pop buffers))
	(fp-mml-stop-edit-part)))))
(add-hook 'message-send-hook 'fp-mml-stop-edit-all-parts)

;; Editing text/enriched parts.

(defun fp-mml-new-text/enriched-at-point ()
  "Make a new text/enriched part and setup a buffer for editing its contents.
The editing buffer starts empty and is aimed for the current position."
  (interactive)
  (fp-mml-new-part-from-region "text/enriched" (point) (point)))

(defun fp-mml-new-text/enriched-from-region (start end)
  "Make a new text/enriched part and setup a buffer for editing its contents.
The editing buffer is initialized with the text going from START to END."
  (interactive "r")
  (fp-mml-new-part-from-region "text/enriched" start end))

(defun fp-mml-text/enriched-start-edit ()
  (let ((contents (buffer-substring-no-properties (point-min) (point-max))))
    (delete-region (point-min) (point-max))
    (insert contents))
  (format-decode-region (point-min) (point-max) 'text/enriched)
  (enriched-mode 1))

(defun fp-mml-text/enriched-stop-edit ()
  (format-encode-region (point-min) (point-max) 'text/enriched)
  (goto-char (point-min))
  (forward-line 3)
  (delete-region (point-min) (point)))

;; Editing parts keymap.

(eval-after-load "mml"
  '(progn
     ;; FIXME: the \M-m is required?  "mml.el" does not use it, how comes?
     (define-key mml-mode-map "\M-mP" 'fp-mml-new-text/enriched-at-point)
     (define-key mml-mode-map "\M-mR" 'fp-mml-new-text/enriched-from-region)
     (define-key mml-mode-map "\M-me" 'fp-mml-edit-current-part)
     (define-key mml-mode-map "\M-mp" 'fp-mml-new-part-at-point)
     (define-key mml-mode-map "\M-mr" 'fp-mml-new-part-from-region)
     (define-key mml-mode-map "\M-mt" 'fp-mml-replace-current-part-type)
     (define-key mml-mode-map "\M-mx" 'mml-attach-external)))

;;; End of editing parts.

[-- Attachment #7: Type: text/plain, Size: 62 bytes --]


-- 
François Pinard   http://www.iro.umontreal.ca/~pinard

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: MML composing snippet
  1999-05-03 14:31 MML composing snippet François Pinard
@ 1999-06-12  6:21 ` Lars Magne Ingebrigtsen
  0 siblings, 0 replies; 2+ messages in thread
From: Lars Magne Ingebrigtsen @ 1999-06-12  6:21 UTC (permalink / raw)


François Pinard <pinard@iro.umontreal.ca> writes:

> I'm not very happy with the keymap, and need good ideas there as well.
> For now, I moved `M-m e' (mml-attach-external) out of the way on `M-m x', so
> `M-m e' can be used for editing the already existing part where the point is.
> Command `M-m t' changes, with completion, the type/subtype of the existing
> part where the point is (this might not be that useful, I do not know).
> Commands `M-m p' and `M-m r' create a new part, either at point or from
> the region, both interactively asking for its type/subtype with completion.
> `M-m P' and `M-m R' do the same, yet text/enriched is then implied.

You'll send me patches for mml.el, of course?  :-)

-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~1999-06-12  6:21 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-05-03 14:31 MML composing snippet François Pinard
1999-06-12  6:21 ` Lars Magne Ingebrigtsen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).