(Note to English-speaking readers: the links entitled Komentarze na tej stronie lead to comment pages.)
I use mu4e as my MUA, and I’m quite happy with it. (I toyed with the idea of setting up Gnus, but I can’t afford quitting my job to find the time for that.) However, one thing with the message mode (which mu4e uses) did annoy me a lot: the attachments. I really didn’t like having my attachments after the signature, since I have a signature changing function which (as part of its operation) deletes everything from the sigdashes up to the end of buffer.
It turned out, I was wrong: having the attachments anywhere but at the end of buffer is asking for trouble. Read this thread for the explanation why. This means that I had to change my functions (which was not really that hard anyway). But now I had another problem: how to avoid putting the attachment in the message body? In message mode, when I press
C-c C-a (
mml-attach-file), the attachment is put at point (unless it’s in the header).
Advice to the rescue. Advising functions is quite a powerful concept. The idea is that you can take any Elisp function and “add” a piece of code to be executed before, or after, or instead of that function. Basically that means changing the functionality of a function without actually modifying it. And it especially nice that you can put a few pieces of advice on a single function, and turn them on or off independently.
Before you go and advise your functions like crazy, be warned that it is not always a good idea. The manual mentions a few situations when advising is dangerous. An extreme case is primitive functions: while you can advise them, you should never do it. Check the manual to learn why. Also, advising might lead to unexpected behavior simply because you may not know about it or expect it.
In this case, however, I do not expect any problems. The idea is to arrange so that the point is at the end of the buffer when
mml-attach-file does its job, and restore the state afterwards. (Restoring the state is easy, you just use the
save-... macros.) So, here’s the code.
(defun mml-attach-file--go-to-eob (orig-fun &rest args) "Go to the end of buffer before attaching files." (save-excursion (save-restriction (widen) (goto-char (point-max)) (apply orig-fun args)))) (advice-add 'mml-attach-file :around #'mml-attach-file--go-to-eob)
And that’s pretty much it. The function
mml-attach-file–go-to-eob is simple; the only point of interest is that it gets the original function as the first argument and its arguments as the rest, so
(apply orig-fun args) does the bulk of the job; both
save-... forms ensure that the point position and the narrowing will be restored. Look up the manual to check other possible arguments to
:around is the most general, there are more possibilities, like
:after, which are less powerful, but simpler to use).
A long time ago, I read about the Emacs Widget library. My first thought was “Wow, that is so cool!”. I didn’t have any use for that, however.
I work for a journal (together with my colleague), and one of our duties is to take submissions and enter them into our system (then, we try to turn them into something compatible with our LaTeX template etc.). We don’t have any automatic submission system – it would probably be an overkill, mainly because we strongly suspect that most authors would not use it anyway (the usual modus operandi for them is to send their papers by email to one of the editors, who then sends it to us).
Therefore, what we have to do for each submission, is to initialize a Mercurial repository with a suitable name, put the file(s) in there, and mould them into our template. This is something that would benefit from automation, so I decided to use Widget to facilitate entering article metadata; my (yet-to-be-written) function will then seed the repository with an article template.
One of the pieces of said metadata is the author’s name. As is often the case, we have two variants of it: the full name and the abbreviated name (for the running header). I decided to have two editing fields for these two strings, but since 99% of the time the abbreviated name can be generated automatically, I wanted the second one to be autofilled when the user enters the full name.
This seemed perfectly doable. Each widget has a
:notify property, which is a function called each time the user changes the state of that widget. (See the manual for details.) So this is (more or less) what I did:
(defun widget-autofilling-example () "An example of what happens if the autofilling is used in a naive way." (interactive) (switch-to-buffer "*Widget-autofill-examle*") (kill-all-local-variables) (let ((inhibit-read-only t)) (erase-buffer)) (remove-overlays) (setq widget-author-list (widget-create 'editable-list :entry-format "%i %d %v" :value '(("" "")) '(group (editable-field :size 48 :format "Full name: %v\n" :notify update-shortname) (editable-field :size 36 :format "Short name: %v\n")))) (use-local-map widget-keymap) (widget-setup)) (defun update-shortname (name-widget &rest ignore) "Update the shortname -- the naive version." (save-excursion (widget-value-set (cadr (widget-get (widget-get name-widget :parent) :children)) (shorten-name (widget-value name-widget))))) (defun shorten-name (name) "Make NAME shorter by abbreviating each word besides the first one to one letter." (let ((case-fold-search nil)) (replace-regexp-in-string "\\(\\b[[:upper:]]\\)[[:lower:]]+\\b\\(.\\)" "\\1.\\2" name)))
A few things might need clarifying. The
shorten-name function is the usual regex fun, which shortens all but the first words (starting with a capital letter) to the first letter. Notice that we temporarily set
nil so that the search actually distinguishes lower and upper case. This way, “John Smith” gets shortened to “J. Smith”, but “Johann von Gutenberg” becomes “J. von Gutenberg”. The second group (containing only the dot, i.e., matching any character) is there to ensure that the last word beginning with a capital letter is not modified – it won’t match since there’s no character after it to be matched by the dot operator. (Emacs regexen have
\', which matches the empty string, but only at the end of the string. They don’t have the reverse: a construct which would match an empty string not at the end of the string.)
The first thing really related to widgets is the
widget-autofilling-example function. It is modelled after the example in the manual, but contains one twist: an editable list whose elements are groups of fields. If I didn’t give the
:value keyword, it would be initialized as an empty list; this is not what we want, we assume that it is best to assume one author as the default. (In the rare case when there are no authors – it happens in some unusual circumstanes – the user can always delete the only element of the editable list anyway, using the
[DEL] button). The
:notify property is the name of the function which will be called when any change occurs to the containing widget (see the docs for details on its argument list).
The actual function doing the work is
update-shortname. Notice the magic
cadr: since there are no functions in the widget library (at least, not documented ones) to walk the widget tree, getting the next widget requires going one level up (so you first get the
:parent and then its
:children), and then selecting the right one from the children (in this example, the second one). It is ugly, it is unmaintainable, and it is implementation-dependent, but I know no other way to accomplish this. (If this wasn’t part of one of the elements of the editable list, things would be different – I could just say
(setq widget-name (widget-create ...)), like in the case of
save-excursion makes sure the point stays where it should.
So, everything looks ok, right? But it’s not. Try it yourself and you’ll see why: with each character automatically entered in the shortened name field, the field seemingly grows by one character, and with each character deleted (either by pressing backspace, or by
shorten-name trickery) it shrinks.
I have to say that I was clueless what to do with it. The “obvious” solution – calling
update-shortname – didn’t help. Inspecting the whole code of the widget library seemed too much work.
Happily, I found what I needed rather quickly. Since
C-h c a in a field shows that
a is bound to the usual
self-insert-command, I suspected that the adjusting of the field size (which works when normally typing) must be either an advice (but grepping for “advice” in the widget sources returned no results) or a hook. Grepping for “hook” yields (after a minute of searching) the function
widget-after-change, which is added to the hook
Looking for that hook in the Elisp reference manual I learned that it doesn’t seem trivial to use it. I was wrong, however; it just required a few minutes of thinking. Each function in that hook should receive three arguments: the position of the beginning and the end of the text that changed, and the length of the text before the change. In normal circumstances, Emacs calls those functions itself, with proper arguments. In our case, they are not called. The reason for that is (more or less) clear: calling those hooks is (temporarily) disabled while they are running, probably in order to avoid infinite loops.
So, here’s the way I figured out to solve my problem:
(defun update-shortname (name-widget &rest ignore) "Update the shortname. A working, though needlessly complicated version." (let* ((short-name-widget (cadr (widget-get (widget-get name-widget :parent) :children))) (old-overlay (widget-get short-name-widget :field-overlay)) (old-beg (overlay-start old-overlay)) (old-end (overlay-end old-overlay)) (old-beg-mark (copy-marker old-beg)) (old-end-mark (copy-marker old-end)) (old-size (- old-end old-beg))) (save-excursion (widget-value-set short-name-widget (shorten-name (widget-value name-widget))) (widget-after-change (marker-position old-beg-mark) (marker-position old-end-mark) old-size))))
As you can see, there’s a lot of juggling of markers and overlays: I first obtain the field overlay, then I convert its beginning and end to markers, then perform the change (so that the markers get relocated properly), and finally convert the markers to integers back again and feed them into the
widget-after-change function (which normally sits in
after-change-functions). Quite a mouthful.
It turns out that this is not really necessary. My final implementation is much better (and it addresses the root of the problem, not its symptoms). It turns out that the inhibition of the modification hooks is governed by the aptly named
inhibit-modification-hooks variable. The best solution (at least the best I could invent) is therefore to bind it to nil for a moment when autoupdating the shortened name field:
(defun update-shortname (name-widget &rest ignore) "Update the shortname -- the naive version." (save-excursion (let ((inhibit-modification-hooks nil)) (widget-value-set (cadr (widget-get (widget-get name-widget :parent) :children)) (shorten-name (widget-value name-widget))))))
The bottom line is that (as usual) Emacs is very customizable, and the widget library is yet another proof for that. It might be not extremely easy to use, but you can do very nice things with it.
While the concept of filling text (i.e., putting hard newlines so that no line is longer than some limit) is well-known and very useful, the question about how to unfill some text (i.e., change all hard newlines to spaces) comes up again and again. The internet is full of solutions to that, but I never bothered to look into any of these – if I had a need to unfill a paragraph, I just used to
set-fill-column to, say, ten thounsand and filled the paragraph. It was never a common thing for me to do, so I didn’t feel the need to optimize it.
Until recently, when it turned out that I need to unfill some text programatically. So I went and implemented a simple solution. Since it was important for me to be able to unfill not only a single paragraph, I had to preserve strings of two or more newlines (in the spirit of TeX, Org-mode and Emacs itself, where a “paragraph” is delimited by one or more blank lines). (One distinction is that a line containing some whitespace characters – tabs or spaces – is not considered “blank” by my code. It doesn’t really matter a lot, though – I never do things like that myself, and I ussually run
delete-trailing-whitespace on files I get from someone else.)
So, here’s the code.
(defun unfill-region (begin end) "Change isolated newlines in region into spaces." (interactive (if (use-region-p) (list (region-beginning) (region-end)) (list nil nil))) (save-restriction (narrow-to-region (or begin (point-min)) (or end (point-max))) (goto-char (point-min)) (while (search-forward "\n" nil t) (if (eq (char-after) ?\n) (skip-chars-forward "\n") (delete-char -1) (insert ?\s)))))
The code itself is fairly straightforward – it just searches for a newline repeatedly, and if found – and not followed by another newline – deletes it and inserts a space instead. What you might find useful is the pattern in the
interactive clause: not everyone seems to know about the
use-region-p function, and also the way you pass the region to the function so that if there is no region to act upon, you end up operating on the whole (visible portion of) buffer.
(Więcej means More in Polish; click it to see older entries.)