Function: icalendar-define-component

icalendar-define-component is a macro defined in icalendar-macs.el.gz.

Signature

(icalendar-define-component SYMBOLIC-NAME COMPONENT-NAME DOC &key ((:keyword-face KEYWORD-FACE) 'icalendar-keyword NONDEFAULT-KEYWORD-FACE) ((:name-face NAME-FACE) 'icalendar-component-name NONDEFAULT-NAME-FACE) CHILD-SPEC OTHER-VALIDATOR LINK)

Documentation

Define iCalendar component COMPONENT-NAME under SYMBOLIC-NAME.

COMPONENT-NAME should be the name of the component as it should appear in iCalendar data.

Regular expressions to match the component boundaries are defined named COMPONENT-NAME-begin and COMPONENT-NAME-end (or OTHER-begin and OTHER-end, where OTHER is derived from SYMBOLIC-NAME by removing any prefix icalendar- and suffix
-component if COMPONENT-NAME is nil).
  Group 1 of these regexes matches the "BEGIN" or "END"
    keyword that marks a component boundary.
  Group 2 matches the component name.

A function to match the component boundaries is defined called icalendar-match-COMPONENT-NAME-component (or icalendar-match-OTHER-component, with OTHER as above). This function is used to provide syntax highlighting in icalendar-mode.

The following keyword arguments are accepted:

:child-spec - a plist mapping the following keywords to lists
of type symbols:
  :one - properties or components that must appear exactly once
  :one-or-more - properties or components that must appear at least once and
                   may appear more than once
  :zero-or-one - properties or components that must appear at most once
  :zero-or-more - properties or components that may appear more than once
  :allow-others - if non-nil, other children besides those listed in the above
                   are allowed to appear. (In this case, a :zero-or-more
                   clause is redundant.)

:other-validator - a function to perform any additional validation of
  the component, beyond what icalendar-ast-node-valid-p checks.
  This function should accept one argument, a syntax node. It
  should return non-nil if the node is valid, or signal an
  icalendar-validation-error if it is not. Its name does not
  need to be quoted.

:keyword-face - a face symbol for highlighting the BEGIN/END keyword
  (default: icalendar-keyword)

:name-face - a face symbol for highlighting the component name
  (default: icalendar-component-name)

