org-element.el: Make affiliated keyword interpreter faster

* lisp/org-element.el (org-element--interpret-affiliated-keywords):
Optimize performance by bypassing unnecessary types and reducing
loop complexity. Added new constant
`org-element-elements-no-affiliated` which stores the types to
be bypassed.

This function was doing redundant work on several levels which
dramatically reduced performance of interpreting element nodes
relative to object nodes.

First, all types were interpreted regardless of if they could
possibly contain affiliated keywords. Skipping these types
dramatically speeds up typical use cases since many of these
skipped types are common (headline, item, etc).

Second, the loop was much more complex than needed. The loop included
:standard-properties which should not be necessary here. It also
duplicated some work between calls to `org-element--properties-mapc`
and `mapconcat` (the code was moved entirely under the former). The
result should be faster and more readable.

TINYCHANGE
This commit is contained in:
Nathan Dwarshuis 2024-11-25 22:04:09 -05:00
parent e87ecf88be
commit 1b4fb607bd
1 changed files with 42 additions and 40 deletions

View File

@ -335,6 +335,11 @@ specially in `org-element--object-lex'.")
(append org-element-recursive-objects '(paragraph table-row verse-block)) (append org-element-recursive-objects '(paragraph table-row verse-block))
"List of object or element types that can directly contain objects.") "List of object or element types that can directly contain objects.")
(defconst org-element-elements-no-affiliated
'(org-data comment clock headline inlinetask item
node-property planning property-drawer
section table-row))
(defconst org-element-affiliated-keywords (defconst org-element-affiliated-keywords
'("CAPTION" "DATA" "HEADER" "HEADERS" "LABEL" "NAME" "PLOT" "RESNAME" "RESULT" '("CAPTION" "DATA" "HEADER" "HEADERS" "LABEL" "NAME" "PLOT" "RESNAME" "RESULT"
"RESULTS" "SOURCE" "SRCNAME" "TBLNAME") "RESULTS" "SOURCE" "SRCNAME" "TBLNAME")
@ -5517,49 +5522,46 @@ to interpret. Return Org syntax as a string."
(make-string blank ?\n))))))))) (make-string blank ?\n)))))))))
(funcall fun data nil))) (funcall fun data nil)))
(defun org-element--keyword-to-org (key value)
(let (dual)
(when (member key org-element-dual-keywords)
(setq dual (cdr value) value (car value)))
(concat "#+" (downcase key)
(and dual
(format "[%s]" (org-element-interpret-data dual)))
": "
(if (member key org-element-parsed-keywords)
(org-element-interpret-data value)
value)
"\n")))
(defun org-element--interpret-affiliated-keywords (element) (defun org-element--interpret-affiliated-keywords (element)
"Return ELEMENT's affiliated keywords as Org syntax. "Return ELEMENT's affiliated keywords as Org syntax.
If there is no affiliated keyword, return the empty string." If there is no affiliated keyword, return the empty string."
(let ((keyword-to-org ;; there are some elements that will never have affiliated keywords,
(lambda (key value) ;; so do nothing for these
(let (dual) (if (member (org-element-type element) org-element-elements-no-affiliated)
(when (member key org-element-dual-keywords) ""
(setq dual (cdr value) value (car value))) (let (acc)
(concat "#+" (downcase key) (org-element-properties-resolve element t)
(and dual (org-element--properties-mapc
(format "[%s]" (org-element-interpret-data dual))) (lambda (prop value)
": " (when value
(if (member key org-element-parsed-keywords) (let ((keyword (upcase (substring (symbol-name prop) 1))))
(org-element-interpret-data value) (when (or (string-match-p "^ATTR_" keyword)
value) (and
"\n"))))) (member keyword org-element-affiliated-keywords)
(mapconcat (not (assoc keyword
(lambda (prop) org-element-keyword-translation-alist))))
(let ((value (org-element-property prop element)) (push (if (or (member keyword org-element-multiple-keywords)
(keyword (upcase (substring (symbol-name prop) 1)))) ;; All attribute keywords can have multiple lines.
(when value (string-match-p "^ATTR_" keyword))
(if (or (member keyword org-element-multiple-keywords) (mapconcat (lambda (line) (org-element--keyword-to-org keyword line))
;; All attribute keywords can have multiple lines. value "")
(string-match-p "^ATTR_" keyword)) (org-element--keyword-to-org keyword value))
(mapconcat (lambda (line) (funcall keyword-to-org keyword line)) acc)))))
value "") element nil t)
(funcall keyword-to-org keyword value))))) (apply #'concat (nreverse acc)))))
;; List all ELEMENT's properties matching an attribute line or an
;; affiliated keyword, but ignore translated keywords since they
;; cannot belong to the property list.
(let (acc)
(org-element-properties-mapc
(lambda (prop _ __)
(let ((keyword (upcase (substring (symbol-name prop) 1))))
(when (or (string-match-p "^ATTR_" keyword)
(and
(member keyword org-element-affiliated-keywords)
(not (assoc keyword
org-element-keyword-translation-alist))))
(push prop acc))))
element t)
(nreverse acc))
"")))
;; Because interpretation of the parse tree must return the same ;; Because interpretation of the parse tree must return the same
;; number of blank lines between elements and the same number of white ;; number of blank lines between elements and the same number of white