JV wrote:

Revised patch set attached.
There was a mistake in the second patch (a "save-excursion" that should be a "save-match-data"). Please pardon the noise. Updated patches attached.
From 7be07019f804aa5d7be857e03b664c68cafaf924 Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Sun, 7 Jun 2026 22:12:26 -0400
Subject: [PATCH 1/4] org: Improve org-in-block-p

* lisp/org.el (org-in-block-p): Replace regexp-based approach with
element API by renaming and generalizing existing function
'org-in-src-block-p'.  This adds flexibility and is more reliable with
nested blocks.
(org-in-src-block-p): Preserve this function name as a thin wrapper
around the improved 'org-in-block-p'.
* lisp/org-element.el (org-element-block-elements): Add new constant.
* lisp/org-list.el: Update arg names in declare-function.
* lisp/org-footnote.el: Update arg names in declare-function.
* testing/lisp/test-org.el (test-org/in-block-p): Add tests for
'org-in-block-p'.
---
 lisp/org-element.el      |   6 ++
 lisp/org-footnote.el     |   2 +-
 lisp/org-list.el         |   2 +-
 lisp/org.el              |  73 ++++++++++++-----------
 testing/lisp/test-org.el | 125 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 171 insertions(+), 37 deletions(-)

diff --git a/lisp/org-element.el b/lisp/org-element.el
index 58c65ad08..6a5adb534 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -350,6 +350,12 @@ specially in `org-element--object-lex'.")
              section table-row)
   "List of paragraph-level node types that cannot have affiliated keywords.")
 
+(defconst org-element-block-elements
+  (seq-filter (lambda (type)
+                (string-suffix-p "-block" (symbol-name type)))
+              org-element-all-elements)
+  "List of block element types.")
+
 (defconst org-element-affiliated-keywords
   '("CAPTION" "DATA" "HEADER" "HEADERS" "LABEL" "NAME" "PLOT" "RESNAME" "RESULT"
     "RESULTS" "SOURCE" "SRCNAME" "TBLNAME")
diff --git a/lisp/org-footnote.el b/lisp/org-footnote.el
index 5603adfe6..bc31485f6 100644
--- a/lisp/org-footnote.el
+++ b/lisp/org-footnote.el
@@ -51,7 +51,7 @@
 (declare-function org-element-type-p "org-element-ast" (node types))
 (declare-function org-end-of-subtree "org"  (&optional invisible-ok to-heading element))
 (declare-function org-fill-paragraph "org" (&optional justify region))
-(declare-function org-in-block-p "org" (names))
+(declare-function org-in-block-p "org" (&optional types inside element))
 (declare-function org-in-verbatim-emphasis "org" ())
 (declare-function org-inside-LaTeX-fragment-p "org" (&optional element))
 (declare-function org-inside-latex-macro-p "org" ())
diff --git a/lisp/org-list.el b/lisp/org-list.el
index 8605c6f08..c62aa88c9 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -139,7 +139,7 @@
 (declare-function org-export-with-backend "ox" (backend data &optional contents info))
 (declare-function org-fix-tags-on-the-fly "org" ())
 (declare-function org-get-todo-state "org" ())
-(declare-function org-in-block-p "org" (names))
+(declare-function org-in-block-p "org" (&optional types inside element))
 (declare-function org-inlinetask-goto-beginning "org-inlinetask" ())
 (declare-function org-inlinetask-goto-end "org-inlinetask" ())
 (declare-function org-inlinetask-in-task-p "org-inlinetask" ())
diff --git a/lisp/org.el b/lisp/org.el
index faecd2b93..2a7e2630d 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -235,6 +235,7 @@ This regular expression matches these groups:
 (declare-function org-toggle-archive-tag "org-archive" (&optional find-done))
 
 (defvar org-agenda-buffer-name)
+(defvar org-element-block-elements)
 (defvar org-element-paragraph-separate)
 (defvar org-element-cache-map-continue-from)
 (defvar org-element--timestamp-regexp)
@@ -19334,24 +19335,48 @@ With prefix arg UNCOMPILED, load the uncompiled versions."
     (setq s (replace-match "\\vert" t t s)))
   s)
 
-(defun org-in-src-block-p (&optional inside element)
-  "Return t when point is at a source block element.
-When INSIDE is non-nil, return t only when point is between #+BEGIN_SRC
-and #+END_SRC lines.
+(defun org-in-block-p (&optional types inside element)
+  "Return non-nil when point is in a block element.
+
+Block TYPES may be constrained using a type symbol, a name string,
+or a list including either.  Name strings are mapped to type symbols
+for defined block types and compared as a `special-block' :type
+property otherwise.
+
+When INSIDE is non-nil, return affirmatively only when point is
+between #+BEGIN and #+END lines.
 
 Note that affiliated keywords and blank lines after are considered a
 part of a source block.
 
 When ELEMENT is provided, it is considered to be element at point."
