(or emacs irrelevant)

Do things after selecting window

I saw this question today at Stack Overflow - Emacs: How to enable toolbar mode and menubar mode only under a certain mode?. Myself, I never use the menubar or the toolbar. But maybe it's just because it's useless in the modes to which I'm used to. What if there was an option to have the menu bar on just for a few select modes? For instance, if I decided that I want to learn Haskell tomorrow, I wouldn't mind to have the menu bar on for a while, especially if there was some useful stuff there in the menu.

The Code

I'm not aware of a convenient hook for this job; I tried after-change-major-mode-hook, and it doesn't work when switching windows. The universal post-command-hook would be totally lame for this task.

Then it occurred to me, that select-window has to be called eventually in most circumstances. Even ace-window calls select-window.

This is what I came up with so far:

(defvar menubar-last
  (make-ring 20))
(ring-insert menubar-last "dummy")

(defadvice select-window (after select-window-menubar activate)
  (unless (equal (buffer-name) (ring-ref menubar-last 0))
    (ring-insert menubar-last (buffer-name))
    (let ((yes-or-no
           (if (memq major-mode '(r-mode lisp-interaction-mode))
               1 -1)))
      (menu-bar-mode yes-or-no)
      (tool-bar-mode yes-or-no))))

The Ring trickery

It's necessary at least for magit. Doing, for instance, ll operation switches windows many times. So I added a check that the current buffer isn't selected twice.

In Emacs, a ring is like a stack with a limited length: you push onto the head, and if the stack becomes too large, the stuff gets removed from the tail. Perfect for debugging this code, since I don't care what happened 20 window switches ago, I'm only interested in the most recent state. One small annoyance is that the following code will throw, if the ring is empty:

(ring-ref menubar-last 0)

I would prefer if it just returned nil instead. Hence the trick of initializing menubar-last with a dummy item. Note that this annoyance propagates to other areas: (current-kill 0) will throw if you haven't yet copied text since Emacs started.


I'm not sure if this code is more annoying than useful, but, at least, I learned something new while writing it, and I hope you did too. If you know of a better way to accomplish the original task, please do share.