tags:

views:

1447

answers:

4

I have the following in my .emacs file:

 (defun c++-mode-untabify ()
   (save-excursion
     (goto-char (point-min))
     (while (re-search-forward "[ \t]+$" nil t)
       (delete-region (match-beginning 0) (match-end 0)))
     (goto-char (point-min))
     (if (search-forward "\t" nil t)
         (untabify (1- (point)) (point-max))))
   nil)

 (add-hook 'c++-mode-hook
           '(lambda ()
              (make-local-hook 'write-contents-hooks)
              (add-hook 'write-contents-hooks 'c++-mode-untabify)))

Mostly ripped off from http://www.jwz.org/doc/tabs-vs-spaces.html. This causes emacs to run untabify on the buffer before saving a C++ file.

The problem is that after I have loaded a C++ file, the untabify hook is being applied to all subsequent file writes, even for buffers of other file types. This means that if I open a C++ file and then edit, say, a tab-delimited text file, the tabs get clobbered when saving the file.

I'm not an elisp guru, but I think the (make-local-hook 'write-contents-hooks) line is trying to make the addition to write-contents-hooks apply only to the local buffer. However, it isn't working, and c++-mode-untabify is in write-contents-hooks for all buffers.

I'm using EmacsW32 22.0 on a Windows XP box. Does anyone have any idea how to make the write-contents-hooks change local to a specific buffer or how to reset it to nil when switching to other, non-C++ buffers?

+3  A: 

The documentation in my Emacs says that make-local-hook is now obsolete as of 21.1, since add-hook now takes an optional argument for making a hook buffer-local. So you could try:

(add-hook 'c++-mode-hook
           '(lambda ()
              (add-hook 'write-contents-hooks 'c++-mode-untabify nil t)))

Another option is to have the c++-mode-untabify function check the current mode. I'd probably just write that as something like:

(defun c++-mode-untabify ()
  (if (string= (substring mode-name 0 3) "C++")
      (save-excursion
       (delete-trailing-whitespace)
       (untabify (point-min) (point-max)))))
Boojum
+1  A: 

Try adding your hook like this:

 (add-hook 'c++-mode-hook
           '(lambda ()
              (add-hook 'write-contents-hooks 'c++-mode-untabify nil t)))

Notice the two extra aguments to add-hook. If I'm reading it right, according to the documentation that trailing t should do what make-local-hook does (or visa versa), but I've also seen references to make-local-hook being deprecated. At least on my box (linux, gnu emacs 21.3.1) I'm seeing the effect you want -- the hook is only attached to c++ buffers and not all the others.

As a last resort you could always put a check in your c++-mode-untabify function to only do it's magic if the current major mode is c++-mode.

This is a curious solution, however. If you remove all the tabs, what are you doing to put the tabs back in? If you stop whatever that is you won't need this hack.

Bryan Oakley
Thanks! I put this in to automatically remove tabs from older code that I didn't write but occasionally have to maintain,
DanielMcP
+7  A: 

write-contents-hooks is also obsolete. This is what you're after:

(add-hook 'c++-mode-hook
      '(lambda ()
         (add-hook 'before-save-hook
                   (lambda ()
                     (untabify (point-min) (point-max))))))

This is distilled from what I use, which does a few other things and is abstracted out to work with programming-specific modes:

(defun untabify-buffer ()
  "Untabify current buffer"
  (interactive)
  (untabify (point-min) (point-max)))

(defun progmodes-hooks ()
  "Hooks for programming modes"
  (yas/minor-mode-on)
  (add-hook 'before-save-hook 'progmodes-write-hooks))

(defun progmodes-write-hooks ()
  "Hooks which run on file write for programming modes"
  (prog1 nil
    (set-buffer-file-coding-system 'utf-8-unix)
    (untabify-buffer)
    (copyright-update)
    (maybe-delete-trailing-whitespace)))

(defun delete-trailing-whitespacep ()
  "Should we delete trailing whitespace when saving this file?"
  (save-excursion
    (goto-char (point-min))
    (ignore-errors (next-line 25))
    (let ((pos (point)))
      (goto-char (point-min))
      (and (re-search-forward (concat "@author +" user-full-name) pos t) t))))

(defun maybe-delete-trailing-whitespace ()
  "Delete trailing whitespace if I am the author of this file."
  (interactive)
  (and (delete-trailing-whitespacep) (delete-trailing-whitespace)))

(add-hook 'php-mode-hook 'progmodes-hooks)
(add-hook 'python-mode-hook 'progmodes-hooks)
(add-hook 'js2-mode-hook 'progmodes-hooks)
A: 

Thanks so much for your progmodes-hooks, your solution is easy and straightforward.