(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.
;; 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.
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)
( 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
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.
(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) "<remap> <beginning-of-defun>"
+ #'treesit-beginning-of-defun)
+ (keymap-set (current-local-map) "<remap> <end-of-defun>"
+ #'treesit-end-of-defun)))
;;; Debugging