《elisp 每日一练》—— Major Mode 管理 abbrev-table

《elisp 每日一练》—— Major Mode 管理 abbrev-table

Masimaro
2026-09-20 / 0 评论 / 0 阅读 / 正在检测是否收录...

在前一篇博文中,我们讨论了Emacs中 abbrev-table 的基础用法,现在我们来看看 define-derived-mode 是如何管理 abbrev-table 资源的

背景知识

我们先来回顾一下之前默认展开时它是如何处理 abbrev-table

(progn
  (defvar my/code-mode-abbrev-table)
  (unless (boundp 'my/code-mode-abbrev-table)
    (put 'my/code-mode-abbrev-table 'definition-name 'my/code-mode)
    (defvar my/code-mode-abbrev-table
      (progn
        (define-abbrev-table 'my/code-mode-abbrev-table nil)
          my/code-mode-abbrev-table)))
        (unless (get 'my/code-mode-abbrev-table 'variable-documentation)
          (put 'my/code-mode-abbrev-table 'variable-documentation
            (purecopy "Abbrev table for `my/code-mode'."))))

首先通过 defvar 定义了一个以 mode 名称加上 -abbrev-table 形式的变量名。根据 defvar 的性质,如果我们在外部有这么一个名称的变量,那么它会使用我们定义的变量作为 Major Mode 的 abbrev-table 进行管理。

后续利用 boundp 如果发现该变量名没有绑定值,那么它会自己使用我们在上一节中介绍的函数 define-abbrev-table 定义一个。另外的一些代码就是设置一些属性用于给对应的交互式工具展示信息。

在mode函数中,主要的代码如下:

(unless
  (or (abbrev-table-get my/code-mode-abbrev-table :parents)
    (eq my/code-mode-abbrev-table local-abbrev-table))
  (abbrev-table-put my/code-mode-abbrev-table :parents
    (list local-abbrev-table)))

这段代码仅仅通过我们前面介绍的 PROP属性中的 :parents 设置了继承关系。

根据上面的代码,我们可以得出结论,默认情况下,它是以 mode 名称加上 -abbrev-table 形式的变量名 作为当前Major Mode 的 abbrev-table 进行管理,并且如果在定义时设置了继承关系,那么它会通过 :parents 属性设置 abbrev-table 的继承关系。

实际上,define-derived-mode 也支持通过 :abbrev-table 关键字来进行abbrev-table 的管理,我们同样的以一个例子来展开讨论:

(define-derived-mode my/code-mode prog-mode "MyCode"
  "继承自 prog-mode 的代码主模式"
  :abbrev-table (defvar major-mode-abbrev-table (define-abbrev-table 'majro-mode-abbrev-table nil "自定义 Major Mode 的 abbrev-table")))

我们展开之后会发现,原来有关 abbrev-table 的一切代码都消失了,仅仅留下了

(setq local-abbrev-table
  (defvar major-mode-abbrev-table
    (define-abbrev-table 'majro-mode-abbrev-table nil
      "自定义 Major Mode 的 abbrev-table")))

我觉得可能是开发者觉得既然我们已经通过 :abbrev-table 指定了外部的 abbrev-table,那么对于继承和创建一定有自己的理解,就不需要我接管了。所以这里仅仅帮我们把当前 local-abbrev-table 变量指向了我们自己定义的 abbrev-table。

所以这就给我们提了一个醒,使用 :abbrev-table 实际上是比较危险的行为,作为一个菜鸟,在不具备深入理解 abbrev-table 机制的情况下贸然使用 :abbrev-table 关键字可能会带来意想不到的问题。

所以在处理 abbrev-table 的时候,最好的办法就是定义一个类似名称 abbrev-table 变量,我们只关心要往里面加入哪些对应的映射关系,而不考虑其他,其他内容由 define-derived-mode 自己处理。

小结

本节我们总结了 define-derived-mode 是如何管理 abbrev-table 的。如果不指定 :abbrev-table 关键字,它会默认将 mode 名称 + -abbrev-table 名称的变量作为当前Major Mode 的 abbrev-table 进行管理,主要操作是:

  • 设置文档属性,方便后续用户通过交互式窗口查看它的相关信息
  • 如果没有定义,默认定义一个空表
  • 设置 :parents 的继承关系
  • 更新当前 local-abbrev-table 设置

而如果我们使用了 :abbrev-table 关键,它只会更新 local-abbrev-table 设置。也就意味着一切由我们自己处理,一切问题自己承担。

所以在处理 abbrev-table 最好的办法是不使用 :abbrev-table 关键字,我们只关系表中的映射关系,其余的交给 define-derived-mode 处理。结合我们昨天提到的 edit-abbrevs 编辑方式,我们完全可以放心的把 abbrev-table 交由 define-derived-mode 自己处理

0

评论 (0)

取消