unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
@ 2023-07-03 17:13 Eli Zaretskii
  2023-07-04  8:41 ` Yuan Fu
  2023-08-19 22:00 ` Yuan Fu
  0 siblings, 2 replies; 15+ messages in thread
From: Eli Zaretskii @ 2023-07-03 17:13 UTC (permalink / raw)
  To: 64442

To reproduce:

  emacs -Q
  C-x C-f src/dispnew.c RET
  M-x c-ts-mode RET
  C-u 220 M-g g
  C-M-a

Observe that instead of going to the beginning of
dump-redisplay-history, the function to which line 220 of dispnew.c
belongs, point goes to the beginning of the previous function, which
happens to be add_frame_display_history.

Can we please fix treesit-beginning-of-defun such that it recognizes C
functions defined via DEFUN?



In GNU Emacs 29.0.92 (build 20, i686-pc-mingw32) of 2023-07-02 built on
 HOME-C4E4A596F7
Repository revision: 15ff87617772c2a2c3d8a3a1e2ed7f96e527ad9e
Repository branch: emacs-29
Windowing system distributor 'Microsoft Corp.', version 5.1.2600
System Description: Microsoft Windows XP Service Pack 3 (v5.1.0.2600)

Configured using:
 'configure -C --prefix=/d/usr --with-wide-int
 --enable-checking=yes,glyphs 'CFLAGS=-O0 -gdwarf-4 -g3''

Configured features:
ACL GIF GMP GNUTLS HARFBUZZ JPEG JSON LCMS2 LIBXML2 MODULES NOTIFY
W32NOTIFY PDUMPER PNG RSVG SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP XPM ZLIB

Important settings:
  value of $LANG: ENU
  locale-coding-system: cp1255

