Emacs: before-save-hook是局部变量吗?

12

我应该如何解决这个问题?

我在我的中添加了delete-trailing-whitespacebefore-save-hook,但是似乎delete-trailing-whitespace被调用了所有文件,而不仅仅是使用c-mode和衍生模式的缓冲区。

我能让before-save-hook局部于缓冲区吗?


1
还有一个我使用的ws-trim:ftp://ftp.lysator.liu.se/pub/emacs/ws-trim.el - Joe Casadonte
1
一个钩子是否是缓冲区本地的并不是正确的问题:所有的钩子都是。问题在于你添加到它的函数是添加到缓冲区本地部分还是全局部分(这取决于你传递给add-hooklocal参数)。 - Stefan
5个回答

21

将其添加到write-contents-functions中:

(add-hook 'c-mode-common-hook
  (lambda()
    (add-hook 'write-contents-functions
      (lambda()
        (save-excursion
          (delete-trailing-whitespace)))
      nil t)))

正如Emacs Lisp参考手册所解释的那样:
“这与write-file-functions的工作方式相同,但它旨在针对缓冲区内容而不是特定访问文件或其位置的挂钩。这些挂钩通常由主要模式设置为此变量的缓冲区本地绑定。每当设置它时,此变量自动变为缓冲区本地; 切换到新的主要模式始终会重置此变量,但调用set-visited-file-name却不会。”
这在我的Emacs 24.2.1中有效(即,它删除C文件中的所有尾随空格,但保留所有其他文件类型中的尾随空格)。

谢谢,我已经在EmacsWiki上纠正了相应的错误,并指向这里以获得认可。 - Dave Abrahams
1
(几乎)所有的钩子都是本地和全局的。只需使用add-hooklocal参数来指定您想要全局添加函数还是缓冲区本地添加函数。自Emacs-22.1以来,local-write-file-hooks已经过时,write-file-hooks也已经过时(被write-file-functions替换),因此您可能需要更新参考手册。 - Stefan

21
不,变量before-save-hook并非自然缓冲区本地。该变量的文档没有说明它是缓冲区本地的,也没有说在设置时会自动变成缓冲区本地。
如果您想为其添加一个缓冲区本地钩子,正确的方法是使用标准的add-hook函数的可选LOCAL参数:
(add-hook 'before-save-hook 'foo nil t)

add-hook文档中提到:
可选的第四个参数LOCAL,如果非空,则表示修改hook的局部值而不是它的全局值。这使hook成为了局部缓冲区,并让t成为局部值的一个成员。这起到标记的作用,以便运行全局值的hook函数以及局部值的hook函数。
我认为将其添加到local-write-file-hooks是错误的选择。如果你查看emacs 24.3的那个函数的文档,会发现该变量自22.1版本以来已经过时,应改用write-file-functions。如果你查找write-file-functions的文档,它描述了更复杂的行为,并在结尾处说:“要在缓冲区保存之前执行各种检查或更新,请使用before-save-hook”。

2
有一个问题,我甚至会说这是关于LOCAL参数的一个错误。你看,使用非nil的LOCAL调用会创建新的空变量,然后将其添加到您的钩子中。也就是说,如果您有任何其他钩子,它们将在缓冲区中消失。因此,您可能希望使用make-local-variable,然后使用普通的add-hook而不带任何其他参数。 - Hi-Angel

3

以前从未想过要这样做,但这应该可以起作用:

(set (make-local-variable 'before-save-hook) '((lambda() (rg-msg "foobie")))) 

一般情况下,输入C-h v将提示输入变量名,并显示描述,告诉您该变量是否为缓冲区本地变量。

before-save-hook是一个在`files.el'中定义的变量。它的值为nil。

当作为文件本地变量使用时,此变量具有潜在风险。

文档:在将缓冲区保存到其文件之前运行的常规钩子。

您可以自定义此变量。

对比:

next-error-function是一个在`simple.el'中定义的变量。它的值为nil。

当以任何方式设置时,会自动变成缓冲区本地变量。当作为文件本地变量使用时,此变量具有潜在风险。

文档:用于查找当前缓冲区中的下一个错误的函数。该函数使用2个参数调用:

[...]


2
我不确定这段代码在编写时是否正确,但当前的文档说明中指出:“不要使用make-local-variable将钩子变量设为缓冲区局部变量。相反,使用add-hook并为LOCAL参数指定t。” - phils
make-local-variable 函数对于钩子的弃用可以追溯到 Emacs-21。 - Stefan

2

是的,在您的项目根目录中创建一个.dir-locals.el文件,并将其内容设置为以下内容:

((c-mode . ((before-save-hook . (lambda() (delete-trailing-whitespace)) )) ))

这将仅将此钩子添加到该目录下的c-mode缓冲区。

但是如果您只想要一个特定的文件而不是整个目录,则应该能够使用文件本地变量将其添加到文件顶部:

-*- eval: (setq before-save-hook (lambda() (delete-trailing-whitespace))); -*-

文件的顶部或底部可以像这样添加内容:
;;; Local Variables: ***
;;; eval: (setq before-save-hook (lambda() (delete-trailing-whitespace))) ***
;;; End: ***

0
请使用write-contents-function代替:
write-contents-functions is a variable defined in `files.el'.
Its value is nil

  Automatically becomes buffer-local when set in any fashion.

Documentation:
List of functions to be called before writing out a buffer to a file.
If one of them returns non-nil, the file is considered already written
and the rest are not called and neither are the functions in
`write-file-functions'.

This variable is meant to be used for hooks that pertain to the
buffer's contents, not to the particular visited file; thus,
`set-visited-file-name' does not clear this variable; but changing the
major mode does clear it.

For hooks that _do_ pertain to the particular visited file, use
`write-file-functions'.  Both this variable and
`write-file-functions' relate to how a buffer is saved to file.
To perform various checks or updates before the buffer is saved,
use `before-save-hook'.

你应该创建一个包装器来调用delete-trailing-whitespace,因为你希望确保从包装器中返回nil,以便进行进一步处理(和最终保存)。

2
为什么应该使用write-contents-functions而不是local-write-file-hooks? - Dave Abrahams

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接