设定 user option 应该用 setq, setopt, custom-set-default, customize-set-value, 还是 customize-set-variable?

如题. 可能还有其它易混淆的赋值方式没列举出来.

1 个赞

我在 :config 里用 setq,速度最快

但是存在一些情况 setq 无法正确设置值(比如这个变量在加载包的 defvar 的时候被另一个变量引用了)我会设置成在 :init 里面使用 setq

(手机打字,某些函数名可能不太对)

setq 是最直接的,它跳过了 option 的变量类型检查和设置函数

setopt 会使用 option 的设置函数,内部貌似是调用 custom-set-variable

customize-set 之类的函数应该是最“正规”的方法?emacs会在你使用那个设置界面时使用它设定option

就我来看的话,在 emacs 29 中,如果你想要把某些 option 设定写死在配置文件中,就使用 setopt吧,我一般把它放到 use-package 里面的 init,其他通过 customize 界面配置的 option 放在自动生成的代码里就行

但是也并不一定,我遇到过 option 类型与正确值冲突的问题,这时候 setopt 可能不太好用,这时候用setq就行

  • 包加载之前用defvar
  • 包加载之后,如果是plain variable用setq,如果是有set函数的variable,用setopt
  • 或者全无脑在init阶段用setopt

别的api语法有点复杂,我个人基本不用

custom-set-default 不是给用户用来设定变量用的,而是给 defcustom 用的 :set 选项的默认值。

customize-set-valuecustomize-set-variable 是用来 M-x 交互式调用的,提供当前 defcustom 变量的䃼全。后者会用 custom-set,前者不会。

setopt 才是设计用在配置代码里做少量修改的。有 custom-set 时用优先用它,没有就用 set-default,是没有加載变量对应的包时也要使变量生效时用的。它不会去主动 require 对应的包。

setq 现在有编译时对变量是否定义的檢查,不在意的话无所谓,在意就用 setq-default,这个也能全局设定 buffer local variable。setq 在 byte code 里有专门的指令,而 setq-default 只会展开成对 set-default 的 funcall。setq-local 一般不用在配置选项里,不提

custom-set-variables 才是用来让用户批量设置 defcustom 的,等价于 (custom-theme-set-variables 'user ...),后者顾名思意是给 theme 用的。和 setopt 区別是只有在对应的 defcustom 加載时才会生效,确保调用的是正确的 custom-set

use-package 如果 :config:defer t 一起用,那就等于 eval-after-load:ensure t:config 等价于 :init:custom 用的是 (custom-theme-set-variables ʻuse-package ...)

Emacs 的 theme 不只管理 face,也能管理 defcustom,而且能同时打开以组合多个 theme,也能在关闭 theme 时正确重置变量。用 setq/setopt 就没有这个好处了。

8 个赞

看了一下 GNU 的 Emacs Lisp Reference Manual,也提到了这点:

The main use case for this macro is setting user options in the user’s init file.

但感觉即使 setopt 有会保证 option 的 setter function 被执行并且会检查 option 被设定值的合法性(类型是否匹配等)的优点,大部分人的 .emacs.d 还是用的 setq

In particular, setopt will run the setter function associated with the variable.

setopt also checks whether the value is valid for the user option. For instance, using setopt to set a user option defined with a number type to a string will signal an error.


话说这里的 less efficient 指的是哪些方面?

The setopt macro can be used on regular, non-user option variables, but is much less efficient than setq .

Emacs 29 才有这个宏,没普及很正常,以至于 buffer local variable 到 25 才有。 而且大部分变量没有特殊的 custom-set 属性,即设置之后要另外调用函数才能完全生效。

更何况不编译的话 setq 是解释器直接支持的 special form,setopt 还要宏展开。

2 个赞

个人习惯,写在个人配置代码里的几乎都是用 setq。事实上,如果你在加载一个包之前就 setq 这个包里定义的变量,那么 custom 对应的 setter 函数也能够顺利执行。但是如果你在加载这个包之前就 setq,那么这个变量在后期就不能被动态绑定(比如在 let 里面绑定这个变量到另一个值)。只有在需要动态绑定这个变量(因此你必须要在加载这个包之后才设置这个变量),同时这个变量还有对应的 setter 函数,这个时候才有必要使用 setopt,我很少很少遇到过这种情况。

至于 custom-*** 是不会出现在我的配置里的。

只要在 setq 之前 defvar 就行。当然,实际上 defvar 只要在 closure variable capture 之前出现就没有问题,不能动态绑定本质上是代码不规范没有在使用动态作用域变量之前 require 其对应的 defvar 所在的文件或另外声明 defvar 导致的。

额外附一条说明,在变量本身 unbound 的情況下,用不带 initvalue 的 defvar 不会给变量赋值,见 为啥一个无关的局部绑定会引起编译警告?

如果没有对应的 setter,在动态绑定行为上 setopt 的行为和 setqsetq-default 都是一样的,并不会自动让变量变 special,所以你的说法是不准确的。

需要包加载完以后改 custotm 变量,或包的加载状态不固定如 pdumper 和 lazy 都在用的情况还是有的。

(用custom可以带备注