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.
This commit is contained in:
Yuan Fu 2023-08-10 14:27:29 -07:00
parent 781ddd7e7d
commit baeb2d71ae
No known key found for this signature in database
GPG key ID: 56E19BC57664A442

View file

@ -880,29 +880,36 @@ Return nil if NODE is not a defun node or doesn't have a name."
(defun c-ts-mode--defun-valid-p (node) (defun c-ts-mode--defun-valid-p (node)
"Return non-nil if NODE is a valid defun node. "Return non-nil if NODE is a valid defun node.
Ie, NODE is not nested." Ie, NODE is not nested."
(or (c-ts-mode--emacs-defun-p node) (let ((top-level-p (lambda (node)
(not (or (and (member (treesit-node-type node) (not (treesit-node-top-level
'("struct_specifier" node (rx (or "function_definition"
"enum_specifier" "type_definition"
"union_specifier" "struct_specifier"
"declaration")) "enum_specifier"
;; If NODE's type is one of the above, make sure it is "union_specifier"
;; top-level. "declaration")))))))
(treesit-node-top-level (pcase (treesit-node-type node)
node (rx (or "function_definition" ;; The declaration part of a DEFUN.
"type_definition" ("expression_statement" (c-ts-mode--emacs-defun-p node))
"struct_specifier" ;; The body of a DEFUN.
"enum_specifier" ("compound_statement" (c-ts-mode--emacs-defun-body-p node))
"union_specifier" ;; If NODE's type is one of these three, make sure it is
"declaration")))) ;; top-level.
((or "struct_specifier"
(and (equal (treesit-node-type node) "declaration") "enum_specifier"
;; If NODE is a declaration, make sure it is not a "union_specifier")
;; function declaration. (funcall top-level-p node))
(equal (treesit-node-type ;; If NODE is a declaration, make sure it's not a function
(treesit-node-child-by-field-name ;; declaration (we only want function_definition) and is a
node "declarator")) ;; top-level declaration.
"function_declarator")))))) ("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) (defun c-ts-mode--defun-for-class-in-imenu-p (node)
"Check if NODE is a valid entry for the Class subindex. "Check if NODE is a valid entry for the Class subindex.
@ -955,6 +962,11 @@ files using the DEFUN macro."
t) t)
"DEFUN"))) "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) (defun c-ts-mode--emacs-defun-at-point (&optional range)
"Return the defun node at point. "Return the defun node at point.
@ -969,31 +981,18 @@ function returns the declaration node.
If RANGE is non-nil, return (BEG . END) where BEG end END If RANGE is non-nil, return (BEG . END) where BEG end END
encloses the whole defun. This is for when the entire defun encloses the whole defun. This is for when the entire defun
is required, not just the declaration part for DEFUN." is required, not just the declaration part for DEFUN."
(or (when-let ((node (treesit-defun-at-point))) (when-let* ((node (treesit-defun-at-point))
(if range (defun-range (cons (treesit-node-start node)
(cons (treesit-node-start node) (treesit-node-end node))))
(treesit-node-end node)) ;; Make some adjustment for DEFUN.
node)) (when c-ts-mode-emacs-sources-support
(and c-ts-mode-emacs-sources-support (cond ((c-ts-mode--emacs-defun-body-p node)
(let ((candidate-1 ; For when point is in the DEFUN statement. (setq node (treesit-node-prev-sibling node))
(treesit-node-prev-sibling (setcar defun-range (treesit-node-start node)))
(treesit-node-top-level ((c-ts-mode--emacs-defun-p node)
(treesit-node-at (point)) (setcdr defun-range (treesit-node-end
"compound_statement"))) (treesit-node-next-sibling node))))))
(candidate-2 ; For when point is in the body. (if range defun-range node)))
(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))))))
(defun c-ts-mode-indent-defun () (defun c-ts-mode-indent-defun ()
"Indent the current top-level declaration syntactically. "Indent the current top-level declaration syntactically.
@ -1111,13 +1110,19 @@ BEG and END are described in `treesit-range-rules'."
;; Navigation. ;; Navigation.
(setq-local treesit-defun-type-regexp (setq-local treesit-defun-type-regexp
(cons (regexp-opt '("function_definition" (cons (regexp-opt (append
"type_definition" '("function_definition"
"struct_specifier" "type_definition"
"enum_specifier" "struct_specifier"
"union_specifier" "enum_specifier"
"class_specifier" "union_specifier"
"namespace_definition")) "class_specifier"
"namespace_definition")
(and c-ts-mode-emacs-sources-support
'(;; DEFUN.
"expression_statement"
;; DEFUN body.
"compound_statement"))))
#'c-ts-mode--defun-valid-p)) #'c-ts-mode--defun-valid-p))
(setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper) (setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper)
(setq-local treesit-defun-name-function #'c-ts-mode--defun-name) (setq-local treesit-defun-name-function #'c-ts-mode--defun-name)