Major mode: C/*

Minor modes in effect:
  bug-reference-prog-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message mailcap yank-media puny dired
dired-loaddefs rfc822 mml mml-sec password-cache epa derived epg rfc6068
epg-config gnus-util text-property-search time-date subr-x mm-decode
mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader
sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils
misearch multi-isearch c-ts-mode c-ts-common treesit cl-seq vc-git
diff-mode easy-mmode vc vc-dispatcher bug-reference byte-opt gv bytecomp
byte-compile cc-mode cc-fonts cc-guess cc-menus cc-cmds cc-styles
cc-align cc-engine cc-vars cc-defs cl-loaddefs cl-lib rmc iso-transl
tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks
lisp-float-type elisp-mode mwheel dos-w32 ls-lisp disp-table
term/w32-win w32-win w32-vars term/common-win tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode
prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu
timer select scroll-bar mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads w32notify w32 lcms2 multi-tty
make-network-process emacs)

Memory information:
((conses 16 81182 12528)
 (symbols 48 9662 0)
 (strings 16 28732 3398)
 (string-bytes 1 924785)
 (vectors 16 15907)
 (vector-slots 8 209599 17044)
 (floats 8 29 78)
 (intervals 40 1068 136)
 (buffers 888 11))





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-03 17:13 bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C Eli Zaretskii
@ 2023-07-04  8:41 ` Yuan Fu
  2023-07-04 11:39   ` Eli Zaretskii
  2023-08-19 22:00 ` Yuan Fu
  1 sibling, 1 reply; 15+ messages in thread
From: Yuan Fu @ 2023-07-04  8:41 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442



> On Jul 3, 2023, at 10:13 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
> To reproduce:
> 
>  emacs -Q
>  C-x C-f src/dispnew.c RET
>  M-x c-ts-mode RET
>  C-u 220 M-g g
>  C-M-a
> 
> Observe that instead of going to the beginning of
> dump-redisplay-history, the function to which line 220 of dispnew.c
> belongs, point goes to the beginning of the previous function, which
> happens to be add_frame_display_history.
> 
> Can we please fix treesit-beginning-of-defun such that it recognizes C
> functions defined via DEFUN?

I’ve tried it when I was fixing fontification for DEFUN, but ultimately gave up. The tree-sitter defun movement functions searches for defun nodes bottom-up, and goes to the beginning or end of that node. 

The problem with DEFUN’s is that a DEFUN is really made of two nodes in the parse tree. One for the DEFUN part, one for the body, and there isn’t a parent node that encloses the two. 

The defun movement functions are not designed to handle a construct made of two adjacent nodes. They can find a node, go to the beginning/end of it; they can’t find a node, and go to the end of the next node.

It sounds easy to add some hack to handle it, but really isn’t. Defun movement need to support forward/backward to beg/end, that’s four movement types; on top of that you have nested defun’s.

Yuan




^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-04  8:41 ` Yuan Fu
@ 2023-07-04 11:39   ` Eli Zaretskii
  2023-07-07  6:15     ` Yuan Fu
  0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-07-04 11:39 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 64442

> From: Yuan Fu <casouri@gmail.com>
> Date: Tue, 4 Jul 2023 01:41:22 -0700
> Cc: 64442@debbugs.gnu.org
> 
> The problem with DEFUN’s is that a DEFUN is really made of two nodes in the parse tree. One for the DEFUN part, one for the body, and there isn’t a parent node that encloses the two. 
> 
> The defun movement functions are not designed to handle a construct made of two adjacent nodes. They can find a node, go to the beginning/end of it; they can’t find a node, and go to the end of the next node.
> 
> It sounds easy to add some hack to handle it, but really isn’t. Defun movement need to support forward/backward to beg/end, that’s four movement types;

Why cannot we look for a top-level expression_statement node which is
a call_expression whose function identifier is "DEFUN" and whose
position is between the place where C-M-a was invoked and the place
where it does find a defun?

> on top of that you have nested defun’s.

DEFUN's cannot be nested, so we don't need to consider that.

Thanks.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-04 11:39   ` Eli Zaretskii
@ 2023-07-07  6:15     ` Yuan Fu
  2023-07-07  6:40       ` Eli Zaretskii
  0 siblings, 1 reply; 15+ messages in thread
From: Yuan Fu @ 2023-07-07  6:15 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442



> On Jul 4, 2023, at 4:39 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Tue, 4 Jul 2023 01:41:22 -0700
>> Cc: 64442@debbugs.gnu.org
>> 
>> The problem with DEFUN’s is that a DEFUN is really made of two nodes in the parse tree. One for the DEFUN part, one for the body, and there isn’t a parent node that encloses the two. 
>> 
>> The defun movement functions are not designed to handle a construct made of two adjacent nodes. They can find a node, go to the beginning/end of it; they can’t find a node, and go to the end of the next node.
>> 
>> It sounds easy to add some hack to handle it, but really isn’t. Defun movement need to support forward/backward to beg/end, that’s four movement types;
> 
> Why cannot we look for a top-level expression_statement node which is
> a call_expression whose function identifier is "DEFUN" and whose
> position is between the place where C-M-a was invoked and the place
> where it does find a defun?

It’s gonna be ugly, but I can take a jab at it this weekend. I’m thinking of a wrapper function that tries to detect DEFUN before falling back to the ordinary tree-sitter defun movement function.

> 
>> on top of that you have nested defun’s.
> 
> DEFUN's cannot be nested, so we don't need to consider that.

Yeah, in general C sources don’t have nested defuns, only C++ ones do. I was trying to illustrate that it’s hard to extend existing defun movement framework such that it handles this special case. The best solution I can think of is what I described above.

Yuan




^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-07  6:15     ` Yuan Fu
@ 2023-07-07  6:40       ` Eli Zaretskii
  2023-07-12  2:10         ` Yuan Fu
  0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-07-07  6:40 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 64442

> From: Yuan Fu <casouri@gmail.com>
> Date: Thu, 6 Jul 2023 23:15:00 -0700
> Cc: 64442@debbugs.gnu.org
> 
> > On Jul 4, 2023, at 4:39 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> > 
> > Why cannot we look for a top-level expression_statement node which is
> > a call_expression whose function identifier is "DEFUN" and whose
> > position is between the place where C-M-a was invoked and the place
> > where it does find a defun?
> 
> It’s gonna be ugly, but I can take a jab at it this weekend. I’m thinking of a wrapper function that tries to detect DEFUN before falling back to the ordinary tree-sitter defun movement function.

Thanks.  let's see how ugly it is before deciding whether it's worth it.

> > DEFUN's cannot be nested, so we don't need to consider that.
> 
> Yeah, in general C sources don’t have nested defuns, only C++ ones do.

No, I meant the use of DEFUN macros in Emacs cannot be nested.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-07  6:40       ` Eli Zaretskii
@ 2023-07-12  2:10         ` Yuan Fu
  2023-07-30  7:10           ` Eli Zaretskii
  0 siblings, 1 reply; 15+ messages in thread
From: Yuan Fu @ 2023-07-12  2:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442



> On Jul 6, 2023, at 11:40 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Thu, 6 Jul 2023 23:15:00 -0700
>> Cc: 64442@debbugs.gnu.org
>> 
>>> On Jul 4, 2023, at 4:39 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>>> 
>>> Why cannot we look for a top-level expression_statement node which is
>>> a call_expression whose function identifier is "DEFUN" and whose
>>> position is between the place where C-M-a was invoked and the place
>>> where it does find a defun?
>> 
>> It’s gonna be ugly, but I can take a jab at it this weekend. I’m thinking of a wrapper function that tries to detect DEFUN before falling back to the ordinary tree-sitter defun movement function.
> 
> Thanks.  let's see how ugly it is before deciding whether it's worth it.
> 
>>> DEFUN's cannot be nested, so we don't need to consider that.
>> 
>> Yeah, in general C sources don’t have nested defuns, only C++ ones do.
> 
> No, I meant the use of DEFUN macros in Emacs cannot be nested.

Just an update. I didn’t forget about this, but it’s more harder than I thought and I’m still working on it :-(

Yuan




^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-12  2:10         ` Yuan Fu
@ 2023-07-30  7:10           ` Eli Zaretskii
  2023-08-10  9:18             ` Eli Zaretskii
  0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-07-30  7:10 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 64442

> From: Yuan Fu <casouri@gmail.com>
> Date: Tue, 11 Jul 2023 19:10:01 -0700
> Cc: 64442@debbugs.gnu.org
> 
> 
> 
> > On Jul 6, 2023, at 11:40 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> > 
> >> From: Yuan Fu <casouri@gmail.com>
> >> Date: Thu, 6 Jul 2023 23:15:00 -0700
> >> Cc: 64442@debbugs.gnu.org
> >> 
> >>> On Jul 4, 2023, at 4:39 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> >>> 
> >>> Why cannot we look for a top-level expression_statement node which is
> >>> a call_expression whose function identifier is "DEFUN" and whose
> >>> position is between the place where C-M-a was invoked and the place
> >>> where it does find a defun?
> >> 
> >> It’s gonna be ugly, but I can take a jab at it this weekend. I’m thinking of a wrapper function that tries to detect DEFUN before falling back to the ordinary tree-sitter defun movement function.
> > 
> > Thanks.  let's see how ugly it is before deciding whether it's worth it.
> > 
> >>> DEFUN's cannot be nested, so we don't need to consider that.
> >> 
> >> Yeah, in general C sources don’t have nested defuns, only C++ ones do.
> > 
> > No, I meant the use of DEFUN macros in Emacs cannot be nested.
> 
> Just an update. I didn’t forget about this, but it’s more harder than I thought and I’m still working on it :-(

Any progress with this?  It would be good to have a solution in Emacs
29.2, if possible.

Thanks.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-07-30  7:10           ` Eli Zaretskii
@ 2023-08-10  9:18             ` Eli Zaretskii
  2023-08-10 21:33               ` Yuan Fu
  0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-08-10  9:18 UTC (permalink / raw)
  To: casouri; +Cc: 64442

> Cc: 64442@debbugs.gnu.org
> Date: Sun, 30 Jul 2023 10:10:35 +0300
> From: Eli Zaretskii <eliz@gnu.org>
> 
> > From: Yuan Fu <casouri@gmail.com>
> > Date: Tue, 11 Jul 2023 19:10:01 -0700
> > Cc: 64442@debbugs.gnu.org
> > 
> > 
> > 
> > > On Jul 6, 2023, at 11:40 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> > > 
> > >> From: Yuan Fu <casouri@gmail.com>
> > >> Date: Thu, 6 Jul 2023 23:15:00 -0700
> > >> Cc: 64442@debbugs.gnu.org
> > >> 
> > >>> On Jul 4, 2023, at 4:39 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> > >>> 
> > >>> Why cannot we look for a top-level expression_statement node which is
> > >>> a call_expression whose function identifier is "DEFUN" and whose
> > >>> position is between the place where C-M-a was invoked and the place
> > >>> where it does find a defun?
> > >> 
> > >> It’s gonna be ugly, but I can take a jab at it this weekend. I’m thinking of a wrapper function that tries to detect DEFUN before falling back to the ordinary tree-sitter defun movement function.
> > > 
> > > Thanks.  let's see how ugly it is before deciding whether it's worth it.
> > > 
> > >>> DEFUN's cannot be nested, so we don't need to consider that.
> > >> 
> > >> Yeah, in general C sources don’t have nested defuns, only C++ ones do.
> > > 
> > > No, I meant the use of DEFUN macros in Emacs cannot be nested.
> > 
> > Just an update. I didn’t forget about this, but it’s more harder than I thought and I’m still working on it :-(
> 
> Any progress with this?  It would be good to have a solution in Emacs
> 29.2, if possible.

Ping!





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-08-10  9:18             ` Eli Zaretskii
@ 2023-08-10 21:33               ` Yuan Fu
  2023-08-12 14:59                 ` Eli Zaretskii
  0 siblings, 1 reply; 15+ messages in thread
From: Yuan Fu @ 2023-08-10 21:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442

[-- Attachment #1: Type: text/plain, Size: 2083 bytes --]



> On Aug 10, 2023, at 2:18 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> Cc: 64442@debbugs.gnu.org
>> Date: Sun, 30 Jul 2023 10:10:35 +0300
>> From: Eli Zaretskii <eliz@gnu.org>
>> 
>>> From: Yuan Fu <casouri@gmail.com>
>>> Date: Tue, 11 Jul 2023 19:10:01 -0700
>>> Cc: 64442@debbugs.gnu.org
>>> 
>>> 
>>> 
>>>> On Jul 6, 2023, at 11:40 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>>>> 
>>>>> From: Yuan Fu <casouri@gmail.com>
>>>>> Date: Thu, 6 Jul 2023 23:15:00 -0700
>>>>> Cc: 64442@debbugs.gnu.org
>>>>> 
>>>>>> On Jul 4, 2023, at 4:39 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>>>>>> 
>>>>>> Why cannot we look for a top-level expression_statement node which is
>>>>>> a call_expression whose function identifier is "DEFUN" and whose
>>>>>> position is between the place where C-M-a was invoked and the place
>>>>>> where it does find a defun?
>>>>> 
>>>>> It’s gonna be ugly, but I can take a jab at it this weekend. I’m thinking of a wrapper function that tries to detect DEFUN before falling back to the ordinary tree-sitter defun movement function.
>>>> 
>>>> Thanks.  let's see how ugly it is before deciding whether it's worth it.
>>>> 
>>>>>> DEFUN's cannot be nested, so we don't need to consider that.
>>>>> 
>>>>> Yeah, in general C sources don’t have nested defuns, only C++ ones do.
>>>> 
>>>> No, I meant the use of DEFUN macros in Emacs cannot be nested.
>>> 
>>> Just an update. I didn’t forget about this, but it’s more harder than I thought and I’m still working on it :-(
>> 
>> Any progress with this?  It would be good to have a solution in Emacs
>> 29.2, if possible.
> 
> Ping!

I still don’t have a good solution. But I just realized that we might be able to make a little compromise: what if Emacs recognizes DEFUN, but as two separate parts (the declaration and the body), rather than one? It’s hard to make it recognize DEFUN as a single defun, but making it recognize DEFUN as two parts is easy.

Try this patch and see if you like the behavior. Personally I find it quite alright.

Yuan


[-- Attachment #2: defun-nav.patch --]
[-- Type: application/octet-stream, Size: 5483 bytes --]

From b0e361b430650e95449c59b8643be58d598cde58 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 10 Aug 2023 14:27:29 -0700
Subject: [PATCH] Support defun navigation for DEFUN in c-ts-mode (bug#64442)

Before this change, beginning/end-of-defun just ignores DEFUN in
c-ts-mode. After this change, beginning/end-of-defun can recognize
DEFUN, but a DEFUN definition is considered two defuns. Eg,
beginning/end-of-defun will stop at (1) (2) and (3) in the following
snippet:

(1)DEFUN ("treesit-node-parser",
       Ftreesit_node_parser, Streesit_node_parser,
       1, 1, 0,
       doc: /* Return the parser to which NODE belongs.  */)
  (Lisp_Object node)
