ace-window display mode
12 Mar 2015Intro
Today, I'll describe a quite recent addition to ace-window, which comes from the idea by @deftsp:
Why not show the
ace-window
dispatch keys in the mode line all the time?
Certainly, there's no reason not to have this option, and it actually makes the whole interface better, since it becomes less feedback-based:
- You glance at a window that you want.
- You know which key you have to press before you call
ace-window
- You make a single-step call instead of:
- press
ace-window
shortcut, - read the dispatch char,
- press the dispatch char.
- press
The feature is implemented as ace-window-display-mode
- a minor mode that you can toggle on or
off whenever you feel like it.
Here's how it looks like:
As you can see, I've made aw-keys
list short on purpose, just to show you that the full path
will be displayed. See, for instance, the *scratch*
window: its path is gf
.
Implementation
Since the implementation is quite short, I'll post it here and go over a few things that might interest people who write Elisp (on some level, please don't mock me for being obvious).
As per request of a curious reader, I've updated the code with the comments from below. Don't try this at home, excessive commenting is bad style.
;; Something that modifies the Emacs behavior should
;; preferrably be implemented as a minor mode.
;;;###autoload
(define-minor-mode ace-window-display-mode
"Minor mode for showing the ace window key in the mode line."
;; And since this minor mode isn't tied to a particular
;; buffer, I declare it as global.
;;
;; An interesting quirk is that I have to put *something*
;; between the docstring and the body, otherwise it won't
;; work.
:global t
;; Dispatch on the variable symbol of the mode -
;; `ace-window-display-mode`. `define-minor-mode' will
;; define both a variable and function symbol.
(if ace-window-display-mode
(progn
;; Update the window parameters
(aw-update)
;; Since `mode-line-format' is a buffer-local
;; variable, I set it with `set-default', in order
;; for the change to not just happen in the current
;; buffer.
(set-default
'mode-line-format
`((ace-window-display-mode
(:eval (window-parameter (selected-window)
'ace-window-path)))
,@(default-value 'mode-line-format)))
(force-mode-line-update t)
;; Each time a window is created or deleted, Emacs
;; will run the `window-configuration-change-hook' -
;; exactly what I need to update `mode-line-format'.
(add-hook 'window-configuration-change-hook 'aw-update))
(set-default
'mode-line-format
(assq-delete-all
'ace-window-display-mode
(default-value 'mode-line-format)))
(remove-hook 'window-configuration-change-hook 'aw-update)))
(defun aw-update ()
"Update ace-window-path window parameter for all windows."
(avy-traverse
(avy-tree (aw-window-list) aw-keys)
(lambda (path leaf)
;; Use `set-window-parameter' to store a variable for
;; each window. Buffer local variables would not work
;; here, since one buffer can be displayed in multiple
;; windows, and those would need a different key each.
(set-window-parameter
leaf 'ace-window-path
(propertize
(apply #'string (reverse path))
'face 'aw-mode-line-face)))))
Since I'm writing something that modifies Emacs behavior when it's enabled, I first try to implement it as a minor mode. And since this minor mode isn't tied to a particular buffer, I declare it as global.
An interesting quirk is that I have to put something between the docstring and the body, otherwise
it won't work. In this case, I put :global t
.
Next, follows that standard dispatch on the variable symbol of the mode - ace-window-display-mode
.
The define-minor-mode
macro will make both variable and function definition for the symbol it's
given:
- the variable is used to check if the mode is on.
- the function is used to turn the mode on / off.
Note the use of assq-delete-all
: this is for when some other package modifies the
mode-line-format
after ace-window
does. In that case, ace-window
's entry wouldn't be the first
one any more.
Since mode-line-format
is a buffer-local variable, I set it with set-default
, in order for the
change to not just happen in the current buffer.
Each time a window is created or deleted, Emacs will run the window-configuration-change-hook
-
exactly what I need to update mode-line-format
.
One final trick is to use set-window-parameter
to store a variable for each window. Buffer local
variables would not work here, since one buffer can be displayed in multiple windows, and those
would need a different key each.
I really like how the avy-tree
/ avy-traverse
interface ended up as: the same functions are used
for selecting window and setting mode-line-format
.
Outro
I hope that you enjoy the update, and keep those interesting ideas coming!