8.4 穿插介绍 C 语言

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 之后的括号内包含七个部分:

在 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 语言角度来看,startend 是两个标记待删除选区起止位置的不透明值。 更精确地说(需要更专业的知识才能理解), 这两个值的类型为 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 非常简单; 但在底层,为了让一切正常运行,隐藏了大量复杂逻辑。