Subject: bug#66763: end-of-defun discrepancy depending mode

From: Simon Tournier <[email-masked]>
Date: Thu, 26 Oct 2023 17:48:26 +0200

Hi,

The manual says:

       The commands to move to the beginning and end of the current defun
    are ‘C-M-a’ (‘beginning-of-defun’) and ‘C-M-e’ (‘end-of-defun’).  If you
    repeat one of these commands, or use a positive numeric argument, each
    repetition moves to the next defun in the direction of motion.

       ‘C-M-a’ with a negative argument −N moves forward N times to the next
    beginning of a defun.  This is not exactly the same place that ‘C-M-e’
    with argument N would move to; the end of this defun is not usually
    exactly the same place as the beginning of the following defun.

and this behaviour is different for nested definitions depending on the
mode.  For instance, python-mode:

     1	def level():
     2	    def sublevel():
     3	        def subsublevel():
     4	            return subsublevel
     5	        return sublevel
     6	    return level

If the point is located at line 4, then I press ’C-M-a’ and it moves to
the beginning of line 3.  Then repeat ’C-M-a’ moves to the beginning of
line 2, then repeat ’C-M-a’ moves to the beginning of line 1.

If the point is located at line 4, then I press ’C-u 3 C-M-a’ and it
moves to the beginning of line 1.

So far, so good!  That’s the first paragraph from the manual. :-)

Now, let do the same with scheme-mode:

     1	(define (level)
     2	  (define (sublevel)
     3	    (define (subsublevel)
     4	      'subsublevel)
     5	    'sublevel)
     6	  'level)

If the point is located at line 4, then I press ’C-M-a’ and it moves to
the beginning of line 1.  Why?  Is it expected?

Here is the first part of the bug.

Now, consider the second paragraph from the manual.  Because of these
discrepancy and considering 4489450f37deafb013b1f0fc00c89f0973fda14a (In
end-of-defun, terminate early if no further defun exists):

--8<---------------cut here---------------start------------->8---
+        ;; At this point, point either didn't move (because we started
+        ;; in between two defun's), or is at the end of a defun
+        ;; (because we started in the middle of a defun).
         (unless (zerop arg)
-          (beginning-of-defun-raw (- arg))
-          (funcall end-of-defun-function)))
+          (when (setq success (beginning-of-defun-raw (- arg)))
+            (funcall end-of-defun-function))))
--8<---------------cut here---------------end--------------->8---

The behaviour can be subtly tweaked when composing ’C-u’ and ’C-M-e’ for
some modes.

Cheers,
simon