Use the new tree-sitter commands

* lisp/progmodes/c-ts-mode.el (c-ts-mode--defun-valid-p)
(c-ts-mode--defun-skipper): New functions.
(c-ts-base-mode): Setup defun navigation.
* lisp/progmodes/sh-script.el (bash-ts-mode): Setup defun navigation.
* lisp/treesit.el (treesit-beginning-of-defun)
(treesit-end-of-defun): Change to new implementation, which is
intended to be used as commands.
(treesit-major-mode-setup): Setup remap for beginning/end-of-defun
commands.
This commit is contained in:
Yuan Fu 2022-12-15 17:44:07 -08:00
parent 037407ad95
commit cb761eb7ac
No known key found for this signature in database
GPG key ID: 56E19BC57664A442
3 changed files with 65 additions and 41 deletions

View file

@ -531,6 +531,27 @@ the subtrees."
(if (looking-at "\\s<\\|\n") (if (looking-at "\\s<\\|\n")
(forward-line 1))))) (forward-line 1)))))
(defun c-ts-mode--defun-valid-p (node)
(if (string-match-p
(rx (or "struct_specifier"
"enum_specifier"
"union_specifier"))
(treesit-node-type node))
(null
(treesit-node-top-level
node (rx (or "function_definition"
"type_definition"))))
t))
(defun c-ts-mode--defun-skipper ()
"Custom defun skipper for `c-ts-mode' and friends.
Structs in C ends with a semicolon, but the semicolon is not
considered part of the struct node, so point would stop before
the semicolon. This function skips the semicolon."
(when (looking-at (rx (* (or " " "\t")) ";"))
(goto-char (match-end 0)))
(treesit-default-defun-skipper))
(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.
@ -559,12 +580,14 @@ the subtrees."
;; Navigation. ;; Navigation.
(setq-local treesit-defun-type-regexp (setq-local treesit-defun-type-regexp
(regexp-opt '("function_definition" (cons (regexp-opt '("function_definition"
"type_definition" "type_definition"
"struct_specifier" "struct_specifier"
"enum_specifier" "enum_specifier"
"union_specifier" "union_specifier"
"class_specifier"))) "class_specifier"))
#'c-ts-mode--defun-valid-p))
(setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper)
;; Nodes like struct/enum/union_specifier can appear in ;; Nodes like struct/enum/union_specifier can appear in
;; function_definitions, so we need to find the top-level node. ;; function_definitions, so we need to find the top-level node.

View file

@ -1613,6 +1613,7 @@ This mode automatically falls back to `sh-mode' if the buffer is
not written in Bash or sh." not written in Bash or sh."
:syntax-table sh-mode-syntax-table :syntax-table sh-mode-syntax-table
(when (treesit-ready-p 'bash) (when (treesit-ready-p 'bash)
(treesit-parser-create 'bash)
(setq-local treesit-font-lock-feature-list (setq-local treesit-font-lock-feature-list
'(( comment function) '(( comment function)
( command declaration-command keyword string) ( command declaration-command keyword string)
@ -1620,6 +1621,7 @@ not written in Bash or sh."
( bracket delimiter misc-punctuation operator))) ( bracket delimiter misc-punctuation operator)))
(setq-local treesit-font-lock-settings (setq-local treesit-font-lock-settings
sh-mode--treesit-settings) sh-mode--treesit-settings)
(setq-local treesit-defun-type-regexp "function_definition")
(treesit-major-mode-setup))) (treesit-major-mode-setup)))
(advice-add 'bash-ts-mode :around #'sh--redirect-bash-ts-mode (advice-add 'bash-ts-mode :around #'sh--redirect-bash-ts-mode

View file

@ -1624,40 +1624,37 @@ For the detailed semantic see `treesit-defun-prefer-top-level'."
finally return node)))) finally return node))))
(defun treesit-beginning-of-defun (&optional arg) (defun treesit-beginning-of-defun (&optional arg)
"Tree-sitter `beginning-of-defun' function. "Move backward to the beginning of a defun.
ARG is the same as in `beginning-of-defun'."
(let ((arg (or arg 1))
(node (treesit-node-at (point))))
(if (> arg 0)
;; Go backward.
(while (and (> arg 0)
(setq node (treesit-search-forward-goto
node treesit-defun-type-regexp t t)))
(setq node (treesit--defun-maybe-top-level node))
(setq arg (1- arg)))
;; Go forward.
(while (and (< arg 0)
(setq node (treesit-search-forward-goto
node treesit-defun-type-regexp)))
(setq node (treesit--defun-maybe-top-level node))
(setq arg (1+ arg))))
(when node
(goto-char (treesit-node-start node))
t)))
(defun treesit-end-of-defun () With argument ARG, do it that many times. Negative ARG means
"Tree-sitter `end-of-defun' function." move forward to the ARGth following beginning of defun.
;; Why not simply get the largest node at point: when point is at
;; (point-min), that gives us the root node. If search is successful, return t, otherwise return nil.
(let* ((node (treesit-search-forward
(treesit-node-at (point)) treesit-defun-type-regexp t t)) This is a tree-sitter equivalent of `beginning-of-defun'.
(top (treesit--defun-maybe-top-level node))) Behavior of this function depends on `treesit-defun-type-regexp'
;; Technically `end-of-defun' should only call this function when and `treesit-defun-skipper'."
;; point is at the beginning of a defun, so TOP should always be (interactive "^p")
;; non-nil, but things happen, and we want to be safe, so check (when-let ((dest (treesit--navigate-defun (point) (- arg) 'beg)))
;; for TOP anyway. (goto-char dest)
(when top (when treesit-defun-skipper
(goto-char (treesit-node-end top))))) (funcall treesit-defun-skipper))
t))
(defun treesit-end-of-defun (&optional arg _)
"Move forward to next end of defun.
With argument ARG, do it that many times.
Negative argument -N means move back to Nth preceding end of defun.
This is a tree-sitter equivalent of `end-of-defun'. Behavior of
this function depends on `treesit-defun-type-regexp' and
`treesit-defun-skipper'."
(interactive "^p\nd")
(when-let ((dest (treesit--navigate-defun (point) arg 'end)))
(goto-char dest)
(when treesit-defun-skipper
(funcall treesit-defun-skipper))))
(defun treesit-default-defun-skipper () (defun treesit-default-defun-skipper ()
"Skips spaces after navigating a defun. "Skips spaces after navigating a defun.
@ -1953,8 +1950,10 @@ before calling this function."
(setq-local indent-region-function #'treesit-indent-region)) (setq-local indent-region-function #'treesit-indent-region))
;; Navigation. ;; Navigation.
(when treesit-defun-type-regexp (when treesit-defun-type-regexp
(setq-local beginning-of-defun-function #'treesit-beginning-of-defun) (keymap-set (current-local-map) "<remap> <beginning-of-defun>"
(setq-local end-of-defun-function #'treesit-end-of-defun))) #'treesit-beginning-of-defun)
(keymap-set (current-local-map) "<remap> <end-of-defun>"
#'treesit-end-of-defun)))
;;; Debugging ;;; Debugging