Hi,

This series fixes `org-skip-over-state-notes' so it skips all configured
log note entries, not just state-change notes.

I noticed this while using Org notes as a lightweight diary.  I keep
inactive timestamps for days and add log notes under them.  When I later
look at what happened on a given day, I naturally read the notes from
oldest to newest.  This makes the insertion point chosen for new notes
matter: new log notes should be inserted after the existing log notes,
not before an older regular note.

Although the function name still mentions "state", there is precedent
for treating plain log notes as state notes in this context.  In a
December 2023 list discussion, Ihor clarified that a plain note added via
`org-add-note' does count here:

  https://mail.gnu.org/archive/html/emacs-orgmode/2023-12/msg00169.html

This series follows that interpretation: the name is kept for
compatibility, but the implementation is adjusted to skip the full log
note list.

The first patch is a small refactoring: it extracts the log note format
regexp construction into a helper.  It is intended to have no behavior
change.

The second patch is the actual fix.  It makes
`org-skip-over-state-notes' consider all non-empty formats from
`org-log-note-headings'.  It also teaches `%s' and `%S' in log note
formats to match quoted inactive timestamps, e.g. "[2024-01-01 Mon]",
which is needed for reschedule notes.

Before this change, a regular note after a state note made the function
stop early.  The new test covers a state note, a regular note, and a
reschedule note, and checks that point reaches the following paragraph.

Feedback is welcome, especially on whether broadening
`org-skip-over-state-notes' is the right way to reflect the existing
interpretation.

Best,
-- 
Slawomir Grochowski

>From 0c5bf0a6ed407787c7fdd61d0903980ae0e7d44d Mon Sep 17 00:00:00 2001
From: Slawomir Grochowski <[email protected]>
Date: Fri, 29 May 2026 09:22:13 +0200
Subject: [PATCH] ; org: Extract log note format regexp helper

* lisp/org.el (org--log-note-format-regexp): New function.
(org-skip-over-state-notes): Use it.

Refactoring: Extract Function.

No behavior change.
---
 lisp/org.el | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index cb36497f4..79419f9a0 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11040,6 +11040,21 @@ EXTRA is additional text that will be inserted into the notes buffer."
         org-log-setup t)
   (add-hook 'post-command-hook 'org-add-log-note 'append))
 
+(defun org--log-note-format-regexp (format)
+  "Return a regexp matching log note FORMAT."
+  (replace-regexp-in-string
+   " +" " +"
+   (org-replace-escapes
+    (regexp-quote format)
+    `(("%d" . ,org-ts-regexp-inactive)
+      ("%D" . ,org-ts-regexp)
+      ("%s" . "\\(?:\"\\S-+\"\\)?")
+      ("%S" . "\\(?:\"\\S-+\"\\)?")
+      ("%t" . ,org-ts-regexp-inactive)
+      ("%T" . ,org-ts-regexp)
+      ("%u" . ".*?")
+      ("%U" . ".*?")))))
+
 (defun org-skip-over-state-notes ()
   "Skip past the list of State notes in an entry.
 The point is assumed to be on a list of State notes, each matching
@@ -11051,18 +11066,8 @@ items are State notes."
 	   (prevs (org-list-prevs-alist struct))
 	   (regexp
 	    (concat "[ \t]*- +"
-		    (replace-regexp-in-string
-		     " +" " +"
-		     (org-replace-escapes
-		      (regexp-quote (cdr (assq 'state org-log-note-headings)))
-		      `(("%d" . ,org-ts-regexp-inactive)
-			("%D" . ,org-ts-regexp)
-			("%s" . "\\(?:\"\\S-+\"\\)?")
-			("%S" . "\\(?:\"\\S-+\"\\)?")
-			("%t" . ,org-ts-regexp-inactive)
-			("%T" . ,org-ts-regexp)
-			("%u" . ".*?")
-			("%U" . ".*?")))))))
+		    (org--log-note-format-regexp
+		     (cdr (assq 'state org-log-note-headings))))))
       (while (looking-at-p regexp)
 	(goto-char (or (org-list-get-next-item (point) struct prevs)
 		       (org-list-get-item-end (point) struct)))))))
-- 
2.39.5

