branch: externals/org
commit bc4ee1c72a638b815dd416b794e0666066a17122
Author: Ihor Radchenko <[email protected]>
Commit: Ihor Radchenko <[email protected]>
org-element--current-element: Micro optimizations
* lisp/org-element.el (org-element--current-element): Try hard
spending minimal amount of time in testing which parser to call.
Prefer `look-at-p' that does not modify match data and thus save us
significant amount of time. Do not call `org-with-limited-levels' and
instead use the simplest possible tests with all unnecessary branches
removed. Add commentary explaining the importance of writing fast
core when adding new elements.
(org-inlinetask-min-level): Declare variable.
* lisp/org-list.el (org--item-re-cache):
(org-item-re): Cache results rather than calculating regexp every time
the parser is invoked.
* lisp/org-macs.el:
(org--headline-re-cache):
(org--headline-re-cache-no-bol):
(org--headline-re-cache-bol):
(org-headline-re): Prefer plists to store headline regexp cache - the
number of items tends to be within 10-20 and hence hash table is an
overkill and will be slower.
---
lisp/org-element.el | 76 +++++++++++++++++++++++++++++------------------------
lisp/org-list.el | 32 ++++++++++++++++------
lisp/org-macs.el | 31 +++++++++++++++-------
3 files changed, 88 insertions(+), 51 deletions(-)
diff --git a/lisp/org-element.el b/lisp/org-element.el
index ed740fc0b4..6d0890f4b5 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -4185,6 +4185,7 @@ Assume point is at the first equal sign marker."
;; It returns the Lisp representation of the element starting at
;; point.
+(defvar org-inlinetask-min-level); Declared in org-inlinetask.el
(defvar org-element--cache-sync-requests); Declared later
(defun org-element--current-element (limit &optional granularity mode
structure add-to-cache)
"Parse the element starting at point.
@@ -4247,30 +4248,40 @@ element it has to parse."
result)
(setq
result
+ ;; Regexp matches below should avoid modifying match data,
+ ;; if possible. Doing it unnecessarily degrades regexp
+ ;; matching performance an order of magnitude, which
+ ;; becomes important when parsing large buffers with huge
+ ;; amount of elements to be parsed.
+ ;;
+ ;; In general, the checks below should be as efficient as
+ ;; possible, especially early in the `cond' form. (The
+ ;; early checks will contribute to al subsequent parsers as
+ ;; well).
(cond
;; Item.
- ((eq mode 'item)
- (org-element-item-parser limit structure raw-secondary-p))
+ ((eq mode 'item) (org-element-item-parser limit structure
raw-secondary-p))
;; Table Row.
((eq mode 'table-row) (org-element-table-row-parser limit))
;; Node Property.
((eq mode 'node-property) (org-element-node-property-parser limit))
;; Headline.
- ((org-with-limited-levels (looking-at-p org-outline-regexp-bol))
+ ((and (looking-at-p "^\\*+ ")
+ (or (not (featurep 'org-inlinetask))
+ (save-excursion
+ (< (skip-chars-forward "*")
+ (if org-odd-levels-only
+ (1- (* org-inlinetask-min-level 2))
+ org-inlinetask-min-level)))))
(org-element-headline-parser limit raw-secondary-p))
;; Sections (must be checked after headline).
- ((eq mode 'section) (org-element-section-parser limit))
- ((eq mode 'first-section)
- (org-element-section-parser
- (or (save-excursion (org-with-limited-levels
(outline-next-heading)))
- limit)))
+ ((memq mode '(section first-section)) (org-element-section-parser
nil))
;; Comments.
- ((looking-at "^[ \t]*#\\(?: \\|$\\)")
- (org-element-comment-parser limit))
+ ((looking-at-p "^[ \t]*#\\(?: \\|$\\)")
(org-element-comment-parser limit))
;; Planning.
((and (eq mode 'planning)
(eq ?* (char-after (line-beginning-position 0)))
- (looking-at org-element-planning-line-re))
+ (looking-at-p org-element-planning-line-re))
(org-element-planning-parser limit))
;; Property drawer.
((and (pcase mode
@@ -4278,19 +4289,17 @@ element it has to parse."
((or `property-drawer `top-comment)
(save-excursion
(beginning-of-line 0)
- (not (looking-at "[[:blank:]]*$"))))
+ (not (looking-at-p "[[:blank:]]*$"))))
(_ nil))
- (looking-at org-property-drawer-re))
+ (looking-at-p org-property-drawer-re))
(org-element-property-drawer-parser limit))
;; When not at bol, point is at the beginning of an item or
;; a footnote definition: next item is always a paragraph.
((not (bolp)) (org-element-paragraph-parser limit (list (point))))
;; Clock.
- ((looking-at org-element-clock-line-re)
- (org-element-clock-parser limit))
+ ((looking-at-p org-element-clock-line-re)
(org-element-clock-parser limit))
;; Inlinetask.
- ((looking-at "^\\*+ ")
- (org-element-inlinetask-parser limit raw-secondary-p))
+ ((looking-at-p "^\\*+ ") (org-element-inlinetask-parser limit
raw-secondary-p))
;; From there, elements can have affiliated keywords.
(t (let ((affiliated (org-element--collect-affiliated-keywords
limit (memq granularity '(nil object)))))
@@ -4301,13 +4310,13 @@ element it has to parse."
(goto-char (car affiliated))
(org-element-keyword-parser limit nil))
;; LaTeX Environment.
- ((looking-at org-element--latex-begin-environment)
+ ((looking-at-p org-element--latex-begin-environment)
(org-element-latex-environment-parser limit affiliated))
;; Drawer.
- ((looking-at org-element-drawer-re)
+ ((looking-at-p org-element-drawer-re)
(org-element-drawer-parser limit affiliated))
;; Fixed Width
- ((looking-at "[ \t]*:\\( \\|$\\)")
+ ((looking-at-p "[ \t]*:\\( \\|$\\)")
(org-element-fixed-width-parser limit affiliated))
;; Inline Comments, Blocks, Babel Calls, Dynamic Blocks and
;; Keywords.
@@ -4327,31 +4336,31 @@ element it has to parse."
(_ #'org-element-special-block-parser))
limit
affiliated))
- ((looking-at "CALL:")
+ ((looking-at-p "CALL:")
(beginning-of-line)
(org-element-babel-call-parser limit affiliated))
((save-excursion
(beginning-of-line)
- (looking-at org-element-dynamic-block-open-re))
+ (looking-at-p org-element-dynamic-block-open-re))
(beginning-of-line)
(org-element-dynamic-block-parser limit affiliated))
- ((looking-at "\\S-+:")
+ ((looking-at-p "\\S-+:")
(beginning-of-line)
(org-element-keyword-parser limit affiliated))
(t
(beginning-of-line)
(org-element-paragraph-parser limit affiliated))))
;; Footnote Definition.
- ((looking-at org-footnote-definition-re)
+ ((looking-at-p org-footnote-definition-re)
(org-element-footnote-definition-parser limit affiliated))
;; Horizontal Rule.
- ((looking-at "[ \t]*-\\{5,\\}[ \t]*$")
+ ((looking-at-p "[ \t]*-\\{5,\\}[ \t]*$")
(org-element-horizontal-rule-parser limit affiliated))
;; Diary Sexp.
- ((looking-at "%%(")
+ ((looking-at-p "%%(")
(org-element-diary-sexp-parser limit affiliated))
;; Table.
- ((or (looking-at "[ \t]*|")
+ ((or (looking-at-p "[ \t]*|")
;; There is no strict definition of a table.el
;; table. Try to prevent false positive while being
;; quick.
@@ -4368,7 +4377,7 @@ element it has to parse."
(next (line-beginning-position 2)))
;; Start with a full rule.
(and
- (looking-at rule-regexp)
+ (looking-at-p rule-regexp)
(< next limit) ;no room for a table.el table
(save-excursion
(end-of-line)
@@ -4376,7 +4385,7 @@ element it has to parse."
;; Must end with a full rule.
((not (re-search-forward non-table.el-line limit
'move))
(if (bolp) (forward-line -1) (beginning-of-line))
- (looking-at rule-regexp))
+ (looking-at-p rule-regexp))
;; Ignore pseudo-tables with a single
;; rule.
((= next (line-beginning-position))
@@ -4384,10 +4393,10 @@ element it has to parse."
;; Must end with a full rule.
(t
(forward-line -1)
- (looking-at rule-regexp)))))))
+ (looking-at-p rule-regexp)))))))
(org-element-table-parser limit affiliated))
;; List.
- ((looking-at (org-item-re))
+ ((looking-at-p (org-item-re))
(org-element-plain-list-parser
limit affiliated
(or structure (org-element--list-struct limit))))
@@ -4396,10 +4405,9 @@ element it has to parse."
(when result
(org-element-put-property result :mode mode)
(org-element-put-property result :granularity granularity))
- (when (and (not (buffer-narrowed-p))
- (org-element--cache-active-p)
+ (when (and add-to-cache(not (buffer-narrowed-p))
(not org-element--cache-sync-requests)
- add-to-cache)
+ (org-element--cache-active-p))
(if (not old-element)
(setq result (org-element--cache-put result))
(org-element-set-element old-element result)
diff --git a/lisp/org-list.el b/lisp/org-list.el
index 0e52e99550..dbbacaeba0 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -364,16 +364,32 @@ group 2: counter
group 3: checkbox
group 4: description tag")
+(defvar org--item-re-cache nil
+ "Results cache for `org-item-re'.")
(defun org-item-re ()
"Return the correct regular expression for plain lists."
- (let ((term (cond
- ((eq org-plain-list-ordered-item-terminator t) "[.)]")
- ((= org-plain-list-ordered-item-terminator ?\)) ")")
- ((= org-plain-list-ordered-item-terminator ?.) "\\.")
- (t "[.)]")))
- (alpha (if org-list-allow-alphabetical "\\|[A-Za-z]" "")))
- (concat "\\([ \t]*\\([-+]\\|\\(\\([0-9]+" alpha "\\)" term
- "\\)\\)\\|[ \t]+\\*\\)\\([ \t]+\\|$\\)")))
+ (or (plist-get
+ (plist-get org--item-re-cache
+ org-list-allow-alphabetical)
+ org-plain-list-ordered-item-terminator)
+ (let* ((term (cond
+ ((eq org-plain-list-ordered-item-terminator t) "[.)]")
+ ((= org-plain-list-ordered-item-terminator ?\)) ")")
+ ((= org-plain-list-ordered-item-terminator ?.) "\\.")
+ (t "[.)]")))
+ (alpha (if org-list-allow-alphabetical "\\|[A-Za-z]" ""))
+ (re (concat "\\([ \t]*\\([-+]\\|\\(\\([0-9]+" alpha "\\)" term
+ "\\)\\)\\|[ \t]+\\*\\)\\([ \t]+\\|$\\)")))
+ (setq org--item-re-cache
+ (plist-put
+ org--item-re-cache
+ org-list-allow-alphabetical
+ (plist-put
+ (plist-get org--item-re-cache
+ org-list-allow-alphabetical)
+ org-plain-list-ordered-item-terminator
+ re)))
+ re)))
(defsubst org-item-beginning-re ()
"Regexp matching the beginning of a plain list item."
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index f771c408b3..06fbc0d64c 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -807,20 +807,33 @@ get an unnecessary O(N²) space complexity, so you're
usually better off using
(eval form t)
(error (format "%%![Error: %s]" error))))
-(defvar org--headline-re-cache (make-hash-table :test #'equal)
- "Hash table holding association between headline level regexp.")
+(defvar org--headline-re-cache-no-bol nil
+ "Plist holding association between headline level regexp.")
+(defvar org--headline-re-cache-bol nil
+ "Plist holding association between headline level regexp.")
(defun org-headline-re (true-level &optional no-bol)
"Generate headline regexp for TRUE-LEVEL.
When NO-BOL is non-nil, regexp will not demand the regexp to start at
beginning of line."
- (or (gethash (cons true-level no-bol) org--headline-re-cache)
- (puthash
- (cons true-level no-bol)
- (rx-to-string
+ (or (plist-get
+ (if no-bol
+ org--headline-re-cache-no-bol
+ org--headline-re-cache-bol)
+ true-level)
+ (let ((re (rx-to-string
+ (if no-bol
+ `(seq (** 1 ,true-level "*") " ")
+ `(seq line-start (** 1 ,true-level "*") " ")))))
(if no-bol
- `(seq (** 1 ,true-level "*") " ")
- `(seq line-start (** 1 ,true-level "*") " ")))
- org--headline-re-cache)))
+ (setq org--headline-re-cache-no-bol
+ (plist-put
+ org--headline-re-cache-no-bol
+ true-level re))
+ (setq org--headline-re-cache-bol
+ (plist-put
+ org--headline-re-cache-bol
+ true-level re)))
+ re)))
(defvar org-outline-regexp) ; defined in org.el
(defvar org-outline-regexp-bol) ; defined in org.el