emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Morgan Smith <morgan.j.smith@outlook.com>
To: Ihor Radchenko <yantar92@posteo.net>
Cc: emacs-orgmode@gnu.org
Subject: Re: [PATCH] lisp/org-element.el: Add repeater-deadline support to org-element
Date: Wed, 10 Apr 2024 17:46:07 -0400	[thread overview]
Message-ID: <CH3PR84MB34244E14B200D207C687925FC5062@CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM> (raw)
In-Reply-To: <87sezxo194.fsf@localhost> (Ihor Radchenko's message of "Sun, 07 Apr 2024 11:33:59 +0000")

[-- Attachment #1: Type: text/plain, Size: 1193 bytes --]

Apologies for the delay.  I had lots of social events surrounding the
solar eclipse.  The eclipse was really cool, I do recommend.

See two patches attached again.  All tests pass on my computer.

I decided to add an extra `let' statement to my changes to
`org-element-timestamp-parser'.  I think it adds a touch of clarity and
maybe performance.

Ihor Radchenko <yantar92@posteo.net> writes:

>> My change in org-syntax.org now implies that a repeater
>> must come before a delay.  I don't know what syntax to use that doesn't
>> make that implication.  Although I don't see the harm in telling people
>> to put the repeater first.
>
> We can simply leave the previous REPEATER-OR-DELAY, but expand on it
> that REPEATER-OR-DELAY is an instance of REPEATER or an instance of
> DELAY. Does it make sense?

Done.

>> Done.  It's my first time using rx though.  I don't know if I should be
>> compiling it or something for performance?
>
> `rx' is a macro. It will be expanded during compilation.

Thank you for the piece of mind.  I'll have to use `rx' more often :).


Thanks for the reviews!  I'll start switching org-habit over to the
element API soon.  I've already played with that a bit


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-lisp-org-element.el-Add-repeater-deadline-support-to.patch --]
[-- Type: text/x-patch, Size: 10496 bytes --]

From 582d4e7372c005f098f213b496de6f85c0c11d2f Mon Sep 17 00:00:00 2001
From: Morgan Smith <Morgan.J.Smith@outlook.com>
Date: Wed, 3 Apr 2024 16:30:42 -0400
Subject: [PATCH] lisp/org-element.el: Add repeater-deadline support to
 org-element

* lisp/org-element.el (org-element-timestamp-parser,
org-element-timestamp-interpreter): Add support for repeater
deadlines.  Adds two new properties: ':repeater-deadline-value' and
':repeater-deadline-unit'.

* testing/lisp/test-org-element.el (test-org-element/timestamp-parser,
test-org-element/timestamp-interpreter): Test support for repeater
deadlines.

* etc/ORG-NEWS: Add relevant news.
---
 etc/ORG-NEWS                     | 14 +++++++
 lisp/org-element.el              | 70 +++++++++++++++++++++++---------
 testing/lisp/test-org-element.el | 38 ++++++++++++++++-
 3 files changed, 100 insertions(+), 22 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index aeb7ffd4b..2b418cd3c 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -512,6 +512,20 @@ timestamp object.  Possible values: ~timerange~, ~daterange~, ~nil~.
 ~org-element-timestamp-interpreter~ takes into account this property
 and returns an appropriate timestamp string.
 
+**** New properties =:repeater-deadline-value= and =:repeater-deadline-unit= for org-element timestamp object
+
+~org-element-timestamp-parser~ now adds =:repeater-deadline-value= and
+=:repeater-deadline-unit= properties to each timestamp object that has
+a repeater deadline.
+
+Possible values for =:repeater-deadline-value=: ~positive integer~, ~nil~.
+
+Possible values for =:repeater-deadline-unit=: ~hour~, ~day~, ~week~,
+~month~, ~year~.
+
+~org-element-timestamp-interpreter~ takes into account these properties
+and returns an appropriate timestamp string.
+
 **** =org-link= store functions are passed an ~interactive?~ argument
 
 The ~:store:~ functions set for link types using
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 8e5416d8b..49a312694 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -4288,12 +4288,13 @@ Assume point is at the target."
   "Parse time stamp at point, if any.
 
 When at a time stamp, return a new syntax node of `timestamp' type
-containing `:type', `:range-type', `:raw-value', `:year-start', `:month-start',
-`:day-start', `:hour-start', `:minute-start', `:year-end',
-`:month-end', `:day-end', `:hour-end', `:minute-end',
+containing `:type', `:range-type', `:raw-value', `:year-start',
+`:month-start', `:day-start', `:hour-start', `:minute-start',
+`:year-end', `:month-end', `:day-end', `:hour-end', `:minute-end',
 `:repeater-type', `:repeater-value', `:repeater-unit',
-`:warning-type', `:warning-value', `:warning-unit', `:begin', `:end'
-and `:post-blank' properties.  Otherwise, return nil.
+`:repeater-deadline-value', `:repeater-deadline-unit', `:warning-type',
+`:warning-value', `:warning-unit', `:begin', `:end' and `:post-blank'
+properties.  Otherwise, return nil.
 
 Assume point is at the beginning of the timestamp."
   (when (looking-at-p org-element--timestamp-regexp)
@@ -4326,20 +4327,38 @@ Assume point is at the beginning of the timestamp."
                           (date-end 'daterange)
                           (time-range 'timerange)
                           (t nil)))
-	     (repeater-props
-	      (and (not diaryp)
-		   (string-match "\\([.+]?\\+\\)\\([0-9]+\\)\\([hdwmy]\\)"
-				 raw-value)
-		   (list
-		    :repeater-type
-		    (let ((type (match-string 1 raw-value)))
-		      (cond ((equal "++" type) 'catch-up)
-			    ((equal ".+" type) 'restart)
-			    (t 'cumulate)))
-		    :repeater-value (string-to-number (match-string 2 raw-value))
-		    :repeater-unit
-		    (pcase (string-to-char (match-string 3 raw-value))
-		      (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year)))))
+             (repeater-props
+              (and (not diaryp)
+                   (string-match
+                    (rx
+                     (group (or "+" "++" ".+"))
+                     (group (+ digit))
+                     (group (or "h" "d" "w" "m" "y"))
+                     (\?
+                      "/"
+                      (group (+ digit))
+                      (group (or "h" "d" "w" "m" "y"))))
+                    raw-value)
+                   (nconc
+                    (list
+                     :repeater-type
+                     (let ((type (match-string 1 raw-value)))
+                       (cond ((equal "++" type) 'catch-up)
+                             ((equal ".+" type) 'restart)
+                             (t 'cumulate)))
+                     :repeater-value (string-to-number (match-string 2 raw-value))
+                     :repeater-unit
+                     (pcase (string-to-char (match-string 3 raw-value))
+                       (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year)))
+
+                    (let ((repeater-deadline-value (match-string 4 raw-value))
+                          (repeater-deadline-unit (match-string 5 raw-value)))
+                      (when (and repeater-deadline-value repeater-deadline-unit)
+                        (list
+                         :repeater-deadline-value (string-to-number repeater-deadline-value)
+                         :repeater-deadline-unit
+                         (pcase (string-to-char repeater-deadline-unit)
+                           (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year))))))))
 	     (warning-props
 	      (and (not diaryp)
 		   (string-match "\\(-\\)?-\\([0-9]+\\)\\([hdwmy]\\)" raw-value)
@@ -4407,7 +4426,18 @@ Assume point is at the beginning of the timestamp."
 	             (let ((val (org-element-property :repeater-value timestamp)))
 	               (and val (number-to-string val)))
 	             (pcase (org-element-property :repeater-unit timestamp)
-	               (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))
+	               (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))
+                     (let ((repeater-deadline-value
+                            (org-element-property :repeater-deadline-value timestamp))
+                           (repeater-deadline-unit
+                            (org-element-property :repeater-deadline-unit timestamp)))
+                       (if (and repeater-deadline-value repeater-deadline-unit)
+                           (concat
+                            "/"
+                            (number-to-string repeater-deadline-value)
+                            (pcase repeater-deadline-unit
+                              (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))
+                         ""))))
                    (range-type (org-element-property :range-type timestamp))
                    (warning-string
 	            (concat
diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el
index c49dc80d1..ddd601690 100644
--- a/testing/lisp/test-org-element.el
+++ b/testing/lisp/test-org-element.el
@@ -3208,11 +3208,18 @@ Outside list"
      (let ((timestamp (org-element-context)))
        (or (org-element-property :hour-end timestamp)
 	   (org-element-property :minute-end timestamp)))))
-  ;; With repeater, warning delay and both.
+  ;; With repeater, repeater deadline, warning delay and combinations.
   (should
    (eq 'catch-up
        (org-test-with-temp-text "<2012-03-29 Thu ++1y>"
 	 (org-element-property :repeater-type (org-element-context)))))
+  (should
+   (equal '(catch-up 2 year)
+       (org-test-with-temp-text "<2012-03-29 Thu ++1y/2y>"
+         (let ((ts (org-element-context)))
+           (list (org-element-property :repeater-type ts)
+                 (org-element-property :repeater-deadline-value ts)
+                 (org-element-property :repeater-deadline-unit ts))))))
   (should
    (eq 'first
        (org-test-with-temp-text "<2012-03-29 Thu --1y>"
@@ -3223,6 +3230,14 @@ Outside list"
 	    (let ((ts (org-element-context)))
 	      (list (org-element-property :repeater-type ts)
 		    (org-element-property :warning-type ts))))))
+  (should
+   (equal '(cumulate all 2 year)
+          (org-test-with-temp-text "<2012-03-29 Thu +1y/2y -1y>"
+            (let ((ts (org-element-context)))
+              (list (org-element-property :repeater-type ts)
+                    (org-element-property :warning-type ts)
+                    (org-element-property :repeater-deadline-value ts)
+                    (org-element-property :repeater-deadline-unit ts))))))
   ;; :range-type property
   (should
    (eq
@@ -3963,7 +3978,7 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
   ;; Diary.
   (should (equal (org-test-parse-and-interpret "<%%diary-float t 4 2>")
 		 "<%%diary-float t 4 2>\n"))
-  ;; Timestamp with repeater interval, with delay, with both.
+  ;; Timestamp with repeater interval, repeater deadline, with delay, with combinations.
   (should
    (string-match "<2012-03-29 .* \\+1y>"
 		 (org-test-parse-and-interpret "<2012-03-29 thu. +1y>")))
@@ -3975,6 +3990,15 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
        (:type active :year-start 2012 :month-start 3 :day-start 29
 	      :repeater-type cumulate :repeater-value 1 :repeater-unit year))
      nil)))
+  (should
+   (string-match
+    "<2012-03-29 .* \\+1y/2y>"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type active :year-start 2012 :month-start 3 :day-start 29
+              :repeater-type cumulate :repeater-value 1 :repeater-unit year
+              :repeater-deadline-value 2 :repeater-deadline-unit year))
+     nil)))
   (should
    (string-match
     "<2012-03-29 .* -1y>"
@@ -3992,6 +4016,16 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
 	      :warning-type all :warning-value 1 :warning-unit year
 	      :repeater-type cumulate :repeater-value 1 :repeater-unit year))
      nil)))
+  (should
+   (string-match
+    "<2012-03-29 .* \\+1y/2y -1y>"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type active :year-start 2012 :month-start 3 :day-start 29
+              :warning-type all :warning-value 1 :warning-unit year
+              :repeater-type cumulate :repeater-value 1 :repeater-unit year
+              :repeater-deadline-value 2 :repeater-deadline-unit year))
+     nil)))
   ;; Timestamp range with repeater interval
   (should
    (string-match "<2012-03-29 .* \\+1y>--<2012-03-30 .* \\+1y>"
-- 
2.41.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Document-repeater-deadline-syntax-and-element-api.patch --]
[-- Type: text/x-patch, Size: 2982 bytes --]

From db69967bed986d96a6d246cd66d6d53b0a63f922 Mon Sep 17 00:00:00 2001
From: Morgan Smith <Morgan.J.Smith@outlook.com>
Date: Thu, 4 Apr 2024 16:49:31 -0400
Subject: [PATCH] Document repeater deadline syntax and element api

* dev/org-element-api.org (Timestamp): Add ':repeater-deadline-unit'
and ':repeater-deadline-value'.
* org-syntax.org (Timestamps): Separate definition of repeater and
delay.  Add repeater deadline to repeater definition.
---
 dev/org-element-api.org |  5 +++++
 org-syntax.org          | 20 ++++++++++++++------
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/dev/org-element-api.org b/dev/org-element-api.org
index ffcda274..9d57238b 100644
--- a/dev/org-element-api.org
+++ b/dev/org-element-api.org
@@ -706,6 +706,11 @@ ** Timestamp
   (symbol: ~year~, ~month~, ~week~, ~day~, ~hour~ or ~nil~).
 - ~:repeater-value~ :: Value of shift, if a repeater is defined
   (integer or ~nil~).
+- ~:repeater-deadline-unit~ :: Unit of shift, if a repeater deadline
+  is defined (symbol: ~year~, ~month~, ~week~, ~day~, ~hour~ or
+  ~nil~).
+- ~:repeater-deadline-value~ :: Value of shift, if a repeater deadline
+  is defined (integer or ~nil~).
 - ~:type~ :: Type of timestamp (symbol: ~active~, ~active-range~,
   ~diary~, ~inactive~, ~inactive-range~).
 - ~:range-type~ :: Type of range (symbol: ~daterange~, ~timerange~ or
diff --git a/org-syntax.org b/org-syntax.org
index bdd372d1..0967ae98 100644
--- a/org-syntax.org
+++ b/org-syntax.org
@@ -1763,20 +1763,28 @@ ** Timestamps
 + TIME (optional) :: An instance of the pattern =H:MM= where =H=
   represents a one to two digit number (and can start with =0=), and =M=
   represents a single digit.
-+ REPEATER-OR-DELAY (optional) :: An instance of the following pattern:
++ REPEATER-OR-DELAY (optional) :: An instance of a single REPEATER and/or an
+  instance of a single DELAY in any order.
++ REPEATER (optional) :: An instance of the following pattern:
   #+begin_example
 MARK VALUE UNIT
+MARK VALUE UNIT/VALUE UNIT
   #+end_example
   Where MARK, VALUE and UNIT are not separated by whitespace characters.
   - MARK :: Either the string =+= (cumulative type), =++= (catch-up type),
-    or =.+= (restart type) when forming a repeater, and either =-= (all
-    type) or =--= (first type) when forming a warning delay.
+    or =.+= (restart type).
+  - VALUE :: A number
+  - UNIT :: Either the character =h= (hour), =d= (day), =w= (week), =m=
+    (month), or =y= (year)
++ DELAY (optional) :: An instance of the following pattern:
+  #+begin_example
+MARK VALUE UNIT
+  #+end_example
+  Where MARK, VALUE and UNIT are not separated by whitespace characters.
+  - MARK :: Either  =-= (all type) or =--= (first type).
   - VALUE :: A number
   - UNIT :: Either the character =h= (hour), =d= (day), =w= (week), =m=
     (month), or =y= (year)
-
-There can be two instances of =REPEATER-OR-DELAY= in the timestamp: one
-as a repeater and one as a warning delay.
 
 *Examples*
 
-- 
2.41.0


  reply	other threads:[~2024-04-10 21:52 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-03 21:21 [PATCH] lisp/org-element.el: Add repeater-deadline support to org-element Morgan Smith
2024-04-04 16:51 ` Ihor Radchenko
2024-04-04 21:08   ` Morgan Smith
2024-04-07 11:33     ` Ihor Radchenko
2024-04-10 21:46       ` Morgan Smith [this message]
2024-04-11 13:24         ` Ihor Radchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CH3PR84MB34244E14B200D207C687925FC5062@CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM \
    --to=morgan.j.smith@outlook.com \
    --cc=emacs-orgmode@gnu.org \
    --cc=yantar92@posteo.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).