diff options
| author | Juri Linkov | 2025-01-28 21:07:16 +0200 |
|---|---|---|
| committer | Juri Linkov | 2025-01-28 21:07:16 +0200 |
| commit | 2e3b085d447bc2cd1a0e779145be9cab9a15d7af (patch) | |
| tree | e286ff6a611db4b3cbb7cb2d9095d6cb44e5980f | |
| parent | cd6aed5c9e905a8fc7608a172a689e99ff27fe25 (diff) | |
| download | emacs-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/NEWS | 4 | ||||
| -rw-r--r-- | lisp/progmodes/hideshow.el | 47 | ||||
| -rw-r--r-- | lisp/treesit.el | 87 |
3 files changed, 123 insertions, 15 deletions
| @@ -1110,6 +1110,10 @@ the tree-sitter library. The new function 'treesit-show-paren-data' is | |||
| 1110 | used to communicate the tree-sitter parsing results to | 1110 | used 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'. | ||
| 1114 | All commands from hideshow.el can selectively display blocks | ||
| 1115 | defined by the new treesit thing 'list'. | ||
| 1116 | |||
| 1113 | *** New treesit thing 'comment'. | 1117 | *** New treesit thing 'comment'. |
| 1114 | The new variable 'forward-comment-function' is set to the new function | 1118 | The 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 | |||
| 481 | Python, where `looking-at' and `syntax-ppss' check is not enough | 481 | Python, where `looking-at' and `syntax-ppss' check is not enough |
| 482 | to check if the point is at the block start.") | 482 | to 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. |
| 486 | You can display this in the mode line by adding the symbol `hs-headline' | 489 | You 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 | |||
| 644 | beginning. If we are inside of a comment but this condition is not met, | 651 | beginning. If we are inside of a comment but this condition is not met, |
| 645 | we return a list having a nil as its car and the end of comment position | 652 | we return a list having a nil as its car and the end of comment position |
| 646 | as cdr." | 653 | as 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)) |