org-element: Use the new org-element-ast library

* lisp/org-element.el (org-element-class):
(org-element-interpret-data): Explicitly consider anonymous syntax
node type.
* lisp/org-element.el (org-element-put-property):
(org-element-set-contents):
(org-element-secondary-p):
(org-element-adopt-elements):
(org-element-extract-element):
(org-element-insert-before):
(org-element-set-element):
(org-element-create):
(org-element-copy):
(org-element-lineage): Remove from org-element.el, using
org-element-ast function versions.
* lisp/org-element.el (org-element-headline-parser):
(org-element-inlinetask-parser):
(org-element-item-parser):
(org-element-citation-parser):
(org-element-citation-reference-parser): Assign :secondary property.
(org-element-parse-buffer):
(org-element-parse-secondary-string): Resolve deferred properties.
(org-element--cache-shift-positions): Use the new AST API.
(org-element--cache-for-removal): Use optional argument for
`org-element-set-element' to keep the needed property values.
(org-element--cache-gapless): Bump element cache version when persisting.
* testing/lisp/test-org-element.el (test-org-element/class): Update
test using the new :secondary property convention.
* lisp/ox-man.el (org-man-paragraph): Use the new AST API.
This commit is contained in:
Ihor Radchenko 2023-05-20 13:29:04 +02:00
parent ea9d5b45db
commit 924a64da39
No known key found for this signature in database
GPG Key ID: 6470762A7DA11D8B
3 changed files with 86 additions and 257 deletions

View File

@ -507,28 +507,7 @@ past the brackets."
;; elements and objects. `org-element-copy' returns an element or ;; elements and objects. `org-element-copy' returns an element or
;; object, stripping its parent property in the process. ;; object, stripping its parent property in the process.
(defsubst org-element-type (element) (require 'org-element-ast)
"Return type of ELEMENT.
The function returns the type of the element or object provided.
It can also return the following special value:
`plain-text' for a string
`org-data' for a complete document
nil in any other case."
(cond
((not (consp element)) (and (stringp element) 'plain-text))
((symbolp (car element)) (car element))))
(defsubst org-element-property (property element)
"Extract the value from the PROPERTY of an ELEMENT."
(if (stringp element) (get-text-property 0 property element)
(plist-get (nth 1 element) property)))
(defsubst org-element-contents (element)
"Extract contents from an ELEMENT."
(cond ((not (consp element)) nil)
((symbolp (car element)) (nthcdr 2 element))
(t element)))
(defsubst org-element-restriction (element) (defsubst org-element-restriction (element)
"Return restriction associated to ELEMENT. "Return restriction associated to ELEMENT.
@ -537,45 +516,12 @@ element or object type."
(cdr (assq (if (symbolp element) element (org-element-type element)) (cdr (assq (if (symbolp element) element (org-element-type element))
org-element-object-restrictions))) org-element-object-restrictions)))
(defsubst org-element-put-property (element property value)
"In ELEMENT set PROPERTY to VALUE.
Return modified element."
(if (stringp element) (org-add-props element nil property value)
(setcar (cdr element) (plist-put (nth 1 element) property value))
element))
(defsubst org-element-set-contents (element &rest contents)
"Set ELEMENT's contents to CONTENTS.
Return ELEMENT."
(cond ((null element) contents)
((not (symbolp (car element)))
(if (not (listp element))
;; Non-element.
contents
;; Anonymous element (el1 el2 ...)
(setcar element (car contents))
(setcdr element (cdr contents))
element))
((cdr element) (setcdr (cdr element) contents) element)
(t (nconc element contents))))
(defun org-element-secondary-p (object)
"Non-nil when OBJECT directly belongs to a secondary string.
Return value is the property name, as a keyword, or nil."
(let* ((parent (org-element-property :parent object))
(properties (cdr (assq (org-element-type parent)
org-element-secondary-value-alist))))
(catch 'exit
(dolist (p properties)
(and (memq object (org-element-property p parent))
(throw 'exit p))))))
(defsubst org-element-class (datum &optional parent) (defsubst org-element-class (datum &optional parent)
"Return class for ELEMENT, as a symbol. "Return class for ELEMENT, as a symbol.
Class is either `element' or `object'. Optional argument PARENT Class is either `element' or `object'. Optional argument PARENT
is the element or object containing DATUM. It defaults to the is the element or object containing DATUM. It defaults to the
value of DATUM `:parent' property." value of DATUM `:parent' property."
(let ((type (org-element-type datum)) (let ((type (org-element-type datum t))
(parent (or parent (org-element-property :parent datum)))) (parent (or parent (org-element-property :parent datum))))
(cond (cond
;; Trivial cases. ;; Trivial cases.
@ -584,153 +530,24 @@ value of DATUM `:parent' property."
;; Special cases. ;; Special cases.
((eq type 'org-data) 'element) ((eq type 'org-data) 'element)
((eq type 'plain-text) 'object) ((eq type 'plain-text) 'object)
((not type) 'object) ((eq type 'anonymous) 'object)
((not type) nil)
;; Pseudo object or elements. Make a guess about its class. ;; Pseudo object or elements. Make a guess about its class.
;; Basically a pseudo object is contained within another object, ;; Basically a pseudo object is contained within another object,
;; a secondary string or a container element. ;; a secondary string or a container element.
((not parent) 'element) ((not parent) 'element)
(t (t
(let ((parent-type (org-element-type parent))) (let ((parent-type (org-element-type parent t)))
(cond ((not parent-type) 'object) (cond ((eq 'anonymous parent-type) 'object)
((memq parent-type org-element-object-containers) 'object) ((memq parent-type org-element-object-containers) 'object)
((org-element-secondary-p datum) 'object) ((org-element-secondary-p datum) 'object)
(t 'element))))))) (t 'element)))))))
(defsubst org-element-adopt-elements (parent &rest children)
"Append elements to the contents of another element.
PARENT is an element or object. CHILDREN can be elements,
objects, or a strings.
The function takes care of setting `:parent' property for CHILD.
Return parent element."
(declare (indent 1))
(if (not children) parent
;; Link every child to PARENT. If PARENT is nil, it is a secondary
;; string: parent is the list itself.
(dolist (child children)
(when child
(org-element-put-property child :parent (or parent children))))
;; Add CHILDREN at the end of PARENT contents.
(when parent
(apply #'org-element-set-contents
parent
(nconc (org-element-contents parent) children)))
;; Return modified PARENT element.
(or parent children)))
(defun org-element-extract-element (element)
"Extract ELEMENT from parse tree.
Remove element from the parse tree by side-effect, and return it
with its `:parent' property stripped out."
(let ((parent (org-element-property :parent element))
(secondary (org-element-secondary-p element)))
(if secondary
(org-element-put-property
parent secondary
(delq element (org-element-property secondary parent)))
(apply #'org-element-set-contents
parent
(delq element (org-element-contents parent))))
;; Return ELEMENT with its :parent removed.
(org-element-put-property element :parent nil)))
(defun org-element-insert-before (element location)
"Insert ELEMENT before LOCATION in parse tree.
LOCATION is an element, object or string within the parse tree.
Parse tree is modified by side effect."
(let* ((parent (org-element-property :parent location))
(property (org-element-secondary-p location))
(siblings (if property (org-element-property property parent)
(org-element-contents parent)))
;; Special case: LOCATION is the first element of an
;; independent secondary string (e.g. :title property). Add
;; ELEMENT in-place.
(specialp (and (not property)
(eq siblings parent)
(eq (car parent) location))))
;; Install ELEMENT at the appropriate LOCATION within SIBLINGS.
(cond (specialp)
((or (null siblings) (eq (car siblings) location))
(push element siblings))
((null location) (nconc siblings (list element)))
(t
(let ((index (cl-position location siblings)))
(unless index (error "No location found to insert element"))
(push element (cdr (nthcdr (1- index) siblings))))))
;; Store SIBLINGS at appropriate place in parse tree.
(cond
(specialp (setcdr parent (copy-sequence parent)) (setcar parent element))
(property (org-element-put-property parent property siblings))
(t (apply #'org-element-set-contents parent siblings)))
;; Set appropriate :parent property.
(org-element-put-property element :parent parent)))
(defconst org-element--cache-element-properties (defconst org-element--cache-element-properties
'(:cached '(:cached
:org-element--cache-sync-key) :org-element--cache-sync-key)
"List of element properties used internally by cache.") "List of element properties used internally by cache.")
(defun org-element-set-element (old new)
"Replace element or object OLD with element or object NEW.
The function takes care of setting `:parent' property for NEW."
;; Ensure OLD and NEW have the same parent.
(org-element-put-property new :parent (org-element-property :parent old))
(dolist (p org-element--cache-element-properties)
(when (org-element-property p old)
(org-element-put-property new p (org-element-property p old))))
(if (or (memq (org-element-type old) '(plain-text nil))
(memq (org-element-type new) '(plain-text nil)))
;; We cannot replace OLD with NEW since one of them is not an
;; object or element. We take the long path.
(progn (org-element-insert-before new old)
(org-element-extract-element old))
;; Since OLD is going to be changed into NEW by side-effect, first
;; make sure that every element or object within NEW has OLD as
;; parent.
(dolist (blob (org-element-contents new))
(org-element-put-property blob :parent old))
;; Transfer contents.
(apply #'org-element-set-contents old (org-element-contents new))
;; Overwrite OLD's properties with NEW's.
(setcar (cdr old) (nth 1 new))
;; Transfer type.
(setcar old (car new))))
(defun org-element-create (type &optional props &rest children)
"Create a new element of type TYPE.
Optional argument PROPS, when non-nil, is a plist defining the
properties of the element. CHILDREN can be elements, objects or
strings."
(apply #'org-element-adopt-elements (list type props) children))
(defun org-element-copy (datum)
"Return a copy of DATUM.
DATUM is an element, object, string or nil. `:parent' property
is cleared and contents are removed in the process."
(when datum
(let ((type (org-element-type datum)))
(pcase type
(`org-data (list 'org-data nil))
(`plain-text (substring-no-properties datum))
(`nil (copy-sequence datum))
(_
(let ((element-copy (list type (plist-put (copy-sequence (nth 1 datum)) :parent nil))))
;; We cannot simply return the copies property list. When
;; DATUM is i.e. a headline, it's property list (`:title'
;; in case of headline) can contain parsed objects. The
;; objects will contain `:parent' property set to the DATUM
;; itself. When copied, these inner `:parent' property
;; values will contain incorrect object decoupled from
;; DATUM. Changes to the DATUM copy will not longer be
;; reflected in the `:parent' properties. So, we need to
;; reassign inner `:parent' properties to the DATUM copy
;; explicitly.
(org-element-map element-copy (cons 'plain-text org-element-all-objects)
(lambda (obj) (when (equal datum (org-element-property :parent obj))
(org-element-put-property obj :parent element-copy))))
element-copy))))))
(defvar org-element--string-cache (obarray-make) (defvar org-element--string-cache (obarray-make)
"Obarray holding tag strings and todo keyword objects. "Obarray holding tag strings and todo keyword objects.
We use shared string storage to reduce memory footprint of the syntax We use shared string storage to reduce memory footprint of the syntax
@ -1201,7 +1018,10 @@ Assume point is at beginning of the headline."
:footnote-section-p footnote-section-p :footnote-section-p footnote-section-p
:archivedp archivedp :archivedp archivedp
:commentedp commentedp :commentedp commentedp
:post-affiliated begin) :post-affiliated begin
:secondary (alist-get
'headline
org-element-secondary-value-alist))
time-props time-props
standard-props)))) standard-props))))
(org-element-put-property (org-element-put-property
@ -1438,7 +1258,10 @@ Assume point is at beginning of the inline task."
:post-blank (1- (count-lines (or task-end begin) end)) :post-blank (1- (count-lines (or task-end begin) end))
:post-affiliated begin :post-affiliated begin
:archivedp archivedp :archivedp archivedp
:commentedp commentedp) :commentedp commentedp
:secondary (alist-get
'inlinetask
org-element-secondary-value-alist))
time-props time-props
standard-props)))) standard-props))))
(org-element-put-property (org-element-put-property
@ -1565,7 +1388,10 @@ Assume point is at the beginning of the item."
:structure struct :structure struct
:pre-blank pre-blank :pre-blank pre-blank
:post-blank (count-lines (or contents-end begin) end) :post-blank (count-lines (or contents-end begin) end)
:post-affiliated begin)))) :post-affiliated begin
:secondary (alist-get
'item
org-element-secondary-value-alist)))))
(org-element-put-property (org-element-put-property
item :tag item :tag
(let ((raw (org-list-get-tag begin struct))) (let ((raw (org-list-get-tag begin struct)))
@ -3090,7 +2916,10 @@ Assume point is at the beginning of the citation."
:post-blank (progn :post-blank (progn
(goto-char closing) (goto-char closing)
(skip-chars-forward " \t")) (skip-chars-forward " \t"))
:end (point))))) :end (point)
:secondary (alist-get
'citation
org-element-secondary-value-alist)))))
;; `:contents-begin' depends on the presence of ;; `:contents-begin' depends on the presence of
;; a non-empty common prefix. ;; a non-empty common prefix.
(goto-char first-key-end) (goto-char first-key-end)
@ -3161,7 +2990,10 @@ Assume point is at the beginning of the reference."
(list :key key (list :key key
:begin begin :begin begin
:end end :end end
:post-blank 0)))) :post-blank 0
:secondary (alist-get
'citation-reference
org-element-secondary-value-alist)))))
(when (< begin key-start) (when (< begin key-start)
(org-element-put-property (org-element-put-property
reference :prefix reference :prefix
@ -4526,11 +4358,17 @@ This function assumes that current major mode is `org-mode'."
(let ((org-data (org-element-org-data-parser)) (let ((org-data (org-element-org-data-parser))
(gc-cons-threshold #x40000000)) (gc-cons-threshold #x40000000))
(org-skip-whitespace) (org-skip-whitespace)
(org-element--parse-elements (setq org-data
(line-beginning-position) (point-max) (org-element--parse-elements
;; Start in `first-section' mode so text before the first (line-beginning-position) (point-max)
;; headline belongs to a section. ;; Start in `first-section' mode so text before the first
'first-section nil granularity visible-only org-data)))) ;; headline belongs to a section.
'first-section nil granularity visible-only org-data))
(org-element-map ; undefer
org-data t
(lambda (el) (org-element-properties-resolve el t))
nil nil nil t)
org-data)))
(defun org-element-parse-secondary-string (string restriction &optional parent) (defun org-element-parse-secondary-string (string restriction &optional parent)
"Recursively parse objects in STRING and return structure. "Recursively parse objects in STRING and return structure.
@ -4546,7 +4384,8 @@ If STRING is the empty string or nil, return nil."
(cond (cond
((not string) nil) ((not string) nil)
((equal string "") nil) ((equal string "") nil)
(t (let ((local-variables (buffer-local-variables))) (t (let ((local-variables (buffer-local-variables))
rtn)
(with-temp-buffer (with-temp-buffer
(dolist (v local-variables) (dolist (v local-variables)
(ignore-errors (ignore-errors
@ -4559,8 +4398,13 @@ If STRING is the empty string or nil, return nil."
(let ((inhibit-read-only t)) (insert string)) (let ((inhibit-read-only t)) (insert string))
;; Prevent "Buffer *temp* modified; kill anyway?". ;; Prevent "Buffer *temp* modified; kill anyway?".
(restore-buffer-modified-p nil) (restore-buffer-modified-p nil)
(org-element--parse-objects (setq rtn
(point-min) (point-max) nil restriction parent)))))) (org-element--parse-objects
(point-min) (point-max) nil restriction parent))
;; Resolve deferred.
(org-element-map rtn t
(lambda (el) (org-element-properties-resolve el t)))
rtn)))))
(defun org-element-map (defun org-element-map
(data types fun &optional info first-match no-recursion with-affiliated) (data types fun &optional info first-match no-recursion with-affiliated)
@ -5029,7 +4873,7 @@ to interpret. Return Org syntax as a string."
(results (results
(cond (cond
;; Secondary string. ;; Secondary string.
((not type) ((eq type 'anonymous)
(mapconcat (lambda (obj) (funcall fun obj parent)) (mapconcat (lambda (obj) (funcall fun obj parent))
data data
"")) ""))
@ -5070,7 +4914,7 @@ to interpret. Return Org syntax as a string."
(eq (org-element-property :pre-blank parent) (eq (org-element-property :pre-blank parent)
0))))) 0)))))
"")))))) ""))))))
(if (memq type '(org-data nil)) results (if (memq type '(org-data anonymous)) results
;; Build white spaces. If no `:post-blank' property ;; Build white spaces. If no `:post-blank' property
;; is specified, assume its value is 0. ;; is specified, assume its value is 0.
(let ((blank (or (org-element-property :post-blank data) 0))) (let ((blank (or (org-element-property :post-blank data) 0)))
@ -5922,21 +5766,21 @@ optional argument PROPS is a list of keywords, only shift
properties provided in that list. properties provided in that list.
Properties are modified by side-effect." Properties are modified by side-effect."
(let ((properties (nth 1 element))) ;; Shift `:structure' property for the first plain list only: it
;; Shift `:structure' property for the first plain list only: it ;; is the only one that really matters and it prevents from
;; is the only one that really matters and it prevents from ;; shifting it more than once.
;; shifting it more than once. (when (and (or (not props) (memq :structure props))
(when (and (or (not props) (memq :structure props)) (eq (org-element-type element) 'plain-list)
(eq (org-element-type element) 'plain-list) (not (eq (org-element-type (org-element-property :parent element)) 'item)))
(not (eq (org-element-type (plist-get properties :parent)) 'item))) (let ((structure (org-element-property-1 :structure element)))
(dolist (item (plist-get properties :structure)) (dolist (item structure)
(cl-incf (car item) offset) (cl-incf (car item) offset)
(cl-incf (nth 6 item) offset))) (cl-incf (nth 6 item) offset))))
(dolist (key '( :begin :contents-begin :contents-end :end (dolist (key '( :begin :contents-begin :contents-end :end
:post-affiliated :robust-begin :robust-end)) :post-affiliated :robust-begin :robust-end))
(let ((value (and (or (not props) (memq key props)) (when (and (or (not props) (memq key props))
(plist-get properties key)))) (org-element-property key element))
(and value (plist-put properties key (+ offset value))))))) (cl-incf (org-element-property-1 key element) offset))))
(defvar org-element--cache-interrupt-C-g t (defvar org-element--cache-interrupt-C-g t
"When non-nil, allow the user to abort `org-element--cache-sync'. "When non-nil, allow the user to abort `org-element--cache-sync'.
@ -6921,7 +6765,7 @@ known element in cache (it may start after END)."
(org-element--cache-log-message (org-element--cache-log-message
"Found non-robust headline that can be updated individually: %S" "Found non-robust headline that can be updated individually: %S"
(org-element--format-element current)) (org-element--format-element current))
(org-element-set-element up current) (org-element-set-element up current org-element--cache-element-properties)
t))) t)))
;; If UP is org-data, the situation is similar to ;; If UP is org-data, the situation is similar to
;; headline case. We just need to re-parse the ;; headline case. We just need to re-parse the
@ -6930,7 +6774,7 @@ known element in cache (it may start after END)."
;; potentially alter first-section). ;; potentially alter first-section).
(when (and (eq 'org-data (org-element-type up)) (when (and (eq 'org-data (org-element-type up))
(>= beg (org-element-property :contents-begin up))) (>= beg (org-element-property :contents-begin up)))
(org-element-set-element up (org-with-point-at 1 (org-element-org-data-parser))) (org-element-set-element up (org-with-point-at 1 (org-element-org-data-parser)) org-element--cache-element-properties)
(org-element--cache-log-message (org-element--cache-log-message
"Found non-robust change invalidating org-data. Re-parsing: %S" "Found non-robust change invalidating org-data. Re-parsing: %S"
(org-element--format-element up)) (org-element--format-element up))
@ -7261,14 +7105,23 @@ the cache persistence in the buffer."
;; Only persist cache in file buffers. ;; Only persist cache in file buffers.
(when (and (buffer-file-name) (not no-persistence)) (when (and (buffer-file-name) (not no-persistence))
(when (not org-element-cache-persistent) (when (not org-element-cache-persistent)
(org-persist-unregister 'org-element--headline-cache (current-buffer)) (org-persist-unregister
(org-persist-unregister 'org-element--cache (current-buffer))) 'org-element--headline-cache
(current-buffer)
:remove-related t)
(org-persist-unregister
'org-element--cache
(current-buffer)
:remove-related t))
(when (and org-element-cache-persistent (when (and org-element-cache-persistent
(buffer-file-name (current-buffer))) (buffer-file-name (current-buffer)))
(org-persist-register 'org-element--cache (current-buffer)) (org-persist-register
(org-persist-register 'org-element--headline-cache '((elisp org-element--cache) (version "2.0"))
(current-buffer) (current-buffer))
:inherit 'org-element--cache))) (org-persist-register
'org-element--headline-cache
(current-buffer)
:inherit '((elisp org-element--cache) (version "2.0")))))
(setq-local org-element--cache-change-tic (buffer-chars-modified-tick)) (setq-local org-element--cache-change-tic (buffer-chars-modified-tick))
(setq-local org-element--cache-last-buffer-size (buffer-size)) (setq-local org-element--cache-last-buffer-size (buffer-size))
(setq-local org-element--cache-gapless nil) (setq-local org-element--cache-gapless nil)
@ -8005,7 +7858,7 @@ Providing it allows for quicker computation."
(and (= pos cend) (and (= pos cend)
(or (= (point-max) pos) (or (= (point-max) pos)
(not (memq (char-before pos) (not (memq (char-before pos)
'(?\s ?\t))))))) '(?\s ?\t)))))))
(goto-char cbeg) (goto-char cbeg)
(narrow-to-region (point) cend) (narrow-to-region (point) cend)
(setq parent next) (setq parent next)
@ -8013,30 +7866,6 @@ Providing it allows for quicker computation."
;; Otherwise, return NEXT. ;; Otherwise, return NEXT.
(t (throw 'exit next)))))))))))))) (t (throw 'exit next))))))))))))))
(defun org-element-lineage (datum &optional types with-self)
"List all ancestors of a given element or object.
DATUM is an object or element.
Return ancestors from the closest to the farthest. When optional
argument TYPES is a list of symbols, return the first element or
object in the lineage whose type belongs to that list instead.
When optional argument WITH-SELF is non-nil, lineage includes
DATUM itself as the first element, and TYPES, if provided, also
apply to it.
When DATUM is obtained through `org-element-context' or
`org-element-at-point', only ancestors from its section can be
found. There is no such limitation when DATUM belongs to a full
parse tree."
(let ((up (if with-self datum (org-element-property :parent datum)))
ancestors)
(while (and up (not (memq (org-element-type up) types)))
(unless types (push up ancestors))
(setq up (org-element-property :parent up)))
(if types up (nreverse ancestors))))
(defun org-element-nested-p (elem-A elem-B) (defun org-element-nested-p (elem-A elem-B)
"Non-nil when elements ELEM-A and ELEM-B are nested." "Non-nil when elements ELEM-A and ELEM-B are nested."
(let ((beg-A (org-element-property :begin elem-A)) (let ((beg-A (org-element-property :begin elem-A))

View File

@ -645,19 +645,19 @@ information."
"Transcode a PARAGRAPH element from Org to Man. "Transcode a PARAGRAPH element from Org to Man.
CONTENTS is the contents of the paragraph, as a string. INFO is CONTENTS is the contents of the paragraph, as a string. INFO is
the plist used as a communication channel." the plist used as a communication channel."
(let ((parent (plist-get (nth 1 paragraph) :parent))) (let ((parent (org-element-property :parent paragraph)))
(when parent (when parent
(let ((parent-type (car parent)) (let ((parent-type (org-element-type parent))
(fixed-paragraph "")) (fixed-paragraph ""))
(cond ((and (eq parent-type 'item) (cond ((and (eq parent-type 'item)
(plist-get (nth 1 parent) :bullet )) (org-element-property :bullet parent))
(setq fixed-paragraph (concat "" contents))) (setq fixed-paragraph (concat "" contents)))
((eq parent-type 'section) ((eq parent-type 'section)
(setq fixed-paragraph (concat ".PP\n" contents))) (setq fixed-paragraph (concat ".PP\n" contents)))
((eq parent-type 'footnote-definition) ((eq parent-type 'footnote-definition)
(setq fixed-paragraph contents)) (setq fixed-paragraph contents))
(t (setq fixed-paragraph (concat "" contents)))) (t (setq fixed-paragraph (concat "" contents))))
fixed-paragraph )))) fixed-paragraph))))
;;; Plain List ;;; Plain List

View File

@ -169,7 +169,7 @@ Some other text
(should (should
(eq 'object (eq 'object
(let* ((datum '(foo nil)) (let* ((datum '(foo nil))
(headline `(headline (:title (,datum))))) (headline `(headline (:title (,datum) :secondary (:title)))))
(org-element-put-property datum :parent headline) (org-element-put-property datum :parent headline)
(org-element-class datum))))) (org-element-class datum)))))