通常,一个 Emacs 会话中会存在多个缓冲区。在任意时刻,其中一个会被指定为当前缓冲区(current buffer)—大多数编辑操作都在该缓冲区中进行。绝大多数用于检查或修改文本的原语都会隐式地作用于当前缓冲区(see 文本)。
默认情况下,选中窗口(see 选中窗口)中显示的缓冲区即为当前缓冲区,但并非总是如此:Lisp 程序可以临时将任意缓冲区指定为当前缓冲区以操作其内容,而无需更改屏幕上显示的内容。指定当前缓冲区最基础的函数是 set-buffer。
该函数返回当前缓冲区。
(current-buffer)
⇒ #<buffer buffers.texi>
该函数将 buffer-or-name 设为当前缓冲区。 buffer-or-name 必须是一个已存在的缓冲区或已存在缓冲区的名称。返回值为被设为当前缓冲区的那个缓冲区。
此函数不会在任何窗口中显示该缓冲区,因此用户不一定能看到这个缓冲区。但此后 Lisp 程序将对其进行操作。
当编辑命令返回到编辑器命令循环时,Emacs 会自动对选中窗口中显示的缓冲区调用 set-buffer(see 选中窗口)。这是为了避免混淆:确保 Emacs 读取命令时,光标所在的缓冲区就是该命令作用的缓冲区(see 命令循环)。因此,不应使用 set-buffer 来可视地切换到另一个缓冲区;若要实现该功能,请使用 在窗口中切换到缓冲区 中描述的函数。
编写 Lisp 函数时,不要依赖命令循环的这一行为来在操作后恢复当前缓冲区。编辑命令也可能被其他程序以 Lisp 函数的形式调用,而非仅从命令循环调用;如果子例程不改变当前缓冲区(除非这是该子例程的设计目的),会为调用方带来便利。
若要临时对另一个缓冲区进行操作,请将 set-buffer 置于 save-current-buffer 表单内。以下是命令 append-to-buffer 的简化版本示例:
(defun append-to-buffer (buffer start end)
"Append the text of the region to BUFFER."
(interactive "BAppend to buffer: \nr")
(let ((oldbuf (current-buffer)))
(save-current-buffer
(set-buffer (get-buffer-create buffer))
(insert-buffer-substring oldbuf start end))))
在此示例中,我们绑定一个局部变量来记录当前缓冲区,随后 save-current-buffer 会安排在后续将其恢复为当前缓冲区。接下来,set-buffer 将指定的缓冲区设为当前缓冲区,而 insert-buffer-substring 会把原缓冲区中的字符串复制到指定的(且此时已是当前的)缓冲区中。
此外,也可以使用 with-current-buffer 宏:
(defun append-to-buffer (buffer start end)
"Append the text of the region to BUFFER."
(interactive "BAppend to buffer: \nr")
(let ((oldbuf (current-buffer)))
(with-current-buffer (get-buffer-create buffer)
(insert-buffer-substring oldbuf start end))))
无论采用哪种方式,若被追加内容的缓冲区恰好显示在某个窗口中,下一次重绘将展示其文本的变化。如果该缓冲区未显示在任何窗口中,则无法立即在屏幕上看到变化。该命令仅会临时将该缓冲区设为当前缓冲区,但不会使其显示出来。
若为可能存在缓冲区局部绑定的变量创建局部绑定(通过 let 或函数参数),请确保在局部绑定的作用域开始和结束时,当前缓冲区保持一致。否则可能会在一个缓冲区中绑定变量,却在另一个缓冲区中解除绑定!
不要依赖使用 set-buffer 来切回原当前缓冲区,因为如果在错误的缓冲区为当前状态时发生退出(quit),这种方式将无法完成恢复工作。例如,在之前的示例中,以下写法是错误的:
(let ((oldbuf (current-buffer)))
(set-buffer (get-buffer-create buffer))
(insert-buffer-substring oldbuf start end)
(set-buffer oldbuf))
正如我们之前所做的,使用 save-current-buffer 或 with-current-buffer 能正确处理退出、错误、throw 以及常规求值场景。
save-current-buffer 特殊形式会保存当前缓冲区的标识,求值 body 表单,最后将该缓冲区恢复为当前缓冲区。返回值为 body 中最后一个表单的值。即使发生通过 throw 或错误导致的异常退出(see 非局部退出),当前缓冲区也会被恢复。
如果在从 save-current-buffer 退出时,原本的当前缓冲区已被删除,那么显然无法再将其设为当前缓冲区。这种情况下,退出前的当前缓冲区会保持为当前状态。
with-current-buffer 宏会保存当前缓冲区的标识,将 buffer-or-name 设为当前缓冲区,求值 body 表单,最后恢复原当前缓冲区。buffer-or-name 必须指定一个已存在的缓冲区或已存在缓冲区的名称。
返回值为 body 中最后一个表单的值。即使发生通过 throw 或错误导致的异常退出(see 非局部退出),当前缓冲区也会被恢复。
with-temp-buffer 宏会在临时缓冲区作为当前缓冲区的情况下求值 body 表单。它会保存当前缓冲区的标识,创建一个临时缓冲区并将其设为当前缓冲区,求值 body 表单,最后恢复之前的当前缓冲区并删除该临时缓冲区。
默认情况下,此宏创建的缓冲区中不会记录撤销信息(see 撤销)(但如果需要,body 可以启用该功能)。该临时缓冲区也不会运行钩子函数 kill-buffer-hook、kill-buffer-query-functions(see 杀死缓冲区)以及 buffer-list-update-hook(see 缓冲区列表)。
返回值为 body 中最后一个表单的值。可以将 (buffer-string) 作为最后一个表单,以返回临时缓冲区的内容。
即使发生通过 throw 或错误导致的异常退出(see 非局部退出),当前缓冲区也会被恢复。
另请参见 Writing to Files 中的 with-temp-file。