From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp12.migadu.com ([2001:41d0:8:6d80::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms5.migadu.com with LMTPS id wO8vArgu8mN8HwEAbAwnHQ (envelope-from ) for ; Sun, 19 Feb 2023 15:14:16 +0100 Received: from aspmx1.migadu.com ([2001:41d0:8:6d80::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp12.migadu.com with LMTPS id uOQ4Argu8mPUBwAAauVa8A (envelope-from ) for ; Sun, 19 Feb 2023 15:14:16 +0100 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 020BC206F1 for ; Sun, 19 Feb 2023 15:14:14 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pTkRV-0004aq-1V; Sun, 19 Feb 2023 09:13:29 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pTkRS-0004ae-L6 for emacs-orgmode@gnu.org; Sun, 19 Feb 2023 09:13:26 -0500 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pTkRQ-0006WV-4H for emacs-orgmode@gnu.org; Sun, 19 Feb 2023 09:13:26 -0500 Received: by mail-lf1-x12d.google.com with SMTP id f25so1014611lfa.5 for ; Sun, 19 Feb 2023 06:13:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:message-id:in-reply-to:date:subject:to:from:user-agent :references:from:to:cc:subject:date:message-id:reply-to; bh=SdvxFV4QvzdKjjMdej6bWAr/01i7D8D0aFKS0ogX1VA=; b=ZnOKp5RsfMc3/IkIC8Hgs9onLwI9fKnQmptNIX84vpt8w9DnvwWClMvOQ1WRIqQ/pi GikzHYR9cpyv4yf2hfNCE9ghasEKz0uQ58JIG0UqKEmtzKhNcdafYnKiL3lnICWjyc5L TBRowcjbNg06U0yXuQ7KTBppQVJbmfJ7qCyqjA4DOvM8DSo7N9/P5ZpyUSbJSyp1+zEQ Hei7Ya/J/vHmSUH+aG7Z1Kaat+Ju3LjddgjWTU4E2TTGZu4HH0IMppl0UgTTDsOZWOUo uhYoy3JKthU4ED09q4hX9t8tnLPVfBu56hQtKSYekFNYYeMJzpkFXZu9SD4aXS4AGxU/ 42Kw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=mime-version:message-id:in-reply-to:date:subject:to:from:user-agent :references:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=SdvxFV4QvzdKjjMdej6bWAr/01i7D8D0aFKS0ogX1VA=; b=dIBTnNKDuQylmYUzrKHCFmuZGFTY3mmZnu8AaO2oDORpTibJs1r7xqHr5pwnKtJeT/ Ryzl2BjglERPuMrjDyetJHBerfI7IkYlVPrvO5XwdFFVor1KVIkW9Cq4D4inAKNPJBkc HjAggxZwauyG+RBKvd4wOSFMEHVNj8Dn2BrIpd8Qt8f238Wg/EfBm2QPFWQ0oD7c7e6a xacRdT1Yij4lsrGsmctRWEon2BEKw2UqaLSu7hHVF1lNaF798GMfFgPKCRoQn1lkZ94K yV7wYm4nRZw97X8P7ETM3ckJ0ZvuM2m7N9MYb/gJjG8jO7etKM7Q4FFMpbntocxTYf6d nD8A== X-Gm-Message-State: AO0yUKWD865iXfAFWUfMGqs1xp4EzNrc7zFb3bhltQC92n0TqyjeJjqh 63TgUzcMJxYDqJorZgit849T9Ke8+JA= X-Google-Smtp-Source: AK7set/Yc3EoDYjVyqqWtLAuDPYDWEMD+JiLZpLgK7fvh+R/TmYFwTahNvvWhpOC7aov7UVAQpWgSw== X-Received: by 2002:ac2:5683:0:b0:4b4:f9df:c6ab with SMTP id 3-20020ac25683000000b004b4f9dfc6abmr208789lfr.60.1676816001846; Sun, 19 Feb 2023 06:13:21 -0800 (PST) Received: from laptop ([62.118.86.114]) by smtp.gmail.com with ESMTPSA id u9-20020ac251c9000000b004db4936c866sm1256807lfm.38.2023.02.19.06.13.20 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 19 Feb 2023 06:13:21 -0800 (PST) References: <87y1ot6dqz.fsf@gmail.com> User-agent: mu4e 1.8.13; emacs 30.0.50 From: Ilya Chernyshov To: emacs-orgmode Subject: Re: [PATCH] org-element-timestamp-interpreter: Return daterange anyway, if DATERANGE is non-nil Date: Sun, 19 Feb 2023 21:11:38 +0700 In-reply-to: <87y1ot6dqz.fsf@gmail.com> Message-ID: <87ttzh68w1.fsf@gmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2a00:1450:4864:20::12d; envelope-from=ichernyshovvv@gmail.com; helo=mail-lf1-x12d.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN ARC-Seal: i=1; s=key1; d=yhetil.org; t=1676816055; a=rsa-sha256; cv=none; b=iNMoJllqdcd1mjSn0IT4qpYlNYHIwlV1psAYbF2wFcM7uh7JBaAknGlnIift9chOrd4yEx aWZkSBTUrQqz0JDYnxQnQtEK6KZPXql8WUaaBHNt8O5Ii2rFsgTuZ4HM5dlgga41GNveae fhY+A+VEna6Y3tn0AAtojB4jRweLmaeYSgPs2v4fZ55u7LPcrFYVowhbFxWKLIXG2bSYwI 6UNXevc/yETFZvUNF+jyuoYdu7FOIhXFA4by2I2dF5klItGTCu9gHlLnejL2JBMKkc2I9M pQzjMw0Z4WhuvnI1xlpXLR3xuzHfRP0v1pseEK1b+cddouI4YHts9nqKbsvsKg== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20210112 header.b=ZnOKp5Rs; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1676816055; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=SdvxFV4QvzdKjjMdej6bWAr/01i7D8D0aFKS0ogX1VA=; b=ap/J0b/cDDSESfwhMEk63VfNsXNGlXYojN8iMPvVEwgVykE4CDghc8s4ro6QUVlU0tuLRn Pe6V5RGXGydTsV9uZJ0MCf/eNhqxYkdrpVm7OKHBABM+44Q8MEqsTA6GhCWL+pmBC4v1d6 tAsr8eOZFi8cC9ghuW3Fg0qHJONKkm1ZbWZPIyacorYKEJRrYu1tmqQ7bmAvZ+jfh8Rlno h9vqwwv70NuHjb11QaFR4NWFy0cNBqhc+AdupXsMrywzIp4rTIxJ7bdwLG73s/FUaOpBUE XIvinS1xrBf2Ei5fg1xhuhqRJLDWN07h/u4MmlxjnYvzM8dBCebLqVMzeRTKOA== X-Migadu-Scanner: scn1.migadu.com X-Migadu-Spam-Score: -7.10 X-Spam-Score: -7.10 X-Migadu-Queue-Id: 020BC206F1 Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20210112 header.b=ZnOKp5Rs; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com X-TUID: 69ARBFjVa5IE --=-=-= Content-Type: text/plain; format=flowed I forgot to attach the patch :) --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-org-element-timestamp-interpreter-Return-daterange-a.patch >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 --=-=-= Content-Type: text/plain; format=flowed -- Best, Ilya --=-=-=--