From cb761eb7ac4197706658a68bb853c3fc8909d84e Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 15 Dec 2022 17:44:07 -0800 Subject: [PATCH] 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. --- lisp/progmodes/c-ts-mode.el | 35 ++++++++++++++---- lisp/progmodes/sh-script.el | 2 ++ lisp/treesit.el | 71 ++++++++++++++++++------------------- 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index ff2ff63fd82..97e29ca13bc 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -531,6 +531,27 @@ the subtrees." (if (looking-at "\\s<\\|\n") (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 () "Indent the current top-level declaration syntactically. @@ -559,12 +580,14 @@ the subtrees." ;; Navigation. (setq-local treesit-defun-type-regexp - (regexp-opt '("function_definition" - "type_definition" - "struct_specifier" - "enum_specifier" - "union_specifier" - "class_specifier"))) + (cons (regexp-opt '("function_definition" + "type_definition" + "struct_specifier" + "enum_specifier" + "union_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 ;; function_definitions, so we need to find the top-level node. diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index 76e8d5b0748..85da9e89f9a 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -1613,6 +1613,7 @@ This mode automatically falls back to `sh-mode' if the buffer is not written in Bash or sh." :syntax-table sh-mode-syntax-table (when (treesit-ready-p 'bash) + (treesit-parser-create 'bash) (setq-local treesit-font-lock-feature-list '(( comment function) ( command declaration-command keyword string) @@ -1620,6 +1621,7 @@ not written in Bash or sh." ( bracket delimiter misc-punctuation operator))) (setq-local treesit-font-lock-settings sh-mode--treesit-settings) + (setq-local treesit-defun-type-regexp "function_definition") (treesit-major-mode-setup))) (advice-add 'bash-ts-mode :around #'sh--redirect-bash-ts-mode diff --git a/lisp/treesit.el b/lisp/treesit.el index 58b85ddba39..353a9bc12c1 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1624,40 +1624,37 @@ For the detailed semantic see `treesit-defun-prefer-top-level'." finally return node)))) (defun treesit-beginning-of-defun (&optional arg) - "Tree-sitter `beginning-of-defun' function. -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 () - "Tree-sitter `end-of-defun' function." - ;; Why not simply get the largest node at point: when point is at - ;; (point-min), that gives us the root node. - (let* ((node (treesit-search-forward - (treesit-node-at (point)) treesit-defun-type-regexp t t)) - (top (treesit--defun-maybe-top-level node))) - ;; Technically `end-of-defun' should only call this function when - ;; point is at the beginning of a defun, so TOP should always be - ;; non-nil, but things happen, and we want to be safe, so check - ;; for TOP anyway. - (when top - (goto-char (treesit-node-end top))))) + "Move backward to the beginning of a defun. + +With argument ARG, do it that many times. Negative ARG means +move forward to the ARGth following beginning of defun. + +If search is successful, return t, otherwise return nil. + +This is a tree-sitter equivalent of `beginning-of-defun'. +Behavior of this function depends on `treesit-defun-type-regexp' +and `treesit-defun-skipper'." + (interactive "^p") + (when-let ((dest (treesit--navigate-defun (point) (- arg) 'beg))) + (goto-char dest) + (when treesit-defun-skipper + (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 () "Skips spaces after navigating a defun. @@ -1953,8 +1950,10 @@ before calling this function." (setq-local indent-region-function #'treesit-indent-region)) ;; Navigation. (when treesit-defun-type-regexp - (setq-local beginning-of-defun-function #'treesit-beginning-of-defun) - (setq-local end-of-defun-function #'treesit-end-of-defun))) + (keymap-set (current-local-map) " " + #'treesit-beginning-of-defun) + (keymap-set (current-local-map) " " + #'treesit-end-of-defun))) ;;; Debugging -- 2.39.2