:link - a string containing a URL for documentation of this component

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/icalendar-macs.el.gz
;; Define components:
(cl-defmacro ical:define-component (symbolic-name component-name doc
                                    &key
                                    ((:keyword-face keyword-face)
                                     'ical:keyword nondefault-keyword-face)
                                    ((:name-face name-face)
                                     'ical:component-name nondefault-name-face)
                                    child-spec
                                    other-validator
                                    link)
  "Define iCalendar component COMPONENT-NAME under SYMBOLIC-NAME.
COMPONENT-NAME should be the name of the component as it should
appear in iCalendar data.

Regular expressions to match the component boundaries are defined
named `COMPONENT-NAME-begin' and `COMPONENT-NAME-end' (or
`OTHER-begin' and `OTHER-end', where `OTHER' is derived from
SYMBOLIC-NAME by removing any prefix `icalendar-' and suffix
`-component' if COMPONENT-NAME is nil).
  Group 1 of these regexes matches the \"BEGIN\" or \"END\"
    keyword that marks a component boundary.
  Group 2 matches the component name.

A function to match the component boundaries is defined called
`icalendar-match-COMPONENT-NAME-component' (or
`icalendar-match-OTHER-component', with OTHER as above).  This
function is used to provide syntax highlighting in
`icalendar-mode'.

The following keyword arguments are accepted:

:child-spec - a plist mapping the following keywords to lists
of type symbols:
  :one           - properties or components that must appear exactly once
  :one-or-more   - properties or components that must appear at least once and
                   may appear more than once
  :zero-or-one   - properties or components that must appear at most once
  :zero-or-more  - properties or components that may appear more than once
  :allow-others  - if non-nil, other children besides those listed in the above
                   are allowed to appear.  (In this case, a :zero-or-more
                   clause is redundant.)

:other-validator - a function to perform any additional validation of
  the component, beyond what `icalendar-ast-node-valid-p' checks.
  This function should accept one argument, a syntax node.  It
  should return non-nil if the node is valid, or signal an
  `icalendar-validation-error' if it is not.  Its name does not
  need to be quoted.

:keyword-face - a face symbol for highlighting the BEGIN/END keyword
  (default: `icalendar-keyword')

:name-face - a face symbol for highlighting the component name
  (default: `icalendar-component-name')

:link - a string containing a URL for documentation of this component"
  (declare (doc-string 2))
  (let* (;; Regexes:
         (name-rx (or component-name 'ical:name))
         (component-dname (if component-name
                              (downcase component-name)
                            (string-trim (symbol-name symbolic-name)
                                         "icalendar-" "-component")))
         (begin-rx-name (intern (concat "icalendar-" component-dname "-begin")))
         (end-rx-name (intern (concat "icalendar-" component-dname "-end")))
         ;; Related functions:
         (matcher-name
          (intern (concat "icalendar-match-" component-dname "-component")))
         (type-predicate-name
          (intern (concat "icalendar-" component-dname "-component-p")))
         ;; Faces:
         (has-faces (or nondefault-name-face nondefault-keyword-face))
         ;; Documentation:
         (header "It names a component type defined by
`icalendar-define-component'.")
         (name-doc (if (not component-name)
                       "\nNAME must match rx `icalendar-name'"
                     ""))
         (syntax-doc (format "Syntax:\nBEGIN:%s\n[contentline ...]\nEND:%1$s%s"
                             (or component-name "NAME")
                             name-doc))
         (child-doc
          (concat
           "The following properties and components are required or "
           "allowed\nas children in syntax nodes of this type:\n\n"
           (ical:format-child-spec child-spec)
           (when (plist-get child-spec :allow-others)
             "\nOther properties and components of any type are also allowed.\n")))
         (full-doc (concat header "\n\n" doc "\n\n" syntax-doc "\n\n" child-doc)))

    `(progn
       ;; Type metadata needs to be available at both compile time and
       ;; run time.  In particular, `ical:value-type-symbol-p' needs to
       ;; work at compile time.
       (eval-and-compile
         (setplist (quote ,symbolic-name)
                   (list
                    'ical:is-type t
                    'ical:is-component t
                    'ical:matcher (function ,matcher-name)
                    'ical:begin-rx (quote ,begin-rx-name)
                    'ical:end-rx (quote ,end-rx-name)
                    'ical:child-spec (quote ,child-spec)
                    'ical:other-validator (function ,other-validator)
                    'ical:type-documentation ,full-doc
                    'ical:link ,link)))

       ;; Regexes which match:
       ;; Group 1: BEGIN or END, and
       ;; Group 2: the component name
       (rx-define ,begin-rx-name
         (seq line-start
              (group-n 1 "BEGIN")
              ":"
              (group-n 2 ,name-rx)
              line-end))

       (rx-define ,end-rx-name
         (seq line-start
              (group-n 1  "END")
              ":"
              (group-n 2 ,name-rx)
              line-end))

       (defun ,matcher-name (limit)
         ,(concat (format "Matcher for %s component boundaries.\n"
                          (or component-name "unrecognized"))
                  "(Defined by `icalendar-define-component'.)")
           (re-search-forward (rx (or ,begin-rx-name ,end-rx-name)) limit t))

       ;; CL-type to represent syntax nodes for this component:
       (defun ,type-predicate-name (node)
         ,(format "Return non-nil if NODE represents a %s component."
                  (or component-name "unrecognized"))
         (and (ical:ast-node-p node)
              (eq (ical:ast-node-type node) (quote ,symbolic-name))))

       (cl-deftype ,symbolic-name () '(satisfies ,type-predicate-name))

       ;; Generate an entry for font-lock-keywords in icalendar-mode:
       (when ,has-faces
         ;; Avoid circular load of icalendar-mode.el in
         ;; icalendar-parser.el (which does not use the *-face
         ;; keywords), while still allowing external code to add to
         ;; font-lock-keywords dynamically:
         (require 'icalendar-mode)
         (push (quote (,matcher-name
                       (1 (quote ,keyword-face) t t)
                       (2 (quote ,name-face) t t)))
               ical:font-lock-keywords))

       ;; Associate the print name with the type symbol for
       ;; `icalendar-parse-component', `icalendar-print-component' etc.:
       (when ,component-name
         (push (cons ,component-name (quote ,symbolic-name))
               ical:component-types)))))