From unknown Fri Sep 19 18:23:45 2025 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Mailer: MIME-tools 5.509 (Entity 5.509) Content-Type: text/plain; charset=utf-8 From: bug#79294 <79294@debbugs.gnu.org> To: bug#79294 <79294@debbugs.gnu.org> Subject: Status: [PATCH] Add hideable indicators for hideshow. Reply-To: bug#79294 <79294@debbugs.gnu.org> Date: Sat, 20 Sep 2025 01:23:45 +0000 retitle 79294 [PATCH] Add hideable indicators for hideshow. reassign 79294 emacs submitter 79294 Elijah Gabe P=C3=A9rez severity 79294 normal tag 79294 patch thanks From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 23 00:56:56 2025 Received: (at submit) by debbugs.gnu.org; 23 Aug 2025 04:56:56 +0000 Received: from localhost ([127.0.0.1]:38385 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1upgJC-0006FQ-FB for submit@debbugs.gnu.org; Sat, 23 Aug 2025 00:56:56 -0400 Received: from lists.gnu.org ([2001:470:142::17]:39596) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1upgJ9-0006F3-E6 for submit@debbugs.gnu.org; Sat, 23 Aug 2025 00:56:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1upgJ1-0002c1-1I for bug-gnu-emacs@gnu.org; Sat, 23 Aug 2025 00:56:43 -0400 Received: from mail-ot1-x343.google.com ([2607:f8b0:4864:20::343]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1upgIy-0002Cz-57 for bug-gnu-emacs@gnu.org; Sat, 23 Aug 2025 00:56:42 -0400 Received: by mail-ot1-x343.google.com with SMTP id 46e09a7af769-74381fdb5faso1619935a34.2 for ; Fri, 22 Aug 2025 21:56:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1755924996; x=1756529796; darn=gnu.org; h=mime-version:message-id:date:subject:to:from:from:to:cc:subject :date:message-id:reply-to; bh=uNC7Q/Lz6b27mTLAO/kOZPuNBTFHTzGH6bwXQumhtA4=; b=QIEuRn6jLiVtVfu41B1vZUC4Lew+eokkOQ5HhhBbzmT9fXf4NCZLXGk2Lh9bEZVdub dQUYbu0n8agCBj2t47KPrEQrrb2/x4B+2YDVuxZTm8veSZV1ue9OAfKYjxI358Y4czsc HQuUD1jUCI0PDcDJn1lFktHE1KWbKucJSqsuo7SltKnFNcssis5NaOcCnx2gH88je1er K/bCXJAuqejm5tonJ/1kESto2NYoYMSFjHNiYB3AXM45wT31OJTSaJrVBAKEadT/ZqhC qE22QcudDPpC9UVKlAfLyUhuCFqjJIOnMdO/trNb46SseUSABYYgT+ICf+jeL6G4FTRK EjAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755924996; x=1756529796; h=mime-version:message-id:date:subject:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=uNC7Q/Lz6b27mTLAO/kOZPuNBTFHTzGH6bwXQumhtA4=; b=JGObJdDfeObKj3lAHEI9GAmN5ECsQ/CQDZrphtbGq6xxRRpTdbZ/ikm0taPtTTX/rl Xg5X35SgaJuhnGbetZU58v1V1WKe0odKIDyMkiqA2vww9kf7vX0ItbasJFFVzczjoQfm T9TZiPbToLirZkY7L+ryGlc5dikLsAp+Lv1B2oPA/iH/xxW1GE7dSMFxbMxozBJlYaGW XHyxX0oj6OTWa3/tLpUUhlZbaOfzhWC31OjqAsZ4qOycM3uLdFj50anQmXkLv+KWqufb u7x+wi2XgVtBVFkaoriStQ0+yys8XUC+fXYcNAFIqrlDNEgLFTJqhQe9x9oAcQInRiOo w7Qw== X-Gm-Message-State: AOJu0YziKtk4YwQuTm8ILGustosdqEiqlDmkzQMyTNvrokvsI0Fj81X1 z+mkLb8WNKNU6/ZLmaBmxLT9dYP6Ak3xfWjONvLLrhQHoNF6k8a75NC9Z1gZWXVW X-Gm-Gg: ASbGnctkubn1G9b6NZaEt1Jjsa38Wpahpqj2V9dCr+1gDd3Fqsdp5mk6KJG8oBJCiZB ew4kiEC/Hs3ZJPF9p2PgL+J0BGQgKEb2s3FJxUs2ZBKx1Us4QESSXlABImC34Ksi660/fAXEoMG NQ3S3BnFmHWTvo9RCtYAPAGb4aTYUGHYhNd/AbVPmF4EM8MGuqKxxuxquIxp+eJvbZG3YyhPkru 3tYP0gexgs6NMQoBHBv0iP+WBjVnarVsM+Pp0JBDZiFzpiZcoXWJD8ioZRnG7hhhNTDMmk16EVK R3Z6rR/JBSvww9wLzJ4hZ35ieyLkcKDVmZT3c6pCVdmMm3GHdjeke0pl5NkimJNTWNadRXcwvVT g/zD1539lA0SUMcY= X-Google-Smtp-Source: AGHT+IGwd5EvViyMp0r3Fcsun6SRDDR7x4WfzF2TbX3Uy87adSjmqdjlFfMRgOqrfxM1uqw/XiV5sQ== X-Received: by 2002:a05:6830:3c83:b0:744:f0db:a196 with SMTP id 46e09a7af769-74500a8d4f5mr2601089a34.34.1755924996229; Fri, 22 Aug 2025 21:56:36 -0700 (PDT) Received: from fedora ([189.215.164.127]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7450e268cf4sm406888a34.4.2025.08.22.21.56.34 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 Aug 2025 21:56:35 -0700 (PDT) From: =?utf-8?Q?Elijah_Gabe_P=C3=A9rez?= To: bug-gnu-emacs@gnu.org Subject: [PATCH] Add hideable indicators for hideshow. Date: Fri, 22 Aug 2025 22:56:33 -0600 Message-ID: <87a53q3abi.fsf@gmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::343; envelope-from=eg642616@gmail.com; helo=mail-ot1-x343.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: 1.2 (+) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: This patch adds indicators to toggle source block visibility (as shown in the emacs-devel thread). Currently the implementation makes scrolling a bit slow, due to the computation required to get the hideable blocks. Content analysis details: (1.2 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2001:470:142:0:0:0:0:17 listed in] [list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record 0.2 FREEMAIL_ENVFROM_END_DIGIT Envelope-from freemail username ends in digit (eg642616[at]gmail.com) 1.0 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (eg642616[at]gmail.com) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: 0.2 (/) --=-=-= Content-Type: text/plain This patch adds indicators to toggle source block visibility (as shown in the emacs-devel thread). Currently the implementation makes scrolling a bit slow, due to the computation required to get the hideable blocks. I'm not sure if there is a way to add the indicators lazily or a better solution. --=-=-= Content-Type: text/patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Add-hideable-indicators-for-hideshow.-Bug.patch Content-Transfer-Encoding: quoted-printable >From 2d8f1d84bc0eba4fa3d8fb89ec953e43ba43e7bb Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?El=3DC3=3DADas=3D20Gabriel=3D20P=3DC3=3DA9rez?=3D Date: Sun, 17 Aug 2025 20:02:59 -0600 Subject: [PATCH] Add hideable indicators for hideshow. (Bug#) * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes. * lisp/progmodes/hideshow.el (hs-show-indicators) (hs-indicators-type): New user options. (hs-indicator-hide, hs-indicator-show): New icons. (hs-discard-indicator-overlays, hs-block-positions) (hs--add-indicators, hs--refresh-indicators): New functions. (hs-hide-block-at-point): Use hs-block-positions. (hs-minor-mode): Rework. --- doc/emacs/programs.texi | 19 +++ etc/NEWS | 27 ++++ lisp/progmodes/hideshow.el | 305 +++++++++++++++++++++++++++++++++---- 3 files changed, 324 insertions(+), 27 deletions(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index f00d79f499a..6df97a82818 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1730,6 +1730,8 @@ Hideshow =20 @vindex hs-hide-comments-when-hiding-all @vindex hs-display-lines-hidden +@vindex hs-show-indicators +@vindex hs-indicators-type @vindex hs-isearch-open @vindex hs-special-modes-alist These variables can be used to customize Hideshow mode: @@ -1743,6 +1745,23 @@ Hideshow If non-@code{nil}, display the number of hidden lines next to the ellipsis. =20 +@item hs-show-indicators +This variable specifies if hideshow should display indicators to toggle +the block hiding. If this is set to non-@code{nil}, the indicators are +enabled except in comments, otherwise, if set to +@code{include-comments}, the indicators will be displayed also in +comments. + +@item hs-indicactors-type +This variable specifies which indicator type to use. Its value should +be either @code{left-fringe} (display the indicators in the left +fringe), @code{right-fringe} (display the indicators in the right +fringe), @code{left-margin} (display the indicators in the left margin), +@code{right-margin} (display the indicators in the right margin), or +@code{nil} (display a string as indicator after the block beginning). +If @code{left-fringe} or @code{right-fringe} are selected and current +Emacs session does not support fringes, margins will be used instead. + @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/etc/NEWS b/etc/NEWS index e94464b203b..1f608170884 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -895,10 +895,37 @@ at point to explore. =20 ** Hideshow =20 ++++ *** New user option 'hs-display-lines-hidden'. If this option is non-nil, Hideshow displays the number of hidden lines next to the ellipsis. =20 ++++ +*** New user option 'hs-show-indicators'. +This user option determines if hideshow should display indicators to +toggle the block hiding. If non-nil, the indicators are enabled except +in comments. + +Otherwise if this is set to 'include-comments', the indicators will be +displayed also in comments. + +By default this is disabled. + ++++ +*** New user option 'hs-indicators-type'. +This user option determine which indicator type should be used for the +block indicators. + +The possible values can be: 'left-fringe', display the indicators in the +left fringe (the default); 'right-fringe', display the indicators in the +right fringe; 'left-margin', display the indicators in the left margin; +'right-margin', display the indicators in the right margin; nil, display +a string as indicator after the block beginning. + +The new icons 'hs-indicator-show' and 'hs-indicator-hide', can be used +for customize the indicators appearance, only if 'hs-indicators-type' is +set to 'left-margin', 'right-margin' or nil. + ** C-ts mode =20 +++ diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 445bdeeb7a7..4407cb78b94 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -220,6 +220,9 @@ =20 ;;; Code: (require 'mule-util) ; For `truncate-string-ellipsis' +;; For indicators +(require 'icons) +(require 'fringe) =20 ;;------------------------------------------------------------------------= --- ;; user-configurable variables @@ -236,6 +239,16 @@ hs-ellipsis use that face for the ellipsis instead." :version "31.1") =20 +(defface hs-indicator-hide + '((t :inherit (shadow default))) + "Face used in hideshow indicator to hide current block." + :version "31.1") + +(defface hs-indicator-show + '((t :inherit hs-indicator-hide :weight bold)) + "Face used in hideshow indicator to show current block." + :version "31.1") + (defcustom hs-hide-comments-when-hiding-all t "Hide the comments too when you do an `hs-hide-all'." :type 'boolean) @@ -265,6 +278,87 @@ hs-isearch-open (const :tag "open both code and comment blocks" t) (const :tag "don't open any of them" nil))) =20 +(defcustom hs-show-indicators nil + "Whether hideshow should display block indicators. +If non-nil, hideshow will display indicators for toggle the +visibility of a block. + +If set to `include-comments', the indicators are displayed also in +comments. + +The indicators appearance are specified in `hs-indicators-type' (which see= )." + :type '(choice boolean (const :tag "Include comments" include-comments)) + :version "31.1") + +(defcustom hs-indicators-type 'left-fringe + "Indicate which indicator type to use for the block indicators. + +The possible values can be: + + - `left-fringe', display the indicators in the left fringe. + - `right-fringe', display the indicators in the right fringe. + + - `left-margin', display the indicators in the left margin. + - `right-margin', display the indicators in the right margin. + + - nil, display a string as indicator after the block beginning. + +If `left-fringe' or `right-fringe' are selected and fringes are not +supported, margins will be used instead. + +This only have effect if `hs-show-indicators' is non-nil." + :type '(choice + (const :tag "Left fringe" left-fringe) + (const :tag "Right fringe" right-fringe) + (const :tag "Left margin" left-margin) + (const :tag "Right margin" right-margin) + (const :tag "String after block beginning." nil)) + :version "31.1") + +(define-fringe-bitmap + 'hs-hide + [#b0000000 + #b1000001 + #b1100011 + #b0110110 + #b0011100 + #b0001000 + #b0000000]) + +(define-fringe-bitmap + 'hs-show + [#b0110000 + #b0011000 + #b0001100 + #b0000110 + #b0001100 + #b0011000 + #b0110000]) + +(define-icon hs-indicator-hide nil + `((image "outline-open.svg" "outline-open.pbm" + :face hs-indicator-hide + :height (0.6 . em) + :ascent center) + (symbol "=F0=9F=9E=83" "=E2=96=B6") + (text "-")) + "Icon used for hide block at point. +This is only used if `hs-indicators-type' is set to `left-margin', +`right-margin' or nil." + :version "31.1") + +(define-icon hs-indicator-show nil + `((image "outline-close.svg" "outline-close.pbm" + :face hs-indicator-show + :height (0.6 . em) + :ascent center) + (symbol "=E2=96=B8" "=E2=96=B6") + (text "+")) + "Icon used for show block at point. +This is only used if `hs-indicators-type' is set to `left-margin', +`right-margin' or nil." + :version "31.1") + ;;;###autoload (defvar hs-special-modes-alist ;; FIXME: Currently the check is made via @@ -506,6 +600,17 @@ hs-headline =20 Note that `mode-line-format' is buffer-local.") =20 +(defvar hs--indicators-rx + `((hs-discard-indicator-overlays) + (,(lambda (bound) + (funcall hs-find-next-block-func + hs-block-start-regexp + bound + (unless (eq hs-show-indicators 'exclude-comments) + t))) + 0 (hs--add-indicators))) + "Hideshow indicator regexp for `font-lock-keywords'.") + ;;------------------------------------------------------------------------= --- ;; support functions =20 @@ -524,6 +629,11 @@ hs-discard-overlays (when (overlay-get ov 'hs) (delete-overlay ov))))) =20 +(defun hs-discard-indicator-overlays (limit) + "Remove hideshow indicator overlays from point to LIMIT." + (remove-overlays (point) limit 'hs-indicator t) + nil) + (defun hs-make-overlay (b e kind &optional b-offset e-offset) "Return a new overlay in region defined by B and E with type KIND. KIND is either `code' or `comment'. Optional fourth arg B-OFFSET @@ -562,6 +672,137 @@ hs-make-overlay (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) ov)) =20 +(defun hs-block-positions (&optional beg) + "Return block positions. +If BEG is defined, start from BEG." + (save-match-data + (save-excursion + (if beg (goto-char beg)) + (when (funcall hs-looking-at-block-start-p-func) + (let ((mdata (match-data t)) + (header-end (match-end 0)) + p q) + ;; `p' is the point at the end of the block beginning, which + ;; may need to be adjusted + (save-excursion + (goto-char (funcall (or hs-adjust-block-beginning #'identity) + header-end)) + (setq p (line-end-position))) + ;; `q' is the point at the end of the block + (hs-forward-sexp mdata 1) + (setq q (cond ((and (stringp hs-block-end-regexp) + (looking-back hs-block-end-regexp nil)) + (match-beginning 0)) + ((functionp hs-block-end-regexp) + (funcall hs-block-end-regexp) + (match-beginning 0)) + (t (point)))) + (cons p q)))))) + +(defun hs--add-indicators () + "Add block indicators." + ;; Using `get-text-property' here should be faster than using + ;; `syntax-ppss' + (unless (memq (get-text-property (match-beginning 0) 'face) + '(font-lock-string-face font-lock-doc-face)) + (let* ((beg (match-beginning 0)) + ;; Check if block is already hidden, we + ;; cannot call `hs-already-hidden-p' + ;; because we are already at the block + ;; beginning + (hidden-p (hs-overlay-at (line-end-position))) + (ov (make-overlay + ;; FIXME: The mouse-1 event doesn't work well for + ;; fringes, a workaround for this is set the overlay + ;; from the beginning of the line only if fringes are + ;; going to be used as indicators. + (if (memq hs-indicators-type '(left-fringe right-fringe)) + (line-beginning-position) + beg) + (match-end 0))) + (map (make-sparse-keymap)) + (indicator (if hidden-p + `(hs-show hs-indicator-show hs-indicator-show) + `(hs-hide hs-indicator-hide hs-indicator-hide))) + (commentp (when (and (eq hs-show-indicators 'include-comments)) + (hs-inside-comment-p)))) + + (when (or (and commentp (> (count-lines (car commentp) (nth 1 commen= tp)) 1)) + ;; Check if block is longer than 1 line, + ;; `ignore-errors' is used here if the search fail due + ;; unbalanced parenthesis or any other unknown error + ;; caused in `hs-forward-sexp'. + (and-let* ((block (ignore-errors + (hs-block-positions beg))) + (p (car-safe block)) + (q (cdr-safe block)) + ((< p q)) + ((> (count-lines p q) 1))))) + (overlay-put ov 'hs-indicator t) + (overlay-put ov 'evaporate t) + + (define-key map + `[,@(if hs-indicators-type + `(,hs-indicators-type mouse-1) + '(mouse-1))] + (lambda () + (interactive) + ;; Goto block beginning before calling + ;; `hs-toggle-hiding' + (goto-char beg) + (hs-toggle-hiding))) + + (pcase hs-indicators-type + ;;;;;;;;;;;;;;;;;;;; + ;; Fringes + ((or 'left-fringe 'right-fringe) + (overlay-put + ov 'before-string + (propertize + "+" 'display + `(,hs-indicators-type + ,(car indicator) ,(nth 2 indicator)))) + (overlay-put ov 'keymap map)) + ;;;;;;;;;;;;;;;;;;;; + ;; Margins + ((or 'left-margin 'right-margin) + (overlay-put + ov 'before-string + (propertize + "+" 'display + `((margin ,hs-indicators-type) + ,(or (plist-get (icon-elements (nth 1 indicator)) 'image) + (propertize + (plist-get (icon-elements (nth 1 indicator)) 'string) + 'rear-nonsticky nil + 'face (nth 2 indicator) + 'keymap map))) + 'rear-nonsticky nil + 'face (nth 2 indicator) + 'keymap map))) + ;;;;;;;;;;;;;;;;;;;; + ;; Plain string + ('nil + (overlay-put + ov 'after-string + (propertize + (icon-string (nth 1 indicator)) + 'face (nth 2 indicator) + 'mouse-face 'highlight + 'keymap map)))) + + ;; Only 1 indicator per line if `hs-indicators-type' is not nil. + (if hs-indicators-type + (goto-char (line-end-position)))))) + nil) + +(defun hs--refresh-indicators () + "Update indicators appearance." + (let* ((block (hs-block-positions (1- (point)))) + (beg (car block)) + (end (cdr block))) + (font-lock-flush beg end))) + (defun hs--get-ellipsis (b e) "Helper function for `hs-make-overlay'. This returns the ellipsis string to use and its face." @@ -669,32 +910,19 @@ hs-hide-block-at-point and then further adjusted to be at the end of the line." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) - (when (funcall hs-looking-at-block-start-p-func) - (let ((mdata (match-data t)) - (header-end (match-end 0)) - p q ov) - ;; `p' is the point at the end of the block beginning, which - ;; may need to be adjusted - (save-excursion - (goto-char (funcall (or hs-adjust-block-beginning #'identity) - header-end)) - (setq p (line-end-position))) - ;; `q' is the point at the end of the block - (hs-forward-sexp mdata 1) - (setq q (cond ((and (stringp hs-block-end-regexp) - (looking-back hs-block-end-regexp nil)) - (match-beginning 0)) - ((functionp hs-block-end-regexp) - (funcall hs-block-end-regexp) - (match-beginning 0)) - (t (point)))) - (when (and (< p q) (> (count-lines p q) 1)) - (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) - (delete-overlay ov)) - ((not hs-allow-nesting) - (hs-discard-overlays p q))) - (hs-make-overlay p q 'code (- header-end p))) - (goto-char (if end q (min p header-end))))))) + (let* ((block (hs-block-positions)) + (p (car-safe block)) + (q (cdr-safe block)) + ov) + (if (and block (< p q) (> (count-lines p q) 1)) + (progn + (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) + (delete-overlay ov)) + ((not hs-allow-nesting) + (hs-discard-overlays p q))) + (goto-char q) + (hs-make-overlay p q 'code (- (match-end 0) p))) + (goto-char (if end q (min p (match-end 0)))))))) =20 (defun hs-inside-comment-p () "Return non-nil if point is inside a comment, otherwise nil. @@ -1086,8 +1314,31 @@ hs-minor-mode #'turn-off-hideshow nil t) (setq-local line-move-ignore-invisible t) - (add-to-invisibility-spec '(hs . t))) + (add-to-invisibility-spec '(hs . t)) + + ;; Add block indicators + (when hs-show-indicators + (when-let* ((type + (if (not (display-graphic-p)) + 'left-margin hs-indicators-type))) + (setq-local + hs-indicators-type + (cond + ((not (eq (current-bidi-paragraph-direction) 'right-to-left)= ) type) + ((eq hs-indicators-type 'left-fringe) 'right-fringe) + ((eq hs-indicators-type 'left-margin) 'right-margin) + (t hs-indicators-type)))) + + (font-lock-add-keywords nil hs--indicators-rx) + (add-hook 'hs-show-hook #'hs--refresh-indicators nil t) + (add-hook 'hs-hide-hook #'hs--refresh-indicators nil t) + (font-lock-flush))) (remove-from-invisibility-spec '(hs . t)) + (when hs-show-indicators + (font-lock-remove-keywords nil hs--indicators-rx) + (remove-hook 'hs-show-hook #'hs--refresh-indicators t) + (remove-hook 'hs-hide-hook #'hs--refresh-indicators t) + (remove-overlays nil nil 'hs-indicator t)) ;; hs-show-all does nothing unless h-m-m is non-nil. (let ((hs-minor-mode t)) (hs-show-all)))) --=20 2.50.1 --=-=-= Content-Type: text/plain -- - E.G via Gnus and Org. --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 23 04:00:51 2025 Received: (at 79294) by debbugs.gnu.org; 23 Aug 2025 08:00:51 +0000 Received: from localhost ([127.0.0.1]:38626 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1upjBD-0006Z6-3E for submit@debbugs.gnu.org; Sat, 23 Aug 2025 04:00:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:43878) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1upjBA-0006Yl-Gs for 79294@debbugs.gnu.org; Sat, 23 Aug 2025 04:00:49 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1upjB3-0004A1-Ob; Sat, 23 Aug 2025 04:00:43 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=1tgJjnGwTe4mOP49wB7lCoz3XniBZEfc5T0gY3F6MwI=; b=g3T040nyiVjHECAdktSi DVIE9PNQx0Iq1N4Wsr9d5v4iTKnltbDo/5v/Vg3+qulE+yKICg9e8KylvASBobWIgc8LR7vBCVXjk uOBgoRYHqC4AoHzZy163gsrYJGhteXFhiR83ZfoGgsExlh4sa9sd5mK5rYB0YeNdIbCam2FrTgRKU Qwge5S2WVOEK1uBZXGT5Hec5HuJYZDAk/G1AOH6xggwm77RSx3b1yS/0rD9vcfV6WPUnLWoyUDfH6 jAe6fGD1Pj88+rhgerR7yaAXh2SsBPTTDzRmGHBReGQqSfId6i/mzuvau3AvKN0W1U7UgOz0piwZm s7E4/vTzAw24JA==; Date: Sat, 23 Aug 2025 11:00:37 +0300 Message-Id: <868qjacvru.fsf@gnu.org> From: Eli Zaretskii To: Elijah Gabe =?utf-8?Q?P=C3=A9rez?= In-Reply-To: <87a53q3abi.fsf@gmail.com> (message from Elijah Gabe =?utf-8?Q?P=C3=A9rez?= on Fri, 22 Aug 2025 22:56:33 -0600) Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. References: <87a53q3abi.fsf@gmail.com> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Elijah Gabe Pérez > Date: Fri, 22 Aug 2025 22:56:33 -0600 > > This patch adds indicators to toggle source block visibility (as shown > in the emacs-devel thread). > > Currently the implementation makes scrolling a bit slow, due to the > computation required to get the hideable blocks. How slow? Can you post some measurements, comparing the scroll times with and without the indicators? > +@vindex hs-indicators-type I think this variable should be named "hs-indicator-type", in singular. > +@item hs-show-indicators > +This variable specifies if hideshow should display indicators to toggle > +the block hiding. This sentence reads awkward ("indicators to toggle") because you conflated two separate features together. I would leave them separate: This variable controls whether Hideshow mode should display indicators of hidden and shown blocks. The indicators also allow toggling the hide/show state of each block. > If this is set to non-@code{nil}, the indicators are > +enabled except in comments, otherwise, if set to > +@code{include-comments}, the indicators will be displayed also in > +comments. It is better to design this the other way around: If the value is @code{t}, the indicators are enabled. The special value @code{exclude-comments} enables the indicators in all blocks except comment blocks. The default is @code{nil}, which disables the indicators. Note that this also documents the default value. > +@item hs-indicactors-type > +This variable specifies which indicator type to use. Its value should > +be either @code{left-fringe} (display the indicators in the left > +fringe), @code{right-fringe} (display the indicators in the right > +fringe), @code{left-margin} (display the indicators in the left margin), > +@code{right-margin} (display the indicators in the right margin), or > +@code{nil} (display a string as indicator after the block beginning). > +If @code{left-fringe} or @code{right-fringe} are selected and current > +Emacs session does not support fringes, margins will be used instead. This is too detailed for such a minor feature. I would simply say This variable controls where to show the indicators, if they are enabled. You can show them on the fringe (@pxref{Windows Fringes}) or in the window's margin. The default is to use the fringe if it's available, otherwise to use the margin. The other details are clear from the variable's doc string, and need not be repeated here. Note that I deliberately omitted the left/right part, because I think your current design is wrong in this area, see below. > ++++ > *** New user option 'hs-display-lines-hidden'. > If this option is non-nil, Hideshow displays the number of hidden > lines next to the ellipsis. Please tell the default value. > ++++ > +*** New user option 'hs-show-indicators'. > +This user option determines if hideshow should display indicators to > +toggle the block hiding. If non-nil, the indicators are enabled except > +in comments. ^^ Two spaces between sentences. In addition "in comments" is problematic here, as the indicators are not part of text. Did you mean "for comment blocks" or something? > +The new icons 'hs-indicator-show' and 'hs-indicator-hide', can be used > +for customize the indicators appearance, only if 'hs-indicators-type' is ^^^^^^^^^^^^^ Either "to customize" or "for customizing". > +set to 'left-margin', 'right-margin' or nil. > + > ** C-ts mode > > +++ > diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el > index 445bdeeb7a7..4407cb78b94 100644 > --- a/lisp/progmodes/hideshow.el > +++ b/lisp/progmodes/hideshow.el > @@ -220,6 +220,9 @@ > > ;;; Code: > (require 'mule-util) ; For `truncate-string-ellipsis' > +;; For indicators > +(require 'icons) > +(require 'fringe) > > ;;--------------------------------------------------------------------------- > ;; user-configurable variables > @@ -236,6 +239,16 @@ hs-ellipsis > use that face for the ellipsis instead." > :version "31.1") > > +(defface hs-indicator-hide > + '((t :inherit (shadow default))) > + "Face used in hideshow indicator to hide current block." Not "to show", but "to indicate a shown block". > + :version "31.1") > + > +(defface hs-indicator-show > + '((t :inherit hs-indicator-hide :weight bold)) > + "Face used in hideshow indicator to show current block." Same here. > +(defcustom hs-show-indicators nil > + "Whether hideshow should display block indicators. I'd say Whether hideshow should display block hide/show indicators. > +If non-nil, hideshow will display indicators for toggle the > +visibility of a block. ^^^^^^^^^^ "for toggling" > +If set to `include-comments', the indicators are displayed also in > +comments. See the comment above about "in comments". Also, I would design the values the other way around: the value t shows indicators everywhere, the value exclude-comments excludes comment blocks. > +(defcustom hs-indicators-type 'left-fringe > + "Indicate which indicator type to use for the block indicators. > + > +The possible values can be: > + > + - `left-fringe', display the indicators in the left fringe. > + - `right-fringe', display the indicators in the right fringe. > + > + - `left-margin', display the indicators in the left margin. > + - `right-margin', display the indicators in the right margin. > + > + - nil, display a string as indicator after the block beginning. > + > +If `left-fringe' or `right-fringe' are selected and fringes are not > +supported, margins will be used instead. > + > +This only have effect if `hs-show-indicators' is non-nil." > + :type '(choice > + (const :tag "Left fringe" left-fringe) > + (const :tag "Right fringe" right-fringe) > + (const :tag "Left margin" left-margin) > + (const :tag "Right margin" right-margin) > + (const :tag "String after block beginning." nil)) > + :version "31.1") I think this is sub-optimal, if not fundamentally wrong. In a left-to-right text (which is an overwhelmingly the usual case), I don't expect users to want to show the indicators on the right. OTOH, when the text is right-to-left, the indicators should be on the right. Also, when showing the "show" indicator on the right, the direction of the arrow should be reversed: it should point to the left, not to the right. So I think we should allow just two values: 'fringe' and 'margin', and determine the left/right part from the actual text directionality. In the remote possibility that we would like to allow the indicators "on the opposite part" of the display (but why? it sounds like unnecessary feature to me), we could have values like 'opposite-fringe' etc. The main point is to avoid "left" and "right" when we choose which one to use dynamically. > +(define-icon hs-indicator-hide nil > + `((image "outline-open.svg" "outline-open.pbm" > + :face hs-indicator-hide > + :height (0.6 . em) > + :ascent center) > + (symbol "🞃" "â–¶") > + (text "-")) > + "Icon used for hide block at point. > +This is only used if `hs-indicators-type' is set to `left-margin', > +`right-margin' or nil." > + :version "31.1") > + > +(define-icon hs-indicator-show nil > + `((image "outline-close.svg" "outline-close.pbm" > + :face hs-indicator-show > + :height (0.6 . em) > + :ascent center) > + (symbol "â–¸" "â–¶") > + (text "+")) > + "Icon used for show block at point. The image and symbol parts of the "show" indicators should support the right margin with an arrow pointing to the left, so we'd need additional images for that. Also, the "â–¶" symbol is the same for both hide and show indicators? Is this intentional? > +(defvar hs--indicators-rx > + `((hs-discard-indicator-overlays) > + (,(lambda (bound) > + (funcall hs-find-next-block-func > + hs-block-start-regexp > + bound > + (unless (eq hs-show-indicators 'exclude-comments) > + t))) > + 0 (hs--add-indicators))) > + "Hideshow indicator regexp for `font-lock-keywords'.") Why do we need the value in a defvar, instead of invoking the function directly? > + p q) > + ;; `p' is the point at the end of the block beginning, which > + ;; may need to be adjusted > + (save-excursion > + (goto-char (funcall (or hs-adjust-block-beginning #'identity) > + header-end)) > + (setq p (line-end-position))) > + ;; `q' is the point at the end of the block Can you name these variables by more meaningful names, like pt-at-block-beg and pt-at-block-end? > + (when hs-show-indicators > + (when-let* ((type > + (if (not (display-graphic-p)) > + 'left-margin hs-indicators-type))) > + (setq-local > + hs-indicators-type > + (cond > + ((not (eq (current-bidi-paragraph-direction) 'right-to-left)) type) > + ((eq hs-indicators-type 'left-fringe) 'right-fringe) > + ((eq hs-indicators-type 'left-margin) 'right-margin) > + (t hs-indicators-type)))) > + > + (font-lock-add-keywords nil hs--indicators-rx) I think the fact that you modify overlays inside font-lock is the main reason for this being slow: it requires an immediate second redisplay cycle after a new window-full was displayed. It's the same problem as the one which made linenum-mode so slow. Did you try using pre-redisplay-function(s) for this purpose instead? From debbugs-submit-bounces@debbugs.gnu.org Sun Aug 24 01:15:13 2025 Received: (at 79294) by debbugs.gnu.org; 24 Aug 2025 05:15:13 +0000 Received: from localhost ([127.0.0.1]:42914 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uq34S-0008Sg-D5 for submit@debbugs.gnu.org; Sun, 24 Aug 2025 01:15:13 -0400 Received: from mail-oa1-x43.google.com ([2001:4860:4864:20::43]:46511) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1uq34Q-0008SK-20 for 79294@debbugs.gnu.org; Sun, 24 Aug 2025 01:15:11 -0400 Received: by mail-oa1-x43.google.com with SMTP id 586e51a60fabf-3111c59720dso1652736fac.1 for <79294@debbugs.gnu.org>; Sat, 23 Aug 2025 22:15:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756012504; x=1756617304; darn=debbugs.gnu.org; h=content-transfer-encoding:mime-version:user-agent:message-id:date :references:in-reply-to:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZQqPcd7O3V5WGBvJQRTqg+6SiPO2CqW1qqec6ugOETU=; b=QLscVny5dWkyVOilZ38gLhssFddf6oCYamD2bUTFbDyu6yhjnXkCgz8ArrlBSz0x8W MJjYyKqGIq0nMTlFAuHJoDqiGGqFgYnrwIhTDFbf7bgo8qlv8H9LSm9mb2vQMIRACJEj VHYnKf8qrGYa7CQUv7ynEDwDwcBVWt17cEpesQQjm7XrpkemPmySGKNUameuE6ydw3u2 hasy4AO2m3JrUryzI8osdS8MhG4shNXcKHcsuX+u3ytZb0E3AM6ARftAE80q1/5unVk9 JNM43eTsqByaSU7LSR6MbFifWjzt5zDZYM/m2ijEd0UoAiiCYBW1nVFnj/MejR46uuRP Y3UQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756012504; x=1756617304; h=content-transfer-encoding:mime-version:user-agent:message-id:date :references:in-reply-to:subject:cc:to:from:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ZQqPcd7O3V5WGBvJQRTqg+6SiPO2CqW1qqec6ugOETU=; b=wxeEI5rfXZVv2xSbbXGUtFZz7MmBpKzf7pQ304tXmgBO+M165hZnG9vVJKhGPMdodi 8k4Hdvs9plMXxYDLv9cj8GRbTkaYpkx9OgqKfvjz7oqjRi93f+cVja+ezZ7hK9hcvR47 mSChlbX/2kR+lEIKmgTxL90jJ4R6MNb0jvA1Lzu5wokHC2HhKLgfZ6FoNTqKM7viUHgS /wU9KhIRqqhJvnnUtBrx9hA5934cCPXuUYdGj5QkcQriXsIokqwRVEEpOkg4v8tKclS3 VdLZf5+zvkgC27XdYB5P+x6CLl98Xt2qLVllitFbDng39wj5RfpLejkTLq3tiJXw7tj+ al8Q== X-Gm-Message-State: AOJu0YwRpVayRDHbAS6ev1keBfySP4MI1Rr3AZBtbGN20PyGkoODQ3PY q8lZRsS6c1dLCtIGc9Qak5oBIofHBOkY+UEj1Be3vxuM/ltCTyOKs1dqYTmh7InT X-Gm-Gg: ASbGncsL3AtX+mtfYLAuuv8irYa9HjI1SUvqk/c3Eg/KzWvOwsw+EGIn6wIkfr1m8Qt L8FP8wa4Q0jT5tDeYmHT2BAyJ87yS/PLcBtjJiEuoYQUR1WVieRz4Hh1sRtHuZtpAN0VvGRFrc2 7EL10xO8+TfFKXipE1iDjDOc3lZSxMjjZnVnOimDZjfzSSPWUVvm1XMubRA/VQOymkI9Wz3F796 CV30MY2FrP23LrLU+IysxoxRKZBbRnaZxUiWt4yUwAEPyK3vZ1KRRU3KEKvMhUTlhPQnOFjeDQz iIDawW87YRNTsxDg0yPETN7zSsxuer396OwwGKvt7S18DtmmLC5BIsfXZxP/bxF9Ez8CLRjJFzn XpPUUOrjzXrBXHJ4= X-Google-Smtp-Source: AGHT+IFxr4znFdmDrgTPZaX1szcYshnBseHa8xPwv5D1XX9cu7Dlk+cOSsSGfpMPZlh2XuiKaV6qZw== X-Received: by 2002:a05:6870:d693:b0:2d8:957a:5178 with SMTP id 586e51a60fabf-314dcbb7fd6mr4119314fac.21.1756012503526; Sat, 23 Aug 2025 22:15:03 -0700 (PDT) Received: from fedora ([189.215.164.127]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-314f79c54basm1010960fac.5.2025.08.23.22.15.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Aug 2025 22:15:03 -0700 (PDT) From: =?utf-8?Q?Elijah_Gabe_P=C3=A9rez?= To: Eli Zaretskii Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. In-Reply-To: <868qjacvru.fsf@gnu.org> References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> Date: Sat, 23 Aug 2025 23:15:00 -0600 Message-ID: <87ikidcncb.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/31.0.50 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Score: 0.3 (/) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) Eli Zaretskii writes: >> From: Elijah Gabe P=C3=A9rez >> Date: Fri, 22 Aug 2025 22:56:33 -0600 >>=20 >> This patch adds indicators to toggle source block visibility (as shown >> in the emacs-devel thread). >>=20 >> Currently the implementation makes scrolling a bit slow, due to the >> computation required to get the hideable blocks. > > How slow? Can you post some measurements, comparing the scroll times > with and without the indicators? Well, since i have fast-but-imprecise-scrolling enabled, i've noticed a few chunks of text unfontified when scrolling faster, and it gets a little stuck, this doesn't happen if i disable the feature. > [...] >> ++++ >> +*** New user option 'hs-show-indicators'. >> +This user option determines if hideshow should display indicators to >> +toggle the block hiding. If non-nil, the indicators are enabled except >> +in comments. ^^ > > Two spaces between sentences. In addition "in comments" is > problematic here, as the indicators are not part of text. Did you > mean "for comment blocks" or something? Yes, comment blocks. [...] >> +(defcustom hs-indicators-type 'left-fringe >> + "Indicate which indicator type to use for the block indicators. >> + >> +The possible values can be: >> + >> + - `left-fringe', display the indicators in the left fringe. >> + - `right-fringe', display the indicators in the right fringe. >> + >> + - `left-margin', display the indicators in the left margin. >> + - `right-margin', display the indicators in the right margin. >> + >> + - nil, display a string as indicator after the block beginning. >> + >> +If `left-fringe' or `right-fringe' are selected and fringes are not >> +supported, margins will be used instead. >> + >> +This only have effect if `hs-show-indicators' is non-nil." >> + :type '(choice >> + (const :tag "Left fringe" left-fringe) >> + (const :tag "Right fringe" right-fringe) >> + (const :tag "Left margin" left-margin) >> + (const :tag "Right margin" right-margin) >> + (const :tag "String after block beginning." nil)) >> + :version "31.1") > > I think this is sub-optimal, if not fundamentally wrong. In a > left-to-right text (which is an overwhelmingly the usual case), I > don't expect users to want to show the indicators on the right. OTOH, > when the text is right-to-left, the indicators should be on the right. > Also, when showing the "show" indicator on the right, the direction of > the arrow should be reversed: it should point to the left, not to the > right. > > So I think we should allow just two values: 'fringe' and 'margin', and > determine the left/right part from the actual text directionality. In > the remote possibility that we would like to allow the indicators "on > the opposite part" of the display (but why? it sounds like unnecessary > feature to me), we could have values like 'opposite-fringe' etc. The > main point is to avoid "left" and "right" when we choose which one to > use dynamically. I agree, I have attempted to follow the same behavior as in flymake, but I think it is better to hardcode this. Furthermore, since hideshow is intended to be used in programming language modes, I don't think it's necessary to change the orientation of the indicators for bidirectional text. There are very few cases (or non-existent) in which a programming language uses (or supports) right-to-left text, (maybe for code comments?, I don't know). Compared to outline, which is intended to be used anywhere, so the orientation it handles makes more sense in outline than in hideshow. >> +(define-icon hs-indicator-hide nil >> + `((image "outline-open.svg" "outline-open.pbm" >> + :face hs-indicator-hide >> + :height (0.6 . em) >> + :ascent center) >> + (symbol "=F0=9F=9E=83" "=E2=96=B6") >> + (text "-")) >> + "Icon used for hide block at point. >> +This is only used if `hs-indicators-type' is set to `left-margin', >> +`right-margin' or nil." >> + :version "31.1") >> + >> +(define-icon hs-indicator-show nil >> + `((image "outline-close.svg" "outline-close.pbm" >> + :face hs-indicator-show >> + :height (0.6 . em) >> + :ascent center) >> + (symbol "=E2=96=B8" "=E2=96=B6") >> + (text "+")) >> + "Icon used for show block at point. > > The image and symbol parts of the "show" indicators should support the > right margin with an arrow pointing to the left, so we'd need > additional images for that. Sure. > Also, the "=E2=96=B6" symbol is the same for both hide and show indicator= s? > Is this intentional? No, I forgot to change it. >> +(defvar hs--indicators-rx >> + `((hs-discard-indicator-overlays) >> + (,(lambda (bound) >> + (funcall hs-find-next-block-func >> + hs-block-start-regexp >> + bound >> + (unless (eq hs-show-indicators 'exclude-comments) >> + t))) >> + 0 (hs--add-indicators))) >> + "Hideshow indicator regexp for `font-lock-keywords'.") > > Why do we need the value in a defvar, instead of invoking the function > directly? I don't understand this, is not this how the keywords should be appended to font-lock-keywords? >> + p q) >> + ;; `p' is the point at the end of the block beginning, which >> + ;; may need to be adjusted >> + (save-excursion >> + (goto-char (funcall (or hs-adjust-block-beginning #'identit= y) >> + header-end)) >> + (setq p (line-end-position))) >> + ;; `q' is the point at the end of the block > > Can you name these variables by more meaningful names, like > pt-at-block-beg and pt-at-block-end? Done. >> + (when hs-show-indicators >> + (when-let* ((type >> + (if (not (display-graphic-p)) >> + 'left-margin hs-indicators-type))) >> + (setq-local >> + hs-indicators-type >> + (cond >> + ((not (eq (current-bidi-paragraph-direction) 'right-to-le= ft)) type) >> + ((eq hs-indicators-type 'left-fringe) 'right-fringe) >> + ((eq hs-indicators-type 'left-margin) 'right-margin) >> + (t hs-indicators-type)))) >> + >> + (font-lock-add-keywords nil hs--indicators-rx) > > I think the fact that you modify overlays inside font-lock is the main > reason for this being slow: it requires an immediate second redisplay > cycle after a new window-full was displayed. It's the same problem as > the one which made linenum-mode so slow. Did you try using > pre-redisplay-function(s) for this purpose instead? Thanks for the explanation, i will try to use `pre-redisplay-functions` instead, but i'm not sure how to add the indicators to only the visible part of the buffer (that is why I choose to use font-lock for this, so the performance would be somewhat normal). I don't know if using jit-lock-functions instead would be better or would have the same result. --=20 - E.G via Gnus and Org. From debbugs-submit-bounces@debbugs.gnu.org Sun Aug 24 02:47:17 2025 Received: (at 79294) by debbugs.gnu.org; 24 Aug 2025 06:47:17 +0000 Received: from localhost ([127.0.0.1]:43132 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uq4VZ-0004n5-Da for submit@debbugs.gnu.org; Sun, 24 Aug 2025 02:47:17 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:42354) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uq4VW-0004mp-BY for 79294@debbugs.gnu.org; Sun, 24 Aug 2025 02:47:15 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uq4VQ-0000tI-T9; Sun, 24 Aug 2025 02:47:08 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=94qpPYMS1MT8S2wGe6gHVdLwda5VJyhV4FTAwdoeK1I=; b=bH38tlYBpDcEaco1C55J RC7jQrjbhwakGPf336/WX7bgPkHQr1fRNTFWrcmq8HQmgL7Vgnmg8FQaCh0uUOqL26YOOeLj+em9q IhVf6yJZeQQ9IcAVBY8wW16ajOy/eDRCI0ehgcd/XrABd9ULftVtJ6KzadlcW2qpU8A6sVDzn5/Di wtUzDcP1tTJRUUfpawMLQMHBh3MH2XuX+5Z6agzAbZPlVcfchc1WauRpX703oyjti5N5Z4OBRd5XP VLMbbbnsnrZoSg40kZ2BxENcEJWx1FXiLECLkMtyiEReLobAVlT0WRLFfyet4SGw2ZCrASqawlfSC AMJSDDyYJ48Faw==; Date: Sun, 24 Aug 2025 09:47:06 +0300 Message-Id: <867byt8bdh.fsf@gnu.org> From: Eli Zaretskii To: Elijah Gabe =?utf-8?Q?P=C3=A9rez?= In-Reply-To: <87ikidcncb.fsf@gmail.com> (message from Elijah Gabe =?utf-8?Q?P=C3=A9rez?= on Sat, 23 Aug 2025 23:15:00 -0600) Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Elijah Gabe Pérez > Cc: 79294@debbugs.gnu.org > Date: Sat, 23 Aug 2025 23:15:00 -0600 > > Eli Zaretskii writes: > > > So I think we should allow just two values: 'fringe' and 'margin', and > > determine the left/right part from the actual text directionality. In > > the remote possibility that we would like to allow the indicators "on > > the opposite part" of the display (but why? it sounds like unnecessary > > feature to me), we could have values like 'opposite-fringe' etc. The > > main point is to avoid "left" and "right" when we choose which one to > > use dynamically. > > I agree, I have attempted to follow the same behavior as in flymake, but > I think it is better to hardcode this. > > Furthermore, since hideshow is intended to be used in programming > language modes, I don't think it's necessary to change the orientation > of the indicators for bidirectional text. If Hideshow supports only modes for programming languages, then yes, we only need the indicators on the left. > >> +(defvar hs--indicators-rx > >> + `((hs-discard-indicator-overlays) > >> + (,(lambda (bound) > >> + (funcall hs-find-next-block-func > >> + hs-block-start-regexp > >> + bound > >> + (unless (eq hs-show-indicators 'exclude-comments) > >> + t))) > >> + 0 (hs--add-indicators))) > >> + "Hideshow indicator regexp for `font-lock-keywords'.") > > > > Why do we need the value in a defvar, instead of invoking the function > > directly? > > I don't understand this, is not this how the keywords should be appended > to font-lock-keywords? font-lock-keywords supports elements that call functions, see the ELisp manual where font-lock-keywords is described. > > I think the fact that you modify overlays inside font-lock is the main > > reason for this being slow: it requires an immediate second redisplay > > cycle after a new window-full was displayed. It's the same problem as > > the one which made linenum-mode so slow. Did you try using > > pre-redisplay-function(s) for this purpose instead? > > Thanks for the explanation, i will try to use `pre-redisplay-functions` > instead, but i'm not sure how to add the indicators to only the visible > part of the buffer (that is why I choose to use font-lock for this, so > the performance would be somewhat normal). You can use window-start and add the indicators to some reasonable vicinity of it. There's no need to do that _only_ in the visible part (and using the font-lock machinery will not limit the indicators only to the visible part, either). From debbugs-submit-bounces@debbugs.gnu.org Sat Sep 06 04:06:02 2025 Received: (at 79294) by debbugs.gnu.org; 6 Sep 2025 08:06:02 +0000 Received: from localhost ([127.0.0.1]:33811 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uunvt-0000ue-Cf for submit@debbugs.gnu.org; Sat, 06 Sep 2025 04:06:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:41418) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uunvq-0000tf-OY for 79294@debbugs.gnu.org; Sat, 06 Sep 2025 04:05:59 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uunvk-0007sn-Bn; Sat, 06 Sep 2025 04:05:52 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=JwaLX12VkdRimOX6s/+86KdUy996mWE6oLPgdSbRc4o=; b=BPJG468lpVebf8AfaK8h /ozqALfSIc5MSnq2RssZtK2twYEkB7Fd/Dn8+8CGFAdZDvOn8+BjU3uAM3OEhfYOeQ1vNrOtUj4jP qKVf+8Xkb8mmSx6ZYO4Wuv6cRf1L52Fc/lurH3dbehejhmWFoqYWP6wgqoN1/dXNkuFT0GYHqgMM7 8JwRylvYkFJosnyYoMcQQXExjSfqfFjpy45ytuMVNhsrL4uv++R5n8NuRnXe+FtLLOnwcAvhTbYJs 9snTjJRlRYrFFErtxvdRRnxT5Ww4ueOHGHXJtcZy7E3fTqpCzji3WcS7KruiRH9JHNaG2QhNM/hkt WCQZFXtUeZ6XPQ==; Date: Sat, 06 Sep 2025 11:05:49 +0300 Message-Id: <86ecskgg4i.fsf@gnu.org> From: Eli Zaretskii To: eg642616@gmail.com In-Reply-To: <867byt8bdh.fsf@gnu.org> (message from Eli Zaretskii on Sun, 24 Aug 2025 09:47:06 +0300) Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) Ping! How can we make some progress in this matter? > Cc: 79294@debbugs.gnu.org > Date: Sun, 24 Aug 2025 09:47:06 +0300 > From: Eli Zaretskii > > > From: Elijah Gabe Pérez > > Cc: 79294@debbugs.gnu.org > > Date: Sat, 23 Aug 2025 23:15:00 -0600 > > > > Eli Zaretskii writes: > > > > > So I think we should allow just two values: 'fringe' and 'margin', and > > > determine the left/right part from the actual text directionality. In > > > the remote possibility that we would like to allow the indicators "on > > > the opposite part" of the display (but why? it sounds like unnecessary > > > feature to me), we could have values like 'opposite-fringe' etc. The > > > main point is to avoid "left" and "right" when we choose which one to > > > use dynamically. > > > > I agree, I have attempted to follow the same behavior as in flymake, but > > I think it is better to hardcode this. > > > > Furthermore, since hideshow is intended to be used in programming > > language modes, I don't think it's necessary to change the orientation > > of the indicators for bidirectional text. > > If Hideshow supports only modes for programming languages, then yes, > we only need the indicators on the left. > > > >> +(defvar hs--indicators-rx > > >> + `((hs-discard-indicator-overlays) > > >> + (,(lambda (bound) > > >> + (funcall hs-find-next-block-func > > >> + hs-block-start-regexp > > >> + bound > > >> + (unless (eq hs-show-indicators 'exclude-comments) > > >> + t))) > > >> + 0 (hs--add-indicators))) > > >> + "Hideshow indicator regexp for `font-lock-keywords'.") > > > > > > Why do we need the value in a defvar, instead of invoking the function > > > directly? > > > > I don't understand this, is not this how the keywords should be appended > > to font-lock-keywords? > > font-lock-keywords supports elements that call functions, see the > ELisp manual where font-lock-keywords is described. > > > > I think the fact that you modify overlays inside font-lock is the main > > > reason for this being slow: it requires an immediate second redisplay > > > cycle after a new window-full was displayed. It's the same problem as > > > the one which made linenum-mode so slow. Did you try using > > > pre-redisplay-function(s) for this purpose instead? > > > > Thanks for the explanation, i will try to use `pre-redisplay-functions` > > instead, but i'm not sure how to add the indicators to only the visible > > part of the buffer (that is why I choose to use font-lock for this, so > > the performance would be somewhat normal). > > You can use window-start and add the indicators to some reasonable > vicinity of it. There's no need to do that _only_ in the visible part > (and using the font-lock machinery will not limit the indicators only > to the visible part, either). > > > > From debbugs-submit-bounces@debbugs.gnu.org Thu Sep 11 23:54:45 2025 Received: (at 79294) by debbugs.gnu.org; 12 Sep 2025 03:54:45 +0000 Received: from localhost ([127.0.0.1]:47078 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uwus0-0007YV-Dr for submit@debbugs.gnu.org; Thu, 11 Sep 2025 23:54:45 -0400 Received: from mail-yx1-xb142.google.com ([2607:f8b0:4864:20::b142]:59613) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1uwurv-0007YA-92 for 79294@debbugs.gnu.org; Thu, 11 Sep 2025 23:54:40 -0400 Received: by mail-yx1-xb142.google.com with SMTP id 956f58d0204a3-6241ab7fc41so600502d50.2 for <79294@debbugs.gnu.org>; Thu, 11 Sep 2025 20:54:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1757649273; x=1758254073; darn=debbugs.gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=ebPzaAH8mcOOF/Vgzpl/xzYXZYtW2k9GIHrKeOlSgZE=; b=IxktK/Pc7f6JxemT2FlQGa6SbgEX9zdBcKXYlrhz4zgH+90ywJZxHg4mrta2iemERJ R/yZeUSjkSjP7Z1/e6doE2zuTJmub7PaRQwMoNy1Kbfa9itoQmEhXbulM4hU2AeAaym2 6Ae7MhX2hI+t4AwwH93N8nMp9/9AVY+Oqt1c6q0QCCOqH7FoUD6xY/618xzF7h4xxGWc 7ak3qupe/a30rhIpKCFiFH0Nd0kHfPPh4PTDFpkZI/RrNfh1V51Lc6FICjOuBwYxHZyM BsLRMk7/bf0RfR+d1POz/oU+jUvlVVmz62J4OXvpRc2fbP7xXBqKyFU+U50NFGrXQAwk HrEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757649273; x=1758254073; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=ebPzaAH8mcOOF/Vgzpl/xzYXZYtW2k9GIHrKeOlSgZE=; b=DmvENFR5BayBT/m64PbEhsTsKRYeOaTrKwiQm2U/hgrfMSQqAEe0ushNPGf/Tqa6xh 5sPPdFeEQwvX/4iGQwNJyKxCoQDJjJ00MU0Egtmi4FxymtTQH5LJzIDLk46l5F7BMDGK /sKSxlZsnYLH7sWqvtAjEsrzaOPvpHaKaEfIsgrDXb7kbGMPIHdvRFziOLZgULWvIdg6 ANaSTwslcipTqSWaxCfob9GKsAbCl1hgOzBw/C8FCL6wQ7cbLr0D3nJGH2lfwr41RCw8 HUEZf++Z2LbsujlzQED9k6ObkaDg2Bax1OoEWLOjUaVyz3nlkllQYhRZu7OQGP7ODQZC 1STA== X-Gm-Message-State: AOJu0YzOnXS0DV2fl6ReD32hqVilOWncrdio3QkItlmmMgmMjZSOlnYR 7IPLuj5cG7iQ8tckDZxEmp0g7c4WkvowT0T/lp4I+QQD+c2bg3oUD8xfqAChU33A X-Gm-Gg: ASbGnctqBQKSZVJNqec/H8LTYZzU2Qo/C5xej9VYS7zBKwNhlY0VtlzTihdmzqL3Muz /3gluPgjvV9zgIVam/HuyAy3KqRkhRqNQqJXNshU/q700Qb3aJFuu4gvkdahitPrVAdFz++KDZ+ +B8D94ZK9+mlFpQdK6trwxbCgDd6MNOp9tqdV23SLcj85LNqvK9qq/YV4bZKy4/e3wRdJEFhcr+ dAYrJbw6jqRlc+Oq+emNPbfCRrwpZgUys9HLvgY84DxS2jUjth8/AikyR4Bv3Wx6uAcYT2sfhXo ObbG1ZLEROXCz5NchB79/JWhYNCWB25l30PuOjf/e4ycYiHzR7W4W3lZMIHbGFNB+wcXRCm7C2d kyyU0Cz0b67LQf7lo X-Google-Smtp-Source: AGHT+IFYj09OvZVEXxMtkFTvr2KY/8WJl0E8Xm2vWuc655d2Q0bm55CPmitOy3uR7OUx/FH9QZT3xQ== X-Received: by 2002:a05:690e:4304:b0:628:410:966e with SMTP id 956f58d0204a3-6280410979amr439311d50.37.1757649273202; Thu, 11 Sep 2025 20:54:33 -0700 (PDT) Received: from fedora ([189.215.164.177]) by smtp.gmail.com with ESMTPSA id 3f1490d57ef6-ea3cf009aadsm1052741276.13.2025.09.11.20.54.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Sep 2025 20:54:32 -0700 (PDT) From: =?utf-8?Q?Elijah_Gabe_P=C3=A9rez?= To: Eli Zaretskii Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. In-Reply-To: <86ecskgg4i.fsf@gnu.org> References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> Date: Thu, 11 Sep 2025 21:54:30 -0600 Message-ID: <878qik9vgp.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/31.0.50 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 0.3 (/) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) --=-=-= Content-Type: text/plain Eli Zaretskii writes: > Ping! How can we make some progress in this matter? Sorry for the delay, I have been very busy and have not had time to respond. I tried using `pre-redisplay-functions`, but performance was worse. I was able to mitigate the poor performance by removing the indicators for comment blocks (that implementation was broken and very slow) and adding a limit for the indicators. I also tried using the new feature in larger files for a while to test its performance (since there were no volunteers), and from what I've tested, it is stable. At the moment, the implementation can now be installed. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Add-hideable-indicators-for-hideshow.-Bug-79294.patch Content-Transfer-Encoding: quoted-printable >From 63ed5c9182abf1ca4d4c4f18b66dbd5cf38f22da Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?El=3DC3=3DADas=3D20Gabriel=3D20P=3DC3=3DA9rez?=3D Date: Sun, 17 Aug 2025 20:02:59 -0600 Subject: [PATCH] Add hideable indicators for hideshow. (Bug#79294) * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes. * lisp/progmodes/hideshow.el (hs-show-indicators) (hs-indicators-type, hs-indicator-maximum-buffer-size): New user options. (hs-indicator-hide, hs-indicator-show): New icons. (hs-block-positions, hs--get-indicator-appearance) (hs--make-indicators-overlays, hs--add-indicators) (hs--refresh-indicators): New functions. (hs-hide-block-at-point): Use hs-block-positions. (hs-minor-mode): Rework. --- doc/emacs/programs.texi | 22 +++ etc/NEWS | 37 ++++- lisp/progmodes/hideshow.el | 279 +++++++++++++++++++++++++++++++++---- 3 files changed, 309 insertions(+), 29 deletions(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index f00d79f499a..43ea263cc30 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1730,6 +1730,9 @@ Hideshow =20 @vindex hs-hide-comments-when-hiding-all @vindex hs-display-lines-hidden +@vindex hs-show-indicators +@vindex hs-indicator-type +@vindex hs-indicator-maximum-buffer-size @vindex hs-isearch-open @vindex hs-special-modes-alist These variables can be used to customize Hideshow mode: @@ -1743,6 +1746,25 @@ Hideshow If non-@code{nil}, display the number of hidden lines next to the ellipsis. =20 +@item hs-show-indicators +This variable controls whether Hideshow mode should display indicators +of hidden and shown blocks. The indicators also allow toggling the +hide/show state of each block. If the value is non-@code{nil}, enables +the indicators in all blocks except comment blocks. The default is +@code{nil}, which disables the indicators. + +@item hs-indicactors-type +This variable controls where to show the indicators, if they are +enabled. You can show them on the fringe (@pxref{Windows Fringes}) or +in the window's margin. The default is to use the fringe if it's +available, otherwise to use the margin. + +@item hs-indicator-maximum-buffer-size +This variable determines if the indicators should be enabled if the +current buffer size is not larger than this variable value. If set to +@code{nil}, the indicators will be activated regardless of the buffer +size. The default value is 2000000 bytes (2 megabytes). + @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/etc/NEWS b/etc/NEWS index 630d03a1fa0..b03efc94d4f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -938,9 +938,42 @@ at point to explore. =20 ** Hideshow =20 ++++ *** New user option 'hs-display-lines-hidden'. -If this option is non-nil, Hideshow displays the number of hidden -lines next to the ellipsis. +If this option is non-nil, Hideshow displays the number of hidden lines +next to the ellipsis. By default this is disabled. + ++++ +*** New user option 'hs-show-indicators'. +This user option determines if hideshow should display indicators to +toggle the block hiding. If non-nil, the indicators are enabled in all +blocks except comment blocks. + +Otherwise if this is set to 'include-comments', the indicators will be +displayed also in comments block. + +By default this is disabled. + +*** New user option 'hs-indicator-maximum-buffer-size'. +This variable determines whether the indicators should be enabled if the +current buffer size is not larger than this variable value. If set to +nil, the indicators will be activated regardless of the buffer size. +The default value is 2000000 bytes (2 megabytes). + ++++ +*** New user option 'hs-indicators-type'. +This user option determine which indicator type should be used for the +block indicators. + +The possible values can be: 'left-fringe', display the indicators in the +left fringe (the default); 'right-fringe', display the indicators in the +right fringe; 'left-margin', display the indicators in the left margin; +'right-margin', display the indicators in the right margin; nil, display +a string as indicator after the block beginning. + +The new icons 'hs-indicator-show' and 'hs-indicator-hide', can be used +for customize the indicators appearance, only if 'hs-indicators-type' is +set to 'left-margin', 'right-margin' or nil. =20 ** C-ts mode =20 diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 445bdeeb7a7..9e848bc4114 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -220,6 +220,9 @@ =20 ;;; Code: (require 'mule-util) ; For `truncate-string-ellipsis' +;; For indicators +(require 'icons) +(require 'fringe) =20 ;;------------------------------------------------------------------------= --- ;; user-configurable variables @@ -236,6 +239,16 @@ hs-ellipsis use that face for the ellipsis instead." :version "31.1") =20 +(defface hs-indicator-hide + '((t :inherit (shadow default))) + "Face used in hideshow indicator to indicate a hidden block." + :version "31.1") + +(defface hs-indicator-show + '((t :inherit hs-indicator-hide :weight bold)) + "Face used in hideshow indicator to indicate a shown block." + :version "31.1") + (defcustom hs-hide-comments-when-hiding-all t "Hide the comments too when you do an `hs-hide-all'." :type 'boolean) @@ -265,6 +278,85 @@ hs-isearch-open (const :tag "open both code and comment blocks" t) (const :tag "don't open any of them" nil))) =20 +(defcustom hs-show-indicators nil + "Whether hideshow should display block hide/show indicators. +If non-nil, hideshow will display indicators for toggling the +visibility of a block everywhere except in comment blocks. + +The indicators appearance are specified in `hs-indicator-type' (which see)= ." + :type 'boolean + :version "31.1") + +(defcustom hs-indicator-type 'fringe + "Indicate which indicator type to use for the block indicators. + +The possible values can be: + + - `fringe', display the indicators in the fringe. + - `margin', display the indicators in the margin. + - nil, display a string as indicator after the block beginning. + +This only have effect if `hs-show-indicators' is non-nil." + :type '(choice + (const :tag "Fringes" fringe) + (const :tag "Margins" margin) + (const :tag "String after block beginning." nil)) + :version "31.1") + +(defcustom hs-indicator-maximum-buffer-size 2000000 ;2mb + "Max buffer size in bytes where the indicators should be enabled. +If current buffer is larger than this variable value, the indicators +will be disabled. + +If set to nil, the indicators will be activated regardless of the buffer +size." + :type '(choice natnum (const :tag "No limit" nil)) + :version "31.1") + +(define-fringe-bitmap + 'hs-hide + [#b0000000 + #b1000001 + #b1100011 + #b0110110 + #b0011100 + #b0001000 + #b0000000]) + +(define-fringe-bitmap + 'hs-show + [#b0110000 + #b0011000 + #b0001100 + #b0000110 + #b0001100 + #b0011000 + #b0110000]) + +(define-icon hs-indicator-hide nil + `((image "outline-open.svg" "outline-open.pbm" + :face hs-indicator-hide + :height (0.6 . em) + :ascent center) + (symbol "=F0=9F=9E=83" "=E2=96=BC") + (text "-")) + "Icon used for hide block at point. +This is only used if `hs-indicator-type' is set to `left-margin', +`right-margin' or nil." + :version "31.1") + +(define-icon hs-indicator-show nil + `((image "outline-close.svg" "outline-close.pbm" + :face hs-indicator-show + :height (0.6 . em) + :ascent center) + (symbol "=E2=96=B8" "=E2=96=B6") + (text "+")) + "Icon used for show block at point. +This is only used if `hs-indicator-type' is set to `left-margin', +`right-margin' or nil." + :version "31.1") + ;;;###autoload (defvar hs-special-modes-alist ;; FIXME: Currently the check is made via @@ -562,6 +654,137 @@ hs-make-overlay (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) ov)) =20 +(defun hs-block-positions (&optional beg) + "Return block positions. +If BEG is defined, start from BEG." + (save-match-data + (save-excursion + (if beg (goto-char beg)) + (when (funcall hs-looking-at-block-start-p-func) + (let ((mdata (match-data t)) + (header-end (match-end 0)) + block-beg block-end) + ;; `block-start' is the point at the end of the block + ;; beginning, which may need to be adjusted + (save-excursion + (goto-char (funcall (or hs-adjust-block-beginning #'identity) + header-end)) + (setq block-beg (line-end-position))) + ;; `block-end' is the point at the end of the block + (hs-forward-sexp mdata 1) + (setq block-end + (cond ((and (stringp hs-block-end-regexp) + (looking-back hs-block-end-regexp nil)) + (match-beginning 0)) + ((functionp hs-block-end-regexp) + (funcall hs-block-end-regexp) + (match-beginning 0)) + (t (point)))) + (cons block-beg block-end)))))) + +(defun hs--get-indicator-appearance () + (if (hs-overlay-at (pos-eol)) + [hs-show hs-indicator-show hs-indicator-show] + [hs-hide hs-indicator-hide hs-indicator-hide])) + +(defun hs--make-indicators-overlays (beg end) + "Helper function for make the indicators overlays." + (let* ((o (make-overlay + ;; FIXME: The mouse-1 event doesn't work well for fringes, + ;; a workaround for this is set the overlay at the + ;; beginning of the line only for fringe indicators + (if (eq hs-indicator-type 'fringe) (pos-bol) beg) + (1+ beg))) + (map (make-sparse-keymap)) + (indicator (hs--get-indicator-appearance))) + + (when (and (< beg end) (not (=3D (line-number-at-pos beg) (line-number= -at-pos end)))) + (define-key map `[,@(append + (pcase hs-indicator-type + ('fringe '(left-fringe)) + ('margin '(left-margin))) + '(mouse-1))] + (lambda () + (interactive) + ;; Goto block beginning before calling + ;; `hs-toggle-hiding' + (goto-char beg) + (hs-toggle-hiding))) + + (overlay-put o 'hs-indicator t) + (overlay-put o 'evaporate t) + (overlay-put o 'priority -50) + + (pcase hs-indicator-type + ;; Fringes + ('fringe (overlay-put + o 'before-string + (propertize + "+" 'display + `(left-fringe ,(aref indicator 0) ,(aref indicator 2)))) + (overlay-put o 'keymap map)) + ;; Margins + ('margin (overlay-put + o 'before-string + (propertize + "+" 'display + `((margin left-margin) + ,(or (plist-get (icon-elements (aref indicator 1)) 'i= mage) + (propertize + (plist-get (icon-elements (aref indicator 1)) '= string) + 'rear-nonsticky nil + 'face (aref indicator 2) + 'keymap map))) + 'rear-nonsticky nil + 'face (aref indicator 2) + 'keymap map))) + ;; Plain string + ('nil (overlay-put + o 'after-string + (propertize + (icon-string (aref indicator 1)) + 'face (aref 2 indicator) + 'mouse-face 'highlight + 'keymap map))))))) + +(defun hs--add-indicators (&optional beg end) + "Add hideable indicators from BEG to END." + (save-excursion + (setq beg (if (null beg) (window-start) (goto-char beg) (pos-bol)) + end (if (null end) (window-end) (goto-char end) (pos-bol)))) + (goto-char beg) + (remove-overlays beg end 'hs-indicator t) + + (while (funcall hs-find-next-block-func hs-block-start-regexp end nil) + (when-let* ((b-beg (match-beginning 0)) + ((save-excursion + (goto-char b-beg) + (funcall hs-looking-at-block-start-p-func))) + ;; Check if block is longer than 1 line, + ;; `catch' is used here if the search fail due + ;; unbalanced parenthesis or any other unknown error + ;; caused in `hs-forward-sexp'. + (b-end (catch 'hs-indicator-error + (save-excursion + (goto-char b-beg) + (condition-case _ + (funcall hs-forward-sexp-func 1) + (scan-error (throw 'hs-indicator-error nil))) + (point))))) + (hs--make-indicators-overlays b-beg b-end)) + ;; Only 1 indicator per line if `hs-indicator-type' is not nil. + (if hs-indicator-type (forward-line 1))) + `(jit-lock-bounds ,beg . ,end)) + +(defun hs--refresh-indicators () + "Update indicators appearance at current block." + (when hs-show-indicators + (save-match-data + (save-excursion + ;; Using window-start and window-end is more faster + ;; than computing again the block positions + (hs--add-indicators (window-start) (window-end)))))) + (defun hs--get-ellipsis (b e) "Helper function for `hs-make-overlay'. This returns the ellipsis string to use and its face." @@ -669,32 +892,19 @@ hs-hide-block-at-point and then further adjusted to be at the end of the line." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) - (when (funcall hs-looking-at-block-start-p-func) - (let ((mdata (match-data t)) - (header-end (match-end 0)) - p q ov) - ;; `p' is the point at the end of the block beginning, which - ;; may need to be adjusted - (save-excursion - (goto-char (funcall (or hs-adjust-block-beginning #'identity) - header-end)) - (setq p (line-end-position))) - ;; `q' is the point at the end of the block - (hs-forward-sexp mdata 1) - (setq q (cond ((and (stringp hs-block-end-regexp) - (looking-back hs-block-end-regexp nil)) - (match-beginning 0)) - ((functionp hs-block-end-regexp) - (funcall hs-block-end-regexp) - (match-beginning 0)) - (t (point)))) - (when (and (< p q) (> (count-lines p q) 1)) - (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) - (delete-overlay ov)) - ((not hs-allow-nesting) - (hs-discard-overlays p q))) - (hs-make-overlay p q 'code (- header-end p))) - (goto-char (if end q (min p header-end))))))) + (let* ((block (hs-block-positions)) + (p (car-safe block)) + (q (cdr-safe block)) + ov) + (if (and block (< p q) (> (count-lines p q) 1)) + (progn + (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) + (delete-overlay ov)) + ((not hs-allow-nesting) + (hs-discard-overlays p q))) + (goto-char q) + (hs-make-overlay p q 'code (- (match-end 0) p))) + (goto-char (if end q (min p (match-end 0)))))))) =20 (defun hs-inside-comment-p () "Return non-nil if point is inside a comment, otherwise nil. @@ -976,6 +1186,7 @@ hs-hide-block (funcall hs-looking-at-block-start-p-func) (funcall hs-find-block-beginning-func)) (hs-hide-block-at-point end c-reg) + (hs--refresh-indicators) (run-hooks 'hs-hide-hook)))))) =20 (defun hs-show-block (&optional end) @@ -1011,6 +1222,7 @@ hs-show-block (when (and p q) (hs-discard-overlays p q) (goto-char (if end q (1+ p)))))) + (hs--refresh-indicators) (run-hooks 'hs-show-hook))) =20 (defun hs-hide-level (arg) @@ -1086,8 +1298,21 @@ hs-minor-mode #'turn-off-hideshow nil t) (setq-local line-move-ignore-invisible t) - (add-to-invisibility-spec '(hs . t))) + (add-to-invisibility-spec '(hs . t)) + ;; Add block indicators + (when (and hs-show-indicators + (or (and (integerp hs-indicator-maximum-buffer-size) + (< (buffer-size) hs-indicator-maximum-buffer-s= ize)) + (not hs-indicator-maximum-buffer-size))) + (when (and (not (display-graphic-p)) + (eq hs-indicator-type 'fringe)) + (setq-local hs-indicator-type 'margin)) + (jit-lock-register #'hs--add-indicators))) + (remove-from-invisibility-spec '(hs . t)) + (when hs-show-indicators + (jit-lock-unregister #'hs--add-indicators) + (remove-overlays nil nil 'hs-indicator t)) ;; hs-show-all does nothing unless h-m-m is non-nil. (let ((hs-minor-mode t)) (hs-show-all)))) --=20 2.51.0 --=-=-= Content-Type: text/plain -- - E.G via Gnus and Org. --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Sep 13 06:30:15 2025 Received: (at 79294) by debbugs.gnu.org; 13 Sep 2025 10:30:15 +0000 Received: from localhost ([127.0.0.1]:53809 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uxNWI-0008LU-19 for submit@debbugs.gnu.org; Sat, 13 Sep 2025 06:30:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58670) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uxNWE-0008Id-Px for 79294@debbugs.gnu.org; Sat, 13 Sep 2025 06:30:11 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uxNW9-0005T0-9O; Sat, 13 Sep 2025 06:30:05 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=nRDbGOH7IQYg07691nFMsiqp2t9OfxxpPJxqFBN5dSs=; b=hqlE1n7z1IwCG+CIELsX cYMUul/wmpO2q1MYS7pC/O31R8HKeOrCHzTn+yUGzMmeonGmiQIP5zFUwbTZB7vy4ZFaz681diO5M /tPwG7tN2Y/Si5l8twPH3K9Bf1ukD1gZOehj99b5pMTbV0MkPYmOk1nVsEjakobqZs6/dn2+22azP W+0JLVUkZXrC/Gjur6V+G8wEGOD1ln5h6ChdcgwmE4C3fd+uMyEq8s2kpUYQoAAVc4anvtb/b+Et7 vxFac98a2xhPLsOfDuJvUaeSaTc+jUDwP/3q07u9E5ZnOWNb/FI2OGPIZ2T0bb9jjczgAETfucyxU F2zCd2+8AdNnyw==; Date: Sat, 13 Sep 2025 13:30:02 +0300 Message-Id: <86ecsar6fp.fsf@gnu.org> From: Eli Zaretskii To: Elijah Gabe =?iso-8859-1?Q?P=E9rez?= In-Reply-To: <878qik9vgp.fsf@gmail.com> (message from Elijah Gabe =?iso-8859-1?Q?P=E9rez?= on Thu, 11 Sep 2025 21:54:30 -0600) Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> <878qik9vgp.fsf@gmail.com> MIME-version: 1.0 Content-type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Elijah Gabe Pérez > Cc: 79294@debbugs.gnu.org > Date: Thu, 11 Sep 2025 21:54:30 -0600 > > Eli Zaretskii writes: > > > Ping! How can we make some progress in this matter? > > Sorry for the delay, I have been very busy and have not had time to > respond. > > I tried using `pre-redisplay-functions`, but performance was worse. > > I was able to mitigate the poor performance by removing the indicators > for comment blocks (that implementation was broken and very slow) and > adding a limit for the indicators. > > I also tried using the new feature in larger files for a while to test > its performance (since there were no volunteers), and from what I've > tested, it is stable. > > At the moment, the implementation can now be installed. Thanks, I have a few minor comments, and then we can install this. > +@item hs-show-indicators > +This variable controls whether Hideshow mode should display indicators > +of hidden and shown blocks. The indicators also allow toggling the > +hide/show state of each block. If the value is non-@code{nil}, enables > +the indicators in all blocks except comment blocks. The default is > +@code{nil}, which disables the indicators. You don't mention the value include-comments here. It's okay not to mention it for brevity, but t6hen we should not say "all blocks except comment blocks", just that non-nil shows them and nil doesn't. > +@item hs-indicator-maximum-buffer-size > +This variable determines if the indicators should be enabled if the > +current buffer size is not larger than this variable value. If set to > +@code{nil}, the indicators will be activated regardless of the buffer > +size. The default value is 2000000 bytes (2 megabytes). I suggest to rephrase as follows: This variable limits the display of hideshow indicators to buffers that are not too large. (Larger buffers might adversely affect redisplay performance.) By default, buffers larger than 2MB have the indicators disabled; the value of nil will activate the indicators regardless of the buffer size. > +*** New user option 'hs-indicator-maximum-buffer-size'. > +This variable determines whether the indicators should be enabled if the > +current buffer size is not larger than this variable value. If set to > +nil, the indicators will be activated regardless of the buffer size. > +The default value is 2000000 bytes (2 megabytes). Suggest the same rephrase here. > +*** New user option 'hs-indicators-type'. > +This user option determine which indicator type should be used for the > +block indicators. > + > +The possible values can be: 'left-fringe', display the indicators in the > +left fringe (the default); 'right-fringe', display the indicators in the > +right fringe; 'left-margin', display the indicators in the left margin; > +'right-margin', display the indicators in the right margin; nil, display > +a string as indicator after the block beginning. This needs to be updated, since the values are now different. > +(defun hs-block-positions (&optional beg) > + "Return block positions. > +If BEG is defined, start from BEG." ^^^^^^^^^^^^^^^^^ "If BEG is non-nil" is our style. And the doc string should also explain: . what do you mean by "block positions" -- is it a list of positions? . what is the significance and the role of the starting position . where does the function start if BEG is nil or omitted > +(defun hs--make-indicators-overlays (beg end) > + "Helper function for make the indicators overlays." ^^^^^^^^ Either "to make" or "for making". > + (let* ((o (make-overlay > + ;; FIXME: The mouse-1 event doesn't work well for fringes, > + ;; a workaround for this is set the overlay at the > + ;; beginning of the line only for fringe indicators > + (if (eq hs-indicator-type 'fringe) (pos-bol) beg) > + (1+ beg))) What doesn't work well in mouse-1 clicks on the fringes? From debbugs-submit-bounces@debbugs.gnu.org Sun Sep 14 21:50:24 2025 Received: (at 79294) by debbugs.gnu.org; 15 Sep 2025 01:50:24 +0000 Received: from localhost ([127.0.0.1]:37369 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uxyMJ-000855-82 for submit@debbugs.gnu.org; Sun, 14 Sep 2025 21:50:24 -0400 Received: from mail-yb1-xb41.google.com ([2607:f8b0:4864:20::b41]:48162) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1uxyM7-0007yp-KE for 79294@debbugs.gnu.org; Sun, 14 Sep 2025 21:50:18 -0400 Received: by mail-yb1-xb41.google.com with SMTP id 3f1490d57ef6-ea4032e406dso705092276.0 for <79294@debbugs.gnu.org>; Sun, 14 Sep 2025 18:50:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1757901005; x=1758505805; darn=debbugs.gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=VtR+gcgF+nFfgXYkNvKK/BU51nqbRCPFv8/P0aPmGTc=; b=lE5CfB9N8ZjYb/wOSV8hiATsXL0YrK8xMfCDnKqlqml7tcCdB0TVhS90bJ+eupIt7o wTpAvxccvbjn+4ZtTSEb0OIO3e7VvAlePSJLKVfX6yRo3eOwKFmeGDfhpEbwdasmjag5 XJX7uZaN3bwI9O9+rs9Iw4b8suuq5ibrjx58cqARjlzFIkeFTV+IOs1XjABasUbPtOY7 JYaWXouEhqlJM3dSLcbPN9WvNGFPO3C9NDTzhCs+2Ib4fKKqABRBxaLOPL6ihxoAW9HA JpgO3pgXVJYP0h3lJe1IWkdIkXPN6bplyClx3fUJskV7KY6pEDE8+Eg2+ZQzDXMsWLrc 1fCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757901005; x=1758505805; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=VtR+gcgF+nFfgXYkNvKK/BU51nqbRCPFv8/P0aPmGTc=; b=FXR33AMG9dA7nXy4WIjUUEVjLbfyGtGN5RDSQQF9U40gxT4cTnByN4vzZ4oHr2KFV2 iZbwy0xvOGGEEHOUzWpVNHzJYRJTFGV3QpTaEpmJ9VvBIg7/t8laIzfsl7mV5nvhW3fP WpcRQWefQcV8WRHeUK1rAkp0w/rIREgP6+c/9zSoPs2pVeZP3wrEmd2ZM9sZmNeTNep3 C4hWUCxWPJBAKzGmzHxqZ+X5c3CE23JVHqVt3Q4UYli1woT+8b0Xt8qrBYGwc8fLQgc5 u4TZef8Gesjn+PJue/6zq5wK92M4Si2Xl0uutOJlGSkV9bOPjqM84poAfmkgFwwNY3Fj ZAPA== X-Gm-Message-State: AOJu0YzyOducw1n9l020mLPjfy4a5ZwRN6c49mmvgI10WlEvu+wXPDle N6XDzlE4OUBmETmtjyjjN+Hm/UefmfSFuX7KPbCTf49DQM6mgvg/840VoUUewzOM X-Gm-Gg: ASbGncvnZNRA0UvxVczuIrWOqjnPVCsWHVJznkrgZgGd35aK83OOURCmqm92jYDXiaj 4kSND/W3RF7I20/5FX6Dr35gfb4sMDQg64Ch4BYgMJXkqFnZCXnFCwv8s7RvQrKXkKRCWmg5aYb 0zl3CupNBTQFY8kUu7g73PFDNw1lWvvd59hSAn626oGYOdN/SbIQSn/ZCuRzXnuPStYjA40NmfV 9NF2OVrDjwRzYKuWwqin0CM1sFYgmvBKBNKamMptuWJQCFg2tw2m26HUycacXvS5UFsjMyu7xdF gGjeBhHH3m/WwtuIVJ6Yed3PkBvpWT7hFBMzWI7rbh+stW2Ar8mEwuOoWTOK+U+/LtsHukDgcG2 UglTd4b3ZXRy8N5m5 X-Google-Smtp-Source: AGHT+IH9lW2cbxt2eDLJA4+KtZDFxVoxXydP8z2a2NJ3hV7PIHzdsa/D1ye+6eVssA7FmS9UvDxV7Q== X-Received: by 2002:a53:c70a:0:b0:627:a036:d3dc with SMTP id 956f58d0204a3-627a036d5afmr6875413d50.28.1757901004574; Sun, 14 Sep 2025 18:50:04 -0700 (PDT) Received: from fedora ([189.215.165.139]) by smtp.gmail.com with ESMTPSA id 00721157ae682-7346746b482sm3761877b3.44.2025.09.14.18.50.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 14 Sep 2025 18:50:03 -0700 (PDT) From: =?utf-8?Q?Elijah_Gabe_P=C3=A9rez?= To: Eli Zaretskii Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. In-Reply-To: <86ecsar6fp.fsf@gnu.org> References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> <878qik9vgp.fsf@gmail.com> <86ecsar6fp.fsf@gnu.org> Date: Sun, 14 Sep 2025 19:50:02 -0600 Message-ID: <87tt1432np.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/31.0.50 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 0.3 (/) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) --=-=-= Content-Type: text/plain Eli Zaretskii writes: >> +@item hs-show-indicators >> +This variable controls whether Hideshow mode should display indicators >> +of hidden and shown blocks. The indicators also allow toggling the >> +hide/show state of each block. If the value is non-@code{nil}, enables >> +the indicators in all blocks except comment blocks. The default is >> +@code{nil}, which disables the indicators. > > You don't mention the value include-comments here. It's okay not to > mention it for brevity, but t6hen we should not say "all blocks except > comment blocks", just that non-nil shows them and nil doesn't. I removed the include-comments value from the code, so this mean that the indicators will not be enabled in comment blocks, that's why I had to specify it. I've already updated that part. [...] >> +*** New user option 'hs-indicators-type'. >> +This user option determine which indicator type should be used for the >> +block indicators. >> + >> +The possible values can be: 'left-fringe', display the indicators in the >> +left fringe (the default); 'right-fringe', display the indicators in the >> +right fringe; 'left-margin', display the indicators in the left margin; >> +'right-margin', display the indicators in the right margin; nil, display >> +a string as indicator after the block beginning. > > This needs to be updated, since the values are now different. Thanks i forgot to update it. > And the doc string should also explain: > > . what do you mean by "block positions" -- is it a list of positions? > . what is the significance and the role of the starting position > . where does the function start if BEG is nil or omitted Thanks, I've updated it. The BEG argument was intended for internal use only, and I forgot to remove it. >> + (let* ((o (make-overlay >> + ;; FIXME: The mouse-1 event doesn't work well for fringes, >> + ;; a workaround for this is set the overlay at the >> + ;; beginning of the line only for fringe indicators >> + (if (eq hs-indicator-type 'fringe) (pos-bol) beg) >> + (1+ beg))) > > What doesn't work well in mouse-1 clicks on the fringes? Apparently the fringe keymap does not work if the overlay is not created from BOL: e.g. The mouse-1 clicks works for this since it is created at BOL: #+begin_src elisp |+| (defvar-local var nil | | "Docstring.") #+end_src But for code blocks that are not at BOL this will not work: #+begin_src elisp |+| (defvar-local var ; <- This works |+| (when foo ; <- This does not | | (bar)) | | "Docstring.") #+end_src I have added a new alternative in the patch, and for the moment, it should work well for fringes. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Add-hideable-indicators-for-hideshow.-Bug-79294.patch Content-Transfer-Encoding: quoted-printable >From 59d2f530e3410bf61dfbe9c63427673e532c9276 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?El=3DC3=3DADas=3D20Gabriel=3D20P=3DC3=3DA9rez?=3D Date: Sun, 17 Aug 2025 20:02:59 -0600 Subject: [PATCH] Add hideable indicators for hideshow. (Bug#79294) * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes. * lisp/progmodes/hideshow.el (hs-show-indicators) (hs-indicator-type, hs-indicator-maximum-buffer-size): New user options. (hs-indicator-hide, hs-indicator-show): New icons. (hs-block-positions, hs--make-indicators-overlays) (hs-indicator-mouse-toggle-hidding, hs--add-indicators) (hs--refresh-indicators): New functions. (hs-hide-block-at-point): Use hs-block-positions. (hs-minor-mode): Rework. --- doc/emacs/programs.texi | 23 +++ etc/NEWS | 35 ++++- lisp/progmodes/hideshow.el | 280 +++++++++++++++++++++++++++++++++---- 3 files changed, 308 insertions(+), 30 deletions(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index f00d79f499a..2000bc2b785 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1730,6 +1730,9 @@ Hideshow =20 @vindex hs-hide-comments-when-hiding-all @vindex hs-display-lines-hidden +@vindex hs-show-indicators +@vindex hs-indicator-type +@vindex hs-indicator-maximum-buffer-size @vindex hs-isearch-open @vindex hs-special-modes-alist These variables can be used to customize Hideshow mode: @@ -1743,6 +1746,26 @@ Hideshow If non-@code{nil}, display the number of hidden lines next to the ellipsis. =20 +@item hs-show-indicators +This variable controls whether Hideshow mode should display indicators +of hidden and shown blocks. The indicators also allow toggling the +hide/show state of each block. If the value is non-@code{nil}, enables +the indicators in all blocks. The default is @code{nil}, which disables +the indicators. + +@item hs-indicactor-type +This variable controls where to show the indicators, if they are +enabled. You can show them on the fringe (@pxref{Windows Fringes}) or +in the window's margin. The default is to use the fringe if it's +available, otherwise to use the margin. + +@item hs-indicator-maximum-buffer-size +This variable limits the display of hideshow indicators to buffers that +are not too large. (Larger buffers might adversely affect redisplay +performance.) By default, buffers larger than 2MB have the indicators +disabled; the value of @code{nil} will activate the indicators +regardless of the buffer size. + @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/etc/NEWS b/etc/NEWS index 27e3f1e4ce0..345788c118f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -955,9 +955,40 @@ at point to explore. =20 ** Hideshow =20 ++++ *** New user option 'hs-display-lines-hidden'. -If this option is non-nil, Hideshow displays the number of hidden -lines next to the ellipsis. +If this option is non-nil, Hideshow displays the number of hidden lines +next to the ellipsis. By default this is disabled. + ++++ +*** New user option 'hs-show-indicators'. +This user option determines if hideshow should display indicators to +toggle the block hiding. If non-nil, the indicators are enabled in all +blocks except comment blocks. + +Otherwise if this is set to 'include-comments', the indicators will be +displayed also in comments block. + +By default this is disabled. + +*** New user option 'hs-indicator-maximum-buffer-size'. +This user option limits the display of hideshow indicators to buffers +that are not too large. By default, buffers larger than 2MB have the +indicators disabled; the value of nil will activate the indicators +regardless of the buffer size. + ++++ +*** New user option 'hs-indicator-type'. +This user option determine which indicator type should be used for the +block indicators. + +The possible values can be: 'fringe', display the indicators in the +fringe (the default); 'margin', display the indicators in the margin; +nil, display the indicators at end-of-line. + +The new icons 'hs-indicator-show' and 'hs-indicator-hide', can be used +for customize the indicators appearance, only if 'hs-indicator-type' is +set to 'margin' or nil. =20 ** C-ts mode =20 diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 445bdeeb7a7..d8dfd2b559b 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -220,6 +220,9 @@ =20 ;;; Code: (require 'mule-util) ; For `truncate-string-ellipsis' +;; For indicators +(require 'icons) +(require 'fringe) =20 ;;------------------------------------------------------------------------= --- ;; user-configurable variables @@ -236,6 +239,16 @@ hs-ellipsis use that face for the ellipsis instead." :version "31.1") =20 +(defface hs-indicator-hide + '((t :inherit (shadow default))) + "Face used in hideshow indicator to indicate a hidden block." + :version "31.1") + +(defface hs-indicator-show + '((t :inherit hs-indicator-hide :weight bold)) + "Face used in hideshow indicator to indicate a shown block." + :version "31.1") + (defcustom hs-hide-comments-when-hiding-all t "Hide the comments too when you do an `hs-hide-all'." :type 'boolean) @@ -265,6 +278,83 @@ hs-isearch-open (const :tag "open both code and comment blocks" t) (const :tag "don't open any of them" nil))) =20 +(defcustom hs-show-indicators nil + "Whether hideshow should display block hide/show indicators. +If non-nil, hideshow will display indicators for toggling the +visibility of a block everywhere except in comment blocks. + +The indicators appearance are specified in `hs-indicator-type' (which see)= ." + :type 'boolean + :version "31.1") + +(defcustom hs-indicator-type 'fringe + "Indicate which indicator type to use for the block indicators. + +The possible values can be: + + - `fringe', display the indicators in the fringe. + - `margin', display the indicators in the margin. + - nil, display the indicators at end-of-line. + +This only have effect if `hs-show-indicators' is non-nil." + :type '(choice + (const :tag "Fringes" fringe) + (const :tag "Margins" margin) + (const :tag "Indicator at end-of-line" nil)) + :version "31.1") + +(defcustom hs-indicator-maximum-buffer-size 2000000 ;2mb + "Max buffer size in bytes where the indicators should be enabled. +If current buffer is larger than this variable value, the indicators +will be disabled. + +If set to nil, the indicators will be activated regardless of the buffer +size." + :type '(choice natnum (const :tag "No limit" nil)) + :version "31.1") + +(define-fringe-bitmap + 'hs-hide + [#b0000000 + #b1000001 + #b1100011 + #b0110110 + #b0011100 + #b0001000 + #b0000000]) + +(define-fringe-bitmap + 'hs-show + [#b0110000 + #b0011000 + #b0001100 + #b0000110 + #b0001100 + #b0011000 + #b0110000]) + +(define-icon hs-indicator-hide nil + `((image "outline-open.svg" "outline-open.pbm" + :face hs-indicator-hide + :height (0.6 . em) + :ascent center) + (symbol "=F0=9F=9E=83" "=E2=96=BC" :face hs-indicator-hide) + (text "-" :face hs-indicator-hide)) + "Icon used for hide block at point. +This is only used if `hs-indicator-type' is set to `margin' or nil." + :version "31.1") + +(define-icon hs-indicator-show nil + `((image "outline-close.svg" "outline-close.pbm" + :face hs-indicator-show + :height (0.6 . em) + :ascent center) + (symbol "=E2=96=B8" "=E2=96=B6" :face hs-indicator-show) + (text "+" :face hs-indicator-show)) + "Icon used for show block at point. +This is only used if `hs-indicator-type' is set to `margin' or nil." + :version "31.1") + ;;;###autoload (defvar hs-special-modes-alist ;; FIXME: Currently the check is made via @@ -378,7 +468,13 @@ hs-minor-mode-map "C-c @ C-t" #'hs-hide-all "C-c @ C-d" #'hs-hide-block "C-c @ C-e" #'hs-toggle-hiding - "S-" #'hs-toggle-hiding) + "S-" #'hs-toggle-hiding + " " #'hs-indicator-mouse-toggle-hidding) + +(defvar-keymap hs-indicators-map + :doc "Keymap for hideshow indicators." + " " #'hs-indicator-mouse-toggle-hidding + "" #'hs-toggle-hiding) =20 (easy-menu-define hs-minor-mode-menu hs-minor-mode-map "Menu used when hideshow minor mode is active." @@ -562,6 +658,118 @@ hs-make-overlay (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) ov)) =20 +(defun hs-block-positions () + "Return the current code block positions. +This return a cons-cell with the current code block beginning and end +positions. This does nothing if there is not a code block at current +point." + (save-match-data + (save-excursion + (when (funcall hs-looking-at-block-start-p-func) + (let ((mdata (match-data t)) + (header-end (match-end 0)) + block-beg block-end) + ;; `block-start' is the point at the end of the block + ;; beginning, which may need to be adjusted + (save-excursion + (goto-char (funcall (or hs-adjust-block-beginning #'identity) + header-end)) + (setq block-beg (line-end-position))) + ;; `block-end' is the point at the end of the block + (hs-forward-sexp mdata 1) + (setq block-end + (cond ((and (stringp hs-block-end-regexp) + (looking-back hs-block-end-regexp nil)) + (match-beginning 0)) + ((functionp hs-block-end-regexp) + (funcall hs-block-end-regexp) + (match-beginning 0)) + (t (point)))) + (cons block-beg block-end)))))) + +(defun hs--make-indicators-overlays (beg) + "Helper function to make the indicators overlays." + (let ((hiddenp (hs-overlay-at (pos-eol)))) + (when-let* (;; If we are going to use the EOL indicators then don't + ;; make the overlay if current block is already hidden + (_ (not (and (not hs-indicator-type) hiddenp))) + + (o (make-overlay + (if hs-indicator-type beg (pos-eol)) + (1+ (if hs-indicator-type beg (pos-eol))))) + (fringe-type (if hiddenp 'hs-show 'hs-hide)) + (face-or-icon (if hiddenp 'hs-indicator-show 'hs-indicator= -hide))) + + (overlay-put o 'hs-indicator t) + (overlay-put o 'hs-indicator-block-start beg) + (overlay-put o 'evaporate t) + (overlay-put o 'priority -50) + + (overlay-put + o 'before-string + (pcase hs-indicator-type + ;; Fringes + ('fringe + (propertize + "+" 'display + `(left-fringe ,fringe-type ,face-or-icon))) + ;; Margins + ('margin + (propertize + "+" 'display + `((margin left-margin) + ,(or (plist-get (icon-elements face-or-icon) 'image) + (plist-get (icon-elements face-or-icon) 'string))) + 'face face-or-icon + 'rear-nonsticky nil + 'keymap hs-indicators-map)) + ;; EOL string + ('nil + (propertize + (icon-string face-or-icon) + 'mouse-face 'highlight + 'keymap hs-indicators-map))))))) + +(defun hs--add-indicators (&optional beg end) + "Add hideable indicators from BEG to END." + (save-excursion + (setq beg (if (null beg) (window-start) (goto-char beg) (pos-bol)) + end (if (null end) (window-end) (goto-char end) (pos-bol)))) + (goto-char beg) + (remove-overlays beg end 'hs-indicator t) + + (while (funcall hs-find-next-block-func hs-block-start-regexp end nil) + (when-let* ((b-beg (match-beginning 0)) + (_ (save-excursion + (goto-char b-beg) + (funcall hs-looking-at-block-start-p-func))) + ;; `catch' is used here if the search fail due + ;; unbalanced parenthesis or any other unknown error + ;; caused in `hs-forward-sexp'. + (b-end (catch 'hs-indicator-error + (save-excursion + (goto-char b-beg) + (condition-case _ + (funcall hs-forward-sexp-func 1) + (scan-error (throw 'hs-indicator-error nil))) + (point)))) + ;; Check if block is longer than 1 line. + (_ (< b-beg b-end)) + (_ (not (=3D (line-number-at-pos b-beg) (line-number-at-po= s b-end))))) + (hs--make-indicators-overlays b-beg)) + ;; Only 1 indicator per line + (forward-line 1)) + `(jit-lock-bounds ,beg . ,end)) + +(defun hs--refresh-indicators () + "Update indicators appearance at current block." + (when hs-show-indicators + (save-match-data + (save-excursion + ;; Using window-start and window-end is more faster + ;; than computing again the block positions + (hs--add-indicators (window-start) (window-end)))))) + (defun hs--get-ellipsis (b e) "Helper function for `hs-make-overlay'. This returns the ellipsis string to use and its face." @@ -669,32 +877,19 @@ hs-hide-block-at-point and then further adjusted to be at the end of the line." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) - (when (funcall hs-looking-at-block-start-p-func) - (let ((mdata (match-data t)) - (header-end (match-end 0)) - p q ov) - ;; `p' is the point at the end of the block beginning, which - ;; may need to be adjusted - (save-excursion - (goto-char (funcall (or hs-adjust-block-beginning #'identity) - header-end)) - (setq p (line-end-position))) - ;; `q' is the point at the end of the block - (hs-forward-sexp mdata 1) - (setq q (cond ((and (stringp hs-block-end-regexp) - (looking-back hs-block-end-regexp nil)) - (match-beginning 0)) - ((functionp hs-block-end-regexp) - (funcall hs-block-end-regexp) - (match-beginning 0)) - (t (point)))) - (when (and (< p q) (> (count-lines p q) 1)) - (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) - (delete-overlay ov)) - ((not hs-allow-nesting) - (hs-discard-overlays p q))) - (hs-make-overlay p q 'code (- header-end p))) - (goto-char (if end q (min p header-end))))))) + (let* ((block (hs-block-positions)) + (p (car-safe block)) + (q (cdr-safe block)) + ov) + (if (and block (< p q) (> (count-lines p q) 1)) + (progn + (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) + (delete-overlay ov)) + ((not hs-allow-nesting) + (hs-discard-overlays p q))) + (goto-char q) + (hs-make-overlay p q 'code (- (match-end 0) p))) + (goto-char (if end q (min p (match-end 0)))))))) =20 (defun hs-inside-comment-p () "Return non-nil if point is inside a comment, otherwise nil. @@ -976,6 +1171,7 @@ hs-hide-block (funcall hs-looking-at-block-start-p-func) (funcall hs-find-block-beginning-func)) (hs-hide-block-at-point end c-reg) + (hs--refresh-indicators) (run-hooks 'hs-hide-hook)))))) =20 (defun hs-show-block (&optional end) @@ -1011,6 +1207,7 @@ hs-show-block (when (and p q) (hs-discard-overlays p q) (goto-char (if end q (1+ p)))))) + (hs--refresh-indicators) (run-hooks 'hs-show-hook))) =20 (defun hs-hide-level (arg) @@ -1037,6 +1234,20 @@ hs-toggle-hiding =20 (define-obsolete-function-alias 'hs-mouse-toggle-hiding #'hs-toggle-hiding= "27.1") =20 +(defun hs-indicator-mouse-toggle-hidding (event) + "Toggle block hidding with indicators." + (interactive "e") + (let* ((overlays (save-excursion + (goto-char (posn-point (event-end event))) + (overlays-in (pos-bol) (pos-eol)))) + (pos (catch 'hs--indicator-ov + (dolist (ov overlays) + (when-let* ((ov (overlay-get ov 'hs-indicator-block-star= t))) + (throw 'hs--indicator-ov ov)))))) + (when pos + (goto-char pos) + (hs-toggle-hiding)))) + (defun hs-hide-initial-comment-block () "Hide the first block of comments in a file. This can be useful if you have huge RCS logs in those comments." @@ -1086,8 +1297,21 @@ hs-minor-mode #'turn-off-hideshow nil t) (setq-local line-move-ignore-invisible t) - (add-to-invisibility-spec '(hs . t))) + (add-to-invisibility-spec '(hs . t)) + ;; Add block indicators + (when (and hs-show-indicators + (or (and (integerp hs-indicator-maximum-buffer-size) + (< (buffer-size) hs-indicator-maximum-buffer-s= ize)) + (not hs-indicator-maximum-buffer-size))) + (when (and (not (display-graphic-p)) + (eq hs-indicator-type 'fringe)) + (setq-local hs-indicator-type 'margin)) + (jit-lock-register #'hs--add-indicators))) + (remove-from-invisibility-spec '(hs . t)) + (when hs-show-indicators + (jit-lock-unregister #'hs--add-indicators) + (remove-overlays nil nil 'hs-indicator t)) ;; hs-show-all does nothing unless h-m-m is non-nil. (let ((hs-minor-mode t)) (hs-show-all)))) --=20 2.51.0 --=-=-= Content-Type: text/plain -- - E.G via Gnus and Org. --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Mon Sep 15 08:20:07 2025 Received: (at 79294) by debbugs.gnu.org; 15 Sep 2025 12:20:07 +0000 Received: from localhost ([127.0.0.1]:40246 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uy8Bj-0006i5-7E for submit@debbugs.gnu.org; Mon, 15 Sep 2025 08:20:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:53932) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uy8Bf-0006f1-96 for 79294@debbugs.gnu.org; Mon, 15 Sep 2025 08:20:04 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uy8BX-0007RK-H6; Mon, 15 Sep 2025 08:19:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=Q8ZF5QZt/SAb2OO41+HKWEwIGogc0CIhckJuoBizmhU=; b=rekMkhlUhG/10kC+GqUR bR9uTwdvqbiVZC4ZetBJCt2evuugruIh605ijhEpma6OIlGpZu3ih0ffXrL/CEw1RzdsDtMcfQuwg pfWmi/4BcmOxOEQh0pNcNNQk5KRovwWj/fwrFuagxJ0d1QGSvXZnRFvbRkLSby5sxZvCYYKqYnyPU IwFnQXTPwv3ICUMvLxYdHZRecI5EQryWOhBgUSzaDBFvMC+W+HjzRtHy5xUcmIMe4YIxk+Pxbxsnl 93OGN6OSFwT4AaOzUfNdGi22T1jLz6H7BC+fcBlu6DLey5lURcQrl+211DN43EAWsk+ZlqgmHY2KJ +22ZikBqVRdv1w==; Date: Mon, 15 Sep 2025 15:19:51 +0300 Message-Id: <867bxzoql4.fsf@gnu.org> From: Eli Zaretskii To: Elijah Gabe =?utf-8?Q?P=C3=A9rez?= In-Reply-To: <87tt1432np.fsf@gmail.com> (message from Elijah Gabe =?utf-8?Q?P=C3=A9rez?= on Sun, 14 Sep 2025 19:50:02 -0600) Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> <878qik9vgp.fsf@gmail.com> <86ecsar6fp.fsf@gnu.org> <87tt1432np.fsf@gmail.com> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Elijah Gabe Pérez > Cc: 79294@debbugs.gnu.org > Date: Sun, 14 Sep 2025 19:50:02 -0600 > > >> + (let* ((o (make-overlay > >> + ;; FIXME: The mouse-1 event doesn't work well for fringes, > >> + ;; a workaround for this is set the overlay at the > >> + ;; beginning of the line only for fringe indicators > >> + (if (eq hs-indicator-type 'fringe) (pos-bol) beg) > >> + (1+ beg))) > > > > What doesn't work well in mouse-1 clicks on the fringes? > > Apparently the fringe keymap does not work if the overlay is not created > from BOL: > > e.g. > > The mouse-1 clicks works for this since it is created at BOL: > > #+begin_src elisp > |+| (defvar-local var nil > | | "Docstring.") > #+end_src > > But for code blocks that are not at BOL this will not work: > > #+begin_src elisp > |+| (defvar-local var ; <- This works > |+| (when foo ; <- This does not > | | (bar)) > | | "Docstring.") > #+end_src Maybe it's a bug? Can you show a simple Lisp program that I could evaluate in *scratch* and use to look into this issue? From debbugs-submit-bounces@debbugs.gnu.org Mon Sep 15 16:21:53 2025 Received: (at 79294) by debbugs.gnu.org; 15 Sep 2025 20:21:53 +0000 Received: from localhost ([127.0.0.1]:43454 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uyFhv-0008NV-Q4 for submit@debbugs.gnu.org; Mon, 15 Sep 2025 16:21:52 -0400 Received: from mail-yw1-x1141.google.com ([2607:f8b0:4864:20::1141]:51236) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1uyFhp-0008N8-MA for 79294@debbugs.gnu.org; Mon, 15 Sep 2025 16:21:47 -0400 Received: by mail-yw1-x1141.google.com with SMTP id 00721157ae682-71d6051afbfso34529907b3.2 for <79294@debbugs.gnu.org>; Mon, 15 Sep 2025 13:21:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1757967699; x=1758572499; darn=debbugs.gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=BMxyJfTFPIzUMVl/c5Na+4prGlS4yQe32e0O1BNjG64=; b=FBRjWVPLyYRVzkA3mpCpntIo0iHROse4J4S/+0kdmMiH1hhq65uj87BOvLyD4gEwjO O2d9EE/Q3C6RDFsrhHqJFjxGe9SzNGgp4+BInEFCmcVIgo9oPaCa030UV3+8nBHYwTiY Yr1LqA3DFMBBcG1dZlt/0oJw5x4pZakFkvSdjwoJeggScoAn/SugHCIR4Jmi728ll0h0 oMMsasVGdEGkasGBKUZFda0ppvj/azelQEST+s8X24CqUIPemsbAIB66z22ZXOGPTUTw lyZP8TGlE7yMTbKtNy5Ncfh4ao7TCKoqyqbLdPNeDhVvMlADfg5/APPkvpff8p+YL8yf D1UQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757967699; x=1758572499; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=BMxyJfTFPIzUMVl/c5Na+4prGlS4yQe32e0O1BNjG64=; b=QxBfsgEC3YXWsxiIh49jg/lVcrTAP8jbnAvCdPAunHmA4X3qeRJ/M875mbpxd3yy/l HwnMUShFsErJrlOp03DSYeX6oB/nqhqk8kMVKD7jGDqXJM9BmkbYkgNA0T0r1c8l14JZ Bh6skza09wFfN87fSSUzzhhrrORVdLcv7+N4QVemkKx0yhGgekIR5bkfaDOjVBnHoOXP p5QeydacHUvjnDsw1iDjWAl+ORNi4T37/XA6hMC+BCmDAsXMtxXvmzlP4MTm6xjZYyzd Vmo8yjtSgRyVEV3Qr4axOntJ56ifVV2lGSgFpTEcKYuAIJy0Mo5/vgLTV+e/Z1udfdGM Ue8Q== X-Gm-Message-State: AOJu0YyrKBJIVnBM2w+86VdiTYW+8jJnB2JCq3CAL80TNx3pi3VaGBEZ rlpQG6vl63nH7gtoXPTz4nZZHjvf+QNDBNRweP9GiNINj1f6vYowKEouKNzWms/F X-Gm-Gg: ASbGncvxXGwO5+BuH+/2XrgnMJC4z7PKnUlT68UQztdcCye60IXI5LrhvX5Vguzv8pp FJh7h73mm9S+6HNSccm1xcdH+aDNjSwCqtNeAgyvcMozNSgCu1EcqVdpgBowQJg0mpWT/HpsCY9 ay3/UGQRHTvxwxSa7XocusiF9LN5Gk67ykr1hk3J2bLvToZmb0AyWyowHRFc51azOKNlrqBBfFN /i8wVo6L3vAuw6NUMjkMCykfvl4DZLx67Vedt3KMJCjUkIhCWhb9pxsQ6aUtFDI5n24QW1gUDph F+UyNRDaWO8ZtswAw6GvkjlyqRAURIXjBqOj6u8Y7hg/axO1P4TYnYMvupDwJX7GIEUWUmFRdvg Xmci/KTLwA2U8yhE8 X-Google-Smtp-Source: AGHT+IGvydW6N/KHuxThIV5XtATVNUhM43fxDChtkeH/6JI7o2vGrOdGujCeptqyGlVA9BQ6XBTefQ== X-Received: by 2002:a05:690c:4d07:b0:727:121e:fa4e with SMTP id 00721157ae682-73064df793dmr121020837b3.42.1757967698923; Mon, 15 Sep 2025 13:21:38 -0700 (PDT) Received: from fedora ([189.215.165.139]) by smtp.gmail.com with ESMTPSA id 00721157ae682-72f79a8c8f7sm35019087b3.69.2025.09.15.13.21.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Sep 2025 13:21:38 -0700 (PDT) From: =?utf-8?Q?Elijah_Gabe_P=C3=A9rez?= To: Eli Zaretskii Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. In-Reply-To: <867bxzoql4.fsf@gnu.org> References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> <878qik9vgp.fsf@gmail.com> <86ecsar6fp.fsf@gnu.org> <87tt1432np.fsf@gmail.com> <867bxzoql4.fsf@gnu.org> Date: Mon, 15 Sep 2025 14:21:35 -0600 Message-ID: <87y0qflb5c.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/31.0.50 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 0.3 (/) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) --=-=-= Content-Type: text/plain >> > What doesn't work well in mouse-1 clicks on the fringes? >> >> Apparently the fringe keymap does not work if the overlay is not created >> from BOL: >> >> e.g. >> >> The mouse-1 clicks works for this since it is created at BOL: >> >> #+begin_src elisp >> |+| (defvar-local var nil >> | | "Docstring.") >> #+end_src >> >> But for code blocks that are not at BOL this will not work: >> >> #+begin_src elisp >> |+| (defvar-local var ; <- This works >> |+| (when foo ; <- This does not >> | | (bar)) >> | | "Docstring.") >> #+end_src > > Maybe it's a bug? Can you show a simple Lisp program that I could > evaluate in *scratch* and use to look into this issue? Sure, here is a recipe: 1. M-x fundamental-mode 2. M-: (insert " " (propertize "x" 'display '(left-fringe left-arrow error) 'keymap (let ((map (make-sparse-keymap))) (define-key map (kbd " ") (lambda () (interactive) (print "message!"))) map))) 3. Click on the fringe icon, it should not work an throw the " is undefined" message. 4. M-: ; (Same code but without the spaces at BOL) (insert (propertize "x" 'display '(left-fringe left-arrow error) 'keymap (let ((map (make-sparse-keymap))) (define-key map (kbd " ") (lambda () (interactive) (print "message!"))) map))) 5. Click on the fringe icon, it should display the message "message!". I'm not sure if this is a known bug, but I've seen packages that locally (in the buffer local map) bind mouse-1 to a command that scans for the overlay in some region of the buffer, probably the fringe keymap doesn't support this yet. For the patch, I have already solved this problem, although I am not happy with the solution, it is possibly the only way to solve this: --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Add-hideable-indicators-for-hideshow.-Bug-79294.patch Content-Transfer-Encoding: quoted-printable >From 21002bcb67f0b35cf3e47a96bd2c1fab1985ddcd Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?El=3DC3=3DADas=3D20Gabriel=3D20P=3DC3=3DA9rez?=3D Date: Sun, 17 Aug 2025 20:02:59 -0600 Subject: [PATCH] Add hideable indicators for hideshow. (Bug#79294) * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes. * lisp/progmodes/hideshow.el (hs-show-indicators) (hs-indicator-type, hs-indicator-maximum-buffer-size): New user options. (hs-indicator-hide, hs-indicator-show): New icons. (hs-block-positions, hs--make-indicators-overlays) (hs-indicator-mouse-toggle-hidding, hs--add-indicators) (hs--refresh-indicators): New functions. (hs-hide-block-at-point): Use hs-block-positions. (hs-inside-comment-p): Use 'get-char-property' instead of 'hs-overlay-at'. (hs-minor-mode): Rework. --- doc/emacs/programs.texi | 23 +++ etc/NEWS | 32 ++++- lisp/progmodes/hideshow.el | 280 +++++++++++++++++++++++++++++++++---- 3 files changed, 304 insertions(+), 31 deletions(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index f00d79f499a..2000bc2b785 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1730,6 +1730,9 @@ Hideshow =20 @vindex hs-hide-comments-when-hiding-all @vindex hs-display-lines-hidden +@vindex hs-show-indicators +@vindex hs-indicator-type +@vindex hs-indicator-maximum-buffer-size @vindex hs-isearch-open @vindex hs-special-modes-alist These variables can be used to customize Hideshow mode: @@ -1743,6 +1746,26 @@ Hideshow If non-@code{nil}, display the number of hidden lines next to the ellipsis. =20 +@item hs-show-indicators +This variable controls whether Hideshow mode should display indicators +of hidden and shown blocks. The indicators also allow toggling the +hide/show state of each block. If the value is non-@code{nil}, enables +the indicators in all blocks. The default is @code{nil}, which disables +the indicators. + +@item hs-indicactor-type +This variable controls where to show the indicators, if they are +enabled. You can show them on the fringe (@pxref{Windows Fringes}) or +in the window's margin. The default is to use the fringe if it's +available, otherwise to use the margin. + +@item hs-indicator-maximum-buffer-size +This variable limits the display of hideshow indicators to buffers that +are not too large. (Larger buffers might adversely affect redisplay +performance.) By default, buffers larger than 2MB have the indicators +disabled; the value of @code{nil} will activate the indicators +regardless of the buffer size. + @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/etc/NEWS b/etc/NEWS index 27e3f1e4ce0..ee02f732795 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -955,9 +955,37 @@ at point to explore. =20 ** Hideshow =20 ++++ *** New user option 'hs-display-lines-hidden'. -If this option is non-nil, Hideshow displays the number of hidden -lines next to the ellipsis. +If this option is non-nil, Hideshow displays the number of hidden lines +next to the ellipsis. By default this is disabled. + ++++ +*** New user option 'hs-show-indicators'. +This user option determines if hideshow should display indicators to +toggle the block hiding. If non-nil, the indicators are enabled in all +blocks. + +By default this is disabled. + +*** New user option 'hs-indicator-maximum-buffer-size'. +This user option limits the display of hideshow indicators to buffers +that are not too large. By default, buffers larger than 2MB have the +indicators disabled; the value of nil will activate the indicators +regardless of the buffer size. + ++++ +*** New user option 'hs-indicator-type'. +This user option determine which indicator type should be used for the +block indicators. + +The possible values can be: 'fringe', display the indicators in the +fringe (the default); 'margin', display the indicators in the margin; +nil, display the indicators at end-of-line. + +The new icons 'hs-indicator-show' and 'hs-indicator-hide', can be used +for customize the indicators appearance, only if 'hs-indicator-type' is +set to 'margin' or nil. =20 ** C-ts mode =20 diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 445bdeeb7a7..f1235515a36 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -220,6 +220,9 @@ =20 ;;; Code: (require 'mule-util) ; For `truncate-string-ellipsis' +;; For indicators +(require 'icons) +(require 'fringe) =20 ;;------------------------------------------------------------------------= --- ;; user-configurable variables @@ -236,6 +239,16 @@ hs-ellipsis use that face for the ellipsis instead." :version "31.1") =20 +(defface hs-indicator-hide + '((t :inherit (shadow default))) + "Face used in hideshow indicator to indicate a hidden block." + :version "31.1") + +(defface hs-indicator-show + '((t :inherit hs-indicator-hide :weight bold)) + "Face used in hideshow indicator to indicate a shown block." + :version "31.1") + (defcustom hs-hide-comments-when-hiding-all t "Hide the comments too when you do an `hs-hide-all'." :type 'boolean) @@ -265,6 +278,83 @@ hs-isearch-open (const :tag "open both code and comment blocks" t) (const :tag "don't open any of them" nil))) =20 +(defcustom hs-show-indicators nil + "Whether hideshow should display block hide/show indicators. +If non-nil, hideshow will display indicators for toggling the visibility +of code blocks. + +The indicators appearance are specified in `hs-indicator-type' (which see)= ." + :type 'boolean + :version "31.1") + +(defcustom hs-indicator-type 'fringe + "Indicate which indicator type to use for the block indicators. + +The possible values can be: + + - `fringe', display the indicators in the fringe. + - `margin', display the indicators in the margin. + - nil, display the indicators at end-of-line. + +This only have effect if `hs-show-indicators' is non-nil." + :type '(choice + (const :tag "Fringes" fringe) + (const :tag "Margins" margin) + (const :tag "Indicator at end-of-line" nil)) + :version "31.1") + +(defcustom hs-indicator-maximum-buffer-size 2000000 ;2mb + "Max buffer size in bytes where the indicators should be enabled. +If current buffer is larger than this variable value, the indicators +will be disabled. + +If set to nil, the indicators will be activated regardless of the buffer +size." + :type '(choice natnum (const :tag "No limit" nil)) + :version "31.1") + +(define-fringe-bitmap + 'hs-hide + [#b0000000 + #b1000001 + #b1100011 + #b0110110 + #b0011100 + #b0001000 + #b0000000]) + +(define-fringe-bitmap + 'hs-show + [#b0110000 + #b0011000 + #b0001100 + #b0000110 + #b0001100 + #b0011000 + #b0110000]) + +(define-icon hs-indicator-hide nil + `((image "outline-open.svg" "outline-open.pbm" + :face hs-indicator-hide + :height (0.6 . em) + :ascent center) + (symbol "=F0=9F=9E=83" "=E2=96=BC" :face hs-indicator-hide) + (text "-" :face hs-indicator-hide)) + "Icon used for hide block at point. +This is only used if `hs-indicator-type' is set to `margin' or nil." + :version "31.1") + +(define-icon hs-indicator-show nil + `((image "outline-close.svg" "outline-close.pbm" + :face hs-indicator-show + :height (0.6 . em) + :ascent center) + (symbol "=E2=96=B8" "=E2=96=B6" :face hs-indicator-show) + (text "+" :face hs-indicator-show)) + "Icon used for show block at point. +This is only used if `hs-indicator-type' is set to `margin' or nil." + :version "31.1") + ;;;###autoload (defvar hs-special-modes-alist ;; FIXME: Currently the check is made via @@ -378,7 +468,13 @@ hs-minor-mode-map "C-c @ C-t" #'hs-hide-all "C-c @ C-d" #'hs-hide-block "C-c @ C-e" #'hs-toggle-hiding - "S-" #'hs-toggle-hiding) + "S-" #'hs-toggle-hiding + " " #'hs-indicator-mouse-toggle-hidding) + +(defvar-keymap hs-indicators-map + :doc "Keymap for hideshow indicators." + " " #'hs-indicator-mouse-toggle-hidding + "" #'hs-toggle-hiding) =20 (easy-menu-define hs-minor-mode-menu hs-minor-mode-map "Menu used when hideshow minor mode is active." @@ -562,6 +658,116 @@ hs-make-overlay (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) ov)) =20 +(defun hs-block-positions () + "Return the current code block positions. +This return a cons-cell with the current code block beginning and end +positions. This does nothing if there is not a code block at current +point." + (save-match-data + (save-excursion + (when (funcall hs-looking-at-block-start-p-func) + (let ((mdata (match-data t)) + (header-end (match-end 0)) + block-beg block-end) + ;; `block-start' is the point at the end of the block + ;; beginning, which may need to be adjusted + (save-excursion + (goto-char (funcall (or hs-adjust-block-beginning #'identity) + header-end)) + (setq block-beg (line-end-position))) + ;; `block-end' is the point at the end of the block + (hs-forward-sexp mdata 1) + (setq block-end + (cond ((and (stringp hs-block-end-regexp) + (looking-back hs-block-end-regexp nil)) + (match-beginning 0)) + ((functionp hs-block-end-regexp) + (funcall hs-block-end-regexp) + (match-beginning 0)) + (t (point)))) + (cons block-beg block-end)))))) + +(defun hs--make-indicators-overlays (beg) + "Helper function to make the indicators overlays." + (let ((hiddenp (eq 'hs (get-char-property (pos-eol) 'invisible)))) + (when-let* ((o (make-overlay + (if hs-indicator-type beg (pos-eol)) + (1+ (if hs-indicator-type beg (pos-eol))))) + (fringe-type (if hiddenp 'hs-show 'hs-hide)) + (face-or-icon (if hiddenp 'hs-indicator-show 'hs-indicator= -hide))) + + (overlay-put o 'hs-indicator t) + (overlay-put o 'hs-indicator-block-start beg) + (overlay-put o 'evaporate t) + (overlay-put o 'priority -50) + + (overlay-put + o 'before-string + (pcase hs-indicator-type + ;; Fringes + ('fringe + (propertize + "+" 'display + `(left-fringe ,fringe-type ,face-or-icon))) + ;; Margins + ('margin + (propertize + "+" 'display + `((margin left-margin) + ,(or (plist-get (icon-elements face-or-icon) 'image) + (icon-string face-or-icon))) + 'face face-or-icon + 'keymap hs-indicators-map)) + ;; EOL string + ('nil + (propertize + (icon-string face-or-icon) + 'mouse-face 'highlight + 'keymap hs-indicators-map))))))) + +(defun hs--add-indicators (&optional beg end) + "Add hideable indicators from BEG to END." + (save-excursion + (setq beg (if (null beg) (window-start) (goto-char beg) (pos-bol)) + end (if (null end) (window-end) (goto-char end) (pos-bol)))) + (goto-char beg) + (remove-overlays beg end 'hs-indicator t) + + (while (funcall hs-find-next-block-func hs-block-start-regexp end nil) + (when-let* ((b-beg (match-beginning 0)) + (_ (save-excursion + (goto-char b-beg) + (funcall hs-looking-at-block-start-p-func))) + ;; `catch' is used here if the search fail due + ;; unbalanced parenthesis or any other unknown error + ;; caused in `hs-forward-sexp'. + (b-end (catch 'hs-indicator-error + (save-excursion + (goto-char b-beg) + (condition-case _ + (funcall hs-forward-sexp-func 1) + (scan-error (throw 'hs-indicator-error nil))) + (point)))) + ;; Check if block is longer than 1 line. + (_ (< b-beg b-end)) + ;; If we are going to use the EOL indicators, then + ;; ignore the invisible lines which mostly are already + ;; hidden blocks. + (_ (> (count-lines b-beg b-end (not hs-indicator-type)) 1)= )) + (hs--make-indicators-overlays b-beg)) + ;; Only 1 indicator per line + (forward-line 1)) + `(jit-lock-bounds ,beg . ,end)) + +(defun hs--refresh-indicators () + "Update indicators appearance at current block." + (when hs-show-indicators + (save-match-data + (save-excursion + ;; Using window-start and window-end is more faster + ;; than computing again the block positions + (hs--add-indicators (window-start) (window-end)))))) + (defun hs--get-ellipsis (b e) "Helper function for `hs-make-overlay'. This returns the ellipsis string to use and its face." @@ -669,32 +875,19 @@ hs-hide-block-at-point and then further adjusted to be at the end of the line." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) - (when (funcall hs-looking-at-block-start-p-func) - (let ((mdata (match-data t)) - (header-end (match-end 0)) - p q ov) - ;; `p' is the point at the end of the block beginning, which - ;; may need to be adjusted - (save-excursion - (goto-char (funcall (or hs-adjust-block-beginning #'identity) - header-end)) - (setq p (line-end-position))) - ;; `q' is the point at the end of the block - (hs-forward-sexp mdata 1) - (setq q (cond ((and (stringp hs-block-end-regexp) - (looking-back hs-block-end-regexp nil)) - (match-beginning 0)) - ((functionp hs-block-end-regexp) - (funcall hs-block-end-regexp) - (match-beginning 0)) - (t (point)))) - (when (and (< p q) (> (count-lines p q) 1)) - (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) - (delete-overlay ov)) - ((not hs-allow-nesting) - (hs-discard-overlays p q))) - (hs-make-overlay p q 'code (- header-end p))) - (goto-char (if end q (min p header-end))))))) + (let* ((block (hs-block-positions)) + (p (car-safe block)) + (q (cdr-safe block)) + ov) + (if (and block (< p q) (> (count-lines p q) 1)) + (progn + (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) + (delete-overlay ov)) + ((not hs-allow-nesting) + (hs-discard-overlays p q))) + (goto-char q) + (hs-make-overlay p q 'code (- (match-end 0) p))) + (goto-char (if end q (min p (match-end 0)))))))) =20 (defun hs-inside-comment-p () "Return non-nil if point is inside a comment, otherwise nil. @@ -885,7 +1078,7 @@ hs-already-hidden-p (beginning-of-line) (hs-find-block-beginning-match))))) (end-of-line) - (hs-overlay-at (point)))) + (eq 'hs (get-char-property (point) 'invisible)))) =20 ;; This function is not used anymore (Bug#700). (defun hs-c-like-adjust-block-beginning (initial) @@ -976,6 +1169,7 @@ hs-hide-block (funcall hs-looking-at-block-start-p-func) (funcall hs-find-block-beginning-func)) (hs-hide-block-at-point end c-reg) + (hs--refresh-indicators) (run-hooks 'hs-hide-hook)))))) =20 (defun hs-show-block (&optional end) @@ -1011,6 +1205,7 @@ hs-show-block (when (and p q) (hs-discard-overlays p q) (goto-char (if end q (1+ p)))))) + (hs--refresh-indicators) (run-hooks 'hs-show-hook))) =20 (defun hs-hide-level (arg) @@ -1037,6 +1232,20 @@ hs-toggle-hiding =20 (define-obsolete-function-alias 'hs-mouse-toggle-hiding #'hs-toggle-hiding= "27.1") =20 +(defun hs-indicator-mouse-toggle-hidding (event) + "Toggle block hidding with indicators." + (interactive "e") + (let* ((overlays (save-excursion + (goto-char (posn-point (event-end event))) + (overlays-in (pos-bol) (pos-eol)))) + (pos (catch 'hs--indicator-ov + (dolist (ov overlays) + (when-let* ((ov (overlay-get ov 'hs-indicator-block-star= t))) + (throw 'hs--indicator-ov ov)))))) + (when pos + (goto-char pos) + (hs-toggle-hiding)))) + (defun hs-hide-initial-comment-block () "Hide the first block of comments in a file. This can be useful if you have huge RCS logs in those comments." @@ -1086,8 +1295,21 @@ hs-minor-mode #'turn-off-hideshow nil t) (setq-local line-move-ignore-invisible t) - (add-to-invisibility-spec '(hs . t))) + (add-to-invisibility-spec '(hs . t)) + ;; Add block indicators + (when (and hs-show-indicators + (or (and (integerp hs-indicator-maximum-buffer-size) + (< (buffer-size) hs-indicator-maximum-buffer-s= ize)) + (not hs-indicator-maximum-buffer-size))) + (when (and (not (display-graphic-p)) + (eq hs-indicator-type 'fringe)) + (setq-local hs-indicator-type 'margin)) + (jit-lock-register #'hs--add-indicators))) + (remove-from-invisibility-spec '(hs . t)) + (when hs-show-indicators + (jit-lock-unregister #'hs--add-indicators) + (remove-overlays nil nil 'hs-indicator t)) ;; hs-show-all does nothing unless h-m-m is non-nil. (let ((hs-minor-mode t)) (hs-show-all)))) --=20 2.51.0 --=-=-= Content-Type: text/plain -- - E.G via Gnus and Org. --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Tue Sep 16 07:30:56 2025 Received: (at 79294) by debbugs.gnu.org; 16 Sep 2025 11:30:57 +0000 Received: from localhost ([127.0.0.1]:47523 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uyTtg-0004EE-0B for submit@debbugs.gnu.org; Tue, 16 Sep 2025 07:30:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:60802) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uyTtb-0003s0-Fn for 79294@debbugs.gnu.org; Tue, 16 Sep 2025 07:30:52 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uyTtT-00020T-Fe; Tue, 16 Sep 2025 07:30:44 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=xIJkKLwktyyiTaYj65/n5r0xhLwi97uWclmmeUbKVOs=; b=AWDeG3nQONszPNeoms4W kV47NR3m9xKiZ+iGKcbtEwgmUueB7kFgOVhwbfZVJczDTmqdZwUFVMi17IymqD5ZoBGNsHpNQjIhS 6C5VNSgJPpm2ddQLfmA02bMwrSKLdGEYdTAU51KT65bWod/T2FWMOBCSHzU7BT+o8xx3HxPPznRjF zifQHObr4yrpwC1Z0FVJEz4tiTd1uIMeAbBhchtognh2smxvEG+TJZlEJ4yuldSPbaeHR+8RjsWC4 JAhNPfOMYSoyj2VlNkF0dPmsi/9VZKTMHb8GQV/UzQSYTsVz25nK+0Brkgn7G53Ll6ZRcqVg6zm7L ck57r61PFNlXtg==; Date: Tue, 16 Sep 2025 14:30:16 +0300 Message-Id: <86zfaumy7r.fsf@gnu.org> From: Eli Zaretskii To: Elijah Gabe =?utf-8?Q?P=C3=A9rez?= In-Reply-To: <87y0qflb5c.fsf@gmail.com> (message from Elijah Gabe =?utf-8?Q?P=C3=A9rez?= on Mon, 15 Sep 2025 14:21:35 -0600) Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> <878qik9vgp.fsf@gmail.com> <86ecsar6fp.fsf@gnu.org> <87tt1432np.fsf@gmail.com> <867bxzoql4.fsf@gnu.org> <87y0qflb5c.fsf@gmail.com> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Elijah Gabe Pérez > Cc: 79294@debbugs.gnu.org > Date: Mon, 15 Sep 2025 14:21:35 -0600 > > > Maybe it's a bug? Can you show a simple Lisp program that I could > > evaluate in *scratch* and use to look into this issue? > > Sure, here is a recipe: > > 1. M-x fundamental-mode > > 2. M-: > (insert > " " > (propertize > "x" > 'display '(left-fringe > left-arrow > error) > 'keymap (let ((map (make-sparse-keymap))) > (define-key map (kbd " ") > (lambda () > (interactive) > (print "message!"))) > map))) > > 3. Click on the fringe icon, it should not work an throw the > " is undefined" message. I don't understand: you set the 'keymap' property on a character 'x', and expect a click on the fringe to somehow magically use that keymap? That's not how this works: the 'keymap' property defines the keymap to use when clicking on the character with the property. > 4. M-: ; (Same code but without the spaces at BOL) > (insert > (propertize > "x" > 'display '(left-fringe > left-arrow > error) > 'keymap (let ((map (make-sparse-keymap))) > (define-key map (kbd " ") > (lambda () > (interactive) > (print "message!"))) > map))) > > 5. Click on the fringe icon, it should display the message "message!". This is just a side effect of the implementation, arguably a bug. > I'm not sure if this is a known bug, but I've seen packages that locally > (in the buffer local map) bind mouse-1 to a command that scans for the > overlay in some region of the buffer, probably the fringe keymap doesn't > support this yet. That's exactly what you should do. See, for example, gdb-mouse-set-clear-breakpoint in gdb-mi.el. > For the patch, I have already solved this problem, although I am not > happy with the solution, it is possibly the only way to solve this: That is the right solution. From debbugs-submit-bounces@debbugs.gnu.org Tue Sep 16 15:21:38 2025 Received: (at 79294) by debbugs.gnu.org; 16 Sep 2025 19:21:38 +0000 Received: from localhost ([127.0.0.1]:50452 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uybFB-0007wx-8n for submit@debbugs.gnu.org; Tue, 16 Sep 2025 15:21:38 -0400 Received: from mail-ot1-x343.google.com ([2607:f8b0:4864:20::343]:52337) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1uybF8-0007wM-5D for 79294@debbugs.gnu.org; Tue, 16 Sep 2025 15:21:35 -0400 Received: by mail-ot1-x343.google.com with SMTP id 46e09a7af769-74a61973bedso5225418a34.0 for <79294@debbugs.gnu.org>; Tue, 16 Sep 2025 12:21:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758050488; x=1758655288; darn=debbugs.gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=sUio7stinGKRW6y61bIqKpsizzIlKe0e9yztA7na5Ew=; b=PD+3FJHa7sl5YGLChoEIzQwsJqZrFn2yq6l++3qxfJu+9egldXlBr1Tjc4gRmwkheD md7sYEMpZXIwcxP5Zslpk0Dv76uyR5MgeEqH1RjYuj/nJLG8NLAKuZbrW8kAq3/6qct5 YmWXoN+JnuKLYI9+rHlXXNxWFfCT7P+zQiA5uHQHIKVgLG+UZ+L0Zfslx2VCxUuLuHWy Ftjk7obIxbRlkLa2228bPP7oXSq+Uwt5pgRcgSaecX4wFMwwSgAOsDUrCRb5lB8QNder y5KypVCh+JBtdv2UV9OLWEFjnbsGIqub6OxwdpA9eYKalExuRKMA0YfEsg7cPgEtszfT bSfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758050488; x=1758655288; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=sUio7stinGKRW6y61bIqKpsizzIlKe0e9yztA7na5Ew=; b=RioxDFvd5ULzNn2K4YMEvborKURBpieSMRysvH3hTS5BCDJPkwThHJ+cLA1jWxnBOW F4Qt1Bi2ukAQlcxlzTHK72r5J2JCtpAHO2DwkwoRIf8LJPCUT5t5sZkXbg6rVqT1/aHn mlwYBR5l1O5g3jURhjYcJJz6Nx6AShcEuI1qOkdgRtA5DvmhSFGHbY7k1gsq4IvD93E3 rZ8C/DHS0812ekYFqkY6mchkCO+sadyG201jSTteMA+YXoFxviTsSNjWQKYfF73xGyTy 66nz1gDlCjz0ansHSBCHLY+y1bjcB3z+6dM7IKyj38J0ncBiF6Iy5P/Glxnv0yVuK6Mh OtFA== X-Gm-Message-State: AOJu0YxbLa+LDCOicHrhlO9YqnPTuc2zyDA4e0DifEjxHqAdA9zEgSif MAmXnSsdQXofZAGGqllUOCkSVidJEqS2bCVSBmXT2fwXKZRzcL/Qt0EHvhPouMm2 X-Gm-Gg: ASbGncs6BrJJouKEw/BFld7lWxYcdzE+CBKfuE457b+o647wCXE1qNRgwqRoTgQWgXm zXLA5S7gtQwAQ68eyKfdbCaQCsn/g+JhGHO7dJK37J++gkTrBSg0SkTP6I84EQmOrvcwa7+G9rF QhT7BBza6g5rUaIoTXpFamfEsavS1CeIKzg7W6S3OPW6cVl1GYKA87bZBmuAZV3YUEZeMm2z8ly aPIxaveC0VeIR6umwmVNbd4yoaRB/dmfEyQMtHcepqfmsQ11gA/J/aMpsOaHAkvIUTBu7v/ManW tIpBOZ/O5Cvfx61h6WaO/14PymHJdJHaQIrGqcSn04Ubmqc8F9QoP4PzNzFEf2vIKr5TwEJQBMl ikWq/Xy0tOkA5vRJWjIcnEfLygyA= X-Google-Smtp-Source: AGHT+IFEEcjBFildrg5AINVAevIoMcOgUAvgeUxJMPLFlqilKKcTkxD+v3pXMVvYxGPasD43ovT7sA== X-Received: by 2002:a05:6830:267:b0:74b:7c40:3580 with SMTP id 46e09a7af769-75354df3a40mr6973572a34.20.1758050487977; Tue, 16 Sep 2025 12:21:27 -0700 (PDT) Received: from fedora ([189.215.165.139]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7524be79747sm4306198a34.30.2025.09.16.12.21.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 16 Sep 2025 12:21:27 -0700 (PDT) From: =?utf-8?Q?Elijah_Gabe_P=C3=A9rez?= To: Eli Zaretskii Subject: Re: bug#79294: [PATCH] Add hideable indicators for hideshow. In-Reply-To: <86zfaumy7r.fsf@gnu.org> References: <87a53q3abi.fsf@gmail.com> <868qjacvru.fsf@gnu.org> <87ikidcncb.fsf@gmail.com> <867byt8bdh.fsf@gnu.org> <86ecskgg4i.fsf@gnu.org> <878qik9vgp.fsf@gmail.com> <86ecsar6fp.fsf@gnu.org> <87tt1432np.fsf@gmail.com> <867bxzoql4.fsf@gnu.org> <87y0qflb5c.fsf@gmail.com> <86zfaumy7r.fsf@gnu.org> Date: Tue, 16 Sep 2025 13:21:17 -0600 Message-ID: <87qzw6mceq.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/31.0.50 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 0.3 (/) X-Debbugs-Envelope-To: 79294 Cc: 79294@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Elijah Gabe P=C3=A9rez >> Cc: 79294@debbugs.gnu.org >> Date: Mon, 15 Sep 2025 14:21:35 -0600 >>=20 >> > Maybe it's a bug? Can you show a simple Lisp program that I could >> > evaluate in *scratch* and use to look into this issue? >>=20 >> Sure, here is a recipe: >>=20 >> 1. M-x fundamental-mode >>=20 >> 2. M-: >> (insert >> " " >> (propertize >> "x" >> 'display '(left-fringe >> left-arrow >> error) >> 'keymap (let ((map (make-sparse-keymap))) >> (define-key map (kbd " ") >> (lambda () >> (interactive) >> (print "message!"))) >> map))) >>=20 >> 3. Click on the fringe icon, it should not work an throw the >> " is undefined" message. > > I don't understand: you set the 'keymap' property on a character 'x', > and expect a click on the fringe to somehow magically use that keymap? > That's not how this works: the 'keymap' property defines the keymap to > use when clicking on the character with the property. I see, thanks for the explanation. >> I'm not sure if this is a known bug, but I've seen packages that locally >> (in the buffer local map) bind mouse-1 to a command that scans for the >> overlay in some region of the buffer, probably the fringe keymap doesn't >> support this yet. > > That's exactly what you should do. See, for example, > gdb-mouse-set-clear-breakpoint in gdb-mi.el. > >> For the patch, I have already solved this problem, although I am not >> happy with the solution, it is possibly the only way to solve this: > > That is the right solution. Fine, then i think the patch is now ready to merge (if there are no objections), I'm resending it since i found a bug. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Add-hideable-indicators-for-hideshow.-Bug-79294.patch Content-Transfer-Encoding: quoted-printable Content-Description: Fixed patch >From 675827fb213cb6c0bbb5cd3c8b0a75af8cf34459 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?El=3DC3=3DADas=3D20Gabriel=3D20P=3DC3=3DA9rez?=3D Date: Sun, 17 Aug 2025 20:02:59 -0600 Subject: [PATCH] Add hideable indicators for hideshow. (Bug#79294) * doc/emacs/programs.texi (Hideshow): Update documentation. * etc/NEWS: Announce changes. * lisp/progmodes/hideshow.el (hs-show-indicators) (hs-indicator-type, hs-indicator-maximum-buffer-size): New user options. (hs-indicator-hide, hs-indicator-show): New icons. (hs-block-positions, hs--make-indicators-overlays) (hs-indicator-mouse-toggle-hidding, hs--add-indicators) (hs--refresh-indicators): New functions. (hs-hide-block-at-point): Use hs-block-positions. (hs-inside-comment-p): Use 'get-char-property' instead of 'hs-overlay-at'. (hs-minor-mode): Rework. --- doc/emacs/programs.texi | 23 +++ etc/NEWS | 32 ++++- lisp/progmodes/hideshow.el | 281 +++++++++++++++++++++++++++++++++---- 3 files changed, 305 insertions(+), 31 deletions(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index f00d79f499a..2000bc2b785 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1730,6 +1730,9 @@ Hideshow =20 @vindex hs-hide-comments-when-hiding-all @vindex hs-display-lines-hidden +@vindex hs-show-indicators +@vindex hs-indicator-type +@vindex hs-indicator-maximum-buffer-size @vindex hs-isearch-open @vindex hs-special-modes-alist These variables can be used to customize Hideshow mode: @@ -1743,6 +1746,26 @@ Hideshow If non-@code{nil}, display the number of hidden lines next to the ellipsis. =20 +@item hs-show-indicators +This variable controls whether Hideshow mode should display indicators +of hidden and shown blocks. The indicators also allow toggling the +hide/show state of each block. If the value is non-@code{nil}, enables +the indicators in all blocks. The default is @code{nil}, which disables +the indicators. + +@item hs-indicactor-type +This variable controls where to show the indicators, if they are +enabled. You can show them on the fringe (@pxref{Windows Fringes}) or +in the window's margin. The default is to use the fringe if it's +available, otherwise to use the margin. + +@item hs-indicator-maximum-buffer-size +This variable limits the display of hideshow indicators to buffers that +are not too large. (Larger buffers might adversely affect redisplay +performance.) By default, buffers larger than 2MB have the indicators +disabled; the value of @code{nil} will activate the indicators +regardless of the buffer size. + @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/etc/NEWS b/etc/NEWS index 27e3f1e4ce0..ee02f732795 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -955,9 +955,37 @@ at point to explore. =20 ** Hideshow =20 ++++ *** New user option 'hs-display-lines-hidden'. -If this option is non-nil, Hideshow displays the number of hidden -lines next to the ellipsis. +If this option is non-nil, Hideshow displays the number of hidden lines +next to the ellipsis. By default this is disabled. + ++++ +*** New user option 'hs-show-indicators'. +This user option determines if hideshow should display indicators to +toggle the block hiding. If non-nil, the indicators are enabled in all +blocks. + +By default this is disabled. + +*** New user option 'hs-indicator-maximum-buffer-size'. +This user option limits the display of hideshow indicators to buffers +that are not too large. By default, buffers larger than 2MB have the +indicators disabled; the value of nil will activate the indicators +regardless of the buffer size. + ++++ +*** New user option 'hs-indicator-type'. +This user option determine which indicator type should be used for the +block indicators. + +The possible values can be: 'fringe', display the indicators in the +fringe (the default); 'margin', display the indicators in the margin; +nil, display the indicators at end-of-line. + +The new icons 'hs-indicator-show' and 'hs-indicator-hide', can be used +for customize the indicators appearance, only if 'hs-indicator-type' is +set to 'margin' or nil. =20 ** C-ts mode =20 diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 445bdeeb7a7..d270b5677e3 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -220,6 +220,9 @@ =20 ;;; Code: (require 'mule-util) ; For `truncate-string-ellipsis' +;; For indicators +(require 'icons) +(require 'fringe) =20 ;;------------------------------------------------------------------------= --- ;; user-configurable variables @@ -236,6 +239,16 @@ hs-ellipsis use that face for the ellipsis instead." :version "31.1") =20 +(defface hs-indicator-hide + '((t :inherit (shadow default))) + "Face used in hideshow indicator to indicate a hidden block." + :version "31.1") + +(defface hs-indicator-show + '((t :inherit hs-indicator-hide :weight bold)) + "Face used in hideshow indicator to indicate a shown block." + :version "31.1") + (defcustom hs-hide-comments-when-hiding-all t "Hide the comments too when you do an `hs-hide-all'." :type 'boolean) @@ -265,6 +278,83 @@ hs-isearch-open (const :tag "open both code and comment blocks" t) (const :tag "don't open any of them" nil))) =20 +(defcustom hs-show-indicators nil + "Whether hideshow should display block hide/show indicators. +If non-nil, hideshow will display indicators for toggling the visibility +of code blocks. + +The indicators appearance are specified in `hs-indicator-type' (which see)= ." + :type 'boolean + :version "31.1") + +(defcustom hs-indicator-type 'fringe + "Indicate which indicator type to use for the block indicators. + +The possible values can be: + + - `fringe', display the indicators in the fringe. + - `margin', display the indicators in the margin. + - nil, display the indicators at end-of-line. + +This only have effect if `hs-show-indicators' is non-nil." + :type '(choice + (const :tag "Fringes" fringe) + (const :tag "Margins" margin) + (const :tag "Indicator at end-of-line" nil)) + :version "31.1") + +(defcustom hs-indicator-maximum-buffer-size 2000000 ;2mb + "Max buffer size in bytes where the indicators should be enabled. +If current buffer is larger than this variable value, the indicators +will be disabled. + +If set to nil, the indicators will be activated regardless of the buffer +size." + :type '(choice natnum (const :tag "No limit" nil)) + :version "31.1") + +(define-fringe-bitmap + 'hs-hide + [#b0000000 + #b1000001 + #b1100011 + #b0110110 + #b0011100 + #b0001000 + #b0000000]) + +(define-fringe-bitmap + 'hs-show + [#b0110000 + #b0011000 + #b0001100 + #b0000110 + #b0001100 + #b0011000 + #b0110000]) + +(define-icon hs-indicator-hide nil + `((image "outline-open.svg" "outline-open.pbm" + :face hs-indicator-hide + :height (0.6 . em) + :ascent center) + (symbol "=F0=9F=9E=83" "=E2=96=BC" :face hs-indicator-hide) + (text "-" :face hs-indicator-hide)) + "Icon used for hide block at point. +This is only used if `hs-indicator-type' is set to `margin' or nil." + :version "31.1") + +(define-icon hs-indicator-show nil + `((image "outline-close.svg" "outline-close.pbm" + :face hs-indicator-show + :height (0.6 . em) + :ascent center) + (symbol "=E2=96=B8" "=E2=96=B6" :face hs-indicator-show) + (text "+" :face hs-indicator-show)) + "Icon used for show block at point. +This is only used if `hs-indicator-type' is set to `margin' or nil." + :version "31.1") + ;;;###autoload (defvar hs-special-modes-alist ;; FIXME: Currently the check is made via @@ -378,7 +468,13 @@ hs-minor-mode-map "C-c @ C-t" #'hs-hide-all "C-c @ C-d" #'hs-hide-block "C-c @ C-e" #'hs-toggle-hiding - "S-" #'hs-toggle-hiding) + "S-" #'hs-toggle-hiding + " " #'hs-indicator-mouse-toggle-hidding) + +(defvar-keymap hs-indicators-map + :doc "Keymap for hideshow indicators." + " " #'hs-indicator-mouse-toggle-hidding + "" #'hs-toggle-hiding) =20 (easy-menu-define hs-minor-mode-menu hs-minor-mode-map "Menu used when hideshow minor mode is active." @@ -562,6 +658,116 @@ hs-make-overlay (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) ov)) =20 +(defun hs-block-positions () + "Return the current code block positions. +This return a cons-cell with the current code block beginning and end +positions. This does nothing if there is not a code block at current +point." + (save-match-data + (save-excursion + (when (funcall hs-looking-at-block-start-p-func) + (let ((mdata (match-data t)) + (header-end (match-end 0)) + block-beg block-end) + ;; `block-start' is the point at the end of the block + ;; beginning, which may need to be adjusted + (save-excursion + (goto-char (funcall (or hs-adjust-block-beginning #'identity) + header-end)) + (setq block-beg (line-end-position))) + ;; `block-end' is the point at the end of the block + (hs-forward-sexp mdata 1) + (setq block-end + (cond ((and (stringp hs-block-end-regexp) + (looking-back hs-block-end-regexp nil)) + (match-beginning 0)) + ((functionp hs-block-end-regexp) + (funcall hs-block-end-regexp) + (match-beginning 0)) + (t (point)))) + (cons block-beg block-end)))))) + +(defun hs--make-indicators-overlays (beg) + "Helper function to make the indicators overlays." + (let ((hiddenp (eq 'hs (get-char-property (pos-eol) 'invisible)))) + (when-let* ((o (make-overlay + (if hs-indicator-type beg (pos-eol)) + (1+ (if hs-indicator-type beg (pos-eol))))) + (fringe-type (if hiddenp 'hs-show 'hs-hide)) + (face-or-icon (if hiddenp 'hs-indicator-show 'hs-indicator= -hide))) + + (overlay-put o 'hs-indicator t) + (overlay-put o 'hs-indicator-block-start beg) + (overlay-put o 'evaporate t) + (overlay-put o 'priority -50) + + (overlay-put + o 'before-string + (pcase hs-indicator-type + ;; Fringes + ('fringe + (propertize + "+" 'display + `(left-fringe ,fringe-type ,face-or-icon))) + ;; Margins + ('margin + (propertize + "+" 'display + `((margin left-margin) + ,(or (plist-get (icon-elements face-or-icon) 'image) + (icon-string face-or-icon))) + 'face face-or-icon + 'keymap hs-indicators-map)) + ;; EOL string + ('nil + (propertize + (icon-string face-or-icon) + 'mouse-face 'highlight + 'keymap hs-indicators-map))))))) + +(defun hs--add-indicators (&optional beg end) + "Add hideable indicators from BEG to END." + (save-excursion + (setq beg (if (null beg) (window-start) (goto-char beg) (pos-bol)) + end (if (null end) (window-end) (goto-char end) (pos-bol)))) + (goto-char beg) + (remove-overlays beg end 'hs-indicator t) + + (while (funcall hs-find-next-block-func hs-block-start-regexp end nil) + (when-let* ((b-beg (match-beginning 0)) + (_ (save-excursion + (goto-char b-beg) + (funcall hs-looking-at-block-start-p-func))) + ;; `catch' is used here if the search fail due + ;; unbalanced parenthesis or any other unknown error + ;; caused in `hs-forward-sexp'. + (b-end (catch 'hs-indicator-error + (save-excursion + (goto-char b-beg) + (condition-case _ + (funcall hs-forward-sexp-func 1) + (scan-error (throw 'hs-indicator-error nil))) + (point)))) + ;; Check if block is longer than 1 line. + (_ (< b-beg b-end)) + ;; If we are going to use the EOL indicators, then + ;; ignore the invisible lines which mostly are already + ;; hidden blocks. + (_ (> (count-lines b-beg b-end (not hs-indicator-type)) 1)= )) + (hs--make-indicators-overlays b-beg)) + ;; Only 1 indicator per line + (forward-line 1)) + `(jit-lock-bounds ,beg . ,end)) + +(defun hs--refresh-indicators () + "Update indicators appearance at current block." + (when hs-show-indicators + (save-match-data + (save-excursion + ;; Using window-start and window-end is more faster + ;; than computing again the block positions + (hs--add-indicators (window-start) (window-end)))))) + (defun hs--get-ellipsis (b e) "Helper function for `hs-make-overlay'. This returns the ellipsis string to use and its face." @@ -669,32 +875,19 @@ hs-hide-block-at-point and then further adjusted to be at the end of the line." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) - (when (funcall hs-looking-at-block-start-p-func) - (let ((mdata (match-data t)) - (header-end (match-end 0)) - p q ov) - ;; `p' is the point at the end of the block beginning, which - ;; may need to be adjusted - (save-excursion - (goto-char (funcall (or hs-adjust-block-beginning #'identity) - header-end)) - (setq p (line-end-position))) - ;; `q' is the point at the end of the block - (hs-forward-sexp mdata 1) - (setq q (cond ((and (stringp hs-block-end-regexp) - (looking-back hs-block-end-regexp nil)) - (match-beginning 0)) - ((functionp hs-block-end-regexp) - (funcall hs-block-end-regexp) - (match-beginning 0)) - (t (point)))) - (when (and (< p q) (> (count-lines p q) 1)) - (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) - (delete-overlay ov)) - ((not hs-allow-nesting) - (hs-discard-overlays p q))) - (hs-make-overlay p q 'code (- header-end p))) - (goto-char (if end q (min p header-end))))))) + (let* ((block (hs-block-positions)) + (p (car-safe block)) + (q (cdr-safe block)) + ov) + (if (and block (< p q) (> (count-lines p q) 1)) + (progn + (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) + (delete-overlay ov)) + ((not hs-allow-nesting) + (hs-discard-overlays p q))) + (goto-char q) + (hs-make-overlay p q 'code (- (match-end 0) p))) + (goto-char (if end q (min p (match-end 0)))))))) =20 (defun hs-inside-comment-p () "Return non-nil if point is inside a comment, otherwise nil. @@ -885,7 +1078,7 @@ hs-already-hidden-p (beginning-of-line) (hs-find-block-beginning-match))))) (end-of-line) - (hs-overlay-at (point)))) + (eq 'hs (get-char-property (point) 'invisible)))) =20 ;; This function is not used anymore (Bug#700). (defun hs-c-like-adjust-block-beginning (initial) @@ -976,6 +1169,7 @@ hs-hide-block (funcall hs-looking-at-block-start-p-func) (funcall hs-find-block-beginning-func)) (hs-hide-block-at-point end c-reg) + (hs--refresh-indicators) (run-hooks 'hs-hide-hook)))))) =20 (defun hs-show-block (&optional end) @@ -1011,6 +1205,7 @@ hs-show-block (when (and p q) (hs-discard-overlays p q) (goto-char (if end q (1+ p)))))) + (hs--refresh-indicators) (run-hooks 'hs-show-hook))) =20 (defun hs-hide-level (arg) @@ -1037,6 +1232,21 @@ hs-toggle-hiding =20 (define-obsolete-function-alias 'hs-mouse-toggle-hiding #'hs-toggle-hiding= "27.1") =20 +(defun hs-indicator-mouse-toggle-hidding (event) + "Toggle block hidding with indicators." + (interactive "e") + (when hs-show-indicators + (let* ((overlays (save-excursion + (goto-char (posn-point (event-end event))) + (overlays-in (pos-bol) (pos-eol)))) + (pos (catch 'hs--indicator-ov + (dolist (ov overlays) + (when-let* ((ov (overlay-get ov 'hs-indicator-block-st= art))) + (throw 'hs--indicator-ov ov)))))) + (when pos + (goto-char pos) + (hs-toggle-hiding))))) + (defun hs-hide-initial-comment-block () "Hide the first block of comments in a file. This can be useful if you have huge RCS logs in those comments." @@ -1086,8 +1296,21 @@ hs-minor-mode #'turn-off-hideshow nil t) (setq-local line-move-ignore-invisible t) - (add-to-invisibility-spec '(hs . t))) + (add-to-invisibility-spec '(hs . t)) + ;; Add block indicators + (when (and hs-show-indicators + (or (and (integerp hs-indicator-maximum-buffer-size) + (< (buffer-size) hs-indicator-maximum-buffer-s= ize)) + (not hs-indicator-maximum-buffer-size))) + (when (and (not (display-graphic-p)) + (eq hs-indicator-type 'fringe)) + (setq-local hs-indicator-type 'margin)) + (jit-lock-register #'hs--add-indicators))) + (remove-from-invisibility-spec '(hs . t)) + (when hs-show-indicators + (jit-lock-unregister #'hs--add-indicators) + (remove-overlays nil nil 'hs-indicator t)) ;; hs-show-all does nothing unless h-m-m is non-nil. (let ((hs-minor-mode t)) (hs-show-all)))) --=20 2.51.0 --=-=-= Content-Type: text/plain -- - E.G via Gnus and Org. --=-=-=--