光标与标记

不过在讲解 save-excursion 之前,先回顾一下 GNU Emacs 中的 光标(point)和标记(mark)会很有帮助。光标(point) 就是当前指针所在位置。光标在哪里,point 就在哪里。 更精确地说,在光标显示在某个字符上方的终端上,point 位于该字符紧前方。 在 Emacs Lisp 中,point 是一个整数。缓冲区的第一个字符编号为 1, 第二个为 2,依此类推。函数 point 会以数字形式返回光标当前位置。 每个缓冲区都有自己的 point 值。

标记(mark) 是缓冲区中的另一个位置,可以通过 C-SPCset-mark-command)等命令设置。 如果已经设置了标记,可以使用命令 C-x C-xexchange-point-and-mark)让光标跳转到标记处, 并将标记设为光标原先的位置。此外,如果你设置了新标记, 旧标记的位置会保存在标记环中。可以用这种方式保存多个标记位置。 输入一次或多次 C-u C-SPC 可以让光标跳转到已保存的标记。

缓冲区中光标与标记之间的区域称为选区(region)。 许多命令都作用于选区,包括 center-regioncount-words-regionkill-regionprint-region

save-excursion 这个特殊形式会保存光标位置, 并在 Lisp 解释器对其内部代码求值后恢复该位置。 因此,如果光标原本在一段文本开头,而某段代码将光标移到了缓冲区末尾, 在函数体内的表达式求值完成后,save-excursion 会把光标放回原来的位置。

在 Emacs 中,很多函数在内部工作时会移动光标, 即便用户并不期望如此。例如 count-words-region 就会移动光标。 为了避免用户被这些意外且(从用户角度看)不必要的跳转打扰, 经常使用 save-excursion 让光标停留在用户预期的位置。 使用 save-excursion 是一种良好的编程习惯。

为了保证现场整洁,即使内部代码出现问题(更准确地说, 使用专业术语:“异常退出”时),save-excursion 也会恢复光标位置。这个特性非常实用。

除了记录光标位置,save-excursion 还会记录并恢复当前缓冲区。 这意味着你可以编写切换缓冲区的代码, 并由 save-excursion 切回原始缓冲区。 append-to-buffer 中就是这样使用 save-excursion 的。 (See The Definition of append-to-buffer。)