diff options
Diffstat (limited to 'lisp/org/org-element.el')
-rw-r--r-- | lisp/org/org-element.el | 430 |
1 files changed, 266 insertions, 164 deletions
diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el index 04e2fda55e3..56b3cc4131f 100644 --- a/lisp/org/org-element.el +++ b/lisp/org/org-element.el @@ -58,10 +58,55 @@ ;;; Code: -(require 'org) (require 'avl-tree) (require 'cl-lib) - +(require 'ol) +(require 'org) +(require 'org-compat) +(require 'org-entities) +(require 'org-footnote) +(require 'org-list) +(require 'org-macs) +(require 'org-table) + +(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)) +(declare-function org-unescape-code-in-string "org-src" (s)) +(declare-function outline-next-heading "outline" ()) +(declare-function outline-previous-heading "outline" ()) + +(defvar org-archive-tag) +(defvar org-clock-line-re) +(defvar org-closed-string) +(defvar org-comment-string) +(defvar org-complex-heading-regexp) +(defvar org-dblock-start-re) +(defvar org-deadline-string) +(defvar org-done-keywords) +(defvar org-drawer-regexp) +(defvar org-edit-src-content-indentation) +(defvar org-emph-re) +(defvar org-emphasis-regexp-components) +(defvar org-keyword-time-not-clock-regexp) +(defvar org-match-substring-regexp) +(defvar org-odd-levels-only) +(defvar org-outline-regexp-bol) +(defvar org-planning-line-re) +(defvar org-property-drawer-re) +(defvar org-property-format) +(defvar org-property-re) +(defvar org-scheduled-string) +(defvar org-src-preserve-indentation) +(defvar org-tags-column) +(defvar org-time-stamp-formats) +(defvar org-todo-regexp) +(defvar org-ts-regexp-both) +(defvar org-verbatim-re) ;;; Definitions And Rules @@ -91,7 +136,7 @@ specially in `org-element--object-lex'.") (setq org-element-paragraph-separate (concat "^\\(?:" ;; Headlines, inlinetasks. - org-outline-regexp "\\|" + "\\*+ " "\\|" ;; Footnote definitions. "\\[fn:[-_[:word:]]+\\]" "\\|" ;; Diary sexps. @@ -117,7 +162,7 @@ specially in `org-element--object-lex'.") ;; LaTeX environments. "\\\\begin{\\([A-Za-z0-9*]+\\)}" "\\|" ;; Clock lines. - (regexp-quote org-clock-string) "\\|" + "CLOCK:" "\\|" ;; Lists. (let ((term (pcase org-plain-list-ordered-item-terminator (?\) ")") (?. "\\.") (_ "[.)]"))) @@ -307,8 +352,9 @@ Don't modify it, set `org-element-affiliated-keywords' instead.") (strike-through ,@standard-set) (subscript ,@standard-set) (superscript ,@standard-set) - ;; Ignore inline babel call and inline src block as formulas are - ;; possible. Also ignore line breaks and statistics cookies. + ;; 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) @@ -491,6 +537,7 @@ objects, or a strings. The function takes care of setting `:parent' property for CHILD. Return parent element." + (declare (indent 1)) (if (not children) parent ;; Link every child to PARENT. If PARENT is nil, it is a secondary ;; string: parent is the list itself. @@ -677,7 +724,7 @@ Assume point is at the beginning of the block." (defun org-element-center-block-interpreter (_ contents) "Interpret a center-block element as Org syntax. CONTENTS is the contents of the element." - (format "#+BEGIN_CENTER\n%s#+END_CENTER" contents)) + (format "#+begin_center\n%s#+end_center" contents)) ;;;; Drawer @@ -787,7 +834,7 @@ Assume point is at beginning of dynamic block." (defun org-element-dynamic-block-interpreter (dynamic-block contents) "Interpret DYNAMIC-BLOCK element as Org syntax. CONTENTS is the contents of the element." - (format "#+BEGIN: %s%s\n%s#+END:" + (format "#+begin: %s%s\n%s#+end:" (org-element-property :block-name dynamic-block) (let ((args (org-element-property :arguments dynamic-block))) (if args (concat " " args) "")) @@ -812,7 +859,8 @@ their value. Return a list whose CAR is `footnote-definition' and CDR is a plist containing `:label', `:begin' `:end', `:contents-begin', -`:contents-end', `:post-blank' and `:post-affiliated' keywords. +`:contents-end', `:pre-blank',`:post-blank' and +`:post-affiliated' keywords. Assume point is at the beginning of the footnote definition." (save-excursion @@ -838,12 +886,16 @@ Assume point is at the beginning of the footnote definition." ((eq ?* (char-after (match-beginning 0))) (match-beginning 0)) (t (skip-chars-forward " \r\t\n" limit) (if (= limit (point)) limit (line-beginning-position)))))) + (pre-blank 0) (contents-begin (progn (search-forward "]") (skip-chars-forward " \r\t\n" end) (cond ((= (point) end) nil) ((= (line-beginning-position) post-affiliated) (point)) - (t (line-beginning-position))))) + (t + (setq pre-blank + (count-lines (line-beginning-position) begin)) + (line-beginning-position))))) (contents-end (progn (goto-char end) (skip-chars-backward " \r\t\n") @@ -855,6 +907,7 @@ Assume point is at the beginning of the footnote definition." :end end :contents-begin contents-begin :contents-end (and contents-begin contents-end) + :pre-blank pre-blank :post-blank (count-lines contents-end end) :post-affiliated post-affiliated) (cdr affiliated)))))) @@ -862,9 +915,18 @@ Assume point is at the beginning of the footnote definition." (defun org-element-footnote-definition-interpreter (footnote-definition contents) "Interpret FOOTNOTE-DEFINITION element as Org syntax. CONTENTS is the contents of the footnote-definition." - (concat (format "[fn:%s]" (org-element-property :label footnote-definition)) - " " - contents)) + (let ((pre-blank + (min (or (org-element-property :pre-blank footnote-definition) + ;; 0 is specific to paragraphs at the beginning of + ;; the footnote definition, so we use 1 as + ;; a fall-back value, which is more universal. + 1) + ;; Footnote ends after more than two consecutive empty + ;; lines: limit ourselves to 2 newline characters. + 2))) + (concat (format "[fn:%s]" (org-element-property :label footnote-definition)) + (if (= pre-blank 0) (concat " " (org-trim contents)) + (concat (make-string pre-blank ?\n) contents))))) ;;;; Headline @@ -911,7 +973,7 @@ Return value is a plist." Return a list whose CAR is `headline' and CDR is a plist containing `:raw-value', `:title', `:begin', `:end', `:pre-blank', `:contents-begin' and `:contents-end', `:level', -`:priority', `:tags', `:todo-keyword',`:todo-type', `:scheduled', +`:priority', `:tags', `:todo-keyword', `:todo-type', `:scheduled', `:deadline', `:closed', `:archivedp', `:commentedp' `:footnote-section-p', `:post-blank' and `:post-affiliated' keywords. @@ -931,10 +993,10 @@ Assume point is at beginning of the headline." (level (prog1 (org-reduced-level (skip-chars-forward "*")) (skip-chars-forward " \t"))) (todo (and org-todo-regexp - (let (case-fold-search) (looking-at org-todo-regexp)) + (let (case-fold-search) (looking-at (concat org-todo-regexp " "))) (progn (goto-char (match-end 0)) (skip-chars-forward " \t") - (match-string 0)))) + (match-string 1)))) (todo-type (and todo (if (member todo org-done-keywords) 'done 'todo))) (priority (and (looking-at "\\[#.\\][ \t]*") @@ -1172,18 +1234,18 @@ CONTENTS is the contents of inlinetask." (concat (make-string (max (- (+ org-tags-column (length task) (length tags))) 1) - ? ) + ?\s) tags)) (t (concat - (make-string (max (- org-tags-column (length task)) 1) ? ) + (make-string (max (- org-tags-column (length task)) 1) ?\s) tags)))) ;; Prefer degenerate inlinetasks when there are no ;; contents. (when contents (concat "\n" contents - (make-string level ?*) " END"))))) + (make-string level ?*) " end"))))) ;;;; Item @@ -1195,8 +1257,8 @@ STRUCT is the structure of the plain list. Return a list whose CAR is `item' and CDR is a plist containing `:bullet', `:begin', `:end', `:contents-begin', `:contents-end', -`:checkbox', `:counter', `:tag', `:structure', `:post-blank' and -`:post-affiliated' keywords. +`:checkbox', `:counter', `:tag', `:structure', `:pre-blank', +`:post-blank' and `:post-affiliated' keywords. When optional argument RAW-SECONDARY-P is non-nil, item's tag, if any, will not be parsed as a secondary string, but as a plain @@ -1223,20 +1285,25 @@ Assume point is at the beginning of the item." (string-to-number (match-string 0 c))))))) (end (progn (goto-char (nth 6 (assq (point) struct))) (if (bolp) (point) (line-beginning-position 2)))) + (pre-blank 0) (contents-begin - (progn (goto-char - ;; 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))) - (match-beginning 4) - (match-end 0))) - (skip-chars-forward " \r\t\n" end) - (cond ((= (point) end) nil) - ;; If first line isn't empty, contents really - ;; start at the text after item's meta-data. - ((= (line-beginning-position) begin) (point)) - (t (line-beginning-position))))) + (progn + (goto-char + ;; 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))) + (match-beginning 4) + (match-end 0))) + (skip-chars-forward " \r\t\n" end) + (cond ((= (point) end) nil) + ;; If first line isn't empty, contents really + ;; start at the text after item's meta-data. + ((= (line-beginning-position) begin) (point)) + (t + (setq pre-blank + (count-lines (line-beginning-position) begin)) + (line-beginning-position))))) (contents-end (and contents-begin (progn (goto-char end) (skip-chars-backward " \r\t\n") @@ -1251,6 +1318,7 @@ Assume point is at the beginning of the item." :checkbox checkbox :counter counter :structure struct + :pre-blank pre-blank :post-blank (count-lines (or contents-end begin) end) :post-affiliated begin)))) (org-element-put-property @@ -1266,35 +1334,43 @@ Assume point is at the beginning of the item." (defun org-element-item-interpreter (item contents) "Interpret ITEM element as Org syntax. CONTENTS is the contents of the element." - (let* ((bullet (let ((bullet (org-element-property :bullet item))) - (org-list-bullet-string - (cond ((not (string-match "[0-9a-zA-Z]" bullet)) "- ") - ((eq org-plain-list-ordered-item-terminator ?\)) "1)") - (t "1."))))) - (checkbox (org-element-property :checkbox item)) - (counter (org-element-property :counter item)) - (tag (let ((tag (org-element-property :tag item))) - (and tag (org-element-interpret-data tag)))) - ;; Compute indentation. - (ind (make-string (length bullet) 32)) - (item-starts-with-par-p - (eq (org-element-type (car (org-element-contents item))) - 'paragraph))) - ;; Indent contents. + (let ((tag (pcase (org-element-property :tag item) + (`nil nil) + (tag (format "%s :: " (org-element-interpret-data tag))))) + (bullet + (org-list-bullet-string + (cond + ((not (string-match-p "[0-9a-zA-Z]" + (org-element-property :bullet item))) "- ") + ((eq org-plain-list-ordered-item-terminator ?\)) "1)") + (t "1."))))) (concat bullet - (and counter (format "[@%d] " counter)) - (pcase checkbox + (pcase (org-element-property :counter item) + (`nil nil) + (counter (format "[@%d] " counter))) + (pcase (org-element-property :checkbox item) (`on "[X] ") (`off "[ ] ") (`trans "[-] ") (_ nil)) - (and tag (format "%s :: " tag)) + tag (when contents - (let ((contents (replace-regexp-in-string - "\\(^\\)[ \t]*\\S-" ind contents nil nil 1))) - (if item-starts-with-par-p (org-trim contents) - (concat "\n" contents))))))) + (let* ((ind (make-string (if tag 5 (length bullet)) ?\s)) + (pre-blank + (min (or (org-element-property :pre-blank item) + ;; 0 is specific to paragraphs at the + ;; beginning of the item, so we use 1 as + ;; a fall-back value, which is more universal. + 1) + ;; Lists ends after more than two consecutive + ;; empty lines: limit ourselves to 2 newline + ;; characters. + 2)) + (contents (replace-regexp-in-string + "\\(^\\)[ \t]*\\S-" ind contents nil nil 1))) + (if (= pre-blank 0) (org-trim contents) + (concat (make-string pre-blank ?\n) contents))))))) ;;;; Plain List @@ -1516,7 +1592,7 @@ Assume point is at the beginning of the block." (defun org-element-quote-block-interpreter (_ contents) "Interpret quote-block element as Org syntax. CONTENTS is the contents of the element." - (format "#+BEGIN_QUOTE\n%s#+END_QUOTE" contents)) + (format "#+begin_quote\n%s#+end_quote" contents)) ;;;; Section @@ -1602,7 +1678,7 @@ Assume point is at the beginning of the block." "Interpret SPECIAL-BLOCK element as Org syntax. CONTENTS is the contents of the element." (let ((block-type (org-element-property :type special-block))) - (format "#+BEGIN_%s\n%s#+END_%s" block-type contents block-type))) + (format "#+begin_%s\n%s#+end_%s" block-type contents block-type))) @@ -1670,7 +1746,7 @@ containing `:call', `:inside-header', `:arguments', (defun org-element-babel-call-interpreter (babel-call _) "Interpret BABEL-CALL element as Org syntax." - (concat "#+CALL: " + (concat "#+call: " (org-element-property :call babel-call) (let ((h (org-element-property :inside-header babel-call))) (and h (format "[%s]" h))) @@ -1692,7 +1768,7 @@ Return a list whose CAR is `clock' and CDR is a plist containing (save-excursion (let* ((case-fold-search nil) (begin (point)) - (value (progn (search-forward org-clock-string (line-end-position) t) + (value (progn (search-forward "CLOCK:" (line-end-position) t) (skip-chars-forward " \t") (org-element-timestamp-parser))) (duration (and (search-forward " => " (line-end-position) t) @@ -1717,7 +1793,7 @@ Return a list whose CAR is `clock' and CDR is a plist containing (defun org-element-clock-interpreter (clock _) "Interpret CLOCK element as Org syntax." - (concat org-clock-string " " + (concat "CLOCK: " (org-element-timestamp-interpreter (org-element-property :value clock) nil) (let ((duration (org-element-property :duration clock))) @@ -1824,7 +1900,7 @@ Assume point is at comment block beginning." (defun org-element-comment-block-interpreter (comment-block _) "Interpret COMMENT-BLOCK element as Org syntax." - (format "#+BEGIN_COMMENT\n%s#+END_COMMENT" + (format "#+begin_comment\n%s#+end_comment" (org-element-normalize-string (org-remove-indentation (org-element-property :value comment-block))))) @@ -1951,15 +2027,22 @@ containing `:begin', `:end', `:number-lines', `:preserve-indent', (defun org-element-example-block-interpreter (example-block _) "Interpret EXAMPLE-BLOCK element as Org syntax." (let ((switches (org-element-property :switches example-block)) - (value (org-element-property :value example-block))) - (concat "#+BEGIN_EXAMPLE" (and switches (concat " " switches)) "\n" - (org-element-normalize-string - (org-escape-code-in-string - (if (or org-src-preserve-indentation - (org-element-property :preserve-indent example-block)) - value - (org-remove-indentation value)))) - "#+END_EXAMPLE"))) + (value + (let ((val (org-element-property :value example-block))) + (cond + ((or org-src-preserve-indentation + (org-element-property :preserve-indent example-block)) + val) + ((= 0 org-edit-src-content-indentation) + (org-remove-indentation val)) + (t + (let ((ind (make-string org-edit-src-content-indentation ?\s))) + (replace-regexp-in-string "^[ \t]*\\S-" + (concat ind "\\&") + (org-remove-indentation val)))))))) + (concat "#+begin_example" (and switches (concat " " switches)) "\n" + (org-element-normalize-string (org-escape-code-in-string value)) + "#+end_example"))) ;;;; Export Block @@ -2012,7 +2095,7 @@ Assume point is at export-block beginning." (defun org-element-export-block-interpreter (export-block _) "Interpret EXPORT-BLOCK element as Org syntax." - (format "#+BEGIN_EXPORT %s\n%s#+END_EXPORT" + (format "#+begin_export %s\n%s#+end_export" (org-element-property :type export-block) (org-element-property :value export-block))) @@ -2035,26 +2118,22 @@ Assume point is at the beginning of the fixed-width area." (save-excursion (let* ((begin (car affiliated)) (post-affiliated (point)) - value (end-area (progn (while (and (< (point) limit) (looking-at "[ \t]*:\\( \\|$\\)")) - ;; Accumulate text without starting colons. - (setq value - (concat value - (buffer-substring-no-properties - (match-end 0) (point-at-eol)) - "\n")) (forward-line)) - (point))) + (if (bolp) (line-end-position 0) (point)))) (end (progn (skip-chars-forward " \r\t\n" limit) (if (eobp) (point) (line-beginning-position))))) (list 'fixed-width (nconc (list :begin begin :end end - :value value + :value (replace-regexp-in-string + "^[ \t]*: ?" "" + (buffer-substring-no-properties post-affiliated + end-area)) :post-blank (count-lines end-area end) :post-affiliated post-affiliated) (cdr affiliated)))))) @@ -2062,10 +2141,7 @@ Assume point is at the beginning of the fixed-width area." (defun org-element-fixed-width-interpreter (fixed-width _) "Interpret FIXED-WIDTH element as Org syntax." (let ((value (org-element-property :value fixed-width))) - (and value - (replace-regexp-in-string - "^" ": " - (if (string-match "\n\\'" value) (substring value 0 -1) value))))) + (and value (replace-regexp-in-string "^" ": " value)))) ;;;; Horizontal Rule @@ -2139,7 +2215,7 @@ containing `:key', `:value', `:begin', `:end', `:post-blank' and (defun org-element-keyword-interpreter (keyword _) "Interpret KEYWORD element as Org syntax." (format "#+%s: %s" - (org-element-property :key keyword) + (downcase (org-element-property :key keyword)) (org-element-property :value keyword))) @@ -2369,7 +2445,7 @@ containing `:closed', `:deadline', `:scheduled', `:begin', ;;;; Src Block (defun org-element-src-block-parser (limit affiliated) - "Parse a src block. + "Parse a source block. LIMIT bounds the search. AFFILIATED is a list of which CAR is the buffer position at the beginning of the first affiliated @@ -2425,7 +2501,7 @@ Assume point is at the beginning of the block." (string-match "-l +\"\\([^\"\n]+\\)\"" switches) (match-string 1 switches))) ;; Should labels be retained in (or stripped from) - ;; src blocks? + ;; source blocks? (retain-labels (or (not switches) (not (string-match "-r\\>" switches)) @@ -2480,14 +2556,14 @@ Assume point is at the beginning of the block." (org-remove-indentation val)) (t (let ((ind (make-string org-edit-src-content-indentation ?\s))) - (replace-regexp-in-string - "^" ind (org-remove-indentation val)))))))) - (concat (format "#+BEGIN_SRC%s\n" - (concat (and lang (concat " " lang)) - (and switches (concat " " switches)) - (and params (concat " " params)))) - (org-element-normalize-string (org-escape-code-in-string value)) - "#+END_SRC"))) + (replace-regexp-in-string "^[ \t]*\\S-" + (concat ind "\\&") + (org-remove-indentation val)))))))) + (format "#+begin_src%s\n%s#+end_src" + (concat (and lang (concat " " lang)) + (and switches (concat " " switches)) + (and params (concat " " params))) + (org-element-normalize-string (org-escape-code-in-string value))))) ;;;; Table @@ -2635,7 +2711,7 @@ Assume point is at beginning of the block." (defun org-element-verse-block-interpreter (_ contents) "Interpret verse-block element as Org syntax. CONTENTS is verse block contents." - (format "#+BEGIN_VERSE\n%s#+END_VERSE" contents)) + (format "#+begin_verse\n%s#+end_verse" contents)) @@ -2803,7 +2879,7 @@ Assume point is at the beginning of the snippet." When at a footnote reference, return a list whose car is `footnote-reference' and cdr a plist with `:label', `:type', -`:begin', `:end', `:content-begin', `:contents-end' and +`:begin', `:end', `:contents-begin', `:contents-end' and `:post-blank' as keywords. Otherwise, return nil." (when (looking-at org-footnote-re) (let ((closing (with-syntax-table org-element--pair-square-table @@ -2899,7 +2975,7 @@ When at an inline source block, return a list whose car is `:language', `:value', `:parameters' and `:post-blank' as keywords. Otherwise, return nil. -Assume point is at the beginning of the inline src block." +Assume point is at the beginning of the inline source block." (save-excursion (catch :no-object (when (let ((case-fold-search nil)) @@ -3066,13 +3142,13 @@ Assume point is at the beginning of the link." (setq contents-begin (match-beginning 1)) (setq contents-end (match-end 1))) ;; Type 2: Standard link, i.e. [[https://orgmode.org][homepage]] - ((looking-at org-bracket-link-regexp) + ((looking-at org-link-bracket-re) (setq format 'bracket) - (setq contents-begin (match-beginning 3)) - (setq contents-end (match-end 3)) + (setq contents-begin (match-beginning 2)) + (setq contents-end (match-end 2)) (setq link-end (match-end 0)) - ;; RAW-LINK is the original link. Expand any - ;; abbreviation in it. + ;; RAW-LINK is the original link. Decode any encoding. + ;; Expand any abbreviation in it. ;; ;; Also treat any newline character and associated ;; indentation as a single space character. This is not @@ -3083,9 +3159,10 @@ Assume point is at the beginning of the link." ;; [[shell:ls *.org]], which defeats Org's focus on ;; simplicity. (setq raw-link (org-link-expand-abbrev - (replace-regexp-in-string - "[ \t]*\n[ \t]*" " " - (match-string-no-properties 1)))) + (org-link-unescape + (replace-regexp-in-string + "[ \t]*\n[ \t]*" " " + (match-string-no-properties 1))))) ;; Determine TYPE of link and set PATH accordingly. According ;; to RFC 3986, remove whitespaces from URI in external links. ;; In internal ones, treat indentation as a single space. @@ -3115,7 +3192,7 @@ Assume point is at the beginning of the link." (setq type "fuzzy") (setq path raw-link)))) ;; Type 3: Plain link, e.g., https://orgmode.org - ((looking-at org-plain-link-re) + ((looking-at org-link-plain-re) (setq format 'plain) (setq raw-link (match-string-no-properties 0)) (setq type (match-string-no-properties 1)) @@ -3124,7 +3201,7 @@ Assume point is at the beginning of the link." ;; Type 4: Angular link, e.g., <https://orgmode.org>. Unlike to ;; bracket links, follow RFC 3986 and remove any extra ;; whitespace in URI. - ((looking-at org-angle-link-re) + ((looking-at org-link-angle-re) (setq format 'angle) (setq type (match-string-no-properties 1)) (setq link-end (match-end 0)) @@ -3218,15 +3295,18 @@ a plist with `:key', `:args', `:begin', `:end', `:value' and Assume point is at the macro." (save-excursion - (when (looking-at "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\(([ \t\n]*\\([^\000]*?\\))\\)?}}}") + (when (looking-at "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\((\\([^\000]*?\\))\\)?}}}") (let ((begin (point)) (key (downcase (match-string-no-properties 1))) (value (match-string-no-properties 0)) (post-blank (progn (goto-char (match-end 0)) (skip-chars-forward " \t"))) (end (point)) - (args (let ((args (match-string-no-properties 3))) - (and args (org-macro-extract-arguments args))))) + (args (pcase (match-string-no-properties 3) + (`nil nil) + (a (org-macro-extract-arguments + (replace-regexp-in-string + "[ \t\r\n]+" " " (org-trim a))))))) (list 'macro (list :key key :value value @@ -3237,7 +3317,11 @@ Assume point is at the macro." (defun org-element-macro-interpreter (macro _) "Interpret MACRO object as Org syntax." - (org-element-property :value macro)) + (format "{{{%s%s}}}" + (org-element-property :key macro) + (pcase (org-element-property :args macro) + (`nil "") + (args (format "(%s)" (apply #'org-macro-escape-arguments args)))))) ;;;; Radio-target @@ -3815,7 +3899,8 @@ element it has to parse." ((org-at-heading-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))) + (t (let ((affiliated (org-element--collect-affiliated-keywords + limit (memq granularity '(nil object))))) (cond ;; Jumping over affiliated keywords put point off-limits. ;; Parse them as regular keywords. @@ -3874,7 +3959,18 @@ element it has to parse." ((looking-at "%%(") (org-element-diary-sexp-parser limit affiliated)) ;; Table. - ((looking-at "[ \t]*\\(|\\|\\+\\(-+\\+\\)+[ \t]*$\\)") + ((or (looking-at "[ \t]*|") + ;; There is no strict definition of a table.el + ;; table. Try to prevent false positive while being + ;; quick. + (let ((rule-regexp "[ \t]*\\+\\(-+\\+\\)+[ \t]*$") + (next (line-beginning-position 2))) + (and (looking-at rule-regexp) + (save-excursion + (forward-line) + (re-search-forward "^[ \t]*\\($\\|[^|]\\)" limit t) + (and (> (line-beginning-position) next) + (org-match-line rule-regexp)))))) (org-element-table-parser limit affiliated)) ;; List. ((looking-at (org-item-re)) @@ -3890,7 +3986,7 @@ element it has to parse." ;; that element, and, in the meantime, collect information they give ;; into appropriate properties. Hence the following function. -(defun org-element--collect-affiliated-keywords (limit) +(defun org-element--collect-affiliated-keywords (limit parse) "Collect affiliated keywords from point down to LIMIT. Return a list whose CAR is the position at the first of them and @@ -3899,13 +3995,16 @@ beginning of the first line after them. As a special case, if element doesn't start at the beginning of the line (e.g., a paragraph starting an item), CAR is current -position of point and CDR is nil." +position of point and CDR is nil. + +When PARSE is non-nil, values from keywords belonging to +`org-element-parsed-keywords' are parsed as secondary strings." (if (not (bolp)) (list (point)) (let ((case-fold-search t) (origin (point)) ;; RESTRICT is the list of objects allowed in parsed - ;; keywords value. - (restrict (org-element-restriction 'keyword)) + ;; keywords value. If PARSE is nil, no object is allowed. + (restrict (and parse (org-element-restriction 'keyword))) output) (while (and (< (point) limit) (looking-at org-element--affiliated-re)) (let* ((raw-kwd (upcase (match-string 1))) @@ -3914,35 +4013,35 @@ position of point and CDR is nil." (kwd (or (cdr (assoc raw-kwd org-element-keyword-translation-alist)) raw-kwd)) + ;; PARSED? is non-nil when keyword should have its + ;; value parsed. + (parsed? (member kwd org-element-parsed-keywords)) ;; Find main value for any keyword. (value - (save-match-data - (org-trim - (buffer-substring-no-properties - (match-end 0) (line-end-position))))) - ;; PARSEDP is non-nil when keyword should have its - ;; value parsed. - (parsedp (member kwd org-element-parsed-keywords)) - ;; If KWD is a dual keyword, find its secondary - ;; value. Maybe parse it. - (dualp (member kwd org-element-dual-keywords)) + (let ((beg (match-end 0)) + (end (save-excursion + (end-of-line) + (skip-chars-backward " \t") + (point)))) + (if parsed? + (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. + (dual? (member kwd org-element-dual-keywords)) (dual-value - (and dualp + (and dual? (let ((sec (match-string-no-properties 2))) - (if (or (not sec) (not parsedp)) sec + (cond + ((and sec parsed?) (save-match-data (org-element--parse-objects - (match-beginning 2) (match-end 2) nil restrict)))))) + (match-beginning 2) (match-end 2) nil restrict))) + (sec sec))))) ;; Attribute a property name to KWD. (kwd-sym (and kwd (intern (concat ":" (downcase kwd)))))) ;; Now set final shape for VALUE. - (when parsedp - (setq value - (org-element--parse-objects - (match-end 0) - (progn (end-of-line) (skip-chars-backward " \t") (point)) - nil restrict))) - (when dualp + (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. @@ -4046,7 +4145,10 @@ If STRING is the empty string or nil, return nil." (ignore-errors (if (symbolp v) (makunbound v) (set (make-local-variable (car v)) (cdr v))))) - (insert string) + ;; 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)) + ;; Prevent "Buffer *temp* modified; kill anyway?". (restore-buffer-modified-p nil) (org-element--parse-objects (point-min) (point-max) nil restriction parent)))))) @@ -4532,8 +4634,9 @@ to interpret. Return Org syntax as a string." (and (eq type 'paragraph) (memq (org-element-type parent) '(footnote-definition item)) - (eq data - (car (org-element-contents parent))))))) + (eq data (car (org-element-contents parent))) + (eq (org-element-property :pre-blank parent) + 0))))) "")))))) (if (memq type '(org-data plain-text nil)) results ;; Build white spaces. If no `:post-blank' property @@ -4555,7 +4658,7 @@ If there is no affiliated keyword, return the empty string." (let (dual) (when (member key org-element-dual-keywords) (setq dual (cdr value) value (car value))) - (concat "#+" key + (concat "#+" (downcase key) (and dual (format "[%s]" (org-element-interpret-data dual))) ": " @@ -4950,7 +5053,6 @@ A and B are either integers or lists of integers, as returned by (defsubst org-element--cache-root () "Return root value in cache. This function assumes `org-element--cache' is a valid AVL tree." - ;; FIXME: Why use internal functions of avl-tree? (avl-tree--node-left (avl-tree--dummyroot org-element--cache))) @@ -4979,7 +5081,6 @@ the cache." (aref (car org-element--cache-sync-requests) 0))) (node (org-element--cache-root)) lower upper) - ;; FIXME: Why use internal functions of avl-tree? (while node (let* ((element (avl-tree--node-data node)) (begin (org-element-property :begin element))) @@ -5055,7 +5156,7 @@ Assume ELEMENT belongs to cache and that a cache is active." (setq org-element--cache-sync-timer (run-with-idle-timer (let ((idle (current-idle-time))) - (if idle (time-add idle org-element-cache-sync-break) + (if idle (org-time-add idle org-element-cache-sync-break) org-element-cache-sync-idle-time)) nil #'org-element--cache-sync @@ -5066,7 +5167,7 @@ Assume ELEMENT belongs to cache and that a cache is active." TIME-LIMIT is a time value or nil." (and time-limit (or (input-pending-p) - (time-less-p time-limit nil)))) + (org-time-less-p time-limit nil)))) (defsubst org-element--cache-shift-positions (element offset &optional props) "Shift ELEMENT properties relative to buffer positions by OFFSET. @@ -5120,7 +5221,8 @@ updated before current modification are actually submitted." (and next (aref next 0)) threshold (and (not threshold) - (time-add nil org-element-cache-sync-duration)) + (org-time-add nil + org-element-cache-sync-duration)) future-change) ;; Request processed. Merge current and next offsets and ;; transfer ending position. @@ -5460,7 +5562,7 @@ the process stopped before finding the expected result." (defconst org-element--cache-sensitive-re (concat - org-outline-regexp-bol "\\|" + "^\\*+ " "\\|" "\\\\end{[A-Za-z0-9*]+}[ \t]*$" "\\|" "^[ \t]*\\(?:" "#\\+\\(?:BEGIN[:_]\\|END\\(?:_\\|:?[ \t]*$\\)\\)" "\\|" @@ -5869,24 +5971,24 @@ Providing it allows for quicker computation." ;; Otherwise, return NEXT. (t (throw 'exit next))))))))))))) -(defun org-element-lineage (blob &optional types with-self) +(defun org-element-lineage (datum &optional types with-self) "List all ancestors of a given element or object. -BLOB is an object or element. +DATUM is an object or element. -When optional argument TYPES is a list of symbols, return the -first element or object in the lineage whose type belongs to that -list. +Return ancestors from the closest to the farthest. When optional +argument TYPES is a list of symbols, return the first element or +object in the lineage whose type belongs to that list instead. When optional argument WITH-SELF is non-nil, lineage includes -BLOB itself as the first element, and TYPES, if provided, also +DATUM itself as the first element, and TYPES, if provided, also apply to it. -When BLOB is obtained through `org-element-context' or +When DATUM is obtained through `org-element-context' or `org-element-at-point', only ancestors from its section can be -found. There is no such limitation when BLOB belongs to a full +found. There is no such limitation when DATUM belongs to a full parse tree." - (let ((up (if with-self blob (org-element-property :parent blob))) + (let ((up (if with-self datum (org-element-property :parent datum))) ancestors) (while (and up (not (memq (org-element-type up) types))) (unless types (push up ancestors)) @@ -5914,16 +6016,16 @@ end of ELEM-A." ;; ELEM-A position in such a situation. Note that the case of ;; a footnote definition is impossible: it cannot contain two ;; paragraphs in a row because it cannot contain a blank line. - (if (and specialp - (or (not (eq (org-element-type elem-B) 'paragraph)) - (/= (org-element-property :begin elem-B) - (org-element-property :contents-begin elem-B)))) - (error "Cannot swap elements")) + (when (and specialp + (or (not (eq (org-element-type elem-B) 'paragraph)) + (/= (org-element-property :begin elem-B) + (org-element-property :contents-begin elem-B)))) + (error "Cannot swap elements")) ;; In a special situation, ELEM-A will have no indentation. We'll ;; give it ELEM-B's (which will in, in turn, have no indentation). (let* ((ind-B (when specialp (goto-char (org-element-property :begin elem-B)) - (org-get-indentation))) + (current-indentation))) (beg-A (org-element-property :begin elem-A)) (end-A (save-excursion (goto-char (org-element-property :end elem-A)) |