Package: emacs;
Reported by: James Cherti <contact <at> jamescherti.com>
Date: Thu, 21 Aug 2025 18:10:02 UTC
Severity: normal
View this message in rfc822 format
From: James Cherti <contact <at> jamescherti.com> To: Rahguzar <rahguzar <at> mailbox.org> Cc: 79286 <at> debbugs.gnu.org Subject: bug#79286: outline-mode: attempting to open folded sections does not work as expected inside a deeply nested header Date: Fri, 22 Aug 2025 10:47:15 -0400
Here is an improved version of my-outline-show-entry that I am currently using to address this issue in my configuration. I would greatly appreciate any comments or suggestions prior to finalizing the patch. --8<---------------cut here---------------start------------->8--- ;; Author James Cherti ;; GitHub: https://github.com/jamescherti (defun my-outline-legacy-show-entry () "Show the body directly following this heading. Show the heading too, if it is currently invisible. (This is the Emacs version of `outline-show-entry'.)" (interactive) (save-excursion (outline-back-to-heading t) (outline-flag-region (1- (point)) (progn (outline-next-preface) (if (= 1 (- (point-max) (point))) (point-max) (point))) nil))) (defun my-outline-heading-folded-p () "Return non-nil if the body following the current heading is folded." (save-excursion (end-of-line) (outline-invisible-p (point)))) (defun my-outline-show-entry () "Ensure the current heading and body are fully visible. Repeatedly reveal children and body until the entry is no longer folded." (interactive) (save-excursion ;; Repeatedly reveal children and body until the entry is no longer folded (while (my-outline-heading-folded-p) (save-excursion (outline-back-to-heading) (outline-show-children) (my-outline-legacy-show-entry))) ;; Final pass to guarantee the heading under the cursor and its body are ;; visible (my-outline-legacy-show-entry))) ;; Advice (advice-add 'outline-show-entry :override #'my-outline-show-entry) --8<---------------cut here---------------start------------->8--- -- James Cherti GitHub: https://github.com/jamescherti Website: https://www.jamescherti.com/ On 2025-08-21 15:09, James Cherti wrote: > On 2025-08-21 14:34, Rahguzar wrote: >> Hi James, > > Hi Rahguzar, > >> James Cherti <contact <at> jamescherti.com> writes: >> >>> When using outline-mode or outline-minor-mode, attempting to >>> open folded sections (outline-show-entry) does not work as >>> expected when the cursor is positioned inside a deeply >>> nested header. >>> >>> Steps to Reproduce: >>> ------------------- >>> 1. Open an outline-mode buffer with multiple >>> nested headers, for example: >>> >>> Level 1 >>> ** Level 2 >>> *** Level 3 >>> **** Level 4: A >>> Some content here >>> Some content here >>> Some content here >>> Some content here >>> **** Level 4: B >>> Some content here >>> Some content here >>> Some content here >>> Some content here >>> **** Level 4: C >>> Some content here >>> Some content here< PUT THE CURSOR HERE >>> Some content here >>> Some content here >>> **** Level 4: D >>> Some content here >>> Some content here >>> Some content here >>> Some content here >>> Some content here >>> **** Level 4: E >>> Some content here >>> Some content here >>> Some content here >>> >>> 2. Put the cursor before `< PUT THE CURSOR HERE` >>> >>> 3. Collapse all folds with: (hide-sublevels 1) >>> >>> 6. Show entry: (outline-show-entry) >>> >>> The folds fail to expand fully in nested hierarchies, leaving all >>> content hidden except for `level 4: C`. >>> >>> Screenshot: >>> https://github.com/user-attachments/ >>> assets/2ea6c3bd-0284-40a7-90e5-1619bdf3acc5 >>> >>> Expected Behavior: >>> ------------------ >>> The expected behavior is that only the heading at point (and >>> its associated content) should be expanded, while all >>> ancestor headings remain collapsed. (Similar to other editors.) >>> >>> Actual Behavior: >>> ---------------- >>> Only the heading at point expands, while other headings >>> remain invisible, which is confusing. In some cases, these >>> hidden headings cannot be revealed until the parent heading >>> is first collapsed and then expanded again. >> >> I have encountered this with isearch and was able to fix this >> with in my init file. >> >> ```emacs-lisp >> (defun +outline-reveal (pos) >> (let ((heading-pos nil)) >> (while (not (eq heading-pos (progn (outline-back-to-heading) >> (point)))) >> (setq heading-pos (point)) >> (outline-show-children) >> (goto-char pos)) >> (outline-show-entry) >> (goto-char pos))) >> >> (defun +outline-invsible-open (&optional _) >> (when (and outline-minor-mode >> (outline-invisible-p)) >> (+outline-reveal (point)))) >> >> (setq outline-isearch-open-invisible-function #'+outline-invsible- >> open) >> ``` >> With an interactive spec the function +outline-reveal can be used as an >> alternative to outline-show-entry without this problem but I can't >> imagine a scenario where that might be needed. >> > > Unfortunately, neither `+outline-reveal` nor `+outline-invisible-open` > resolves this issue. > > I wrote the following Elisp code to fix this issue: > --8<---------------cut here---------------start------------->8--- > ;; Author James Cherti > ;; GitHub: https://github.com/jamescherti > (defun my-outline-show-entry () > "Show the body directly following this heading. > Show the heading too, if it is currently invisible." > (interactive) > > ;; Show previous headers > (unless (derived-mode-p 'outline-indent-minor-mode) > (save-excursion > (let ((func-invisible-p > (cond > ((and (derived-mode-p 'org-mode) > (fboundp 'org-invisible-p)) > 'org-invisible-p) > > ((and (or (derived-mode-p 'outline-mode) > (derived-mode-p 'outline-minor-mode)) > (fboundp 'outline-invisible-p)) > 'outline-invisible-p) > > (t > 'invisible-p))) > (initial-point (point)) > (visual-point nil) > (visual-init-point nil)) > ;; Unfolding loop > (while (and > ;; Folded? > (save-excursion > (end-of-line) > (funcall func-invisible-p (point))) > ;; When the real point differs from the visual point, > it indicates > ;; that the cursor is still located within a hidden > header. > ;; > ;; This prevents infinite loops if the unfolding stops > changing > ;; the cursor position. > (or > (not visual-init-point) > (not visual-point) > (/= visual-init-point visual-point))) > > (save-excursion > (outline-back-to-heading) > (beginning-of-line) > (setq visual-point (point)) > (with-no-warnings > (show-children)) > > ;; Go back to point > (goto-char initial-point) > (outline-back-to-heading) > (beginning-of-line) > (setq visual-init-point (point)))) > > ;; Previous version of `outline-show-entry' > (save-excursion > (outline-back-to-heading t) > (outline-flag-region (1- (point)) > (progn > (outline-next-preface) > (if (= 1 (- (point-max) (point))) > (point-max) > (point))) > nil)))))) > > (advice-add 'outline-show-entry :override #'my-outline-show-entry) > --8<---------------cut here---------------start------------->8--- > > This could likely be simplified using built-in functions. > I welcome any suggestions for improvement. > >>> Environment: >>> ------------ >>> * Emacs version: 30.2 and master branch >>> (ade6608e2587452c8ea565ce3057879379ebd0b5) >>> >>> -- >>> James Cherti >>> GitHub: https://github.com/jamescherti >>> Website: https://www.jamescherti.com/ >> >> Rahguzar > > -- > James Cherti > GitHub: https://github.com/jamescherti > Website: https://www.jamescherti.com/ > > >
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.