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: /* 删除START和END之间的文本并返回它。 */)
  (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 的实现方式。

XFIXNUM’ 是一个C宏,从较长的比特集中提取相关整数;类型比特被丢弃。

delete-and-extract-region 中的命令如下:

del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);

它删除开始位置 start 和结束位置 end 之间的区域。

从编写Lisp的人的角度来看,Emacs非常简单;但是在底层隐藏着大量的复杂性,以使一切正常工作。