Subject: Re: bug#32014: 26.1; lisp-indent-line fails in first line of Ielm

From: Noam Postavsky <[email-masked]>
Date: Fri, 29 Jun 2018 20:23:13 -0400

tags 32014 + patch
quit

João Távora <[email-masked]> writes:

> The error's backtrace isn't shown even with debug-on-error set to t.

If you (setq debug-ignored-errors nil) first, then the backtrace is

Debugger entered--Lisp error: (text-read-only)
  indent-line-to(7)
  lisp-indent-line()
  funcall-interactively(lisp-indent-line)
  call-interactively(lisp-indent-line record nil)
  command-execute(lisp-indent-line record)
  execute-extended-command(nil "lisp-indent-line" "lisp-indent-line")
  funcall-interactively(execute-extended-command nil "lisp-indent-line" "lisp-indent-line")
  call-interactively(execute-extended-command nil nil)
  command-execute(execute-extended-command)

> Is this the intended behaviour?  In 25.2 the string foo was correctly
> indented back one character, so this seems like a regression.

No, it's an accident.  In lisp-indent-line, I simplified 

	(setq shift-amt (- indent (current-column)))
	(if (zerop shift-amt)
	    nil
	  (delete-region beg (point))
	  (indent-to indent)))

into

    (indent-line-to indent)

but it turns out not be equivalent in this case.  indent-line-to doesn't
respect the prompt's field property.

I propose this for emacs-26:

>From 2524780c54bcd8faecdb8497c0e1c960752fc9ce Mon Sep 17 00:00:00 2001
From: Noam Postavsky <[email-masked]>
Date: Fri, 29 Jun 2018 20:15:10 -0400
Subject: [PATCH v1 1/2] ; Test for Bug#32014

* test/lisp/emacs-lisp/lisp-mode-tests.el
(lisp-indent-with-read-only-field): New test.
---
 test/lisp/emacs-lisp/lisp-mode-tests.el | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el
index 0b5b0a4019..2ac0e5ce1d 100644
--- a/test/lisp/emacs-lisp/lisp-mode-tests.el
+++ b/test/lisp/emacs-lisp/lisp-mode-tests.el
@@ -224,6 +224,17 @@ lisp-mode-tests--correctly-indented-sexp
       (comment-indent)
       (should (equal (buffer-string) correct)))))
 
+(ert-deftest lisp-indent-with-read-only-field ()
+  "Test indentation on line with read-only field (Bug#32014)."
+  :expected-result :failed
+  (with-temp-buffer
+    (insert (propertize "prompt> " 'field 'output 'read-only t
+                        'rear-nonsticky t 'front-sticky '(read-only)))
+    (insert " foo")
+    (lisp-indent-line)
+    (should (equal (buffer-string) "prompt> foo"))))
+
+
 
 (provide 'lisp-mode-tests)
 ;;; lisp-mode-tests.el ends here
-- 
2.11.0

>From 98e30ee9505f6e8cd21a36b88223e29ad9ee8281 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <[email-masked]>
Date: Fri, 29 Jun 2018 19:58:58 -0400
Subject: [PATCH v1 2/2] Stop using indent-line-to in lisp-indent-line
 (Bug#32014)

This is partial revert of "Remove ignored argument from
lisp-indent-line", because `indent-line-to' doesn't respect field
boundaries.
* lisp/emacs-lisp/lisp-mode.el (lisp-indent-line): Use delete-region
and indent-to instead of `indent-line-to'.
* test/lisp/emacs-lisp/lisp-mode-tests.el
(lisp-indent-with-read-only-field): Expect to pass.

Don't merge to master, we will fix indent-line-to there instead.
---
 lisp/emacs-lisp/lisp-mode.el            | 10 ++++++++--
 test/lisp/emacs-lisp/lisp-mode-tests.el |  1 -
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index 94be5acd6d..3a03b56313 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -867,7 +867,9 @@ lisp-indent-line
   (interactive)
   (let ((pos (- (point-max) (point)))
         (indent (progn (beginning-of-line)
-                       (or indent (calculate-lisp-indent (lisp-ppss))))))
+                       (or indent (calculate-lisp-indent (lisp-ppss)))))
+	(shift-amt nil)
+	(beg (progn (beginning-of-line) (point))))
     (skip-chars-forward " \t")
     (if (or (null indent) (looking-at "\\s<\\s<\\s<"))
 	;; Don't alter indentation of a ;;; comment line
@@ -879,7 +881,11 @@ lisp-indent-line
 	  ;; as comment lines, not as code.
 	  (progn (indent-for-comment) (forward-char -1))
 	(if (listp indent) (setq indent (car indent)))
-        (indent-line-to indent))
+	(setq shift-amt (- indent (current-column)))
+	(if (zerop shift-amt)
+	    nil
+	  (delete-region beg (point))
+	  (indent-to indent)))
       ;; If initial point was within line's indentation,
       ;; position after the indentation.  Else stay at same point in text.
       (if (> (- (point-max) pos) (point))
diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el
index 2ac0e5ce1d..8598d41978 100644
--- a/test/lisp/emacs-lisp/lisp-mode-tests.el
+++ b/test/lisp/emacs-lisp/lisp-mode-tests.el
@@ -226,7 +226,6 @@ lisp-mode-tests--correctly-indented-sexp
 
 (ert-deftest lisp-indent-with-read-only-field ()
   "Test indentation on line with read-only field (Bug#32014)."
-  :expected-result :failed
   (with-temp-buffer
     (insert (propertize "prompt> " 'field 'output 'read-only t
                         'rear-nonsticky t 'front-sticky '(read-only)))
-- 
2.11.0

and this for master:

>From 2dccc6d64669078915a7eda75f40e75408b7794e Mon Sep 17 00:00:00 2001
From: Noam Postavsky <[email-masked]>
Date: Fri, 29 Jun 2018 20:01:53 -0400
Subject: [PATCH] Respect field boundaries in *-to-indentation functions
 (Bug#32014)

* lisp/simple.el (forward-to-indentation)
(backward-to-indentation): Use `beginning-of-line' which respects
field boundaries rather than `forward-line' which doesn't.
---
 lisp/simple.el | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lisp/simple.el b/lisp/simple.el
index f8c02c1dbf..3cece52657 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -872,13 +872,13 @@ quoted-insert
 (defun forward-to-indentation (&optional arg)
   "Move forward ARG lines and position at first nonblank character."
   (interactive "^p")
-  (forward-line (or arg 1))
+  (beginning-of-line (+ 1 (or arg 1)))
   (skip-chars-forward " \t"))
 
 (defun backward-to-indentation (&optional arg)
   "Move backward ARG lines and position at first nonblank character."
   (interactive "^p")
-  (forward-line (- (or arg 1)))
+  (beginning-of-line (- 1 (or arg 1)))
   (skip-chars-forward " \t"))
 
 (defun back-to-indentation ()
-- 
2.11.0