From b47324bc804e64e6cef482ba4897a457252e803a Mon Sep 17 00:00:00 2001 From: Ilya Chernyshov Date: Sat, 18 Feb 2023 14:55:39 +0700 Subject: [PATCH] org-element-timestamp-interpreter: Return daterange anyway, if DATERANGE is non-nil * lisp/org-element (org-element-timestamp-interpreter): Replace silenced argument with optional argument DATERANGE which controls whether to return daterange (even if dates in the range are equal) or timerange (if it's possible). Refactor the code. * lisp/org-element (org-element-planning-interpreter): Call `org-element-timestamp-interpreter' without second argument. * lisp/org-element (org-element-clock-interpreter): Call `org-element-timestamp-interpreter' with DATERANGE set to t, because clock entries only support daterange format. * lisp/org-element (org-element-interpret-data): Call `org-element-timestamp-interpreter' on timestamp object with DATERANGE set to t, if it's inactive-range. * testing/lisp/test-org-element (test-org-element/timestamp-interpreter): Expect timerange returned for timestamp of type active-range from `org-element-timestamp-interpreter' and `org-test-parse-and-interpret' calls. Add tests for `org-element-timestamp-interpreter' with DATERANGE argument. --- lisp/org-element.el | 180 ++++++++++++++----------------- testing/lisp/test-org-element.el | 28 ++++- 2 files changed, 106 insertions(+), 102 deletions(-) diff --git a/lisp/org-element.el b/lisp/org-element.el index d7847a678..9649c6951 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -2042,7 +2042,7 @@ (defun org-element-clock-interpreter (clock _) "Interpret CLOCK element as Org syntax." (concat "CLOCK: " (org-element-timestamp-interpreter - (org-element-property :value clock) nil) + (org-element-property :value clock) t) (let ((duration (org-element-property :duration clock))) (and duration (concat " => " @@ -2673,15 +2673,15 @@ (defun org-element-planning-interpreter (planning _) (list (let ((deadline (org-element-property :deadline planning))) (when deadline (concat org-element-deadline-keyword " " - (org-element-timestamp-interpreter deadline nil)))) + (org-element-timestamp-interpreter deadline)))) (let ((scheduled (org-element-property :scheduled planning))) (when scheduled (concat org-element-scheduled-keyword " " - (org-element-timestamp-interpreter scheduled nil)))) + (org-element-timestamp-interpreter scheduled)))) (let ((closed (org-element-property :closed planning))) (when closed (concat org-element-closed-keyword " " - (org-element-timestamp-interpreter closed nil)))))) + (org-element-timestamp-interpreter closed)))))) " ")) @@ -4020,100 +4020,79 @@ (defun org-element-timestamp-parser () repeater-props warning-props)))))) -(defun org-element-timestamp-interpreter (timestamp _) - "Interpret TIMESTAMP object as Org syntax." - (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")))) - (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")))) - (build-ts-string - ;; Build an Org timestamp string from TIME. ACTIVEP is - ;; non-nil when time stamp is active. If WITH-TIME-P is - ;; non-nil, add a time part. HOUR-END and MINUTE-END - ;; specify a time range in the timestamp. REPEAT-STRING is - ;; the repeater string, if any. - (lambda (time activep &optional with-time-p hour-end minute-end) - (let ((ts (format-time-string - (org-time-stamp-format with-time-p) - time))) - (when (and hour-end minute-end) - (string-match "[012]?[0-9]:[0-5][0-9]" ts) - (setq ts - (replace-match - (format "\\&-%02d:%02d" hour-end minute-end) - nil nil ts))) - (unless activep (setq ts (format "[%s]" (substring ts 1 -1)))) - (dolist (s (list repeat-string warning-string)) - (when (org-string-nw-p s) - (setq ts (concat (substring ts 0 -1) - " " - s - (substring ts -1))))) - ;; Return value. - ts))) - (type (org-element-property :type timestamp))) - (pcase type - ((or `active `inactive) - (let* ((minute-start (org-element-property :minute-start timestamp)) - (minute-end (org-element-property :minute-end timestamp)) - (hour-start (org-element-property :hour-start timestamp)) - (hour-end (org-element-property :hour-end timestamp)) - (time-range-p (and hour-start hour-end minute-start minute-end - (or (/= hour-start hour-end) - (/= minute-start minute-end))))) - (funcall - build-ts-string - (org-encode-time 0 - (or minute-start 0) - (or hour-start 0) - (org-element-property :day-start timestamp) - (org-element-property :month-start timestamp) - (org-element-property :year-start timestamp)) - (eq type 'active) - (and hour-start minute-start) - (and time-range-p hour-end) - (and time-range-p minute-end)))) - ((or `active-range `inactive-range) - (let ((minute-start (org-element-property :minute-start timestamp)) - (minute-end (org-element-property :minute-end timestamp)) - (hour-start (org-element-property :hour-start timestamp)) - (hour-end (org-element-property :hour-end timestamp))) - (concat - (funcall - build-ts-string (org-encode-time - 0 - (or minute-start 0) - (or hour-start 0) - (org-element-property :day-start timestamp) - (org-element-property :month-start timestamp) - (org-element-property :year-start timestamp)) - (eq type 'active-range) - (and hour-start minute-start)) - "--" - (funcall build-ts-string - (org-encode-time - 0 - (or minute-end 0) - (or hour-end 0) - (org-element-property :day-end timestamp) - (org-element-property :month-end timestamp) - (org-element-property :year-end timestamp)) - (eq type 'active-range) - (and hour-end minute-end))))) - (_ (org-element-property :raw-value timestamp))))) - +(defun org-element-timestamp-interpreter (timestamp &optional daterange) + "Interpret TIMESTAMP object as Org syntax. + +If DATERANGE is non-nil, create daterange even if dates in the +range are equal." + (if (member + (org-element-property :type timestamp) + '(active inactive inactive-range active-range)) + (let((start-date + (format-time-string + (org-time-stamp-format nil 'no-brackets) + (org-encode-time + 0 0 0 + (org-element-property :day-start timestamp) + (org-element-property :month-start timestamp) + (org-element-property :year-start timestamp))))) + (when start-date + (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")))) + (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)) + (start-time + (and hour-start minute-start + (format "%02d:%02d" hour-start minute-start))) + (hour-end (org-element-property :hour-end timestamp)) + (minute-end (org-element-property :minute-end timestamp)) + (end-time (and hour-end minute-end + (format "%02d:%02d" hour-end minute-end))) + (day-end (org-element-property :day-end timestamp)) + (month-end (org-element-property :month-end timestamp)) + (year-end (org-element-property :year-end timestamp)) + (time-range-p (and start-time end-time + (not (string= start-time end-time)))) + (end-date + (and year-end month-end day-end + (format-time-string + (org-time-stamp-format nil 'no-brackets) + (org-encode-time + 0 0 0 day-end month-end year-end)))) + (date-range-p (or (and daterange time-range-p) + (and end-date (not (string= end-date start-date))))) + (ts + (concat + "<" start-date (and start-time (concat " " start-time)) + (if date-range-p + (concat ">--<" end-date (and end-time (concat " " end-time))) + (if time-range-p (concat "-" end-time))) + ">"))) + (dolist (s (list repeat-string warning-string)) + (when (org-string-nw-p s) + (setq ts (string-replace ">" (concat " " s ">") ts)))) + (pcase (org-element-property :type timestamp) + ((or `active `active-range) + ts) + ((or `inactive `inactive-range) + (string-replace + "<" "[" + (string-replace ">" "]" ts))))))) + (org-element-property :raw-value timestamp))) ;;;; Underline @@ -5050,7 +5029,10 @@ (defun org-element-interpret-data (data) ((stringp data) data) ;; Element or object without contents. ((not (org-element-contents data)) - (funcall interpret data nil)) + (cond ((eq type 'timestamp) + (funcall interpret data + (eq (org-element-property :type data) 'inactive-range))) + (t (funcall interpret data nil)))) ;; Element or object with contents. (t (funcall diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index 43f1d860f..fd865f38b 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -3235,17 +3235,28 @@ (ert-deftest test-org-element/timestamp-interpreter () :hour-start 16 :minute-start 40)) nil))) ;; Active range. (should - (string-match "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" + (string-match "<2012-03-29 .* 16:40-16:41>" (org-test-parse-and-interpret "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>"))) (should (string-match - "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" + "<2012-03-29 .* 16:40-16:41>" (org-element-timestamp-interpreter '(timestamp (:type active-range :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 :day-end 29 :hour-end 16 :minute-end 41)) nil))) + + (should + (string-match + "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" + (org-element-timestamp-interpreter + '(timestamp + (:type active-range :year-start 2012 :month-start 3 :day-start 29 + :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 + :day-end 29 :hour-end 16 :minute-end 41)) + t))) + ;; Inactive range. (should (string-match "\\[2012-03-29 .* 16:40\\]--\\[2012-03-29 .* 16:41\\]" @@ -3258,7 +3269,18 @@ (ert-deftest test-org-element/timestamp-interpreter () '(timestamp (:type inactive-range :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 - :day-end 29 :hour-end 16 :minute-end 41)) nil))) + :day-end 29 :hour-end 16 :minute-end 41)) t))) + + (should + (string-match + "\\[2012-03-29 .* 16:40-16:41\\]" + (org-element-timestamp-interpreter + '(timestamp + (:type inactive-range :year-start 2012 :month-start 3 :day-start 29 + :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 + :day-end 29 :hour-end 16 :minute-end 41)) + nil))) + ;; Diary. (should (equal (org-test-parse-and-interpret "<%%diary-float t 4 2>") "<%%diary-float t 4 2>\n")) -- 2.39.0