(2){
  CHECK_TS_NODE (node);
  return XTS_NODE (node)->parser;
}
(3)

Ideally we want point to only stop at (1) and (3), but that'll be a
lot harder to do.

* lisp/progmodes/c-ts-mode.el:
(c-ts-mode--defun-valid-p): Refactor to take in account of DEFUN body.
(c-ts-mode--emacs-defun-body-p): New function.
(c-ts-base-mode): Add DEFUN and DEFUN body to recognized types.
---
 lisp/progmodes/c-ts-mode.el | 64 +++++++++++++++++++++++--------------
 1 file changed, 40 insertions(+), 24 deletions(-)

diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 7f4f6f11387..0eb1f2a17a7 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -882,29 +882,36 @@ c-ts-mode--defun-name
 (defun c-ts-mode--defun-valid-p (node)
   "Return non-nil if NODE is a valid defun node.
 Ie, NODE is not nested."
-  (or (c-ts-mode--emacs-defun-p node)
-      (not (or (and (member (treesit-node-type node)
-                            '("struct_specifier"
-                              "enum_specifier"
-                              "union_specifier"
-                              "declaration"))
-                    ;; If NODE's type is one of the above, make sure it is
-                    ;; top-level.
-                    (treesit-node-top-level
-                     node (rx (or "function_definition"
-                                  "type_definition"
-                                  "struct_specifier"
-                                  "enum_specifier"
-                                  "union_specifier"
-                                  "declaration"))))
-
-               (and (equal (treesit-node-type node) "declaration")
-                    ;; If NODE is a declaration, make sure it is not a
-                    ;; function declaration.
-                    (equal (treesit-node-type
-                            (treesit-node-child-by-field-name
-                             node "declarator"))
-                           "function_declarator"))))))
+  (let ((top-level-p (lambda (node)
+                       (not (treesit-node-top-level
+                             node (rx (or "function_definition"
+                                          "type_definition"
+                                          "struct_specifier"
+                                          "enum_specifier"
+                                          "union_specifier"
+                                          "declaration")))))))
+    (pcase (treesit-node-type node)
+      ;; The declaration part of a DEFUN.
+      ("expression_statement" (c-ts-mode--emacs-defun-p node))
+      ;; The body of a DEFUN.
+      ("compound_statement" (c-ts-mode--emacs-defun-body-p node))
+      ;; If NODE's type is one of these three, make sure it is
+      ;; top-level.
+      ((or "struct_specifier"
+           "enum_specifier"
+           "union_specifier")
+       (funcall top-level-p node))
+      ;; If NODE is a declaration, make sure it's not a function
+      ;; declaration (we only want function_definition) and is a
+      ;; top-level declaration.
+      ("declaration"
+       (and (not (equal (treesit-node-type
+                         (treesit-node-child-by-field-name
+                          node "declarator"))
+                        "function_declarator"))
+            (funcall top-level-p node)))
+      ;; Other types don't need further verification.
+      (_ t))))
 
 (defun c-ts-mode--defun-for-class-in-imenu-p (node)
   "Check if NODE is a valid entry for the Class subindex.
@@ -957,6 +964,11 @@ c-ts-mode--emacs-defun-p
                t)
               "DEFUN")))
 
