[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/org fefaadc2d5 050/101: org-element: Optimize working w
From: |
ELPA Syncer |
Subject: |
[elpa] externals/org fefaadc2d5 050/101: org-element: Optimize working with match data and Emacs regexps |
Date: |
Sat, 1 Jul 2023 09:59:10 -0400 (EDT) |
branch: externals/org
commit fefaadc2d594d0c57cee25fbccb8db758aeb7174
Author: Ihor Radchenko <yantar92@posteo.net>
Commit: Ihor Radchenko <yantar92@posteo.net>
org-element: Optimize working with match data and Emacs regexps
* lisp/org-element.el: Add commentary explaining some regexp-related
optimizations useful for the parser.
Link: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=63225
(org-element--headline-deferred):
(org-element-item-parser):
(org-element-item-interpreter):
(org-element--list-struct):
(org-element-plain-list-parser):
(org-element-example-block-parser):
(org-element-fixed-width-interpreter):
(org-element-paragraph-parser):
(org-element-src-block-parser):
(org-element-table-parser):
(org-element--parse-generic-emphasis):
(org-element-export-snippet-interpreter):
(org-element-link-parser):
(org-element--current-element):
(org-element--collect-affiliated-keywords):
(org-element-parse-buffer):
(org-element-normalize-string):
(org-element-normalize-contents):
(org-element--parse-to):
(org-element--cache-before-change):
(org-element--cache-for-removal):
(org-element-context): Avoid storing match data unless strictly
necessary. Explain the necessity in places where we have to use
`save-match-data'. Prefer `looking-at-p' (does not alter match data)
to `looking-at'. Simplify regexps. Update docstrings clearly
indicating when match data might be modified.
* lisp/org.el:
(org-offer-links-in-entry):
* lisp/ob-exp.el (org-babel-exp-process-buffer):
* lisp/org-agenda.el: Fix places where we need to protect match data.
---
lisp/ob-exp.el | 2 +-
lisp/org-agenda.el | 2 +-
lisp/org-element.el | 904 +++++++++++++++++++++++++++-------------------------
lisp/org.el | 2 +-
4 files changed, 475 insertions(+), 435 deletions(-)
diff --git a/lisp/ob-exp.el b/lisp/ob-exp.el
index 40c0dd8e6f..e54be77eb5 100644
--- a/lisp/ob-exp.el
+++ b/lisp/ob-exp.el
@@ -173,7 +173,7 @@ this template."
;; buffer.
(org-fold-core-ignore-modifications
(while (re-search-forward regexp nil t)
- (setq element (org-element-at-point))
+ (setq element (save-match-data (org-element-at-point)))
(unless (save-match-data
(or (org-in-commented-heading-p nil element)
(org-in-archived-heading-p nil element)))
diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index da74ed5381..e3b9163ad6 100644
--- a/lisp/org-agenda.el
+++ b/lisp/org-agenda.el
@@ -6892,7 +6892,7 @@ scheduled items with an hour specification like [h]h:mm."
(while (re-search-forward regexp nil t)
(catch :skip
(unless (save-match-data (org-at-planning-p)) (throw :skip nil))
- (org-agenda-skip)
+ (save-match-data (org-agenda-skip))
(let* ((s (match-string 1))
(pos (1- (match-beginning 1)))
(todo-state (save-match-data (org-get-todo-state)))
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 6b2fdf6d25..3db77db02e 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -655,7 +655,26 @@ and END-OFFSET."
;; greater element requires tweaking `org-element--current-element'.
;; Moreover, the newly defined type must be added to both
;; `org-element-all-elements' and `org-element-greater-elements'.
-
+;;
+;; When adding or modifying the parser, please keep in mind the
+;; following rules. They are important to keep parser performance
+;; optimal.
+;;
+;; 1. When you can use `looking-at-p' or `string-match-p' instead of
+;; `looking-at' or `string-match' and keep match data unmodified,
+;; do it.
+;; 2. When regexps can be grouped together, avoiding multiple regexp
+;; match calls, they should be grouped.
+;; 3. When `save-match-data' can be avoided, avoid it.
+;; 4. When simpler regexps can be used for analysis, use the simpler
+;; regexps.
+;; 5. When regexps can be calculated in advance, not dynamically, they
+;; should be calculated in advance.
+;; 6 Note that it is not an obligation of a given function to preserve
+;; match data - `save-match-data' is costly and must be arranged by
+;; the caller if necessary.
+;;
+;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=63225
;;;; Center Block
@@ -973,7 +992,7 @@ parse properties for property drawer at point."
"Return time properties associated to headline at point.
Return value is a plist."
(save-excursion
- (when (progn (forward-line) (looking-at org-element-planning-line-re))
+ (when (progn (forward-line) (looking-at-p org-element-planning-line-re))
(let ((end (line-end-position))
plist)
(while (re-search-forward org-element-planning-keywords-re end t)
@@ -1483,19 +1502,12 @@ Assume point is at the beginning of the item."
(looking-at org-list-full-item-re)
(let* ((begin (point))
(bullet (org-element--get-cached-string (match-string-no-properties
1)))
+ (tag-begin (match-beginning 4))
+ (tag-end (match-end 4))
(checkbox (let ((box (match-string 3)))
(cond ((equal "[ ]" box) 'off)
((equal "[X]" box) 'on)
((equal "[-]" box) 'trans))))
- (counter (let ((c (match-string 2)))
- (save-match-data
- (cond
- ((not c) nil)
- ((string-match "[A-Za-z]" c)
- (- (string-to-char (upcase
(match-string-no-properties 0 c)))
- 64))
- ((string-match "[0-9]+" c)
- (string-to-number (match-string-no-properties 0
c)))))))
(end (progn (goto-char (nth 6 (assq (point) struct)))
(if (bolp) (point) (line-beginning-position 2))))
(pre-blank 0)
@@ -1505,7 +1517,7 @@ Assume point is at the beginning of the item."
;; Ignore tags in un-ordered lists: they are just
;; a part of item's body.
(if (and (match-beginning 4)
- (save-match-data (string-match "[.)]" bullet)))
+ (string-match-p "[.)]" bullet))
(match-beginning 4)
(match-end 0)))
(skip-chars-forward " \r\t\n" end)
@@ -1521,6 +1533,14 @@ Assume point is at the beginning of the item."
(progn (goto-char end)
(skip-chars-backward " \r\t\n")
(line-beginning-position 2))))
+ (counter (let ((c (match-string 2)))
+ (cond
+ ((not c) nil)
+ ((string-match "[A-Za-z]" c)
+ (- (string-to-char (upcase (match-string-no-properties
0 c)))
+ 64))
+ ((string-match "[0-9]+" c)
+ (string-to-number (match-string-no-properties 0 c))))))
(item
(org-element-create
'item
@@ -1544,7 +1564,7 @@ Assume point is at the beginning of the item."
(when raw
(if raw-secondary-p raw
(org-element--parse-objects
- (match-beginning 4) (match-end 4) nil
+ tag-begin tag-end nil
(org-element-restriction 'item)
item))))))))
@@ -1616,11 +1636,11 @@ CONTENTS is the contents of the element."
(dolist (item items) (setcar (nthcdr 6 item) end)))
(throw :exit (sort (nconc items struct) #'car-less-than-car)))
;; At list end: end all items.
- ((looking-at org-list-end-re)
+ ((looking-at-p org-list-end-re)
(dolist (item items) (setcar (nthcdr 6 item) (point)))
(throw :exit (sort (nconc items struct) #'car-less-than-car)))
;; At a new item: end previous sibling.
- ((looking-at item-re)
+ ((looking-at-p item-re)
(let ((ind (save-excursion (skip-chars-forward " \t")
(org-current-text-column))))
(setq top-ind (min top-ind ind))
@@ -1636,17 +1656,17 @@ CONTENTS is the contents of the element."
(match-string-no-properties 2) ; counter
(match-string-no-properties 3) ; checkbox
;; Description tag.
- (and (save-match-data
- (string-match "[-+*]" bullet))
- (match-string-no-properties 4))
+ (and
+ (string-match-p "[-+*]" bullet)
+ (match-string-no-properties 4))
;; Ending position, unknown so far.
nil)))
items))
(forward-line))
;; Skip empty lines.
- ((looking-at "^[ \t]*$") (forward-line))
+ ((looking-at-p "^[ \t]*$") (forward-line))
;; Skip inline tasks and blank lines along the way.
- ((and inlinetask-re (looking-at inlinetask-re))
+ ((and inlinetask-re (looking-at-p inlinetask-re))
(forward-line)
(let ((origin (point)))
(when (re-search-forward inlinetask-re limit t)
@@ -1672,7 +1692,7 @@ CONTENTS is the contents of the element."
(re-search-forward
(format "^[ \t]*#\\+END%s[ \t]*$" (match-string 1))
limit t)))
- ((and (looking-at org-element-drawer-re)
+ ((and (looking-at-p org-element-drawer-re)
(re-search-forward "^[ \t]*:END:[ \t]*$" limit t))))
(forward-line))))))))
@@ -2218,19 +2238,19 @@ Return a new syntax node of `example-block' type
containing `:begin',
;; first line.
(1- (string-to-number (match-string 2 switches)))))))
(preserve-indent
- (and switches (string-match "-i\\>" switches)))
+ (and switches (string-match-p "-i\\>" switches)))
;; Should labels be retained in (or stripped from) example
;; blocks?
(retain-labels
(or (not switches)
- (not (string-match "-r\\>" switches))
- (and number-lines (string-match "-k\\>" switches))))
+ (not (string-match-p "-r\\>" switches))
+ (and number-lines (string-match-p "-k\\>" switches))))
;; What should code-references use - labels or
;; line-numbers?
(use-labels
(or (not switches)
(and retain-labels
- (not (string-match "-k\\>" switches)))))
+ (not (string-match-p "-k\\>" switches)))))
(label-fmt
(and switches
(string-match "-l +\"\\([^\"\n]+\\)\"" switches)
@@ -2364,7 +2384,7 @@ Assume point is at the beginning of the fixed-width area."
(end-area
(progn
(while (and (< (point) limit)
- (looking-at "[ \t]*:\\( \\|$\\)"))
+ (looking-at-p "[ \t]*:\\( \\|$\\)"))
(forward-line))
(if (bolp) (line-end-position 0) (point))))
(end (progn (skip-chars-forward " \r\t\n" limit)
@@ -2593,9 +2613,9 @@ Assume point is at the beginning of the paragraph."
(while (not
(cond
((not (and (re-search-forward
- org-element-paragraph-separate limit 'move)
- (progn (beginning-of-line) t))))
- ((looking-at org-element-drawer-re)
+ org-element-paragraph-separate limit 'move)
+ (progn (beginning-of-line) t))))
+ ((looking-at-p org-element-drawer-re)
(save-excursion
(re-search-forward "^[ \t]*:END:[ \t]*$" limit t)))
((looking-at "[ \t]*#\\+BEGIN_\\(\\S-+\\)")
@@ -2752,7 +2772,7 @@ Assume point is at the beginning of the block."
;; first line.
(1- (string-to-number (match-string 2 switches)))))))
(preserve-indent (and switches
- (string-match "-i\\>" switches)))
+ (string-match-p "-i\\>" switches)))
(label-fmt
(and switches
(string-match "-l +\"\\([^\"\n]+\\)\"" switches)
@@ -2761,14 +2781,14 @@ Assume point is at the beginning of the block."
;; source blocks?
(retain-labels
(or (not switches)
- (not (string-match "-r\\>" switches))
- (and number-lines (string-match "-k\\>" switches))))
+ (not (string-match-p "-r\\>" switches))
+ (and number-lines (string-match-p "-k\\>" switches))))
;; What should code-references use - labels or
;; line-numbers?
(use-labels
(or (not switches)
(and retain-labels
- (not (string-match "-k\\>" switches)))))
+ (not (string-match-p "-k\\>" switches)))))
;; Retrieve code.
(value
(org-element-deferred-create
@@ -2844,7 +2864,7 @@ Assume point is at the beginning of the table."
(save-excursion
(let* ((case-fold-search t)
(table-begin (point))
- (type (if (looking-at "[ \t]*|") 'org 'table.el))
+ (type (if (looking-at-p "[ \t]*|") 'org 'table.el))
(end-re (format "^[ \t]*\\($\\|[^| \t%s]\\)"
(if (eq type 'org) "" "+")))
(begin (car affiliated))
@@ -2903,7 +2923,7 @@ Return a new syntax node of `table-row' type containing
`:begin',
`:end', `:contents-begin', `:contents-end', `:type', `:post-blank' and
`:post-affiliated' properties."
(save-excursion
- (let* ((type (if (looking-at "^[ \t]*|-") 'rule 'standard))
+ (let* ((type (if (looking-at-p "^[ \t]*|-") 'rule 'standard))
(begin (point))
;; A table rule has no contents. In that case, ensure
;; CONTENTS-BEGIN matches CONTENTS-END.
@@ -3012,7 +3032,7 @@ Assume point is at first MARK."
`(seq (or line-start (any space ?- ?\( ?' ?\" ?\{))
,mark
(not space)))))
- (when (looking-at opening-re)
+ (when (looking-at-p opening-re)
(goto-char (1+ origin))
(let ((closing-re
(rx-to-string
@@ -3263,30 +3283,30 @@ When at an export snippet, return a new syntax node of
Assume point is at the beginning of the snippet."
(save-excursion
- (let (contents-end)
- (when (and (looking-at "@@\\([-A-Za-z0-9]+\\):")
- (setq contents-end
- (save-match-data (goto-char (match-end 0))
- (when
- (re-search-forward "@@" nil t)
- (match-beginning 0)))))
- (let* ((begin (match-beginning 0))
- (backend (org-element--get-cached-string
- (match-string-no-properties 1)))
- (value
+ (when (looking-at "@@\\([-A-Za-z0-9]+\\):")
+ (goto-char (match-end 0))
+ (let* ((begin (match-beginning 0))
+ (contents-begin (match-end 0))
+ (backend (org-element--get-cached-string
+ (match-string-no-properties 1)))
+ (contents-end
+ (when (re-search-forward "@@" nil t)
+ (match-beginning 0)))
+ (value
+ (when contents-end
(org-element-deferred-create
nil #'org-element--substring
- (- (match-end 0) begin)
- (- contents-end begin)))
- (post-blank (skip-chars-forward " \t"))
- (end (point)))
- (org-element-create
- 'export-snippet
- (list :back-end backend
- :value value
- :begin begin
- :end end
- :post-blank post-blank)))))))
+ (- contents-begin begin)
+ (- contents-end begin))))
+ (post-blank (skip-chars-forward " \t"))
+ (end (point)))
+ (org-element-create
+ 'export-snippet
+ (list :back-end backend
+ :value value
+ :begin begin
+ :end end
+ :post-blank post-blank))))))
(defun org-element-export-snippet-interpreter (export-snippet _)
"Interpret EXPORT-SNIPPET object as Org syntax."
@@ -3590,7 +3610,7 @@ Assume point is at the beginning of the link."
(cond
;; File type.
((or (file-name-absolute-p raw-link)
- (string-match "\\`\\.\\.?/" raw-link))
+ (string-match-p "\\`\\.\\.?/" raw-link))
(setq type "file")
(setq path raw-link))
;; Explicit type (http, irc, bbdb...).
@@ -4215,6 +4235,26 @@ Assume point is at the first equal sign marker."
;; It returns the Lisp representation of the element starting at
;; point.
+(defconst org-element--current-element-re
+ (rx
+ (or
+ (group-n 1 (regexp org-element--latex-begin-environment))
+ (group-n 2 (regexp org-element-drawer-re-nogroup))
+ (group-n 3 (regexp "[ \t]*:\\( \\|$\\)"))
+ (group-n 7 (regexp org-element-dynamic-block-open-re))
+ (seq (group-n 4 (regexp "[ \t]*#\\+"))
+ (or
+ (seq "BEGIN_" (group-n 5 (1+ (not space))))
+ (group-n 6 "CALL:")
+ (group-n 8 (1+ (not space)) ":")
+ ))
+ (group-n 9 (regexp org-footnote-definition-re))
+ (group-n 10 (regexp "[ \t]*-\\{5,\\}[ \t]*$"))
+ (group-n 11 "%%(")))
+ "Bulk regexp matching multiple elements in a single regexp.
+This is a bit more efficient compared to invoking regexp search
+multiple times.")
+
(defvar org-inlinetask-min-level); Declared in org-inlinetask.el
(defvar org-element--cache-sync-requests); Declared later
(defsubst org-element--current-element (limit &optional granularity mode
structure)
@@ -4309,29 +4349,29 @@ element it has to parse."
;; From there, elements can have affiliated keywords.
(t (let ((affiliated (org-element--collect-affiliated-keywords
limit (memq granularity '(nil object)))))
- (cond
+ (cond
;; Jumping over affiliated keywords put point off-limits.
;; Parse them as regular keywords.
((and (cdr affiliated) (>= (point) limit))
(goto-char (car affiliated))
(org-element-keyword-parser limit nil))
- ;; LaTeX Environment.
- ((looking-at-p org-element--latex-begin-environment)
- (org-element-latex-environment-parser limit affiliated))
- ;; Drawer.
- ((looking-at-p org-element-drawer-re)
- (org-element-drawer-parser limit affiliated))
- ;; Fixed Width
- ((looking-at-p "[ \t]*:\\( \\|$\\)")
- (org-element-fixed-width-parser limit affiliated))
- ;; Inline Comments, Blocks, Babel Calls, Dynamic Blocks and
- ;; Keywords.
- ((looking-at "[ \t]*#\\+")
- (goto-char (match-end 0))
- (cond
- ((looking-at "BEGIN_\\(\\S-+\\)")
- (beginning-of-line)
- (funcall (pcase (upcase (match-string 1))
+ ;; Do a single regexp match do reduce overheads for
+ ;; multiple regexp search invocations.
+ ((looking-at org-element--current-element-re)
+ (cond
+ ;; LaTeX Environment.
+ ((match-beginning 1)
+ (org-element-latex-environment-parser limit affiliated))
+ ;; Drawer.
+ ((match-beginning 2)
+ (org-element-drawer-parser limit affiliated))
+ ;; Fixed Width
+ ((match-beginning 3)
+ (org-element-fixed-width-parser limit affiliated))
+ ;; Inline Comments, Blocks, Babel Calls, Dynamic Blocks and
+ ;; Keywords.
+ ((match-beginning 5)
+ (funcall (pcase (upcase (match-string 5))
("CENTER" #'org-element-center-block-parser)
("COMMENT" #'org-element-comment-block-parser)
("EXAMPLE" #'org-element-example-block-parser)
@@ -4342,29 +4382,24 @@ element it has to parse."
(_ #'org-element-special-block-parser))
limit
affiliated))
- ((looking-at-p "CALL:")
- (beginning-of-line)
+ ((match-beginning 6)
(org-element-babel-call-parser limit affiliated))
- ((save-excursion
- (beginning-of-line)
- (looking-at-p org-element-dynamic-block-open-re))
+ ((match-beginning 7)
(beginning-of-line)
(org-element-dynamic-block-parser limit affiliated))
- ((looking-at-p "\\S-+:")
- (beginning-of-line)
+ ((match-beginning 8)
(org-element-keyword-parser limit affiliated))
- (t
- (beginning-of-line)
- (org-element-paragraph-parser limit affiliated))))
- ;; Footnote Definition.
- ((looking-at-p org-footnote-definition-re)
- (org-element-footnote-definition-parser limit affiliated))
- ;; Horizontal Rule.
- ((looking-at-p "[ \t]*-\\{5,\\}[ \t]*$")
- (org-element-horizontal-rule-parser limit affiliated))
- ;; Diary Sexp.
- ((looking-at-p "%%(")
- (org-element-diary-sexp-parser limit affiliated))
+ ((match-beginning 4)
+ (org-element-paragraph-parser limit affiliated))
+ ;; Footnote Definition.
+ ((match-beginning 9)
+ (org-element-footnote-definition-parser limit affiliated))
+ ;; Horizontal Rule.
+ ((match-beginning 10)
+ (org-element-horizontal-rule-parser limit affiliated))
+ ;; Diary Sexp.
+ ((match-beginning 11)
+ (org-element-diary-sexp-parser limit affiliated))))
;; Table.
((or (looking-at-p "[ \t]*|")
;; There is no strict definition of a table.el
@@ -4451,16 +4486,13 @@ When PARSE is non-nil, values from keywords belonging to
;; value parsed.
(parsed? (member kwd org-element-parsed-keywords))
;; Find main value for any keyword.
- (value
- (let ((beg (match-end 0))
- (end (save-excursion
- (end-of-line)
- (skip-chars-backward " \t")
- (point))))
- (if parsed?
- (save-match-data
- (org-element--parse-objects beg end nil restrict))
- (org-trim (buffer-substring-no-properties beg end)))))
+ (value-begin (match-end 0))
+ (value-end
+ (save-excursion
+ (end-of-line)
+ (skip-chars-backward " \t")
+ (point)))
+ value
;; If KWD is a dual keyword, find its secondary value.
;; Maybe parse it.
(dual? (member kwd org-element-dual-keywords))
@@ -4469,18 +4501,23 @@ When PARSE is non-nil, values from keywords belonging to
(let ((sec (match-string-no-properties 2)))
(cond
((and sec parsed?)
- (save-match-data
- (org-element--parse-objects
- (match-beginning 2) (match-end 2) nil restrict)))
+ (org-element--parse-objects
+ (match-beginning 2) (match-end 2) nil restrict))
(sec sec)))))
;; Attribute a property name to KWD.
(kwd-sym (and kwd (intern (concat ":" (downcase kwd))))))
+ (setq value
+ (if parsed?
+ (org-element--parse-objects
+ value-begin value-end nil restrict)
+ (org-trim (buffer-substring-no-properties
+ value-begin value-end))))
;; Now set final shape for VALUE.
(when dual?
(setq value (and (or value dual-value) (cons value dual-value))))
(when (or (member kwd org-element-multiple-keywords)
;; Attributes can always appear on multiple lines.
- (string-match "^ATTR_" kwd))
+ (string-match-p "^ATTR_" kwd))
(setq value (nconc (plist-get output kwd-sym) (list value))))
;; Eventually store the new value in OUTPUT.
(setq output (plist-put output kwd-sym value))
@@ -4488,7 +4525,7 @@ When PARSE is non-nil, values from keywords belonging to
(forward-line)))
;; If affiliated keywords are orphaned: move back to first one.
;; They will be parsed as a paragraph.
- (when (looking-at "[ \t]*$") (goto-char origin) (setq output nil))
+ (when (looking-at-p "[ \t]*$") (goto-char origin) (setq output nil))
;; Return value.
(cons origin output))))
@@ -5108,7 +5145,7 @@ If there is no affiliated keyword, return the empty
string."
(when value
(if (or (member keyword org-element-multiple-keywords)
;; All attribute keywords can have multiple lines.
- (string-match "^ATTR_" keyword))
+ (string-match-p "^ATTR_" keyword))
(mapconcat (lambda (line) (funcall keyword-to-org keyword line))
value "")
(funcall keyword-to-org keyword value)))))
@@ -5119,7 +5156,7 @@ If there is no affiliated keyword, return the empty
string."
(org-element-properties-mapc
(lambda (prop _ _)
(let ((keyword (upcase (substring (symbol-name prop) 1))))
- (when (or (string-match "^ATTR_" keyword)
+ (when (or (string-match-p "^ATTR_" keyword)
(and
(member keyword org-element-affiliated-keywords)
(not (assoc keyword
@@ -6476,214 +6513,212 @@ when the parsing should stop. The function throws
`org-element--cache-interrupt' if the process stopped before finding
the expected result."
(catch 'exit
- (save-match-data
- (org-with-base-buffer nil
- (org-with-wide-buffer
- (goto-char pos)
- (save-excursion
- (end-of-line)
- (skip-chars-backward " \r\t\n")
- ;; Within blank lines at the beginning of buffer, return nil.
- (when (bobp) (throw 'exit nil)))
- (let* ((cached (and (org-element--cache-active-p)
- (org-element--cache-find pos nil)))
- (mode (org-element-property :mode cached))
- element next)
- (cond
- ;; Nothing in cache before point: start parsing from first
- ;; element in buffer down to POS or from the beginning of the
- ;; file.
- ((and (not cached) (org-element--cache-active-p))
+ (org-with-base-buffer nil
+ (org-with-wide-buffer
+ (goto-char pos)
+ (save-excursion
+ (end-of-line)
+ (skip-chars-backward " \r\t\n")
+ ;; Within blank lines at the beginning of buffer, return nil.
+ (when (bobp) (throw 'exit nil)))
+ (let* ((cached (and (org-element--cache-active-p)
+ (org-element--cache-find pos nil)))
+ (mode (org-element-property :mode cached))
+ element next)
+ (cond
+ ;; Nothing in cache before point: start parsing from first
+ ;; element in buffer down to POS or from the beginning of the
+ ;; file.
+ ((and (not cached) (org-element--cache-active-p))
+ (setq element (org-element-org-data-parser))
+ (unless (org-element-begin element)
+ (org-element--cache-warn "Error parsing org-data. Got %S\nPlease
report to Org mode mailing list (M-x org-submit-bug-report)." element))
+ (org-element--cache-log-message
+ "Nothing in cache. Adding org-data: %S"
+ (org-element--format-element element))
+ (org-element--cache-put element)
+ (goto-char (org-element-contents-begin element))
+ (setq mode 'org-data))
+ ;; Nothing in cache before point because cache is not active.
+ ;; Parse from previous heading to avoid re-parsing the whole
+ ;; buffer above. Arrange `:parent' to be calculated on demand.
+ ((not cached)
+ (end-of-line) ; ensure the end of current heading.
+ (if (re-search-backward
+ (org-get-limited-outline-regexp t)
+ nil 'move)
+ (progn
+ (setq element (org-element-headline-parser nil 'fast))
+ (org-element-put-property
+ element :parent
+ (org-element-deferred-create
+ t #'org-element--headline-parent-deferred))
+ (setq mode 'planning)
+ (forward-line))
(setq element (org-element-org-data-parser))
- (unless (org-element-begin element)
- (org-element--cache-warn "Error parsing org-data. Got
%S\nPlease report to Org mode mailing list (M-x org-submit-bug-report)."
element))
- (org-element--cache-log-message
- "Nothing in cache. Adding org-data: %S"
- (org-element--format-element element))
- (org-element--cache-put element)
- (goto-char (org-element-contents-begin element))
(setq mode 'org-data))
- ;; Nothing in cache before point because cache is not active.
- ;; Parse from previous heading to avoid re-parsing the whole
- ;; buffer above. Arrange `:parent' to be calculated on demand.
- ((not cached)
- (end-of-line) ; ensure the end of current heading.
- (if (re-search-backward
- (org-get-limited-outline-regexp t)
- nil 'move)
- (progn
- (setq element (org-element-headline-parser nil 'fast))
- (org-element-put-property
- element :parent
- (org-element-deferred-create
- t #'org-element--headline-parent-deferred))
- (setq mode 'planning)
- (forward-line))
- (setq element (org-element-org-data-parser))
- (setq mode 'org-data))
- (org-skip-whitespace)
- (beginning-of-line))
- ;; Check if CACHED or any of its ancestors contain point.
- ;;
- ;; If there is such an element, we inspect it in order to know
- ;; if we return it or if we need to parse its contents.
- ;; Otherwise, we just start parsing from location, which is
- ;; right after the top-most element containing CACHED but
- ;; still before POS.
- ;;
- ;; As a special case, if POS is at the end of the buffer, we
- ;; want to return the innermost element ending there.
- ;;
- ;; Also, if we find an ancestor and discover that we need to
- ;; parse its contents, make sure we don't start from
- ;; `:contents-begin', as we would otherwise go past CACHED
- ;; again. Instead, in that situation, we will resume parsing
- ;; from NEXT, which is located after CACHED or its higher
- ;; ancestor not containing point.
- (t
- (let ((up cached)
- (pos (if (= (point-max) pos) (1- pos) pos)))
- (while (and up (<= (org-element-end up) pos))
- (setq next (org-element-end up)
- element up
- mode (org-element--next-mode (org-element-property
:mode element) (org-element-type element) nil)
- up (org-element-parent up)))
- (when next (goto-char next))
- (when up (setq element up)))))
- ;; Parse successively each element until we reach POS.
- (let ((end (or (org-element-end element) (point-max)))
- (parent (when (org-element--cache-active-p)
- ;; Cache is not active. Parent is deferred.
- ;; We will not actually use parent during
- ;; the first iteration of the `while' loop.
- ;; Avoid undeferring here.
- (org-element-parent element))))
- (while t
- (when (org-element--cache-interrupt-p time-limit)
- (throw 'org-element--cache-interrupt nil))
- (when (and inhibit-quit org-element--cache-interrupt-C-g
quit-flag)
- (when quit-flag
- (cl-incf org-element--cache-interrupt-C-g-count)
- (setq quit-flag nil))
- (when (>= org-element--cache-interrupt-C-g-count
- org-element--cache-interrupt-C-g-max-count)
- (setq quit-flag t)
- (setq org-element--cache-interrupt-C-g-count 0)
- (org-element-cache-reset)
- (error "org-element: Parsing aborted by user. Cache has
been cleared.
+ (org-skip-whitespace)
+ (beginning-of-line))
+ ;; Check if CACHED or any of its ancestors contain point.
+ ;;
+ ;; If there is such an element, we inspect it in order to know
+ ;; if we return it or if we need to parse its contents.
+ ;; Otherwise, we just start parsing from location, which is
+ ;; right after the top-most element containing CACHED but
+ ;; still before POS.
+ ;;
+ ;; As a special case, if POS is at the end of the buffer, we
+ ;; want to return the innermost element ending there.
+ ;;
+ ;; Also, if we find an ancestor and discover that we need to
+ ;; parse its contents, make sure we don't start from
+ ;; `:contents-begin', as we would otherwise go past CACHED
+ ;; again. Instead, in that situation, we will resume parsing
+ ;; from NEXT, which is located after CACHED or its higher
+ ;; ancestor not containing point.
+ (t
+ (let ((up cached)
+ (pos (if (= (point-max) pos) (1- pos) pos)))
+ (while (and up (<= (org-element-end up) pos))
+ (setq next (org-element-end up)
+ element up
+ mode (org-element--next-mode (org-element-property :mode
element) (org-element-type element) nil)
+ up (org-element-parent up)))
+ (when next (goto-char next))
+ (when up (setq element up)))))
+ ;; Parse successively each element until we reach POS.
+ (let ((end (or (org-element-end element) (point-max)))
+ (parent (when (org-element--cache-active-p)
+ ;; Cache is not active. Parent is deferred.
+ ;; We will not actually use parent during
+ ;; the first iteration of the `while' loop.
+ ;; Avoid undeferring here.
+ (org-element-parent element))))
+ (while t
+ (when (org-element--cache-interrupt-p time-limit)
+ (throw 'org-element--cache-interrupt nil))
+ (when (and inhibit-quit org-element--cache-interrupt-C-g
quit-flag)
+ (when quit-flag
+ (cl-incf org-element--cache-interrupt-C-g-count)
+ (setq quit-flag nil))
+ (when (>= org-element--cache-interrupt-C-g-count
+ org-element--cache-interrupt-C-g-max-count)
+ (setq quit-flag t)
+ (setq org-element--cache-interrupt-C-g-count 0)
+ (org-element-cache-reset)
+ (error "org-element: Parsing aborted by user. Cache has been
cleared.
If you observe Emacs hangs frequently, please report this to Org mode mailing
list (M-x org-submit-bug-report)."))
- (message (substitute-command-keys
- "`org-element--parse-buffer': Suppressed
`\\[keyboard-quit]'. Press `\\[keyboard-quit]' %d more times to force
interruption.")
- (- org-element--cache-interrupt-C-g-max-count
- org-element--cache-interrupt-C-g-count)))
- (unless element
- ;; Do not try to parse within blank at EOB.
- (unless (save-excursion
- (org-skip-whitespace)
- (eobp))
- (setq element (org-element--current-element
- end 'element mode
- (org-element-property :structure parent))))
- ;; Make sure that we return referenced element in cache
- ;; that can be altered directly.
- (if element
- (setq element (or (org-element--cache-put element)
element))
- ;; Nothing to parse (i.e. empty file).
- (throw 'exit parent))
- (unless (or parent (not (org-element--cache-active-p)))
- (org-element--cache-warn
- "Got empty parent while parsing. Please report it to Org
mode mailing list (M-x org-submit-bug-report).\n Backtrace:\n%S"
- (when (and (fboundp 'backtrace-get-frames)
- (fboundp 'backtrace-to-string))
- (backtrace-to-string (backtrace-get-frames 'backtrace))
- (org-element-cache-reset)
- (error "org-element--cache: Emergency exit"))))
- (org-element-put-property element :parent parent))
- (let ((elem-end (org-element-end element))
- (type (org-element-type element)))
- (cond
- ;; Skip any element ending before point. Also skip
- ;; element ending at point (unless it is also the end of
- ;; buffer) since we're sure that another element begins
- ;; after it.
- ((and (<= elem-end pos) (/= (point-max) elem-end))
- ;; Avoid parsing headline siblings above.
- (goto-char elem-end)
- (when (eq type 'headline)
- (save-match-data
- (unless (when (and (/= 1 (org-element-property
:true-level element))
- (re-search-forward
- (org-headline-re (1-
(org-element-property :true-level element)))
- pos t))
- (beginning-of-line)
- t)
- ;; There are headings with lower level than
- ;; ELEMENT between ELEM-END and POS. Siblings
- ;; may exist though. Parse starting from the
- ;; last sibling or from ELEM-END if there are
- ;; no other siblings.
- (goto-char pos)
- (unless
- (re-search-backward
- (org-headline-re (org-element-property
:true-level element))
- elem-end t)
- ;; Roll-back to normal parsing.
- (goto-char elem-end)))))
- (setq mode (org-element--next-mode mode type nil)))
- ;; A non-greater element contains point: return it.
- ((not (memq type org-element-greater-elements))
- (throw 'exit (if syncp parent element)))
- ;; Otherwise, we have to decide if ELEMENT really
- ;; contains POS. In that case we start parsing from
- ;; contents' beginning.
- ;;
- ;; If POS is at contents' beginning but it is also at
- ;; the beginning of the first item in a list or a table.
- ;; In that case, we need to create an anchor for that
- ;; list or table, so return it.
- ;;
- ;; Also, if POS is at the end of the buffer, no element
- ;; can start after it, but more than one may end there.
- ;; Arbitrarily, we choose to return the innermost of
- ;; such elements.
- ((let ((cbeg (org-element-contents-begin element))
- (cend (org-element-contents-end element)))
- (when (and cbeg cend
- (or (< cbeg pos)
- (and (= cbeg pos)
- (not (memq type '(plain-list table)))))
- (or (> cend pos)
- ;; When we are at cend or within blank
- ;; lines after, it is a special case:
- ;; 1. At the end of buffer we return
- ;; the innermost element.
- ;; 2. At cend of element with return
- ;; that element.
- ;; 3. At the end of element, we would
- ;; return in the earlier cond form.
- ;; 4. Within blank lines after cend,
- ;; when element does not have a
- ;; closing keyword, we return that
- ;; outermost element, unless the
- ;; outermost element is a non-empty
- ;; headline. In the latter case, we
- ;; return the outermost element inside
- ;; the headline section.
- (and (org-element--open-end-p element)
- (or (= (org-element-end element)
(point-max))
- (and (>= pos
(org-element-contents-end element))
- (org-element-type-p element
'(org-data section headline)))))))
- (goto-char (or next cbeg))
- (setq mode (if next mode (org-element--next-mode mode
type t))
- next nil
- parent element
- end (if (org-element--open-end-p element)
- (org-element-end element)
- (org-element-contents-end element))))))
- ;; Otherwise, return ELEMENT as it is the smallest
- ;; element containing POS.
- (t (throw 'exit (if syncp parent element)))))
- (setq element nil)))))))))
+ (message (substitute-command-keys
+ "`org-element--parse-buffer': Suppressed
`\\[keyboard-quit]'. Press `\\[keyboard-quit]' %d more times to force
interruption.")
+ (- org-element--cache-interrupt-C-g-max-count
+ org-element--cache-interrupt-C-g-count)))
+ (unless element
+ ;; Do not try to parse within blank at EOB.
+ (unless (save-excursion
+ (org-skip-whitespace)
+ (eobp))
+ (setq element (org-element--current-element
+ end 'element mode
+ (org-element-property :structure parent))))
+ ;; Make sure that we return referenced element in cache
+ ;; that can be altered directly.
+ (if element
+ (setq element (or (org-element--cache-put element) element))
+ ;; Nothing to parse (i.e. empty file).
+ (throw 'exit parent))
+ (unless (or parent (not (org-element--cache-active-p)))
+ (org-element--cache-warn
+ "Got empty parent while parsing. Please report it to Org
mode mailing list (M-x org-submit-bug-report).\n Backtrace:\n%S"
+ (when (and (fboundp 'backtrace-get-frames)
+ (fboundp 'backtrace-to-string))
+ (backtrace-to-string (backtrace-get-frames 'backtrace))
+ (org-element-cache-reset)
+ (error "org-element--cache: Emergency exit"))))
+ (org-element-put-property element :parent parent))
+ (let ((elem-end (org-element-end element))
+ (type (org-element-type element)))
+ (cond
+ ;; Skip any element ending before point. Also skip
+ ;; element ending at point (unless it is also the end of
+ ;; buffer) since we're sure that another element begins
+ ;; after it.
+ ((and (<= elem-end pos) (/= (point-max) elem-end))
+ ;; Avoid parsing headline siblings above.
+ (goto-char elem-end)
+ (when (eq type 'headline)
+ (unless (when (and (/= 1 (org-element-property :true-level
element))
+ (re-search-forward
+ (org-headline-re (1-
(org-element-property :true-level element)))
+ pos t))
+ (beginning-of-line)
+ t)
+ ;; There are headings with lower level than
+ ;; ELEMENT between ELEM-END and POS. Siblings
+ ;; may exist though. Parse starting from the
+ ;; last sibling or from ELEM-END if there are
+ ;; no other siblings.
+ (goto-char pos)
+ (unless
+ (re-search-backward
+ (org-headline-re (org-element-property :true-level
element))
+ elem-end t)
+ ;; Roll-back to normal parsing.
+ (goto-char elem-end))))
+ (setq mode (org-element--next-mode mode type nil)))
+ ;; A non-greater element contains point: return it.
+ ((not (memq type org-element-greater-elements))
+ (throw 'exit (if syncp parent element)))
+ ;; Otherwise, we have to decide if ELEMENT really
+ ;; contains POS. In that case we start parsing from
+ ;; contents' beginning.
+ ;;
+ ;; If POS is at contents' beginning but it is also at
+ ;; the beginning of the first item in a list or a table.
+ ;; In that case, we need to create an anchor for that
+ ;; list or table, so return it.
+ ;;
+ ;; Also, if POS is at the end of the buffer, no element
+ ;; can start after it, but more than one may end there.
+ ;; Arbitrarily, we choose to return the innermost of
+ ;; such elements.
+ ((let ((cbeg (org-element-contents-begin element))
+ (cend (org-element-contents-end element)))
+ (when (and cbeg cend
+ (or (< cbeg pos)
+ (and (= cbeg pos)
+ (not (memq type '(plain-list table)))))
+ (or (> cend pos)
+ ;; When we are at cend or within blank
+ ;; lines after, it is a special case:
+ ;; 1. At the end of buffer we return
+ ;; the innermost element.
+ ;; 2. At cend of element with return
+ ;; that element.
+ ;; 3. At the end of element, we would
+ ;; return in the earlier cond form.
+ ;; 4. Within blank lines after cend,
+ ;; when element does not have a
+ ;; closing keyword, we return that
+ ;; outermost element, unless the
+ ;; outermost element is a non-empty
+ ;; headline. In the latter case, we
+ ;; return the outermost element inside
+ ;; the headline section.
+ (and (org-element--open-end-p element)
+ (or (= (org-element-end element)
(point-max))
+ (and (>= pos
(org-element-contents-end element))
+ (org-element-type-p element
'(org-data section headline)))))))
+ (goto-char (or next cbeg))
+ (setq mode (if next mode (org-element--next-mode mode type
t))
+ next nil
+ parent element
+ end (if (org-element--open-end-p element)
+ (org-element-end element)
+ (org-element-contents-end element))))))
+ ;; Otherwise, return ELEMENT as it is the smallest
+ ;; element containing POS.
+ (t (throw 'exit (if syncp parent element)))))
+ (setq element nil))))))))
;;;; Staging Buffer Changes
@@ -6744,6 +6779,7 @@ The function returns the new value of
`org-element--cache-change-warning'."
(let ((org-element--cache-change-warning-before
org-element--cache-change-warning)
(org-element--cache-change-warning-after))
(setq org-element--cache-change-warning-after
+ ;; We must preserve match data when called as
`before-change-functions'.
(save-match-data
(let ((case-fold-search t))
(when (re-search-forward
@@ -6802,6 +6838,7 @@ that range. See `after-change-functions' for more
information."
(line-beginning-position)))
;; Store synchronization request.
(let ((offset (- end beg pre)))
+ ;; We must preserve match data when called as
`after-change-functions'.
(save-match-data
(org-element--cache-submit-request beg (- end offset) offset)))
;; Activate a timer to process the request during idle time.
@@ -6909,7 +6946,7 @@ known element in cache (it may start after END)."
(org-element-property :level up)))
(org-with-point-at (org-element-contents-begin up)
(unless
- (save-match-data
+ (progn
(when (looking-at-p
org-element-planning-line-re)
(forward-line))
(when (looking-at org-property-drawer-re)
@@ -6926,9 +6963,8 @@ known element in cache (it may start after END)."
;; Should not see property
;; drawer within changed
;; region.
- (save-match-data
- (or (not (looking-at
org-property-drawer-re))
- (> beg (match-end 0)))))))
+ (or (not (looking-at
org-property-drawer-re))
+ (> beg (match-end 0))))))
(_ 'robust)))))
;; UP is a robust greater element containing changes.
;; We only need to extend its ending boundaries.
@@ -7508,6 +7544,7 @@ the cache."
;; can be found. When RE is nil, just find element at
;; point.
(move-start-to-next-match
+ ;; Preserve match data that might be set by FUNC.
(re) `(save-match-data
(if (or (not ,re)
(if org-element--cache-map-statistics
@@ -7890,7 +7927,9 @@ the first row of a table, returned element will be the
table
instead of the first row.
When point is at the end of the buffer, return the innermost
-element ending there."
+element ending there.
+
+This function may modify the match data."
(setq pom (or pom (point)))
;; Allow re-parsing when the command can benefit from it.
(when (and cached-only
@@ -7969,117 +8008,118 @@ the beginning of any other object, return that object.
Optional argument ELEMENT, when non-nil, is the closest element
containing point, as returned by `org-element-at-point'.
-Providing it allows for quicker computation."
- (save-match-data
- (catch 'objects-forbidden
- (org-with-wide-buffer
- (let* ((pos (point))
- (element (or element (org-element-at-point)))
- (type (org-element-type element))
- (post (org-element-post-affiliated element)))
- ;; If point is inside an element containing objects or
- ;; a secondary string, narrow buffer to the container and
- ;; proceed with parsing. Otherwise, return ELEMENT.
- (cond
- ;; At a parsed affiliated keyword, check if we're inside main
- ;; or dual value.
- ((and post (< pos post))
- (beginning-of-line)
- (let ((case-fold-search t)) (looking-at org-element--affiliated-re))
- (cond
- ((not (member-ignore-case (match-string 1)
+Providing it allows for quicker computation.
+
+This function may modify match data."
+ (catch 'objects-forbidden
+ (org-with-wide-buffer
+ (let* ((pos (point))
+ (element (or element (org-element-at-point)))
+ (type (org-element-type element))
+ (post (org-element-post-affiliated element)))
+ ;; If point is inside an element containing objects or
+ ;; a secondary string, narrow buffer to the container and
+ ;; proceed with parsing. Otherwise, return ELEMENT.
+ (cond
+ ;; At a parsed affiliated keyword, check if we're inside main
+ ;; or dual value.
+ ((and post (< pos post))
+ (beginning-of-line)
+ (let ((case-fold-search t)) (looking-at org-element--affiliated-re))
+ (cond
+ ((not (member-ignore-case (match-string 1)
org-element-parsed-keywords))
- (throw 'objects-forbidden element))
- ((< (match-end 0) pos)
- (narrow-to-region (match-end 0) (line-end-position)))
- ((and (match-beginning 2)
- (>= pos (match-beginning 2))
- (< pos (match-end 2)))
- (narrow-to-region (match-beginning 2) (match-end 2)))
- (t (throw 'objects-forbidden element)))
- ;; Also change type to retrieve correct restrictions.
- (setq type 'keyword))
- ;; At an item, objects can only be located within tag, if any.
- ((eq type 'item)
- (let ((tag (org-element-property :tag element)))
- (if (or (not tag) (/= (line-beginning-position) post))
- (throw 'objects-forbidden element)
- (beginning-of-line)
- (search-forward tag (line-end-position))
- (goto-char (match-beginning 0))
- (if (and (>= pos (point)) (< pos (match-end 0)))
- (narrow-to-region (point) (match-end 0))
- (throw 'objects-forbidden element)))))
- ;; At an headline or inlinetask, objects are in title.
- ((memq type '(headline inlinetask))
- (let ((case-fold-search nil))
- (goto-char (org-element-begin element))
- (looking-at org-complex-heading-regexp)
- (let ((end (match-end 4)))
- (if (not end) (throw 'objects-forbidden element)
- (goto-char (match-beginning 4))
- (when (looking-at org-element-comment-string)
- (goto-char (match-end 0)))
- (if (>= (point) end) (throw 'objects-forbidden element)
- (narrow-to-region (point) end))))))
- ;; At a paragraph, a table-row or a verse block, objects are
- ;; located within their contents.
- ((memq type '(paragraph table-row verse-block))
- (let ((cbeg (org-element-contents-begin element))
- (cend (org-element-contents-end element)))
- ;; CBEG is nil for table rules.
- (if (and cbeg cend (>= pos cbeg)
- (or (< pos cend) (and (= pos cend) (eobp))))
- (narrow-to-region cbeg cend)
- (throw 'objects-forbidden element))))
+ (throw 'objects-forbidden element))
+ ((< (match-end 0) pos)
+ (narrow-to-region (match-end 0) (line-end-position)))
+ ((and (match-beginning 2)
+ (>= pos (match-beginning 2))
+ (< pos (match-end 2)))
+ (narrow-to-region (match-beginning 2) (match-end 2)))
(t (throw 'objects-forbidden element)))
- (goto-char (point-min))
- (let ((restriction (org-element-restriction type))
- (parent element)
- last)
- (catch 'exit
- (while t
- (let ((next (org-element--object-lex restriction)))
- (when next (org-element-put-property next :parent parent))
- ;; Process NEXT, if any, in order to know if we need to
- ;; skip it, return it or move into it.
- (if (or (not next) (> (org-element-begin next) pos))
- (throw 'exit (or last parent))
- (let ((end (org-element-end next))
- (cbeg (org-element-contents-begin next))
- (cend (org-element-contents-end next)))
- (cond
- ;; Skip objects ending before point. Also skip
- ;; objects ending at point unless it is also the
- ;; end of buffer, since we want to return the
- ;; innermost object.
- ((and (<= end pos) (/= (point-max) end))
- (goto-char end)
- ;; For convenience, when object ends at POS,
- ;; without any space, store it in LAST, as we
- ;; will return it if no object starts here.
- (when (and (= end pos)
- (not (memq (char-before) '(?\s ?\t))))
- (setq last next)))
- ;; If POS is within a container object, move into
- ;; that object.
- ((and cbeg cend
- (>= pos cbeg)
- (or (< pos cend)
- ;; At contents' end, if there is no
- ;; space before point, also move into
- ;; object, for consistency with
- ;; convenience feature above.
- (and (= pos cend)
- (or (= (point-max) pos)
- (not (memq (char-before pos)
- '(?\s ?\t)))))))
- (goto-char cbeg)
- (narrow-to-region (point) cend)
- (setq parent next)
- (setq restriction (org-element-restriction next)))
- ;; Otherwise, return NEXT.
- (t (throw 'exit next))))))))))))))
+ ;; Also change type to retrieve correct restrictions.
+ (setq type 'keyword))
+ ;; At an item, objects can only be located within tag, if any.
+ ((eq type 'item)
+ (let ((tag (org-element-property :tag element)))
+ (if (or (not tag) (/= (line-beginning-position) post))
+ (throw 'objects-forbidden element)
+ (beginning-of-line)
+ (search-forward tag (line-end-position))
+ (goto-char (match-beginning 0))
+ (if (and (>= pos (point)) (< pos (match-end 0)))
+ (narrow-to-region (point) (match-end 0))
+ (throw 'objects-forbidden element)))))
+ ;; At an headline or inlinetask, objects are in title.
+ ((memq type '(headline inlinetask))
+ (let ((case-fold-search nil))
+ (goto-char (org-element-begin element))
+ (looking-at org-complex-heading-regexp)
+ (let ((end (match-end 4)))
+ (if (not end) (throw 'objects-forbidden element)
+ (goto-char (match-beginning 4))
+ (when (looking-at org-element-comment-string)
+ (goto-char (match-end 0)))
+ (if (>= (point) end) (throw 'objects-forbidden element)
+ (narrow-to-region (point) end))))))
+ ;; At a paragraph, a table-row or a verse block, objects are
+ ;; located within their contents.
+ ((memq type '(paragraph table-row verse-block))
+ (let ((cbeg (org-element-contents-begin element))
+ (cend (org-element-contents-end element)))
+ ;; CBEG is nil for table rules.
+ (if (and cbeg cend (>= pos cbeg)
+ (or (< pos cend) (and (= pos cend) (eobp))))
+ (narrow-to-region cbeg cend)
+ (throw 'objects-forbidden element))))
+ (t (throw 'objects-forbidden element)))
+ (goto-char (point-min))
+ (let ((restriction (org-element-restriction type))
+ (parent element)
+ last)
+ (catch 'exit
+ (while t
+ (let ((next (org-element--object-lex restriction)))
+ (when next (org-element-put-property next :parent parent))
+ ;; Process NEXT, if any, in order to know if we need to
+ ;; skip it, return it or move into it.
+ (if (or (not next) (> (org-element-begin next) pos))
+ (throw 'exit (or last parent))
+ (let ((end (org-element-end next))
+ (cbeg (org-element-contents-begin next))
+ (cend (org-element-contents-end next)))
+ (cond
+ ;; Skip objects ending before point. Also skip
+ ;; objects ending at point unless it is also the
+ ;; end of buffer, since we want to return the
+ ;; innermost object.
+ ((and (<= end pos) (/= (point-max) end))
+ (goto-char end)
+ ;; For convenience, when object ends at POS,
+ ;; without any space, store it in LAST, as we
+ ;; will return it if no object starts here.
+ (when (and (= end pos)
+ (not (memq (char-before) '(?\s ?\t))))
+ (setq last next)))
+ ;; If POS is within a container object, move into
+ ;; that object.
+ ((and cbeg cend
+ (>= pos cbeg)
+ (or (< pos cend)
+ ;; At contents' end, if there is no
+ ;; space before point, also move into
+ ;; object, for consistency with
+ ;; convenience feature above.
+ (and (= pos cend)
+ (or (= (point-max) pos)
+ (not (memq (char-before pos)
+ '(?\s ?\t)))))))
+ (goto-char cbeg)
+ (narrow-to-region (point) cend)
+ (setq parent next)
+ (setq restriction (org-element-restriction next)))
+ ;; Otherwise, return NEXT.
+ (t (throw 'exit next)))))))))))))
(defun org-element-nested-p (elem-A elem-B)
"Non-nil when elements ELEM-A and ELEM-B are nested."
diff --git a/lisp/org.el b/lisp/org.el
index 68aaa635b6..1a215ca320 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -8537,7 +8537,7 @@ there is one, return it."
;; Only consider valid links or links openable via
;; `org-open-at-point'.
(when (org-element-type-p
- (org-element-context)
+ (save-match-data (org-element-context))
'(link comment comment-block node-property keyword))
(push (match-string 0) links)))
(setq links (org-uniquify (reverse links))))
- [elpa] externals/org 37a58cc6ee 094/101: org-agenda-skip: Do not alter match data, (continued)
- [elpa] externals/org 37a58cc6ee 094/101: org-agenda-skip: Do not alter match data, ELPA Syncer, 2023/07/01
- [elpa] externals/org 66c8e94732 100/101: lisp/org-element-ast.el: Fix typo in commentary, ELPA Syncer, 2023/07/01
- [elpa] externals/org 948c896448 101/101: Rename `org-element-property-1' to `org-element-property-raw', ELPA Syncer, 2023/07/01
- [elpa] externals/org 932c49c7bb 019/101: org-element-headline-parser: Use deferred properties, ELPA Syncer, 2023/07/01
- [elpa] externals/org 244cc7d560 029/101: org-element-org-data-parser: Defer parsing, ELPA Syncer, 2023/07/01
- [elpa] externals/org b00c2d0e1d 039/101: org-element--parse-to: Provide deferred :parent when cache is inactive, ELPA Syncer, 2023/07/01
- [elpa] externals/org 81a7e8c836 037/101: org-element-lineage: Allow TYPES to be a symbol, ELPA Syncer, 2023/07/01
- [elpa] externals/org 4d63e571e0 042/101: org-get-tags: Use org-element API, ELPA Syncer, 2023/07/01
- [elpa] externals/org f225e24869 040/101: org-element--current-element: Make defsubst, ELPA Syncer, 2023/07/01
- [elpa] externals/org dfd36d1969 049/101: org-up-heading-safe: Use element API, ELPA Syncer, 2023/07/01
- [elpa] externals/org fefaadc2d5 050/101: org-element: Optimize working with match data and Emacs regexps,
ELPA Syncer <=
- [elpa] externals/org 6266197bbd 051/101: org-at-item-p: Use org-element API, ELPA Syncer, 2023/07/01
- [elpa] externals/org 6938857369 052/101: org-goto-first-child: Switch to org-element API, ELPA Syncer, 2023/07/01
- [elpa] externals/org 7c549f4841 060/101: org-babel-tangle-collect-blocks: Switch to org-element API, ELPA Syncer, 2023/07/01
- [elpa] externals/org b2482d9cc4 070/101: * testing/lisp/test-org-element.el: Add new tests, ELPA Syncer, 2023/07/01
- [elpa] externals/org b0a2deaec6 073/101: org-element--current-element: Faster property drawer check, ELPA Syncer, 2023/07/01
- [elpa] externals/org 110601ce74 074/101: org-element--current-element: Add FIXME, ELPA Syncer, 2023/07/01
- [elpa] externals/org 48e4a76b99 079/101: org-element: Avoid computing static regexps dynamically, ELPA Syncer, 2023/07/01
- [elpa] externals/org f895be1ad9 086/101: org-element--properties-mapc: Small refactoring, ELPA Syncer, 2023/07/01
- [elpa] externals/org a04e16bd11 082/101: lisp/org-macs.el: Restructure file outline, fixing compiler warning, ELPA Syncer, 2023/07/01
- [elpa] externals/org 5a3224a325 085/101: etc/ORG-NEWS: Document Org API changes, ELPA Syncer, 2023/07/01