Subject: [PATCH] Improve foldout-exit-fold behavior with negative argument

From: "Paul D. Nelson" <[email-masked]>
Date: Sat, 29 Mar 2025 22:06:40 +0100

The foldout-* commands, available in outline-minor-mode, give a way to
zoom in and out of "folds", which are subtrees defined by the outline
structure.  To follow along with this email, start by doing
foldout-zoom-subtree (C-c @ C-z) on a subtree (e.g., an elisp defun).

By default, foldout-exit-fold (C-c @ C-x) exits one fold, hides the text
that was in that fold (outline-hide-subtree), moves point to the
heading, and recenters.  When invoked with a negative prefix argument
like C-- C-c @ C-x, the command instead

 (1) exits one fold without hiding the text,

 (2) moves point to the bottom of the fold, and

 (3) recenters.

I think that better behavior would be to skip steps (2) and (3), so that
the point stays put and the window view does not change.

[Step (2) seems incidental rather than by design, so "skip" really means
"counter".]

Advantages of omitting (2) are:

- We don't lose our place when zooming out.

- If we zoom in and zoom out (C-c @ C-z, C-- C-c @ C-x), then point
  doesn't move.

- Consistency with other outline/foldout commands that preserve "point
  being on a heading".

Regarding (3), recentering helps us reorient when hiding an exited fold,
but is disruptive when the text remains visible.

We retain the flexibility to imitate the old behavior using M-> and C-l.

The change could be guarded by a defcustom, but it seems to me like a
clear improvement.  I'd be happy to learn if I missed some scenario
where it is not.

Any feedback would be welcome.

>From a384ca50aeb65c976df6e536d54f96acf1d143d2 Mon Sep 17 00:00:00 2001
From: Paul Nelson <[email-masked]>
Date: Sat, 29 Mar 2025 19:07:13 +0100
Subject: [PATCH] Improve foldout-exit-fold with negative arg

* lisp/foldout.el (foldout-exit-fold): When called with a
negative prefix argument (so that the exited fold is not
hidden), preserve the position of point and do not recenter.
---
 etc/NEWS        | 5 +++++
 lisp/foldout.el | 9 ++++++---
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 1bd2fd6d486..7a3419ee59a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1367,6 +1367,11 @@ string instead of preprending it and 'tmm-mid-prompt' to said entry.
 
 ** Foldout
 
+*** Improved behavior of 'foldout-exit-fold' with negative prefix argument.
+When 'foldout-exit-fold' is called with a negative argument (so that the
+exited fold remains visible), the position of point and window view are
+preserved.
+
 ---
 *** New command 'foldout-widen-to-current-fold'.
 This command widens the view to the current fold level when in a fold,
diff --git a/lisp/foldout.el b/lisp/foldout.el
index fcc5e294561..748f56c0c9c 100644
--- a/lisp/foldout.el
+++ b/lisp/foldout.el
@@ -290,10 +290,10 @@ foldout-exit-fold
   "Return to the ARG'th enclosing fold view.  With ARG = 0 exit all folds.
 
 Normally causes exited folds to be hidden, but with ARG < 0, -ARG folds are
-exited and text is left visible."
+exited, text is left visible, and point position is preserved."
   (interactive "p")
   (let ((hide-fold t) start-marker end-marker
-	beginning-of-heading end-of-subtree)
+        beginning-of-heading end-of-subtree (original-point (point)))
 
     ;; check there are some folds to leave
     (if (null foldout-fold-list)
@@ -369,7 +369,10 @@ foldout-exit-fold
 			    (if end-marker
 				(1- (marker-position end-marker))
 			      (point-max)))))
-    (recenter)
+
+    (if hide-fold
+        (recenter)
+      (goto-char original-point))
 
     ;; update the mode line
     (foldout-update-mode-line)))
-- 
2.39.3 (Apple Git-145)