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)))))))