JV wrote:

Ihor Radchenko wrote:

+(defun org-fontify-leading-spaces (limit)
+  "Fontify leading whitespace."
+  (when (re-search-forward "^[ \t]+" limit t)
+    (let* ((beg (match-beginning 0))
+           (end (match-end 0)))
+      ;; Indentation is significant for plain list items and their
+      ;; paragraphs, which lack bullet or numeral indicators.
+      ;; Fontify, skipping quoted block types. See `org-protecting- blocks'. +      (unless (memq 'org-block (ensure-list (get-text-property beg 'face)))

This is not reliable and depends on the fontification order.
I suggest using org-element-at-point instead.

This would seem better, but we would need to determine whether point is inside the block element's :value contents.

I've updated org-in-block-p to support this (with tests).

+        (add-face-text-property beg end 'org-indentation)))
+    t))

org-indentation sounds very similar to org-indent.
Should they be linked somehow?

This face is now renamed org-list-indent and is only applied within plain lists where it is syntactically relevant. I think this is similar to a suggestion you made earlier in this thread.

Yes, these are related and are now linked. All syntax that conveys outline structure now inherits from a new org-structure face. This includes the new faces for headline stars, list bullets, and list indentation. It also includes org-hide from which org-indent inherits.

+          ;; Plain lists
+          `(,(if org-list-allow-alphabetical
+                 "^[ \t]*\\(\\([-+]\\|[0-9]+[.)]\\|[a-zA-Z][.)]\\) [ \t\r\n]\\)"
+               "^[ \t]*\\(\\([-+]\\|[0-9]+[.)]\\)[ \t\r\n]\\)")
+            (1 'org-plain-list-indicator))
+          '("^[ \t]+\\(\\*[ \t\r\n]\\)" ; star as bullet requires indentation
+            (1 'org-plain-list-indicator))

Why do you need to produce a custom regexp here?
Can just use org-item-re.

I've updated org-item-re capture groups to support this (with tests).

Please see the patch set attached.

All tests pass against main prior to the recent introduction of a bug in org-colview function org-columns--line-face that doesn't handle multiple faces on headlines. I submitted a bug report for this separately.
From cccb98971830d88ade1bca48c8d03857308c1050 Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Sat, 23 May 2026 18:54:31 -0400
Subject: [PATCH 1/3] Update list item regexp to isolate bullet

* lisp/org-list.el (org-item-re): Update generated regexp so the
bullet is always matched as group 1, independently of leading
whitespace.

* testing/lisp/test-org-list.el (test-org-list/match-item): Add test
for identification of list items, ensuring group 1 matches bullet.
---
 lisp/org-list.el              | 21 ++++++-----
 testing/lisp/test-org-list.el | 68 +++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 9 deletions(-)

diff --git a/lisp/org-list.el b/lisp/org-list.el
index 8df83663c..23946e0c7 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -375,19 +375,22 @@ group 4: description tag")
 (defvar org--item-re-cache nil
   "Results cache for `org-item-re'.")
 (defsubst org-item-re ()
-  "Return the correct regular expression for plain lists."
+  "Return the correct regular expression for plain lists.
+Group 1 matches the bullet without leading whitespace."
   (or (plist-get
        (plist-get org--item-re-cache
                   org-list-allow-alphabetical)
        org-plain-list-ordered-item-terminator)
-      (let* ((term (cond
-	            ((eq org-plain-list-ordered-item-terminator t) "[.)]")
-	            ((= org-plain-list-ordered-item-terminator ?\)) ")")
-	            ((= org-plain-list-ordered-item-terminator ?.) "\\.")
-	            (t "[.)]")))
-	     (alpha (if org-list-allow-alphabetical "\\|[A-Za-z]" ""))
-             (re (concat "\\([ \t]*\\([-+]\\|\\(\\([0-9]+" alpha "\\)" term
-	                 "\\)\\)\\|[ \t]+\\*\\)\\([ \t]+\\|$\\)")))
+      (let* ((counter `(regexp ,(if org-list-allow-alphabetical
+                                    "[0-9]+\\|[A-Za-z]"
+                                  "[0-9]+")))
+             (term (or (car (memq org-plain-list-ordered-item-terminator '(?\) ?.)))
+                       '(any ".)")))
+             (bullet `(or (any "-+") (seq ,counter ,term)))
+             (ws '(any " \t"))
+             (re (rx-to-string
+                  `(or (seq (0+ ,ws) (group-n 1 ,bullet (or ,ws eol)))
+                       (seq (1+ ,ws) (group-n 1 "*" (or ,ws eol)))))))
         (setq org--item-re-cache
               (plist-put
                org--item-re-cache
diff --git a/testing/lisp/test-org-list.el b/testing/lisp/test-org-list.el
index 1da621143..66daa2547 100644
--- a/testing/lisp/test-org-list.el
+++ b/testing/lisp/test-org-list.el
@@ -28,6 +28,74 @@
 (require 'org-list)
 (require 'org)
 
+(ert-deftest test-org-list/match-item ()
+  "Test identification of list items.
+Match group 1 should contain the list item bullet.
+This tests `org-item-beginning-re' and by extension `org-item-re'."
+  (cl-flet ((match-group-1 ()
+              (re-search-forward (org-item-beginning-re))
+              (match-string-no-properties 1)))
+    ;; Unordered, unindented
+    (org-test-with-temp-text "- item"
+      (should (equal "- " (match-group-1))))
+    (org-test-with-temp-text "+ item"
+      (should (equal "+ " (match-group-1))))
+    ;; Unordered, indented
+    (org-test-with-temp-text "    - item"
+      (should (equal "- " (match-group-1))))
+    (org-test-with-temp-text "    + item"
+      (should (equal "+ " (match-group-1))))
+    (org-test-with-temp-text "    * item"
+      (should (equal "* " (match-group-1))))
+    ;; Ordered, default terminators
+    (let ((org-plain-list-ordered-item-terminator t))
+      ;; unindented
+      (org-test-with-temp-text "1. item"
+        (should (equal "1. " (match-group-1))))
+      (org-test-with-temp-text "123) item"
+        (should (equal "123) " (match-group-1))))
+      ;; indented
+      (org-test-with-temp-text "    1. item"
+        (should (equal "1. " (match-group-1))))
+      (org-test-with-temp-text "    123) item"
+        (should (equal "123) " (match-group-1))))
+      ;; alpha
+      (let ((org-list-allow-alphabetical t))
+        ;; unindented
+        (org-test-with-temp-text "a) item"
+          (should (equal "a) " (match-group-1))))
+        (org-test-with-temp-text "Z. item"
+          (should (equal "Z. " (match-group-1))))
+        ;; indented
+        (org-test-with-temp-text "    a) item"
+          (should (equal "a) " (match-group-1))))
+        (org-test-with-temp-text "    Z. item"
+          (should (equal "Z. " (match-group-1))))))
+    ;; Ordered, specific terminator
+    (let ((org-plain-list-ordered-item-terminator ?.))
+      (org-test-with-temp-text "1. item"
+        (should (equal "1. " (match-group-1))))
+      (org-test-with-temp-text "1) item"
+        (should-error (match-group-1))))
+    (let ((org-plain-list-ordered-item-terminator ?\)))
+      (org-test-with-temp-text "1. item"
+        (should-error (match-group-1)))
+      (org-test-with-temp-text "1) item"
+        (should (equal "1) " (match-group-1)))))
+    ;; Invalid list items
+    (org-test-with-temp-text "* item" ; star without indentation
+      (should-error (match-group-1)))
+    (org-test-with-temp-text "1 item" ; no ordinal terminator
+      (should-error (match-group-1)))
+    (org-test-with-temp-text "1] item" ; invalid ordinal terminator
+      (should-error (match-group-1)))
+    (let ((org-list-allow-alphabetical nil))
+      (org-test-with-temp-text "a) item" ; alpha without option
+        (should-error (match-group-1))))
+    (let ((org-list-allow-alphabetical t))
+      (org-test-with-temp-text "ab) item" ; multiple alpha characters
+        (should-error (match-group-1))))))
+
 (ert-deftest test-org-list/list-ending ()
   "Test if lists end at the right place."
   ;; With two blank lines.
-- 
2.54.0

From 9cc8b6d273c8ae7bd9efabc25e6aa1709bbf42ee Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Sun, 7 Jun 2026 22:12:26 -0400
Subject: [PATCH 2/3] 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.
(org-element-block-element-names): Add new constant.

* testing/lisp/test-org.el
(test-org/in-block-p): Add tests for org-in-block-p.
---
 lisp/org-element.el      |  15 +++++
 lisp/org.el              |  59 +++++++++---------
 testing/lisp/test-org.el | 125 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 172 insertions(+), 27 deletions(-)

diff --git a/lisp/org-element.el b/lisp/org-element.el
index 313922ecc..7b9b35c27 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -338,6 +338,21 @@ 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
+  '(center-block comment-block dynamic-block example-block
+                 export-block quote-block special-block src-block verse-block)
+  "List of block element types.")
+
+(defconst org-element-block-element-names
+  '(("CENTER"  . center-block)
+    ("COMMENT" . comment-block)
+    ("EXAMPLE" . example-block)
+    ("EXPORT"  . export-block)
+    ("QUOTE"   . quote-block)
+    ("SRC"     . src-block)
+    ("VERSE"   . verse-block))
+  "Alist of block names to 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.el b/lisp/org.el
index b418bd7ec..84fa3ce6c 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -19262,17 +19262,39 @@ 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--block-types (types)
+  "Convert block names to types and filter non-strings to block types.
+Name strings that do not map to a defined block type are returned unchanged."
+  (mapcar (lambda (type)
+            (if (stringp type)
+                (alist-get type org-element-block-element-names
+                           type nil #'string-equal-ignore-case)
+              (car (memq type org-element-block-elements))))
+          (ensure-list types)))
+
+(defun org-in-block-p (&optional types inside element)
+  "Return t when point is in a block element.
+
+Block TYPES may optionally 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 t 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)
+  (when-let* ((types (or (org--block-types types) org-element-block-elements))
+              (element
+               (or (org-element-lineage element types t)
+                   (org-element-lineage-map element
+                       `(when (member (org-element-property :type node) ',types)
+                          node)
+                     'special-block t t))))
     (or (not inside)
         (not (or (<= (line-beginning-position)
                   (org-element-post-affiliated element))
@@ -19281,6 +19303,11 @@ When ELEMENT is provided, it is considered to be element at point."
                     (skip-chars-backward " \t\n\r")
                     (point))))))))
 
+(defun org-in-src-block-p (&optional inside element)
+  "Return t 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.
 If several contexts apply, all are returned.
@@ -19433,28 +19460,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 80f95f3dd..709895c5b 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -5996,6 +5996,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.54.0

From c35b0947bf194a49661f1ac97717162941790a9d Mon Sep 17 00:00:00 2001
From: Jeff Valk <[email protected]>
Date: Mon, 8 Jun 2026 21:16:45 -0400
Subject: [PATCH 3/3] 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. 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 prior to the end of a plain list might either sever or conjoin
the outermost list. This can change the syntactic significance of
indentation up to the end of the outermost list, and thus requires the
fontification region to be extended.

* lisp/org-faces.el
(org-structure): Add new face.
(org-headline-stars): Add new face, inheriting from org-structure.
(org-list-bullet): Add new face, inheriting from org-structure.
(org-list-indent): Add new face, 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-list-outermost): Add function.

* lisp/org.el
(org-set-font-lock-defaults): Apply org-list-bullet face.
(org-get-level-face): Apply org-headline-stars face.
(org-fontify-leading-spaces): Add new function.
(org-before-change-function): Set org-list-outermost-end.
(org-fontify-extend-region): Extend region when outermost list is
severed or conjoined. Delegate to helper functions for readability.
---
 lisp/org-faces.el | 23 +++++++++++++++++-
 lisp/org-list.el  |  9 +++++++
 lisp/org.el       | 62 ++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 87 insertions(+), 7 deletions(-)

diff --git a/lisp/org-faces.el b/lisp/org-faces.el
index b0f5ef9d7..2c7496ee8 100644
--- a/lisp/org-faces.el
+++ b/lisp/org-faces.el
@@ -41,8 +41,29 @@
   "Face used for default text."
   :group 'org-faces)
 
+(defface org-structure
+  '((t ()))
+  "Face used for syntax that defines outline structure."
+  :group 'org-faces)
+
+(defface org-headline-stars
+  '((t (:inherit org-structure)))
+  "Face used for stars in headlines."
+  :group 'org-faces)
+
+(defface org-list-bullet
+  '((t (:inherit org-structure)))
+  "Face used for bullets or numerals in plain lists."
+  :group 'org-faces)
+
+(defface org-list-indent
+  '((t (:inherit org-structure)))
+  "Face used for significant leading whitespace in plain lists."
+  :group 'org-faces)
+
 (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 23946e0c7..1867af487 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -113,6 +113,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))
@@ -531,6 +532,14 @@ Modify match data, matching against `org-item-re'."
 
 ;;; Structures and helper functions
 
+(defvar-local org-list-outermost-end nil
+  "The end position of the outermost plain list around point, or nil.")
+
+(defun org-list-outermost ()
+  "Return the outermost plain list ancestor around point, or nil."
+  (let ((elem (save-match-data (org-element-at-point))))
+    (car (last (org-element-lineage-map elem #'identity 'plain-list t)))))
+
 (defun org-list-context ()
   "Determine context, and its boundaries, around point.
 
diff --git a/lisp/org.el b/lisp/org.el
index 84fa3ce6c..b63a7bbb1 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -100,6 +100,7 @@
 (require 'ol)
 (require 'oc)
 (require 'org-table)
+(require 'org-list)
 (require 'org-fold)
 
 (require 'org-cycle)
@@ -5079,7 +5080,8 @@ This is for getting out of special buffers like capture.")
 ;;;; Define the Org mode
 
 (defun org-before-change-function (_beg _end)
-  "Every change indicates that a table might need an update."
+  "Every change indicates that a plain list or table might need an update."
+  (setq org-list-outermost-end (org-element-end (org-list-outermost)))
   (setq org-table-may-need-update t))
 (defvar org-mode-map)
 (defvar org-inhibit-startup-visibility-stuff nil) ; Dynamically-scoped param.
@@ -5099,7 +5101,6 @@ This is for getting out of special buffers like capture.")
 
 (require 'org-entities)
 (require 'org-faces)
-(require 'org-list)
 (require 'org-pcomplete)
 (require 'org-src)
 (require 'org-footnote)
@@ -5830,7 +5831,37 @@ by a #."
 	    (add-text-properties closing-start end '(invisible t)))
 	  t)))))
 
-(defun org-fontify-extend-region (beg end _old-len)
+(defun org-fontify-leading-spaces (limit)
+  "Fontify leading whitespace in plain lists."
+  (when (re-search-forward "^[ \t]+" limit t)
+    (let ((beg (match-beginning 0))
+          (end (match-end 0))
+          (elem (save-match-data (org-element-at-point))))
+      ;; Indentation is significant for plain list items and
+      ;; paragraphs within these, which lack bullets.
+      ;; Fontify, omitting blocks in which lists are not allowed.
+      (when (org-element-lineage elem 'plain-list t)
+        (unless (org-in-block-p org-list-forbidden-blocks t)
+          (add-face-text-property beg end 'org-list-indent))))
+    t))
+
+(defun org--changed-list-region (beg end old-len)
+  "Return the region to refontify after a change to a plain list.
+
+Indentation is only syntactically meaningful in plain lists.  If the
+outermost plain list is either severed or conjoined by a change, it
+alters the significance of indentation up to the end of the list."
+  (let* ((change-len (- end beg old-len))
+         (list-end (or (org-element-end (org-list-outermost)) -1))
+         (prev-end (if org-list-outermost-end
+                       (+ org-list-outermost-end change-len)
+                     -1)))
+    (if (/= list-end prev-end)
+        (cons beg (max list-end prev-end))
+      (cons beg end))))
+
+(defun org--changed-block-region (beg end _old-len)
+  "Return the region to refontify after a change to a block."
   (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.
@@ -5854,6 +5885,19 @@ by a #."
 	     (cons beg (or (funcall extend "end" "]" 1) end)))
 	    (t (cons beg end))))))
 
+(defun org-fontify-extend-region (&rest args)
+  "Return the region to refontify after a change.
+
+This dispatches to element-specific functions that return expanded
+bounds if needed and the default bounds otherwise.
+
+See `font-lock-extend-after-change-region-function'"
+  (let* ((fns '(org--changed-block-region
+                org--changed-list-region))
+         (bounds (mapcar (lambda (f) (apply f args)) fns)))
+    (cons (apply #'min (mapcar #'car bounds))
+          (apply #'max (mapcar #'cdr bounds)))))
+
 (defun org-activate-footnote-links (limit)
   "Add text properties for footnotes."
   (let ((fn (org-footnote-next-reference-or-definition limit)))
@@ -6100,6 +6144,9 @@ needs to be inserted at a specific position in the font-lock sequence.")
 	    (1 (org-get-level-face 1))
 	    (2 (org-get-level-face 2))
 	    (3 (org-get-level-face 3)))
+          ;; Plain lists
+          `(,(org-item-beginning-re)
+            (1 'org-list-bullet))
 	  ;; Table lines
 	  '("^[ \t]*\\(\\(|\\|\\+-[-+]\\).*\\S-\\)\n?"
             (0 'org-table-row t)
@@ -6208,6 +6255,8 @@ needs to be inserted at a specific position in the font-lock sequence.")
               (org-cite-try-load-processor org-cite-activate-processor))
             ;; prepends faces
             '(org-cite-activate))
+          ;; Leading whitespace
+          '(org-fontify-leading-spaces)
 	  ;; COMMENT
           ;; Apply this last, after all the markup is highlighted, so
           ;; that even "bright" markup will become dim.
@@ -6316,10 +6365,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.54.0

Reply via email to