org-element-parse-buffer: Avoid interference with element cache

* lisp/org-element.el (org-element-copy): Make sure that element
properties containing secondary objects are also copied.
(org-element--parse-elements): Avoid modifying cached elements.

Fixes https://list.orgmode.org/CAHyO48yS2EAJnhiYoK7syjb1_Fbfxcv2A0fk4t5RFzTLj1hSGA@mail.gmail.com/
In the backtrace provided in the email, org-roam calls to
`org-element-parse-buffer' add uncached `org-data' element as cached
element `:parent' property.  Uncached elements in `:parent' property
break the cache code.
This commit is contained in:
Ihor Radchenko 2021-11-03 19:29:18 +08:00
parent 2e99997830
commit dc0c60f123
No known key found for this signature in database
GPG Key ID: 6470762A7DA11D8B
1 changed files with 29 additions and 3 deletions

View File

@ -669,7 +669,21 @@ is cleared and contents are removed in the process."
(`plain-text (substring-no-properties datum))
(`nil (copy-sequence datum))
(_
(list type (plist-put (copy-sequence (nth 1 datum)) :parent nil)))))))
(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
;; iteself. When copied, these inner `:parent' propery
;; 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' propreties 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))))))
@ -4729,8 +4743,20 @@ Elements are accumulated into ACC."
(when (and (eolp) (not (eobp))) (forward-char)))
;; Find current element's type and parse it accordingly to
;; its category.
(let* ((element (org-element--current-element
end granularity mode structure))
(let* ((element (org-element-copy
;; `org-element--current-element' may return cached
;; elements. Below code reassigns
;; `:parent' property of the element and
;; may interfere with cache
;; synchronisation if parent element is not
;; yet in cache. Moreover, the returned
;; structure may be altered by caller code
;; arbitrarily. Hence, we return a copy of
;; the potentially cached element to make
;; potential modifications safe for element
;; cache.
(org-element--current-element
end granularity mode structure)))
(type (org-element-type element))
(cbeg (org-element-property :contents-begin element)))
(goto-char (org-element-property :end element))