15.4.5 定义新类型

在前面的章节中,我们介绍了如何为 defcustom 构造复杂的类型规范。 某些情况下,你可能希望给这样的类型规范赋予一个名称。 最典型的场景是:当你为多个用户选项使用 相同类型 时—— 不必在每个选项中重复书写类型规范,你可以为类型规范命名, 然后在每个 defcustom 中直接使用该名称。 另一种场景是:用户选项的值是 递归数据结构。 为了让数据类型能够引用自身,它必须拥有名称。

由于自定义类型是通过界面组件(widget)实现的, 定义新自定义类型的方式就是定义一个新组件。 我们不会在这里详细描述组件接口,相关内容请参见 Introduction in The Emacs Widget Library。 我们将通过一个简单示例,演示定义新自定义类型所需的**最小功能集**。

(define-widget 'binary-tree-of-string 'lazy
  "A binary tree made of cons-cells and strings."
  :offset 4
  :tag "Node"
  :type '(choice (string :tag "Leaf" :value "")
                 (cons :tag "Interior"
                       :value ("" . "")
                       binary-tree-of-string
                       binary-tree-of-string)))

(defcustom foo-bar ""
  "Sample variable holding a binary tree of strings."
  :type 'binary-tree-of-string)

用于定义新组件的函数名为 define-widget。 第一个参数是我们要作为新组件类型的符号; 第二个参数是代表已有组件的符号,新组件将基于与该现有组件的差异来定义。 对于定义新自定义类型的用途来说,lazy 组件非常合适, 因为它接受一个 :type 关键字参数,其语法与同名的 defcustom 关键字参数一致。 第三个参数是新组件的文档字符串, 你可以通过命令 M-x widget-browse RET binary-tree-of-string RET 查看该字符串。

在这些必选参数之后是关键字参数。 其中最重要的是 :type,它描述此组件要匹配的数据类型。 在本例中,binary-tree-of-string 被定义为: 要么是一个字符串,要么是一个 cons 单元, 且其 car 和 cdr 本身又都是 binary-tree-of-string。 注意这里对**正在定义中的组件类型**的引用。 :tag 是另一个重要的关键字参数,因为我们的新组件使用了 lazy 组件。 默认情况下,lazy 组件没有标签, 如果缺少标签,自定义缓冲区会显示整个组件的值(即正在自定义的用户选项的值)。 这通常不是合适的做法,因此我们为 binary-tree-of-string 组件提供了一个名称字符串。 :offset 参数用于确保子节点相对于父节点缩进 4 个空格, 让树结构在自定义缓冲区中清晰可见。

后面的 defcustom 展示了如何将新组件当作普通自定义类型使用。

lazy 这个名字的由来是: 其他复合组件在缓冲区中实例化时,会将其下层组件转换为内部形式。 这种转换是递归的,下层组件会继续转换 它们 的下层组件。 如果数据结构本身是递归的,这种转换就会造成**无限递归**。 lazy 组件可以避免这种递归:它只在需要时才转换其 :type 参数。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike