From aa1d697f6b991238e234a469801a9c80577fc806 Mon Sep 17 00:00:00 2001 From: =?utf8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= Date: Fri, 2 May 2025 22:44:07 -0600 Subject: [PATCH] Prettify and improve hideshow (bug#78234) Buttonize the ellipsis and optionally display in the ellipsis the total number of hidden lines. * lisp/progmodes/hideshow.el (hs-display-lines-hidden): New user option. (hs-ellipsis): New face. (hs-make-overlay): Tweak. (hs--get-ellipsis): New function. * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes. (cherry picked from commit 8b6e1d8435712a1d312244f9fe3a43d8b346f49a) --- doc/emacs/programs.texi | 5 +++ lisp/progmodes/hideshow.el | 65 +++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index 4168519da8d..c62b6c252c6 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1631,6 +1631,7 @@ Hide all blocks @var{n} levels below this block @end table @vindex hs-hide-comments-when-hiding-all +@vindex hs-display-lines-hidden @vindex hs-isearch-open @vindex hs-special-modes-alist These variables can be used to customize Hideshow mode: @@ -1640,6 +1641,10 @@ Hide all blocks @var{n} levels below this block If non-@code{nil}, @kbd{C-c @@ C-M-h} (@code{hs-hide-all}) hides comments too. +@item hs-display-lines-hidden +If non-@code{nil}, display the number of hidden lines next to the +ellipsis. + @item hs-isearch-open This variable specifies the conditions under which incremental search should unhide a hidden block when matching text occurs within the diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index c1d62fb92ab..e7987cab5fb 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -228,10 +228,22 @@ :prefix "hs-" :group 'languages) +(defface hs-ellipsis + '((t :height 0.80 :box (:line-width -1) :inherit default)) + "Face used for hideshow ellipsis. +Note: If `selective-display' ellipsis already has a face, hideshow will +use that face for the ellipsis instead." + :version "31.1") + (defcustom hs-hide-comments-when-hiding-all t "Hide the comments too when you do an `hs-hide-all'." :type 'boolean) +(defcustom hs-display-lines-hidden nil + "If non-nil, display the number of hidden lines next to the ellipsis." + :type 'boolean + :version "31.1") + (defcustom hs-minor-mode-hook nil "Hook called when hideshow minor mode is activated or deactivated." :type 'hook @@ -528,8 +540,17 @@ to call with the newly initialized overlay." (io (if (eq 'block hs-isearch-open) ;; backward compatibility -- `block'<=>`code' 'code - hs-isearch-open))) + hs-isearch-open)) + (map (make-sparse-keymap))) (overlay-put ov 'invisible 'hs) + (define-key map (kbd "") #'hs-show-block) + (overlay-put ov 'display + (propertize + (hs--get-ellipsis b e) + 'mouse-face + 'highlight + 'help-echo "mouse-1: show hidden lines" + 'keymap map)) (overlay-put ov 'hs kind) (overlay-put ov 'hs-b-offset b-offset) (overlay-put ov 'hs-e-offset e-offset) @@ -540,6 +561,48 @@ to call with the newly initialized overlay." (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) ov)) +(defun hs--get-ellipsis (b e) + "Helper function for `hs-make-overlay'. +This return the ellipsis string to use and its face." + (cond* + ((bind* + (d-t-ellipsis (display-table-slot standard-display-table 'selective-display)) + (d-t-face ; Get only the first glyph face + (if d-t-ellipsis (glyph-face (aref d-t-ellipsis 0)))) + ;; Convert the ellipsis vector from display-table to a propertized + ;; string + (ellipsis + (cond + ((and d-t-ellipsis + (not (stringp d-t-ellipsis)) + ;; Ensure the vector is not empty + (not (length= d-t-ellipsis 0))) + (let ((string + (mapconcat + (lambda (g) + (apply #'propertize + (char-to-string (glyph-char g)) + (if (glyph-face g) + (list 'face (glyph-face g))))) + d-t-ellipsis))) + ;; If the ellipsis string has no face, use `hs-ellipsis' + (if (get-text-property 0 'face string) + string + (propertize string 'face 'hs-ellipsis)))) + ((char-displayable-on-frame-p ?…) + (propertize "…" 'face 'hs-ellipsis)) + (t (propertize "..." 'face 'hs-ellipsis)))))) + (hs-display-lines-hidden + (let ((lines (1- (count-lines b e)))) + (concat + (propertize + (concat (number-to-string lines) + (if (= lines 1) " line" " lines")) + 'face (or d-t-face 'hs-ellipsis)) + ellipsis))) + (t + ellipsis))) + (defun hs-isearch-show (ov) "Delete overlay OV, and set `hs-headline' to nil. -- 2.39.5