6

I want all my files to end with only one newline, while trimming trailing whitespaces on regular lines.

Like so : ($ is newline face)

text$
$
last_line$

I use whitespace-cleanup in my init file, like so :

(add-hook 'before-save-hook 'whitespace-cleanup)

With whitespace-style :

(face tabs spaces trailing lines space-before-tab
 newline indentation empty space-after-tab
 space-mark tab-mark newline-mark)

To keep newlines I add :

(setq require-final-newline t)

Then eval-buffer my init file. But Emacs does not take my setting unless I use setq-default. There is no mention in require-final-newline documentation that it is buffer local.

John Doe
  • 159
  • 6
  • @NickD Can you elaborate? there is nothing in documentation of `require-final-newline` that hints variable is buffer local @jue : replace q.uestions in NickD's link – John Doe Apr 15 '21 at 19:05
  • (Can't do anything right today - I guess I added a period inadvertently. Thanks for pointing it out). See https://emacs.stackexchange.com/questions/58104 – NickD Apr 15 '21 at 19:05
  • I thought 'keep one newline at end of file' was one newline alone on last line, when it actually means one newline after last character of last line. I edit question as problem shifted to setq-default behavior. – John Doe Apr 15 '21 at 19:23
  • @JohnDoe with your new edit, I can't see what is your question now. – jue Apr 15 '21 at 19:32
  • @JohnDoe: I'm as confused as you are. It quacks like a buffer-local, but I'm probably missing something (it's been that kind of a day...), so I don't think I should even try to elaborate, even if I could. – NickD Apr 15 '21 at 20:24
  • 1
    @JohnDoe: I did some research and I've summarized my current understanding in the new answer, although it's not quite complete: there is a leap of faith in there. I hope it helps. – NickD Apr 16 '21 at 01:41

1 Answers1

5

You need to either set the default value of the variable, if you want it for every file:

(setq-default require-final-newline t)
;; or ...
(customize-set-variable 'require-final-newline t)

or to set it in the mode hook, if you only want it for files of that mode:

(add-hook 'some-mode-hook (lambda () (setq-local require-final-newline t)))

The reason that a bare (setq require-final-newline t) does not work is that require-final-newline is buffer-local in certain cases. A number of modes (most prominently prog-mode and text-mode) do this:

(setq-local require-final-newline mode-require-final-newline)

That also affects their derived modes (e.g. emacs-lisp-mode in the first case and org-mode in the second). So in those modes, require-final-newline is indeed buffer-local.

I don't know exactly how the loading and evaluation of the init file happens, but if it happens in such a buffer (e.g. in an emacs-lisp-mode buffer), require-final-newline is indeed a buffer-local, so the setq does not change the global default and your (future) buffer does not see the changed value. I suspect (but I don't know for sure) that this is the underlying cause of the failure of the bare setq in your init file and why you need setq-default.

EDIT: In answer to your question in the comment, I don't think there is a way to ask whether the variable is buffer-local in any buffer that exists in the current session, except by checking every buffer.

The problem is that any variable can be made buffer-local in a particular buffer, and nobody would know about that except that particular buffer: if you try tosetq from that buffer, you would only change the local value. So if you ask about the variable anywhere except in that buffer, it would say "not local". There are two functions local-variable-p and local-variable-is-set-p that do this kind of asking and they both say nil for a buffer in e.g. fundamental-mode for the variable require-final-newline. You can check for local-ness in other buffers so you could write something like this to check every buffer:

(defun is-local (sym)
  (catch 'found
    (dolist (buf (buffer-list))
      (when (local-variable-p sym buf)
        (throw 'found t)))
    nil))

If you evaluate (is-local 'require-final-newline) in any buffer, it says t, because it is local in the *scratch* buffer e.g.

But even this is not completely reliable: if during initialization a buffer is created and makes require-final-newline buffer-local but then gets deleted, the loop above will not find it afterwards. If there is no other buffer with a buffer-local require-final-newline, the function says nil. You can check that by setting initial-major-mode to fundamental, in a small init file that also contains the function above and the setq of require-final-newline, and invoking emacs with emacs -Q -l /path/to/minimal.init.el. The *scratch* buffer is now in fundamental mode and no other existing buffer (minibuffer, the Messages buffer etc) makes the variable buffer-local, so the function happily says nil - but the setq still does not work. To me, that indicates that the scenario I've been describing here is correct (but I can easily imagine being Horatio to someone's Hamlet :-) ).

NickD
  • 27,023
  • 3
  • 23
  • 42