>From 02aba707c087b29bb75ac894eae95cdc177b849a Mon Sep 17 00:00:00 2001
From: Slawomir Grochowski <[email protected]>
Date: Fri, 29 May 2026 09:23:07 +0200
Subject: [PATCH] org: Fix skipping over non-state log notes

* lisp/org.el (org--log-note-format-regexp): Match quoted timestamps
in %s and %S placeholders.
(org-skip-over-state-notes): Skip all configured log note formats.

* testing/lisp/test-org.el (test-org/org-skip-over-state-notes): Add
test for state, note, and reschedule log entries.

Behavior change: Skip non-state log notes instead of stopping at the
first regular note.  Also recognize reschedule notes that quote inactive
timestamps.
---
 lisp/org.el              | 29 ++++++++++++++++++++---------
 testing/lisp/test-org.el | 14 ++++++++++++++
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 79419f9a0..eaf1982a7 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11048,26 +11048,37 @@ EXTRA is additional text that will be inserted into the notes buffer."
     (regexp-quote format)
     `(("%d" . ,org-ts-regexp-inactive)
       ("%D" . ,org-ts-regexp)
-      ("%s" . "\\(?:\"\\S-+\"\\)?")
-      ("%S" . "\\(?:\"\\S-+\"\\)?")
+      ("%s" . ,(concat "\\(?:\"\\S-+\"\\|\""
+		       org-ts-regexp-inactive
+		       "\"\\)?"))
+      ("%S" . ,(concat "\\(?:\"\\S-+\"\\|\""
+		       org-ts-regexp-inactive
+		       "\"\\)?"))
       ("%t" . ,org-ts-regexp-inactive)
       ("%T" . ,org-ts-regexp)
       ("%u" . ".*?")
       ("%U" . ".*?")))))
 
 (defun org-skip-over-state-notes ()
-  "Skip past the list of State notes in an entry.
-The point is assumed to be on a list of State notes, each matching
-`org-log-note-headings'.  The function moves point to the first list
-item that is not a State note or to the end of the list if all the
-items are State notes."
+  "Skip past the list of log notes in an entry.
+The point is assumed to be on a list of notes, each matching a format
+configured in `org-log-note-headings'.  The function moves point to the
+first list item that is not a log note or to the end of the list if all
+the items are log notes."
   (when (ignore-errors (goto-char (org-in-item-p)))
     (let* ((struct (org-list-struct))
 	   (prevs (org-list-prevs-alist struct))
 	   (regexp
 	    (concat "[ \t]*- +"
-		    (org--log-note-format-regexp
-		     (cdr (assq 'state org-log-note-headings))))))
+		    "\\(?:"
+		    (mapconcat #'org--log-note-format-regexp
+			       (delq nil
+				     (mapcar (lambda (entry)
+					       (let ((format (cdr entry)))
+						 (and (stringp format) (not (string-empty-p format)) format)))
+					     org-log-note-headings))
+			       "\\|")
+		    "\\)")))
       (while (looking-at-p regexp)
 	(goto-char (or (org-list-get-next-item (point) struct prevs)
 		       (org-list-get-item-end (point) struct)))))))
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 4ba7aa560..242079eec 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -9232,6 +9232,20 @@ Behavior can be modified by setting `org-log-into-drawer', by keywords in
                                   (concat "#+STARTUP: nologdrawer\n* WAIT task\n"
                                           state-change-string))))))
 
+(ert-deftest test-org/org-skip-over-state-notes ()
+  "Test `org-skip-over-state-notes' skips over all configured note types."
+  (org-test-with-temp-text
+      "* TODO Task
+<point>- State \"DONE\"       from \"TODO\"       [2024-01-01 Mon]
+- Note taken on [2024-01-01 Mon]
+- Rescheduled from \"[2024-01-01 Mon]\" on [2024-01-01 Mon]
+Paragraph"
+    (let ((org-log-note-headings '((state . "State %-12s from %-12S %t")
+                                   (note . "Note taken on %t")
+                                   (reschedule . "Rescheduled from %S on %t"))))
+      (org-skip-over-state-notes)
+      (should (looking-at-p "Paragraph")))))
+
 (ert-deftest test-org/org-todo-prefix ()
   "Test `org-todo' prefix arg behavior."
   ;; FIXME: Add tests `org-todo-keywords' set to a `type' list
-- 
2.39.5

Reply via email to