Do things after selecting window
24 Jan 2015I 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.
Outro
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.