-  (save-match-data (setq element (or element (org-element-at-point))))
-  (when (org-element-type-p element 'src-block)
-    (or (not inside)
-        (not (or (<= (line-beginning-position)
-                  (org-element-post-affiliated element))
-               (>= (line-end-position)
-                  (org-with-point-at (org-element-end element)
-                    (skip-chars-backward " \t\n\r")
-                    (point))))))))
+  (when-let*
+      ((types (or (ensure-list types) org-element-block-elements))
+       (element (or element (save-match-data (org-element-at-point))))
+       (match (org-element-lineage-map element
+                  `(when-let*
+                       ((type (org-element-type node))
+                        (name (or (org-element-property :type node) ; special-block name
+                                  (string-remove-suffix "-block" (symbol-name type))))
+                        (found (seq-intersection (list type name) ',types)))
+                     (cons node (car found)))
+                org-element-block-elements t t))
+       (block (car match)) ; matched block element
+       (query (cdr match)) ; type/name query that matched it
+       (_ (or (not inside)
+              (not (or (<= (line-beginning-position)
+                           (org-element-post-affiliated block))
+                       (>= (line-end-position)
+                           (org-with-point-at (org-element-end block)
+                             (skip-chars-backward " \t\n\r")
+                             (point))))))))
+    ;; Return matched block type/name for legacy compatibility.
+    query))
+
+(defun org-in-src-block-p (&optional inside element)
+  "Return non-nil when point is at a source block element.
+This simply wraps `org-in-block-p' for type \\='src-block."
+  (org-in-block-p 'src-block inside element))
 
 (defun org-context ()
   "Return a list of contexts of the current cursor position.
@@ -19505,28 +19530,6 @@ position before START-RE (resp. after END-RE)."
 	     ;; Return value.
 	     (cons beg end))))))
 
-(defun org-in-block-p (names)
-  "Non-nil when point belongs to a block whose name belongs to NAMES.
-
-NAMES is a list of strings containing names of blocks.
-
-Return first block name matched, or nil.  Beware that in case of
-nested blocks, the returned name may not belong to the closest
-block from point."
-  (save-match-data
-    (catch 'exit
-      (let ((case-fold-search t)
-	    (lim-up (save-excursion (outline-previous-heading)))
-	    (lim-down (save-excursion (outline-next-heading))))
-	(dolist (name names)
-	  (let ((n (regexp-quote name)))
-	    (when (org-between-regexps-p
-		   (concat "^[ \t]*#\\+begin_" n)
-		   (concat "^[ \t]*#\\+end_" n)
-		   lim-up lim-down)
-	      (throw 'exit n)))))
-      nil)))
-
 ;; Defined in org-agenda.el
 (defvar org-agenda-restrict)
 (defvar org-agenda-restrict-begin)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 68942ff3e..09b6ca360 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -6098,6 +6098,131 @@ Text.
        (org-next-block 1 "^[ \t]*#\\+BEGIN_QUOTE")
        (looking-at "#\\+begin_quote")))))
 
+(ert-deftest test-org/in-block-p ()
+  "Test `org-in-block-p' specifications."
+  ;; Not in block
+  (should-not
+   (org-test-with-temp-text
+       "<point>\n#+NAME: A\n#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p)))
+  ;; Without inside flag
+  ;;; On affiliated line
+  (should
+   (org-test-with-temp-text
+       "\n<point>#+NAME: A\n#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p)))
+  ;;; On begin line
+  (should
+   (org-test-with-temp-text
+       "\n#+NAME: A\n<point>#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p)))
+  ;;; Inside block
+  (should
+   (org-test-with-temp-text
+       "\n#+NAME: A\n#+BEGIN_EXAMPLE\n<point>B\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p)))
+  ;;; On end line
+  (should
+   (org-test-with-temp-text
+       "\n#+NAME: A\n#+BEGIN_EXAMPLE\nB\n<point>#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p)))
+  ;;; In post blank
+  (should
+   (org-test-with-temp-text
+       "\n#+NAME: A\n#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n<point>\nC\n"
+     (org-in-block-p)))
+  ;; With inside flag
+  ;;; On affiliated line
+  (should-not
+   (org-test-with-temp-text
+       "\n<point>#+NAME: A\n#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p nil t)))
+  ;;; On begin line
+  (should-not
+   (org-test-with-temp-text
+       "\n#+NAME: A\n<point>#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p nil t)))
+  ;;; Inside block
+  (should
+   (org-test-with-temp-text
+       "\n#+NAME: A\n#+BEGIN_EXAMPLE\n<point>B\n#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p nil t)))
+  ;;; On end line
+  (should-not
+   (org-test-with-temp-text
+       "\n#+NAME: A\n#+BEGIN_EXAMPLE\nB\n<point>#+END_EXAMPLE\n\nC\n"
+     (org-in-block-p nil t)))
+  ;;; In post blank
+  (should-not
+   (org-test-with-temp-text
+       "\n#+NAME: A\n#+BEGIN_EXAMPLE\nB\n#+END_EXAMPLE\n<point>\nC\n"
+     (org-in-block-p nil t)))
+  ;; Nested blocks
+  ;;; Block within greater block
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_QUOTE\n#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n#+END_QUOTE\n"
+     (org-in-block-p 'example-block)))
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_QUOTE\n#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n#+END_QUOTE\n"
+     (org-in-block-p 'quote-block)))
+  ;;; Block within lesser block
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_SRC\n#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n#+END_SRC\n"
+     (org-in-block-p 'src-block)))
+  (should-not
+   (org-test-with-temp-text
+       "#+BEGIN_SRC\n#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n#+END_SRC\n"
+     (org-in-block-p 'quote-block)))
+  ;; Type selectors
+  ;;; Symbols
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n"
+     (org-in-block-p 'example-block)))
+  ;; Name strings
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n"
+     (org-in-block-p "example")))
+  ;; Special (named) blocks
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_abcd\n<point>A\n#+END_abcd\n"
+     (org-in-block-p "abcd")))
+  (should-not
+   (org-test-with-temp-text
+       "#+BEGIN_abcd\n<point>A\n#+END_abcd\n"
+     (org-in-block-p 'abcd-block)))
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_abcd\n<point>A\n#+END_abcd\n"
+     (org-in-block-p 'special-block)))
+  ;; Dynamic blocks
+  (should-not
+   (org-test-with-temp-text
+       "#+BEGIN: abcd\n<point>A\n#+END\n"
+     (org-in-block-p "abcd")))
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN: abcd\n<point>A\n#+END\n"
+     (org-in-block-p 'dynamic-block)))
+  ;;; Multiple type selectors
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_EXAMPLE\n<point>A\n#+END_EXAMPLE\n"
+     (org-in-block-p '(example-block "src" "abcd"))))
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_SRC\n<point>A\n#+END_SRC\n"
+     (org-in-block-p '(example-block "src" "abcd"))))
+  (should
+   (org-test-with-temp-text
+       "#+BEGIN_abcd\n<point>A\n#+END_abcd\n"
+     (org-in-block-p '(example-block "src" "abcd")))))
+
 
 ;;; Outline structure
 
-- 
2.55.0

From 5db829d1010a79ef510e51786fb02cf63e60b44d Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Mon, 8 Jun 2026 21:16:45 -0400
Subject: [PATCH 2/4] Add faces for structural syntax elements

This adds an identifying face and fontification for syntax elements
that convey document outline structure: headline stars, list bullets,
list indentation.  Structure faces inherit from a newly introduced
common ancestor.  The new faces are defined without any properties,
which adds useful semantic information but reserves any visual
customization to the user.

Fontification of indentation is context-sensitive: it is applied only
within a plain list.  This is straightforward with one caveat: a
buffer change might either create or end an outermost plain list
around indented content.  This would change the syntactic significance
of indentation, and thus requires the fontification region to be
extended.

* lisp/org-faces.el (org-structure): Add new face.
(org-headline-stars, org-list-bullet, org-list-indent): Add new faces,
inheriting from org-structure.
(org-hide): Modify face to inherit from org-structure.  This face is
used for structural syntax, even if made invisible.  Importantly, the
org-indent face inherits from org-hide.

* lisp/org-list.el (org-in-list-context-p,
org-list--forbidden-block-types): Add function to determine whether
plain list indentation syntax applies, and a constant to support this.

* lisp/org.el (org-fontify-extend-region-indentation): Add function to
extend region to absorb subsequent indented lines.  This ensures
indented lines are refontified when a change creates or ends a plain
list that would include them.  This function is set as a
font-lock-extend-region-functions hook.
(org-set-font-lock-defaults): Apply org-list-bullet and
org-list-indent faces.
(org-get-level-face): Apply org-headline-stars face.

* etc/ORG-NEWS: Describe the new outline structure faces.
---
 etc/ORG-NEWS      | 18 ++++++++++++++++++
 lisp/org-faces.el | 27 ++++++++++++++++++++++++++-
 lisp/org-list.el  | 26 ++++++++++++++++++++++++++
 lisp/org.el       | 41 ++++++++++++++++++++++++++++++++++++-----
 4 files changed, 106 insertions(+), 6 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 9ea139fb5..90522a4d9 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -172,6 +172,24 @@ Presentations written with Beamer and ltx-talk are mostly portable,
 except that Beamer theme information is ignored when using ltx-talk
 and some environments listed in the manual are not supported.
 
+*** New faces and fontification for document outline structure
+There are now identifying faces applied to syntax elements that convey
+document outline structure: headline stars (=org-headline-stars=),
+list bullets (=org-list-bullet=), and indentation within plain lists
+(=org-list-indent=).  These faces, as well as existing faces
+=org-hide= and =org-indent= (via =org-hide=), now inherit from a newly
+introduced common ancestor, =org-structure=.
+
+This enables more flexible control over how outline structure is
+rendered.  As a motivating example: customizing =org-structure= to
+use a fixed-pitch font will ensure consistent alignment of outline
+elements while using a variable-pitch face for default text.
+
+A note to theme authors: if your theme customizes =org-hide= or
+=org-indent=, it is /strongly/ recommended that you ensure these
+inherit from =org-structure=.  The same is true for the newly
+introduced faces noted above.
+
 ** New and changed options
 
 # Changes dealing with changing default values of customizations,
diff --git a/lisp/org-faces.el b/lisp/org-faces.el
index b0f5ef9d7..3b40abc2c 100644
--- a/lisp/org-faces.el
+++ b/lisp/org-faces.el
@@ -41,8 +41,33 @@
   "Face used for default text."
   :group 'org-faces)
 
+(defface org-structure
+  '((t ()))
+  "Face used for syntax that defines outline structure."
+  :group 'org-faces
+  :package-version '(Org . "10.0"))
+
+(defface org-headline-stars
+  '((t (:inherit org-structure)))
+  "Face used for stars in headlines."
+  :group 'org-faces
+  :package-version '(Org . "10.0"))
+
+(defface org-list-bullet
+  '((t (:inherit org-structure)))
+  "Face used for bullets or numerals in plain lists."
+  :group 'org-faces
+  :package-version '(Org . "10.0"))
+
+(defface org-list-indent
+  '((t (:inherit org-structure)))
+  "Face used for significant leading whitespace in plain lists."
+  :group 'org-faces
+  :package-version '(Org . "10.0"))
+
 (defface org-hide
-  '((((background light)) (:foreground "white"))
+  '((default :inherit org-structure)
+    (((background light)) (:foreground "white"))
     (((background dark)) (:foreground "black")))
   "Face used to hide leading stars in headlines.
 The foreground color of this face should be equal to the background
diff --git a/lisp/org-list.el b/lisp/org-list.el
index c62aa88c9..f38323798 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -95,6 +95,8 @@
 (defvar org-done-keywords)
 (defvar org-drawer-regexp)
 (defvar org-element-all-objects)
+(defvar org-element-block-elements)
+(defvar org-element-greater-elements)
 (defvar org-inhibit-startup)
 (defvar org-loop-over-headlines-in-active-region)
 (defvar org-odd-levels-only)
@@ -113,6 +115,7 @@
 (declare-function org-element-context "org-element" (&optional element))
 (declare-function org-element-interpret-data "org-element" (data))
 (declare-function org-element-lineage "org-element-ast" (blob &optional types with-self))
+(declare-function org-element-lineage-map "org-element-ast" (datum fun &optional types with-self first-match))
 (declare-function org-element-macro-interpreter "org-element" (macro ##))
 (declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated no-undefer))
 (declare-function org-element-normalize-string "org-element" (s &optional keep-newlines))
@@ -127,6 +130,7 @@
 (declare-function org-element-parent "org-element-ast" (node))
 (declare-function org-element-put-property "org-element-ast" (node property value))
 (declare-function org-element-set "org-element-ast" (old new &optional keep-props))
+(declare-function org-element-type "org-element-ast" (node &optional anonymous))
 (declare-function org-element-type-p "org-element-ast" (node types))
 (declare-function org-element-update-syntax "org-element" ())
 (declare-function org-end-of-meta-data "org" (&optional full))
@@ -356,6 +360,12 @@ clearly distinguish sub-items in a list."
   "Names of blocks where lists are not allowed.
 Names must be in lower case.")
 
+(with-eval-after-load 'org-element
+  (defconst org-list--forbidden-block-types
+    (seq-difference org-element-block-elements
+                    org-element-greater-elements)
+    "Types of blocks where lists are not allowed."))
+
 
 ;;; Predicates and regexps
 
@@ -422,6 +432,22 @@ group 4: description tag")
   "Is point in a context where lists are allowed?"
   (not (org-in-block-p org-list-forbidden-blocks)))
 
+(defun org-in-list-context-p (&optional element)
+  "Return t if in a plain list and not inside a list-forbidding block.
+This identifies whether indentation is meaningful syntax for ELEMENT."
+  ;; This function should maintain performance suitable for frequently
+  ;; repeated calls by fontify machinery.  Accordingly, it minimizes
+  ;; traversal and calls org-in-block-p only if needed after a faster
+  ;; type check.
+  (let ((element (or element (save-match-data (org-element-at-point)))))
+    (eq 'plain-list
+        (org-element-lineage-map element
+            `(let ((type (org-element-type node)))
+               (or (and (memq type '(item plain-list)) 'plain-list)
+                   (and (memq type org-list--forbidden-block-types)
+                        (org-in-block-p type t node))))
+          nil t t))))
+
 (defun org-in-item-p ()
   "Return item beginning position when in a plain list, nil otherwise."
   (save-excursion
diff --git a/lisp/org.el b/lisp/org.el
index 2a7e2630d..59c5ed98b 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -234,6 +234,7 @@ This regular expression matches these groups:
 (declare-function org-timer-start "org-timer" (&optional offset))
 (declare-function org-toggle-archive-tag "org-archive" (&optional find-done))
 
+(defvar font-lock-end)
 (defvar org-agenda-buffer-name)
 (defvar org-element-block-elements)
 (defvar org-element-paragraph-separate)
@@ -5203,6 +5204,9 @@ The following commands are available:
   (when (and org-tag-faces (not org-tags-special-faces-re))
     ;; tag faces set outside customize.... force initialization.
     (org-set-tag-faces 'org-tag-faces org-tag-faces))
+  ;; Add extend-region hook
+  (add-hook 'font-lock-extend-region-functions
+            #'org-fontify-extend-region-indentation)
   ;; Calc embedded
   (setq-local calc-embedded-open-mode "# ")
   ;; Set syntax table.  Ensure that buffer-local changes to the syntax
@@ -5844,6 +5848,25 @@ by a #."
 	    (add-text-properties closing-start end '(invisible t)))
 	  t)))))
 
+(defun org-fontify-extend-region-indentation ()
+  "Move fontification boundary forward past any indented lines.
+
+If an outermost plain list is either created or ended by a change, it
+alters whether indentation is significant for child contents.  To
+accommodate, extend the fontification region to include any subsequent
+indented lines up to where the next list item begins or where an
+outermost list would necessarily end (a non-indented line or the end
+of the buffer).
+
+When the region should be extended, this sets `font-lock-end' and
+returns non-nil.  See `font-lock-extend-region-functions'."
+  (goto-char font-lock-end)
+  (forward-line)
+  (let ((re (concat "\\S-\\|\\'\\|" (org-item-re))))
+    (unless (looking-at-p re)
+      (while (not (looking-at-p re)) (forward-line))
+      (setq font-lock-end (point)))))
+
 (defun org-fontify-extend-region (beg end _old-len)
   (let ((end (if (progn (goto-char end) (looking-at-p "^[*#]"))
                  (min (point-max) (1+ end))
@@ -6188,8 +6211,10 @@ needs to be inserted at a specific position in the font-lock sequence.")
 	  ;; Description list items
           '("\\(?:^[ \t]*[-+]\\|^[ \t]+[*]\\)[ \t]+\\(.*?[ \t]+::\\)\\([ \t]+\\|$\\)"
 	    1 'org-list-dt prepend)
-	  ;; Checkboxes
-	  `(,org-list-full-item-re 3 'org-checkbox prepend lax)
+          ;; List bullets and checkboxes
+          `(,org-list-full-item-re
+            (1 'org-list-bullet)
+            (3 'org-checkbox prepend lax))
 	  (when (cdr (assq 'checkbox org-list-automatic-rules))
 	    '("\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]"
 	      (0 (org-get-checkbox-statistics-face) prepend)))
@@ -6225,6 +6250,11 @@ needs to be inserted at a specific position in the font-lock sequence.")
             ;; prepends faces
             '(org-cite-activate))
 	  ;; COMMENT
+          ;; Indentation (applied in plain lists only)
+          '("^[ \t]+"
+            (0 (when (org-in-list-context-p)
+                 'org-list-indent)
+               prepend))
           ;; Apply this last, after all the markup is highlighted, so
           ;; that even "bright" markup will become dim.
 	  (list (format
@@ -6332,10 +6362,11 @@ needs to be inserted at a specific position in the font-lock sequence.")
 	 (org-l (if org-odd-levels-only (1+ (/ org-l0 2)) org-l0))
 	 (org-f (if org-cycle-level-faces
 		    (nth (% (1- org-l) org-n-level-faces) org-level-faces)
-		  (nth (1- (min org-l org-n-level-faces)) org-level-faces))))
+		  (nth (1- (min org-l org-n-level-faces)) org-level-faces)))
+         (org-f-stars (list 'org-headline-stars org-f)))
     (cond
-     ((eq n 1) (if org-hide-leading-stars 'org-hide org-f))
-     ((eq n 2) org-f)
+     ((eq n 1) (if org-hide-leading-stars 'org-hide org-f-stars))
+     ((eq n 2) org-f-stars)
      (t (unless org-level-color-stars-only org-f)))))
 
 (defun org-face-from-face-or-color (context inherit face-or-color)
-- 
2.55.0

From 3f65366cbcd6571bb026069003391eeff39bc825 Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Thu, 13 Aug 2026 13:55:14 -0400
Subject: [PATCH 3/4] Add face for margin of blocks within plain lists

This ensures that blocks in plain lists visually align with their
parent list items, making the outline hierarchy clear.  Specifically,
this causes background color applied to the block to respect the
indentation level of the block #+begin line.

* lisp/org-faces.el (org-block-margin): Add new face.
* lisp/org.el (org-mode): Set background color on org-block-margin
face to be invisible.
(org-find-invisible-color): Rename function since color is now used
as background as well as foreground.
(org-fontify-meta-lines-and-blocks-1): When block #+begin line is
indented, prepend org-block-margin face to each line of the block to
reflect this indentation level.
---
 lisp/org-faces.el | 12 ++++++++++++
 lisp/org.el       | 22 +++++++++++++++++-----
 2 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/lisp/org-faces.el b/lisp/org-faces.el
index 3b40abc2c..a755b5b8b 100644
--- a/lisp/org-faces.el
+++ b/lisp/org-faces.el
@@ -483,6 +483,18 @@ verse and quote blocks are fontified using the `org-verse' and
   :group 'org-faces
   :version "26.1")
 
+(defface org-block-margin
+  '((t (:inherit org-structure)))
+  "Face used to depict indentation level of blocks in plain lists.
+
+When a block is a part of a nested list item, this face is prepended
+to block lines to visually align the start of `org-block' and related
+faces to the list item.  The background color of this face should be
+equal to the background color of the frame; this color is set
+automatically when `org-mode' is enabled."
+  :group 'org-faces
+  :package-version '(Org . "10.0"))
+
 (defface org-block-begin-line '((t (:inherit org-meta-line)))
   "Face used for the line delimiting the begin of source blocks."
   :group 'org-faces)
diff --git a/lisp/org.el b/lisp/org.el
index 59c5ed98b..6ce0ec986 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -5346,10 +5346,10 @@ The following commands are available:
   ;; Activate `org-table-header-line-mode'
   (when org-table-header-line-p
     (org-table-header-line-mode 1))
-  ;; Try to set `org-hide' face correctly.
-  (let ((foreground (org-find-invisible-foreground)))
-    (when foreground
-      (set-face-foreground 'org-hide foreground)))
+  ;; Try to set `org-hide' and `org-block-margin' faces correctly.
+  (when-let* ((color (org-find-invisible-color)))
+    (set-face-foreground 'org-hide color)
+    (set-face-background 'org-block-margin color))
   ;; Set face extension as requested.
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
@@ -5385,7 +5385,7 @@ The following commands are available:
   (abbrev-table-put org-mode-abbrev-table
 		    :parents (list text-mode-abbrev-table)))
 
-(defun org-find-invisible-foreground ()
+(defun org-find-invisible-color ()
   (let ((candidates (remove
 		     "unspecified-bg"
 		     (nconc
@@ -5699,6 +5699,7 @@ by a #."
       (let ((beg (match-beginning 0))
 	    (end-of-beginline (match-end 0))
 	    (lang (match-string 7)) ; The language, if it is a source block.
+	    (indent (- (match-beginning 2) (match-beginning 0) 1))
 	    (bol-after-beginline (line-beginning-position 2))
 	    (dc1 (downcase (match-string 2)))
 	    (dc3 (downcase (match-string 3)))
@@ -5774,6 +5775,17 @@ by a #."
 		     (min (point-max) beg-of-next-line))
 		 (min (point-max) end-of-endline))
 	       '(face org-block-end-line)))
+            ;; Within a plain list, if the #+begin line is indented,
+            ;; fontify the start of each block line to correspond.
+            ;; This ensures that the background of block content and
+            ;; begin/end lines aligns to the parent list item.
+            (when (org-in-list-context-p)
+              (save-excursion
+                (goto-char beg)
+                (while (< (point) end-of-endline)
+                  (let ((margin-end (min (+ (point) indent) (pos-eol))))
+                    (add-face-text-property (point) margin-end 'org-block-margin))
+                  (forward-line))))
 	    t))
 	 ((member dc1 '("+title:" "+subtitle:" "+author:" "+email:" "+date:"))
 	  (org-remove-flyspell-overlays-in
-- 
2.55.0

From af27360ea2799b4402c11f090803418f0a142a4a Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Wed, 12 Aug 2026 19:27:18 -0400
Subject: [PATCH 4/4] org: Use font-lock-extend-region-functions for blocks

* lisp/org.el (org-fontify-extend-region-block, org-mode):
Move fontification region extension from
font-lock-extend-after-change-region-function to
font-lock-extend-region-functions to improve performance.

The revised logic avoids edge cases present in the prior function,
which relied on the block begin/end line being at the start of the
changed region.  It differs from the prior logic in two other
ways: (1) region extension only looks backward since forward extension
is already handled by org-fontify-meta-lines-and-blocks-1; and (2) the
line end workaround from d74a82448 and 1abff3859 is retired since
this should be addressed by font-lock-extend-region-wholelines.
---
 lisp/org.el | 50 ++++++++++++++++++++++----------------------------
 1 file changed, 22 insertions(+), 28 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 6ce0ec986..460feda89 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -234,6 +234,7 @@ This regular expression matches these groups:
 (declare-function org-timer-start "org-timer" (&optional offset))
 (declare-function org-toggle-archive-tag "org-archive" (&optional find-done))
 
+(defvar font-lock-beg)
 (defvar font-lock-end)
 (defvar org-agenda-buffer-name)
 (defvar org-element-block-elements)
@@ -5204,9 +5205,10 @@ The following commands are available:
   (when (and org-tag-faces (not org-tags-special-faces-re))
     ;; tag faces set outside customize.... force initialization.
     (org-set-tag-faces 'org-tag-faces org-tag-faces))
-  ;; Add extend-region hook
-  (add-hook 'font-lock-extend-region-functions
-            #'org-fontify-extend-region-indentation)
+  ;; Add extend-region hooks
+  (dolist (hook '(org-fontify-extend-region-indentation
+                  org-fontify-extend-region-block))
+    (add-hook 'font-lock-extend-region-functions hook))
   ;; Calc embedded
   (setq-local calc-embedded-open-mode "# ")
   ;; Set syntax table.  Ensure that buffer-local changes to the syntax
@@ -5879,29 +5881,23 @@ returns non-nil.  See `font-lock-extend-region-functions'."
       (while (not (looking-at-p re)) (forward-line))
       (setq font-lock-end (point)))))
 
-(defun org-fontify-extend-region (beg end _old-len)
-  (let ((end (if (progn (goto-char end) (looking-at-p "^[*#]"))
-                 (min (point-max) (1+ end))
-               ;; See `font-lock-extend-jit-lock-region-after-change' and bug#68849.
-               (min (point-max) (1+ end))))
-        (begin-re "\\(\\\\\\[\\|\\(#\\+begin_\\|\\\\begin{\\)\\S-+\\)")
-	(end-re "\\(\\\\\\]\\|\\(#\\+end_\\|\\\\end{\\)\\S-+\\)")
-	(extend
-         (lambda (r1 r2 dir)
-	   (let ((re (replace-regexp-in-string
-                      "\\(begin\\|end\\)" r1
-		      (replace-regexp-in-string
-                       "[][]" r2
-		       (match-string-no-properties 0)))))
-	     (re-search-forward (regexp-quote re) nil t dir)))))
-    (goto-char beg)
-    (back-to-indentation)
-    (save-match-data
-      (cond ((looking-at end-re)
-	     (cons (or (funcall extend "begin" "[" -1) beg) end))
-	    ((looking-at begin-re)
-	     (cons beg (or (funcall extend "end" "]" 1) end)))
-	    (t (cons beg end))))))
+(defun org-fontify-extend-region-block ()
+  "Move fontification boundary to fully encompass blocks being closed.
+
+This checks for any blocks that end in the fontification region but
+begin prior to it, uses the outermost of these if nested, and extends
+the region backward to the beginning of the block.
+
+When the region should be extended, this sets `font-lock-beg' and
+returns non-nil.  See `font-lock-extend-region-functions'."
+  (when-let* ((blocks (org-element-lineage-map
+                          (org-element-at-point font-lock-beg)
+                          `(and (< (org-element-begin node) font-lock-beg)
+                                (<= (org-element-end node) font-lock-end)
+                                node)
+                        org-element-block-elements t)))
+    (goto-char (org-element-begin (car (last blocks)))) ; outermost
+    (setq font-lock-beg (pos-bol))))
 
 (defun org-activate-footnote-links (limit)
   "Add text properties for footnotes."
@@ -6282,8 +6278,6 @@ needs to be inserted at a specific position in the font-lock sequence.")
     (setq-local org-font-lock-keywords org-font-lock-extra-keywords)
     (setq-local font-lock-defaults
 		'(org-font-lock-keywords t nil nil backward-paragraph))
-    (setq-local font-lock-extend-after-change-region-function
-		#'org-fontify-extend-region)
     nil))
 
 (defun org-toggle-pretty-entities ()
-- 
2.55.0

Reply via email to