hideshow: New minor mode 'hs-indentation-mode'. (Bug#80179)
This minor mode configures hs-minor-mode to use indentation-based folding. * lisp/progmodes/hideshow.el (hs-hideable-block-p): New function. (hs-indentation-respect-end-block): New option. (hs-indentation--store-vars): New variable. (hs-cycle-filter, hs-get-first-block-on-line, hs-get-near-block) (hs-find-block-beg-fn--default): Adapt code to use 'hs-hideable-block-p'. (hs-block-positions): Update. (hs-indentation-mode): New minor mode. * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes * test/lisp/progmodes/hideshow-tests.el: Add 'require'. (hideshow-check-indentation-folding): New test.
This commit is contained in:
committed by
Eli Zaretskii
parent
1f04208898
commit
c911495fb1
@@ -1688,6 +1688,13 @@ row). Just what constitutes a block depends on the major mode. In C
|
|||||||
mode and related modes, blocks are delimited by braces, while in Lisp
|
mode and related modes, blocks are delimited by braces, while in Lisp
|
||||||
mode they are delimited by parentheses. Multi-line comments also
|
mode they are delimited by parentheses. Multi-line comments also
|
||||||
count as blocks.
|
count as blocks.
|
||||||
|
|
||||||
|
Additionally, Hideshow mode supports optional indentation-based
|
||||||
|
hiding/showing. By default this is disabled; to enable it, turn on the
|
||||||
|
buffer-local minor mode @code{hs-indentation-mode}. Enabling
|
||||||
|
@code{hs-indentation-mode} does not require that @code{hs-minor-mode} is
|
||||||
|
already enabled.
|
||||||
|
|
||||||
@vindex hs-prefix-map
|
@vindex hs-prefix-map
|
||||||
|
|
||||||
Hideshow mode provides the following commands (defined in @code{hs-prefix-map}):
|
Hideshow mode provides the following commands (defined in @code{hs-prefix-map}):
|
||||||
@@ -1743,6 +1750,7 @@ Either hide or show all the blocks in the current buffer. (@code{hs-toggle-all})
|
|||||||
@vindex hs-isearch-open
|
@vindex hs-isearch-open
|
||||||
@vindex hs-hide-block-behavior
|
@vindex hs-hide-block-behavior
|
||||||
@vindex hs-cycle-filter
|
@vindex hs-cycle-filter
|
||||||
|
@vindex hs-indentation-respect-end-block
|
||||||
These variables can be used to customize Hideshow mode:
|
These variables can be used to customize Hideshow mode:
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
@@ -1795,6 +1803,11 @@ block. Its value should be either @code{code} (unhide only code
|
|||||||
blocks), @code{comment} (unhide only comments), @code{t} (unhide both
|
blocks), @code{comment} (unhide only comments), @code{t} (unhide both
|
||||||
code blocks and comments), or @code{nil} (unhide neither code blocks
|
code blocks and comments), or @code{nil} (unhide neither code blocks
|
||||||
nor comments). The default value is @code{code}.
|
nor comments). The default value is @code{code}.
|
||||||
|
|
||||||
|
@item hs-indentation-respect-end-block
|
||||||
|
This variable controls whether the end of the block should be hidden
|
||||||
|
together with the hidden region. This only has effect if
|
||||||
|
@code{hs-indentation-mode} is enabled.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@node Symbol Completion
|
@node Symbol Completion
|
||||||
|
|||||||
8
etc/NEWS
8
etc/NEWS
@@ -1374,6 +1374,14 @@ buffer-local variables 'hs-block-start-regexp', 'hs-c-start-regexp',
|
|||||||
*** 'hs-hide-level' can now hide comments too.
|
*** 'hs-hide-level' can now hide comments too.
|
||||||
This is controlled by 'hs-hide-comments-when-hiding-all'.
|
This is controlled by 'hs-hide-comments-when-hiding-all'.
|
||||||
|
|
||||||
|
+++
|
||||||
|
*** New minor mode 'hs-indentation-mode'.
|
||||||
|
This buffer-local minor mode configures 'hs-indentation-mode' to detect
|
||||||
|
blocks based on indentation.
|
||||||
|
|
||||||
|
The new user option 'hs-indentation-respect-end-block' can be used to
|
||||||
|
adjust the hiding range for this minor mode.
|
||||||
|
|
||||||
** C-ts mode
|
** C-ts mode
|
||||||
|
|
||||||
+++
|
+++
|
||||||
|
|||||||
@@ -63,6 +63,8 @@
|
|||||||
;; hideshow minor mode by typing `M-x hs-minor-mode'. After hideshow is
|
;; hideshow minor mode by typing `M-x hs-minor-mode'. After hideshow is
|
||||||
;; activated or deactivated, `hs-minor-mode-hook' is run with `run-hooks'.
|
;; activated or deactivated, `hs-minor-mode-hook' is run with `run-hooks'.
|
||||||
;;
|
;;
|
||||||
|
;; To enable indentation-based hiding/showing turn on `hs-indentation-mode'.
|
||||||
|
;;
|
||||||
;; Additionally, Joseph Eydelnant writes:
|
;; Additionally, Joseph Eydelnant writes:
|
||||||
;; I enjoy your package hideshow.el Version 5.24 2001/02/13
|
;; I enjoy your package hideshow.el Version 5.24 2001/02/13
|
||||||
;; a lot and I've been looking for the following functionality:
|
;; a lot and I've been looking for the following functionality:
|
||||||
@@ -83,28 +85,16 @@
|
|||||||
;; Hideshow provides the following user options:
|
;; Hideshow provides the following user options:
|
||||||
;;
|
;;
|
||||||
;; - `hs-hide-comments-when-hiding-all'
|
;; - `hs-hide-comments-when-hiding-all'
|
||||||
;; If non-nil, `hs-hide-all', `hs-cycle' and `hs-hide-level' will hide
|
|
||||||
;; comments too.
|
|
||||||
;; - `hs-hide-all-non-comment-function'
|
;; - `hs-hide-all-non-comment-function'
|
||||||
;; If non-nil, after calling `hs-hide-all', this function is called
|
|
||||||
;; with no arguments.
|
|
||||||
;; - `hs-isearch-open'
|
;; - `hs-isearch-open'
|
||||||
;; What kind of hidden blocks to open when doing isearch.
|
|
||||||
;; - `hs-set-up-overlay'
|
;; - `hs-set-up-overlay'
|
||||||
;; Function called with one arg (an overlay), intended to customize
|
|
||||||
;; the block hiding appearance.
|
|
||||||
;; - `hs-display-lines-hidden'
|
;; - `hs-display-lines-hidden'
|
||||||
;; Displays the number of hidden lines next to the ellipsis.
|
|
||||||
;; - `hs-show-indicators'
|
;; - `hs-show-indicators'
|
||||||
;; Display indicators to show and toggle the block hiding.
|
|
||||||
;; - `hs-indicator-type'
|
;; - `hs-indicator-type'
|
||||||
;; Which indicator type should be used for the block indicators.
|
|
||||||
;; - `hs-indicator-maximum-buffer-size'
|
;; - `hs-indicator-maximum-buffer-size'
|
||||||
;; Max buffer size in bytes where the indicators should be enabled.
|
|
||||||
;; - `hs-allow-nesting'
|
;; - `hs-allow-nesting'
|
||||||
;; If non-nil, hiding remembers internal blocks.
|
|
||||||
;; - `hs-cycle-filter'
|
;; - `hs-cycle-filter'
|
||||||
;; Control where typing a `TAB' cycles the visibility.
|
;; - `hs-indentation-respect-end-block'
|
||||||
;;
|
;;
|
||||||
;; The variable `hs-hide-all-non-comment-function' may be useful if you
|
;; The variable `hs-hide-all-non-comment-function' may be useful if you
|
||||||
;; only want to hide some N levels blocks for some languages/files or
|
;; only want to hide some N levels blocks for some languages/files or
|
||||||
@@ -458,10 +448,7 @@ Currently it affects only the command `hs-toggle-hiding' by default,
|
|||||||
but it can be easily replaced with the command `hs-cycle'."
|
but it can be easily replaced with the command `hs-cycle'."
|
||||||
:type `(choice (const :tag "Nowhere" nil)
|
:type `(choice (const :tag "Nowhere" nil)
|
||||||
(const :tag "Everywhere on the headline" t)
|
(const :tag "Everywhere on the headline" t)
|
||||||
(const :tag "At block beginning"
|
(const :tag "At block beginning" hs-hideable-block-p)
|
||||||
,(lambda ()
|
|
||||||
(pcase-let ((`(,beg ,end) (hs-block-positions)))
|
|
||||||
(and beg (hs-hideable-region-p beg end)))))
|
|
||||||
(const :tag "At line beginning" bolp)
|
(const :tag "At line beginning" bolp)
|
||||||
(const :tag "Not at line beginning"
|
(const :tag "Not at line beginning"
|
||||||
,(lambda () (not (bolp))))
|
,(lambda () (not (bolp))))
|
||||||
@@ -469,6 +456,18 @@ but it can be easily replaced with the command `hs-cycle'."
|
|||||||
(function :tag "Custom filter function"))
|
(function :tag "Custom filter function"))
|
||||||
:version "31.1")
|
:version "31.1")
|
||||||
|
|
||||||
|
;; Used in `hs-indentation-mode'
|
||||||
|
(defcustom hs-indentation-respect-end-block nil
|
||||||
|
"If non-nil, the end of the block will not be hidden.
|
||||||
|
This only has effect if `hs-indentation-mode' is enabled.
|
||||||
|
|
||||||
|
NOTE: For some modes, enabling this may result in hiding wrong parts of
|
||||||
|
the buffer. If this happens, enable this only for some modes (usually
|
||||||
|
using `add-hook')."
|
||||||
|
:type 'boolean
|
||||||
|
:local t
|
||||||
|
:version "31.1")
|
||||||
|
|
||||||
;;;; Icons
|
;;;; Icons
|
||||||
|
|
||||||
(define-icon hs-indicator-hide nil
|
(define-icon hs-indicator-hide nil
|
||||||
@@ -616,6 +615,9 @@ Note that `mode-line-format' is buffer-local.")
|
|||||||
;; Used in `hs-toggle-all'
|
;; Used in `hs-toggle-all'
|
||||||
(defvar-local hs--toggle-all-state)
|
(defvar-local hs--toggle-all-state)
|
||||||
|
|
||||||
|
;; Used in `hs-indentation-mode'
|
||||||
|
(defvar-local hs-indentation--store-vars nil)
|
||||||
|
|
||||||
|
|
||||||
;;;; API variables
|
;;;; API variables
|
||||||
|
|
||||||
@@ -788,6 +790,17 @@ Skip \"internal\" overlays if `hs-allow-nesting' is non-nil."
|
|||||||
(and beg end
|
(and beg end
|
||||||
(< beg (save-excursion (goto-char end) (pos-bol)))))
|
(< beg (save-excursion (goto-char end) (pos-bol)))))
|
||||||
|
|
||||||
|
(defun hs-hideable-block-p (&optional include-comment)
|
||||||
|
"Return t if block at point is hideable.
|
||||||
|
If INCLUDE-COMMENT is non-nil, include comments first.
|
||||||
|
|
||||||
|
If there is no block at point, return nil."
|
||||||
|
(pcase-let ((`(,beg ,end)
|
||||||
|
(or (and include-comment
|
||||||
|
(funcall hs-inside-comment-predicate))
|
||||||
|
(hs-block-positions))))
|
||||||
|
(hs-hideable-region-p beg end)))
|
||||||
|
|
||||||
(defun hs-already-hidden-p ()
|
(defun hs-already-hidden-p ()
|
||||||
"Return non-nil if point is in an already-hidden block, otherwise nil."
|
"Return non-nil if point is in an already-hidden block, otherwise nil."
|
||||||
(save-excursion
|
(save-excursion
|
||||||
@@ -820,14 +833,13 @@ This is for code block positions only, for comments use
|
|||||||
(save-match-data
|
(save-match-data
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(when (funcall hs-looking-at-block-start-predicate)
|
(when (funcall hs-looking-at-block-start-predicate)
|
||||||
(let* ((beg (match-end 0)) end)
|
(let ((beg (match-end 0)) end)
|
||||||
;; `beg' is the point at the block beginning, which may need
|
;; `beg' is the point at the block beginning, which may need
|
||||||
;; to be adjusted
|
;; to be adjusted
|
||||||
(when adjust-beg
|
(when adjust-beg
|
||||||
(setq beg (pos-eol))
|
(setq beg (if hs-adjust-block-beginning-function
|
||||||
(save-excursion
|
(funcall hs-adjust-block-beginning-function beg)
|
||||||
(when hs-adjust-block-beginning-function
|
(pos-eol))))
|
||||||
(goto-char (funcall hs-adjust-block-beginning-function beg)))))
|
|
||||||
|
|
||||||
(goto-char (match-beginning hs-block-start-mdata-select))
|
(goto-char (match-beginning hs-block-start-mdata-select))
|
||||||
(condition-case _
|
(condition-case _
|
||||||
@@ -897,13 +909,9 @@ If INCLUDE-COMMENTS is non-nil, also search for a comment block."
|
|||||||
(funcall hs-find-next-block-function regexp (pos-eol) include-comments)
|
(funcall hs-find-next-block-function regexp (pos-eol) include-comments)
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (match-beginning 0))
|
(goto-char (match-beginning 0))
|
||||||
(pcase-let ((`(,beg ,end)
|
(if (hs-hideable-block-p include-comments)
|
||||||
(or (and include-comments
|
(setq exit (point))
|
||||||
(funcall hs-inside-comment-predicate))
|
t))))
|
||||||
(hs-block-positions))))
|
|
||||||
(if (and beg (hs-hideable-region-p beg end))
|
|
||||||
(setq exit (point))
|
|
||||||
t)))))
|
|
||||||
(unless exit (goto-char bk-point))
|
(unless exit (goto-char bk-point))
|
||||||
exit))
|
exit))
|
||||||
|
|
||||||
@@ -930,10 +938,10 @@ Intended to be used in commands."
|
|||||||
(goto-char pos)
|
(goto-char pos)
|
||||||
t)
|
t)
|
||||||
|
|
||||||
((and (or (funcall hs-looking-at-block-start-predicate)
|
((and (or (hs-hideable-block-p)
|
||||||
(and (forward-line 0)
|
(and (forward-line 0)
|
||||||
(funcall hs-find-block-beginning-function)))
|
(funcall hs-find-block-beginning-function)
|
||||||
(apply #'hs-hideable-region-p (hs-block-positions)))
|
(hs-hideable-block-p))))
|
||||||
t))))
|
t))))
|
||||||
|
|
||||||
(defun hs-hide-level-recursive (arg beg end &optional include-comments func progress)
|
(defun hs-hide-level-recursive (arg beg end &optional include-comments func progress)
|
||||||
@@ -1268,7 +1276,7 @@ region (point BOUND)."
|
|||||||
Return point, or nil if original point was not in a block."
|
Return point, or nil if original point was not in a block."
|
||||||
(let ((here (point)) done)
|
(let ((here (point)) done)
|
||||||
;; look if current line is block start
|
;; look if current line is block start
|
||||||
(if (funcall hs-looking-at-block-start-predicate)
|
(if (hs-hideable-block-p)
|
||||||
here
|
here
|
||||||
;; look backward for the start of a block that contains the cursor
|
;; look backward for the start of a block that contains the cursor
|
||||||
(save-excursion
|
(save-excursion
|
||||||
@@ -1276,8 +1284,8 @@ Return point, or nil if original point was not in a block."
|
|||||||
(goto-char (match-beginning 0))
|
(goto-char (match-beginning 0))
|
||||||
;; go again if in a comment or a string
|
;; go again if in a comment or a string
|
||||||
(or (save-match-data (nth 8 (syntax-ppss)))
|
(or (save-match-data (nth 8 (syntax-ppss)))
|
||||||
(not (setq done (and (<= here (cadr (hs-block-positions)))
|
(not (setq done (pcase-let ((`(_ ,end) (hs-block-positions)))
|
||||||
(point))))))))
|
(and end (<= here end) (point)))))))))
|
||||||
(when done (goto-char done)))))
|
(when done (goto-char done)))))
|
||||||
|
|
||||||
;; This function is not used anymore (Bug#700).
|
;; This function is not used anymore (Bug#700).
|
||||||
@@ -1478,6 +1486,61 @@ only blocks which are that many levels below the level of point."
|
|||||||
(hs-hide-all))
|
(hs-hide-all))
|
||||||
(setq-local hs--toggle-all-state (not hs--toggle-all-state)))
|
(setq-local hs--toggle-all-state (not hs--toggle-all-state)))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(define-minor-mode hs-indentation-mode
|
||||||
|
"Toggle indentation-based hiding/showing."
|
||||||
|
:group 'hideshow
|
||||||
|
(if hs-indentation-mode
|
||||||
|
(progn
|
||||||
|
(setq hs-indentation--store-vars
|
||||||
|
(buffer-local-set-state
|
||||||
|
hs-forward-sexp-function
|
||||||
|
(lambda (_)
|
||||||
|
(let ((size (current-indentation)) end)
|
||||||
|
(save-match-data
|
||||||
|
(save-excursion
|
||||||
|
(forward-line 1) ; Start from next line
|
||||||
|
(while (and (not (eobp))
|
||||||
|
(re-search-forward hs-block-start-regexp nil t)
|
||||||
|
(> (current-indentation) size))
|
||||||
|
(setq end (point))
|
||||||
|
(forward-line 1))))
|
||||||
|
(when end (goto-char end) (end-of-line))))
|
||||||
|
hs-block-start-regexp (rx (0+ blank) (1+ nonl))
|
||||||
|
hs-block-end-regexp nil
|
||||||
|
hs-adjust-block-end-function
|
||||||
|
;; Adjust line to the "end of the block" (Usually this is
|
||||||
|
;; the next line after the position by
|
||||||
|
;; `hs-forward-sexp-function' with the same indentation
|
||||||
|
;; level as the block start)
|
||||||
|
(if hs-indentation-respect-end-block
|
||||||
|
(lambda (beg)
|
||||||
|
(save-excursion
|
||||||
|
(when (and (not (eobp))
|
||||||
|
(forward-line 1)
|
||||||
|
(not (looking-at-p (rx (0+ blank) eol)))
|
||||||
|
(= (current-indentation)
|
||||||
|
(save-excursion
|
||||||
|
(goto-char beg)
|
||||||
|
(current-indentation)))
|
||||||
|
(progn (back-to-indentation)
|
||||||
|
(not (hs-hideable-block-p))))
|
||||||
|
(point))))
|
||||||
|
hs-adjust-block-end-function)
|
||||||
|
;; Set the other variables to their default values
|
||||||
|
hs-looking-at-block-start-predicate #'hs-looking-at-block-start-p--default
|
||||||
|
hs-find-next-block-function #'hs-find-next-block-fn--default
|
||||||
|
hs-find-block-beginning-function #'hs-find-block-beg-fn--default
|
||||||
|
hs-c-start-regexp (string-trim-right (regexp-quote comment-start))))
|
||||||
|
;; Refresh indicators (if needed)
|
||||||
|
(when (and hs-show-indicators hs-minor-mode)
|
||||||
|
(hs-minor-mode -1)
|
||||||
|
(hs-minor-mode +1)))
|
||||||
|
(buffer-local-restore-state hs-indentation--store-vars)
|
||||||
|
(when (and hs-show-indicators hs-minor-mode)
|
||||||
|
(hs-minor-mode -1)
|
||||||
|
(hs-minor-mode +1))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(define-minor-mode hs-minor-mode
|
(define-minor-mode hs-minor-mode
|
||||||
"Minor mode to selectively hide/show code and comment blocks.
|
"Minor mode to selectively hide/show code and comment blocks.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
;; Dependencies for testing:
|
;; Dependencies for testing:
|
||||||
(require 'cc-mode)
|
(require 'cc-mode)
|
||||||
|
(require 'sh-script)
|
||||||
|
|
||||||
|
|
||||||
(defmacro hideshow-tests-with-temp-buffer (mode contents &rest body)
|
(defmacro hideshow-tests-with-temp-buffer (mode contents &rest body)
|
||||||
@@ -475,6 +476,35 @@ def test1 ():
|
|||||||
(beginning-of-line)
|
(beginning-of-line)
|
||||||
(should-not (hs-block-positions)))))
|
(should-not (hs-block-positions)))))
|
||||||
|
|
||||||
|
(ert-deftest hideshow-check-indentation-folding ()
|
||||||
|
"Check indentation-based folding with and without end of the block respected."
|
||||||
|
(let ((contents "
|
||||||
|
if [1]
|
||||||
|
then 2
|
||||||
|
fi"))
|
||||||
|
(hideshow-tests-with-temp-buffer
|
||||||
|
sh-mode
|
||||||
|
contents
|
||||||
|
(hs-indentation-mode t)
|
||||||
|
(hideshow-tests-look-at "if")
|
||||||
|
(beginning-of-line)
|
||||||
|
(hs-hide-block)
|
||||||
|
(should (string=
|
||||||
|
(hideshow-tests-visible-string)
|
||||||
|
"
|
||||||
|
if [1]
|
||||||
|
fi"))
|
||||||
|
(hs-show-all)
|
||||||
|
;; End of the block respected
|
||||||
|
(hs-indentation-mode nil) ; Reset variables
|
||||||
|
(setq-local hs-indentation-respect-end-block t)
|
||||||
|
(hs-indentation-mode t)
|
||||||
|
(hs-hide-block)
|
||||||
|
(should (string=
|
||||||
|
(hideshow-tests-visible-string)
|
||||||
|
"
|
||||||
|
if [1]fi")))))
|
||||||
|
|
||||||
(provide 'hideshow-tests)
|
(provide 'hideshow-tests)
|
||||||
|
|
||||||
;;; hideshow-tests.el ends here
|
;;; hideshow-tests.el ends here
|
||||||
|
|||||||
Reference in New Issue
Block a user