GNU bug report logs - #56197
lisp-fill-paragraph behavior changed in Emacs 28

Previous Next

Package: emacs;

Reported by: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>

Date: Fri, 24 Jun 2022 16:18:01 UTC

Severity: normal

Tags: patch

Found in version 28.1

Done: Eli Zaretskii <eliz <at> gnu.org>

Bug is archived. No further changes may be made.

Full log


View this message in rfc822 format

From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
To: 56197 <at> debbugs.gnu.org
Cc: eliz <at> gnu.org, Maxim Cournoyer <maxim.cournoyer <at> gmail.com>, larsi <at> gnus.org, felix.lechner <at> lease-up.com, stefankangas <at> gmail.com
Subject: bug#56197: [PATCH v2] lisp: Introduce a `lisp-fill-paragraph-as-displayed' variable.
Date: Sun, 19 Jan 2025 21:51:56 +0900
Starting with Emacs 28, filling strings now happens in a narrowed scope,
and looses the leading indentation and can cause the string to extend
past the fill-column value.  Introduce `lisp-fill-paragraph-as-displayed'
as a new variable allowing opting out of this new behavior in specific
scenarios (such as when using the Scheme major mode, say).

* lisp/emacs-lisp/lisp-mode.el (lisp-fill-paragraph-as-displayed): New
variable.
(lisp-fill-paragraph): Honor it, by avoiding the logic narrow to strings
before applying fill-paragraph.
* test/lisp/emacs-lisp/lisp-mode-tests.el
(lisp-fill-paragraph-respects-fill-column): Test it.
(lisp-fill-paragraph-docstring-boundaries): New test, as a safeguard to
avoid regressions.

Fixes: bug#56197
---
Changes since v1: Use defvar, not defcustom + light rewordings of some
doc/comments

 lisp/emacs-lisp/lisp-mode.el            | 72 ++++++++++++++-----------
 test/lisp/emacs-lisp/lisp-mode-tests.el | 47 ++++++++++++++++
 2 files changed, 88 insertions(+), 31 deletions(-)

diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index d0c32d238bc..c55dd3c6528 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -1,6 +1,6 @@
 ;;; lisp-mode.el --- Lisp mode, and its idiosyncratic commands  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1985-1986, 1999-2024 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1986, 1999-2025 Free Software Foundation, Inc.
 
 ;; Maintainer: emacs-devel <at> gnu.org
 ;; Keywords: lisp, languages
@@ -1431,6 +1431,14 @@ emacs-lisp-docstring-fill-column
   :group 'lisp
   :version "30.1")
 
+(defvar lisp-fill-paragraph-as-displayed nil
+  "This variable can be set to true to fill paragraphs as displayed in the
+buffer, preserving surrounding context such as the leading indentation.
+This is useful if respecting `fill-column' is more important than
+preventing surrounding code from being filled, and makes
+`lisp-fill-paragraph' behave as it used to in Emacs 27 and prior
+versions.")
+
 (defun lisp-fill-paragraph (&optional justify)
   "Like \\[fill-paragraph], but handle Emacs Lisp comments and docstrings.
 If any of the current line is a comment, fill the comment or the
@@ -1480,42 +1488,44 @@ lisp-fill-paragraph
                                   (derived-mode-p 'emacs-lisp-mode))
                              emacs-lisp-docstring-fill-column
                            fill-column)))
-        (let ((ppss (syntax-ppss))
-              (start (point))
-              ;; Avoid recursion if we're being called directly with
-              ;; `M-x lisp-fill-paragraph' in an `emacs-lisp-mode' buffer.
-              (fill-paragraph-function t))
+        (let* ((ppss (syntax-ppss))
+               (start (point))
+               ;; Avoid recursion if we're being called directly with
+               ;; `M-x lisp-fill-paragraph' in an `emacs-lisp-mode' buffer.
+               (fill-paragraph-function t)
+               (string-start (ppss-comment-or-string-start ppss)))
           (save-excursion
             (save-restriction
               ;; If we're not inside a string, then do very basic
               ;; filling.  This avoids corrupting embedded strings in
               ;; code.
-              (if (not (ppss-comment-or-string-start ppss))
+              (if (not string-start)
                   (lisp--fill-line-simple)
-                ;; If we're in a string, then narrow (roughly) to that
-                ;; string before filling.  This avoids filling Lisp
-                ;; statements that follow the string.
-                (when (ppss-string-terminator ppss)
-                  (goto-char (ppss-comment-or-string-start ppss))
-                  ;; The string may be unterminated -- in that case, don't
-                  ;; narrow.
-                  (when (ignore-errors
-                          (progn
-                            (forward-sexp 1)
-                            t))
-                    (narrow-to-region (1+ (ppss-comment-or-string-start ppss))
-                                      (1- (point)))))
-                ;; Move back to where we were.
-                (goto-char start)
-                ;; We should fill the first line of a string
-                ;; separately (since it's usually a doc string).
-                (if (= (line-number-at-pos) 1)
-                    (narrow-to-region (line-beginning-position)
-                                      (line-beginning-position 2))
-                  (save-excursion
-                    (goto-char (point-min))
-                    (forward-line 1)
-                    (narrow-to-region (point) (point-max))))
+                (unless lisp-fill-paragraph-as-displayed
+                  ;; If we're in a string, then narrow (roughly) to that
+                  ;; string before filling.  This avoids filling Lisp
+                  ;; statements that follow the string.
+                  (when (ppss-string-terminator ppss)
+                    (goto-char string-start)
+                    ;; The string may be unterminated -- in that case, don't
+                    ;; narrow.
+                    (when (ignore-errors
+                            (progn
+                              (forward-sexp 1)
+                              t))
+                      (narrow-to-region (1+ string-start)
+                                        (1- (point)))))
+                  ;; Move back to where we were.
+                  (goto-char start)
+                  ;; We should fill the first line of a string
+                  ;; separately (since it's usually a doc string).
+                  (if (= (line-number-at-pos) 1)
+                      (narrow-to-region (line-beginning-position)
+                                        (line-beginning-position 2))
+                    (save-excursion
+                      (goto-char (point-min))
+                      (forward-line 1)
+                      (narrow-to-region (point) (point-max)))))
 	        (fill-paragraph justify)))))))
   ;; Never return nil.
   t)
diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el
index da02be65d03..7f5c97ace4d 100644
--- a/test/lisp/emacs-lisp/lisp-mode-tests.el
+++ b/test/lisp/emacs-lisp/lisp-mode-tests.el
@@ -308,6 +308,53 @@ lisp-indent-defun
       (indent-region (point-min) (point-max))
       (should (equal (buffer-string) orig)))))
 
+
+;;; Filling
+
+(ert-deftest lisp-fill-paragraph-docstring-boundaries ()
+  "Test bug#28937, ensuring filling the docstring filled is properly
+bounded."
+  (with-temp-buffer
+    (insert "\
+(defun test ()
+  \"This is a test docstring.
+Here is some more text.\"
+  1
+  2
+  3
+  4
+  5)")
+    (let ((correct (buffer-string)))
+      (emacs-lisp-mode)
+      (search-backward "This is a test docstring")
+      (fill-paragraph)                  ;function under test
+      (should (equal (buffer-string) correct)))))
+
+(ert-deftest lisp-fill-paragraph-as-displayed ()
+  "Test bug#56197 -- more specifically, validate that a leading indentation
+for a string is preserved in the filled string."
+  (let ((lisp-fill-paragraph-as-displayed t) ;variable under test
+        ;; The following is a contrived example that demonstrates the
+        ;; fill-column problem when the string to fill is indented.
+        (source "\
+'(description \"This is a very long string which is indented by a considerable value, causing it to
+protrude from the configured `fill-column' since
+lisp-fill-paragraph was refactored in version 28.\")"))
+    (with-temp-buffer
+      (insert source)
+      (emacs-lisp-mode)
+      (search-backward "This is a very long string")
+      (fill-paragraph)                  ;function under test
+      (goto-char (point-min))
+      (message "%s" (buffer-substring-no-properties (point-min) (point-max)))
+      (let ((i 1)
+            (lines-count (count-lines (point-min) (point-max))))
+        (while (< i lines-count)
+          (beginning-of-line i)
+          (end-of-line)
+          (should (<= (current-column) fill-column))
+          (setq i (1+ i)))))))
+
 
 ;;; Fontification
 
-- 
2.46.0





This bug report was last modified 109 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.