GNU bug report logs - #53981
28.0.91; shortdoc: Add support for outline-minor-mode

Previous Next

Package: emacs;

Reported by: Daniel Mendler <mail <at> daniel-mendler.de>

Date: Sun, 13 Feb 2022 22:40:02 UTC

Severity: wishlist

Found in version 28.0.91

Fixed in version 29.0.50

Done: Juri Linkov <juri <at> linkov.net>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 53981 in the body.
You can then email your comments to 53981 AT debbugs.gnu.org in the normal way.

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#53981; Package emacs. (Sun, 13 Feb 2022 22:40:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Daniel Mendler <mail <at> daniel-mendler.de>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sun, 13 Feb 2022 22:40:02 GMT) Full text and rfc822 format available.

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

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: bug-gnu-emacs <at> gnu.org
Subject: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Sun, 13 Feb 2022 23:39:11 +0100
The shortdoc buffer currently lacks support for the outline-minor-mode.
By setting the two variables outline-regexp and outline-level, we can
unlock this feature. Does it make sense to provide this by default?

(defun shortdoc--outline-level () (if (eq (char-after) ?\() 2 1)))
(add-hook 'shortdoc-mode-hook
          (lambda ()
            (setq-local outline-level #'shortdoc--outline-level
                        outline-regexp "[A-Z(]")))

In GNU Emacs 28.0.91 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.5,
cairo version 1.16.0)
 of 2022-02-09 built on projects
Repository revision: 82e74e4559b8becd44f3e7ac0134e2baddd69921
Repository branch: emacs-28
Windowing system distributor 'The X.Org Foundation', version 11.0.12004000
System Description: Debian GNU/Linux 10 (buster)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Mon, 14 Feb 2022 11:07:01 GMT) Full text and rfc822 format available.

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

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Mon, 14 Feb 2022 12:06:25 +0100
[Message part 1 (text/plain, inline)]
Daniel Mendler <mail <at> daniel-mendler.de> writes:

> The shortdoc buffer currently lacks support for the outline-minor-mode.
> By setting the two variables outline-regexp and outline-level, we can
> unlock this feature. Does it make sense to provide this by default?
>
> (defun shortdoc--outline-level () (if (eq (char-after) ?\() 2 1)))
> (add-hook 'shortdoc-mode-hook
>           (lambda ()
>             (setq-local outline-level #'shortdoc--outline-level
>                         outline-regexp "[A-Z(]")))

I tried the following:

diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 658edd6752..fd79cf5116 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -1435,7 +1435,10 @@ shortdoc-mode-map
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
-  :interactive nil)
+  :interactive nil
+  (setq-local outline-level (lambda ()
+                              (if (eq (char-after) ?\() 2 1))
+              outline-regexp "[A-Z(]"))
 
 (defun shortdoc--goto-section (arg sym &optional reverse)
   (unless (natnump arg)

But then hiding levels made the display pretty weird:

[Message part 2 (image/png, inline)]
[Message part 3 (text/plain, inline)]
So if we want that, it needs more work.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Mon, 14 Feb 2022 12:25:02 GMT) Full text and rfc822 format available.

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

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Mon, 14 Feb 2022 13:23:58 +0100
On 2/14/22 12:06, Lars Ingebrigtsen wrote:
> I tried the following:
> ...> But then hiding levels made the display pretty weird:
>
> So if we want that, it needs more work.

I see. This looks indeed not that good due to the newly introduced
`separator-line` face. I use a different setting for the
`separator-line` in my configuration, which has only a height of a
single pixel by using the `:underline` face attribute:

(set-face-attribute 'separator-line nil
  :inherit 'shadow :background nil :underline t :height 1)

With this setting, the separator line generally looks better and less
obtrusive. So we may want to consider using no background and
`:underline t` instead? With outline mode folding the separator line is
still visible, but doesn't disturb the visuals so severely.

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Mon, 14 Feb 2022 14:19:02 GMT) Full text and rfc822 format available.

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

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Mon, 14 Feb 2022 15:18:11 +0100
Daniel Mendler <mail <at> daniel-mendler.de> writes:

> With this setting, the separator line generally looks better and less
> obtrusive. So we may want to consider using no background and
> `:underline t` instead? With outline mode folding the separator line is
> still visible, but doesn't disturb the visuals so severely.

I think the separator line looks fine the way it is, so if we're to
enable outline-minor-mode in that buffer, the outlining will have to be
adjusted to take that into account.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Mon, 14 Feb 2022 14:31:02 GMT) Full text and rfc822 format available.

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

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Mon, 14 Feb 2022 15:29:58 +0100
On 2/14/22 15:18, Lars Ingebrigtsen wrote:
>> With this setting, the separator line generally looks better and less
>> obtrusive. So we may want to consider using no background and
>> `:underline t` instead? With outline mode folding the separator line is
>> still visible, but doesn't disturb the visuals so severely.
> 
> I think the separator line looks fine the way it is, so if we're to
> enable outline-minor-mode in that buffer, the outlining will have to be
> adjusted to take that into account.

If you want to keep the separator as is, I am not sure if we can solve
the outline display issue, since it is an artifact of the current
separator face. Maybe it would help to introduce an additional line of
small height after the separator line, such that an outline-folded block
will not end with the separator line?

I like to have good outline support since it also helps with commands
like my `consult-outline` and `counsel-outline`, which can be used to
browse and export a TOC. For me, there is more to outlines than folding
only.

But overall I don't feel strongly about this - it is a minor feature. I
can just keep outline support in my user config in combination with the
single pixel separator lines, which looks better. ;)

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Tue, 15 Feb 2022 07:33:02 GMT) Full text and rfc822 format available.

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

From: Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Tue, 15 Feb 2022 08:32:07 +0100
Lars Ingebrigtsen <larsi <at> gnus.org> writes:

> But then hiding levels made the display pretty weird:

Looking at your screenshot, this reminds me of the discussion we're
having on bug#52587?  See in particular the three screenshots in

<https://debbugs.gnu.org/cgi/bugreport.cgi?bug=52587#19>
<<87fsqnvl98.fsf <at> gmail.com>>

I.e. given this outline buffer:

<https://debbugs.gnu.org/cgi/bugreport.cgi?att=1;filename=unfolded.png;msg=19;bug=52587>

The current folding causes this:

<https://debbugs.gnu.org/cgi/bugreport.cgi?bug=52587;msg=19;filename=folded-current.png;att=3>

Whereas it looks like this would be more useful:

<https://debbugs.gnu.org/cgi/bugreport.cgi?att=5;msg=19;filename=folded-requested.png;bug=52587>

IOW, instead of eliding everything from the heading's end-of-line
(included) to the section's last end-of-line (*excluded*, so its
font-locking clashes with the heading line), we would like to elide
everything *after* the heading's end-of-line (*including* the section's
last end-of-line).

I have not found the time to dig into outline.el to understand how this
could be pulled off yet.  FWIW though, issues such as the one we're
seeing now with shortdoc comfort me in the idea that this is a
fundamental problem with outline.el (although a mostly aesthetic one,
granted).

<tangent>

  I'm also reminded of bug#51016, where we debated whether FORM FEEDs
  belonged in outline-regexp; I'm seeing a pattern with shortdoc's
  separator lines, and I wonder if outline.el should grow a concept of
  "section separators".

  That would allow us to stop conflating FORM FEEDs with level-1
  headings, letting outline-forward-same-level do DTRT, yet keeping the
  ^L displayed even when folding all top-level headings, which IIRC was
  a contentious issue in that bug report

</tangent>




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Tue, 15 Feb 2022 09:30:02 GMT) Full text and rfc822 format available.

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

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Tue, 15 Feb 2022 10:29:23 +0100
Kévin Le Gouguec <kevin.legouguec <at> gmail.com> writes:

> I have not found the time to dig into outline.el to understand how this
> could be pulled off yet.  FWIW though, issues such as the one we're
> seeing now with shortdoc comfort me in the idea that this is a
> fundamental problem with outline.el (although a mostly aesthetic one,
> granted).

Yeah, I think it's an issue in outline (that should be fixed).

>   I'm also reminded of bug#51016, where we debated whether FORM FEEDs
>   belonged in outline-regexp; I'm seeing a pattern with shortdoc's
>   separator lines, and I wonder if outline.el should grow a concept of
>   "section separators".
>
>   That would allow us to stop conflating FORM FEEDs with level-1
>   headings, letting outline-forward-same-level do DTRT, yet keeping the
>   ^L displayed even when folding all top-level headings, which IIRC was
>   a contentious issue in that bug report

