> > + (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
