在程序局部范围内临时移动点的位置通常很有用。这称作 临时移动(excursion),通过 save-excursion 特殊形式实现。该结构会记录当前缓冲区的初始标识及其点位置,并在临时移动结束后恢复。这是在程序某部分移动点且不影响其余部分的标准方式,在 Emacs 的 Lisp 源码中被大量使用。
若只需保存并恢复当前缓冲区标识,可改用 save-current-buffer 或 with-current-buffer(see 当前缓冲区)。若需保存或恢复窗口配置,参见 窗口配置 与 框架配置 中介绍的结构。
该特殊形式保存当前缓冲区标识及其点位置,执行 body,最后恢复缓冲区与点的位置。即使通过 throw 或错误异常退出,两处保存的值仍会被恢复(see 非局部退出)。
save-excursion 的返回值为 body 中最后一个表达式的结果,若无表达式则返回 nil。
由于 save-excursion 仅保存临时移动开始时当前缓冲区的点位置,临时移动期间对其他缓冲区中点的修改会持续生效。这常导致意外结果,因此字节编译器会对在临时移动中调用 set-buffer 的行为发出警告:
Warning: Use ‘with-current-buffer’ rather than
save-excursion+set-buffer
为避免此类问题,应在设置好目标当前缓冲区后再调用 save-excursion,如下例:
(defun append-string-to-buffer (string buffer)
"Append STRING to the end of BUFFER."
(with-current-buffer buffer
(save-excursion
(goto-char (point-max))
(insert string))))
同样,save-excursion 不会恢复由 switch-to-buffer 等函数改变的窗口—缓冲区对应关系。
警告:在保存的点位置附近普通插入文本会使保存的点位置发生偏移,与所有标记行为一致。更准确地说,保存的值是一个插入类型为 nil 的标记。See 标记插入类型。因此恢复保存的点位置时,它通常位于插入文本之前。
该宏与 save-excursion 类似,但额外保存并恢复标记位置与 mark-active。该宏实现了 Emacs 25.1 之前 save-excursion 的原有行为。