diff options
| author | Elías Gabriel Pérez | 2025-12-12 15:45:09 -0600 |
|---|---|---|
| committer | Juri Linkov | 2025-12-20 20:09:41 +0200 |
| commit | e93a9a905799b2e1e371fe6292a003e6f5480e95 (patch) | |
| tree | ed9e5a64422f7e431605ddd0023d6771dc0d6abc | |
| parent | c989d096f19d875a9a96ba56bfc479af4b14f700 (diff) | |
| download | emacs-e93a9a905799b2e1e371fe6292a003e6f5480e95.tar.gz emacs-e93a9a905799b2e1e371fe6292a003e6f5480e95.zip | |
hideshow: Support nested comment block in 'hs-hide-level-recursive'
bug#80009
* doc/emacs/programs.texi (Hideshow): Update documentation.
* lisp/progmodes/hideshow.el (hs-hide-level-recursive): Rework.
(hs-get-first-block-on-line): Minor changes.
(hs--add-indicators, hs-hide-comments-when-hiding-all)
(hs-minor-mode-menu, hs-hide-level, hs-cycle): Update code.
* test/lisp/progmodes/hideshow-tests.el
(hideshow-hide-levels-with-comments-1): New test.
| -rw-r--r-- | doc/emacs/programs.texi | 4 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/progmodes/hideshow.el | 75 | ||||
| -rw-r--r-- | test/lisp/progmodes/hideshow-tests.el | 49 |
4 files changed, 98 insertions, 35 deletions
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index 6acd04d0bae..1e487120272 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi | |||
| @@ -1747,8 +1747,8 @@ Either hide or show all the blocks in the current buffer. (@code{hs-toggle-all}) | |||
| 1747 | 1747 | ||
| 1748 | @table @code | 1748 | @table @code |
| 1749 | @item hs-hide-comments-when-hiding-all | 1749 | @item hs-hide-comments-when-hiding-all |
| 1750 | If non-@code{nil}, @kbd{C-c @@ C-M-h} (@code{hs-hide-all}) hides | 1750 | If non-@code{nil}, @code{hs-hide-all}, @code{hs-cycle} and |
| 1751 | comments too. | 1751 | @code{hs-hide-level} hide comments too. |
| 1752 | 1752 | ||
| 1753 | @item hs-hide-block-behavior | 1753 | @item hs-hide-block-behavior |
| 1754 | This variable controls how @code{hs-hide-block} and | 1754 | This variable controls how @code{hs-hide-block} and |
| @@ -1169,6 +1169,11 @@ FORWARD-SEXP-FUNC, etc., major mode authors should set the corresponding | |||
| 1169 | buffer-local variables 'hs-block-start-regexp', 'hs-c-start-regexp', | 1169 | buffer-local variables 'hs-block-start-regexp', 'hs-c-start-regexp', |
| 1170 | 'hs-forward-sexp-function', etc. | 1170 | 'hs-forward-sexp-function', etc. |
| 1171 | 1171 | ||
| 1172 | +++ | ||
| 1173 | *** 'hs-hide-level' and 'hs-cycle' can now hide comments too. | ||
| 1174 | This is controlled by 'hs-hide-comments-when-hiding-all'. | ||
| 1175 | |||
| 1176 | |||
| 1172 | ** C-ts mode | 1177 | ** C-ts mode |
| 1173 | 1178 | ||
| 1174 | +++ | 1179 | +++ |
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 41e804ae3d0..688f2e7cdff 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el | |||
| @@ -83,7 +83,8 @@ | |||
| 83 | ;; Hideshow provides the following user options: | 83 | ;; Hideshow provides the following user options: |
| 84 | ;; | 84 | ;; |
| 85 | ;; - `hs-hide-comments-when-hiding-all' | 85 | ;; - `hs-hide-comments-when-hiding-all' |
| 86 | ;; self-explanatory! | 86 | ;; If non-nil, `hs-hide-all', `hs-cycle' and `hs-hide-level' will hide |
| 87 | ;; comments too. | ||
| 87 | ;; - `hs-hide-all-non-comment-function' | 88 | ;; - `hs-hide-all-non-comment-function' |
| 88 | ;; If non-nil, after calling `hs-hide-all', this function is called | 89 | ;; If non-nil, after calling `hs-hide-all', this function is called |
| 89 | ;; with no arguments. | 90 | ;; with no arguments. |
| @@ -322,7 +323,9 @@ a block), `hs-show-all' and `hs-show-block'." | |||
| 322 | :version "31.1") | 323 | :version "31.1") |
| 323 | 324 | ||
| 324 | (defcustom hs-hide-comments-when-hiding-all t | 325 | (defcustom hs-hide-comments-when-hiding-all t |
| 325 | "Hide the comments too when you do an `hs-hide-all'." | 326 | "Whether the comments should be hidden. |
| 327 | If non-nil, `hs-hide-all', `hs-cycle' and `hs-hide-level' will hide | ||
| 328 | comments too." | ||
| 326 | :type 'boolean) | 329 | :type 'boolean) |
| 327 | 330 | ||
| 328 | (defcustom hs-hide-block-behavior 'after-bol | 331 | (defcustom hs-hide-block-behavior 'after-bol |
| @@ -560,7 +563,8 @@ This is only used if `hs-indicator-type' is set to `margin' or nil." | |||
| 560 | ["Hide comments when hiding all" | 563 | ["Hide comments when hiding all" |
| 561 | (setq hs-hide-comments-when-hiding-all | 564 | (setq hs-hide-comments-when-hiding-all |
| 562 | (not hs-hide-comments-when-hiding-all)) | 565 | (not hs-hide-comments-when-hiding-all)) |
| 563 | :help "If t also hide comment blocks when doing `hs-hide-all'" | 566 | :help "\ |
| 567 | If t also hide comment blocks when doing `hs-hide-all', `hs-cycle' or `hs-hide-level'" | ||
| 564 | :style toggle :selected hs-hide-comments-when-hiding-all] | 568 | :style toggle :selected hs-hide-comments-when-hiding-all] |
| 565 | ("Reveal on isearch" | 569 | ("Reveal on isearch" |
| 566 | ["Code blocks" (setq hs-isearch-open 'code) | 570 | ["Code blocks" (setq hs-isearch-open 'code) |
| @@ -693,12 +697,13 @@ to find the beginning of the current block.") | |||
| 693 | "Function used to do `hs-find-next-block'. | 697 | "Function used to do `hs-find-next-block'. |
| 694 | It should reposition point at next block start. | 698 | It should reposition point at next block start. |
| 695 | 699 | ||
| 696 | It is called with three arguments REGEXP, BOUND, and COMMENTS. | 700 | It is called with three arguments REGEXP, BOUND, and COMMENTS. REGEXP |
| 697 | REGEXP is a regexp representing block start. When block start is found, | 701 | is a regexp representing block start. When block start is found, |
| 698 | `match-data' should be set using REGEXP. BOUND is a buffer position | 702 | `match-data' should be set using REGEXP. BOUND is a buffer position |
| 699 | that limits the search. When COMMENTS is non-nil, REGEXP matches not | 703 | that limits the search. When COMMENTS is non-nil, REGEXP matches not |
| 700 | only beginning of a block but also beginning of a comment. In this | 704 | only beginning of a block but also beginning of a comment. In this |
| 701 | case, the function should find nearest block or comment. | 705 | case, the function should find nearest block or comment and return |
| 706 | non-nil. | ||
| 702 | 707 | ||
| 703 | Specifying this function is necessary for languages such as Python, | 708 | Specifying this function is necessary for languages such as Python, |
| 704 | where regexp search is not enough to find the beginning of the next | 709 | where regexp search is not enough to find the beginning of the next |
| @@ -871,7 +876,8 @@ line and returns the start position of the first block found. | |||
| 871 | Otherwise, if no block is found, it returns nil. | 876 | Otherwise, if no block is found, it returns nil. |
| 872 | 877 | ||
| 873 | If INCLUDE-COMMENTS is non-nil, also search for a comment block." | 878 | If INCLUDE-COMMENTS is non-nil, also search for a comment block." |
| 874 | (let ((regexp (if include-comments | 879 | (let ((bk-point (point)) |
| 880 | (regexp (if include-comments | ||
| 875 | (concat "\\(" hs-block-start-regexp "\\)" | 881 | (concat "\\(" hs-block-start-regexp "\\)" |
| 876 | "\\|\\(" hs-c-start-regexp "\\)") | 882 | "\\|\\(" hs-c-start-regexp "\\)") |
| 877 | hs-block-start-regexp)) | 883 | hs-block-start-regexp)) |
| @@ -887,6 +893,7 @@ If INCLUDE-COMMENTS is non-nil, also search for a comment block." | |||
| 887 | (if (and beg (hs-hideable-region-p beg end)) | 893 | (if (and beg (hs-hideable-region-p beg end)) |
| 888 | (setq exit (point)) | 894 | (setq exit (point)) |
| 889 | t))))) | 895 | t))))) |
| 896 | (unless exit (goto-char bk-point)) | ||
| 890 | exit)) | 897 | exit)) |
| 891 | 898 | ||
| 892 | (defun hs-get-near-block (&optional include-comment) | 899 | (defun hs-get-near-block (&optional include-comment) |
| @@ -929,21 +936,21 @@ commands." | |||
| 929 | (goto-char beg) | 936 | (goto-char beg) |
| 930 | (while (not (>= (point) end)) | 937 | (while (not (>= (point) end)) |
| 931 | (when-let* ((_ (not (invisible-p (point)))) ; Skip invisible lines | 938 | (when-let* ((_ (not (invisible-p (point)))) ; Skip invisible lines |
| 932 | (block (save-excursion | 939 | (b-start (hs-get-first-block-on-line include-comments))) |
| 933 | (hs-get-first-block-on-line include-comments)))) | 940 | (goto-char b-start) |
| 934 | (goto-char (match-beginning 0)) | 941 | (let ((comment (and include-comments (funcall hs-inside-comment-predicate))) |
| 935 | (if (> arg 1) | 942 | (code (hs-block-positions))) |
| 936 | ;; Find a block recursively according to ARG. | 943 | ;; Find a block recursively according to ARG. |
| 937 | (pcase-let ((`(,beg ,end) (or (and include-comments | 944 | (if (> arg 1) |
| 938 | (funcall hs-inside-comment-predicate)) | 945 | ;; Nested comment blocks in a comment block are impossible, |
| 939 | (hs-block-positions)))) | 946 | ;; so skip them. |
| 940 | (hs-hide-level-recursive (1- arg) beg end include-comments)) | 947 | (if comment |
| 941 | ;; Now hide the block we found. | 948 | (goto-char (cadr comment)) |
| 942 | (if func (funcall func) | 949 | (pcase-let ((`(,beg ,end) code)) |
| 943 | (hs-hide-block-at-point | 950 | (hs-hide-level-recursive (1- arg) beg end include-comments))) |
| 944 | (and include-comments (funcall hs-inside-comment-predicate)))) | 951 | ;; Now hide the block we found. |
| 945 | (when progress | 952 | (if func (funcall func) (hs-hide-block-at-point comment)) |
| 946 | (progress-reporter-update progress (point))))) | 953 | (when progress (progress-reporter-update progress (point)))))) |
| 947 | (forward-line 1)) | 954 | (forward-line 1)) |
| 948 | (goto-char end)) | 955 | (goto-char end)) |
| 949 | 956 | ||
| @@ -1086,10 +1093,9 @@ the overlay: `invisible' `hs'. Also, depending on variable | |||
| 1086 | (remove-overlays beg end 'hs-indicator t) | 1093 | (remove-overlays beg end 'hs-indicator t) |
| 1087 | 1094 | ||
| 1088 | (while (not (>= (point) end)) | 1095 | (while (not (>= (point) end)) |
| 1089 | (save-excursion | 1096 | (when-let* ((_ (not (invisible-p (point)))) ; Skip invisible lines |
| 1090 | (when-let* ((_ (not (invisible-p (point)))) ; Skip invisible lines | 1097 | (b-beg (hs-get-first-block-on-line))) |
| 1091 | (b-beg (hs-get-first-block-on-line))) | 1098 | (hs--make-indicators-overlays b-beg)) |
| 1092 | (hs--make-indicators-overlays b-beg))) | ||
| 1093 | ;; Only 1 indicator per line | 1099 | ;; Only 1 indicator per line |
| 1094 | (forward-line)) | 1100 | (forward-line)) |
| 1095 | `(jit-lock-bounds ,beg . ,end)) | 1101 | `(jit-lock-bounds ,beg . ,end)) |
| @@ -1354,14 +1360,14 @@ The hook `hs-hide-hook' is run; see `run-hooks'." | |||
| 1354 | (message "Hiding blocks ...") | 1360 | (message "Hiding blocks ...") |
| 1355 | (if (hs-get-near-block) | 1361 | (if (hs-get-near-block) |
| 1356 | ;; Hide block if we are looking at one. | 1362 | ;; Hide block if we are looking at one. |
| 1357 | (apply #'hs-hide-level-recursive arg | 1363 | (pcase-let ((`(,beg ,end) (hs-block-positions))) |
| 1358 | (hs-block-positions)) | 1364 | (hs-hide-level-recursive arg beg end hs-hide-comments-when-hiding-all)) |
| 1359 | ;; Otherwise hide all the blocks in the current buffer | 1365 | ;; Otherwise hide all the blocks in the current buffer |
| 1360 | (hs-hide-level-recursive | 1366 | (hs-hide-level-recursive |
| 1361 | ;; Increment ARG by 1, avoiding it acts like | 1367 | ;; Increment ARG by 1, avoiding it acts like |
| 1362 | ;; `hs-hide-all' | 1368 | ;; `hs-hide-all' |
| 1363 | (1+ arg) | 1369 | (1+ arg) (point-min) (point-max) |
| 1364 | (point-min) (point-max))) | 1370 | hs-hide-comments-when-hiding-all)) |
| 1365 | (message "Hiding blocks ... done")) | 1371 | (message "Hiding blocks ... done")) |
| 1366 | (run-hooks 'hs-hide-hook))) | 1372 | (run-hooks 'hs-hide-hook))) |
| 1367 | 1373 | ||
| @@ -1422,8 +1428,9 @@ only blocks which are that many levels below the level of point." | |||
| 1422 | (hs-toggle-hiding) | 1428 | (hs-toggle-hiding) |
| 1423 | (message "Toggle visibility")) | 1429 | (message "Toggle visibility")) |
| 1424 | ((> level 1) | 1430 | ((> level 1) |
| 1425 | (apply #'hs-hide-level-recursive level | 1431 | (pcase-let ((`(,beg ,end) (hs-block-positions))) |
| 1426 | (hs-block-positions)) | 1432 | (hs-hide-level-recursive |
| 1433 | level beg end hs-hide-comments-when-hiding-all)) | ||
| 1427 | (message "Hide %d level" level)) | 1434 | (message "Hide %d level" level)) |
| 1428 | (t | 1435 | (t |
| 1429 | (let* (hs-allow-nesting | 1436 | (let* (hs-allow-nesting |
| @@ -1440,7 +1447,9 @@ only blocks which are that many levels below the level of point." | |||
| 1440 | ;; Hide the children blocks if the parent block is hidden | 1447 | ;; Hide the children blocks if the parent block is hidden |
| 1441 | ((and (= (overlay-start ov) (car block)) | 1448 | ((and (= (overlay-start ov) (car block)) |
| 1442 | (= (overlay-end ov) (cadr block))) | 1449 | (= (overlay-end ov) (cadr block))) |
| 1443 | (apply #'hs-hide-level-recursive 1 block) | 1450 | (hs-hide-level-recursive |
| 1451 | 1 (car block) (cadr block) | ||
| 1452 | hs-hide-comments-when-hiding-all) | ||
| 1444 | (message "Hide first nested blocks")) | 1453 | (message "Hide first nested blocks")) |
| 1445 | ;; Otherwise show all in the parent block, we cannot use | 1454 | ;; Otherwise show all in the parent block, we cannot use |
| 1446 | ;; `hs-show-block' here because we already know the | 1455 | ;; `hs-show-block' here because we already know the |
diff --git a/test/lisp/progmodes/hideshow-tests.el b/test/lisp/progmodes/hideshow-tests.el index 49f661a2390..cf13a064f39 100644 --- a/test/lisp/progmodes/hideshow-tests.el +++ b/test/lisp/progmodes/hideshow-tests.el | |||
| @@ -281,6 +281,55 @@ main(int argc, char **argv) | |||
| 281 | } | 281 | } |
| 282 | ")))) | 282 | ")))) |
| 283 | 283 | ||
| 284 | (ert-deftest hideshow-hide-levels-with-comments-1 () | ||
| 285 | "Should hide 2nd and then 3rd level blocks including comment blocks." | ||
| 286 | (hideshow-tests-with-temp-buffer | ||
| 287 | lisp-data-mode | ||
| 288 | ;; 2nd | ||
| 289 | " | ||
| 290 | ;; comment | ||
| 291 | ;; comment | ||
| 292 | ;; comment | ||
| 293 | |||
| 294 | (list | ||
| 295 | ;; comment2 | ||
| 296 | ;; comment2 | ||
| 297 | (list | ||
| 298 | ;; comment3 | ||
| 299 | ;; comment3 | ||
| 300 | '(lv3 | ||
| 301 | lv3))) | ||
| 302 | " | ||
| 303 | (hs-hide-level-recursive 2 (point-min) (point-max) :comments) | ||
| 304 | (should (string= | ||
| 305 | (hideshow-tests-visible-string) | ||
| 306 | " | ||
| 307 | ;; comment | ||
| 308 | ;; comment | ||
| 309 | ;; comment | ||
| 310 | |||
| 311 | (list | ||
| 312 | ;; comment2 | ||
| 313 | (list)) | ||
| 314 | ")) | ||
| 315 | ;; 3rd | ||
| 316 | (hs-show-all) | ||
| 317 | (hs-hide-level-recursive 3 (point-min) (point-max) :comments) | ||
| 318 | (should (string= | ||
| 319 | (hideshow-tests-visible-string) | ||
| 320 | " | ||
| 321 | ;; comment | ||
| 322 | ;; comment | ||
| 323 | ;; comment | ||
| 324 | |||
| 325 | (list | ||
| 326 | ;; comment2 | ||
| 327 | ;; comment2 | ||
| 328 | (list | ||
| 329 | ;; comment3 | ||
| 330 | '(lv3))) | ||
| 331 | ")))) | ||
| 332 | |||
| 284 | (ert-deftest hideshow-toggle-hiding-1 () | 333 | (ert-deftest hideshow-toggle-hiding-1 () |
| 285 | "Should toggle hiding/showing of a block." | 334 | "Should toggle hiding/showing of a block." |
| 286 | (let ((contents " | 335 | (let ((contents " |