org-element: Handle special cases in setter functions

* contrib/lisp/org-element.el (org-element-put-property,
  org-element-set-contents): Handle special cases like empty or string
  arguments.
(org-element-parse-secondary-string): Correctly set `:parent' property
in objects within the secondary string if element or object containing
it is provided as an optional argument.
(org-element-parse-elements, org-element-parse-objects): Rewrite
functions using setter functions.
This commit is contained in:
Nicolas Goaziou 2012-07-15 12:25:01 +02:00
parent 6762c7ea60
commit ceaeb33629
1 changed files with 96 additions and 132 deletions

View File

@ -3100,13 +3100,16 @@ element or object type."
(defsubst org-element-put-property (element property value) (defsubst org-element-put-property (element property value)
"In ELEMENT set PROPERTY to VALUE. "In ELEMENT set PROPERTY to VALUE.
Return modified element." Return modified element."
(setcar (cdr element) (plist-put (nth 1 element) property value)) (when (consp element)
(setcar (cdr element) (plist-put (nth 1 element) property value)))
element) element)
(defsubst org-element-set-contents (element &rest contents) (defsubst org-element-set-contents (element &rest contents)
"Set ELEMENT contents to CONTENTS. "Set ELEMENT contents to CONTENTS.
Return modified element." Return modified element."
(setcdr (cdr element) contents)) (cond ((not element) (list contents))
((cdr element) (setcdr (cdr element) contents))
(t (nconc element contents))))
(defsubst org-element-set-element (old new) (defsubst org-element-set-element (old new)
"Replace element or object OLD with element or object NEW. "Replace element or object OLD with element or object NEW.
@ -3135,14 +3138,15 @@ at the end.
The function takes care of setting `:parent' property for CHILD. The function takes care of setting `:parent' property for CHILD.
Return parent element." Return parent element."
(let ((contents (org-element-contents parent))) (if (not parent) (list child)
(apply 'org-element-set-contents (let ((contents (org-element-contents parent)))
parent (apply 'org-element-set-contents
(if append (append contents (list child)) (cons child contents)))) parent
;; Link the child element with parent. (if append (append contents (list child)) (cons child contents))))
(when (consp child) (org-element-put-property child :parent parent)) ;; Link the CHILD element with PARENT.
;; Return the parent element. (when (consp child) (org-element-put-property child :parent parent))
parent) ;; Return the parent element.
parent))
@ -3430,14 +3434,21 @@ Assume buffer is in Org mode."
;; headline belongs to a section. ;; headline belongs to a section.
'section nil granularity visible-only (list 'org-data nil)))) 'section nil granularity visible-only (list 'org-data nil))))
(defun org-element-parse-secondary-string (string restriction) (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.
RESTRICTION, when non-nil, is a symbol limiting the object types RESTRICTION is a symbol limiting the object types that will be
that will be looked after." looked after.
Optional argument PARENT, when non-nil, is the element or object
containing the secondary string. It is used to set correctly
`:parent' property within the string."
(with-temp-buffer (with-temp-buffer
(insert string) (insert string)
(org-element-parse-objects (point-min) (point-max) nil restriction))) (let ((secondary (org-element-parse-objects
(point-min) (point-max) nil restriction)))
(mapc (lambda (obj) (org-element-put-property obj :parent parent))
secondary))))
(defun org-element-map (data types fun &optional info first-match no-recursion) (defun org-element-map (data types fun &optional info first-match no-recursion)
"Map a function on selected elements or objects. "Map a function on selected elements or objects.
@ -3584,58 +3595,40 @@ Elements are accumulated into ACC."
end granularity special structure)) end granularity special structure))
(type (org-element-type element)) (type (org-element-type element))
(cbeg (org-element-property :contents-begin element))) (cbeg (org-element-property :contents-begin element)))
;; Set ACC as parent of current element. It will be
;; completed by side-effect. If the element contains any
;; secondary string, also set `:parent' property of every
;; object within it as current element.
(plist-put (nth 1 element) :parent acc)
(let ((sec-loc (assq type org-element-secondary-value-alist)))
(when sec-loc
(let ((sec-value (org-element-property (cdr sec-loc) element)))
(unless (stringp sec-value)
(mapc (lambda (obj)
(unless (stringp obj)
(plist-put (nth 1 obj) :parent element)))
sec-value)))))
(goto-char (org-element-property :end element)) (goto-char (org-element-property :end element))
(nconc ;; Fill ELEMENT contents by side-effect.
acc (cond
(list ;; If VISIBLE-ONLY is true and element is hidden or if it has
(cond ;; no contents, don't modify it.
;; Case 1. Simply accumulate element if VISIBLE-ONLY is ((or (and visible-only (org-element-property :hiddenp element))
;; true and element is hidden or if it has no contents (not cbeg)))
;; anyway. ;; Greater element: parse it between `contents-begin' and
((or (and visible-only (org-element-property :hiddenp element)) ;; `contents-end'. Make sure GRANULARITY allows the
(not cbeg)) element) ;; recursion, or ELEMENT is an headline, in which case going
;; Case 2. Greater element: parse it between ;; inside is mandatory, in order to get sub-level headings.
;; `contents-begin' and `contents-end'. Make sure ((and (memq type org-element-greater-elements)
;; GRANULARITY allows the recursion, or ELEMENT is an (or (memq granularity '(element object nil))
;; headline, in which case going inside is mandatory, in (and (eq granularity 'greater-element)
;; order to get sub-level headings. (eq type 'section))
((and (memq type org-element-greater-elements) (eq type 'headline)))
(or (memq granularity '(element object nil)) (org-element-parse-elements
(and (eq granularity 'greater-element) cbeg (org-element-property :contents-end element)
(eq type 'section)) ;; Possibly switch to a special mode.
(eq type 'headline))) (case type
(org-element-parse-elements (headline
cbeg (org-element-property :contents-end element) (if (org-element-property :quotedp element) 'quote-section
;; Possibly switch to a special mode. 'section))
(case type (plain-list 'item)
(headline (table 'table-row))
(if (org-element-property :quotedp element) 'quote-section (org-element-property :structure element)
'section)) granularity visible-only element))
(plain-list 'item) ;; ELEMENT has contents. Parse objects inside, if
(table 'table-row)) ;; GRANULARITY allows it.
(org-element-property :structure element) ((memq granularity '(object nil))
granularity visible-only element)) (org-element-parse-objects
;; Case 3. ELEMENT has contents. Parse objects inside, cbeg (org-element-property :contents-end element) element
;; if GRANULARITY allows it. (org-element-restriction type))))
((and cbeg (memq granularity '(object nil))) (org-element-adopt-element acc element t)))
(org-element-parse-objects
cbeg (org-element-property :contents-end element)
element (org-element-restriction type)))
;; Case 4. Else, just accumulate ELEMENT.
(t element))))))
;; Return result. ;; Return result.
acc)) acc))
@ -3646,79 +3639,50 @@ Objects are accumulated in ACC.
RESTRICTION is a list of object types which are allowed in the RESTRICTION is a list of object types which are allowed in the
current object." current object."
(let ((get-next-object (let (candidates)
(function
(lambda (cand)
;; Return the parsing function associated to the nearest
;; object among list of candidates CAND.
(let ((pos (apply 'min (mapcar 'cdr cand))))
(save-excursion
(goto-char pos)
(funcall
(intern
(format "org-element-%s-parser" (car (rassq pos cand))))))))))
next-object candidates)
(save-excursion (save-excursion
(goto-char beg) (goto-char beg)
(while (setq candidates (org-element-get-next-object-candidates (while (setq candidates (org-element-get-next-object-candidates
end restriction candidates)) end restriction candidates))
(setq next-object (funcall get-next-object candidates)) (let ((next-object
;; Set ACC as parent of current element. It will be completed (let ((pos (apply 'min (mapcar 'cdr candidates))))
;; by side-effect. (save-excursion
(plist-put (nth 1 next-object) :parent acc) (goto-char pos)
;; 1. Text before any object. Untabify it. (funcall (intern (format "org-element-%s-parser"
(let ((obj-beg (org-element-property :begin next-object))) (car (rassq pos candidates)))))))))
(unless (= (point) obj-beg) ;; 1. Text before any object. Untabify it.
(let ((beg-text (let ((obj-beg (org-element-property :begin next-object)))
(list (unless (= (point) obj-beg)
(replace-regexp-in-string (setq acc
"\t" (make-string tab-width ? ) (org-element-adopt-element
(buffer-substring-no-properties (point) obj-beg))))) acc
(if acc (nconc acc beg-text) (setq acc beg-text))))) (replace-regexp-in-string
;; 2. Object... "\t" (make-string tab-width ? )
(let* ((obj-end (org-element-property :end next-object)) (buffer-substring-no-properties (point) obj-beg)) t))))
(cont-beg (org-element-property :contents-begin next-object)) ;; 2. Object...
(complete-next-object (let ((obj-end (org-element-property :end next-object))
(if (and (memq (car next-object) org-element-recursive-objects) (cont-beg (org-element-property :contents-begin next-object)))
cont-beg) ;; Fill contents of NEXT-OBJECT by side-effect, if it has
;; ... recursive. The CONT-BEG check is for ;; a recursive type.
;; links, as some of them might not be recursive (when (and (memq (car next-object) org-element-recursive-objects)
;; (i.e. plain links). cont-beg)
(save-restriction (save-restriction
(narrow-to-region (narrow-to-region
cont-beg cont-beg
(org-element-property :contents-end next-object)) (org-element-property :contents-end next-object))
(org-element-parse-objects (org-element-parse-objects
(point-min) (point-max) next-object (point-min) (point-max) next-object
;; Restrict allowed objects. (org-element-restriction next-object))))
(org-element-restriction next-object))) (setq acc (org-element-adopt-element acc next-object t))
next-object))) (goto-char obj-end))))
(if acc (nconc acc (list complete-next-object))
(setq acc (list complete-next-object)))
;; If the object contains any secondary string, also set
;; `:parent' property of every object within it as current
;; object.
(let ((sec-loc (assq (org-element-type next-object)
org-element-secondary-value-alist)))
(when sec-loc
(let ((sec-value
(org-element-property (cdr sec-loc) next-object)))
(unless (stringp sec-value)
(mapc (lambda (obj)
(unless (stringp obj)
(plist-put (nth 1 obj)
:parent
complete-next-object)))
sec-value)))))
(goto-char obj-end)))
;; 3. Text after last object. Untabify it. ;; 3. Text after last object. Untabify it.
(unless (= (point) end) (unless (= (point) end)
(let ((end-text (setq acc
(list (org-element-adopt-element
(replace-regexp-in-string acc
"\t" (make-string tab-width ? ) (replace-regexp-in-string
(buffer-substring-no-properties (point) end))))) "\t" (make-string tab-width ? )
(if acc (nconc acc end-text) (setq acc end-text)))) (buffer-substring-no-properties (point) end)) t)))
;; Result. ;; Result.
acc))) acc)))