> > +         (headline (save-match-data (org-element-at-point)))
>
> Not that this function may be called with point inside heading.
> Then, org-element-at-point may not return the heading itself, but may
> return some element inside.
>
> > +       (let* ((match-point (match-beginning 0))
> > +                 (curr-element (save-match-data (save-excursion
> > +                                                  (goto-char
match-point)
> > +
 (org-element-timestamp-parser))))
>

Did not realize this. I now confirm that we are at a headline before
parsing. I also moved this down closer to the code that actually uses it.

> This will always attempt to parse a timestamp.
>
> > +                 (planningp (org-element-type-p curr-element
'planning))
>
> So, this will always be nil.
>

I removed planningp.


Le sam. 4 juil. 2026 à 04:38, Ihor Radchenko <[email protected]> a écrit :

> Earl Chase <[email protected]> writes:
>
> > This is an attempt to get `org-auto-repeat-maybe' to use the org-element
> > API. Because this function relies heavily on match-data and seems to be
> > intertwined with `org-timestamp-change', half of the function still does
> > not use org-element API. My goal was just to prepare this function for a
> > bigger and more complete refactor.
> > From 2d397b6a6c1b835bcad5697470beeca3f939fd6c Mon Sep 17 00:00:00 2001
> > From: ApollonDeParnasse <[email protected]>
> > Date: Wed, 1 Jul 2026 17:44:58 -0500
> > Subject: [PATCH] lisp/org.el: Transition `org-auto-repeat-maybe' to
> >  org-element API
> >
> > * lisp/org.el (org-auto-repeat-maybe): Use org-element
> > API to get repeater data for timestamps.
>
> Thanks!
>
> > +         (headline (save-match-data (org-element-at-point)))
>
> Not that this function may be called with point inside heading.
> Then, org-element-at-point may not return the heading itself, but may
> return some element inside.
>
> > +       (let* ((match-point (match-beginning 0))
> > +                 (curr-element (save-match-data (save-excursion
> > +                                                  (goto-char
> match-point)
> > +
> (org-element-timestamp-parser))))
>
> This will always attempt to parse a timestamp.
>
> > +                 (planningp (org-element-type-p curr-element 'planning))
>
> So, this will always be nil.
>
> --
> Ihor Radchenko // yantar92,
> Org mode maintainer,
> Learn more about Org mode at <https://orgmode.org/>.
> Support Org development at <https://liberapay.com/org-mode>,
> or support my work at <https://liberapay.com/yantar92>
>
From 13ddf526c41b07ceb45592733a08dbeba27b7e9a Mon Sep 17 00:00:00 2001
From: ApollonDeParnasse <[email protected]>
Date: Wed, 1 Jul 2026 17:44:58 -0500
Subject: [PATCH] lisp/org.el: Transition `org-auto-repeat-maybe' to
 org-element API

* lisp/org.el (org-auto-repeat-maybe): Use org-element
API to get repeater data for timestamps.
---
 lisp/org.el | 130 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 74 insertions(+), 56 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 294a5eb52..890b5f0fc 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -215,6 +215,7 @@ This regular expression matches these groups:
 (declare-function org-element-timestamp-parser "org-element" ())
 (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-headline-parser "org-element" (&optional _ raw-secondary-p))
 (declare-function org-export-dispatch "ox" (&optional arg))
 (declare-function org-export-get-backend "ox" (name))
 (declare-function org-export-get-environment "ox" (&optional backend subtreep ext-plist))
@@ -10481,7 +10482,6 @@ This function is run automatically after each state change to a DONE state."
 	 (aa (assoc org-last-state org-todo-kwd-alist))
 	 (interpret (nth 1 aa))
 	 (head (nth 2 aa))
-	 (whata '(("h" . hour) ("d" . day) ("m" . month) ("y" . year)))
 	 (msg "Entry repeats: ")
 	 (org-log-done nil)
 	 (org-todo-log-states nil)
@@ -10525,75 +10525,93 @@ This function is run automatically after each state change to a DONE state."
       ;; a SCHEDULED timestamp without one is removed, as they are no
       ;; longer relevant.
       (save-excursion
-	(let ((scheduled (org-entry-get (point) "SCHEDULED")))
-	  (when (and scheduled (not (string-match-p org-repeat-re scheduled)))
+	(let* ((headline (save-match-data
+                           (save-excursion
+                             (if (org-at-heading-p)
+                                 (org-element-headline-parser)
+                               (progn
+                                 (org-back-to-heading t)
+                                 (org-element-headline-parser))))))
+               (scheduled (org-element-property :scheduled headline))
+               (repeater-unit (org-element-property :repeater-unit scheduled))
+               (repeater-value (org-element-property :repeater-value scheduled))
+               (repeater-type (org-element-property :repeater-type scheduled))
+               (has-valid-repeater (and repeater-unit repeater-value repeater-type)))
+	  (when (and scheduled (not has-valid-repeater))
 	    (org-remove-timestamp-with-keyword org-scheduled-string 'planning))))
       ;; Update every timestamp with a repeater in the entry.
       (let ((planning-re (regexp-opt
 			  (list org-scheduled-string org-deadline-string))))
 	(while (re-search-forward org-repeat-re end t)
-	  (let* ((ts (match-string 0))
+	  (let* ((match-point (match-beginning 0))
+                 (timestamp (save-match-data (save-excursion
+                                               (goto-char match-point)
+                                               (org-element-timestamp-parser))))
+                 (repeater-type (org-element-property :repeater-type timestamp))
+                 (repeater-unit (org-element-property :repeater-unit timestamp))
+                 (repeater-value (org-element-property :repeater-value timestamp))
+                 (has-start-time (and (org-element-property :hour-start timestamp)
+                                      (org-element-property :minute-start timestamp)))
+                 (ts (match-string 0))
 		 (type (if (not (org-at-planning-p)) "Plain:"
 			 (save-excursion
 			   (re-search-backward
 			    planning-re (line-beginning-position) t)
 			   (match-string 0)))))
 	    (when (and (org-at-timestamp-p 'agenda)
-		       (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)" ts))
-	      (let ((n (string-to-number (match-string 2 ts)))
-		    (what (match-string 3 ts)))
-		(when (equal what "w") (setq n (* n 7) what "d"))
-		(when (and (equal what "h")
-			   (not (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}"
-						ts)))
-		  (user-error
-		   "Cannot repeat in %d hour(s) because no hour has been set"
-		   n))
-		;; Preparation, see if we need to modify the start
-		;; date for the change.
-		(when (match-end 1)
-		  (let ((time (save-match-data (org-time-string-to-time ts)))
-			(repeater-type (match-string 1 ts)))
-		    (cond
-		     ((equal "." repeater-type)
-		      ;; Shift starting date to today, or now if
-		      ;; repeater is by hours.
-		      (if (equal what "h")
-			  (org-timestamp-change
-			   (floor (- (org-timestamp-to-now ts t)) 60) 'minute)
-			(org-timestamp-change
-			 (- (org-today) (time-to-days time)) 'day)))
-		     ((equal "+" repeater-type)
-		      (let ((nshiftmax 10)
-			    (nshift 0))
-			(while (or (= nshift 0)
-				   (if (equal what "h")
-				       (not (time-less-p nil time))
-				     (>= (org-today)
-					 (time-to-days time))))
-			  (when (= nshiftmax (cl-incf nshift))
-			    (or (y-or-n-p
-				 (format "%d repeater intervals were not \
+		       repeater-unit
+                       repeater-value)
+	      (when (equal repeater-unit `week)
+                (setq repeater-value (* repeater-value 7)
+                      repeater-unit "d"))
+	      (when (and (equal repeater-unit `hour)
+			 (not has-start-time))
+		(user-error
+		 "Cannot repeat in %d hour(s) because no hour has been set"
+		 repeater-value))
+	      ;; Preparation, see if we need to modify the start
+	      ;; date for the change.
+	      (let ((time (org-timestamp-to-time timestamp)))
+		(cond
+		 ((equal `restart repeater-type)
+		  ;; Shift starting date to today, or now if
+		  ;; repeater is by hours.
+		  (if (equal repeater-unit `hour)
+		      (org-timestamp-change
+		       (floor (- (org-timestamp-to-now ts t)) 60) 'minute)
+		    (org-timestamp-change
+		     (- (org-today) (time-to-days time)) 'day)))
+		 ((equal `catch-up repeater-type)
+		  (let ((nshiftmax 10)
+			(nshift 0))
+		    (while (or (= nshift 0)
+			       (if (equal repeater-unit `hour)
+				   (not (time-less-p nil time))
+				 (>= (org-today)
+				     (time-to-days time))))
+		      (when (= nshiftmax (cl-incf nshift))
+			(or (y-or-n-p
+			     (format "%d repeater intervals were not \
 enough to shift date past today.  Continue? "
-					 nshift))
-				(user-error "Abort")))
-			  (org-timestamp-change n (cdr (assoc what whata)))
-			  (org-in-regexp org-ts-regexp3)
-			  (setq ts (match-string 1))
-			  (setq time
-				(save-match-data
-				  (org-time-string-to-time ts)))))
-		      (org-timestamp-change (- n) (cdr (assoc what whata)))
-		      ;; Rematch, so that we have everything in place
-		      ;; for the real shift.
+				     nshift))
+			    (user-error "Abort")))
+		      (org-timestamp-change repeater-value repeater-unit)
 		      (org-in-regexp org-ts-regexp3)
 		      (setq ts (match-string 1))
-		      (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)"
-				    ts)))))
-		(save-excursion
-		  (org-timestamp-change n (cdr (assoc what whata)) nil t))
-		(setq msg
-		      (concat msg type " " org-last-changed-timestamp " ")))))))
+		      (setq time
+			    (save-match-data
+			      (org-time-string-to-time ts)))))
+		  (org-timestamp-change (- repeater-value) repeater-unit)
+		  ;; Rematch, so that we have everything in place
+		  ;; for the real shift.
+		  (org-in-regexp org-ts-regexp3)
+		  (setq ts (match-string 1))
+		  (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)"
+				ts))))
+	      (save-excursion
+		(org-timestamp-change repeater-value repeater-unit nil t))
+	      (setq msg
+		    (concat msg type " " org-last-changed-timestamp " "))))))
       (save-excursion
         (run-hooks 'org-todo-repeat-hook))
       (setq org-log-post-message msg)
-- 
2.54.0

Reply via email to