GNU bug report logs - #79286
outline-mode: attempting to open folded sections does not work as expected inside a deeply nested header

Previous Next

Package: emacs;

Reported by: James Cherti <contact <at> jamescherti.com>

Date: Thu, 21 Aug 2025 18:10:02 UTC

Severity: normal

To reply to this bug, email your comments to 79286 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#79286; Package emacs. (Thu, 21 Aug 2025 18:10:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to James Cherti <contact <at> jamescherti.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Thu, 21 Aug 2025 18:10:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: James Cherti <contact <at> jamescherti.com>
To: bug-gnu-emacs <at> gnu.org
Subject: outline-mode: attempting to open folded sections does not work as
 expected inside a deeply nested header
Date: Thu, 21 Aug 2025 14:08:49 -0400
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:
--8<---------------cut here---------------start------------->8---
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
--8<---------------cut here---------------start------------->8---

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.

Environment:
------------
* Emacs version: 30.2 and master branch 
(ade6608e2587452c8ea565ce3057879379ebd0b5)

--
James Cherti
GitHub: https://github.com/jamescherti
Website: https://www.jamescherti.com/




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79286; Package emacs. (Thu, 21 Aug 2025 18:35:02 GMT) Full text and rfc822 format available.

Message #8 received at 79286 <at> debbugs.gnu.org (full text, mbox):

From: Rahguzar <rahguzar <at> mailbox.org>
To: James Cherti <contact <at> jamescherti.com>
Cc: 79286 <at> debbugs.gnu.org
Subject: Re: bug#79286: outline-mode: attempting to open folded sections
 does not work as expected inside a deeply nested header
Date: Thu, 21 Aug 2025 23:34:27 +0500
Hi James,

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.

> Environment:
> ------------
> * Emacs version: 30.2 and master branch 
> (ade6608e2587452c8ea565ce3057879379ebd0b5)
>
> --
> James Cherti
> GitHub: https://github.com/jamescherti
> Website: https://www.jamescherti.com/

Rahguzar




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79286; Package emacs. (Thu, 21 Aug 2025 19:10:02 GMT) Full text and rfc822 format available.

Message #11 received at 79286 <at> debbugs.gnu.org (full text, mbox):

From: James Cherti <contact <at> jamescherti.com>
To: Rahguzar <rahguzar <at> mailbox.org>
Cc: 79286 <at> debbugs.gnu.org
Subject: Re: bug#79286: outline-mode: attempting to open folded sections does
 not work as expected inside a deeply nested header
Date: Thu, 21 Aug 2025 15:09:19 -0400
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/




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79286; Package emacs. (Fri, 22 Aug 2025 14:48:02 GMT) Full text and rfc822 format available.

Message #14 received at 79286 <at> debbugs.gnu.org (full text, mbox):

From: James Cherti <contact <at> jamescherti.com>
To: Rahguzar <rahguzar <at> mailbox.org>
Cc: 79286 <at> debbugs.gnu.org
Subject: Re: 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/
> 
> 
> 





This bug report was last modified 29 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.