Emacs
Tags

Efficient Window-Switching in Emacs

Posted September 30, 2007

I had a totally awesome post 80% written when I updated Gutsy... and X crashed. That’s not supposed to happen, but it’s what I get for running on the cutting edge. Anyway, I no longer store half-written blogs in /tmp, and I have a shorter post for you instead.

This is just a cool little Emacs customization I came up with.

I tend to use a lot of windows. For non-Emacsers (or Emacsers who have yet to discover this wonderful feature), in Emacs parlance, a “window” is not the same as the things that float around on your desktop and provide a frontend for programs. An Emacs window is a division of the “frame”, which is the same as the things that float around on your desktop.

Each window is a view into another “buffer”, which is the Emacs term for a document you’re editing1. Thus, you can have multiple frames open editing more than one document at a time. Here’s a screenshot of my Emacs window editing this entry and a file from the Haml source (JPEGed for filesize):

Emacs

Using handy keybindings, you can divide a window in half horizontally (C-x 2) or vertically (C-x 3) as many times as you want. C-x 0 merges the window into its parent. I usually have four or more windows open at a time.

I could go on and on about why windows are wonderful. But that’s not what I wanted to talk about.

When you have a bunch of windows open, you tend to want to switch between them. There’s not much point in having them open if you’re not going to use them. Emacs offers a keybinding for this: C-x o, for “other window”. It moves to the next window in the “canonical ordering”, which is determined in a somewhat logical manner.

This is fine, but not great. If you’re switching a lot, a two-key chord is kind of annoying. Plus there’s no way to move backwards.

So I came up with a couple really simple bindings to help remedy this. Here they are:

(defun select-next-window ()
  "Switch to the next window" 
  (interactive)
  (select-window (next-window)))

(defun select-previous-window ()
  "Switch to the previous window" 
  (interactive)
  (select-window (previous-window)))

