Function: diary-icalendar-parse-entry

diary-icalendar-parse-entry is a byte-compiled function defined in diary-icalendar.el.gz.

Signature

(diary-icalendar-parse-entry BEGIN END &optional VTIMEZONE TYPE DATE-NODES)

Documentation

Convert the entry between BEGIN and END to a list of iCalendar components.

The region between BEGIN and END will be parsed for a date, time, summary, description, attendees, and UID. This information will be combined into an icalendar-vevent (or icalendar-vjournal or icalendar-vtodo, depending on the values of diary-icalendar-export-nonmarking-entries, diary-icalendar-export-nonmarking-as-vjournal and diary-icalendar-todo-regexp) and that component will be returned wrapped in a list. Returns nil if the entry should not be exported according to diary-icalendar-export-nonmarking-entries.

If diary-icalendar-export-linewise is non-nil, then a top-level call to this function will return a list of several such components. (Thus, the function always returns a list of components.)

VTIMEZONE, if specified, should be the icalendar-vtimezone in which times in the entry appear. If diary-icalendar-time-zone-export-strategy is not either 'to-utc or
'floating, VTIMEZONE must be provided.

DATE-NODES and TYPE should be nil in a top-level call; they are used in recursive calls to this function made by diary-icalendar-parse-entry-linewise.

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/diary-icalendar.el.gz
(defun di:parse-entry (begin end &optional vtimezone type date-nodes)
  "Convert the entry between BEGIN and END to a list of iCalendar components.

The region between BEGIN and END will be parsed for a date, time,
summary, description, attendees, and UID.  This information will be
combined into an `icalendar-vevent' (or `icalendar-vjournal' or
`icalendar-vtodo', depending on the values of
`diary-icalendar-export-nonmarking-entries',
`diary-icalendar-export-nonmarking-as-vjournal' and
`diary-icalendar-todo-regexp') and that component will be returned
wrapped in a list.  Returns nil if the entry should not be exported
according to `diary-icalendar-export-nonmarking-entries'.

If `diary-icalendar-export-linewise' is non-nil, then a top-level call
to this function will return a list of several such components.  (Thus,
the function always returns a list of components.)

VTIMEZONE, if specified, should be the `icalendar-vtimezone' in which
times in the entry appear.  If
`diary-icalendar-time-zone-export-strategy' is not either \\='to-utc or
\\='floating, VTIMEZONE must be provided.

DATE-NODES and TYPE should be nil in a top-level call; they are used in
recursive calls to this function made by
`diary-icalendar-parse-entry-linewise'."
  (save-restriction
    (narrow-to-region begin end)
    (goto-char (point-min))
    (let (sexp dateform weekday tzid transparency all-props should-recurse)
      (setq should-recurse (and di:export-linewise (not date-nodes) (not type)))
      (when (ical:vtimezone-component-p vtimezone)
        (setq tzid (ical:with-property-of vtimezone 'ical:tzid)))
      (unless date-nodes
        ;; If we don't already have date information, we are in a
        ;; top-level call and need to collect the date and type
        ;; information from the start of the entry:
        (setq type (di:parse-entry-type))
        ;; N.B. the following four parsing functions successively
        ;; narrow the current restriction past anything they parse:
        (setq transparency (di:parse-transparency type))
        (setq sexp (di:parse-sexp))
        (setq dateform (di:parse-date-form))
        (setq weekday (di:parse-weekday-name))
        (setq date-nodes
              (append
               transparency
               (when sexp (di:sexp-to-nodes sexp vtimezone))
               (when dateform
                 (apply #'di:dates-to-recurrence dateform))
               (when (and weekday (not dateform))
                 (di:weekday-to-recurrence weekday)))))

      (when type ; nil means entry should not be exported
        (if should-recurse
            ;; If we are in a top level call and should export linewise,
            ;; do that recursively now:
            (di:parse-entry-linewise (point) end vtimezone type date-nodes)

          ;; Otherwise, we are either in a recursive call with a
          ;; narrower restriction, or don't need to export linewise.  In
          ;; both cases, we gather the remaining data from the current
          ;; restriction and combine everything into a component node:
          (let* ((times (di:parse-time))
                 (start-time (when times (car times)))
                 (end-time (when times (cadr times))))
            ;; Combine clock time values in the current restriction with
            ;; date information parsed at the top level.  Doing this here
            ;; allows us to combine a different time on each line of an
            ;; entry exported linewise with the date information for the
            ;; whole entry:
            (dolist (node date-nodes)
              (ical:with-property node nil
                (cond
                 ((and (ical:dtstart-property-p node)
                       (eq 'ical:date value-type)
                       start-time)
                  (let ((dtstart
                         (di:convert-time-via-strategy
                          (ical:date-time-variant
                           start-time
                           :year (calendar-extract-year value)
                           :month (calendar-extract-month value)
                           :day (calendar-extract-day value))
                          vtimezone)))
                    (push (ical:make-property ical:dtstart dtstart
                            (ical:tzidparam tzid))
                          all-props)
                    (when end-time
                      ;; an end time parsed from a time specification
                      ;; in the entry is always on the same day as
                      ;; DTSTART.
                      (let* ((dtend
                              (di:convert-time-via-strategy
                               (ical:date-time-variant
                                end-time
                                :year (calendar-extract-year value)
                                :month (calendar-extract-month value)
                                :day (calendar-extract-day value))
                               vtimezone))
                             (is-recurring
                              (seq-find
                               (lambda (n) (or (ical:rrule-property-p n)
                                               (ical:rdate-property-p n)))
                               date-nodes)))
                        (if is-recurring
                            ;; If the entry is recurring, we interpret
                            ;; the end time as giving us a duration for all
                            ;; recurrences:
                            (progn
                              (when (seq-find #'ical:duration-property-p
                                              date-nodes)
                                (ical:warn
                                 (concat "Parsed both duration and end time; "
                                         "ignoring end time specification")
                                 :buffer (current-buffer)
                                 :position (point)))
                              (push (ical:make-property ical:duration
                                        (ical:duration-between dtstart dtend))
                                    all-props))
                          ;; Otherwise we make a normal DTEND:
                          (push (ical:make-property ical:dtend dtend)
                                all-props))))))

                 ((and (ical:rdate-property-p node)
                       start-time
                       (seq-every-p (apply-partially #'eq 'ical:date)
                                    value-types))
                  (let ((rdates
                         (mapcar
                          (lambda (dt)
                            (if end-time
                                (ical:make-period
                                 (di:convert-time-via-strategy
                                  (ical:date-time-variant
                                   start-time
                                   :year (calendar-extract-year dt)
                                   :month (calendar-extract-month dt)
                                   :day (calendar-extract-day dt))
                                  vtimezone)
                                 :end
                                 (di:convert-time-via-strategy
                                  (ical:date-time-variant
                                   end-time
                                   :year (calendar-extract-year dt)
                                   :month (calendar-extract-month dt)
                                   :day (calendar-extract-day dt))
                                  vtimezone))
                              (di:convert-time-via-strategy
                               (ical:date-time-variant
                                start-time
                                :year (calendar-extract-year dt)
                                :month (calendar-extract-month dt)
                                :day (calendar-extract-day dt))
                               vtimezone)))
                          values)))
                    (push (ical:make-property ical:rdate rdates
                            (ical:tzidparam tzid))
                          all-props)))

                   ;; preserve any other node read from date, e.g. RRULE, as is:
                   (node (push node all-props))))))

          ;; In a VTODO, entry date must become the DUE date; either
          ;; DTEND becomes DUE, or if there is no DTEND, then DTSTART:
          (when (eq type 'ical:vtodo)
            (unless (catch 'found-dtend
                      (dolist (node all-props)
                        (when (ical:dtend-property-p node)
                          (ical:ast-node-set-type node 'ical:due)
                          (throw 'found-dtend t))))
              (dolist (node all-props)
                (when (ical:dtstart-property-p node)
                  (ical:ast-node-set-type node 'ical:due)))))

          ;; Collect the remaining properties:
          (setq all-props (append (di:parse-summary-and-description) all-props))
          (setq all-props (append (di:parse-attendees-and-organizer) all-props))
          (push
           (ical:make-property ical:dtstamp
               (icr:tz-decode-time (current-time) t)) ; ensure UTC
           all-props)
          (let ((class (di:parse-class))
                (location (di:parse-location))
                (status (di:parse-status))
                (url (di:parse-url)))
            (when class (push class all-props))
            (when location (push location all-props))
            (when status (push status all-props))
            (when url (push url all-props)))
          (push (or (di:parse-uid)
                     (ical:make-property ical:uid
                         (ical:make-uid all-props)))
                all-props)

          ;; Allow users to add to the properties parsed:
          (when (functionp di:other-properties-parser)
            (let ((others (funcall di:other-properties-parser type all-props)))
              (dolist (p others)
                (condition-case nil
                    (push (ical:ast-node-valid-p p)
                          all-props)
                  (ical:validation-error
                   (ical:warn
                    (format "`%s' returned invalid `%s' property; ignoring"
                            di:other-properties-parser
                            (ical:ast-node-type p))
                    :buffer (current-buffer)
                    :position (point)))))))

          ;; Construct, validate and return a component of the appropriate type:
          (let ((component
                 (ical:ast-node-valid-p
                  (ical:make-ast-node type nil all-props))))

            ;; Add alarms per `diary-icalendar-export-alarms', except for
            ;; in VJOURNAL, where alarms are not allowed:
            ;; TODO: should we also add alarms for `diary-remind' sexps?
            (when (not (eq type 'ical:vjournal))
              (di:add-valarms component vtimezone))

            ;; Return the component wrapped in a list (for type consistency):
            (list component)))))))