Add to Technorati Favorites

Emacs buffer mode histogram

Tonight I noticed that I had over 200 buffers open in emacs. I’ve been programming a lot in Python recently, so many of them are in Python mode. I wondered how many Python files I had open, and I counted them by hand. About 90. I then wondered how many were in Javascript mode, in RST mode, etc. I wondered what a histogram would look like, for me and for others, at times when I’m programming versus working on documentation, etc.

Because it’s emacs, it wasn’t hard to write a function to display a buffer mode histogram. Here’s mine:

235 buffers open, in 23 distinct modes

91               python +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
47          fundamental +++++++++++++++++++++++++++++++++++++++++++++++
24                  js2 ++++++++++++++++++++++++
21                dired +++++++++++++++++++++
16                 html ++++++++++++++++
 7                 text +++++++
 4                 help ++++
 4           emacs-lisp ++++
 3                   sh +++
 3       makefile-gmake +++
 2          compilation ++
 2                  css ++
 1          Buffer-menu +
 1                 mail +
 1                 grep +
 1      completion-list +
 1                   vm +
 1                  org +
 1               comint +
 1              apropos +
 1                 Info +
 1           vm-summary +
 1      vm-presentation +

Tempting as it is, I’m not going to go on about the heady delights of having a fully programmable editor. You either already know, or you can just drool in slack-jawed wonder.

Unfortunately I’m a terrible emacs lisp programmer. I can barely remember a thing each time I use it. But the interpreter is of course just emacs itself and the elisp documentation is in emacs, so it’s a really fun environment to develop in. And because emacs lisp has a ton of support for doing things to itself, code that acts on emacs and your own editing session or buffers is often very succinct. See for example the save-excursion and with-output-to-temp-buffer functions below.

(defun buffer-mode-histogram ()
  "Display a histogram of emacs buffer modes."
  (interactive)
  (let* ((totals '())
         (buffers (buffer-list()))
         (total-buffers (length buffers))
         (ht (make-hash-table :test 'equal)))
    (save-excursion
      (dolist (buffer buffers)
        (set-buffer buffer)
        (let
            ((mode-name (symbol-name major-mode)))
          (puthash mode-name (1+ (gethash mode-name ht 0)) ht))))
    (maphash (lambda (key value)
               (setq totals (cons (list key value) totals)))
             ht)
    (setq totals (sort totals (lambda (x y) (> (cadr x) (cadr y)))))
    (with-output-to-temp-buffer "Buffer mode histogram"
      (princ (format "%d buffers open, in %d distinct modes\n\n"
                      total-buffers (length totals)))
      (dolist (item totals)
        (let
            ((key (car item))
             (count (cadr item)))
          (if (equal (substring key -5) "-mode")
              (setq key (substring key 0 -5)))
          (princ (format "%2d %20s %s\n" count key
                         (make-string count ?+))))))))

Various things about the formatting could be improved. E.g., not use fixed-width fields for the count and the mode names, and make the + signs indicate more than one buffer mode when there are many.


You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

6 Responses to “Emacs buffer mode histogram”

  1. Neat idea, saw it on python planet; reminded me that while I’ve hacked in elisp almost-literally forever, python is my language of choice (and greatest ease), and I’d recently decided to start making more use of pymacs, since I always have it around (at least on Debian and Ubuntu boxes), to overcome the friction I find in elisp… cribbing from some older pymacs code of mine and just looking at your output, it took me less time to come up with a pymacs version than it did to write this comment.  (Not trying to brag :-) just noting that it really is that frictionless compared to elisp.)  To load it, if you have pymacs, just M-: (pymacs-load “mode-stats”) and the m-x mode-stats-mode-stats RET to actually run it.

    Assuming disqus doesn’t mangle this, I’m curious if you find it more readable; the lisp “module” basically gives you everything elisp side, and there’s some complexity regarding data being python-side or emacs-side, plus there can be issues regarding shovelling large objects back and forth.  But for things where elisp is weirdly limiting, I find it a nice approach…

     11 Fundamental     ***********
      2 Python          **
      2 Man             **
      1 Shell           *
      1 Lisp Interaction *
      1 Help            *
      1 Zephyr          *
      1 Completion List *
      1 Dired by name   *
      1 Apropos         *

    #!/usr/bin/python

    # (pymacs-load (expand-file-name “~/elisp/mode-stats”))

    from Pymacs import lisp
    from collections import defaultdict

    def mode_stats():
        stats = defaultdict(int)
        for buf in lisp.buffer_list():
            stats[lisp.buffer_local_value(lisp.intern(“mode-name”), buf)] += 1

        outbuf = lisp.get_buffer_create(“*mode-stats*”)
        lisp.set_buffer(outbuf)
        lisp.erase_buffer()
        for bufname in sorted(stats, key=lambda x: stats[x], reverse=True):
            lisp.insert(“%3d %-15s %sn” % (stats[bufname], bufname, “*” * stats[bufname]))
        lisp.goto_char(0)
        lisp.switch_to_buffer(outbuf)

    interactions = {mode_stats: “”}

  2. Wow!  That’s fantastic. I didn’t even know pymacs existed :-)   Thanks a lot, I’m going to use it on my next little emacs project….

  3. Linked in says we have one friend in common: Gumby :-)

  4. Quite neat.
    Here is my version derived from yours by incrementally using more loop and less setq:
    (Apologies in advance for having that sneaky side effect of filling the hash-table in the loop which ostensibly calculates only the number of buffers.)

    (defun buffer-mode-histogram ()
    “Display a histogram of emacs buffer modes.”
    (interactive)
    (let* ((ht (make-hash-table :test ‘equal))
    (number-of-buffers (loop for buffer being the buffers
    for mode-name = (symbol-name (buffer-local-value ‘major-mode buffer))
    do (incf (gethash mode-name ht 0))
    count 1))
    (totals (sort (loop for key being the hash-keys of ht
    using (hash-values value)
    collect (list key value))
    (lambda (x y) (if (eql (second x) (second y))
    (string-lessp (first x) (first y))
    (> (second x) (second y)))))))
    (with-output-to-temp-buffer “Buffer mode histogram”
    (princ (format “%d buffers open, in %d distinct modesnn”
    number-of-buffers (length totals)))
    (loop for (key count) in totals
    do (princ (format “%2d %20s %sn”
    count
    (if (equal (substring key -5) “-mode”)
    (substring key 0 -5) key)
    (make-string count ?+)))))))

  5. Cool. I adapted your code to make a histogram of word frequency instead. The regexp is kind of messed up so it matches punctuation though. I also added a bit of code to limit the number of +s to some limit and scale the rest.


    (defun word-histogram-region (posBegin posEnd)
    "Display word histogram showing frequency of word occurrence."
    (interactive "r")
    (message "Counting...")
    (let* ((ht (make-hash-table :test 'equal))
    (totals '()))
    (save-excursion
    (goto-char posBegin)
    (while (and ( (cadr x) (cadr y)))))
    (with-output-to-temp-buffer "Word histogram"
    (princ (format "%d different wordsnn"
    (length totals)))
    (dolist (item totals)
    (let
    ((key (car item))
    (count (cadr item))
    (maxcount (cadr (car totals))))
    (princ (format "%2d %20s %sn" count key
    (make-string (/ (* count (min 36 maxcount)) maxcount) ?+))))))))

  6. Cool :-) Thanks for sharing!