bug#81512: [PATCH] Add command `outline-occur'
Roi Martin <[email protected]> Fri, 31 Jul 2026 02:25:21 +0200
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <[email protected]> |
I attach the v3 of the patch, which includes the following changes: - Rename `display-buffer-bound-alist' to `display-buffer-internal-alist'. - Remove the warning against setting it buffer-locally or globally. - Reword documentation to avoid limiting the use of the variable to let-binding it. Juri Linkov <[email protected]> writes: >> But I'm not 100% convinced >> >> - that we really never want to set this buffer-locally or globally > > Indeed, I see no reason to warn against setting it > buffer-locally or globally by a package. I removed the warning and reworded the documentation accordingly. >> - and we consequently should name it differently. > > Maybe by naming it as internal: display-buffer--alist? > Or if it's too confusing, then maybe display-buffer-internal-alist? I renamed the variable to `display-buffer-internal-alist'. Roi
v3-0001-Add-outline-occur-command-and-display-buffer-inte.patch
(text/x-patch, 11.9 KB)
From 2ad162b0b5670af721c546c0f915ae2fc9752a55 Mon Sep 17 00:00:00 2001 From: Roi Martin <[email protected]> Date: Tue, 28 Jul 2026 20:19:03 +0200 Subject: [PATCH v3] Add `outline-occur' command and `display-buffer-internal-alist' variable Add the `outline-occur' command that allows navigating the current buffer's outline using Occur. Also add the `display-buffer-internal-alist' variable, which Elisp code may set to specify conditional actions for nested `display-buffer' calls. * doc/lispref/windows.texi (Choosing Window): Document `display-buffer-internal-alist' variable. * etc/NEWS: Add `outline-occur' and `display-buffer-internal-alist' entries. * lisp/outline.el (outline-mode-prefix-map): Bind `outline-occur'. (outline-mode-menu-bar-map): Add `outline-occur' menu item. (outline-occur-regexp): New buffer-local variable. (outline-occur): New command. * lisp/replace.el (occur-1): Call `display-buffer' with category. * lisp/window.el (display-buffer-alist): Update docstring. (display-buffer-internal-alist): New variable. (display-buffer): Handle `display-buffer-internal-alist'. * test/lisp/outline-resources/outline.txt: New test file. * test/lisp/outline-tests.el: New test suite. (Bug#81512) Co-authored-by: Martin Rudalics <[email protected]> Co-authored-by: Juri Linkov <[email protected]> --- doc/lispref/windows.texi | 10 ++++ etc/NEWS | 12 +++++ lisp/outline.el | 31 ++++++++++- lisp/replace.el | 2 +- lisp/window.el | 29 +++++++++-- test/lisp/outline-resources/outline.txt | 2 + test/lisp/outline-tests.el | 69 +++++++++++++++++++++++++ 7 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 test/lisp/outline-resources/outline.txt create mode 100644 test/lisp/outline-tests.el diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index ddc8524ddb6b..d32d5e1b72e5 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -3119,6 +3119,9 @@ Choosing Window @item The user option @code{display-buffer-alist}. +@item +The variable @code{display-buffer-internal-alist}. + @item The @var{action} argument. @@ -3181,6 +3184,13 @@ Choosing Window default value is an empty display action, i.e., @w{@code{(nil . nil)}}. @end defvar +@defvar display-buffer-internal-alist +The value of this variable is an alist mapping conditions to display +actions (@pxref{display-buffer-alist}). Elisp code may set it to +specify conditional actions for nested @code{display-buffer} calls. +@end defvar + +@anchor{display-buffer-alist} @defopt display-buffer-alist The value of this option is an alist mapping conditions to display actions. Each condition is passed to @code{buffer-match-p} diff --git a/etc/NEWS b/etc/NEWS index 92033851c74c..a8701c896513 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -188,6 +188,13 @@ with named choices for the predefined search functions ('outline-search-from-regexp' and 'outline-search-level') as well as the default nil and arbitrary user functions. +--- +*** New command 'outline-occur'. +It allows users to navigate the current buffer's outline using Occur. +The 'outline-regexp' variable is used to find the beginning of the +outline headings. The 'outline-occur-regexp' buffer-local variable +allows overriding this regexp. It is bound to 'M-o'. + ** Newsticker --- @@ -296,6 +303,11 @@ how to analyze the arguments of a function by declaring the specification of each argument, rather than implementing an analyzer function as you would with 'elisp-scope-define-function-analyzer'. ++++ +** New variable 'display-buffer-internal-alist' +Elisp code may set this variable to specify conditional actions for +nested 'display-buffer' calls. + * Changes in Emacs 32.1 on Non-Free Operating Systems diff --git a/lisp/outline.el b/lisp/outline.el index 4b4f3c2d520e..7945863dec33 100644 --- a/lisp/outline.el +++ b/lisp/outline.el @@ -107,7 +107,8 @@ outline-mode-prefix-map "/ h" #'outline-hide-by-heading-regexp "C-<" #'outline-promote "C->" #'outline-demote - "RET" #'outline-insert-heading) + "RET" #'outline-insert-heading + "M-o" #'outline-occur) (defvar outline-mode-menu-bar-map (let ((map (make-sparse-keymap))) @@ -148,6 +149,9 @@ outline-mode-menu-bar-map :help "Show all of the text in the buffer")) (define-key map [headings] (cons "Headings" (make-sparse-keymap "Headings"))) + (define-key map [headings outline-occur] + '(menu-item "Show in Occur" outline-occur + :help "Navigate the buffer's outline using Occur")) (define-key map [headings demote-subtree] '(menu-item "Demote Subtree" outline-demote :help "Demote headings lower down the tree")) @@ -2179,6 +2183,31 @@ outline-editing-repeat-map "C-<" #'outline-promote "<" #'outline-promote) + +;;; Occur outline navigation + +(defvar-local outline-occur-regexp nil + "Regexp used by `outline-occur' to override `outline-regexp'. +Matches the beginning of the outline headings. Any line whose beginning +matches this regexp is considered to start a heading. As Outline mode +does with `outline-regexp', `outline-occur' only checks this regexp at +the start of a line, so the regexp need not start with `^'.") + +(defun outline-occur () + "Navigate the current buffer's outline using `occur'. +The `outline-regexp' variable is used to find the beginning of the +outline headings. The `outline-occur-regexp' buffer-local variable +allows overriding this regexp." + (interactive) + (if-let* ((regexp (or outline-occur-regexp outline-regexp))) + (let ((display-buffer-internal-alist + (append '(((category . occur) + (display-buffer-pop-up-window) + (post-command-select-window . t))) + display-buffer-internal-alist))) + (occur (concat "^\\(?:" regexp "\\)"))) + (user-error "No outline regexp defined"))) + (provide 'outline) (provide 'noutline) diff --git a/lisp/replace.el b/lisp/replace.el index e7407869ca27..be8b4d1baffb 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -2062,7 +2062,7 @@ occur-1 (setq occur-revert-arguments (list regexp nlines bufs)) (if (= count 0) (kill-buffer occur-buf) - (display-buffer occur-buf) + (display-buffer occur-buf '(nil (category . occur))) (when occur--final-pos (set-window-point (get-buffer-window occur-buf 'all-frames) diff --git a/lisp/window.el b/lisp/window.el index 06128cf0483c..624aa1526234 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -8084,8 +8084,9 @@ display-buffer-overriding-action (defcustom display-buffer-alist nil "Alist of user-defined conditional actions for `display-buffer'. -Its value takes effect before processing the ACTION argument of -`display-buffer' and before `display-buffer-base-action' and +Its value takes effect before processing +`display-buffer-internal-alist', the ACTION argument of +`display-buffer', `display-buffer-base-action' and `display-buffer-fallback-action', but after `display-buffer-overriding-action', which see. @@ -8110,6 +8111,21 @@ display-buffer-alist :version "24.1" :group 'windows) +(defvar display-buffer-internal-alist nil + "Alist of internally defined conditional actions for `display-buffer'. +Its value takes effect before processing the ACTION argument of +`display-buffer' and before `display-buffer-base-action' and +`display-buffer-fallback-action', but after +`display-buffer-overriding-action' and `display-buffer-alist', which +see. + +Elisp code may set this variable to specify conditional actions for +nested `display-buffer' calls. + +If non-nil, this is an alist of elements (CONDITION . ACTION) like +`display-buffer-alist'.") +(put 'display-buffer-internal-alist 'risky-local-variable t) + (defcustom display-buffer-base-action '(nil . nil) "User-specified default action for `display-buffer'. This is the default action used by `display-buffer' if no other @@ -8398,6 +8414,9 @@ display-buffer (display-buffer-assq-regexp buf-name display-buffer-alist action)) (special-action (display-buffer--special-action buffer)) + (internal-action + (display-buffer-assq-regexp + buf-name display-buffer-internal-alist action)) ;; Extra actions from the arguments to this function: (extra-action (cons nil (append (if inhibit-same-window @@ -8405,9 +8424,9 @@ display-buffer (if frame `((reusable-frames . ,frame)))))) ;; Construct action function list and action alist. - (actions (list display-buffer-overriding-action - user-action special-action action extra-action - display-buffer-base-action + (actions (list display-buffer-overriding-action user-action + special-action internal-action action + extra-action display-buffer-base-action display-buffer-fallback-action)) (functions (apply #'append (mapcar (lambda (x) diff --git a/test/lisp/outline-resources/outline.txt b/test/lisp/outline-resources/outline.txt new file mode 100644 index 000000000000..f3704f9438a0 --- /dev/null +++ b/test/lisp/outline-resources/outline.txt @@ -0,0 +1,2 @@ +* Star heading +- Dash heading diff --git a/test/lisp/outline-tests.el b/test/lisp/outline-tests.el new file mode 100644 index 000000000000..da87a0d1171f --- /dev/null +++ b/test/lisp/outline-tests.el @@ -0,0 +1,69 @@ +;;; outline-tests.el --- ERT tests for outline.el -*- lexical-binding: t -*- + +;; Copyright (C) 2026 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Tests for the `outline' feature. + +;;; Code: + +(require 'ert) +(require 'ert-x) +(require 'outline) + +(ert-deftest outline-tests--outline-occur () + "Test the `outline-occur' function with `outline-regexp'." + (let ((test-file (ert-resource-file "outline.txt"))) + (with-temp-buffer + (insert-file-contents test-file) + (setq-local outline-regexp "\\*" + outline-occur-regexp nil) + (outline-occur)) + (with-current-buffer (get-buffer "*Occur*") + (goto-char (point-min)) + (should (search-forward "* Star heading")) + (goto-char (point-min)) + (should-error (search-forward "- Dash heading"))))) + +(ert-deftest outline-tests--outline-occur-override () + "Test the `outline-occur' function with `outline-occur-regexp'." + (let ((test-file (ert-resource-file "outline.txt"))) + (with-temp-buffer + (insert-file-contents test-file) + (setq-local outline-regexp "\\*" + outline-occur-regexp "-") + (outline-occur)) + (with-current-buffer (get-buffer "*Occur*") + (goto-char (point-min)) + (should-error (search-forward "* Star heading")) + (goto-char (point-min)) + (should (search-forward "- Dash heading"))))) + +(ert-deftest outline-tests--outline-occur-undefined-regexp () + "Test the `outline-occur' function with undefined regexp." + (let ((test-file (ert-resource-file "outline.txt"))) + (with-temp-buffer + (insert-file-contents test-file) + (setq-local outline-regexp nil + outline-occur-regexp nil) + (should-error (outline-occur))))) + +(provide 'outline-tests) + +;;; outline-tests.el ends here -- 2.55.0