5.2.5 insert-buffer 中的 let 表达式

在确保变量 buffer 指向缓冲区对象而非名称之后,insert-buffer 继续执行一个 let 表达式。它声明了三个局部变量 startendnewmark,并将它们初始值绑定为 nil。这些变量在 let 剩余部分中使用,并会临时屏蔽 Emacs 中其他同名变量,直到 let 结束。

let 的函数体包含两个 save-excursion 表达式。我们先详细看内层的 save-excursion,表达式如下:

(save-excursion
  (set-buffer buffer)
  (setq start (point-min) end (point-max)))

表达式 (set-buffer buffer) 将 Emacs 的操作目标从当前缓冲区切换到待复制文本的源缓冲区。在该缓冲区中,通过 point-minpoint-max 命令,将变量 startend 分别设为缓冲区开头和结尾。这里也展示了 setq 如何在同一个表达式中设置两个变量:第一个参数被设为第二个参数的值,第三个参数被设为第四个参数的值。

内层 save-excursion 的函数体执行完毕后,save-excursion 会恢复原始缓冲区,但 startend 仍然保留着源缓冲区开头和结尾的值。

外层 save-excursion 表达式如下:

(save-excursion
  (inner-save-excursion-expression
     (go-to-new-buffer-and-set-start-and-end)
  (insert-buffer-substring buffer start end)
  (setq newmark (point)))

insert-buffer-substring 函数从 bufferstartend 标记的区域复制文本插入到当前缓冲区。由于源缓冲区的全部内容都在 startend 之间,因此整个源缓冲区都会被复制到正在编辑的缓冲区。随后,光标位置(此时位于插入文本的末尾)被记录到变量 newmark 中。

外层 save-excursion 的函数体执行完毕后,光标会恢复到原来的位置。

不过,更方便的做法是在新插入文本的末尾设置标记,并将光标留在开头。变量 newmark 记录了插入文本的末尾位置。在 let 表达式的最后一行,(push-mark newmark) 在此位置设置了一个标记。(原标记位置仍然可用,它被保存在标记环中,可以通过 C-u C-SPC 返回。)与此同时,光标位于插入文本的开头,也就是调用插入函数之前的位置,该位置由第一个 save-excursion 保存。

整个 let 表达式如下:

(let (start end newmark)
  (save-excursion
    (save-excursion
      (set-buffer buffer)
      (setq start (point-min) end (point-max)))
    (insert-buffer-substring buffer start end)
    (setq newmark (point)))
  (push-mark newmark))

append-to-buffer 一样,insert-buffer 也使用了 letsave-excursionset-buffer,此外还展示了 or 的一种用法。这些函数都是常用的基础模块,后续会反复出现和使用。