+(defun c-ts-mode--emacs-defun-body-p (node)
+  "Return non-nil if NODE is the function body of a DEFUN."
+  (and (equal (treesit-node-type node) "compound_statement")
+       (c-ts-mode--emacs-defun-p (treesit-node-prev-sibling node))))
+
 (defun c-ts-mode--emacs-defun-at-point (&optional range)
   "Return the defun node at point.
 
@@ -1119,7 +1131,11 @@ c-ts-base-mode
                                   "enum_specifier"
                                   "union_specifier"
                                   "class_specifier"
-                                  "namespace_definition"))
+                                  "namespace_definition"
+                                  ;; DEFUN.
+                                  "expression_statement"
+                                  ;; DEFUN body.
+                                  "compound_statement"))
                     #'c-ts-mode--defun-valid-p))
   (setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper)
   (setq-local treesit-defun-name-function #'c-ts-mode--defun-name)
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-08-10 21:33               ` Yuan Fu
@ 2023-08-12 14:59                 ` Eli Zaretskii
  2023-08-14  5:20                   ` Yuan Fu
  0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-08-12 14:59 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 64442

> From: Yuan Fu <casouri@gmail.com>
> Date: Thu, 10 Aug 2023 14:33:09 -0700
> Cc: 64442@debbugs.gnu.org
> 
> I still don’t have a good solution. But I just realized that we might be able to make a little compromise: what if Emacs recognizes DEFUN, but as two separate parts (the declaration and the body), rather than one? It’s hard to make it recognize DEFUN as a single defun, but making it recognize DEFUN as two parts is easy.
> 
> Try this patch and see if you like the behavior. Personally I find it quite alright.

I like this much better than what we have now, thanks.  But I have a
question: can we perhaps recognize the "function" of the body as such,
and then automatically move to the previous defun, which is the right
place?  The "defun" that is the body has no name, so maybe that could
be used as a sign?  That would allow "C-x 4 a" to work inside a DEFUN,
something that still works less reliably with this patch: you must be
in the "first defun" to get it to find the name of the function.

But if improving this is hard, I'll settle for what you have now,
thanks a lot.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-08-12 14:59                 ` Eli Zaretskii
@ 2023-08-14  5:20                   ` Yuan Fu
  2023-08-14 11:59                     ` Eli Zaretskii
  0 siblings, 1 reply; 15+ messages in thread
From: Yuan Fu @ 2023-08-14  5:20 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442



> On Aug 12, 2023, at 7:59 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Thu, 10 Aug 2023 14:33:09 -0700
>> Cc: 64442@debbugs.gnu.org
>> 
>> I still don’t have a good solution. But I just realized that we might be able to make a little compromise: what if Emacs recognizes DEFUN, but as two separate parts (the declaration and the body), rather than one? It’s hard to make it recognize DEFUN as a single defun, but making it recognize DEFUN as two parts is easy.
>> 
>> Try this patch and see if you like the behavior. Personally I find it quite alright.
> 
> I like this much better than what we have now, thanks.  But I have a
> question: can we perhaps recognize the "function" of the body as such,
> and then automatically move to the previous defun, which is the right
> place?  The "defun" that is the body has no name, so maybe that could
> be used as a sign?  

We can easily tell the body from the declaration, but we can’t easily tell whether we should automatically move forward or backward. When point arrives at the point between the declaration and the body, should it move to the beginning of the next defun or the beginning of the declaration? This, plus it’s not straightforward to know whether we are in between a body and a declaration. I really don’t want to add even more cursed hacks into c-ts-mode.el :-)

> That would allow "C-x 4 a" to work inside a DEFUN,
> something that still works less reliably with this patch: you must be
> in the "first defun" to get it to find the name of the function.

C-x 4 a should’ve been fixed already. And it shouldn’t rely on this fix to work. Do you have a recipe for when it doesn’t work?

Yuan




^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-08-14  5:20                   ` Yuan Fu
@ 2023-08-14 11:59                     ` Eli Zaretskii
  2023-08-15  7:30                       ` Yuan Fu
  0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-08-14 11:59 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 64442

> From: Yuan Fu <casouri@gmail.com>
> Date: Sun, 13 Aug 2023 22:20:56 -0700
> Cc: 64442@debbugs.gnu.org
> 
> > I like this much better than what we have now, thanks.  But I have a
> > question: can we perhaps recognize the "function" of the body as such,
> > and then automatically move to the previous defun, which is the right
> > place?  The "defun" that is the body has no name, so maybe that could
> > be used as a sign?  
> 
> We can easily tell the body from the declaration, but we can’t easily tell whether we should automatically move forward or backward. When point arrives at the point between the declaration and the body, should it move to the beginning of the next defun or the beginning of the declaration? This, plus it’s not straightforward to know whether we are in between a body and a declaration. I really don’t want to add even more cursed hacks into c-ts-mode.el :-)

Too bad, but okay.

> > That would allow "C-x 4 a" to work inside a DEFUN,
> > something that still works less reliably with this patch: you must be
> > in the "first defun" to get it to find the name of the function.
> 
> C-x 4 a should’ve been fixed already. And it shouldn’t rely on this fix to work. Do you have a recipe for when it doesn’t work?

Just try it with your patch.  If point is inside the body, the
function's name is not captured by "C-x 4 a".





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-08-14 11:59                     ` Eli Zaretskii
@ 2023-08-15  7:30                       ` Yuan Fu
  2023-08-17  8:18                         ` Eli Zaretskii
  0 siblings, 1 reply; 15+ messages in thread
