Emacs microhabit: Switching windows with windmove, ace-window, and ace-jump
Posted: - Modified: | emacsOne of the Emacs micro-habits I'm working on is getting better at switching between windows. When there are only two windows, C-x o
(other-window
) works just fine. However, when there are three or more, it can take a few repetitions of C-x o
to get to where I want. I could get around that by binding other-window
to M-o
instead, replacing the default keymap for that. Or I could try to get the hang of other ways to move around.
Here's an 8-minute video showing windmove
, ace-window
, and ace-jump
:
https://www.youtube.com/watch?v=nKCKuRuvAOw&list=UUlT2UAbC6j7TqOWurVhkuHQ
Windmove lets you move around with cursor keys, if you set up the appropriate keyboard shortcuts. Ace-window works like ace-jump. In addition, you can use C-u to swap windows and C-u C-u to delete windows. Ace-jump works across windows, so that's handy too.
Here's my relevant code snippet for Windmove. I changed this to use define-key
instead of bind-key
.
(defvar sacha/windmove-map (make-sparse-keymap)) (define-key sacha/windmove-map "h" 'windmove-left) (define-key sacha/windmove-map "t" 'windmove-up) (define-key sacha/windmove-map "n" 'windmove-down) (define-key sacha/windmove-map "s" 'windmove-right) (define-key sacha/windmove-map "[left]" 'windmove-left) (define-key sacha/windmove-map "[up]" 'windmove-up) (define-key sacha/windmove-map "[down]" 'windmove-down) (define-key sacha/windmove-map "[right]" 'windmove-right) (key-chord-define-global "yy" sacha/windmove-map)
Here's the cheat sheet I made for myself:
2015-01-12 Emacs microhabit – window management – index card #emacs
And here's a simpler reference that you can personalize with your own shortcuts:
2015-01-18 Emacs microhabit – Switching windows – index card #emacs #microhabit
Naturally, after recording the video, I thought of a better way to manage my windows. I took advantage of the def-repeat-command
that abo-abo posted on (or emacs so that I could repeat keybindings easily. I modified the function to accept nil
as the first value if you don't want the keymap to run a command by default, and to use kbd
for the keybinding definitions.
(defun sacha/def-rep-command (alist) "Return a lambda that calls the first function of ALIST. It sets the transient map to all functions of ALIST, allowing you to repeat those functions as needed." (lexical-let ((keymap (make-sparse-keymap)) (func (cdar alist))) (mapc (lambda (x) (when x (define-key keymap (kbd (car x)) (cdr x)))) alist) (lambda (arg) (interactive "p") (when func (funcall func arg)) (set-transient-map keymap t))))
Here's my new binding for yy
. It lets me bounce on y
to use other-window
as normal, use the arrow keys to move between windows thanks to windmove
, and use ace-window
as well: h
is the regular ace-window, s
swaps, and d
deletes.
(key-chord-define-global "yy" (sacha/def-rep-command '(nil ("<left>" . windmove-left) ("<right>" . windmove-right) ("<down>" . windmove-down) ("<up>" . windmove-up) ("y" . other-window) ("h" . ace-window) ("s" . (lambda () (interactive) (ace-window 4))) ("d" . (lambda () (interactive) (ace-window 16))) )))
Neat, eh?