As previously discussed in some other bug report *vague hand wave*, it
makes no sense to use regexps for outline in generated buffers.  The
functions that generate the buffers knows what's a heading, and should
mark the headings properly (with text properties), and
outline-minor-mode should just react to these text properties in these
buffers.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Tue, 15 Feb 2022 09:37:02 GMT) Full text and rfc822 format available.

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

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Lars Ingebrigtsen <larsi <at> gnus.org>,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Cc: 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Tue, 15 Feb 2022 10:36:27 +0100
On 2/15/22 10:29, Lars Ingebrigtsen wrote:
> As previously discussed in some other bug report *vague hand wave*, it
> makes no sense to use regexps for outline in generated buffers.  The
> functions that generate the buffers knows what's a heading, and should
> mark the headings properly (with text properties), and
> outline-minor-mode should just react to these text properties in these
> buffers.

That's a good idea, but only tangentially related to the aesthetic
outline issue with the face. I wanted to have something like this
before, when I attached text properties to headlines. If outline-mode
could automatically find headlines by a text property that would be great!

Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 16 Feb 2022 18:45:03 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 53981 <at> debbugs.gnu.org,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 16 Feb 2022 20:18:35 +0200
[Message part 1 (text/plain, inline)]
> As previously discussed in some other bug report *vague hand wave*, it
> makes no sense to use regexps for outline in generated buffers.  The
> functions that generate the buffers knows what's a heading, and should
> mark the headings properly (with text properties), and
> outline-minor-mode should just react to these text properties in these
> buffers.

That was in https://debbugs.gnu.org/31094#40

Is this a promising direction?