From: Yuan Fu @ 2023-08-15  7:30 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442

[-- Attachment #1: Type: text/plain, Size: 1639 bytes --]



> On Aug 14, 2023, at 4:59 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Sun, 13 Aug 2023 22:20:56 -0700
>> Cc: 64442@debbugs.gnu.org
>> 
>>> I like this much better than what we have now, thanks.  But I have a
>>> question: can we perhaps recognize the "function" of the body as such,
>>> and then automatically move to the previous defun, which is the right
>>> place?  The "defun" that is the body has no name, so maybe that could
>>> be used as a sign?  
>> 
>> We can easily tell the body from the declaration, but we can’t easily tell whether we should automatically move forward or backward. When point arrives at the point between the declaration and the body, should it move to the beginning of the next defun or the beginning of the declaration? This, plus it’s not straightforward to know whether we are in between a body and a declaration. I really don’t want to add even more cursed hacks into c-ts-mode.el :-)
> 
> Too bad, but okay.
> 
>>> That would allow "C-x 4 a" to work inside a DEFUN,
>>> something that still works less reliably with this patch: you must be
>>> in the "first defun" to get it to find the name of the function.
>> 
>> C-x 4 a should’ve been fixed already. And it shouldn’t rely on this fix to work. Do you have a recipe for when it doesn’t work?
> 
> Just try it with your patch.  If point is inside the body, the
> function's name is not captured by "C-x 4 a”.

My bad, I must’ve been trying C-x 4 a in a different Emacs session, which worked. Anyway, I updated the patch and C-x 4 a should now work.

Yuan


