在emacs中,如何在不运行保存钩子的情况下保存?

27

我在我的'before-save-hook中设置了各种东西。例如,我运行'delete-trailing-whitespace。这是我几乎所有情况下想要的。

但有时,我正在处理与其他人共享的文件,文件已经有大量尾随空格。如果我保存文件,我将得到一个很大的差异,这很令人困惑,因为我的更改被埋藏在数十个或数百个无意义的更改中。是的,每个人都可以告诉他们的差异工具不显示空格更改,但这是每次查看差异时每个人都必须做的事情。我宁愿没有空格变化。

除了使用没有init.el文件的新Emacs实例或具有不带hook的修改后的init.el之外,我是否可以执行不带空格更改的文件保存操作?


5
由于你遇到的确切原因,使用 before-save-hook 剥离行末空格只是一个不好的想法。相反,你应该使用像 ws-trimws-butler 这样的工具,仅从你自己编辑过的行中去除空格。 - phils
@zck:我使用Emacs的ethan-ws-mode,它非常好用:尾随空格会以红色显示。我不会关注已经存在的空格,但是如果我正在创建新的空格,我会立即看到。然后,每当我检查某个文件并且ethan-ws-mode显示了许多尾随空格时,我可以告诉我的同事:“请停止提交源代码中任意散布的空格”。所以,我使用一种被动的方法,而不是使用激进的钩子:它只是显示这些令人讨厌的空格,然后由我决定如何处理它们。 - TacticalCoder
1
快速且简单,使用其他编辑器进行修改。 - Vasantha Ganesh
6个回答

24
这是我如何保存而不触发“delete-trailing-whitespace”命令的方法: C-x C-q C-x C-s C-x C-q:只读,保存,还原只读。

1
在GNU Emacs 26.1中对我无效。尝试保存时收到“缓冲区是只读”的消息,它无法保存。 - AAAfarmclub
3
绝对是最有用的答案! - Simon Edlund

16
我想到的一个更简单的解决方案是,我的fundamental-mode没有安装任何钩子(hooks),因为我希望它尽可能地简单。因此,如果我想要在不运行钩子(hooks)的情况下保存文件,我暂时切换到fundamental-mode模式。

1
这个不起作用。在Emacs 27中,保存钩子仍然在fundamental-mode中运行。 - user3827326

5

根据与@Stefan的评论讨论,这里提供两个可能的(未经测试的)解决方案:

使用let

(defun save-buffer-without-dtw ()
  (interactive)
  (let ((b (current-buffer)))   ; memorize the buffer
    (with-temp-buffer ; new temp buffer to bind the global value of before-save-hook
      (let ((before-save-hook (remove 'delete-trailing-whitespace before-save-hook))) 
        (with-current-buffer b  ; go back to the current buffer, before-save-hook is now buffer-local
          (let ((before-save-hook (remove 'delete-trailing-whitespace before-save-hook)))
            (save-buffer)))))))

使用unwind-protect
(defun save-buffer-without-dtw ()
  (interactive)
  (let ((restore-global
         (memq 'delete-trailing-whitespace (default-value before-save-hook)))
        (restore-local
         (and (local-variable-p 'before-save-hook)
              (memq 'delete-trailing-whitespace before-save-hook))))
    (unwind-protect
         (progn
           (when restore-global
             (remove-hook 'before-save-hook 'delete-trailing-whitespace))
           (when restore-local
             (remove-hook 'before-save-hook 'delete-trailing-whitespace t))
           (save-buffer))
      (when restore-global
        (add-hook 'before-save-hook 'delete-trailing-whitespace))
      (when restore-local
        (add-hook 'before-save-hook 'delete-trailing-whitespace nil t)))))

第二种解决方案的问题是,before-save-hook 中函数的顺序可能会改变。

4

我根据Nicholas Douma的解决方案编写了一个命令。

(defun olav-save-buffer-as-is ()
  "Save file \"as is\", that is in read-only-mode."
  (interactive)
  (if buffer-read-only
      (save-buffer)
    (read-only-mode 1)
    (save-buffer)
    (read-only-mode 0)))

4
这里有另一种解决方案:
(defvar my-inhibit-dtw nil)
(defun my-delete-trailing-whitespace ()
  (unless my-inhibit-dtw (delete-trailing-whitespace)))
(add-hook 'before-save-hook 'my-delete-trailing-whitespace)

然后

(defun my-inhibit-dtw ()
  (interactive)
  (set (make-local-variable 'my-inhibit-dtw) t))

因此,您可以在不想修剪空格的缓冲区中使用M-x my-inhibit-dtw RET


我已经测试过了,它可以正常工作。我认为这是定义我的钩子最好的解决方案,因为它可以保持钩子的顺序而不需要将它们删除和添加回来。 - miguelmorin

1
我们需要做的是从before-save-hook中删除'delete-trailing-whitespace,保存缓冲区,然后再添加回去。
这段代码将执行此操作,但仅在存在时才会删除和添加。
;; save the buffer, removing and readding the 'delete-trailing-whitespace function
;; to 'before-save-hook if it's there
(defun save-buffer-no-delete-trailing-whitespace ()
  (interactive)
  (let ((normally-should-delete-trailing-whitespace (memq 'delete-trailing-whitespace before-save-hook)))
    (when normally-should-delete-trailing-whitespace
      (remove-hook 'before-save-hook 'delete-trailing-whitespace))
    (save-buffer)
    (when normally-should-delete-trailing-whitespace
      (add-hook 'before-save-hook 'delete-trailing-whitespace))))
(global-set-key (kbd "C-c C-s") 'save-buffer-no-delete-trailing-whitespace)

它还将命令绑定到(kbd C-c C-s),以方便使用。

你需要使用 condition-case 来确保即使 save-buffer 失败,也能恢复 before-save-hook。或者(更好的方法!)可以在 let 中绑定 before-save-hook(let ((before-save-hook (remove 'delete-trailing-whitespace before-save-hook))) (save-buffer)) - sds
@sds:你的let技巧看起来很不错,但如果在缓冲区本地添加了before-save-hook,它将失败(在这种情况下,delete-trailing-whitespace将在(default-value 'before-save-hook)中,但不在before-save-hook的值中)。 - Stefan
@sds:那不是问题。问题在于(memq 'delete-trailing-whitespace before-save-hook)将返回nil,因为delete-trailing-whitespace仅在挂钩的全局部分(即在(default-value 'before-save-hook)中)存在。 - Stefan
@sds:可能会起作用,是的。当然,在这个阶段,“unwind-proptect + remove-hook + add-hook”看起来比较有吸引力。 - Stefan
@Stefan:请看一下我对这个问题的回答,并在那里发表评论。 - sds
显示剩余6条评论

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