[outline-search-function.patch (text/x-diff, inline)]
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 658edd6752..63e799a2be 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -1435,7 +1435,19 @@ shortdoc-mode-map
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
-  :interactive nil)
+  :interactive nil
+  (setq-local outline-level (lambda () (if (eq (char-after) ?\() 2 1)))
+  (setq-local outline-search-function
+              (lambda (&optional looking-at)
+                (save-excursion
+                  (let* ((prop-at (if looking-at
+                                      (get-text-property (point) 'shortdoc-section)
+                                    t))
+                         (prop-match (and prop-at (text-property-search-forward 'shortdoc-section))))
+                    (when prop-match
+                      (set-match-data (list (prop-match-beginning prop-match)
+                                            (prop-match-end prop-match)))
+                      t))))))
 
 (defun shortdoc--goto-section (arg sym &optional reverse)
   (unless (natnump arg)
diff --git a/lisp/outline.el b/lisp/outline.el
index 012e219fc3..a282237696 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -301,7 +303,9 @@ outline-font-lock-face
   "Return one of `outline-font-lock-faces' for current level."
   (save-excursion
     (goto-char (match-beginning 0))
-    (looking-at outline-regexp)
+    (if outline-search-function
+        (funcall outline-search-function t)
+      (looking-at outline-regexp))
     (aref outline-font-lock-faces
           (% (1- (funcall outline-level))
              (length outline-font-lock-faces)))))
@@ -393,14 +397,19 @@ outline-minor-mode-highlight
   :safe #'symbolp
   :version "28.1")
 
+(defvar outline-search-function nil
+  "Function to search the next outline.
+It should be like `re-search-forward' and `looking-at'.")
+
 (defun outline-minor-mode-highlight-buffer ()
   ;; Fallback to overlays when font-lock is unsupported.
   (save-excursion
     (goto-char (point-min))
     (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
-      (while (re-search-forward regexp nil t)
-        (let ((overlay (make-overlay (match-beginning 0)
-                                     (match-end 0))))
+      (while (if outline-search-function
+                 (funcall outline-search-function)
+               (re-search-forward regexp nil t))
+        (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
           (overlay-put overlay 'outline-overlay t)
           (when (or (eq outline-minor-mode-highlight 'override)
                     (and (eq outline-minor-mode-highlight t)
@@ -535,7 +544,9 @@ outline-on-heading-p
   (save-excursion
     (beginning-of-line)
     (and (bolp) (or invisible-ok (not (outline-invisible-p)))
-	 (looking-at outline-regexp))))
+	 (if outline-search-function
+             (funcall outline-search-function t)
+           (looking-at outline-regexp)))))
 
 (defun outline-insert-heading ()
   "Insert a new heading at same depth at point."

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Thu, 17 Feb 2022 11:44:02 GMT) Full text and rfc822 format available.

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

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Juri Linkov <juri <at> linkov.net>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 53981 <at> debbugs.gnu.org,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Thu, 17 Feb 2022 12:43:37 +0100
Juri Linkov <juri <at> linkov.net> writes:

> Is this a promising direction?

[...]

> +  (setq-local outline-level (lambda () (if (eq (char-after) ?\() 2 1)))
> +  (setq-local outline-search-function
> +              (lambda (&optional looking-at)
> +                (save-excursion
> +                  (let* ((prop-at (if looking-at
> +                                      (get-text-property (point) 'shortdoc-section)
> +                                    t))
> +                         (prop-match (and prop-at (text-property-search-forward 'shortdoc-section))))
> +                    (when prop-match
> +                      (set-match-data (list (prop-match-beginning prop-match)
> +                                            (prop-match-end prop-match)))
> +                      t))))))

No, I was thinking that modes like this would just put a text property
like `outline-heading' (or even just `heading') on the heading, and then
outline-minor-mode would use that.

I.e., we make modes that generate buffers provide semantics for the data
it's inserted, and then outline-minor-mode reacts to those semantics.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Thu, 17 Feb 2022 13:45:01 GMT) Full text and rfc822 format available.

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

From: Daniel Mendler <mail <at> daniel-mendler.de>
To: Lars Ingebrigtsen <larsi <at> gnus.org>, Juri Linkov <juri <at> linkov.net>
Cc: 53981 <at> debbugs.gnu.org,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Thu, 17 Feb 2022 14:44:20 +0100

On 2/17/22 12:43, Lars Ingebrigtsen wrote:
> Juri Linkov <juri <at> linkov.net> writes:
> 
>> Is this a promising direction?
> 
> [...]
> 
>> +  (setq-local outline-level (lambda () (if (eq (char-after) ?\() 2 1)))
>> +  (setq-local outline-search-function
>> +              (lambda (&optional looking-at)
>> +                (save-excursion
>> +                  (let* ((prop-at (if looking-at
>> +                                      (get-text-property (point) 'shortdoc-section)
>> +                                    t))
>> +                         (prop-match (and prop-at (text-property-search-forward 'shortdoc-section))))
>> +                    (when prop-match
>> +                      (set-match-data (list (prop-match-beginning prop-match)
>> +                                            (prop-match-end prop-match)))
>> +                      t))))))
> 
> No, I was thinking that modes like this would just put a text property
> like `outline-heading' (or even just `heading') on the heading, and then
> outline-minor-mode would use that.
> 
> I.e., we make modes that generate buffers provide semantics for the data
> it's inserted, and then outline-minor-mode reacts to those semantics.

I think Juri's approach to provide a search function is better, since it
gives more flexibility. At least please don't hard code the
`outline-heading` property. Then one could use different properties and
retroactively enable outline minor mode for modes which don't come with
the outline-heading property. Maybe support a list of properties which
indicate headlines? But at that point a function may just be simpler...

Daniel





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Thu, 17 Feb 2022 15:03:01 GMT) Full text and rfc822 format available.

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

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: 53981 <at> debbugs.gnu.org,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>,
 Juri Linkov <juri <at> linkov.net>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Thu, 17 Feb 2022 16:02:18 +0100
Daniel Mendler <mail <at> daniel-mendler.de> writes:

> I think Juri's approach to provide a search function is better, since it
> gives more flexibility. At least please don't hard code the
> `outline-heading` property. Then one could use different properties and
> retroactively enable outline minor mode for modes which don't come with
> the outline-heading property. Maybe support a list of properties which
> indicate headlines? But at that point a function may just be simpler...

I don't see the point.  We're marking up buffers with semantic
information -- there isn't much point in trying to retrofit a machinery
like that for older packages.

I don't think it's necessary to create a whole machinery around this --
we should design with the future, not the past, in mind.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Thu, 17 Feb 2022 17:49:02 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 53981 <at> debbugs.gnu.org,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Thu, 17 Feb 2022 19:45:28 +0200
> No, I was thinking that modes like this would just put a text property
> like `outline-heading' (or even just `heading') on the heading, and then
> outline-minor-mode would use that.

Should these modes also put the text property `outline-level'?

Then maybe instead of two text properties `outline-heading'=t and
`outline-level'=N, it should be enough to put just `outline-level':

diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 658edd6752..7bcf331eb9 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -1284,7 +1284,12 @@ shortdoc-display-group
          (unless (bobp)
            (insert "\n"))
          (insert (propertize
-                  (concat (substitute-command-keys data) "\n\n")
+                  (substitute-command-keys data)
+                  'face 'shortdoc-heading
+                  'shortdoc-section t
+                  'outline-level 1))
+         (insert (propertize
+                  "\n\n"
                   'face 'shortdoc-heading
                   'shortdoc-section t)))
         ;; There may be functions not yet defined in the data.
@@ -1307,7 +1312,7 @@ shortdoc--display-function
         (start-section (point))
         arglist-start)
     ;; Function calling convention.
-    (insert (propertize "(" 'shortdoc-function function))
+    (insert (propertize "(" 'shortdoc-function function 'outline-level 2))
     (if (plist-get data :no-manual)
         (insert-text-button
          (symbol-name function)





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Sat, 19 Feb 2022 12:50:01 GMT) Full text and rfc822 format available.

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

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Juri Linkov <juri <at> linkov.net>
Cc: Daniel Mendler <mail <at> daniel-mendler.de>, 53981 <at> debbugs.gnu.org,
 Kévin Le Gouguec <kevin.legouguec <at> gmail.com>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Sat, 19 Feb 2022 13:49:16 +0100
Juri Linkov <juri <at> linkov.net> writes:

> Then maybe instead of two text properties `outline-heading'=t and
> `outline-level'=N, it should be enough to put just `outline-level':

Yes, sounds like a good idea to me.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Severity set to 'wishlist' from 'normal' Request was from Stefan Kangas <stefan <at> marxist.se> to control <at> debbugs.gnu.org. (Sun, 19 Jun 2022 19:05:03 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Tue, 08 Nov 2022 19:15:02 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Daniel Mendler <mail <at> daniel-mendler.de>
Cc: 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Tue, 08 Nov 2022 21:12:45 +0200
[Message part 1 (text/plain, inline)]
> The shortdoc buffer currently lacks support for the outline-minor-mode.
> By setting the two variables outline-regexp and outline-level, we can
> unlock this feature. Does it make sense to provide this by default?
>
> (defun shortdoc--outline-level () (if (eq (char-after) ?\() 2 1)))
> (add-hook 'shortdoc-mode-hook
>           (lambda ()
>             (setq-local outline-level #'shortdoc--outline-level
>                         outline-regexp "[A-Z(]")))

Unfortunately, outline-regexp is not a reliable way to find
outline headings.  For this reason currently outlines in apropos
are broken: outline-regexp in apropos-mode is set to "^[^ \n]+",
that matches too many false positives, all blue lines below
are incorrectly identified as outline headings:

[apropos-outline-1.png (image/png, inline)]
[Message part 3 (text/plain, inline)]
As previously discussed, we need to introduce a new function
to search outline headings.  Using such function, the outlines
are identified with 100% precision:

[apropos-outline-2.png (image/png, inline)]
[Message part 5 (text/plain, inline)]
Here is a patch that adds 'outline-search-function'.  However,
the requirements for this function arguments are quite non-standard:
its first argument should be a limit that is used in
outline-font-lock-keywords where MATCHER has the argument LIMIT.
Also the same function should be able to search in both directions:
forward and backward.  And the third requirement is that it should be
able also to be used as looking-at without moving point.  In this patch
the second argument HOW supports two values: 'backward' and 'looking-at'.
But this could be split to two separate boolean arguments,
this is not the final patch.  Also another optional argument
could be added to define an arbitrary property to search.

Then here the same function is used for apropos-mode and shortdoc:

[outline-search-function.patch (text/x-diff, inline)]
diff --git a/lisp/apropos.el b/lisp/apropos.el
index 624c29cb410..5b7fe4cb23a 100644
--- a/lisp/apropos.el
+++ b/lisp/apropos.el
@@ -493,7 +493,7 @@ apropos-mode
 \\{apropos-mode-map}"
   (make-local-variable 'apropos--current)
   (setq-local revert-buffer-function #'apropos--revert-buffer)
-  (setq-local outline-regexp "^[^ \n]+"
+  (setq-local outline-search-function #'outline-search-level-prop
               outline-level (lambda () 1)
               outline-minor-mode-cycle t
               outline-minor-mode-highlight t
@@ -1188,7 +1188,8 @@ apropos-print
 	  (insert-text-button (symbol-name symbol)
 			      'type 'apropos-symbol
 			      'skip apropos-multi-type
-			      'face 'apropos-symbol)
+			      'face 'apropos-symbol
+			      'outline-level 1)
 	  (setq button-end (point))
 	  (if (and (eq apropos-sort-by-scores 'verbose)
 		   (cadr apropos-item))
diff --git a/lisp/outline.el b/lisp/outline.el
index a646f71db8b..442d51b71bc 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -59,6 +59,14 @@ outline-heading-end-regexp
 in the file it applies to.")
 ;;;###autoload(put 'outline-heading-end-regexp 'safe-local-variable 'stringp)
 
+(defvar outline-search-function nil
+  "Function to search the next outline heading.
+The function is called with two arguments: the limit of the search
+and the optional argument for the backward search; it should return
+non-nil, move point (to the end of the buffer when search fails),
+and set match-data appropriately if it succeeds;
+like re-search-forward with `outline-regexp' would.")
+
 (defvar outline-mode-prefix-map
   (let ((map (make-sparse-keymap)))
     (define-key map "@" 'outline-mark-subtree)
@@ -233,7 +241,8 @@ outline-mode-map
 (defvar outline-font-lock-keywords
   '(
     ;; Highlight headings according to the level.
-    (eval . (list (concat "^\\(?:" outline-regexp "\\).*")
+    (eval . (list (or outline-search-function
+                      (concat "^\\(?:" outline-regexp "\\).*"))
                   0 '(if outline-minor-mode
                          (if outline-minor-mode-highlight
                              (list 'face (outline-font-lock-face)))
@@ -366,7 +375,9 @@ outline-font-lock-face
   "Return one of `outline-font-lock-faces' for current level."
   (save-excursion
     (goto-char (match-beginning 0))
-    (looking-at outline-regexp)
+    (if outline-search-function
+        (funcall outline-search-function nil 'looking-at)
+      (looking-at outline-regexp))
     (aref outline-font-lock-faces
           (% (1- (funcall outline-level))
              (length outline-font-lock-faces)))))
@@ -474,16 +485,17 @@ outline-minor-mode-highlight-buffer
   ;; Fallback to overlays when font-lock is unsupported.
   (save-excursion
     (goto-char (point-min))
-    (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
-      (while (re-search-forward regexp nil t)
-        (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
-          (overlay-put overlay 'outline-highlight t)
-          ;; FIXME: Is it possible to override all underlying face attributes?
-          (when (or (memq outline-minor-mode-highlight '(append override))
-                    (and (eq outline-minor-mode-highlight t)
-                         (not (get-text-property (match-beginning 0) 'face))))
-            (overlay-put overlay 'face (outline-font-lock-face))))
-        (goto-char (match-end 0))))))
+    (while (if outline-search-function
+               (funcall outline-search-function)
+             (re-search-forward outline-regexp nil t))
+      (let ((overlay (make-overlay (match-beginning 0) (pos-eol))))
+        (overlay-put overlay 'outline-highlight t)
+        ;; FIXME: Is it possible to override all underlying face attributes?
+        (when (or (memq outline-minor-mode-highlight '(append override))
+                  (and (eq outline-minor-mode-highlight t)
+                       (not (get-text-property (match-beginning 0) 'face))))
+          (overlay-put overlay 'face (outline-font-lock-face))))
+      (move-end-of-line 1))))
 
 ;;;###autoload
 (define-minor-mode outline-minor-mode
@@ -592,26 +604,32 @@ outline-next-preface
   "Skip forward to just before the next heading line.
 If there's no following heading line, stop before the newline
 at the end of the buffer."
-  (if (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
-			 nil 'move)
-      (goto-char (match-beginning 0)))
-  (if (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
-      (forward-char -1)))
+  (when (if outline-search-function
+            (funcall outline-search-function)
+          (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
+			     nil 'move))
+    (goto-char (match-beginning 0)))
+  (when (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
+    (forward-char -1)))
 
 (defun outline-next-heading ()
   "Move to the next (possibly invisible) heading line."
   (interactive)
   ;; Make sure we don't match the heading we're at.
-  (if (and (bolp) (not (eobp))) (forward-char 1))
-  (if (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move)
-      (goto-char (match-beginning 0))))
+  (when (and (bolp) (not (eobp))) (forward-char 1))
+  (when (if outline-search-function
+            (funcall outline-search-function)
+          (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
+			     nil 'move))
+    (goto-char (match-beginning 0))))
 
 (defun outline-previous-heading ()
   "Move to the previous (possibly invisible) heading line."
   (interactive)
-  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-		      nil 'move))
+  (if outline-search-function
+      (funcall outline-search-function nil 'backward)
+    (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+		        nil 'move)))
 
 (defsubst outline-invisible-p (&optional pos)
   "Non-nil if the character after POS has outline invisible property.
@@ -628,8 +646,10 @@ outline-back-to-heading
       (let (found)
 	(save-excursion
 	  (while (not found)
-	    (or (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-				    nil t)
+	    (or (if outline-search-function
+                    (funcall outline-search-function nil 'backward)
+                  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+				      nil t))
                 (signal 'outline-before-first-heading nil))
 	    (setq found (and (or invisible-ok (not (outline-invisible-p)))
 			     (point)))))
@@ -642,7 +662,9 @@ outline-on-heading-p
   (save-excursion
     (beginning-of-line)
     (and (bolp) (or invisible-ok (not (outline-invisible-p)))
-	 (looking-at outline-regexp))))
+	 (if outline-search-function
+             (funcall outline-search-function nil 'looking-at)
+           (looking-at outline-regexp)))))
 
 (defun outline-insert-heading ()
   "Insert a new heading at same depth at point."
@@ -754,7 +776,9 @@ outline-demote
 		      (while (and (progn (outline-next-heading) (not (eobp)))
 				  (<= (funcall outline-level) level))))
 		    (unless (eobp)
-		      (looking-at outline-regexp)
+		      (if outline-search-function
+                          (funcall outline-search-function nil 'looking-at)
+                        (looking-at outline-regexp))
 		      (match-string-no-properties 0))))
                 ;; Bummer!! There is no higher-level heading in the buffer.
                 (outline-invent-heading head nil))))
@@ -805,7 +829,9 @@ outline-map-region
   (save-excursion
     (setq end (copy-marker end))
     (goto-char beg)
-    (when (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t)
+    (when (if outline-search-function
+              (funcall outline-search-function)
+            (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t))
       (goto-char (match-beginning 0))
       (funcall fun)
       (while (and (progn
@@ -877,17 +903,21 @@ outline-next-visible-heading
     (while (and (not (bobp)) (< arg 0))
       (while (and (not (bobp))
 		  (setq found-heading-p
-			(re-search-backward
-			 (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move))
+			(if outline-search-function
+                            (funcall outline-search-function nil 'backward)
+                          (re-search-backward
+			   (concat "^\\(?:" outline-regexp "\\)")
+			   nil 'move)))
 		  (outline-invisible-p)))
       (setq arg (1+ arg)))
     (while (and (not (eobp)) (> arg 0))
       (while (and (not (eobp))
 		  (setq found-heading-p
-			(re-search-forward
-			 (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move))
+			(if outline-search-function
+                            (funcall outline-search-function)
+                          (re-search-forward
+			   (concat "^\\(?:" outline-regexp "\\)")
+			   nil 'move)))
 		  (outline-invisible-p (match-beginning 0))))
       (setq arg (1- arg)))
     (if found-heading-p (beginning-of-line))))
@@ -1108,7 +1138,9 @@ outline-hide-sublevels
 		(cond
 		 (current-prefix-arg (prefix-numeric-value current-prefix-arg))
 		 ((save-excursion (beginning-of-line)
-				  (looking-at outline-regexp))
+				  (if outline-search-function
+                                      (funcall outline-search-function nil 'looking-at)
+                                    (looking-at outline-regexp)))
 		  (funcall outline-level))
 		 (t 1))))
   (if (< levels 1)
@@ -1255,7 +1287,9 @@ outline-up-heading
 	  (setq level (funcall outline-level)))
 	(setq start-level level))
       (setq arg (- arg 1))))
-  (looking-at outline-regexp))
+  (if outline-search-function
+      (funcall outline-search-function nil 'looking-at)
+    (looking-at outline-regexp)))
 
 (defun outline-forward-same-level (arg)
   "Move forward to the ARG'th subheading at same level as this one.
@@ -1346,6 +1380,35 @@ outline-headers-as-kill
                     (insert "\n\n"))))))
           (kill-new (buffer-string)))))))
 
+
+;;; Search text-property for outline headings
+
+(defun outline-search-level-prop (&optional limit how)
+  (let* ((prop 'outline-level)
+         (prop-at
+          (if (eq how 'looking-at)
+              (get-text-property (point) prop)
+            (when (get-text-property (point) prop)
+              ;; Go to the end of the current heading
+              (if (eq how 'backward)
+                  (text-property-search-backward prop)
+                (text-property-search-forward prop)))
+            t))
+         (prop-match
+          (when prop-at
+            (if (eq how 'backward)
+                (text-property-search-backward prop)
+              (text-property-search-forward prop)))))
+    (if prop-match
+        (let ((beg (prop-match-beginning prop-match))
+              (end (prop-match-end prop-match)))
+          (if (or (null limit) (< end limit))
+              (set-match-data (list beg end))
+            (goto-char (or limit (point-max))))
+          t)
+      (goto-char (point-max))
+      nil)))
+
 
 ;;; Initial visibility
 
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index dbac03432c1..d3c824a4e93 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -1374,7 +1374,12 @@ shortdoc-display-group
          (unless (bobp)
            (insert "\n"))
          (insert (propertize
-                  (concat (substitute-command-keys data) "\n\n")
+                  (substitute-command-keys data)
+                  'face 'shortdoc-heading
+                  'shortdoc-section t
+                  'outline-level 1))
+         (insert (propertize
+                  "\n\n"
                   'face 'shortdoc-heading
                   'shortdoc-section t)))
         ;; There may be functions not yet defined in the data.
@@ -1397,7 +1402,7 @@ shortdoc--display-function
         (start-section (point))
         arglist-start)
     ;; Function calling convention.
-    (insert (propertize "(" 'shortdoc-function function))
+    (insert (propertize "(" 'shortdoc-function function 'outline-level 2))
     (if (plist-get data :no-manual)
         (insert-text-button
          (symbol-name function)
@@ -1531,7 +1536,9 @@ shortdoc-mode-map
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
-  :interactive nil)
+  :interactive nil
+  (setq-local outline-search-function #'outline-search-level-prop)
+  (setq-local outline-level (lambda () (get-text-property (point) 'outline-level))))
 
 (defun shortdoc--goto-section (arg sym &optional reverse)
   (unless (natnump arg)

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Tue, 08 Nov 2022 19:33:01 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Juri Linkov <juri <at> linkov.net>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Tue, 08 Nov 2022 21:32:03 +0200
> Cc: 53981 <at> debbugs.gnu.org
> From: Juri Linkov <juri <at> linkov.net>
> Date: Tue, 08 Nov 2022 21:12:45 +0200
> 
> @@ -474,16 +485,17 @@ outline-minor-mode-highlight-buffer
>    ;; Fallback to overlays when font-lock is unsupported.
>    (save-excursion
>      (goto-char (point-min))
> -    (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
> -      (while (re-search-forward regexp nil t)
> -        (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
> -          (overlay-put overlay 'outline-highlight t)
> -          ;; FIXME: Is it possible to override all underlying face attributes?
> -          (when (or (memq outline-minor-mode-highlight '(append override))
> -                    (and (eq outline-minor-mode-highlight t)
> -                         (not (get-text-property (match-beginning 0) 'face))))
> -            (overlay-put overlay 'face (outline-font-lock-face))))
> -        (goto-char (match-end 0))))))
> +    (while (if outline-search-function
> +               (funcall outline-search-function)
> +             (re-search-forward outline-regexp nil t))

This changes the effect of the code because the new code searches for
a different regexp.

> @@ -877,17 +903,21 @@ outline-next-visible-heading
>      (while (and (not (bobp)) (< arg 0))
>        (while (and (not (bobp))
>  		  (setq found-heading-p
> -			(re-search-backward
> -			 (concat "^\\(?:" outline-regexp "\\)")
> -			 nil 'move))
> +			(if outline-search-function
> +                            (funcall outline-search-function nil 'backward)
> +                          (re-search-backward
> +			   (concat "^\\(?:" outline-regexp "\\)")
> +			   nil 'move)))
>  		  (outline-invisible-p)))
>        (setq arg (1+ arg)))
>      (while (and (not (eobp)) (> arg 0))
>        (while (and (not (eobp))
>  		  (setq found-heading-p
> -			(re-search-forward
> -			 (concat "^\\(?:" outline-regexp "\\)")
> -			 nil 'move))
> +			(if outline-search-function
> +                            (funcall outline-search-function)
> +                          (re-search-forward
> +			   (concat "^\\(?:" outline-regexp "\\)")
> +			   nil 'move)))
>  		  (outline-invisible-p (match-beginning 0))))

These two loops cons a new string each iteration.  (So did the
original code, but if we are touching this, might as well fix that.)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 09 Nov 2022 17:28:01 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 09 Nov 2022 19:17:31 +0200
[Message part 1 (text/plain, inline)]
>> +    (while (if outline-search-function
>> +               (funcall outline-search-function)
>> +             (re-search-forward outline-regexp nil t))
>
> This changes the effect of the code because the new code searches for
> a different regexp.

Sorry, this was an attempt to unify code branches,
but this change remained untested.  Now fixed below.

>> +			(if outline-search-function
>> +                            (funcall outline-search-function)
>> +                          (re-search-forward
>> +			   (concat "^\\(?:" outline-regexp "\\)")
>> +			   nil 'move)))
>
> These two loops cons a new string each iteration.  (So did the
> original code, but if we are touching this, might as well fix that.)

This is optimized as well:

[outline-search-level.patch (text/x-diff, inline)]
diff --git a/lisp/apropos.el b/lisp/apropos.el
index 624c29cb410..02a32a2e7ce 100644
--- a/lisp/apropos.el
+++ b/lisp/apropos.el
@@ -493,7 +493,7 @@ apropos-mode
 \\{apropos-mode-map}"
   (make-local-variable 'apropos--current)
   (setq-local revert-buffer-function #'apropos--revert-buffer)
-  (setq-local outline-regexp "^[^ \n]+"
+  (setq-local outline-search-function #'outline-search-level
               outline-level (lambda () 1)
               outline-minor-mode-cycle t
               outline-minor-mode-highlight t
@@ -1188,7 +1188,8 @@ apropos-print
 	  (insert-text-button (symbol-name symbol)
 			      'type 'apropos-symbol
 			      'skip apropos-multi-type
-			      'face 'apropos-symbol)
+			      'face 'apropos-symbol
+			      'outline-level 1)
 	  (setq button-end (point))
 	  (if (and (eq apropos-sort-by-scores 'verbose)
 		   (cadr apropos-item))
diff --git a/lisp/outline.el b/lisp/outline.el
index a646f71db8b..fcac9d1950b 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -59,6 +59,14 @@ outline-heading-end-regexp
 in the file it applies to.")
 ;;;###autoload(put 'outline-heading-end-regexp 'safe-local-variable 'stringp)
 
+(defvar outline-search-function nil
+  "Function to search the next outline heading.
+The function is called with two arguments: the limit of the search
+and the optional argument for the backward search; it should return
+non-nil, move point (to the end of the buffer when search fails),
+and set match-data appropriately if it succeeds;
+like re-search-forward with `outline-regexp' would.")
+
 (defvar outline-mode-prefix-map
   (let ((map (make-sparse-keymap)))
     (define-key map "@" 'outline-mark-subtree)
@@ -233,7 +241,8 @@ outline-mode-map
 (defvar outline-font-lock-keywords
   '(
     ;; Highlight headings according to the level.
-    (eval . (list (concat "^\\(?:" outline-regexp "\\).*")
+    (eval . (list (or outline-search-function
+                      (concat "^\\(?:" outline-regexp "\\).*"))
                   0 '(if outline-minor-mode
                          (if outline-minor-mode-highlight
                              (list 'face (outline-font-lock-face)))
@@ -366,7 +375,9 @@ outline-font-lock-face
   "Return one of `outline-font-lock-faces' for current level."
   (save-excursion
     (goto-char (match-beginning 0))
-    (looking-at outline-regexp)
+    (if outline-search-function
+        (funcall outline-search-function nil nil t)
+      (looking-at outline-regexp))
     (aref outline-font-lock-faces
           (% (1- (funcall outline-level))
              (length outline-font-lock-faces)))))
@@ -474,8 +485,11 @@ outline-minor-mode-highlight-buffer
   ;; Fallback to overlays when font-lock is unsupported.
   (save-excursion
     (goto-char (point-min))
-    (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
-      (while (re-search-forward regexp nil t)
+    (let ((regexp (unless outline-search-function
+                    (concat "^\\(?:" outline-regexp "\\).*$"))))
+      (while (if outline-search-function
+                 (funcall outline-search-function)
+               (re-search-forward regexp nil t))
         (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
           (overlay-put overlay 'outline-highlight t)
           ;; FIXME: Is it possible to override all underlying face attributes?
@@ -592,26 +606,32 @@ outline-next-preface
   "Skip forward to just before the next heading line.
 If there's no following heading line, stop before the newline
 at the end of the buffer."
-  (if (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
-			 nil 'move)
-      (goto-char (match-beginning 0)))
-  (if (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
-      (forward-char -1)))
+  (when (if outline-search-function
+            (funcall outline-search-function)
+          (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
+			     nil 'move))
+    (goto-char (match-beginning 0)))
+  (when (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
+    (forward-char -1)))
 
 (defun outline-next-heading ()
   "Move to the next (possibly invisible) heading line."
   (interactive)
   ;; Make sure we don't match the heading we're at.
-  (if (and (bolp) (not (eobp))) (forward-char 1))
-  (if (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move)
-      (goto-char (match-beginning 0))))
+  (when (and (bolp) (not (eobp))) (forward-char 1))
+  (when (if outline-search-function
+            (funcall outline-search-function)
+          (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
+			     nil 'move))
+    (goto-char (match-beginning 0))))
 
 (defun outline-previous-heading ()
   "Move to the previous (possibly invisible) heading line."
   (interactive)
-  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-		      nil 'move))
+  (if outline-search-function
+      (funcall outline-search-function nil t)
+    (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+		        nil 'move)))
 
 (defsubst outline-invisible-p (&optional pos)
   "Non-nil if the character after POS has outline invisible property.
@@ -628,8 +648,10 @@ outline-back-to-heading
       (let (found)
 	(save-excursion
 	  (while (not found)
-	    (or (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-				    nil t)
+	    (or (if outline-search-function
+                    (funcall outline-search-function nil t)
+                  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+				      nil t))
                 (signal 'outline-before-first-heading nil))
 	    (setq found (and (or invisible-ok (not (outline-invisible-p)))
 			     (point)))))
@@ -642,7 +664,9 @@ outline-on-heading-p
   (save-excursion
     (beginning-of-line)
     (and (bolp) (or invisible-ok (not (outline-invisible-p)))
-	 (looking-at outline-regexp))))
+	 (if outline-search-function
+             (funcall outline-search-function nil nil t)
+           (looking-at outline-regexp)))))
 
 (defun outline-insert-heading ()
   "Insert a new heading at same depth at point."
@@ -754,7 +778,9 @@ outline-demote
 		      (while (and (progn (outline-next-heading) (not (eobp)))
 				  (<= (funcall outline-level) level))))
 		    (unless (eobp)
-		      (looking-at outline-regexp)
+		      (if outline-search-function
+                          (funcall outline-search-function nil nil t)
+                        (looking-at outline-regexp))
 		      (match-string-no-properties 0))))
                 ;; Bummer!! There is no higher-level heading in the buffer.
                 (outline-invent-heading head nil))))
@@ -805,7 +831,9 @@ outline-map-region
   (save-excursion
     (setq end (copy-marker end))
     (goto-char beg)
-    (when (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t)
+    (when (if outline-search-function
+              (funcall outline-search-function)
+            (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t))
       (goto-char (match-beginning 0))
       (funcall fun)
       (while (and (progn
@@ -873,21 +901,23 @@ outline-next-visible-heading
   (if (< arg 0)
       (beginning-of-line)
     (end-of-line))
-  (let (found-heading-p)
+  (let ((regexp (unless outline-search-function
+                  (concat "^\\(?:" outline-regexp "\\)")))
+        found-heading-p)
     (while (and (not (bobp)) (< arg 0))
       (while (and (not (bobp))
 		  (setq found-heading-p
-			(re-search-backward
-			 (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move))
+			(if outline-search-function
+                            (funcall outline-search-function nil t)
+                          (re-search-backward regexp nil 'move)))
 		  (outline-invisible-p)))
       (setq arg (1+ arg)))
     (while (and (not (eobp)) (> arg 0))
       (while (and (not (eobp))
 		  (setq found-heading-p
-			(re-search-forward
-			 (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move))
+			(if outline-search-function
+                            (funcall outline-search-function)
+                          (re-search-forward regexp nil 'move)))
 		  (outline-invisible-p (match-beginning 0))))
       (setq arg (1- arg)))
     (if found-heading-p (beginning-of-line))))
@@ -1108,7 +1138,9 @@ outline-hide-sublevels
 		(cond
 		 (current-prefix-arg (prefix-numeric-value current-prefix-arg))
 		 ((save-excursion (beginning-of-line)
-				  (looking-at outline-regexp))
+				  (if outline-search-function
+                                      (funcall outline-search-function nil nil t)
+                                    (looking-at outline-regexp)))
 		  (funcall outline-level))
 		 (t 1))))
   (if (< levels 1)
@@ -1255,7 +1287,9 @@ outline-up-heading
 	  (setq level (funcall outline-level)))
 	(setq start-level level))
       (setq arg (- arg 1))))
-  (looking-at outline-regexp))
+  (if outline-search-function
+      (funcall outline-search-function nil nil t)
+    (looking-at outline-regexp)))
 
 (defun outline-forward-same-level (arg)
   "Move forward to the ARG'th subheading at same level as this one.
@@ -1346,6 +1380,38 @@ outline-headers-as-kill
                     (insert "\n\n"))))))
           (kill-new (buffer-string)))))))
 
+
+;;; Search text-property for outline headings
+
+;;;###autoload
+(defun outline-search-level (&optional limit backward looking-at)
+  (outline-search-text-property 'outline-level limit backward looking-at))
+
+(defun outline-search-text-property (prop &optional limit backward looking-at)
+  (let* ((prop-at
+          (if looking-at
+              (get-text-property (point) prop)
+            (when (get-text-property (point) prop)
+              ;; Go to the end of the current heading
+              (if backward
+                  (text-property-search-backward prop)
+                (text-property-search-forward prop)))
+            t))
+         (prop-match
+          (when prop-at
+            (if backward
+                (text-property-search-backward prop)
+              (text-property-search-forward prop)))))
+    (if prop-match
+        (let ((beg (prop-match-beginning prop-match))
+              (end (prop-match-end prop-match)))
+          (if (or (null limit) (< end limit))
+              (set-match-data (list beg end))
+            (goto-char (or limit (point-max))))
+          t)
+      (goto-char (point-max))
+      nil)))
+
 
 ;;; Initial visibility
 
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index dbac03432c1..18b758a9ca3 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -1374,7 +1374,12 @@ shortdoc-display-group
          (unless (bobp)
            (insert "\n"))
          (insert (propertize
-                  (concat (substitute-command-keys data) "\n\n")
+                  (substitute-command-keys data)
+                  'face 'shortdoc-heading
+                  'shortdoc-section t
+                  'outline-level 1))
+         (insert (propertize
+                  "\n\n"
                   'face 'shortdoc-heading
                   'shortdoc-section t)))
         ;; There may be functions not yet defined in the data.
@@ -1397,7 +1402,7 @@ shortdoc--display-function
         (start-section (point))
         arglist-start)
     ;; Function calling convention.
-    (insert (propertize "(" 'shortdoc-function function))
+    (insert (propertize "(" 'shortdoc-function function 'outline-level 2))
     (if (plist-get data :no-manual)
         (insert-text-button
          (symbol-name function)
@@ -1531,7 +1536,9 @@ shortdoc-mode-map
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
-  :interactive nil)
+  :interactive nil
+  (setq-local outline-search-function #'outline-search-level)
+  (setq-local outline-level (lambda () (get-text-property (point) 'outline-level))))
 
 (defun shortdoc--goto-section (arg sym &optional reverse)
   (unless (natnump arg)

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 09 Nov 2022 17:28:01 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 09 Nov 2022 19:24:32 +0200
[Message part 1 (text/plain, inline)]
> These two loops cons a new string each iteration.  (So did the
> original code, but if we are touching this, might as well fix that.)

BTW, the patch uses functions from text-property-search.el.
But these useful functions are still not autoloaded.
Here is the patch to autoload them:

[text-property-search-autoload.patch (text/x-diff, inline)]
diff --git a/lisp/emacs-lisp/text-property-search.el b/lisp/emacs-lisp/text-property-search.el
index d41222bdbf1..7e78fdfe7d6 100644
--- a/lisp/emacs-lisp/text-property-search.el
+++ b/lisp/emacs-lisp/text-property-search.el
@@ -26,9 +26,12 @@
 
 (eval-when-compile (require 'cl-lib))
 
+;;;###autoload (autoload 'prop-match-beginning "text-property-search")
+;;;###autoload (autoload 'prop-match-end "text-property-search")
 (cl-defstruct (prop-match)
   beginning end value)
 
+;;;###autoload
 (defun text-property-search-forward (property &optional value predicate
                                               not-current)
   "Search for the next region of text where PREDICATE is true.
@@ -131,7 +134,7 @@ text-property--find-end-forward
                      :end end
                      :value (get-text-property start property))))
 
-
+;;;###autoload
 (defun text-property-search-backward (property &optional value predicate
                                                not-current)
   "Search for the previous region of text whose PROPERTY matches VALUE.
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 61a26b504c8..5a27dcd4c27 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -3284,10 +3284,6 @@ reorder-starters
 (defvar reorder-enders "[\u202C\u2069]+\\|\n"
   "Regular expression for characters that end forced-reordered text.")
 
-(autoload 'text-property-search-forward "text-property-search")
-(autoload 'prop-match-beginning "text-property-search")
-(autoload 'prop-match-end "text-property-search")
-
 (defun highlight-confusing-reorderings (beg end &optional remove)
   "Highlight text in region that might be bidi-reordered in suspicious ways.
 This command find and highlights segments of buffer text that could have
diff --git a/lisp/bookmark.el b/lisp/bookmark.el
index b57ad12986d..d0a14b0cebf 100644
--- a/lisp/bookmark.el
+++ b/lisp/bookmark.el
@@ -34,7 +34,6 @@
 
 (require 'pp)
 (require 'tabulated-list)
-(require 'text-property-search)
 (require 'fringe) ; for builds --without-x
 (eval-when-compile (require 'cl-lib))
 

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 09 Nov 2022 17:35:02 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Juri Linkov <juri <at> linkov.net>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 09 Nov 2022 19:34:46 +0200
> From: Juri Linkov <juri <at> linkov.net>
> Cc: mail <at> daniel-mendler.de,  53981 <at> debbugs.gnu.org
> Date: Wed, 09 Nov 2022 19:24:32 +0200
> 
> BTW, the patch uses functions from text-property-search.el.
> But these useful functions are still not autoloaded.
> Here is the patch to autoload them:

I don't necessarily disagree, but do we have any policies or
guidelines regarding when to autoload a function?  It saves us a
'require', but what we "gain" instead is a (small) inflation of the
base memory footprint of the Emacs process.  So it isn't free.

Lars, Stefan, any comments?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 09 Nov 2022 19:49:02 GMT) Full text and rfc822 format available.

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

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org,
 Juri Linkov <juri <at> linkov.net>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 09 Nov 2022 14:47:56 -0500
>> BTW, the patch uses functions from text-property-search.el.
>> But these useful functions are still not autoloaded.
>> Here is the patch to autoload them:
>
> I don't necessarily disagree, but do we have any policies or
> guidelines regarding when to autoload a function?  It saves us a
> 'require', but what we "gain" instead is a (small) inflation of the
> base memory footprint of the Emacs process.  So it isn't free.
>
> Lars, Stefan, any comments?

For interactive functions, it's usually decided by whether the command
can be useful before the package is loaded (i,e,. usually an entry
point to the package).

For functions (i.e. exported from what is basically a library), the same
kind of tradeoff applies:

- is it likely that this one autoload will let other packages use this
  library without a `require` at all (e.g. `define-inline`), or will we
  end up needing N autoloads anyway?
- how commonly is this library used (i.e. is it worth carrying the
  N autoloads in every Emacs session, compared to having to write
  `require` in a handful of files).


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 09 Nov 2022 20:10:01 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org, juri <at> linkov.net
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 09 Nov 2022 22:09:02 +0200
> From: Stefan Monnier <monnier <at> iro.umontreal.ca>
> Cc: Juri Linkov <juri <at> linkov.net>,  mail <at> daniel-mendler.de,
>   53981 <at> debbugs.gnu.org
> Date: Wed, 09 Nov 2022 14:47:56 -0500
> 
> - is it likely that this one autoload will let other packages use this
>   library without a `require` at all (e.g. `define-inline`), or will we
>   end up needing N autoloads anyway?

I don't understand this.

> - how commonly is this library used (i.e. is it worth carrying the
>   N autoloads in every Emacs session, compared to having to write
>   `require` in a handful of files).

The answer to this one is that we currently have just 2 packages that
require this library.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 09 Nov 2022 20:15:02 GMT) Full text and rfc822 format available.

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

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org, juri <at> linkov.net
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 09 Nov 2022 15:14:43 -0500
>> - is it likely that this one autoload will let other packages use this
>>   library without a `require` at all (e.g. `define-inline`), or will we
>>   end up needing N autoloads anyway?
> I don't understand this.

If the library defines N functions and we'd end up needing to autoload
almost all of them because none of them is more of an "entry point" to
the library than any other, then `require` is usually preferable.

In contrast, if most of the functions in the library are only used
internally, or only after some other function in the library has been
called, then we just need a small number of autoloads for the rare few
entry points, making it more worthwhile.

>> - how commonly is this library used (i.e. is it worth carrying the
>>   N autoloads in every Emacs session, compared to having to write
>>   `require` in a handful of files).
>
> The answer to this one is that we currently have just 2 packages that
> require this library.

Then I'd go with `require`.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Thu, 10 Nov 2022 08:09:02 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Thu, 10 Nov 2022 09:42:46 +0200
>> - how commonly is this library used (i.e. is it worth carrying the
>>   N autoloads in every Emacs session, compared to having to write
>>   `require` in a handful of files).
>
> The answer to this one is that we currently have just 2 packages that
> require this library.

I counted more:

- lisp/emacs-lisp/multisession.el
- lisp/emacs-lisp/shortdoc.el
- lisp/emacs-lisp/vtable.el
- lisp/gnus/gnus-util.el
- lisp/help.el
- lisp/image/image-crop.el
- lisp/international/mule-cmds.el
- lisp/net/eww.el
- lisp/net/shr.el
- lisp/net/tramp-crypt.el
- lisp/progmodes/compile.el
- lisp/textmodes/enriched.el
- lisp/textmodes/string-edit.el

All they use text-property-search-forward and text-property-search-backward
as entry points.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Wed, 16 Nov 2022 19:18:02 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> daniel-mendler.de, 53981 <at> debbugs.gnu.org
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Wed, 16 Nov 2022 21:14:37 +0200
[Message part 1 (text/plain, inline)]
>>> +                          (re-search-forward
>>> +			   (concat "^\\(?:" outline-regexp "\\)")
>>> +			   nil 'move)))
>>
>> These two loops cons a new string each iteration.  (So did the
>> original code, but if we are touching this, might as well fix that.)
>
> This is optimized as well:

Here is a more tested patch that works in apropos and shortdoc.

Also tested for group outlines in the Completions buffer with:

```
(setq-local
 outline-search-function
 (lambda (&optional bound move backward looking-at)
   (outline-search-text-property
    'face 'completions-group-separator
    bound move backward looking-at))
 outline-level (lambda () 1))
```

It even works when using the search function that searches for
outline-regexp.  This better shows the meaning of its arguments:

```
(setq-default
 outline-search-function
 (lambda (&optional bound move backward looking-at)
   (cond
    (looking-at (looking-at outline-regexp))
    (backward
     (re-search-backward
      (concat "^\\(?:" outline-regexp "\\).*")
      bound (if move 'move t)))
    (t
     (re-search-forward
      (concat "^\\(?:" outline-regexp "\\).*")
      bound (if move 'move t)))))
 outline-level
 (lambda () (looking-at outline-regexp) (outline-level)))
```

As can be seen, the default outline-level function can't be used,
because the search function is expected to match to the end
of the heading line, but the default outline-level expects
to match only beginning of the outline heading.

[outline-search-function.patch (text/x-diff, inline)]
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index dbac03432c1..494e5c4123b 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -1374,13 +1374,19 @@ shortdoc-display-group
          (unless (bobp)
            (insert "\n"))
          (insert (propertize
-                  (concat (substitute-command-keys data) "\n\n")
+                  (substitute-command-keys data)
+                  'face 'shortdoc-heading
+                  'shortdoc-section t
+                  'outline-level 1))
+         (insert (propertize
+                  "\n\n"
                   'face 'shortdoc-heading
                   'shortdoc-section t)))
         ;; There may be functions not yet defined in the data.
         ((fboundp (car data))
          (when prev
-           (insert (make-separator-line)))
+           (insert (make-separator-line)
+                   (propertize "\n" 'face '(:height 0))))
          (setq prev t)
          (shortdoc--display-function data))))
      (cdr (assq group shortdoc--groups))))
@@ -1397,7 +1403,7 @@ shortdoc--display-function
         (start-section (point))
         arglist-start)
     ;; Function calling convention.
-    (insert (propertize "(" 'shortdoc-function function))
+    (insert (propertize "(" 'shortdoc-function function 'outline-level 2))
     (if (plist-get data :no-manual)
         (insert-text-button
          (symbol-name function)
@@ -1531,7 +1537,9 @@ shortdoc-mode-map
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
-  :interactive nil)
+  :interactive nil
+  (setq-local outline-search-function #'outline-search-level
+              outline-level (lambda () (get-text-property (point) 'outline-level))))
 
 (defun shortdoc--goto-section (arg sym &optional reverse)
   (unless (natnump arg)
diff --git a/lisp/apropos.el b/lisp/apropos.el
index 62a37df8207..e5c998ee77d 100644
--- a/lisp/apropos.el
+++ b/lisp/apropos.el
@@ -492,7 +492,7 @@ apropos-mode
 \\{apropos-mode-map}"
   (make-local-variable 'apropos--current)
   (setq-local revert-buffer-function #'apropos--revert-buffer)
-  (setq-local outline-regexp "^[^ \n]+"
+  (setq-local outline-search-function #'outline-search-level
               outline-level (lambda () 1)
               outline-minor-mode-cycle t
               outline-minor-mode-highlight t
@@ -1187,7 +1187,8 @@ apropos-print
 	  (insert-text-button (symbol-name symbol)
 			      'type 'apropos-symbol
 			      'skip apropos-multi-type
-			      'face 'apropos-symbol)
+			      'face 'apropos-symbol
+			      'outline-level 1)
 	  (setq button-end (point))
 	  (if (and (eq apropos-sort-by-scores 'verbose)
 		   (cadr apropos-item))
diff --git a/lisp/outline.el b/lisp/outline.el
index a646f71db8b..fbc3a57ee91 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -59,6 +59,18 @@ outline-heading-end-regexp
 in the file it applies to.")
 ;;;###autoload(put 'outline-heading-end-regexp 'safe-local-variable 'stringp)
 
+(defvar outline-search-function nil
+  "Function to search the next outline heading.
+The function is called with four optional arguments: BOUND, MOVE, BACKWARD,
+LOOKING-AT.  The first two arguments BOUND and MOVE are almost the same as
+the BOUND and NOERROR arguments of `re-search-forward', with the difference
+that MOVE accepts only a boolean, either nil or non-nil.  When the argument
+BACKWARD is non-nil, the search should search backward like
+`re-search-backward' does.  When the argument LOOKING-AT is non-nil,
+it should imitate the function `looking-at'.  In case of a successful
+search, the function should return non-nil, move point, and set
+match-data appropriately.")
+
 (defvar outline-mode-prefix-map
   (let ((map (make-sparse-keymap)))
     (define-key map "@" 'outline-mark-subtree)
@@ -233,7 +245,8 @@ outline-mode-map
 (defvar outline-font-lock-keywords
   '(
     ;; Highlight headings according to the level.
-    (eval . (list (concat "^\\(?:" outline-regexp "\\).*")
+    (eval . (list (or outline-search-function
+                      (concat "^\\(?:" outline-regexp "\\).*"))
                   0 '(if outline-minor-mode
                          (if outline-minor-mode-highlight
                              (list 'face (outline-font-lock-face)))
@@ -366,7 +379,9 @@ outline-font-lock-face
   "Return one of `outline-font-lock-faces' for current level."
   (save-excursion
     (goto-char (match-beginning 0))
-    (looking-at outline-regexp)
+    (if outline-search-function
+        (funcall outline-search-function nil nil nil t)
+      (looking-at outline-regexp))
     (aref outline-font-lock-faces
           (% (1- (funcall outline-level))
              (length outline-font-lock-faces)))))
@@ -474,8 +489,11 @@ outline-minor-mode-highlight-buffer
   ;; Fallback to overlays when font-lock is unsupported.
   (save-excursion
     (goto-char (point-min))
-    (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
-      (while (re-search-forward regexp nil t)
+    (let ((regexp (unless outline-search-function
+                    (concat "^\\(?:" outline-regexp "\\).*$"))))
+      (while (if outline-search-function
+                 (funcall outline-search-function)
+               (re-search-forward regexp nil t))
         (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
           (overlay-put overlay 'outline-highlight t)
           ;; FIXME: Is it possible to override all underlying face attributes?
@@ -592,26 +610,34 @@ outline-next-preface
   "Skip forward to just before the next heading line.
 If there's no following heading line, stop before the newline
 at the end of the buffer."
-  (if (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
-			 nil 'move)
-      (goto-char (match-beginning 0)))
-  (if (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
-      (forward-char -1)))
+  (when (if outline-search-function
+            (funcall outline-search-function nil t)
+          (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
+			     nil 'move))
+    (goto-char (match-beginning 0))
+    ;; Compensate "\n" from the beginning of regexp
+    (when (and outline-search-function (not (bobp))) (forward-char -1)))
+  (when (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
+    (forward-char -1)))
 
 (defun outline-next-heading ()
   "Move to the next (possibly invisible) heading line."
   (interactive)
   ;; Make sure we don't match the heading we're at.
-  (if (and (bolp) (not (eobp))) (forward-char 1))
-  (if (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move)
-      (goto-char (match-beginning 0))))
+  (when (and (bolp) (not (eobp))) (forward-char 1))
+  (when (if outline-search-function
+            (funcall outline-search-function nil t)
+          (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
+			     nil 'move))
+    (goto-char (match-beginning 0))))
 
 (defun outline-previous-heading ()
   "Move to the previous (possibly invisible) heading line."
   (interactive)
-  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-		      nil 'move))
+  (if outline-search-function
+      (funcall outline-search-function nil t t)
+    (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+		        nil 'move)))
 
 (defsubst outline-invisible-p (&optional pos)
   "Non-nil if the character after POS has outline invisible property.
@@ -628,8 +654,10 @@ outline-back-to-heading
       (let (found)
 	(save-excursion
 	  (while (not found)
-	    (or (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-				    nil t)
+	    (or (if outline-search-function
+                    (funcall outline-search-function nil nil t)
+                  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+				      nil t))
                 (signal 'outline-before-first-heading nil))
 	    (setq found (and (or invisible-ok (not (outline-invisible-p)))
 			     (point)))))
@@ -642,7 +670,9 @@ outline-on-heading-p
   (save-excursion
     (beginning-of-line)
     (and (bolp) (or invisible-ok (not (outline-invisible-p)))
-	 (looking-at outline-regexp))))
+	 (if outline-search-function
+             (funcall outline-search-function nil nil nil t)
+           (looking-at outline-regexp)))))
 
 (defun outline-insert-heading ()
   "Insert a new heading at same depth at point."
@@ -754,7 +784,9 @@ outline-demote
 		      (while (and (progn (outline-next-heading) (not (eobp)))
 				  (<= (funcall outline-level) level))))
 		    (unless (eobp)
-		      (looking-at outline-regexp)
+		      (if outline-search-function
+                          (funcall outline-search-function nil nil nil t)
+                        (looking-at outline-regexp))
 		      (match-string-no-properties 0))))
                 ;; Bummer!! There is no higher-level heading in the buffer.
                 (outline-invent-heading head nil))))
@@ -805,7 +837,9 @@ outline-map-region
   (save-excursion
     (setq end (copy-marker end))
     (goto-char beg)
-    (when (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t)
+    (when (if outline-search-function
+              (funcall outline-search-function end)
+            (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t))
       (goto-char (match-beginning 0))
       (funcall fun)
       (while (and (progn
@@ -873,21 +907,23 @@ outline-next-visible-heading
   (if (< arg 0)
       (beginning-of-line)
     (end-of-line))
-  (let (found-heading-p)
+  (let ((regexp (unless outline-search-function
+                  (concat "^\\(?:" outline-regexp "\\)")))
+        found-heading-p)
     (while (and (not (bobp)) (< arg 0))
       (while (and (not (bobp))
 		  (setq found-heading-p
-			(re-search-backward
-			 (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move))
+			(if outline-search-function
+                            (funcall outline-search-function nil t t)
+                          (re-search-backward regexp nil 'move)))
 		  (outline-invisible-p)))
       (setq arg (1+ arg)))
     (while (and (not (eobp)) (> arg 0))
       (while (and (not (eobp))
 		  (setq found-heading-p
-			(re-search-forward
-			 (concat "^\\(?:" outline-regexp "\\)")
-			 nil 'move))
+			(if outline-search-function
+                            (funcall outline-search-function nil t)
+                          (re-search-forward regexp nil 'move)))
 		  (outline-invisible-p (match-beginning 0))))
       (setq arg (1- arg)))
     (if found-heading-p (beginning-of-line))))
@@ -1107,8 +1143,11 @@ outline-hide-sublevels
   (interactive (list
 		(cond
 		 (current-prefix-arg (prefix-numeric-value current-prefix-arg))
-		 ((save-excursion (beginning-of-line)
-				  (looking-at outline-regexp))
+		 ((save-excursion
+                    (beginning-of-line)
+		    (if outline-search-function
+                        (funcall outline-search-function nil nil nil t)
+                      (looking-at outline-regexp)))
 		  (funcall outline-level))
 		 (t 1))))
   (if (< levels 1)
@@ -1255,7 +1294,9 @@ outline-up-heading
 	  (setq level (funcall outline-level)))
 	(setq start-level level))
       (setq arg (- arg 1))))
-  (looking-at outline-regexp))
+  (if outline-search-function
+      (funcall outline-search-function nil nil nil t)
+    (looking-at outline-regexp)))
 
 (defun outline-forward-same-level (arg)
   "Move forward to the ARG'th subheading at same level as this one.
@@ -1313,6 +1354,51 @@ outline-get-last-sibling
       (if (< (funcall outline-level) level)
 	  nil
         (point)))))
+
+
+;;; Search text-property for outline headings
+
+;;;###autoload
+(defun outline-search-level (&optional bound move backward looking-at)
+  "Search for the next text property `outline-level'.
+The arguments are the same as in `outline-search-text-property',
+except the hard-coded property name `outline-level'.
+This function is intended to be used in `outline-search-function'."
+  (outline-search-text-property 'outline-level nil bound move backward looking-at))
+
+(defun outline-search-text-property (property &optional value bound move backward looking-at)
+  "Search for the next text property PROPERTY with VALUE.
+The rest of arguments are described in `outline-search-function'."
+  (if looking-at
+      (when (if value (eq (get-text-property (point) property) value)
+              (get-text-property (point) property))
+        (set-match-data (list (pos-bol) (pos-eol)))
+        t)
+    ;; Go to the end when in the middle of heading
+    (when (and (not backward)
+               (if value (eq (get-text-property (point) property) value)
+                 (get-text-property (point) property))
+               (not (or (bobp)
+                        (not (if value
+                                 (eq (get-text-property (1- (point)) property) value)
+                               (get-text-property (1- (point)) property))))))
+      (goto-char (pos-eol)))
+    (let ((prop-match (if backward
+                          (text-property-search-backward property value (and value t))
+                        (text-property-search-forward property value (and value t)))))
+      (if prop-match
+          (let ((beg (prop-match-beginning prop-match))
+                (end (prop-match-end prop-match)))
+            (if (or (null bound) (<= end bound))
+                (progn (goto-char end)
+                       (goto-char (pos-eol))
+                       (set-match-data (list beg (point)))
+                       t)
+              (when move (goto-char bound))
+              nil))
+        (when move (goto-char (or bound (point-max))))
+        nil))))
+
 
 (defun outline-headers-as-kill (beg end)
   "Save the visible outline headers between BEG and END to the kill ring.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#53981; Package emacs. (Mon, 21 Nov 2022 07:59:01 GMT) Full text and rfc822 format available.

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

From: Juri Linkov <juri <at> linkov.net>
To: 53981 <at> debbugs.gnu.org
Cc: mail <at> daniel-mendler.de, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#53981: 28.0.91; shortdoc: Add support for outline-minor-mode
Date: Mon, 21 Nov 2022 09:57:02 +0200
close 53981 29.0.50
thanks

> Here is a more tested patch that works in apropos and shortdoc.
>
> Also tested for group outlines in the Completions buffer with:
>
> ```
> (add-hook 'completion-list-mode-hook
>           (lambda ()
>             (setq-local
>              outline-search-function
>              (lambda (&optional bound move backward looking-at)
>                (outline-search-text-property
>                 'face 'completions-group-separator
>                 bound move backward looking-at))
>              outline-level (lambda () 1))
> ```

Now pushed to master and closed.




bug marked as fixed in version 29.0.50, send any further explanations to 53981 <at> debbugs.gnu.org and Daniel Mendler <mail <at> daniel-mendler.de> Request was from Juri Linkov <juri <at> linkov.net> to control <at> debbugs.gnu.org. (Mon, 21 Nov 2022 07:59:02 GMT) Full text and rfc822 format available.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Mon, 19 Dec 2022 12:24:12 GMT) Full text and rfc822 format available.

This bug report was last modified 1 year and 122 days ago.

Previous Next


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