[-- Attachment #2: defun.patch --]
[-- Type: application/octet-stream, Size: 8506 bytes --]

From 779092b28e472d17adfff92c1b9d7403aad7a106 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 10 Aug 2023 14:27:29 -0700
Subject: [PATCH] Support defun navigation for DEFUN in c-ts-mode (bug#64442)

Before this change, beginning/end-of-defun just ignores DEFUN in
c-ts-mode. After this change, beginning/end-of-defun can recognize
DEFUN, but a DEFUN definition is considered two defuns. Eg,
beginning/end-of-defun will stop at (1) (2) and (3) in the following
snippet:

(1)DEFUN ("treesit-node-parser",
       Ftreesit_node_parser, Streesit_node_parser,
       1, 1, 0,
       doc: /* Return the parser to which NODE belongs.  */)
  (Lisp_Object node)
(2){
  CHECK_TS_NODE (node);
  return XTS_NODE (node)->parser;
}
(3)

Ideally we want point to only stop at (1) and (3), but that'll be a
lot harder to do.

* lisp/progmodes/c-ts-mode.el:
(c-ts-mode--defun-valid-p): Refactor to take in account of DEFUN body.
(c-ts-mode--emacs-defun-body-p): New function.
(c-ts-base-mode): Add DEFUN and DEFUN body to recognized types.
(c-ts-mode--emacs-defun-at-point): Now that we recognize both parts of
a DEFUN as defun, c-ts-mode--emacs-defun-at-point needs to be updated
to adapt to it.
---
 lisp/progmodes/c-ts-mode.el | 115 +++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 55 deletions(-)

diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 98797bf3ce7..34a89b4dcad 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -880,29 +880,36 @@ c-ts-mode--defun-name
 (defun c-ts-mode--defun-valid-p (node)
   "Return non-nil if NODE is a valid defun node.
 Ie, NODE is not nested."
-  (or (c-ts-mode--emacs-defun-p node)
-      (not (or (and (member (treesit-node-type node)
-                            '("struct_specifier"
-                              "enum_specifier"
-                              "union_specifier"
-                              "declaration"))
-                    ;; If NODE's type is one of the above, make sure it is
-                    ;; top-level.
-                    (treesit-node-top-level
-                     node (rx (or "function_definition"
-                                  "type_definition"
-                                  "struct_specifier"
-                                  "enum_specifier"
-                                  "union_specifier"
-                                  "declaration"))))
-
-               (and (equal (treesit-node-type node) "declaration")
-                    ;; If NODE is a declaration, make sure it is not a
-                    ;; function declaration.
-                    (equal (treesit-node-type
-                            (treesit-node-child-by-field-name
-                             node "declarator"))
-                           "function_declarator"))))))
+  (let ((top-level-p (lambda (node)
+                       (not (treesit-node-top-level
+                             node (rx (or "function_definition"
+                                          "type_definition"
+                                          "struct_specifier"
+                                          "enum_specifier"
+                                          "union_specifier"
+                                          "declaration")))))))
+    (pcase (treesit-node-type node)
+      ;; The declaration part of a DEFUN.
+      ("expression_statement" (c-ts-mode--emacs-defun-p node))
+      ;; The body of a DEFUN.
+      ("compound_statement" (c-ts-mode--emacs-defun-body-p node))
+      ;; If NODE's type is one of these three, make sure it is
+      ;; top-level.
+      ((or "struct_specifier"
+           "enum_specifier"
+           "union_specifier")
+       (funcall top-level-p node))
+      ;; If NODE is a declaration, make sure it's not a function
+      ;; declaration (we only want function_definition) and is a
+      ;; top-level declaration.
+      ("declaration"
+       (and (not (equal (treesit-node-type
+                         (treesit-node-child-by-field-name
+                          node "declarator"))
+                        "function_declarator"))
+            (funcall top-level-p node)))
+      ;; Other types don't need further verification.
+      (_ t))))
 
 (defun c-ts-mode--defun-for-class-in-imenu-p (node)
   "Check if NODE is a valid entry for the Class subindex.
@@ -955,6 +962,11 @@ c-ts-mode--emacs-defun-p
                t)
               "DEFUN")))
 
