aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuri Linkov2025-01-28 21:07:16 +0200
committerJuri Linkov2025-01-28 21:07:16 +0200
commit2e3b085d447bc2cd1a0e779145be9cab9a15d7af (patch)
treee286ff6a611db4b3cbb7cb2d9095d6cb44e5980f
parentcd6aed5c9e905a8fc7608a172a689e99ff27fe25 (diff)
downloademacs-2e3b085d447bc2cd1a0e779145be9cab9a15d7af.tar.gz
emacs-2e3b085d447bc2cd1a0e779145be9cab9a15d7af.zip
Hideshow support for tree-sitter in hs-minor-mode (bug#75609)
* lisp/progmodes/hideshow.el (hs-inside-comment-p-func): New buffer-local variable. (hs-hide-block-at-point): Check if 'hs-block-end-regexp' is a string or a function. (hs-inside-comment-p): Move body to 'hs-inside-comment-p--default'. Call 'hs-inside-comment-p-func' if it's a function. (hs-inside-comment-p--default): New function with body from 'hs-inside-comment-p'. (hs-hide-all): Don't use 'hs-block-start-regexp' when it's not a string. (hs-minor-mode): Don't call 'hs-grok-mode-type' when 'hs-inside-comment-p-func' already has a buffer-local value. * lisp/treesit.el (treesit-hs-block-end) (treesit-hs-find-block-beginning, treesit-hs-find-next-block) (treesit-hs-looking-at-block-start-p) (treesit-hs-inside-comment-p): New functions. (treesit-major-mode-setup): Set hs-minor-mode buffer-local variables.
-rw-r--r--etc/NEWS4
-rw-r--r--lisp/progmodes/hideshow.el47
-rw-r--r--lisp/treesit.el87
3 files changed, 123 insertions, 15 deletions
diff --git a/etc/NEWS b/etc/NEWS
index ec61f2bc430..09507adecb8 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1110,6 +1110,10 @@ the tree-sitter library. The new function 'treesit-show-paren-data' is
1110used to communicate the tree-sitter parsing results to 1110used to communicate the tree-sitter parsing results to
1111'show-paren-mode'. 1111'show-paren-mode'.
1112 1112
1113*** Tree-sitter enabled modes now properly support 'hs-minor-mode'.
1114All commands from hideshow.el can selectively display blocks
1115defined by the new treesit thing 'list'.
1116
1113*** New treesit thing 'comment'. 1117*** New treesit thing 'comment'.
1114The new variable 'forward-comment-function' is set to the new function 1118The new variable 'forward-comment-function' is set to the new function
1115'treesit-forward-comment' if a major mode defines the thing 'comment'. 1119'treesit-forward-comment' if a major mode defines the thing 'comment'.
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el
index 823eb0527c6..157a8473631 100644
--- a/lisp/progmodes/hideshow.el
+++ b/lisp/progmodes/hideshow.el
@@ -95,7 +95,7 @@
95;; nested level in addition to the top-level: 95;; nested level in addition to the top-level:
96;; 96;;
97;; (defun ttn-hs-hide-level-1 () 97;; (defun ttn-hs-hide-level-1 ()
98;; (when (hs-looking-at-block-start-p) 98;; (when (funcall hs-looking-at-block-start-p-func)
99;; (hs-hide-level 1)) 99;; (hs-hide-level 1))
100;; (forward-sexp 1)) 100;; (forward-sexp 1))
101;; (setq hs-hide-all-non-comment-function 'ttn-hs-hide-level-1) 101;; (setq hs-hide-all-non-comment-function 'ttn-hs-hide-level-1)
@@ -481,6 +481,9 @@ Specifying this function is necessary for languages such as
481Python, where `looking-at' and `syntax-ppss' check is not enough 481Python, where `looking-at' and `syntax-ppss' check is not enough
482to check if the point is at the block start.") 482to check if the point is at the block start.")
483 483
484(defvar-local hs-inside-comment-p-func nil
485 "Function used to check if point is inside a comment.")
486
484(defvar hs-headline nil 487(defvar hs-headline nil
485 "Text of the line where a hidden block begins, set during isearch. 488 "Text of the line where a hidden block begins, set during isearch.
486You can display this in the mode line by adding the symbol `hs-headline' 489You can display this in the mode line by adding the symbol `hs-headline'
@@ -625,9 +628,13 @@ and then further adjusted to be at the end of the line."
625 (setq p (line-end-position))) 628 (setq p (line-end-position)))
626 ;; `q' is the point at the end of the block 629 ;; `q' is the point at the end of the block
627 (hs-forward-sexp mdata 1) 630 (hs-forward-sexp mdata 1)
628 (setq q (if (looking-back hs-block-end-regexp nil) 631 (setq q (cond ((and (stringp hs-block-end-regexp)
629 (match-beginning 0) 632 (looking-back hs-block-end-regexp nil))
630 (point))) 633 (match-beginning 0))
634 ((functionp hs-block-end-regexp)
635 (funcall hs-block-end-regexp)
636 (match-beginning 0))
637 (t (point))))
631 (when (and (< p q) (> (count-lines p q) 1)) 638 (when (and (< p q) (> (count-lines p q) 1))
632 (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) 639 (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p)))
633 (delete-overlay ov)) 640 (delete-overlay ov))
@@ -644,6 +651,11 @@ its starting line there is only whitespace preceding the actual comment
644beginning. If we are inside of a comment but this condition is not met, 651beginning. If we are inside of a comment but this condition is not met,
645we return a list having a nil as its car and the end of comment position 652we return a list having a nil as its car and the end of comment position
646as cdr." 653as cdr."
654 (if (functionp hs-inside-comment-p-func)
655 (funcall hs-inside-comment-p-func)
656 (hs-inside-comment-p--default)))
657
658(defun hs-inside-comment-p--default ()
647 (save-excursion 659 (save-excursion
648 ;; the idea is to look backwards for a comment start regexp, do a 660 ;; the idea is to look backwards for a comment start regexp, do a
649 ;; forward comment, and see if we are inside, then extend 661 ;; forward comment, and see if we are inside, then extend
@@ -850,14 +862,16 @@ If `hs-hide-comments-when-hiding-all' is non-nil, also hide the comments."
850 (syntax-propertize (point-max)) 862 (syntax-propertize (point-max))
851 (let ((spew (make-progress-reporter "Hiding all blocks..." 863 (let ((spew (make-progress-reporter "Hiding all blocks..."
852 (point-min) (point-max))) 864 (point-min) (point-max)))
853 (re (concat "\\(" 865 (re (when (stringp hs-block-start-regexp)
854 hs-block-start-regexp 866 (concat "\\("
855 "\\)" 867 hs-block-start-regexp
856 (if hs-hide-comments-when-hiding-all 868 "\\)"
857 (concat "\\|\\(" 869 (if (and hs-hide-comments-when-hiding-all
858 hs-c-start-regexp 870 (stringp hs-c-start-regexp))
859 "\\)") 871 (concat "\\|\\("
860 "")))) 872 hs-c-start-regexp
873 "\\)")
874 "")))))
861 (while (funcall hs-find-next-block-func re (point-max) 875 (while (funcall hs-find-next-block-func re (point-max)
862 hs-hide-comments-when-hiding-all) 876 hs-hide-comments-when-hiding-all)
863 (if (match-beginning 1) 877 (if (match-beginning 1)
@@ -869,7 +883,9 @@ If `hs-hide-comments-when-hiding-all' is non-nil, also hide the comments."
869 (hs-hide-block-at-point t)) 883 (hs-hide-block-at-point t))
870 ;; Go to end of matched data to prevent from getting stuck 884 ;; Go to end of matched data to prevent from getting stuck
871 ;; with an endless loop. 885 ;; with an endless loop.
872 (when (looking-at hs-block-start-regexp) 886 (when (if (stringp hs-block-start-regexp)
887 (looking-at hs-block-start-regexp)
888 (eq (point) (match-beginning 0)))
873 (goto-char (match-end 0))))) 889 (goto-char (match-end 0)))))
874 ;; found a comment, probably 890 ;; found a comment, probably
875 (let ((c-reg (hs-inside-comment-p))) 891 (let ((c-reg (hs-inside-comment-p)))
@@ -1008,7 +1024,10 @@ Key bindings:
1008 (setq hs-headline nil) 1024 (setq hs-headline nil)
1009 (if hs-minor-mode 1025 (if hs-minor-mode
1010 (progn 1026 (progn
1011 (hs-grok-mode-type) 1027 ;; Use such heuristics that if one buffer-local variable
1028 ;; is already defined, don't overwrite other variables too.
1029 (unless (buffer-local-value 'hs-inside-comment-p-func (current-buffer))
1030 (hs-grok-mode-type))
1012 ;; Turn off this mode if we change major modes. 1031 ;; Turn off this mode if we change major modes.
1013 (add-hook 'change-major-mode-hook 1032 (add-hook 'change-major-mode-hook
1014 #'turn-off-hideshow 1033 #'turn-off-hideshow
diff --git a/lisp/treesit.el b/lisp/treesit.el
index f7d43ab44f8..33fcd9e7207 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -3489,6 +3489,81 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in
3489 (setq level (1+ level))) 3489 (setq level (1+ level)))
3490 (if (zerop level) 1 level))) 3490 (if (zerop level) 1 level)))
3491 3491
3492;;; Hideshow mode
3493
3494(defun treesit-hs-block-end ()
3495 "Tree-sitter implementation of `hs-block-end-regexp'."
3496 (let* ((pred 'list)
3497 (thing (treesit-thing-at
3498 (if (bobp) (point) (1- (point))) pred))
3499 (end (when thing (treesit-node-end thing)))
3500 (last (when thing (treesit-node-child thing -1)))
3501 (beg (if last (treesit-node-start last)
3502 (if (bobp) (point) (1- (point))))))
3503 (when (and thing (eq (point) end))
3504 (set-match-data (list beg end))
3505 t)))
3506
3507(defun treesit-hs-find-block-beginning ()
3508 "Tree-sitter implementation of `hs-find-block-beginning-func'."
3509 (let* ((pred 'list)
3510 (thing (treesit-thing-at (point) pred))
3511 (beg (when thing (treesit-node-start thing)))
3512 (end (when beg (min (1+ beg) (point-max)))))
3513 (when thing
3514 (goto-char beg)
3515 (set-match-data (list beg end))
3516 t)))
3517
3518(defun treesit-hs-find-next-block (_regexp maxp comments)
3519 "Tree-sitter implementation of `hs-find-next-block-func'."
3520 (when (not comments)
3521 (forward-comment (point-max)))
3522 (let* ((comment-pred
3523 (when comments
3524 (if (treesit-thing-defined-p 'comment (treesit-language-at (point)))
3525 'comment "comment")))
3526 (pred (if comment-pred (append '(or list) (list comment-pred)) 'list))
3527 ;; `treesit-navigate-thing' can't find a thing at bobp,
3528 ;; so use `treesit-thing-at' to match at bobp.
3529 (current (treesit-thing-at (point) pred))
3530 (beg (or (and current (eq (point) (treesit-node-start current)) (point))
3531 (treesit-navigate-thing (point) 1 'beg pred)))
3532 ;; Check if we found a list or a comment
3533 (list-thing (when beg (treesit-thing-at beg 'list)))
3534 (comment-thing (when beg (treesit-thing-at beg comment-pred)))
3535 (comment-p (and comment-thing (eq beg (treesit-node-start comment-thing))))
3536 (thing (if comment-p comment-thing list-thing))
3537 (end (if thing (min (1+ (treesit-node-start thing)) (point-max)))))
3538 (when (and end (< end maxp))
3539 (goto-char end)
3540 (set-match-data
3541 (if (and comments comment-p)
3542 (list beg end nil nil beg end)
3543 (list beg end beg end)))
3544 t)))
3545
3546(defun treesit-hs-looking-at-block-start-p ()
3547 "Tree-sitter implementation of `hs-looking-at-block-start-p-func'."
3548 (let* ((pred 'list)
3549 (thing (treesit-thing-at (point) pred))
3550 (beg (when thing (treesit-node-start thing)))
3551 (end (min (1+ (point)) (point-max))))
3552 (when (and thing (eq (point) beg))
3553 (set-match-data (list beg end))
3554 t)))
3555
3556(defun treesit-hs-inside-comment-p ()
3557 "Tree-sitter implementation of `hs-inside-comment-p-func'."
3558 (let* ((comment-pred
3559 (if (treesit-thing-defined-p 'comment (treesit-language-at (point)))
3560 'comment "comment"))
3561 (thing (or (treesit-thing-at (point) comment-pred)
3562 (unless (bobp)
3563 (treesit-thing-at (1- (point)) comment-pred)))))
3564 (when thing
3565 (list (treesit-node-start thing) (treesit-node-end thing)))))
3566
3492;;; Show paren mode 3567;;; Show paren mode
3493 3568
3494(defun treesit-show-paren-data--categorize (pos &optional end-p) 3569(defun treesit-show-paren-data--categorize (pos &optional end-p)
@@ -3672,7 +3747,17 @@ before calling this function."
3672 (setq-local forward-list-function #'treesit-forward-list) 3747 (setq-local forward-list-function #'treesit-forward-list)
3673 (setq-local down-list-function #'treesit-down-list) 3748 (setq-local down-list-function #'treesit-down-list)
3674 (setq-local up-list-function #'treesit-up-list) 3749 (setq-local up-list-function #'treesit-up-list)
3675 (setq-local show-paren-data-function #'treesit-show-paren-data)) 3750 (setq-local show-paren-data-function #'treesit-show-paren-data)
3751 (setq-local hs-c-start-regexp nil
3752 hs-block-start-regexp nil
3753 hs-block-start-mdata-select 0
3754 hs-block-end-regexp #'treesit-hs-block-end
3755 hs-forward-sexp-func #'forward-list
3756 hs-adjust-block-beginning nil
3757 hs-find-block-beginning-func #'treesit-hs-find-block-beginning
3758 hs-find-next-block-func #'treesit-hs-find-next-block
3759 hs-looking-at-block-start-p-func #'treesit-hs-looking-at-block-start-p
3760 hs-inside-comment-p-func #'treesit-hs-inside-comment-p))
3676 3761
3677 (when (treesit-thing-defined-p 'sentence nil) 3762 (when (treesit-thing-defined-p 'sentence nil)
3678 (setq-local forward-sentence-function #'treesit-forward-sentence)) 3763 (setq-local forward-sentence-function #'treesit-forward-sentence))