01 January 2006

While "redo" is almost a "must-have" feature for any serious editor, Emacs seems provide no such functionality. However, actually, Emacs provides a more comprehensive way to "redo". This blog, which was initially written almost 10 years ago, tries to explain that.

Redo by undo "undo"

First, let's check out how to redo in Emacs. Emacs records changes of a file (or, to be acurate) buffer in a ring and undo is to reverse recent changes. This may be nothing special when compared with other editor. The extraodinary idea is that "undo" itself is also a change and therefore is recorded the same as other changes. That is, you simply undo a "undo" to redo.

Here is a detailed example of how to do it.

  1. To begin with, let's assume you have "foo bar" in a buffer.
  2. You changed to "bar foo"
  3. You undo the change and the buffer contains "foo bar" again now.
  4. You change your mind and want to redo.
  5. You hit C-g to abort current undo sequence and the undo you just did is prepended into the undo ring.

    Any command other than an undo command breaks the sequence of undo commands. Starting from that moment, the previous undo commands become ordinary changes that you can undo. Thus, to redo changes you have undone, type `C-f' or any other command that will harmlessly break the sequence of undoing, then type more undo commands. – Emacs Manual

  6. You undo again. This time, since the previous undo is at the head of the undo ring, it was reversed. That is, the file become "foo bar" again.

The advantages of this mechanism

I would not have said it was an extraodinary idea if that is all. In fact, if you examine this mechanism closerly, you might find it enables us to reach any previous state of a buffer.

Let's suppose a buffer change from s1 (state 1) to s2 to s3 at first. Then those changes are reversed and the buffer is back to s1. After that, it change through s4 to s5.

For normal editors, this is something like this:

s1 --------> s2 -----> s3
 \   <-undo     <--undo
   -> s4-> s5 ( no way to back to s3)

Can you see the problem? The issue is we cannot go back to s3.

For Emcas, this is not an issue at all so long as undo-limit is large enough. The same change sequence looks like this in Emacs and you only need to undo 4 times to get back to s3 from s5.

undo list:     edit1    edit2    undo1   undo2     edit3    edit4
text state: s1 ----> s2 ----> s3 -----> s2 -----> s1 ----> s4 ---> s5

Selective undo

除了普通的 undo 外,Emacs 还提供了 "selective undo"。我一直把它叫做 "undo in region"。我写代码的时候常常会在对函数 A 做了修改后又觉得之前在 函数 B(同一文件里)做的改动不好,想要撤销对函数 B 的改动但保留对函数 A 的改动。这个时候就能用上"selective undo" 了。

关于 "selective undo",Emacs manual 的描述同样简洁明了:

Ordinary undo applies to all changes made in the current buffer. You can also perform "selective undo", limited to the current region. To do this, specify the region you want, then run the `undo' command with a prefix argument (the value does not matter): `C-u C-x u' or `C-u C-_'. This undoes the most recent change in the region. To undo further changes in the same region, repeat the `undo' command (no prefix argument is needed). In Transient Mark mode, any use of `undo' when there is an active region performs selective undo; you do not need a prefix argument.

blog comments powered by Disqus