在之前的文章中,我们通过with-current-buffer 和 buffer-substring 实现了一个从指定buffer中拷贝文本到本buffer的功能。这个算是我们入门了跨buffer操作。实际上Emacs为这种需求提供了专门的函数
背景知识
Emacs中的跨buffer文本操作的基础函数是 (insert-buffer-substring BUFFER &optional START END) 。它用于将参数中 BUFFER 指定的缓冲区中从 START 到 END 这个范围内的文本插入到当前缓冲区的光标所在位置。我们可以用下面的代码感受一下
(defun my/copy-messages-buffer-to-this()
(interactive)
(let ((message-buffer (get-buffer "*Messages*")))
(when message-buffer
(let ((start)
(end))
(with-current-buffer message-buffer
(save-restriction
(widen)
(setq start (point-min))
(setq end (point-max))))
(insert-buffer-substring message-buffer start end)))))因为 START 和 END 参数实际上是对应的是 BUFFER 中的范围。所以我们需要切入到对应的buffer中去获取这个坐标的最大和最小值。
基于这个函数我们可以实现很多好用的跨buffer的文本操作,例如将当前buffer中的内容拷贝到目标buffer中,或者将目标buffer中的内容替换为我们想要的内容等等。
因为各种跨buffer的文本操作实在是太频繁了,所以Emacs提供很多有用的函数,下面我列举一些常用的函数
append-to-buffer: 从当前buffer中提取START和END范围内的文本并追加到参数BUFFER所指定的缓冲区的光标位置copy-to-buffer: 从当前 buffer 中提取START和END范围内的文本并对参数中BUFFER所指定的缓冲区进行替换
上述这些函数提取的内容都带有文本属性,如果我们只想提取文本本身,可以使用 buffer-substring-no-properties 。
查阅相关文档,我们发现 buffer-substring-no-properties 实际上对标的是 buffer-substring,都是提取当前buffer的文本,而不涉及到改变buffer。在跨buffer的函数中,只有 insert-buffer-substring 是使用底层C函数编写的代码,而其他两个都是依靠这个函数,在lisp层面实现的。所以说,关于跨buffer的文本操作,我们只需要掌握基本的 insert-buffer-substring 其他的函数我们可以自己实现。这有点像数学,上学的时候老师告诉我初中的几何只有三条基本公理,其他的定理和自己利用这三条推导出来。Emacs也是这样,只提供基本的功能,其余高级的功能可以自己利用基本函数实现出来。但是也提供了这些高级函数的官方实现。
练习
今天我们来实现一个日志收集的功能。我们将当前用户选中的行记录到一个名为 Log-Collection 的buffer中,该buffer平时不允许写入,只能通过调用特定的函数进行收集。如果用户选择并激活了一个区间,则提取区间的内容进行激活,否则提取当前所在行的文本。
(defun my/collection-log()
(interactive)
(let ((log-buffer (get-buffer-create "*Log-Collection*"))
(cur-buf (current-buffer))
(start)
(end))
(if (use-region-p)
;; 激活了选区,则按照选取来决定范围
(progn
(setq start (region-beginning))
(setq end (region-end)))
;; 未激活选取,则按照光标所在行来决定范围
(progn
(setq start (line-beginning-position))
(setq end (line-end-position))))
(with-current-buffer log-buffer
(let ((inhibit-read-only t))
(goto-char (point-max))
(insert-buffer-substring cur-buf start end)))))总结
本节介绍的重点函数只有一个 insert-buffer-substring 因为功能导致它的参数比较割裂,BUFFER 是目标buffer、而START 和 END 对应着源buffer的位置。这也就意味着想要成功调用该函数,无论如何都得使用 with-current-buffer 进行buffer的切换,无法一步到位
其余的函数我们可以自己实现,所以就不在这里列举了。
评论 (0)