emacs: Customize how each tag is displayed

Austin Clements amdragon at MIT.EDU
Tue Jan 22 16:24:48 PST 2013


On Fri, 18 Jan 2013, Damien Cassou <damien.cassou at gmail.com> wrote:
> [PATCH 1/4] emacs: Add notmuch-intersperse to notmuch-lib/
> [PATCH 2/4] emacs: pictures that might be used as icons for tags
> [PATCH 3/4] emacs: possibility to customize the rendering of tags
> [PATCH 4/4] emacs: provide convenience functions for notmuch-tagger
>
>
> These patches are the first of an upcoming series whose goal is to
> integrate notmuch-labeler into notmuch. See the following for more
> details: https://github.com/DamienCassou/notmuch-labeler
>
> Points of discussion:
>
> - This series does not have any unit-test to make it smaller and more
>   amenable to comments. I will send a patch when requested.
>
> - Patch 3/4 formats tags as mode-line templates so that we can show
>   tags in the header-line in a later series.
>
> - Patch 3/4 introduces `notmuch-tagger-formats', a list of pairs (KEY
>   FORMAT) to format a tag matching KEY using a special format.
>   Currently, an example of such a list is:
>
>     (("unread"
>          (:propertize "unread" face
>                       (:foreground "red")))
>      ("flagged"
>          (:propertize "flagged" display
>                       (image :type svg
>                              :file ,(notmuch-tagger-image-path "star.svg")
>                              :ascent center :mask heuristic))))
>
>   to set the unread tag to be red and the flagged tag to have a star
>   picture attached. Because this variable is hard to edit without
>   making mistakes, patch 4/4 introduces customization functions that
>   the user can call on their init.el file like this:
>
>   (notmuch-tagger-propertize "unread" :foreground "red")
>   (notmuch-tagger-image-star "flagged")
>
>   Nevertheless, implementing a customize interface for this variable
>   is difficult as Emacs does not provide customization widgets for
>   text-property lists. A possible solution could be to change the list
>   value so that it looks like:
>
>     (("unread" (propertize :foreground "red"))
>      ("flagged" (image-star)))
>
>   where the FORMAT part of each pair would be the suffix of a
>   notmuch-tagger customization function name (as introduced by patch
>   4/4) and the rest would be parameters to pass to this function
>   (except the KEY parameter that is already in each pair of the
>   `notmuch-tagger-formats' list). This solution would be more amenable
>   to the customization interface, but maybe less powerful.

I'm not sold on your mode-line-format approach.  On the one hand, it's
rather quaint that you can reuse all of the mode-line formatting
mechanism.  But, on the other hand, 90% of the mode-line formatting
mechanism makes no sense in this context, and all of the utility
functions you add to make this variable accessible as well as the
difficulty in creating a customize interface for it suggest that this is
a false economy: that mode-line formatting is, in fact, the wrong
abstraction for this.

I'm not sure what the best representation is, but here's one idea.
Instead of inventing (or reusing) some specialized formatting language,
use the language already at your disposal: make the tag format a list of
Elisp expressions that transform the tag string into an arbitrary
propertized string to use in place of the tag.  You can't get much more
powerful than that, and, plus, it would be really easy to implement.

Here's what I'm imagining, complete with working (slightly hairy)
customize interface:

(defcustom notmuch-tag-formats
  '(("unread" (propertize tag 'face '(:foreground "red")))
    ("flagged" (notmuch-tag-format-image tag "star.svg")))

  "Custom formats for individual tags.

This gives a list that maps from tag names to lists of formatting
expressions.  The car of each element gives a tag name and the
cdr gives a list of Elisp expressions that modify the tag.  If
the list is empty, the tag will simply be hidden.  Otherwise,
each expression will be evaluated in order: for the first
expression, the variable `tag' will be bound to the tag name; for
each later expression, the variable `tag' will be bound to the
result of the previous expression.  In this way, each expression
can build on the formatting performed by the previous expression.
The result of the last expression will displayed in place of the
tag.

For example, to replace a tag with another string, simply use
that string as a formatting expression.  To change the foreground
of a tag to red, use the expression
  (propertize tag 'face '(:foreground \"red\"))

See also `notmuch-tag-format-image', which can help replace tags
with images."

  :group 'notmuch-search
  :group 'notmuch-show
  :type '(alist :key-type (string :tag "Tag")
		:extra-offset -3
		:value-type
		(radio :format "%v"
		       (const :tag "Hidden" nil)
		       (set :tag "Modified"
			    (string :tag "Display as")
			    (list :tag "Face" :extra-offset -4
				  (const :format "" :inline t
					 (propertize tag 'face))
				  (list :format "%v"
					(const :format "" quote)
					custom-face-edit))
			    (list :format "%v" :extra-offset -4
				  (const :format "" :inline t
					 (notmuch-tag-format-image tag))
				  (choice :tag "Image"
					  (const :tag "Star" "star.svg")
					  (const :tag "Empty star"
						 "star-empty.svg")
					  (const :tag "Tag" "tag.svg")
					  (string :tag "Custom")))
			    (sexp :tag "Custom")))))

where I'm imagining notmuch-tag-format-image would be very similar to
your existing image utilities, with an interface something like

(defun notmuch-tag-format-image (tag image)
  "Replace TAG with IMAGE, if available.

Returns a propertized string that will display IMAGE in place of
TAG.  If IMAGE is a relative path, it will be looked for in the
standard notmuch image directory.  This is designed for use in
`notmuch-tag-formats'."
  ...)

Thoughts?



More information about the notmuch mailing list