+(defun c-ts-mode--emacs-defun-body-p (node)
+  "Return non-nil if NODE is the function body of a DEFUN."
+  (and (equal (treesit-node-type node) "compound_statement")
+       (c-ts-mode--emacs-defun-p (treesit-node-prev-sibling node))))
+
 (defun c-ts-mode--emacs-defun-at-point (&optional range)
   "Return the defun node at point.
 
@@ -969,31 +981,18 @@ c-ts-mode--emacs-defun-at-point
 If RANGE is non-nil, return (BEG . END) where BEG end END
 encloses the whole defun.  This is for when the entire defun
 is required, not just the declaration part for DEFUN."
-  (or (when-let ((node (treesit-defun-at-point)))
-        (if range
-            (cons (treesit-node-start node)
-                (treesit-node-end node))
-            node))
-      (and c-ts-mode-emacs-sources-support
-           (let ((candidate-1 ; For when point is in the DEFUN statement.
-                  (treesit-node-prev-sibling
-                   (treesit-node-top-level
-                    (treesit-node-at (point))
-                    "compound_statement")))
-                 (candidate-2 ; For when point is in the body.
-                  (treesit-node-top-level
-                   (treesit-node-at (point))
-                   "expression_statement")))
-             (when-let
-                 ((node (or (and (c-ts-mode--emacs-defun-p candidate-1)
-                                 candidate-1)
-                            (and (c-ts-mode--emacs-defun-p candidate-2)
-                                 candidate-2))))
-               (if range
-                   (cons (treesit-node-start node)
-                       (treesit-node-end
-                        (treesit-node-next-sibling node)))
-                   node))))))
+  (when-let* ((node (treesit-defun-at-point))
+              (defun-range (cons (treesit-node-start node)
+                                 (treesit-node-end node))))
+    ;; Make some adjustment for DEFUN.
+    (when c-ts-mode-emacs-sources-support
+      (cond ((c-ts-mode--emacs-defun-body-p node)
+             (setq node (treesit-node-prev-sibling node))
+             (setcar defun-range (treesit-node-start node)))
+            ((c-ts-mode--emacs-defun-p node)
+             (setcdr defun-range (treesit-node-end
+                                  (treesit-node-next-sibling node))))))
+    (if range defun-range node)))
 
 (defun c-ts-mode-indent-defun ()
   "Indent the current top-level declaration syntactically.
@@ -1111,13 +1110,19 @@ c-ts-base-mode
 
   ;; Navigation.
   (setq-local treesit-defun-type-regexp
-              (cons (regexp-opt '("function_definition"
-                                  "type_definition"
-                                  "struct_specifier"
-                                  "enum_specifier"
-                                  "union_specifier"
-                                  "class_specifier"
-                                  "namespace_definition"))
+              (cons (regexp-opt (append
+                                 '("function_definition"
+                                   "type_definition"
+                                   "struct_specifier"
+                                   "enum_specifier"
+                                   "union_specifier"
+                                   "class_specifier"
+                                   "namespace_definition")
+                                 (and c-ts-mode-emacs-sources-support
+                                      '(;; DEFUN.
+                                        "expression_statement"
+                                        ;; DEFUN body.
+                                        "compound_statement"))))
                     #'c-ts-mode--defun-valid-p))
   (setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper)
   (setq-local treesit-defun-name-function #'c-ts-mode--defun-name)
-- 
2.41.0


[-- Attachment #3: Type: text/plain, Size: 3 bytes --]





^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C
  2023-08-15  7:30                       ` Yuan Fu
@ 2023-08-17  8:18                         ` Eli Zaretskii
  0 siblings, 0 replies; 15+ messages in thread
From: Eli Zaretskii @ 2023-08-17  8:18 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 64442

> From: Yuan Fu <casouri@gmail.com>
> Date: Tue, 15 Aug 2023 00:30:53 -0700
> Cc: 64442@debbugs.gnu.org
> 
> >>> That would allow "C-x 4 a" to work inside a DEFUN,
> >>> something that still works less reliably with this patch: you must be
> >>> in the "first defun" to get it to find the name of the function.
> >> 
> >> C-x 4 a should’ve been fixed already. And it shouldn’t rely on this fix to work. Do you have a recipe for when it doesn’t work?
> > 
> > Just try it with your patch.  If point is inside the body, the
> > function's name is not captured by "C-x 4 a”.
> 
> My bad, I must’ve been trying C-x 4 a in a different Emacs session, which worked. Anyway, I updated the patch and C-x 4 a should now work.

Thanks, LGTM.  Please install on the emacs-29 branch, and close the
bug when you do.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN  functions in C
  2023-07-03 17:13 bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C Eli Zaretskii
  2023-07-04  8:41 ` Yuan Fu
@ 2023-08-19 22:00 ` Yuan Fu
  1 sibling, 0 replies; 15+ messages in thread
From: Yuan Fu @ 2023-08-19 22:00 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 64442-done


Eli Zaretskii <eliz@gnu.org> writes:

>> From: Yuan Fu <casouri@gmail.com>
>> Date: Tue, 15 Aug 2023 00:30:53 -0700
>> Cc: 64442@debbugs.gnu.org
>> 
>> >>> That would allow "C-x 4 a" to work inside a DEFUN,
>> >>> something that still works less reliably with this patch: you
>> >>> must be
>> >>> in the "first defun" to get it to find the name of the function.
>> >> 
>> >> C-x 4 a should’ve been fixed already. And it shouldn’t rely on
>> >> this fix to work. Do you have a recipe for when it doesn’t work?
>> > 
>> > Just try it with your patch.  If point is inside the body, the
>> > function's name is not captured by "C-x 4 a”.
>> 
>> My bad, I must’ve been trying C-x 4 a in a different Emacs session,
>> which worked. Anyway, I updated the patch and C-x 4 a should now
>> work.
>
> Thanks, LGTM.  Please install on the emacs-29 branch, and close the
> bug when you do.

Pushed, thanks.

Yuan





^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-08-19 22:00 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-03 17:13 bug#64442: 29.0.92; treesit-beginning-of-defun fails in DEFUN functions in C Eli Zaretskii
2023-07-04  8:41 ` Yuan Fu
2023-07-04 11:39   ` Eli Zaretskii
2023-07-07  6:15     ` Yuan Fu
2023-07-07  6:40       ` Eli Zaretskii
2023-07-12  2:10         ` Yuan Fu
2023-07-30  7:10           ` Eli Zaretskii
2023-08-10  9:18             ` Eli Zaretskii
2023-08-10 21:33               ` Yuan Fu
2023-08-12 14:59                 ` Eli Zaretskii
2023-08-14  5:20                   ` Yuan Fu
2023-08-14 11:59                     ` Eli Zaretskii
2023-08-15  7:30                       ` Yuan Fu
2023-08-17  8:18                         ` Eli Zaretskii
2023-08-19 22:00 ` Yuan Fu

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).