Subject: bug#68664: 29.1.50; treesit defun commands broken with nested functions

From: Troy Brown <[email-masked]>
Date: Wed, 24 Jan 2024 12:25:36 -0500

On Wed, Jan 24, 2024 at 9:13 AM Troy Brown <[email-masked]> wrote:
>
> On Wed, Jan 24, 2024 at 1:29 AM Yuan Fu <[email-masked]> wrote:
> >
> > The behavior is expected. But I can see that it doesn’t match your expectations. The logic behind the current behavior is to first move between siblings in the same level; if there’s no sibling to move across anymore, move to the beginning/end of the immediate parent, and so on.
> >
> > To get the behavior you want, we would need to add a fourth defun navigation tactic, in addition to the existing three: nested, top-level, and restricted.
> >
> > If you are interested and able, maybe you can look into adding it to treesit--navigate-thing or treesit-beginning/end-of-defun?
> >
> > Yuan
>
> I find it quite odd that this is the expected behavior.  Per the Emacs
> manual (section "Moving by Defuns"), I expected the point to be moved
> to the "innermost defun around point", since treesit-defun-tactic is
> set to "nested", as that is precisely what is documented there.  I
> interpret "innermost defun around point" to mean the innermost defun
> that encompasses point.  Additionally, the documentation strings for
> treesit-beginning-of-defun and treesit-end-of-defun indicate that they
> are a tree-sitter equivalent of the beginning-of-defun and
> end-of-defun commands respectively.  If so, and since they are mapped
> to the same key bindings in the tree-sitter modes, shouldn't the
> expectation be that they behave the same way as the non-tree-sitter
> commands?  If not, people transitioning between the non-tree-sitter
> mode and the tree-sitter mode are in for a surprise when the commands
> behave differently.
>
> With the current behavior there is no way to move the point directly
> to the beginning of the function without moving through all of the
> nested functions first, which could be significant.  When you say the
> behavior is to "move between siblings in the same level", should the
> level refer to where point is, or to the level corresponding to the
> function encompassing the point?  I think it probably makes sense to
> move between siblings if you are at a function boundary (there is a
> function immediately before or after the point), but if you are
> already deep in a function, I think it makes sense to first move to
> that function's begin/end before attempting to move between siblings.
> I believe this behavior would be consistent with the non-tree-sitter
> modes and expectations based on the description in the manual.

To add further support to my belief that the current implementation is
not the expected behavior, consider how the current implementation
behaves when used with mark-defun.  When the point is on the call to
innerFunction and I execute "M-x mark-defun RET", the nested function
following the point (i.e., innerFunction2) is selected rather than the
function containing point.  For comparison, the non-tree-sitter
python-mode behaves correctly and selects the function containing
point, not the next nested function.