(global-set-key (kbd "M-<right>") 'select-next-window)
(global-set-key (kbd "M-<left>")  'select-previous-window)

This makes Alt and the right or left arrow keys select forward or backwards in the canonical ordering of the windows. It’s incredibly simple, but much more pleasant and intuitive than C-x o.

Note that the definitions could be different. select-previous-window could be defined as

(defun select-previous-window ()
  "Switch to the previous window" 
  (interactive)
  (other-window -1))

and select-next-window could just be replaced by other-window, which is what C-x o is bound to. I decided to do it this way because (select-window (___-window)) seemed more elegant, and because I liked the symmetry between the two @defun@s.

1 Emacs also uses buffers for all sorts of other purposes. You can have buffers for IRC, buffers for a shell, even buffers for tetris. But usually they’re for editing documents.

Sylvain Utard said September 30, 2007:
(global-set-key [M-left] 'windmove-left)          ; move to left windnow
(global-set-key [M-right] 'windmove-right)        ; move to right window
(global-set-key [M-up] 'windmove-up)              ; move to upper window
(global-set-key [M-down] 'windmove-down)          ; move to downer window

I think you should better use this :-D

Nathan said September 30, 2007:

Interesting… I wasn’t aware of those functions. There’s a lot to be said for the ability to cycle with one key, but I’ll have to give those a try to see how they feel.

Also, I edited your comment to use syntax highlighting. I hope you don’t mind.

Joe Grossberg said October 01, 2007:

Sylvain/Nathan:

When I try Sylvain’s approach in my .emacs file, I get an “ESC is undefined” message in the buffer.

I’m using emacs in a terminal, not in windowed mode.

Any ideas?

Joe Grossberg said October 01, 2007:

Oops; that should say “ESC [down] is undefined”.

And it happens when I try the ALT-DOWN key combo.

Nathan said October 01, 2007:

The various ways of sanely setting “unusual” keybindings (like M-left and C-Home and such) tend not to work for terminal Emacs. You’ve got to actually figure out what string of characters your terminal emulator is sending to Emacs and bind the function to those. The easiest way to figure this out is C-q followed by whatever key you want (e.g. M-<left>). This’ll insert the literal character string.

To keep my .emacs clean, I’ve defined the following function and variable:

(defvar real-keyboard-keys
  '(("M-<up>"        . "\M-[1;3A")
    ("M-<down>"      . "\M-[1;3B")
    ("M-<right>"     . "\M-[1;3C")
    ("M-<left>"      . "\M-[1;3D")
    ("C-<return>"    . "\C-j")
    ("C-<delete>"    . "\M-[3;5~")
    ("C-<up>"        . "\M-[1;5A")
    ("C-<down>"      . "\M-[1;5B")
    ("C-<right>"     . "\M-[1;5C")
    ("C-<left>"      . "\M-[1;5D"))
  "An assoc list of pretty key strings
and their terminal equivalents.")

(defun key (desc)
  (or (and window-system (read-kbd-macro desc))
      (or (cdr (assoc desc real-keyboard-keys))
          (read-kbd-macro desc))))

This way, I can call key on a string representing the keystroke and get the right value. Thus Sylvain’s bindings would look like

(global-set-key (key "M-<left>") 'windmove-left)          ; move to left windnow
(global-set-key (key "M-<right>") 'windmove-right)        ; move to right window
(global-set-key (key "M-<up>") 'windmove-up)              ; move to upper window
(global-set-key (key "M-<down>") 'windmove-down)          ; move to downer window

Note that the literal key values I use might be wrong for your terminal. Those are for VTE, the GTK terminal widget used by gnome-terminal; I believe xterm uses at least some of the same bindings as well. If one of them doesn’t work, though, just use C-q to find the right binding.

Chris Done said June 21, 2008:

Wow, thanks so much for this! I was hoping to setup windmove but I use Emacs from GNU Screen and when I do, it literally sends “3A” etc. to the editor when I hit M-Up, and that applies to all the other arrow keys. Same with C-Up etc. So your assoc list and key function really helped! I am window crazy but `C-x o’ drives me mad with inefficiency. Thanks!

Anonymous said June 25, 2008:

That is only marginally more efficient than the default binding of C-x left/right (arrow)

Nathan said June 25, 2008:

C-x left/right arrow isn’t the default on my Emacs (23.0.60.1 on Ubuntu), where they’re bound to previous-buffer and next-buffer, respectively. Anyway, I’d say there’s a more-than-marginal advantage to being able to switch multiple times without having to repeat the prefix over and over.

Ganesh said July 18, 2008:

I use emacs in terminal mode and I am facing a small issue right now. I want to associate C-<right arrow> and C-<left arrow> keys to windmove-right and windmove-left respectively. But when I type C-q C-<right arrow>, I get the same keystroke (^[OC) as just the right arrow alone. i.e. pressing ctrl key doesn’t send a different keystroke. I want the normal operations for <right arrow> and <left arrow> which is forward-char and backward-char respectively, but change the behavior when ctrl key is pressed to move windows instead of characters. Can anyone help me here ? Any help is appreciated !

Nathan said July 19, 2008:

It’s entirely possible that your terminal emulator just doesn’t distinguish between normal arrow keys and arrow keys with control. I’ve found that xterm and its derivatives are the best in terms of supporting lots of meta-keys, but I still haven’t found one that does everything I want. I’ve mostly settled on limiting my bindings to the alphabetic keys.

Ganesh said July 21, 2008:

Thanks for your reply Nathan. My case is kind of peculiar I guess. I am using emacs terminal mode within VNC. If I use normal Putty from WIndows, things work fine as expected. The interesting part is that Ctrl- functions work fine. Just for the arrow keys alone, the Ctrl key gets “eaten up” before it reaches emacs I believe. Is there any way I can change the terminal emulator or something like that ?

Nathan said July 21, 2008:

Sorry, I’m not very familiar with that setup. It sounds like you may need to change your remote-access client.

Make your comments snazzy with Textile!