9

following line removes all training white space while saving.

(add-hook 'write-file-hooks 'delete-trailing-whitespace)

but I want to hook this feature only when i'm in programming mode, so i did

(defun nuke_traling ()
  (add-hook 'write-file-hooks 'delete-trailing-whitespace) 
)

(add-hook 'prog-mode-hook 'nuke_traling)

which doesn't is not stopping which are not in programming mode.

rho
  • 771
  • 9
  • 24

6 Answers6

21

Making the hook variable buffer-local has been mentioned. Don't do that. Or rather, don't do it using make-local-variable.

The normal hook mechanisms have buffer-local support built in -- that's the purpose of the LOCAL argument to add-hook. When the hook is run, it runs both the global and the buffer-local values.

So taking the example code in the question, you could change it to use:

(add-hook 'write-file-hooks 'delete-trailing-whitespace nil t)

And then delete-trailing-whitespace would be called whenever write-file-hooks was run, but only in the buffers in which prog-mode-hook had run.

However there are better ways to achieve this.

I agree with Drew that you are better to test whether your mode is derived from prog-mode, and with juanleon that before-save-hook is a better hook to use. So you might do something like:

(add-hook 'before-save-hook 'my-prog-nuke-trailing-whitespace)

(defun my-prog-nuke-trailing-whitespace ()
  (when (derived-mode-p 'prog-mode)
    (delete-trailing-whitespace)))

But what I actually recommend is using either ws-trim or ws-butler to take care of this in a smarter way.

Blindly removing all trailing whitespace from a file is a great way to wind up committing loads of unrelated lines to a version-control repository. Both of the libraries mentioned will ensure that your own commits are free of trailing whitespace, without also introducing unwanted modifications elsewhere in the file.

Nifle
  • 11,745
  • 10
  • 75
  • 100
phils
  • 71,335
  • 11
  • 153
  • 198
7

write-file-hooks is obsolete since Emacs-22, replaced by write-file-functions. But this hook is a bit delicate to use (because it can also be used to perform the write), so I recommend you use before-save-hook instead. And to make it apply only to the current buffer, just pass a non-nil value for the local argument of add-hook, as in:

(defun nuke_traling ()
  (add-hook 'before-save-hook #'delete-trailing-whitespace nil t))
(add-hook 'prog-mode-hook #'nuke_traling)
Stefan
  • 27,908
  • 4
  • 53
  • 82
1

Yes, because as soon as you enter a prog-mode mode, you add the function to write-file-hooks, where it remains. And that hook applies to writing any file, regardless of the mode of its buffer.

Instead of putting that simple function on the hook, you can add a function that tests the mode and only does the whitespace deletion when it is a mode where you want to do that.

Or else you would need to make write-file-hooks buffer-local (which I doubt you would want want to do --- the hook is used more generally).

Drew
  • 29,895
  • 7
  • 74
  • 104
  • "Make a hook buffer-local" is an obsolete concept since Emacs-21. All hooks have a global and a buffer-local part. You just need to tell `add-hook` which part you want to modify. – Stefan Oct 04 '13 at 14:12
1

Bad way:

(add-to-list 'write-file-functions 'delete-trailing-whitespace)

Better way is using ws-butler:

(straight-use-package 'ws-butler)
(add-hook 'prog-mode-hook #'ws-butler-mode)

ws-butler-mode remove spaces only on changed lines.

Dunaevsky Maxim
  • 3,062
  • 4
  • 21
  • 26
0

You would need to make the variable buffer local:

(defun nuke_traling ()
    (make-variable-buffer-local 'write-file-hooks)
    (add-hook 'write-file-hooks 'delete-trailing-whitespace))

But I would recommend using before-save-hook instead:

(defun nuke_traling ()
    (add-to-list 'before-save-hook 'delete-trailing-whitespace))

write-file-hooks may be risky if used as a file-local variable, and documentation recomends using before-save-hook instead for thing like you want to do.

juanleon
  • 9,220
  • 30
  • 41
  • 1
    `make-variable-buffer-local` should pretty much never be called from a function. You probably meant `make-local-variable`. But that should not be used on hooks. Instead you want to use the `local` argument of `add-hook`. – Stefan Oct 04 '13 at 14:14
-1

In emacs 21 or later you can add this hook to a perticular mode like this:

(add-hook 'prog-mode-hook
                (lambda () (add-to-list 'write-file-functions 'delete-trailing-whitespace)))
3r1k
  • 1