GnusFormatting

The display of the various Gnus buffers is completely configurable through so called formatting variables.

Quoting the manual: “Here’s an example format spec (from the group buffer): `%M%S%5y: %(%g%)\n'. We see that it is indeed extremely ugly, and that there are lots of percentages everywhere.”

Summary Buffer Formatting

Version 1

It is possible to show threads with line drawing characters in a terminal.

First we create the glyphs we want to use for the tree. It doesn’t work to put color on these characters through faces in Emacs so we color them directly using escape sequences.

    (dolist (c '((?\207 . ?q) (?\216 . ?x) (?\212 . ?t) (?\203 . ?m)))
      (aset standard-display-table (car c)
            (vector (create-glyph (concat "\e(0" "\e[35m" ; magenta
                                          (char-to-string (cdr c))
                                          "\e[0m" "\e(B")))))

It may happen that standard-display-table is nil. In that case you can create it with

    (unless standard-display-table
      (setq standard-display-table (make-display-table)))

Setup gnus to use the characters for threading.

    (setq gnus-sum-thread-tree-vertical "\216"
          gnus-sum-thread-tree-root ""
          gnus-sum-thread-tree-false-root ""
          gnus-sum-thread-tree-indent " "
          gnus-sum-thread-tree-single-indent ""
          gnus-sum-thread-tree-leaf-with-other "\212\207>"
          gnus-sum-thread-tree-single-leaf "\203\207>")

Gnus ordinary thread spec %B doesn’t work here because Gnus puts some fancy highlighting on top of the summary lines (normally). So we tell Gnus to stop doing that.

    (defun gnus-user-format-function-thread (dummy)
      (propertize gnus-tmp-thread-tree-header-string 'gnus-face t))

Now we can finally get the nice looking thread by using %u&thread; as a spec in gnus-summary-line-format. Don’t put it inside a %{…} face construct. That won’t work.


Version 2

The above example may not work in GNU Emacs in all cases. It can generate alternative glyphs that do not produce the drawing line characters shown in the image above. Here is an approach that may allow users of gnus to tailor the glyphs to fit with their specific setup.

In .gnus.el replace the code (dolist ...) above with:

    (let ((val 129))
      (while (< val 160)
        (aset standard-display-table val (vector (create-glyph val)))
        (setq val (1+ val))))

The code will produce a full set of glyphs rather than simply four. The function (describe-current-display-table) returns the set of glyphs that have been generated.

The variable glyph-table (shown using C-h v) should now contain a vector:

    [129 130 131 132 133 ... ]

The order of the glyph codes in the vector corresponds to the order of the glyphs shown by (describe-current-display-table). In the example shown here, the fourth glyph shown by (describe-current-display-table) corresponds to the fourth glyph code: 132.

The octal character code ?\204 evaluates to 132. Therefore, to use this glyph in the gnus summary, define one of the gnus-sum-thread-tree- variables to include "\204". With some trial and error the desired glyphs can be identified.

Here is an example, tailored to an alternative setup, that reproduces the drawing line characters shown in the image of the threaded gnus summary above.

    (let ((val 129))
      (while (< val 160)
        (aset standard-display-table val (vector (create-glyph val)))
        (setq val (1+ val))))
    (setq gnus-summary-line-format "%U%R%d %-5,5L %-20,20n %B%-80,80S\n"
          gnus-sum-thread-tree-vertical "\232"
          gnus-sum-thread-tree-root ""
          gnus-sum-thread-tree-false-root ""
          gnus-sum-thread-tree-indent " "
          gnus-sum-thread-tree-single-indent ""
          gnus-sum-thread-tree-leaf-with-other "\226\223>"
          gnus-sum-thread-tree-single-leaf "\217\223>")

Version 3 (Unicode)

Here’s a simpler example of threaded summary view using Unicode characters:

    (setq-default
     gnus-summary-line-format "%U%R%z %(%&user-date class="comment">;  %-15,15f  %B%s%)\n"
     gnus-user-date-format-alist '((t . "%Y-%m-%d %H:%M"))
     gnus-summary-thread-gathering-function 'gnus-gather-threads-by-references
     gnus-thread-sort-functions '(gnus-thread-sort-by-date)
     gnus-sum-thread-tree-false-root ""
     gnus-sum-thread-tree-indent " "
     gnus-sum-thread-tree-leaf-with-other "├► "
     gnus-sum-thread-tree-root ""
     gnus-sum-thread-tree-single-leaf "╰► "
     gnus-sum-thread-tree-vertical "│")

The summary buffer looks like this:

    O   2009-08-01 22:47  joakim@verona.s  imagemagick patch v1
    O   2009-08-02 00:04  Eli Zaretskii    ├► 
    O   2009-08-02 00:29  joakim@verona.s  │╰► 
    O   2009-08-02 03:46  Stephen J. Turn  │ ├► 
    O   2009-08-02 12:52  Andreas Schwab   │ ╰► 
    O   2009-08-02 17:24  Chong Yidong     ╰► 
    O   2009-08-03 01:39  joakim@verona.s   ╰►

Group Buffer Formatting

Topics

Coloring empty topics differently from non-empty topics is a nice idea. Here is how: http://www.dd.chalmers.se/~bojohan/emacs/img/topics_small.png

    (setq gnus-topic-line-format "%i[ %u&topic-line class="comment">; ] %v\n")
    
    ;; this corresponds to a topic line format of "%n %A"
    (defun gnus-user-format-function-topic-line (dummy)
      (let ((topic-face (if (zerop total-number-of-articles)
                            'my-gnus-topic-empty-face
                          'my-gnus-topic-face)))
        (propertize
         (format "%s %d" name total-number-of-articles)
         'face topic-face)))

Replace my-gnus-topic-empty-face and my-gnus-topic-face with some appropriate faces or create your own.

Pretty Printing The Group Lines

RichardRiley

This code uses a prettified group name format based on a server name to “pretty name” mapping.

 (setq rgr/server-name-maps
        '(("RI" . "Private")
          ("SH" . "Work")
          ("FR" . "Friends")
          ("KL" . "Sports")
          ("HA" . "Fun")
          ("GM" . "Gmane")
          ("GN" . "Gnu")
          ("" . "Unknown")
          ))
  (defun gnus-user-format-function-g (headers) ;; gnus-group-line-format use %ug to call this func! e.g  "%M%S%p%P%(%-40,40ug%)%-5uy %ud\n"
    ;; split full group protocol-server:group into three parts.
    (string-match "\\(^.*\\)\\+\\(.*\\):\\(.*\\)" gnus-tmp-group)
    ;; map the first two letters of the server name to a more friendly and cuddly display name
    (let*  ((match-ok (match-string 2 gnus-tmp-group))
            (server-key (if (null match-ok) nil (upcase(substring match-ok 0 2)))))
    (if (zerop (length server-key))
        gnus-tmp-group
      ;; construct new group format line with a small envelope taking the place of any INBOX
      (concat (format "%-8s" (cdr (assoc server-key rgr/server-name-maps))) " -- " (replace-regexp-in-string "INBOX" "\x2709" (match-string 3 gnus-tmp-group))))))

Replacing common prefixes of group names with spaces

Here’s a user-format-function that prints the group name, but if the first part of it is the same as the above group name, it’ll replace the common prefix with spaces. So instead of

    *        0: nntp+news.gmane.org:gmane.emacs.gnus.user
    *        0: nntp+news.gmane.org:gmane.emacs.bbdb.user
    *        0: nntp+news.gmane.org:gmane.linux.hardware.thinkpad
    *        0: INBOX

you get

    *        0: nntp+news.gmane.org:gmane.emacs.gnus.user
    *        0:          .     .         .     .bbdb.user
    *        0:          .     .         .linux.hardware.thinkpad
    *        0: INBOX

and so on:

    (defvar gnus-user-format-function-g-prev "" "")
    (defun empty-common-prefix (left right)
      "Given `left' '(\"foo\" \"bar\" \"fie\") and `right' '(\"foo\"
    \"bar\" \"fum\"), return '(\"   \" \"   \" \"fum\")."
      (if (and (cdr right)			; always keep the last part of right
    	   (equal (car left) (car right)))
          (cons (make-string (length (car left)) ? )
    	    (empty-common-prefix (cdr left) (cdr right)))
        right))
    (defun gnus-user-format-function-g (arg)
      "The full group name, but if it starts with a previously seen
    prefix, empty that prefix."
      (if (equal gnus-user-format-function-g-prev gnus-tmp-group) ; line-format is updated on exiting the summary, making prev equal this
          gnus-tmp-group
        (let* ((prev (split-string-and-unquote gnus-user-format-function-g-prev "\\."))
    	   (this (split-string-and-unquote gnus-tmp-group "\\.")))
          (setq gnus-user-format-function-g-prev gnus-tmp-group)
          (combine-and-quote-strings
           (empty-common-prefix prev this)
           "."))))
    (setq gnus-group-line-format "%M%S%p%P%5y:%B%(%ug%)\n")

Article Buffer Formatting

Header Formatting

Date Header

gnus-article-date-headers variable have many customizations out of the box, one of most useful customization is combined-lapsed which displays original date header with the elapsed time appended.

Unfortunately it does not have local-lapsed customization, which could format date as date string in local time zone with the elapsed time appended.

This snippet implements desired behavior with the help of user-defined customization:

  (setq gnus-article-date-headers '(user-defined)
        gnus-article-time-format
        (lambda (time)
          (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
                 (local (article-make-date-line date 'local))
                 (combined-lapsed (article-make-date-line date
                                                          'combined-lapsed))
                 (lapsed (progn
                           (string-match " (.+" combined-lapsed)
                           (match-string 0 combined-lapsed))))
            (concat local lapsed))))

CategoryGnus