12.12 文件局部变量

文件可以指定局部变量值;Emacs 会利用这些值,在访问该文件的缓冲区中为这些变量创建缓冲区局部绑定。关于文件局部变量的基础信息,可参考 See Local Variables in Files in The GNU Emacs Manual。本节将介绍影响文件局部变量处理逻辑的函数与变量。

若文件局部变量能够指定任意函数或 Lisp 表达式(且这些代码会在后续被调用),那么访问该文件可能会导致你的 Emacs 被恶意控制。为防范此类风险,Emacs 仅会自动设置那些「指定值被判定为安全」的文件局部变量;其余文件局部变量仅会在用户明确同意后才会被设置。

为进一步提升安全性,当 Emacs 读取文件局部变量时,会将 read-circle 临时绑定为 nil(see 输入函数)。这一操作可阻止 Lisp 读取器识别循环型和共享型 Lisp 结构(see 循环对象的读取语法)。

User Option: enable-local-variables

该变量用于控制是否处理文件局部变量。其可选值如下:

t (the default)

设置所有安全变量,并针对所有不安全变量发起一次确认询问。

:safe

仅设置安全变量,且不发起任何询问。

:all

设置所有变量,且不发起任何询问。

nil

不设置任何变量。

anything else

针对所有变量发起一次确认询问。

Variable: inhibit-local-variables-regexps

该变量是一个正则表达式列表。若某个文件的名称匹配此列表中的任意一项,则 Emacs 不会扫描该文件中任何形式的文件局部变量。关于使用该变量的场景示例,可参考 see How Emacs Chooses a Major Mode

Variable: permanently-enabled-local-variables

部分局部变量设置,即便在 enable-local-variables 设为 nil 的情况下,默认仍会生效。默认情况下,仅 lexical-binding 这一项局部变量设置符合此规则;但可通过该变量(一个符号列表)来控制哪些局部变量设置拥有此特性。

Variable: safe-local-variable-directories

该变量是一个目录列表,在这些目录下局部变量会始终启用。从这些目录加载的目录局部变量(例如 .dir-locals.el 文件中的变量),即便存在风险(risky)也会被启用。此列表中的目录必须是完全展开的绝对文件名;若变量 enable-remote-dir-locals 设为非 nil 值,列表中也可包含远程目录。

Function: hack-local-variables &optional handle-mode

该函数会解析当前缓冲区内容中指定的所有局部变量,并根据变量类型进行绑定或求值(以合适的方式)。变量 enable-local-variables 在此处会发挥作用。但该函数不会在 ‘-*- 行中查找 ‘mode:’ 类型的局部变量 —— 这项工作由 set-auto-mode 完成,且该函数同样会考虑 enable-local-variables 的取值(see How Emacs Chooses a Major Mode)。

此函数还会使 .dir-locals.el 文件中指定的目录局部变量生效。若该缓冲区未关联任何文件,则生效的目录局部变量为那些适用于 default-directory 目录下文件的变量(see 目录局部变量)。

该函数的工作机制为:遍历存储在 file-local-variables-alistdir-local-variables-alist 中的关联列表(alist),并依次处理每个局部变量。它会在处理变量之前调用钩子 before-hack-local-variables-hook,在处理变量之后调用钩子 hack-local-variables-hook。仅当 file-local-variables-alist 为非 nil 值时,才会调用前一个钩子;而后一个钩子则始终会被调用。若某个 ‘mode’ 元素指定的主模式与缓冲区当前已启用的主模式一致,该函数会忽略此元素。

这是严格保留 Texinfo 格式、术语统一、可直接用于编译的简体中文翻译: 如果可选参数 handle-modet,那么该函数所做的全部工作就是:若 ‘-*- 行或局部变量列表指定了主模式,则返回一个表示该主模式的符号,否则返回 nil。它不会设置模式或任何其他文件局部变量。 如果 handle-mode 的值既不是 nil 也不是 t,则会忽略 ‘-*- 行或局部变量列表中所有 ‘mode’ 设置,但会应用其他设置。如果 handle-modenil,则会设置所有文件局部变量。

Variable: file-local-variables-alist

这个缓冲区局部变量保存着文件局部变量设置的关联列表(alist)。列表中每个元素的格式为 (var . value),其中 var 是局部变量的符号,value 是它的值。 当 Emacs 打开一个文件时,会先将所有文件局部变量收集到这个关联列表中,然后由 hack-local-variables 函数逐一应用这些变量。

Variable: before-hack-local-variables-hook

Emacs 在即将应用保存在 file-local-variables-alist 中的文件局部变量之前,会立刻调用这个钩子。

Variable: hack-local-variables-hook

Emacs 在应用完保存在 file-local-variables-alist 中的文件局部变量之后,会立刻调用这个钩子。

