summaryrefslogtreecommitdiff
path: root/lisp/org/org-element.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/org/org-element.el')
-rw-r--r--lisp/org/org-element.el268
1 files changed, 137 insertions, 131 deletions
diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el
index 4b5f9a19e6d..2ad557d2179 100644
--- a/lisp/org/org-element.el
+++ b/lisp/org/org-element.el
@@ -72,7 +72,6 @@
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(declare-function org-escape-code-in-string "org-src" (s))
-(declare-function org-find-visible "org" ())
(declare-function org-macro-escape-arguments "org-macro" (&rest args))
(declare-function org-macro-extract-arguments "org-macro" (s))
(declare-function org-reduced-level "org" (l))
@@ -330,7 +329,9 @@ match group 2.
Don't modify it, set `org-element-affiliated-keywords' instead.")
(defconst org-element-object-restrictions
- (let* ((standard-set (remq 'table-cell org-element-all-objects))
+ (let* ((minimal-set '(bold code entity italic latex-fragment strike-through
+ subscript superscript underline verbatim))
+ (standard-set (remq 'table-cell org-element-all-objects))
(standard-set-no-line-break (remq 'line-break standard-set)))
`((bold ,@standard-set)
(footnote-reference ,@standard-set)
@@ -341,23 +342,20 @@ Don't modify it, set `org-element-affiliated-keywords' instead.")
(keyword ,@(remq 'footnote-reference standard-set))
;; Ignore all links in a link description. Also ignore
;; radio-targets and line breaks.
- (link bold code entity export-snippet inline-babel-call inline-src-block
- italic latex-fragment macro statistics-cookie strike-through
- subscript superscript underline verbatim)
+ (link export-snippet inline-babel-call inline-src-block macro
+ statistics-cookie ,@minimal-set)
(paragraph ,@standard-set)
;; Remove any variable object from radio target as it would
;; prevent it from being properly recognized.
- (radio-target bold code entity italic latex-fragment strike-through
- subscript superscript underline superscript)
+ (radio-target ,@minimal-set)
(strike-through ,@standard-set)
(subscript ,@standard-set)
(superscript ,@standard-set)
;; Ignore inline babel call and inline source block as formulas
;; are possible. Also ignore line breaks and statistics
;; cookies.
- (table-cell bold code entity export-snippet footnote-reference italic
- latex-fragment link macro radio-target strike-through
- subscript superscript target timestamp underline verbatim)
+ (table-cell export-snippet footnote-reference link macro radio-target
+ target timestamp ,@minimal-set)
(table-row table-cell)
(underline ,@standard-set)
(verse-block ,@standard-set)))
@@ -367,10 +365,6 @@ key is an element or object type containing objects and value is
a list of types that can be contained within an element or object
of such type.
-For example, in a `radio-target' object, one can only find
-entities, latex-fragments, subscript, superscript and text
-markup.
-
This alist also applies to secondary string. For example, an
`headline' type element doesn't directly contain objects, but
still has an entry since one of its properties (`:title') does.")
@@ -1806,13 +1800,10 @@ Return a list whose CAR is `clock' and CDR is a plist containing
;;;; Comment
-(defun org-element-comment-parser (limit affiliated)
+(defun org-element-comment-parser (limit)
"Parse a comment.
-LIMIT bounds the search. AFFILIATED is a list of which CAR is
-the buffer position at the beginning of the first affiliated
-keyword and CDR is a plist of affiliated keywords along with
-their value.
+LIMIT bounds the search.
Return a list whose CAR is `comment' and CDR is a plist
containing `:begin', `:end', `:value', `:post-blank',
@@ -1820,8 +1811,7 @@ containing `:begin', `:end', `:value', `:post-blank',
Assume point is at comment beginning."
(save-excursion
- (let* ((begin (car affiliated))
- (post-affiliated (point))
+ (let* ((begin (point))
(value (prog2 (looking-at "[ \t]*# ?")
(buffer-substring-no-properties
(match-end 0) (line-end-position))
@@ -1843,13 +1833,11 @@ Assume point is at comment beginning."
(skip-chars-forward " \r\t\n" limit)
(if (eobp) (point) (line-beginning-position)))))
(list 'comment
- (nconc
- (list :begin begin
- :end end
- :value value
- :post-blank (count-lines com-end end)
- :post-affiliated post-affiliated)
- (cdr affiliated))))))
+ (list :begin begin
+ :end end
+ :value value
+ :post-blank (count-lines com-end end)
+ :post-affiliated begin)))))
(defun org-element-comment-interpreter (comment _)
"Interpret COMMENT element as Org syntax.
@@ -2186,9 +2174,9 @@ the buffer position at the beginning of the first affiliated
keyword and CDR is a plist of affiliated keywords along with
their value.
-Return a list whose CAR is `keyword' and CDR is a plist
-containing `:key', `:value', `:begin', `:end', `:post-blank' and
-`:post-affiliated' keywords."
+Return a list whose CAR is a normalized `keyword' (uppercase) and
+CDR is a plist containing `:key', `:value', `:begin', `:end',
+`:post-blank' and `:post-affiliated' keywords."
(save-excursion
;; An orphaned affiliated keyword is considered as a regular
;; keyword. In this case AFFILIATED is nil, so we take care of
@@ -3217,10 +3205,11 @@ Assume point is at the beginning of the link."
(setq post-blank
(progn (goto-char link-end) (skip-chars-forward " \t")))
(setq end (point)))
- ;; Special "file" type link processing. Extract opening
+ ;; Special "file"-type link processing. Extract opening
;; application and search option, if any. Also normalize URI.
(when (string-match "\\`file\\(?:\\+\\(.+\\)\\)?\\'" type)
- (setq application (match-string 1 type) type "file")
+ (setq application (match-string 1 type))
+ (setq type "file")
(when (string-match "::\\(.*\\)\\'" path)
(setq search-option (match-string 1 path))
(setq path (replace-match "" nil nil path)))
@@ -3823,12 +3812,6 @@ Assume point is at the first equal sign marker."
;; `org-element--current-element' is the core function of this section.
;; It returns the Lisp representation of the element starting at
;; point.
-;;
-;; `org-element--current-element' makes use of special modes. They
-;; are activated for fixed element chaining (e.g., `plain-list' >
-;; `item') or fixed conditional element chaining (e.g., `headline' >
-;; `section'). Special modes are: `first-section', `item',
-;; `node-property', `section' and `table-row'.
(defun org-element--current-element (limit &optional granularity mode structure)
"Parse the element starting at point.
@@ -3848,8 +3831,9 @@ nil), secondary values will not be parsed, since they only
contain objects.
Optional argument MODE, when non-nil, can be either
-`first-section', `section', `planning', `item', `node-property'
-and `table-row'.
+`first-section', `item', `node-property', `planning',
+`property-drawer', `section', `table-row', or `top-comment'.
+
If STRUCTURE isn't provided but MODE is set to `item', it will be
computed.
@@ -3879,15 +3863,22 @@ element it has to parse."
(org-element-section-parser
(or (save-excursion (org-with-limited-levels (outline-next-heading)))
limit)))
+ ;; Comments.
+ ((looking-at "^[ \t]*#\\(?: \\|$\\)")
+ (org-element-comment-parser limit))
;; Planning.
((and (eq mode 'planning)
(eq ?* (char-after (line-beginning-position 0)))
(looking-at org-planning-line-re))
(org-element-planning-parser limit))
;; Property drawer.
- ((and (memq mode '(planning property-drawer))
- (eq ?* (char-after (line-beginning-position
- (if (eq mode 'planning) 0 -1))))
+ ((and (pcase mode
+ (`planning (eq ?* (char-after (line-beginning-position 0))))
+ ((or `property-drawer `top-comment)
+ (save-excursion
+ (beginning-of-line 0)
+ (not (looking-at "[[:blank:]]*$"))))
+ (_ nil))
(looking-at org-property-drawer-re))
(org-element-property-drawer-parser limit))
;; When not at bol, point is at the beginning of an item or
@@ -3896,7 +3887,7 @@ element it has to parse."
;; Clock.
((looking-at org-clock-line-re) (org-element-clock-parser limit))
;; Inlinetask.
- ((org-at-heading-p)
+ ((looking-at "^\\*+ ")
(org-element-inlinetask-parser limit raw-secondary-p))
;; From there, elements can have affiliated keywords.
(t (let ((affiliated (org-element--collect-affiliated-keywords
@@ -3910,7 +3901,7 @@ element it has to parse."
;; LaTeX Environment.
((looking-at org-element--latex-begin-environment)
(org-element-latex-environment-parser limit affiliated))
- ;; Drawer and Property Drawer.
+ ;; Drawer.
((looking-at org-drawer-regexp)
(org-element-drawer-parser limit affiliated))
;; Fixed Width
@@ -3918,13 +3909,10 @@ element it has to parse."
(org-element-fixed-width-parser limit affiliated))
;; Inline Comments, Blocks, Babel Calls, Dynamic Blocks and
;; Keywords.
- ((looking-at "[ \t]*#")
+ ((looking-at "[ \t]*#\\+")
(goto-char (match-end 0))
(cond
- ((looking-at "\\(?: \\|$\\)")
- (beginning-of-line)
- (org-element-comment-parser limit affiliated))
- ((looking-at "\\+BEGIN_\\(\\S-+\\)")
+ ((looking-at "BEGIN_\\(\\S-+\\)")
(beginning-of-line)
(funcall (pcase (upcase (match-string 1))
("CENTER" #'org-element-center-block-parser)
@@ -3937,13 +3925,13 @@ element it has to parse."
(_ #'org-element-special-block-parser))
limit
affiliated))
- ((looking-at "\\+CALL:")
+ ((looking-at "CALL:")
(beginning-of-line)
(org-element-babel-call-parser limit affiliated))
- ((looking-at "\\+BEGIN:? ")
+ ((looking-at "BEGIN:? ")
(beginning-of-line)
(org-element-dynamic-block-parser limit affiliated))
- ((looking-at "\\+\\S-+:")
+ ((looking-at "\\S-+:")
(beginning-of-line)
(org-element-keyword-parser limit affiliated))
(t
@@ -4024,7 +4012,8 @@ When PARSE is non-nil, values from keywords belonging to
(skip-chars-backward " \t")
(point))))
(if parsed?
- (org-element--parse-objects beg end nil restrict)
+ (save-match-data
+ (org-element--parse-objects beg end nil restrict))
(org-trim (buffer-substring-no-properties beg end)))))
;; If KWD is a dual keyword, find its secondary value.
;; Maybe parse it.
@@ -4144,7 +4133,9 @@ If STRING is the empty string or nil, return nil."
(dolist (v local-variables)
(ignore-errors
(if (symbolp v) (makunbound v)
- (set (make-local-variable (car v)) (cdr v)))))
+ ;; Don't set file name to avoid mishandling hooks (bug#44524)
+ (unless (memq (car v) '(buffer-file-name buffer-file-truename))
+ (set (make-local-variable (car v)) (cdr v))))))
;; Transferring local variables may put the temporary buffer
;; into a read-only state. Make sure we can insert STRING.
(let ((inhibit-read-only t)) (insert string))
@@ -4320,34 +4311,41 @@ looking into captions:
;; `org-element--object-lex' to find the next object in the current
;; container.
-(defsubst org-element--next-mode (type parentp)
- "Return next special mode according to TYPE, or nil.
-TYPE is a symbol representing the type of an element or object
-containing next element if PARENTP is non-nil, or before it
-otherwise. Modes can be either `first-section', `item',
-`node-property', `planning', `property-drawer', `section',
-`table-row' or nil."
- (if parentp
+(defsubst org-element--next-mode (mode type parent?)
+ "Return next mode according to current one.
+
+MODE is a symbol representing the expectation about the next
+element or object. Meaningful values are `first-section',
+`item', `node-property', `planning', `property-drawer',
+`section', `table-row', `top-comment', and nil.
+
+TYPE is the type of the current element or object.
+
+If PARENT? is non-nil, assume the next element or object will be
+located inside the current one. "
+ (if parent?
(pcase type
(`headline 'section)
+ ((and (guard (eq mode 'first-section)) `section) 'top-comment)
(`inlinetask 'planning)
(`plain-list 'item)
(`property-drawer 'node-property)
(`section 'planning)
(`table 'table-row))
- (pcase type
+ (pcase mode
(`item 'item)
(`node-property 'node-property)
- (`planning 'property-drawer)
- (`table-row 'table-row))))
+ ((and `planning (guard (eq type 'planning))) 'property-drawer)
+ (`table-row 'table-row)
+ ((and `top-comment (guard (eq type 'comment))) 'property-drawer))))
(defun org-element--parse-elements
(beg end mode structure granularity visible-only acc)
"Parse elements between BEG and END positions.
MODE prioritizes some elements over the others. It can be set to
-`first-section', `section', `planning', `item', `node-property'
-or `table-row'.
+`first-section', `item', `node-property', `planning',
+`property-drawer', `section', `table-row', `top-comment', or nil.
When value is `item', STRUCTURE will be used as the current list
structure.
@@ -4361,54 +4359,52 @@ elements.
Elements are accumulated into ACC."
(save-excursion
(goto-char beg)
- ;; Visible only: skip invisible parts at the beginning of the
- ;; element.
- (when (and visible-only (org-invisible-p2))
- (goto-char (min (1+ (org-find-visible)) end)))
;; When parsing only headlines, skip any text before first one.
(when (and (eq granularity 'headline) (not (org-at-heading-p)))
(org-with-limited-levels (outline-next-heading)))
(let (elements)
(while (< (point) end)
- ;; Find current element's type and parse it accordingly to
- ;; its category.
- (let* ((element (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))
- ;; Visible only: skip invisible parts between siblings.
- (when (and visible-only (org-invisible-p2))
- (goto-char (min (1+ (org-find-visible)) end)))
- ;; Fill ELEMENT contents by side-effect.
- (cond
- ;; If element has no contents, don't modify it.
- ((not cbeg))
- ;; Greater element: parse it between `contents-begin' and
- ;; `contents-end'. Make sure GRANULARITY allows the
- ;; recursion, or ELEMENT is a headline, in which case going
- ;; inside is mandatory, in order to get sub-level headings.
- ((and (memq type org-element-greater-elements)
- (or (memq granularity '(element object nil))
- (and (eq granularity 'greater-element)
- (eq type 'section))
- (eq type 'headline)))
- (org-element--parse-elements
- cbeg (org-element-property :contents-end element)
- ;; Possibly switch to a special mode.
- (org-element--next-mode type t)
- (and (memq type '(item plain-list))
- (org-element-property :structure element))
- granularity visible-only element))
- ;; ELEMENT has contents. Parse objects inside, if
- ;; GRANULARITY allows it.
- ((memq granularity '(object nil))
- (org-element--parse-objects
- cbeg (org-element-property :contents-end element) element
- (org-element-restriction type))))
- (push (org-element-put-property element :parent acc) elements)
- ;; Update mode.
- (setq mode (org-element--next-mode type nil))))
+ ;; Visible only: skip invisible parts due to folding.
+ (if (and visible-only (org-invisible-p nil t))
+ (progn
+ (goto-char (org-find-visible))
+ (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))
+ (type (org-element-type element))
+ (cbeg (org-element-property :contents-begin element)))
+ (goto-char (org-element-property :end element))
+ ;; Fill ELEMENT contents by side-effect.
+ (cond
+ ;; If element has no contents, don't modify it.
+ ((not cbeg))
+ ;; Greater element: parse it between `contents-begin' and
+ ;; `contents-end'. Ensure GRANULARITY allows recursion,
+ ;; or ELEMENT is a headline, in which case going inside
+ ;; is mandatory, in order to get sub-level headings.
+ ((and (memq type org-element-greater-elements)
+ (or (memq granularity '(element object nil))
+ (and (eq granularity 'greater-element)
+ (eq type 'section))
+ (eq type 'headline)))
+ (org-element--parse-elements
+ cbeg (org-element-property :contents-end element)
+ ;; Possibly switch to a special mode.
+ (org-element--next-mode mode type t)
+ (and (memq type '(item plain-list))
+ (org-element-property :structure element))
+ granularity visible-only element))
+ ;; ELEMENT has contents. Parse objects inside, if
+ ;; GRANULARITY allows it.
+ ((memq granularity '(object nil))
+ (org-element--parse-objects
+ cbeg (org-element-property :contents-end element) element
+ (org-element-restriction type))))
+ (push (org-element-put-property element :parent acc) elements)
+ ;; Update mode.
+ (setq mode (org-element--next-mode mode type nil)))))
;; Return result.
(apply #'org-element-set-contents acc (nreverse elements)))))
@@ -4498,15 +4494,21 @@ to an appropriate container (e.g., a paragraph)."
(and (memq 'latex-fragment restriction)
(org-element-latex-fragment-parser)))))
(?\[
- (if (eq (aref result 1) ?\[)
- (and (memq 'link restriction)
- (org-element-link-parser))
- (or (and (memq 'footnote-reference restriction)
- (org-element-footnote-reference-parser))
- (and (memq 'timestamp restriction)
- (org-element-timestamp-parser))
- (and (memq 'statistics-cookie restriction)
- (org-element-statistics-cookie-parser)))))
+ (pcase (aref result 1)
+ ((and ?\[
+ (guard (memq 'link restriction)))
+ (org-element-link-parser))
+ ((and ?f
+ (guard (memq 'footnote-reference restriction)))
+ (org-element-footnote-reference-parser))
+ ((and (or ?% ?/)
+ (guard (memq 'statistics-cookie restriction)))
+ (org-element-statistics-cookie-parser))
+ (_
+ (or (and (memq 'timestamp restriction)
+ (org-element-timestamp-parser))
+ (and (memq 'statistics-cookie restriction)
+ (org-element-statistics-cookie-parser))))))
;; This is probably a plain link.
(_ (and (memq 'link restriction)
(org-element-link-parser)))))))
@@ -4821,10 +4823,12 @@ indentation removed from its contents."
;;
;; A single public function is provided: `org-element-cache-reset'.
;;
-;; Cache is enabled by default, but can be disabled globally with
+;; Cache is disabled by default for now because it sometimes triggers
+;; freezes, but it can be enabled globally with
;; `org-element-use-cache'. `org-element-cache-sync-idle-time',
-;; org-element-cache-sync-duration' and `org-element-cache-sync-break'
-;; can be tweaked to control caching behavior.
+;; `org-element-cache-sync-duration' and
+;; `org-element-cache-sync-break' can be tweaked to control caching
+;; behavior.
;;
;; Internally, parsed elements are stored in an AVL tree,
;; `org-element--cache'. This tree is updated lazily: whenever
@@ -4892,7 +4896,7 @@ with `org-element--cache-compare'. This cache is used in
A request is a vector with the following pattern:
- \[NEXT BEG END OFFSET PARENT PHASE]
+ [NEXT BEG END OFFSET PARENT PHASE]
Processing a synchronization request consists of three phases:
@@ -5450,9 +5454,11 @@ the process stopped before finding the expected result."
;; element following headline above, or first element in
;; buffer.
((not cached)
- (when (org-with-limited-levels (outline-previous-heading))
- (setq mode 'planning)
- (forward-line))
+ (if (org-with-limited-levels (outline-previous-heading))
+ (progn
+ (setq mode 'planning)
+ (forward-line))
+ (setq mode 'top-comment))
(skip-chars-forward " \r\t\n")
(beginning-of-line))
;; Cache returned exact match: return it.
@@ -5521,7 +5527,7 @@ the process stopped before finding the expected result."
;; after it.
((and (<= elem-end pos) (/= (point-max) elem-end))
(goto-char elem-end)
- (setq mode (org-element--next-mode type nil)))
+ (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 element))
@@ -5549,7 +5555,7 @@ the process stopped before finding the expected result."
(and (= cend pos) (= (point-max) pos)))))
(goto-char (or next cbeg))
(setq next nil
- mode (org-element--next-mode type t)
+ mode (org-element--next-mode mode type t)
parent element
end cend))))
;; Otherwise, return ELEMENT as it is the smallest
@@ -5813,7 +5819,7 @@ element.
Possible types are defined in `org-element-all-elements'.
Properties depend on element or object type, but always include
-`:begin', `:end', `:parent' and `:post-blank' properties.
+`:begin', `:end', and `:post-blank' properties.
As a special case, if point is at the very beginning of the first
item in a list or sub-list, returned element will be that list