Subject: Re: bug#41198: 28.0.50; heading cycling command for outline

From: "Paul W. Rankin" <[email-masked]>
Date: Tue, 17 Nov 2020 12:47:33 +1000

On 2020-11-17 07:59, Lars Ingebrigtsen wrote:
> 
> The patches look OK to me, but look like they've been slightly mangled
> during transmission, so could you include them as a single patch as an
> attachment?  (And adding a ChangeLog-format text would also be nice.)

2x patches attached with changelog.

I took a more precise approach with outline--cycle-state, where the +1 
case is only handled when the subtree end is point-max, otherwise the 
overlay-end must equal end of subtree point in all other cases.

I rewrote outline-cycle-buffer; it now first checks that the buffer has 
top-level headings before attempting to show only top-level headings, 
thus avoiding the disconcerting state where all buffer content is 
reduced to "...". If buffer has no outline headings, we still get an 
annoying message toggle from "All headings" to "Show all".

Also this avoids using pcase, which is awful.

From c589e4055aad93aceefe0ebb3281c008c82a0b46 Mon Sep 17 00:00:00 2001
From: "Paul W. Rankin" <[email-masked]>
Date: Tue, 17 Nov 2020 12:42:47 +1000
Subject: [PATCH 1/2] * lisp/outline.el: better handing of outline overlays
 when cycling

(outline--cycle-state): only consider outline overlays that are on
  outline headings; when subtree end is point-max, return overlay-end +1
  because final subtree overlay only reaches point-max -1.
---
 lisp/outline.el | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/lisp/outline.el b/lisp/outline.el
index 47e6528859..8cc8a3cd89 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -1121,14 +1121,19 @@ outline--cycle-state
       (setq heading-end (point))
       (outline-end-of-subtree)
       (setq end (point))
-      (setq ov-list (cl-remove-if-not
-                     (lambda (o) (eq (overlay-get o 'invisible) 'outline))
-                     (overlays-in start end)))
-      (cond ((eq ov-list nil) 'show-all)
-            ;; (eq (length ov-list) 1) wouldn’t work: what if there is
-            ;; one folded subheading?
-            ((and (eq (overlay-end (car ov-list)) end)
-                  (eq (overlay-start (car ov-list)) heading-end))
+      (setq ov-list
+            (seq-filter
+             (lambda (o)
+               (and (eq (overlay-get o 'invisible) 'outline)
+                    (save-excursion
+                      (goto-char (overlay-start o))
+                      (outline-on-heading-p t))))
+             (overlays-in start end)))
+      (cond ((null ov-list) 'show-all)
+            ((and (or (= end (point-max)
+                         (1+ (overlay-end (car ov-list))))
+                      (= (overlay-end (car ov-list)) end))
+                  (= (overlay-start (car ov-list)) heading-end))
              'hide-all)
             (t 'headings-only)))))
 
-- 
2.29.2

From 680210c64545389502e7a2d05ec0c98e7b25c24b Mon Sep 17 00:00:00 2001
From: "Paul W. Rankin" <[email-masked]>
Date: Tue, 17 Nov 2020 12:44:44 +1000
Subject: [PATCH 2/2] * lisp/outline.el: avoid hiding all buffer content

(outline-cycle-buffer): check that buffer has top-level headings
  before calling outline-hide-sublevels 1 thus preventing disconcerting
  buffer state of content reduced to single "..."
---
 lisp/outline.el | 38 ++++++++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 14 deletions(-)

diff --git a/lisp/outline.el b/lisp/outline.el
index 8cc8a3cd89..9b11b86b9d 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -1173,20 +1173,30 @@ outline--cycle-buffer-state
 (defun outline-cycle-buffer ()
   "Cycle the whole buffer like in `outline-cycle'."
   (interactive)
-  (pcase outline--cycle-buffer-state
-    ('show-all
-     (outline-hide-sublevels 1)
-     (setq outline--cycle-buffer-state 'top-level)
-     (message "Top level headings"))
-    ('top-level
-     (outline-show-all)
-     (outline-hide-region-body (point-min) (point-max))
-     (setq outline--cycle-buffer-state 'all-heading)
-     (message "All headings"))
-    ('all-heading
-     (outline-show-all)
-     (setq outline--cycle-buffer-state 'show-all)
-     (message "Show all"))))
+  (let (has-top-level)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (or has-top-level (eobp)))
+        (when (outline-on-heading-p t)
+          (when (= (funcall outline-level) 1)
+            (setq has-top-level t)))
+        (outline-next-heading)))
+    (cond
+     ((and (eq outline--cycle-buffer-state 'show-all)
+           has-top-level)
+      (outline-hide-sublevels 1)
+      (setq outline--cycle-buffer-state 'top-level)
+      (message "Top level headings"))
+     ((or (eq outline--cycle-buffer-state 'show-all)
+          (eq outline--cycle-buffer-state 'top-level))
+      (outline-show-all)
+      (outline-hide-region-body (point-min) (point-max))
+      (setq outline--cycle-buffer-state 'all-heading)
+      (message "All headings"))
+     (t
+      (outline-show-all)
+      (setq outline--cycle-buffer-state 'show-all)
+      (message "Show all")))))
 
 (provide 'outline)
 (provide 'noutline)
-- 
2.29.2