2011-08-25

customizing the mode-line

The mode-line is the emacs 'status bar', the bar just above the minibuffer that shows various pieces of information, such as the buffer name, the major mode, maybe the current line number, some indicators for active minor modes, and so on. As I'm looking at it, it starts with 1<U:**- (which is: input-method: latin-1-alt-postfix, buffer-coding-system: utf8-unix, line-ending: unix-style, buffer is writable and buffer is modified – the tooltips help).
As with just about anything in emacs, the mode-line can be customized just the way you like. I give some example below, not because I think it is necessarily the best way, but just to give you a bit of an example to start with when making your own best-mode-line-ever.
I'm not going through all the details of the example, but let me highlight a few things that make it a bit easier to understand.
First of all, the mode-line can be customized by setting the variable mode-line-format; this variable becomes buffer-local automatically when changed, so if you want to set it for all buffers, you'll need to use setq-default in your .emacs (or equivalent). The format is quite similar to the one for frame-title-format, which we discussed in setting the frame title a while back.
mode-line-format is a list of items which are evaluated, and put together as a string which then ends up as the mode-line contents. These properties can be any string. The following types of items can be used:
  • First, normal strings are just shown as-is;
  • Then, there are some special format parameters which will be replaced with their value in the mode-line, from the Emacs-documentation:
  %b -- print buffer name.      %f -- print visited file name.
  %F -- print frame name.
  %* -- print %, * or hyphen.   %+ -- print *, % or hyphen.
        %& is like %*, but ignore read-only-ness.
        % means buffer is read-only and * means it is modified.
        For a modified read-only buffer, %* gives % and %+ gives *.
  %s -- print process status.   %l -- print the current line number.
  %c -- print the current column number (this makes editing slower).
        To make the column number update correctly in all cases,
        `column-number-mode' must be non-nil.
  %i -- print the size of the buffer.
  %I -- like %i, but use k, M, G, etc., to abbreviate.
  %p -- print percent of buffer above top of window, or Top, Bot or All.
  %P -- print percent of buffer above bottom of window, perhaps plus Top,
        or print Bottom or All.
  %n -- print Narrow if appropriate.
  %t -- visited file is text or binary (if OS supports this distinction).
  %z -- print mnemonics of keyboard, terminal, and buffer coding systems.
  %Z -- like %z, but including the end-of-line format.
  %e -- print error message about full memory.
  %@ -- print @ or hyphen.  @ means that default-directory is on a
        remote machine.
  %[ -- print one [ for each recursive editing level.  %] similar.
  %% -- print %.   %- -- print infinitely many dashes.
Decimal digits after the % specify field width to which to pad.
  • Forms of the type (:eval ...) are evaluated each time the mode-line is drawn (just like the '%'-parameters) ; so, if you have a value that changes of the course your emacs session, you should use (:eval ...). For example, for your emacs-uptime you could use (:eval (emacs-uptime "%hh")); while the emacs-PID does not change, so simply you could simply use (format "PID:%d").
    The format parameter mentioned above are of evaluated each time as well. Note that you have to be a bit careful with evaluations - don't do too heavy operations there, and be careful the updates don't recurse.
  • There are many others which I won't go into now - please check the Elisp reference. It's a rather baroque format…
Now, let's put this all together in an example (tested with emacs 23 and 24). As I said, this is for demonstration purposes only; but hopefully it gives you some inspiration. A lot of the 'magic' (colors, tooltips, faces) happens with the propertize function; again, the Elisp documentation can tell you a lot more about that. I'm (ab)using the various font-lock-faces to have colors that blend in nicely with your current theme.
And it has a limitation still, namely that it does not react to mouse clicks; how to that, I will discuss in some future article.



;; use setq-default to set it for /all/ modes
(setq mode-line-format
  (list
    ;; the buffer name; the file name as a tool tip
    '(:eval (propertize "%b " 'face 'font-lock-keyword-face
        'help-echo (buffer-file-name)))

    ;; line and column
    "(" ;; '%02' to set to 2 chars at least; prevents flickering
      (propertize "%02l" 'face 'font-lock-type-face) ","
      (propertize "%02c" 'face 'font-lock-type-face) 
    ") "

    ;; relative position, size of file
    "["
    (propertize "%p" 'face 'font-lock-constant-face) ;; % above top
    "/"
    (propertize "%I" 'face 'font-lock-constant-face) ;; size
    "] "

    ;; the current major mode for the buffer.
    "["

    '(:eval (propertize "%m" 'face 'font-lock-string-face
              'help-echo buffer-file-coding-system))
    "] "


    "[" ;; insert vs overwrite mode, input-method in a tooltip
    '(:eval (propertize (if overwrite-mode "Ovr" "Ins")
              'face 'font-lock-preprocessor-face
              'help-echo (concat "Buffer is in "
                           (if overwrite-mode "overwrite" "insert") " mode")))

    ;; was this buffer modified since the last save?
    '(:eval (when (buffer-modified-p)
              (concat ","  (propertize "Mod"
                             'face 'font-lock-warning-face
                             'help-echo "Buffer has been modified"))))

    ;; is this buffer read-only?
    '(:eval (when buffer-read-only
              (concat ","  (propertize "RO"
                             'face 'font-lock-type-face
                             'help-echo "Buffer is read-only"))))  
    "] "

    ;; add the time, with the date and the emacs uptime in the tooltip
    '(:eval (propertize (format-time-string "%H:%M")
              'help-echo
              (concat (format-time-string "%c; ")
                      (emacs-uptime "Uptime:%hh"))))
    " --"
    ;; i don't want to see minor-modes; but if you want, uncomment this:
    ;; minor-mode-alist  ;; list of minor modes
    "%-" ;; fill with '-'
    ))
Have fun playing with this!