Subject: bug#24427: 25.1.50; end-of-defun jumps too far

From: Noam Postavsky <[email-masked]>
Date: Sun, 17 Jun 2018 13:50:34 -0400

Robert Cochran <[email-masked]> writes:

> Nothing has come to mind for a method to fix it without breaking other
> things. Perhaps the solution is obvious for someone else? Suggestions
> would be nice if you have them.

Not entirely sure if this is correct, but the patch below seems to fix
it for me, without breaking any of the previously mentioned scenarios.
I find the fact that end-of-defun goes to the line following the closing
paren a bit dubious, though I guess since it's been that way so long, we
can hardly change it now.

>From 111ea54ce469d7e601c0e7c2b48d1748021e71a6 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <[email-masked]>
Date: Sun, 17 Jun 2018 13:29:37 -0400
Subject: [PATCH v1] Fix (end-of-defun N) for N >= 2 (Bug#24427)

* lisp/emacs-lisp/lisp.el (end-of-defun): Only skip to next line when
after end of defun when ARG is 1 or less.
* test/lisp/emacs-lisp/lisp-tests.el (end-of-defun-twice): New test.
---
 lisp/emacs-lisp/lisp.el            |  3 ++-
 test/lisp/emacs-lisp/lisp-tests.el | 55 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el
index 5a89923f8f..67e7b3ef7b 100644
--- a/lisp/emacs-lisp/lisp.el
+++ b/lisp/emacs-lisp/lisp.el
@@ -481,7 +481,8 @@ end-of-defun
 		  (if (looking-at "\\s<\\|\n")
 		      (forward-line 1))))))
     (funcall end-of-defun-function)
-    (funcall skip)
+    (when (<= arg 1)
+      (funcall skip))
     (cond
      ((> arg 0)
       ;; Moving forward.
diff --git a/test/lisp/emacs-lisp/lisp-tests.el b/test/lisp/emacs-lisp/lisp-tests.el
index 07eddb74d5..3c87879c3f 100644
--- a/test/lisp/emacs-lisp/lisp-tests.el
+++ b/test/lisp/emacs-lisp/lisp-tests.el
@@ -367,6 +367,61 @@ elisp-tests-with-temp-buffer
 "
     "Test buffer for `mark-defun'."))
 
+;;; end-of-defun
+
+(ert-deftest end-of-defun-twice ()
+  "Test behavior of prefix arg for `end-of-defun' (Bug#24427).
+Calling `end-of-defun' twice should be the same as a prefix arg
+of two."
+  (setq last-command nil)
+  (cl-flet ((eod2 (lambda ()
+                    (goto-char (point-min))
+                    (end-of-defun)
+                    (end-of-defun)
+                    (let ((pt-eod2 (point)))
+                      (goto-char (point-min))
+                      (end-of-defun 2)
+                      (should (= (point) pt-eod2))))))
+    (with-temp-buffer
+      (insert "\
+\(defun a ())
+
+\(defun b ())
+
+\(defun c ())")
+      (eod2))
+    (with-temp-buffer
+      (insert "\
+\(defun a ())
+\(defun b ())
+\(defun c ())")
+      (eod2)))
+  (elisp-tests-with-temp-buffer ";; Comment header
+
+\(defun func-1 (arg)
+  \"docstring\"
+  body)
+=!p1=
+;; Comment before a defun
+\(defun func-2 (arg)
+  \"docstring\"
+  body)
+
+\(defun func-3 (arg)
+  \"docstring\"
+  body)
+=!p2=(defun func-4 (arg)
+  \"docstring\"
+  body)
+
+;; end
+"
+    (goto-char p1)
+    (end-of-defun 2)
+    (should (= (point) p2))))
+
+;;; mark-defun
+
 (ert-deftest mark-defun-no-arg-region-inactive ()
   "Test `mark-defun' with no prefix argument and inactive
 region."
-- 
2.11.0