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)
"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 @@ files using the DEFUN macro."
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 @@ function returns the declaration node.
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 @@ BEG and END are described in `treesit-range-rules'."
;; 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)