Subject: bug#71499: [PATCH] Make whitespace.el cleanup add missing final newline

From: Björn Lindström <[email-masked]>
Date: Sat, 29 Jun 2024 13:59:03 +0200

Hello,

updated patch attached, which ads a note to NEWS, and has a changed implementation of the insertion of missing newline inspired by the one from files.el. It's still slightly different to account for it also being possible to use on a region.

This time there's also an added test.

/ Björn

On Thu, Jun 27, 2024, at 09:37, Eli Zaretskii wrote:
>> Cc: [email-masked], [email-masked], [email-masked]
>> Date: Thu, 13 Jun 2024 11:30:51 +0300
>> From: Eli Zaretskii <[email-masked]>
>> 
>> Meanwhile, Björn, I have a few comments to the patch:
>> 
>>   . it needs a NEWS entry announcing the new feature
>>   . is there any reason your code to handle the missing newline is not
>>     identical to what the implementation of require-final-newline
>>     does?
>
> Ping!  Björn, can you please post an updated patch with the above nits
> taken care of?  We can install this now on the master branch.

From a6ca7984df3eec80e8a36ded73d068073d9a5a0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Lindstr=C3=B6m?= <[email-masked]>
Date: Tue, 11 Jun 2024 19:49:55 +0200
Subject: [PATCH] Make whitespace.el cleanup add missing final newline

* lisp/whitespace.el (whitespace-cleanup-region): if cleaning up at end
of file, add missing newline if indicated by whitespace-style.
---
 etc/NEWS                      |  6 ++++++
 lisp/whitespace.el            | 16 +++++++++++++++-
 test/lisp/whitespace-tests.el | 15 ++++++++++++++-
 3 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index af32a93d9c4..9f61a4aa4ce 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -36,6 +36,12 @@ applies, and please also update docstrings as needed.
 
 * Changes in Specialized Modes and Packages in Emacs 31.1
 
+---
+** Whitespace
+'whitespace-cleanup' now adds missing newline at end of file If
+'whitespace-style' includes 'missing-newline-at-eof (which is the
+default), the 'whitespace-cleanup' function will fix this when run.
+
 
 * New Modes and Packages in Emacs 31.1
 
diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index bc23a8794eb..28d131b054c 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -1465,6 +1465,11 @@ defun whitespace-cleanup-region
    If `whitespace-style' includes the value
    `space-after-tab::space', replace TABs by SPACEs.
 
+5. missing newline at end of file.
+   If `whitespace-style' includes the value `missing-newline-at-eof',
+   and the cleanup region includes the end of file, add a final newline
+   if it is not there already.
+
 See `whitespace-style', `indent-tabs-mode' and `tab-width' for
 documentation."
   (interactive "@r")
@@ -1545,7 +1550,16 @@ defun whitespace-cleanup-region
          ((memq 'space-before-tab::space whitespace-style)
           (whitespace-replace-action
            'untabify rstart rend
-           whitespace-space-before-tab-regexp 2))))
+           whitespace-space-before-tab-regexp 2)))
+        ;; PROBLEM 5: missing newline at end of file
+        (and (memq 'missing-newline-at-eof whitespace-style)
+             (> (point-max) (point-min))
+             (= (point-max) (without-restriction (point-max)))
+             (/= (char-before (point-max)) ?\n)
+             (not (and (eq selective-display t)
+                       (= (char-before (point-max)) ?\r)))
+             (goto-char (point-max))
+             (ignore-errors (insert "\n"))))
       (set-marker rend nil))))		; point marker to nowhere

diff --git a/test/lisp/whitespace-tests.el b/test/lisp/whitespace-tests.el
index 73c7e742ec5..bd35b3ac9f3 100644
--- a/test/lisp/whitespace-tests.el
+++ b/test/lisp/whitespace-tests.el
@@ -8,7 +8,6 @@
 ;; it under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation, either version 3 of the License, or
 ;; (at your option) any later version.
-
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -94,6 +93,20 @@ defun whitespace-tests--cleanup-string
     (should (equal (whitespace-tests--cleanup-string "a  \n\t \n\n")
                    "a  \n"))))
 
+(ert-deftest whitespace-cleanup-missing-newline-at-eof ()
+  (let ((whitespace-style '(empty missing-newline-at-eof)))
+    (should (equal (whitespace-tests--cleanup-string "")
+                   ""))
+    (should (equal (whitespace-tests--cleanup-string "a")
+                   "a\n"))
+    (should (equal (whitespace-tests--cleanup-string "a\n\t")
+                   "a\n"))
+    (should (equal (whitespace-tests--cleanup-string "a\n\t ")
+                   "a\n"))
+    (should (equal (whitespace-tests--cleanup-string "a\n\t ")
+                   "a\n"))
+    (should (equal (whitespace-tests--cleanup-string "\n\t")
+                   ""))))
 
 ;; We cannot call whitespace-mode because it will do nothing in batch
 ;; mode.  So we call its innards instead.
-- 
2.45.2