copy-region-as-kill 函数(see copy-region-as-kill)使用了 filter-buffer-substring 函数,
而后者又调用了 delete-and-extract-region 函数。
该函数会移除选区内容,且无法恢复。
与本节讨论的其他代码不同,
delete-and-extract-region 函数并非由 Emacs Lisp 编写,
而是由 C 语言实现,是 GNU Emacs 系统的原语之一。
由于它非常简单,我将暂时脱离 Lisp 主题,在此对其进行说明。
与许多其他 Emacs 原语一样,
delete-and-extract-region 以 C 宏的形式实现,
宏是一段代码模板。完整的宏定义如下:
DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
Sdelete_and_extract_region, 2, 2, 0,
doc: /* Delete the text between START and END and return it. */)
(Lisp_Object start, Lisp_Object end)
{
validate_region (&start, &end);
if (XFIXNUM (start) == XFIXNUM (end))
return empty_unibyte_string;
return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);
}
我们不深入宏的编写细节,
只需注意该宏以 DEFUN 开头。
选择 DEFUN 这个名称,
是因为其作用与 Lisp 中的 defun 相同。
(DEFUN 这个 C 宏定义在 emacs/src/lisp.h 中。)
DEFUN 之后的括号内包含七个部分:
delete-and-extract-region。
Fdelete_and_extract_region。
按照惯例,它以 ‘F’ 开头。
由于 C 语言不允许名称中使用连字符,因此改用下划线。
interactive 声明后的参数:
一个字母,可附带提示信息。
与 Lisp 唯一的区别是,当宏不接收参数时,
此处写 0(表示空字符串),本例即是如此。
如果需要指定参数,需将其放在引号内。
例如 goto-char 对应的 C 宏在此处填写
"NGoto char: ",表示该函数接收一个原始前缀参数
(此处为缓冲区中的数值位置),并提供提示信息。
lib-src/make-docfile
会提取这些注释并生成文档。)
在 C 宏中,接下来是形参及其类型声明,
然后是宏的主体。
delete-and-extract-region 的主体包含以下四行:
validate_region (&start, &end); if (XFIXNUM (start) == XFIXNUM (end)) return empty_unibyte_string; return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);
validate_region 函数会检查
作为选区起始与结束传入的值是否类型正确、范围合法。
如果起始与结束位置相同,则返回空字符串。
del_range_1 函数负责实际删除文本,
它是一个复杂函数,我们不深入探究,
它会更新缓冲区并执行其他操作。
不过值得关注传递给 del_range_1 的两个参数:
XFIXNUM (start) 与 XFIXNUM (end)。
从 C 语言角度来看,start 和 end
是两个标记待删除选区起止位置的不透明值。
更精确地说(需要更专业的知识才能理解),
这两个值的类型为 Lisp_Object,
它可以是 C 指针、C 整数或 C 结构体;
C 代码通常无需关心 Lisp_Object 的具体实现。
Lisp_Object 的宽度取决于机器,
通常为 32 位或 64 位。
其中少数几位用于标识信息类型,
其余位用于存储内容。
‘XFIXNUM’ 是一个 C 宏, 它从较长的位串中提取出对应的整数, 并丢弃类型标识位。
delete-and-extract-region 中的关键语句如下:
del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);
它会删除起始位置 start
与结束位置 end 之间的选区内容。
从编写 Lisp 代码的开发者视角来看,Emacs 非常简单; 但在底层,为了让一切正常运行,隐藏了大量复杂逻辑。