你可以通过为变量设置 safe-local-variable 属性来指定其安全值。该属性的值必须是一个单参数函数;当传入某个值时,若该函数返回非 nil,则此值即为安全值。许多常见的文件变量都带有 safe-local-variable 属性,包括 fill-columnfill-prefixindent-tabs-mode。对于「值为布尔类型且本身安全」的变量,可将 booleanp 设为此属性的值。

若你希望为在 C 源代码中定义的变量设置 safe-local-variable 属性,需将这些变量的名称及对应的属性添加到 files.el 文件中 “Safe local variables(安全局部变量)” 章节的列表里。

使用 defcustom 定义用户选项时,可通过向 defcustom 添加 :safe function 参数来设置其 safe-local-variable 属性(see 定义自定义变量)。但需注意:通过 :safe 定义的安全谓词,仅在包含该 defcustom 定义的包被加载后才会生效 —— 这往往为时已晚。作为替代方案,你可以使用自动加载标记(see 自动加载)为该选项分配安全谓词,示例如下:

;;;###autoload (put 'var 'safe-local-variable 'pred)

通过 autoload 指定的安全值定义会被复制到包的自动加载文件中(对于 Emacs 自带的大多数包,是 loaddefs.el),并且从会话一开始就对 Emacs 生效。

User Option: safe-local-variable-values

该变量提供了另一种将某些变量值标记为安全的方式。它是一个由 cons 单元 (var . val) 组成的列表,其中 var 是变量名,val 是对该变量而言安全的值。

当 Emacs 询问用户是否采纳一组文件局部变量设置时,用户可以选择将它们标记为安全。这样做会把这些「变量 / 值」对添加到 safe-local-variable-values 中,并保存到用户的自定义文件里。

User Option: ignored-local-variable-values

如果你希望始终完全忽略某些特定局部变量的某些值,可以使用该变量。它的格式与 safe-local-variable-values 相同;在处理文件指定的局部变量时,与列表中出现的值相匹配的文件局部变量设置将始终被忽略。 与上面那个变量一样,当 Emacs 询问用户是否采纳文件局部变量时,用户可以选择永久忽略它们的特定值,这会修改此变量并保存到用户的自定义文件中。出现在本变量中的「变量‑值」对,优先级高于 safe-local-variable-values 中的相同配对。

Function: safe-local-variable-p sym val

基于上述判定标准,若为 sym 赋予值 val 是安全的,则该函数返回非 nil 值。

部分变量会被认定为 高风险(risky) 变量。若一个变量属于高风险类型,它绝不会被自动加入 safe-local-variable-values;除非用户通过直接自定义 safe-local-variable-values 明确允许某个值,否则 Emacs 在设置高风险变量前总会发起询问。

满足以下任一条件的变量会被判定为高风险变量:

  1. 变量名拥有非 nilrisky-local-variable 属性; 使用 defcustom 定义用户选项时,可通过向 defcustom 添加 :risky value 参数来设置该属性(see 定义自定义变量)。
  2. 变量名以以下任一后缀结尾: ‘-command’、‘-frame-alist’、‘-function’、‘-functions’、‘-hook’、‘-hooks’、‘-form’、‘-forms’、‘-map’、‘-map-alist’、‘-mode-alist’、‘-program’ 或 ‘-predicate’;
  3. 变量名为 ‘font-lock-keywords’、‘font-lock-keywords’ 后接数字(如 font-lock-keywords1),或 ‘font-lock-syntactic-keywords’。
Function: risky-local-variable-p sym

如果依据上述判定标准,sym 是一个「有风险的变量」(risky variable), 该函数会返回非 nil 的值

Variable: ignored-local-variables

该变量存储一个变量列表,这些变量不应由文件赋予局部值。 为其中任一变量指定的任何值都会被完全忽略。

Eval:’ “伪变量(variable)” 同样是一个潜在的安全漏洞, 因此 Emacs 在处理它之前通常会要求用户确认。

User Option: enable-local-eval

该变量控制在被访问文件的 ‘-*-’ 行或局部变量列表中, 对 ‘Eval:’ 的处理行为。值为 t 表示无条件处理; 值为 nil 表示忽略;其他任何值表示针对每个文件询问用户如何处理。 默认值为 maybe

User Option: safe-local-eval-forms

该变量存储一个表达式列表,当在文件局部变量列表的 ‘Eval:’ “伪变量(variable)” 中 发现这些表达式时,对其求值是安全的。

如果该表达式是一个函数调用,且该函数具有 safe-local-eval-function 属性,那么该属性的值 将决定这个表达式是否可以安全求值。该属性的值可以是: 一个用于测试表达式的断言函数(predicate)、一组此类断言函数的列表 (只要任一断言函数判定成功即视为安全),或 t (只要参数为常量则始终安全)。

文本属性(text properties)同样是潜在的安全漏洞,因为其值 可能包含待调用的函数。因此 Emacs 会丢弃文件局部变量中指定的 字符串值所附带的所有文本属性。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike