Subject: bug#68881: closed (Re: bug#68881: 30.0.50; [PATCH] Field properties confuse 'outline-minor-mode')

From: [email-masked] (GNU bug Tracking System)
Date: Tue, 13 Feb 2024 04:06:02 +0000

Your bug report

#68881: 30.0.50; [PATCH] Field properties confuse 'outline-minor-mode'

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
68881: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=68881
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#68881: 30.0.50; [PATCH] Field properties confuse 'outline-minor-mode'
From: Jim Porter <[email-masked]>
To: Juri Linkov <[email-masked]>
Cc: [email-masked]
Date: Mon, 12 Feb 2024 20:03:38 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

On 2/12/2024 10:25 AM, Juri Linkov wrote:
>>>> @@ -725,7 +725,7 @@ outline-insert-heading
>>>>    		(not (string-match (concat "\\`\\(?:" outline-regexp "\\)")
>>>>    				   (concat head " "))))
>>>>          (setq head (concat head " ")))
>>>> -    (unless (bolp) (end-of-line) (newline))
>>>> +    (unless (bolp) (goto-char (pos-bol)) (newline))
>>> This looks like a typo.
>>
>> So it is. Fixed.
> 
> Thanks.  I see no more problems.

Thanks. Merged as d570864bebf, so closing this now.

(I'll file a new bug later for adding outline-minor-mode support to 
Eshell once I've worked out all the remaining issues.)

Subject: 30.0.50; [PATCH] Field properties confuse 'outline-minor-mode'
From: Jim Porter <[email-masked]>
To: [email-masked]
Date: Thu, 1 Feb 2024 15:51:57 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

I'd like to add support for 'outline-minor-mode' in Eshell. However, 
Eshell's use of field properties confuses outline.el. Attached is a WIP 
patch + demo code for Eshell that should resolve this.

The main issue was that outline.el uses 'line-beginning-position' and 
friends, which respects field boundaries, but I think we want to avoid 
that for 'outline-minor-mode'. Maybe we could use 'pos-bol' and friends 
instead, but my understanding is that 'line-beginning-position' respects 
display directionality (which we probably want), but 'pos-bol' doesn't.

You can try things out here by starting Eshell and activating 
'outline-minor-mode'. You can also see the problems by applying only the 
Eshell part of the patch.

Any thoughts? Is this the right way to go about this? (Note: I think the 
Eshell side of things will take more work, which I'll address in a later 
bug. However, this should be enough to show off the problems on the 
outline.el side.)

From fad64a23e38fbc43cee121db78f00e255df3ecd8 Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Feb 2024 13:58:20 -0800
Subject: [PATCH] [WIP] Make outline.el able to handle buffers with field
 properties

* lisp/outline.el (outline-back-to-heading, outline-on-heading-p)
(outline-next-visible-heading, outline-mark-subtree)
(outline-hide-sublevels, outline--insert-button)
(outline--fix-up-all-buttons): Inhibit field text motion.

* lisp/eshell/em-prompt.el (eshell-outline-search): New function (for
testing only).
(eshell-prompt-initialize): Initialize outline-mode variables.
---
 lisp/eshell/em-prompt.el | 14 +++++++++++++-
 lisp/outline.el          | 32 +++++++++++++++++++++-----------
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el
index 3662c1fa895..bf3a24689f8 100644
--- a/lisp/eshell/em-prompt.el
+++ b/lisp/eshell/em-prompt.el
@@ -107,6 +107,14 @@ eshell-prompt-repeat-map
 
 ;;; Functions:
 
+(defun eshell-outline-search (&optional bound move backward looking-at)
+  "Search for outline headings.  See `outline-search-function'."
+  ;; FIXME: This probably isn't the right way to do things.  Does it
+  ;; work with multiline prompts?  Still, it's useful for a proof of
+  ;; concept.
+  (outline-search-text-property 'field 'prompt bound move backward
+                                looking-at))
+
 (define-minor-mode eshell-prompt-mode
   "Minor mode for eshell-prompt module.
 
@@ -117,7 +125,11 @@ eshell-prompt-initialize
   "Initialize the prompting code."
   (unless eshell-non-interactive-p
     (add-hook 'eshell-post-command-hook 'eshell-emit-prompt nil t)
-    (eshell-prompt-mode)))
+    (eshell-prompt-mode)
+
+    (setq-local outline-search-function #'eshell-outline-search
+                outline-level (lambda () 1)
+                outline-minor-mode-use-buttons 'in-margins)))
 
 (defun eshell-emit-prompt ()
   "Emit a prompt if eshell is being used interactively."
diff --git a/lisp/outline.el b/lisp/outline.el
index b50708c1a7b..e7e8d269640 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -686,7 +686,8 @@ 'outline-before-first-heading
 (defun outline-back-to-heading (&optional invisible-ok)
   "Move to previous heading line, or beg of this line if it's a heading.
 Only visible heading lines are considered, unless INVISIBLE-OK is non-nil."
-  (beginning-of-line)
+  (let ((inhibit-field-text-motion t))
+    (beginning-of-line))
   (or (outline-on-heading-p invisible-ok)
       (let (found)
 	(save-excursion
@@ -705,7 +706,8 @@ outline-on-heading-p
   "Return t if point is on a (visible) heading line.
 If INVISIBLE-OK is non-nil, an invisible heading line is ok too."
   (save-excursion
-    (beginning-of-line)
+    (let ((inhibit-field-text-motion t))
+      (beginning-of-line))
     (and (bolp) (or invisible-ok (not (outline-invisible-p)))
 	 (if outline-search-function
              (funcall outline-search-function nil nil nil t)
@@ -941,9 +943,10 @@ outline-next-visible-heading
 A heading line is one that starts with a `*' (or that
 `outline-regexp' matches)."
   (interactive "p")
-  (if (< arg 0)
-      (beginning-of-line)
-    (end-of-line))
+  (let ((inhibit-field-text-motion t))
+    (if (< arg 0)
+        (beginning-of-line)
+      (end-of-line)))
   (let ((regexp (unless outline-search-function
                   (concat "^\\(?:" outline-regexp "\\)")))
         found-heading-p)
@@ -963,7 +966,9 @@ outline-next-visible-heading
                           (re-search-forward regexp nil 'move)))
 		  (outline-invisible-p (match-beginning 0))))
       (setq arg (1- arg)))
-    (if found-heading-p (beginning-of-line))))
+    (if found-heading-p
+        (let ((inhibit-field-text-motion t))
+          (beginning-of-line)))))
 
 (defun outline-previous-visible-heading (arg)
   "Move to the previous heading line.
@@ -980,7 +985,8 @@ outline-mark-subtree
   (let ((beg))
     (if (outline-on-heading-p)
 	;; we are already looking at a heading
-	(beginning-of-line)
+        (let ((inhibit-field-text-motion t))
+          (beginning-of-line))
       ;; else go back to previous heading
       (outline-previous-visible-heading 1))
     (setq beg (point))
@@ -1183,7 +1189,8 @@ outline-hide-sublevels
 		(cond
 		 (current-prefix-arg (prefix-numeric-value current-prefix-arg))
 		 ((save-excursion
-                    (beginning-of-line)
+                    (let ((inhibit-field-text-motion t))
+                      (beginning-of-line))
 		    (if outline-search-function
                         (funcall outline-search-function nil nil nil t)
                       (looking-at outline-regexp)))
@@ -1834,13 +1841,15 @@ outline--create-button-icons
 (defun outline--insert-button (type)
   (with-silent-modifications
     (save-excursion
-      (beginning-of-line)
+      (let ((inhibit-field-text-motion t))
+        (beginning-of-line))
       (let ((icon (nth (if (eq type 'close) 1 0) outline--button-icons))
             (o (seq-find (lambda (o) (overlay-get o 'outline-button))
                          (overlays-at (point)))))
         (unless o
           (when (eq outline-minor-mode-use-buttons 'insert)
-            (let ((inhibit-read-only t))
+            (let ((inhibit-read-only t)
+                  (inhibit-field-text-motion t))
               (insert (apply #'propertize "  " (text-properties-at (point))))
               (beginning-of-line)))
           (setq o (make-overlay (point) (1+ (point))))
@@ -1866,7 +1875,8 @@ outline--fix-up-all-buttons
     (when from
       (save-excursion
         (goto-char from)
-        (setq from (line-beginning-position))))
+        (let ((inhibit-field-text-motion t))
+          (setq from (line-beginning-position)))))
     (outline-map-region
      (lambda ()
        (let ((close-p (save-excursion
-- 
2.25.1