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.

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
    Commented Apr 15, 2021 at 19:05
  • (Can't do anything right today - I guess I added a period inadvertently. Thanks for pointing it out). See emacs.stackexchange.com/questions/58104
    – NickD
    Commented Apr 15, 2021 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
    Commented Apr 15, 2021 at 19:23
  • @JohnDoe with your new edit, I can't see what is your question now.
    – jue
    Commented Apr 15, 2021 at 19:32
  • 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
    Commented Apr 16, 2021 at 1:41

1 Answer 1

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 :-) ).

1
  • Is there any way when looking up a variable in emacs help to know if it has buffer-local values?
    – John Doe
    Commented Apr 16, 2021 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.