org-element-timestamp-parser: Allow time in diary sexp timestamps
* lisp/org-agenda.el (org-agenda-get-timestamps): * lisp/org-element.el (org-element--timestamp-regexp): Adjust timestamp regexp. (org-element-timestamp-parser): Support the new syntax for diary sexp timestamps. The diary sexp is now stored in :diary-sexp property and the time/time range is stored as usual. (org-element-timestamp-interpreter): Interpret diary timestamp according to its building blocks rather than raw value. * testing/lisp/test-org-agenda.el (test-org-agenda/diary-timestamp): New test checking for agenda support of times in diary timestamps. * testing/lisp/test-org-element.el (test-org-element/timestamp-interpreter): Add parser tests. * doc/org-manual.org (Timestamps): Add an example of the new syntax to the manual. * etc/ORG-NEWS (Diary type timestamps now support optional time/timerange): Document the Org syntax addition. This syntax modification is fixing an omission in org-element.el. In the past, org-agenda had explicit support for diary timestamps with time/timerange, but that support was ad-hoc. Now, after org-agenda switched to use parser, we must modify Org syntax to fix the feature regression.
This commit is contained in:
parent
1cafe3e8e4
commit
5da0eb6ea7
|
@ -6156,7 +6156,7 @@ the agenda (see [[*Weekly/daily agenda]]). We distinguish:
|
|||
|
||||
#+begin_example
|
||||
,* 22:00-23:00 The nerd meeting on every 2nd Thursday of the month
|
||||
<%%(diary-float t 4 2)>
|
||||
<%%(diary-float t 4 2) 22:00-23:00>
|
||||
#+end_example
|
||||
|
||||
- Time range ::
|
||||
|
|
38
etc/ORG-NEWS
38
etc/ORG-NEWS
|
@ -339,7 +339,43 @@ Now, ~org-store-link~ moves the stored link to front of the list of
|
|||
stored links. This way, the link will show up first in the completion
|
||||
and when inserting all the stored links with ~org-insert-all-links~.
|
||||
|
||||
*** Major changes and additions to Org API
|
||||
*** Major changes and additions to Org element API
|
||||
**** Diary type timestamps now support optional time/timerange
|
||||
|
||||
Previously, diary type timestamps could not specify time.
|
||||
Now, it is allowed to add a time or time range:
|
||||
|
||||
: <%%(diary-float t 4 2) 22:00-23:00>
|
||||
: <%%(diary-float t 4 2) 10:30>
|
||||
|
||||
The parsed representation of such timestamps will have ~:hour-start~,
|
||||
~:minute-start~, ~:hour-end~, ~:minute-end~, and ~:range-type~
|
||||
properties set appropriately. In addition, a new ~:diary-sexp~
|
||||
property will store the diary sexp value.
|
||||
|
||||
For example,
|
||||
|
||||
: <%%(diary-float t 4 2) 22:00-23:00>
|
||||
|
||||
will have the following properties
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
:type: diary
|
||||
:range-type: timerange
|
||||
:raw-value: "<%%(diary-float t 4 2) 22:00-23:00>"
|
||||
:year-start: nil
|
||||
:month-start: nil
|
||||
:day-start: nil
|
||||
:hour-start: 22
|
||||
:minute-start: 0
|
||||
:year-end: nil
|
||||
:month-end: nil
|
||||
:day-end: nil
|
||||
:hour-end: 23
|
||||
:minute-end: 0
|
||||
:diary-sexp: "(diary-float t 4 2)"
|
||||
#+end_src
|
||||
|
||||
**** New term: "syntax node"
|
||||
|
||||
To reduce confusion with "element" referring to both "syntax element"
|
||||
|
|
|
@ -5831,7 +5831,7 @@ displayed in agenda view."
|
|||
(org-encode-time ; DATE bound by calendar
|
||||
0 0 0 (nth 1 date) (car date) (nth 2 date))))
|
||||
"\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)"
|
||||
"\\|\\(<%%\\(([^>\n]+)\\)>\\)"))
|
||||
"\\|\\(<%%\\(([^>\n]+)\\)\\([^\n>]*\\)>\\)"))
|
||||
timestamp-items)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward regexp nil t)
|
||||
|
|
|
@ -4282,7 +4282,7 @@ Assume point is at the target."
|
|||
"\\|"
|
||||
"\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)"
|
||||
"\\|"
|
||||
"\\(?:<%%\\(?:([^>\n]+)\\)>\\)")
|
||||
"\\(?:<%%\\(?:([^>\n]+)\\)\\([^\n>]*\\)>\\)")
|
||||
"Regexp matching any timestamp type object.")
|
||||
|
||||
(defconst org-element--timestamp-raw-value-regexp
|
||||
|
@ -4300,8 +4300,8 @@ containing `:type', `:range-type', `:raw-value', `:year-start',
|
|||
`:year-end', `:month-end', `:day-end', `:hour-end', `:minute-end',
|
||||
`:repeater-type', `:repeater-value', `:repeater-unit',
|
||||
`:repeater-deadline-value', `:repeater-deadline-unit', `:warning-type',
|
||||
`:warning-value', `:warning-unit', `:begin', `:end' and `:post-blank'
|
||||
properties. Otherwise, return nil.
|
||||
`:warning-value', `:warning-unit', `:diary-sexp', `: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)
|
||||
|
@ -4312,19 +4312,29 @@ Assume point is at the beginning of the timestamp."
|
|||
(progn
|
||||
(looking-at org-element--timestamp-raw-value-regexp)
|
||||
(match-string-no-properties 0)))
|
||||
(date-start (match-string-no-properties 1))
|
||||
(date-end (match-string-no-properties 3))
|
||||
(diaryp (match-beginning 2))
|
||||
diary-sexp
|
||||
(date-start (if diaryp
|
||||
;; Only consider part after sexp for
|
||||
;; diary timestamps.
|
||||
(save-match-data
|
||||
(looking-at org-element--timestamp-regexp)
|
||||
(setq diary-sexp
|
||||
(buffer-substring-no-properties
|
||||
(+ 3 (match-beginning 0))
|
||||
(match-beginning 2)))
|
||||
(match-string 2))
|
||||
(match-string-no-properties 1)))
|
||||
(date-end (match-string-no-properties 3))
|
||||
(post-blank (progn (goto-char (match-end 0))
|
||||
(skip-chars-forward " \t")))
|
||||
(end (point))
|
||||
(time-range
|
||||
(and (not diaryp)
|
||||
(string-match
|
||||
"[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
|
||||
date-start)
|
||||
(cons (string-to-number (match-string 2 date-start))
|
||||
(string-to-number (match-string 3 date-start)))))
|
||||
(when (string-match
|
||||
"[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
|
||||
date-start)
|
||||
(cons (string-to-number (match-string 2 date-start))
|
||||
(string-to-number (match-string 3 date-start)))))
|
||||
(type (cond (diaryp 'diary)
|
||||
((and activep (or date-end time-range)) 'active-range)
|
||||
(activep 'active)
|
||||
|
@ -4395,6 +4405,17 @@ Assume point is at the beginning of the timestamp."
|
|||
day-end (or (nth 3 date) day-start)
|
||||
hour-end (or (nth 2 date) (car time-range) hour-start)
|
||||
minute-end (or (nth 1 date) (cdr time-range) minute-start))))
|
||||
;; Diary timestamp with time.
|
||||
(when (and diaryp
|
||||
(string-match "\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)?" date-start))
|
||||
(setq hour-start (match-string 1 date-start)
|
||||
minute-start (match-string 2 date-start)
|
||||
hour-end (match-string 4 date-start)
|
||||
minute-end (match-string 5 date-start))
|
||||
(when hour-start (setq hour-start (string-to-number hour-start)))
|
||||
(when minute-start (setq minute-start (string-to-number minute-start)))
|
||||
(when hour-end (setq hour-end (string-to-number hour-end)))
|
||||
(when minute-end (setq minute-end (string-to-number minute-end))))
|
||||
(org-element-create
|
||||
'timestamp
|
||||
(nconc (list :type type
|
||||
|
@ -4413,137 +4434,144 @@ Assume point is at the beginning of the timestamp."
|
|||
:begin begin
|
||||
:end end
|
||||
:post-blank post-blank)
|
||||
(and diary-sexp (list :diary-sexp diary-sexp))
|
||||
repeater-props
|
||||
warning-props))))))
|
||||
|
||||
(defun org-element-timestamp-interpreter (timestamp _)
|
||||
"Interpret TIMESTAMP object as Org syntax."
|
||||
(let((type (org-element-property :type timestamp)))
|
||||
(if (member type '(active inactive inactive-range active-range))
|
||||
(let ((day-start (org-element-property :day-start timestamp))
|
||||
(month-start (org-element-property :month-start timestamp))
|
||||
(year-start (org-element-property :year-start timestamp)))
|
||||
;; Return nil when start date is not available. Could also
|
||||
;; throw an error, but the current behavior is historical.
|
||||
(when (and day-start month-start year-start)
|
||||
(let* ((repeat-string
|
||||
(concat
|
||||
(pcase (org-element-property :repeater-type timestamp)
|
||||
(`cumulate "+") (`catch-up "++") (`restart ".+"))
|
||||
(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"))
|
||||
(when-let ((repeater-deadline-value
|
||||
(org-element-property :repeater-deadline-value timestamp))
|
||||
(repeater-deadline-unit
|
||||
(org-element-property :repeater-deadline-unit timestamp)))
|
||||
(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
|
||||
(pcase (org-element-property :warning-type timestamp)
|
||||
(`first "--") (`all "-"))
|
||||
(let ((val (org-element-property :warning-value timestamp)))
|
||||
(and val (number-to-string val)))
|
||||
(pcase (org-element-property :warning-unit timestamp)
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))
|
||||
(hour-start (org-element-property :hour-start timestamp))
|
||||
(minute-start (org-element-property :minute-start timestamp))
|
||||
(brackets
|
||||
(if (member
|
||||
type
|
||||
'(inactive inactive-range))
|
||||
(cons "[" "]")
|
||||
(cons "<" ">")))
|
||||
(timestamp-end
|
||||
(concat
|
||||
(and (org-string-nw-p repeat-string) (concat " " repeat-string))
|
||||
(and (org-string-nw-p warning-string) (concat " " warning-string))
|
||||
(cdr brackets))))
|
||||
(concat
|
||||
;; Opening backet: [ or <
|
||||
(car brackets)
|
||||
;; Starting date/time: YYYY-MM-DD DAY[ HH:MM]
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-start hour-start)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
0 (or minute-start 0) (or hour-start 0)
|
||||
day-start month-start year-start))
|
||||
;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM]
|
||||
(let ((hour-end (org-element-property :hour-end timestamp))
|
||||
(minute-end (org-element-property :minute-end timestamp)))
|
||||
(pcase type
|
||||
((or `active `inactive)
|
||||
;; `org-element-timestamp-parser' uses this type
|
||||
;; when no time/date range is provided. So,
|
||||
;; should normally return nil in this clause.
|
||||
(pcase range-type
|
||||
(`nil
|
||||
;; `org-element-timestamp-parser' assigns end
|
||||
;; times for `active'/`inactive' TYPE if start
|
||||
;; time is not nil. But manually built
|
||||
;; timestamps may not contain end times, so
|
||||
;; check for end times anyway.
|
||||
(when (and hour-start hour-end minute-start minute-end
|
||||
(or (/= hour-start hour-end)
|
||||
(/= minute-start minute-end)))
|
||||
;; Could also throw an error. Return range
|
||||
;; timestamp nevertheless to preserve
|
||||
;; historical behavior.
|
||||
(format "-%02d:%02d" hour-end minute-end)))
|
||||
((or `timerange `daterange)
|
||||
(error "`:range-type' must be `nil' for `active'/`inactive' type"))))
|
||||
;; Range must be present.
|
||||
((or `active-range `inactive-range)
|
||||
(pcase range-type
|
||||
;; End time: -HH:MM.
|
||||
;; Fall back to start time if end time is not defined (arbitrary historical choice).
|
||||
;; Error will be thrown if both end and begin time is not defined.
|
||||
(`timerange (format "-%02d:%02d" (or hour-end hour-start) (or minute-end minute-start)))
|
||||
;; End date: TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM
|
||||
((or `daterange
|
||||
;; Should never happen in the output of `org-element-timestamp-parser'.
|
||||
;; Treat as an equivalent of `daterange' arbitrarily.
|
||||
`nil)
|
||||
(concat
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end
|
||||
"--" (car brackets)
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-end hour-end)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
;; Closing HH:MM missing is a valid scenario.
|
||||
0 (or minute-end 0) (or hour-end 0)
|
||||
;; YEAR/MONTH/DAY-END will always be present
|
||||
;; for `daterange' range-type, as parsed by
|
||||
;; `org-element-timestamp-parser'.
|
||||
;; For manually constructed timestamp
|
||||
;; object, arbitrarily fall back to starting
|
||||
;; date.
|
||||
(or (org-element-property :day-end timestamp) day-start)
|
||||
(or (org-element-property :month-end timestamp) month-start)
|
||||
(or (org-element-property :year-end timestamp) year-start)))))))))
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end))))
|
||||
;; diary type.
|
||||
(org-element-property :raw-value timestamp))))
|
||||
(let ((day-start (org-element-property :day-start timestamp))
|
||||
(month-start (org-element-property :month-start timestamp))
|
||||
(year-start (org-element-property :year-start timestamp)))
|
||||
;; Return nil when start date is not available. Could also
|
||||
;; throw an error, but the current behavior is historical.
|
||||
(when (or (and day-start month-start year-start)
|
||||
(eq type 'diary))
|
||||
(let* ((repeat-string
|
||||
(concat
|
||||
(pcase (org-element-property :repeater-type timestamp)
|
||||
(`cumulate "+") (`catch-up "++") (`restart ".+"))
|
||||
(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"))
|
||||
(when-let ((repeater-deadline-value
|
||||
(org-element-property :repeater-deadline-value timestamp))
|
||||
(repeater-deadline-unit
|
||||
(org-element-property :repeater-deadline-unit timestamp)))
|
||||
(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
|
||||
(pcase (org-element-property :warning-type timestamp)
|
||||
(`first "--") (`all "-"))
|
||||
(let ((val (org-element-property :warning-value timestamp)))
|
||||
(and val (number-to-string val)))
|
||||
(pcase (org-element-property :warning-unit timestamp)
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))
|
||||
(hour-start (org-element-property :hour-start timestamp))
|
||||
(minute-start (org-element-property :minute-start timestamp))
|
||||
(brackets
|
||||
(if (member
|
||||
type
|
||||
'(inactive inactive-range))
|
||||
(cons "[" "]")
|
||||
;; diary as well
|
||||
(cons "<" ">")))
|
||||
(timestamp-end
|
||||
(concat
|
||||
(and (org-string-nw-p repeat-string) (concat " " repeat-string))
|
||||
(and (org-string-nw-p warning-string) (concat " " warning-string))
|
||||
(cdr brackets))))
|
||||
(concat
|
||||
;; Opening backet: [ or <
|
||||
(car brackets)
|
||||
;; Starting date/time: YYYY-MM-DD DAY[ HH:MM]
|
||||
(if (eq type 'diary)
|
||||
(concat
|
||||
"%%"
|
||||
(org-element-property :diary-sexp timestamp)
|
||||
(when (and minute-start hour-start)
|
||||
(format " %02d:%02d" hour-start minute-start)))
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-start hour-start)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
0 (or minute-start 0) (or hour-start 0)
|
||||
day-start month-start year-start)))
|
||||
;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM]
|
||||
(let ((hour-end (org-element-property :hour-end timestamp))
|
||||
(minute-end (org-element-property :minute-end timestamp)))
|
||||
(pcase type
|
||||
((or `active `inactive)
|
||||
;; `org-element-timestamp-parser' uses this type
|
||||
;; when no time/date range is provided. So,
|
||||
;; should normally return nil in this clause.
|
||||
(pcase range-type
|
||||
(`nil
|
||||
;; `org-element-timestamp-parser' assigns end
|
||||
;; times for `active'/`inactive' TYPE if start
|
||||
;; time is not nil. But manually built
|
||||
;; timestamps may not contain end times, so
|
||||
;; check for end times anyway.
|
||||
(when (and hour-start hour-end minute-start minute-end
|
||||
(or (/= hour-start hour-end)
|
||||
(/= minute-start minute-end)))
|
||||
;; Could also throw an error. Return range
|
||||
;; timestamp nevertheless to preserve
|
||||
;; historical behavior.
|
||||
(format "-%02d:%02d" hour-end minute-end)))
|
||||
((or `timerange `daterange)
|
||||
(error "`:range-type' must be `nil' for `active'/`inactive' type"))))
|
||||
;; Range must be present.
|
||||
((or `active-range `inactive-range
|
||||
(and `diary (guard (eq 'timerange range-type))))
|
||||
(pcase range-type
|
||||
;; End time: -HH:MM.
|
||||
;; Fall back to start time if end time is not defined (arbitrary historical choice).
|
||||
;; Error will be thrown if both end and begin time is not defined.
|
||||
(`timerange (format "-%02d:%02d" (or hour-end hour-start) (or minute-end minute-start)))
|
||||
;; End date: TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM
|
||||
((or `daterange
|
||||
;; Should never happen in the output of `org-element-timestamp-parser'.
|
||||
;; Treat as an equivalent of `daterange' arbitrarily.
|
||||
`nil)
|
||||
(concat
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end
|
||||
"--" (car brackets)
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-end hour-end)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
;; Closing HH:MM missing is a valid scenario.
|
||||
0 (or minute-end 0) (or hour-end 0)
|
||||
;; YEAR/MONTH/DAY-END will always be present
|
||||
;; for `daterange' range-type, as parsed by
|
||||
;; `org-element-timestamp-parser'.
|
||||
;; For manually constructed timestamp
|
||||
;; object, arbitrarily fall back to starting
|
||||
;; date.
|
||||
(or (org-element-property :day-end timestamp) day-start)
|
||||
(or (org-element-property :month-end timestamp) month-start)
|
||||
(or (org-element-property :year-end timestamp) year-start)))))))))
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end))))))
|
||||
;;;; Underline
|
||||
|
||||
(defun org-element-underline-parser ()
|
||||
|
|
|
@ -690,43 +690,65 @@ Sunday 7 January 2024
|
|||
(ert-deftest test-org-agenda/skip-deadline-prewarning-if-scheduled ()
|
||||
"Test `org-agenda-skip-deadline-prewarning-if-scheduled'."
|
||||
(org-test-at-time
|
||||
"2024-01-15"
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled t))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
"2024-01-15"
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled t))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should (search-forward "In " nil t))))
|
||||
;; Custom prewarning cookie "-3d", so there should be no warning anyway.
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat -3d> SCHEDULED: <2024-01-19 Fri>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 3))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled nil))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-16 Tue>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-15 Mon>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should (search-forward "In " nil t))))))
|
||||
|
||||
(ert-deftest test-org-agenda/diary-timestamp ()
|
||||
"Test diary timestamp handling."
|
||||
(org-test-at-time
|
||||
"2024-01-15"
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 01 15 2024)>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(should (search-forward "foo" nil t)))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 02 15 2024)>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should (search-forward "In " nil t))))
|
||||
;; Custom prewarning cookie "-3d", so there should be no warning anyway.
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat -3d> SCHEDULED: <2024-01-19 Fri>"
|
||||
(should-not (search-forward "foo" nil t)))
|
||||
;; Test time and time ranges in diary timestamps.
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 01 15 2024) 12:00>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 3))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(should (search-forward "12:00" nil t)))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 01 15 2024) 12:00-14:00>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled nil))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-16 Tue>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should-not (search-forward "In " nil t))))
|
||||
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-15 Mon>"
|
||||
(org-agenda-list nil nil 1)
|
||||
(should (search-forward "In " nil t))))))
|
||||
(should (search-forward "12:00-14:00" nil t)))))
|
||||
|
||||
|
||||
;; agenda redo
|
||||
|
|
|
@ -3989,8 +3989,31 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
|
|||
(org-test-parse-and-interpret
|
||||
"<2012-03-29 thu. 16:40-16:41>")))
|
||||
;; Diary.
|
||||
(should (equal (org-test-parse-and-interpret "<%%diary-float t 4 2>")
|
||||
"<%%diary-float t 4 2>\n"))
|
||||
(should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2)>")
|
||||
"<%%(diary-float t 4 2)>\n"))
|
||||
;; Diary with time.
|
||||
(should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2) 12:00>")
|
||||
"<%%(diary-float t 4 2) 12:00>\n"))
|
||||
(should (equal (org-test-parse-and-interpret "<%%(diary-cyclic 1 1 1 2020) 12:00-14:00>")
|
||||
"<%%(diary-cyclic 1 1 1 2020) 12:00-14:00>\n"))
|
||||
(org-test-with-temp-text "<%%(diary-float t 4 2) 12:00>"
|
||||
(let ((ts (org-element-context)))
|
||||
(should (org-element-type-p ts 'timestamp))
|
||||
(should (eq 'diary (org-element-property :type ts)))
|
||||
(should (eq nil (org-element-property :range-type ts)))
|
||||
(should (equal 12 (org-element-property :hour-start ts)))
|
||||
(should (equal 0 (org-element-property :minute-start ts)))
|
||||
(should-not (org-element-property :hour-end ts))
|
||||
(should-not (org-element-property :minute-end ts))))
|
||||
(org-test-with-temp-text "<%%(diary-float t 4 2) 12:00-14:01>"
|
||||
(let ((ts (org-element-context)))
|
||||
(should (org-element-type-p ts 'timestamp))
|
||||
(should (eq 'diary (org-element-property :type ts)))
|
||||
(should (eq 'timerange (org-element-property :range-type ts)))
|
||||
(should (equal 12 (org-element-property :hour-start ts)))
|
||||
(should (equal 0 (org-element-property :minute-start ts)))
|
||||
(should (equal 14 (org-element-property :hour-end ts)))
|
||||
(should (equal 1 (org-element-property :minute-end ts)))))
|
||||
;; Timestamp with repeater interval, repeater deadline, with delay, with combinations.
|
||||
(should
|
||||
(string-match "<2012-03-29 .* \\+1y>"
|
||||
|
|
Loading…
Reference in New Issue