4.4.3 save-excursionappend-to-buffer 中的应用

append-to-buffer 中的 let 表达式的主体由一个 save-excursion 表达式组成。

save-excursion 函数保存 point 的位置,并在 save-excursion 主体表达式执行完成后将其恢复到该位置。此外,save-excursion 还跟踪原始缓冲区并将其恢复,这就是在 append-to-buffer 中使用 save-excursion 的方式。

顺便提一下,值得注意的是,Lisp 函数通常格式化为多行展开的形式,其中所有被包裹在多行展开的内容都比第一个符号缩进得更多。在这个函数定义中,let 的缩进比 defun 大,而 save-excursion 的缩进比 let 大,就像这样:

(defun …
  …
  …
  (let…
    (save-excursion
      …

这种格式约定使得很容易看到 save-excursion 主体中的行是由与 save-excursion 相关的括号括起来的,就像 save-excursion 本身由与 let 相关的括号括起来一样:

(let ((oldbuf (current-buffer)))
  (save-excursion
    …
    (set-buffer …)
    (insert-buffer-substring oldbuf start end)
    …))

使用 save-excursion 函数的方式可以看作是填充模板的过程:

(save-excursion
  主体中的第一个表达式
  主体中的第二个表达式主体中的最后一个表达式)

在这个函数中,save-excursion 的主体只包含一个表达式,即 let* 表达式。你已经了解了 let 函数,而 let* 函数则不同。它允许 Emacs 按顺序设置 varlist 中的每个变量,依次设置在 varlist 较早部分设置的变量的值,以便 varlist 较后部分的变量可以使用 varlist 较早部分设置的值。

观察 append-to-buffer 中的 let* 表达式:

(let* ((append-to (get-buffer-create buffer))
       (windows (get-buffer-window-list append-to t t))
       point)
  BODY...)

我们可以看到,append-to 被绑定到由 (get-buffer-create buffer) 返回的值上。在下一行,append-to 作为参数传递给了 get-buffer-window-list;这在 let 表达式中是不可能的。注意,point 被自动绑定为 nil,就像在 let 语句中一样。

现在让我们专注于 let* 表达式的主体中的 set-bufferinsert-buffer-substring 函数。

在旧版本中,set-buffer 表达式是简单的

(set-buffer (get-buffer-create buffer))

但现在它是

(set-buffer append-to)

这是因为 append-tolet* 表达式中先绑定到了 (get-buffer-create buffer) 的值上。

append-to-buffer 函数定义将文本从当前缓冲区插入到一个命名的缓冲区中。巧合的是,insert-buffer-substring 恰好相反——它从另一个缓冲区复制文本到当前缓冲区——这就是为什么 append-to-buffer 定义以一个 let 开始的原因,该 let 将本地符号 oldbuf 绑定到在执行 append-to-buffer 命令时的 current-buffer 的值上。

insert-buffer-substring 表达式看起来像这样:

(insert-buffer-substring oldbuf start end)

insert-buffer-substring 函数将一个字符串从其第一个参数指定的缓冲区中复制并插入到当前缓冲区。在这种情况下,insert-buffer-substring 的参数是由 let 创建并绑定的变量的值,即 oldbuf 的值,这是在给出 append-to-buffer 命令时的当前缓冲区。

insert-buffer-substring 完成其工作后,save-excursion 将恢复操作到原始缓冲区,而 append-to-buffer 将完成其工作。

以骨架形式编写,主体的工作看起来像这样:

(let (将-current-buffer的值绑定到-oldbuf)
  (save-excursion                       ; 跟踪缓冲区。
    更改缓冲区
    从-oldbuf中插入子串到缓冲区)

  完成时切换回原始缓冲区
  完成时使-oldbuf的局部含义消失

总之,append-to-buffer 的工作方式如下:它保存了当前缓冲区的值在名为 oldbuf 的变量中。它获取新的缓冲区(如果需要,创建一个),并将 Emacs 的注意力切换到它。使用 oldbuf 的值,它将来自旧缓冲区的文本区域插入新缓冲区;然后使用 save-excursion,它将你带回原始缓冲区。

通过查看 append-to-buffer,你已经探索了一个相当复杂的函数。它展示了如何使用 letsave-excursion 以及如何在不同缓冲区之间切换和返回的方法。许多函数定义都以这种方式使用 letsave-excursionset-buffer