10 September, 2011

Emacs: Mark Ring

Editing existing code is rarely a sequential task, and emacs has a really neat feature that helps you out with this; the mark ring. Large navigation commands ( M-< , M-> , C-r , C-s, C-v , M-v , etc.. ) all push the position prior to the movement onto the mark ring of the buffer so you can easily get back to where you were. C-u U-SPC pops off the last mark on the ring and takes you to it.

So for example I am editing this test case:
  sub test_pop : Test(4) {
my $array = shift->{test_array};
is(pop @$array, 2, 'pop = 2');
is(pop @$array, 1, 'pop = 1');
is_deeply($array, [], 'array empty');
is(pop @$array, undef, 'pop = undef');
};
And I need to add a 5th test case at the end of the test sub. After adding the line I need to edit the number of tests to 5 so I type:
C-r Test RET          -- Reverse search back to Test
M-f M-f Backspace 5 -- Forward 2 words, Replace 4 for 5
C-u C-SPC -- Back to where we were.
And then I'm back where I was. Free to add another test or fix the added test when it doesn't quite work. ;)

While this example is a touch contrived, for bigger jumps ( e.g jumping to the top of a file ) it becomes really useful.

For added coolness, turns out there is also a global mark ring, so you can jump between all the buffers that you are editing as well with C-x C-SPC. Super neat.

Vim also has the concept of marks, called a tagstack; though I don't know if they are as nicely build into all the navigation commands like they are here. Nothing that a bit of vim script wouldn't fix either way.

For more information:
http://www.gnu.org/s/libtool/manual/emacs/Mark-Ring.html
http://www.gnu.org/s/libtool/manual/emacs/Global-Mark-Ring.html#Global-Mark-Ring