31.3 临时移动

在程序局部范围内临时移动点的位置通常很有用。这称作 临时移动(excursion),通过 save-excursion 特殊形式实现。该结构会记录当前缓冲区的初始标识及其点位置,并在临时移动结束后恢复。这是在程序某部分移动点且不影响其余部分的标准方式,在 Emacs 的 Lisp 源码中被大量使用。

若只需保存并恢复当前缓冲区标识,可改用 save-current-bufferwith-current-buffer(see 当前缓冲区)。若需保存或恢复窗口配置,参见 窗口配置框架配置 中介绍的结构。

Special Form: save-excursion body…

该特殊形式保存当前缓冲区标识及其点位置,执行 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 标记插入类型。因此恢复保存的点位置时,它通常位于插入文本之前。

Macro: save-mark-and-excursion body…

该宏与 save-excursion 类似,但额外保存并恢复标记位置与 mark-active。该宏实现了 Emacs 25.1 之前 save-excursion 的原有行为。