diff options
Diffstat (limited to 'lisp/org-element.el')
-rw-r--r-- | lisp/org-element.el | 1669 |
1 files changed, 870 insertions, 799 deletions
diff --git a/lisp/org-element.el b/lisp/org-element.el index 2576c3f..027eea4 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -1,4 +1,4 @@ -;;; org-element.el --- Parser And Applications for Org syntax +;;; org-element.el --- Parser for Org Syntax -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2016 Free Software Foundation, Inc. @@ -116,9 +116,9 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'org) (require 'avl-tree) +(require 'cl-lib) @@ -151,7 +151,7 @@ specially in `org-element--object-lex'.") ;; Headlines, inlinetasks. org-outline-regexp "\\|" ;; Footnote definitions. - "\\[\\(?:[0-9]+\\|fn:[-_[:word:]]+\\)\\]" "\\|" + "\\[fn:[-_[:word:]]+\\]" "\\|" ;; Diary sexps. "%%(" "\\|" "[ \t]*\\(?:" @@ -177,15 +177,15 @@ specially in `org-element--object-lex'.") ;; Clock lines. (regexp-quote org-clock-string) "\\|" ;; Lists. - (let ((term (case org-plain-list-ordered-item-terminator - (?\) ")") (?. "\\.") (otherwise "[.)]"))) + (let ((term (pcase org-plain-list-ordered-item-terminator + (?\) ")") (?. "\\.") (_ "[.)]"))) (alpha (and org-list-allow-alphabetical "\\|[A-Za-z]"))) (concat "\\(?:[-+*]\\|\\(?:[0-9]+" alpha "\\)" term "\\)" "\\(?:[ \t]\\|$\\)")) "\\)\\)") org-element--object-regexp (mapconcat #'identity - (let ((link-types (regexp-opt org-link-types))) + (let ((link-types (regexp-opt (org-link-types)))) (list ;; Sub/superscript. "\\(?:[_^][-{(*+.,[:alnum:]]\\)" @@ -199,7 +199,12 @@ specially in `org-element--object-lex'.") ;; Objects starting with "[": regular link, ;; footnote reference, statistics cookie, ;; timestamp (inactive). - "\\[\\(?:fn:\\|\\(?:[0-9]\\|\\(?:%\\|/[0-9]*\\)\\]\\)\\|\\[\\)" + (concat "\\[\\(?:" + "fn:" "\\|" + "\\[" "\\|" + "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" "\\|" + "[0-9]*\\(?:%\\|/[0-9]*\\)\\]" + "\\)") ;; Objects starting with "@": export snippets. "@@" ;; Objects starting with "{": macro. @@ -258,17 +263,6 @@ specially in `org-element--object-lex'.") (append org-element-recursive-objects '(paragraph table-row verse-block)) "List of object or element types that can directly contain objects.") -(defvar org-element-block-name-alist - '(("CENTER" . org-element-center-block-parser) - ("COMMENT" . org-element-comment-block-parser) - ("EXAMPLE" . org-element-example-block-parser) - ("QUOTE" . org-element-quote-block-parser) - ("SRC" . org-element-src-block-parser) - ("VERSE" . org-element-verse-block-parser)) - "Alist between block names and the associated parsing function. -Names must be uppercase. Any block whose name has no association -is parsed with `org-element-special-block-parser'.") - (defconst org-element-affiliated-keywords '("CAPTION" "DATA" "HEADER" "HEADERS" "LABEL" "NAME" "PLOT" "RESNAME" "RESULT" "RESULTS" "SOURCE" "SRCNAME" "TBLNAME") @@ -334,7 +328,7 @@ This list is checked after translations have been applied. See ;; Regular affiliated keywords. (format "\\(?1:%s\\)" (regexp-opt - (org-remove-if + (cl-remove-if (lambda (k) (member k org-element-dual-keywords)) org-element-affiliated-keywords))) "\\|" @@ -358,10 +352,11 @@ Don't modify it, set `org-element-affiliated-keywords' instead.") (italic ,@standard-set) (item ,@standard-set-no-line-break) (keyword ,@(remq 'footnote-reference standard-set)) - ;; Ignore all links excepted plain links in a link description. - ;; Also ignore radio-targets and line breaks. + ;; Ignore all links excepted plain links and angular 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 plain-link statistics-cookie + italic latex-fragment macro simple-link statistics-cookie strike-through subscript superscript underline verbatim) (paragraph ,@standard-set) ;; Remove any variable object from radio target as it would @@ -399,6 +394,15 @@ still has an entry since one of its properties (`:title') does.") (item :tag)) "Alist between element types and locations of secondary values.") +(defconst org-element--pair-round-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\( "()" table) + (modify-syntax-entry ?\) ")(" table) + (dolist (char '(?\{ ?\} ?\[ ?\] ?\< ?\>) table) + (modify-syntax-entry char " " table))) + "Table used internally to pair only round brackets. +Other brackets are treated as spaces.") + (defconst org-element--pair-square-table (let ((table (make-syntax-table))) (modify-syntax-entry ?\[ "(]" table) @@ -408,6 +412,33 @@ still has an entry since one of its properties (`:title') does.") "Table used internally to pair only square brackets. Other brackets are treated as spaces.") +(defconst org-element--pair-curly-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + (dolist (char '(?\[ ?\] ?\( ?\) ?\< ?\>) table) + (modify-syntax-entry char " " table))) + "Table used internally to pair only curly brackets. +Other brackets are treated as spaces.") + +(defun org-element--parse-paired-brackets (char) + "Parse paired brackets at point. +CHAR is the opening bracket to consider, as a character. Return +contents between brackets, as a string, or nil. Also move point +past the brackets." + (when (eq char (char-after)) + (let ((syntax-table (pcase char + (?\{ org-element--pair-curly-table) + (?\[ org-element--pair-square-table) + (?\( org-element--pair-round-table) + (_ nil))) + (pos (point))) + (when syntax-table + (with-syntax-table syntax-table + (let ((end (ignore-errors (scan-lists pos 1 0)))) + (when end + (goto-char end) + (buffer-substring-no-properties (1+ pos) (1- end))))))))) ;;; Accessors and Setters @@ -424,8 +455,10 @@ Other brackets are treated as spaces.") ;; high-level functions useful to modify a parse tree. ;; ;; `org-element-secondary-p' is a predicate used to know if a given -;; object belongs to a secondary string. `org-element-copy' returns -;; an element or object, stripping its parent property in the process. +;; object belongs to a secondary string. `org-element-class' tells if +;; some parsed data is an element or an object, handling pseudo +;; elements and objects. `org-element-copy' returns an element or +;; object, stripping its parent property in the process. (defsubst org-element-type (element) "Return type of ELEMENT. @@ -465,10 +498,11 @@ Return modified element." element)) (defsubst org-element-set-contents (element &rest contents) - "Set ELEMENT contents to CONTENTS." - (cond ((not element) (list contents)) + "Set ELEMENT's contents to CONTENTS. +Return ELEMENT." + (cond ((null element) contents) ((not (symbolp (car element))) contents) - ((cdr element) (setcdr (cdr element) contents)) + ((cdr element) (setcdr (cdr element) contents) element) (t (nconc element contents)))) (defun org-element-secondary-p (object) @@ -482,6 +516,32 @@ Return value is the property name, as a keyword, or nil." (and (memq object (org-element-property p parent)) (throw 'exit p)))))) +(defun org-element-class (datum &optional parent) + "Return class for ELEMENT, as a symbol. +Class is either `element' or `object'. Optional argument PARENT +is the element or object containing DATUM. It defaults to the +value of DATUM `:parent' property." + (let ((type (org-element-type datum)) + (parent (or parent (org-element-property :parent datum)))) + (cond + ;; Trivial cases. + ((memq type org-element-all-objects) 'object) + ((memq type org-element-all-elements) 'element) + ;; Special cases. + ((eq type 'org-data) 'element) + ((eq type 'plain-text) 'object) + ((not type) 'object) + ;; Pseudo object or elements. Make a guess about its class. + ;; Basically a pseudo object is contained within another object, + ;; a secondary string or a container element. + ((not parent) 'element) + (t + (let ((parent-type (org-element-type parent))) + (cond ((not parent-type) 'object) + ((memq parent-type org-element-object-containers) 'object) + ((org-element-secondary-p datum) 'object) + (t 'element))))))) + (defsubst org-element-adopt-elements (parent &rest children) "Append elements to the contents of another element. @@ -587,11 +647,11 @@ DATUM is an element, object, string or nil. `:parent' property is cleared and contents are removed in the process." (when datum (let ((type (org-element-type datum))) - (case type - (org-data (list 'org-data nil)) - (plain-text (substring-no-properties datum)) - ((nil) (copy-sequence datum)) - (otherwise + (pcase type + (`org-data (list 'org-data nil)) + (`plain-text (substring-no-properties datum)) + (`nil (copy-sequence datum)) + (_ (list type (plist-put (copy-sequence (nth 1 datum)) :parent nil))))))) @@ -674,8 +734,8 @@ Assume point is at the beginning of the block." :post-affiliated post-affiliated) (cdr affiliated)))))))) -(defun org-element-center-block-interpreter (center-block contents) - "Interpret CENTER-BLOCK element as Org syntax. +(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)) @@ -702,7 +762,7 @@ Assume point is at beginning of drawer." (save-excursion (let* ((drawer-end-line (match-beginning 0)) (name (progn (looking-at org-drawer-regexp) - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (begin (car affiliated)) (post-affiliated (point)) ;; Empty drawers have no contents. @@ -758,8 +818,8 @@ Assume point is at beginning of dynamic block." (let ((block-end-line (match-beginning 0))) (save-excursion (let* ((name (progn (looking-at org-dblock-start-re) - (org-match-string-no-properties 1))) - (arguments (org-match-string-no-properties 3)) + (match-string-no-properties 1))) + (arguments (match-string-no-properties 3)) (begin (car affiliated)) (post-affiliated (point)) ;; Empty blocks have no contents. @@ -817,7 +877,7 @@ a plist containing `:label', `:begin' `:end', `:contents-begin', Assume point is at the beginning of the footnote definition." (save-excursion (let* ((label (progn (looking-at org-footnote-definition-re) - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (begin (car affiliated)) (post-affiliated (point)) (ending @@ -832,7 +892,7 @@ Assume point is at the beginning of the footnote definition." ;; before any affiliated keyword above. (forward-line -1) (while (and (> (point) post-affiliated) - (org-looking-at-p org-element--affiliated-re)) + (looking-at-p org-element--affiliated-re)) (forward-line -1)) (line-beginning-position 2)) (t (match-beginning 0))))) @@ -861,7 +921,7 @@ 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 "[%s]" (org-element-property :label footnote-definition)) + (concat (format "[fn:%s]" (org-element-property :label footnote-definition)) " " contents)) @@ -875,13 +935,13 @@ obtained through property drawer and default properties from the parser (e.g. `:end' and :END:). Return value is a plist." (save-excursion (forward-line) - (when (org-looking-at-p org-planning-line-re) (forward-line)) + (when (looking-at-p org-planning-line-re) (forward-line)) (when (looking-at org-property-drawer-re) (forward-line) (let ((end (match-end 0)) properties) (while (< (line-end-position) end) (looking-at org-property-re) - (push (org-match-string-no-properties 3) properties) + (push (match-string-no-properties 3) properties) (push (intern (concat ":" (upcase (match-string 2)))) properties) (forward-line)) properties)))) @@ -944,7 +1004,7 @@ Assume point is at beginning of the headline." (goto-char (match-end 0)))) (title-start (point)) (tags (when (re-search-forward - (org-re "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") + "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" (line-end-position) 'move) (goto-char (match-beginning 0)) @@ -995,17 +1055,16 @@ Assume point is at beginning of the headline." (org-element-put-property headline :title (if raw-secondary-p raw-value - (let ((title (org-element--parse-objects - (progn (goto-char title-start) - (skip-chars-forward " \t") - (point)) - (progn (goto-char title-end) - (skip-chars-backward " \t") - (point)) - nil - (org-element-restriction 'headline)))) - (dolist (datum title title) - (org-element-put-property datum :parent headline))))))))) + (org-element--parse-objects + (progn (goto-char title-start) + (skip-chars-forward " \t") + (point)) + (progn (goto-char title-end) + (skip-chars-backward " \t") + (point)) + nil + (org-element-restriction 'headline) + headline))))))) (defun org-element-headline-interpreter (headline contents) "Interpret HEADLINE element as Org syntax. @@ -1087,7 +1146,7 @@ Assume point is at beginning of the inline task." (aref (match-string 0) 2)))) (title-start (point)) (tags (when (re-search-forward - (org-re "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") + "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" (line-end-position) 'move) (goto-char (match-beginning 0)) @@ -1098,7 +1157,7 @@ Assume point is at beginning of the inline task." (task-end (save-excursion (end-of-line) (and (re-search-forward org-outline-regexp-bol limit t) - (org-looking-at-p "END[ \t]*$") + (looking-at-p "END[ \t]*$") (line-beginning-position)))) (standard-props (and task-end (org-element--get-node-properties))) (time-props (and task-end (org-element--get-time-properties))) @@ -1131,17 +1190,16 @@ Assume point is at beginning of the inline task." (org-element-put-property inlinetask :title (if raw-secondary-p raw-value - (let ((title (org-element--parse-objects - (progn (goto-char title-start) - (skip-chars-forward " \t") - (point)) - (progn (goto-char title-end) - (skip-chars-backward " \t") - (point)) - nil - (org-element-restriction 'inlinetask)))) - (dolist (datum title title) - (org-element-put-property datum :parent inlinetask)))))))) + (org-element--parse-objects + (progn (goto-char title-start) + (skip-chars-forward " \t") + (point)) + (progn (goto-char title-end) + (skip-chars-backward " \t") + (point)) + nil + (org-element-restriction 'inlinetask) + inlinetask)))))) (defun org-element-inlinetask-interpreter (inlinetask contents) "Interpret INLINETASK element as Org syntax. @@ -1183,7 +1241,7 @@ CONTENTS is the contents of inlinetask." ;;;; Item -(defun org-element-item-parser (limit struct &optional raw-secondary-p) +(defun org-element-item-parser (_ struct &optional raw-secondary-p) "Parse an item. STRUCT is the structure of the plain list. @@ -1202,7 +1260,7 @@ Assume point is at the beginning of the item." (beginning-of-line) (looking-at org-list-full-item-re) (let* ((begin (point)) - (bullet (org-match-string-no-properties 1)) + (bullet (match-string-no-properties 1)) (checkbox (let ((box (match-string 3))) (cond ((equal "[ ]" box) 'off) ((equal "[X]" box) 'on) @@ -1253,11 +1311,10 @@ Assume point is at the beginning of the item." (let ((raw (org-list-get-tag begin struct))) (when raw (if raw-secondary-p raw - (let ((tag (org-element--parse-objects - (match-beginning 4) (match-end 4) nil - (org-element-restriction 'item)))) - (dolist (datum tag tag) - (org-element-put-property datum :parent item)))))))))) + (org-element--parse-objects + (match-beginning 4) (match-end 4) nil + (org-element-restriction 'item) + item)))))))) (defun org-element-item-interpreter (item contents) "Interpret ITEM element as Org syntax. @@ -1280,10 +1337,11 @@ CONTENTS is the contents of the element." (concat bullet (and counter (format "[@%d] " counter)) - (case checkbox - (on "[X] ") - (off "[ ] ") - (trans "[-] ")) + (pcase checkbox + (`on "[X] ") + (`off "[ ] ") + (`trans "[-] ") + (_ nil)) (and tag (format "%s :: " tag)) (when contents (let ((contents (replace-regexp-in-string @@ -1351,7 +1409,7 @@ CONTENTS is the contents of the element." (forward-line) (let ((origin (point))) (when (re-search-forward inlinetask-re limit t) - (if (org-looking-at-p "END[ \t]*$") (forward-line) + (if (looking-at-p "END[ \t]*$") (forward-line) (goto-char origin))))) ;; At some text line. Check if it ends any previous item. (t @@ -1393,7 +1451,7 @@ containing `:type', `:begin', `:end', `:contents-begin' and Assume point is at the beginning of the list." (save-excursion (let* ((struct (or structure (org-element--list-struct limit))) - (type (cond ((org-looking-at-p "[ \t]*[A-Za-z0-9]") 'ordered) + (type (cond ((looking-at-p "[ \t]*[A-Za-z0-9]") 'ordered) ((nth 5 (assq (point) struct)) 'descriptive) (t 'unordered))) (contents-begin (point)) @@ -1421,8 +1479,8 @@ Assume point is at the beginning of the list." :post-affiliated contents-begin) (cdr affiliated)))))) -(defun org-element-plain-list-interpreter (plain-list contents) - "Interpret PLAIN-LIST element as Org syntax. +(defun org-element-plain-list-interpreter (_ contents) + "Interpret plain-list element as Org syntax. CONTENTS is the contents of the element." (with-temp-buffer (insert contents) @@ -1461,8 +1519,8 @@ Assume point is at the beginning of the property drawer." :post-blank (count-lines before-blank end) :post-affiliated begin)))))) -(defun org-element-property-drawer-interpreter (property-drawer contents) - "Interpret PROPERTY-DRAWER element as Org syntax. +(defun org-element-property-drawer-interpreter (_ contents) + "Interpret property-drawer element as Org syntax. CONTENTS is the properties within the drawer." (format ":PROPERTIES:\n%s:END:" contents)) @@ -1511,19 +1569,17 @@ Assume point is at the beginning of the block." :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-quote-block-interpreter (quote-block contents) - "Interpret QUOTE-BLOCK element as Org syntax. +(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)) ;;;; Section -(defun org-element-section-parser (limit) +(defun org-element-section-parser (_) "Parse a section. -LIMIT bounds the search. - Return a list whose CAR is `section' and CDR is a plist containing `:begin', `:end', `:contents-begin', `contents-end', `:post-blank' and `:post-affiliated' keywords." @@ -1534,8 +1590,7 @@ containing `:begin', `:end', `:contents-begin', `contents-end', (end (progn (org-with-limited-levels (outline-next-heading)) (point))) (pos-before-blank (progn (skip-chars-backward " \r\t\n") - (forward-line) - (point)))) + (line-beginning-position 2)))) (list 'section (list :begin begin :end end @@ -1544,8 +1599,8 @@ containing `:begin', `:end', `:contents-begin', `contents-end', :post-blank (count-lines pos-before-blank end) :post-affiliated begin))))) -(defun org-element-section-interpreter (section contents) - "Interpret SECTION element as Org syntax. +(defun org-element-section-interpreter (_ contents) + "Interpret section element as Org syntax. CONTENTS is the contents of the element." contents) @@ -1616,9 +1671,6 @@ CONTENTS is the contents of the element." ;; through the following steps: implement a parser and an interpreter, ;; tweak `org-element--current-element' so that it recognizes the new ;; type and add that new type to `org-element-all-elements'. -;; -;; As a special case, when the newly defined type is a block type, -;; `org-element-block-name-alist' has to be modified accordingly. ;;;; Babel Call @@ -1665,9 +1717,8 @@ containing `:call', `:inside-header', `:arguments', :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-babel-call-interpreter (babel-call contents) - "Interpret BABEL-CALL element as Org syntax. -CONTENTS is nil." +(defun org-element-babel-call-interpreter (babel-call _) + "Interpret BABEL-CALL element as Org syntax." (concat "#+CALL: " (org-element-property :call babel-call) (let ((h (org-element-property :inside-header babel-call))) @@ -1696,7 +1747,7 @@ Return a list whose CAR is `clock' and CDR is a plist containing (duration (and (search-forward " => " (line-end-position) t) (progn (skip-chars-forward " \t") (looking-at "\\(\\S-+\\)[ \t]*$")) - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (status (if duration 'closed 'running)) (post-blank (let ((before-blank (progn (forward-line) (point)))) (skip-chars-forward " \r\t\n" limit) @@ -1713,9 +1764,8 @@ Return a list whose CAR is `clock' and CDR is a plist containing :post-blank post-blank :post-affiliated begin))))) -(defun org-element-clock-interpreter (clock contents) - "Interpret CLOCK element as Org syntax. -CONTENTS is nil." +(defun org-element-clock-interpreter (clock _) + "Interpret CLOCK element as Org syntax." (concat org-clock-string " " (org-element-timestamp-interpreter (org-element-property :value clock) nil) @@ -1774,7 +1824,7 @@ Assume point is at comment beginning." :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-comment-interpreter (comment contents) +(defun org-element-comment-interpreter (comment _) "Interpret COMMENT element as Org syntax. CONTENTS is nil." (replace-regexp-in-string "^" "# " (org-element-property :value comment))) @@ -1821,9 +1871,8 @@ Assume point is at comment block beginning." :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-comment-block-interpreter (comment-block contents) - "Interpret COMMENT-BLOCK element as Org syntax. -CONTENTS is nil." +(defun org-element-comment-block-interpreter (comment-block _) + "Interpret COMMENT-BLOCK element as Org syntax." (format "#+BEGIN_COMMENT\n%s#+END_COMMENT" (org-element-normalize-string (org-remove-indentation @@ -1847,7 +1896,7 @@ containing `:begin', `:end', `:value', `:post-blank' and (let ((begin (car affiliated)) (post-affiliated (point)) (value (progn (looking-at "\\(%%(.*\\)[ \t]*$") - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (pos-before-blank (progn (forward-line) (point))) (end (progn (skip-chars-forward " \r\t\n" limit) (if (eobp) (point) (line-beginning-position))))) @@ -1860,9 +1909,8 @@ containing `:begin', `:end', `:value', `:post-blank' and :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-diary-sexp-interpreter (diary-sexp contents) - "Interpret DIARY-SEXP as Org syntax. -CONTENTS is nil." +(defun org-element-diary-sexp-interpreter (diary-sexp _) + "Interpret DIARY-SEXP as Org syntax." (org-element-property :value diary-sexp)) @@ -1890,12 +1938,20 @@ containing `:begin', `:end', `:number-lines', `:preserve-indent', (let* ((switches (progn (looking-at "^[ \t]*#\\+BEGIN_EXAMPLE\\(?: +\\(.*\\)\\)?") - (org-match-string-no-properties 1))) - ;; Switches analysis + (match-string-no-properties 1))) + ;; Switches analysis. (number-lines - (cond ((not switches) nil) - ((string-match "-n\\>" switches) 'new) - ((string-match "+n\\>" switches) 'continued))) + (and switches + (string-match "\\([-+]\\)n\\(?: *\\([0-9]+\\)\\)?\\>" + switches) + (cons + (if (equal (match-string 1 switches) "-") + 'new + 'continued) + (if (not (match-end 2)) 0 + ;; Subtract 1 to give number of lines before + ;; first line. + (1- (string-to-number (match-string 2 switches))))))) (preserve-indent (and switches (string-match "-i\\>" switches))) ;; Should labels be retained in (or stripped from) example @@ -1917,13 +1973,10 @@ containing `:begin', `:end', `:number-lines', `:preserve-indent', ;; Standard block parsing. (begin (car affiliated)) (post-affiliated (point)) - (block-ind (progn (skip-chars-forward " \t") (current-column))) - (contents-begin (progn (forward-line) (point))) - (value (org-element-remove-indentation - (org-unescape-code-in-string - (buffer-substring-no-properties - contents-begin contents-end)) - block-ind)) + (contents-begin (line-beginning-position 2)) + (value (org-unescape-code-in-string + (buffer-substring-no-properties + contents-begin contents-end))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -1944,9 +1997,8 @@ containing `:begin', `:end', `:number-lines', `:preserve-indent', :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-example-block-interpreter (example-block contents) - "Interpret EXAMPLE-BLOCK element as Org syntax. -CONTENTS is nil." +(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" @@ -1955,7 +2007,7 @@ CONTENTS is nil." (if (or org-src-preserve-indentation (org-element-property :preserve-indent example-block)) value - (org-element-remove-indentation value)))) + (org-remove-indentation value)))) "#+END_EXAMPLE"))) @@ -1974,43 +2026,44 @@ containing `:begin', `:end', `:type', `:value', `:post-blank' and `:post-affiliated' keywords. Assume point is at export-block beginning." - (let* ((case-fold-search t) - (type (progn (looking-at "[ \t]*#\\+BEGIN_\\(\\S-+\\)") - (upcase (org-match-string-no-properties 1))))) + (let* ((case-fold-search t)) (if (not (save-excursion - (re-search-forward - (format "^[ \t]*#\\+END_%s[ \t]*$" type) limit t))) + (re-search-forward "^[ \t]*#\\+END_EXPORT[ \t]*$" limit t))) ;; Incomplete block: parse it as a paragraph. (org-element-paragraph-parser limit affiliated) - (let ((contents-end (match-beginning 0))) - (save-excursion - (let* ((begin (car affiliated)) - (post-affiliated (point)) - (contents-begin (progn (forward-line) (point))) - (pos-before-blank (progn (goto-char contents-end) - (forward-line) - (point))) - (end (progn (skip-chars-forward " \r\t\n" limit) - (if (eobp) (point) (line-beginning-position)))) - (value (buffer-substring-no-properties contents-begin - contents-end))) - (list 'export-block - (nconc - (list :begin begin - :end end - :type type - :value value - :post-blank (count-lines pos-before-blank end) - :post-affiliated post-affiliated) - (cdr affiliated))))))))) + (save-excursion + (let* ((contents-end (match-beginning 0)) + (backend + (progn + (looking-at + "[ \t]*#\\+BEGIN_EXPORT\\(?:[ \t]+\\(\\S-+\\)\\)?[ \t]*$") + (match-string-no-properties 1))) + (begin (car affiliated)) + (post-affiliated (point)) + (contents-begin (progn (forward-line) (point))) + (pos-before-blank (progn (goto-char contents-end) + (forward-line) + (point))) + (end (progn (skip-chars-forward " \r\t\n" limit) + (if (eobp) (point) (line-beginning-position)))) + (value (org-unescape-code-in-string + (buffer-substring-no-properties contents-begin + contents-end)))) + (list 'export-block + (nconc + (list :type (and backend (upcase backend)) + :begin begin + :end end + :value value + :post-blank (count-lines pos-before-blank end) + :post-affiliated post-affiliated) + (cdr affiliated)))))))) -(defun org-element-export-block-interpreter (export-block contents) - "Interpret EXPORT-BLOCK element as Org syntax. -CONTENTS is nil." - (let ((type (org-element-property :type export-block))) - (concat (format "#+BEGIN_%s\n" type) - (org-element-property :value export-block) - (format "#+END_%s" type)))) +(defun org-element-export-block-interpreter (export-block _) + "Interpret EXPORT-BLOCK element as Org syntax." + (format "#+BEGIN_EXPORT %s\n%s#+END_EXPORT" + (org-element-property :type export-block) + (org-element-property :value export-block))) ;;;; Fixed-width @@ -2055,9 +2108,8 @@ Assume point is at the beginning of the fixed-width area." :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-fixed-width-interpreter (fixed-width contents) - "Interpret FIXED-WIDTH element as Org syntax. -CONTENTS is nil." +(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 @@ -2092,9 +2144,8 @@ keywords." :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-horizontal-rule-interpreter (horizontal-rule contents) - "Interpret HORIZONTAL-RULE element as Org syntax. -CONTENTS is nil." +(defun org-element-horizontal-rule-interpreter (&rest _) + "Interpret HORIZONTAL-RULE element as Org syntax." "-----") @@ -2118,7 +2169,7 @@ containing `:key', `:value', `:begin', `:end', `:post-blank' and (let ((begin (or (car affiliated) (point))) (post-affiliated (point)) (key (progn (looking-at "[ \t]*#\\+\\(\\S-+*\\):") - (upcase (org-match-string-no-properties 1)))) + (upcase (match-string-no-properties 1)))) (value (org-trim (buffer-substring-no-properties (match-end 0) (point-at-eol)))) (pos-before-blank (progn (forward-line) (point))) @@ -2134,9 +2185,8 @@ containing `:key', `:value', `:begin', `:end', `:post-blank' and :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-keyword-interpreter (keyword contents) - "Interpret KEYWORD element as Org syntax. -CONTENTS is nil." +(defun org-element-keyword-interpreter (keyword _) + "Interpret KEYWORD element as Org syntax." (format "#+%s: %s" (org-element-property :key keyword) (org-element-property :value keyword))) @@ -2192,9 +2242,8 @@ Assume point is at the beginning of the latex environment." :post-affiliated code-begin) (cdr affiliated)))))))) -(defun org-element-latex-environment-interpreter (latex-environment contents) - "Interpret LATEX-ENVIRONMENT element as Org syntax. -CONTENTS is nil." +(defun org-element-latex-environment-interpreter (latex-environment _) + "Interpret LATEX-ENVIRONMENT element as Org syntax." (org-element-property :value latex-environment)) @@ -2211,8 +2260,8 @@ containing `:key', `:value', `:begin', `:end', `:post-blank' and (looking-at org-property-re) (let ((case-fold-search t) (begin (point)) - (key (org-match-string-no-properties 2)) - (value (org-match-string-no-properties 3)) + (key (match-string-no-properties 2)) + (value (match-string-no-properties 3)) (end (save-excursion (end-of-line) (if (re-search-forward org-property-re limit t) @@ -2226,9 +2275,8 @@ containing `:key', `:value', `:begin', `:end', `:post-blank' and :post-blank 0 :post-affiliated begin)))) -(defun org-element-node-property-interpreter (node-property contents) - "Interpret NODE-PROPERTY element as Org syntax. -CONTENTS is nil." +(defun org-element-node-property-interpreter (node-property _) + "Interpret NODE-PROPERTY element as Org syntax." (format org-property-format (format ":%s:" (org-element-property :key node-property)) (or (org-element-property :value node-property) ""))) @@ -2303,8 +2351,8 @@ Assume point is at the beginning of the paragraph." :post-affiliated contents-begin) (cdr affiliated)))))) -(defun org-element-paragraph-interpreter (paragraph contents) - "Interpret PARAGRAPH element as Org syntax. +(defun org-element-paragraph-interpreter (_ contents) + "Interpret paragraph element as Org syntax. CONTENTS is the contents of the element." contents) @@ -2347,11 +2395,10 @@ containing `:closed', `:deadline', `:scheduled', `:begin', :post-blank post-blank :post-affiliated begin))))) -(defun org-element-planning-interpreter (planning contents) - "Interpret PLANNING element as Org syntax. -CONTENTS is nil." +(defun org-element-planning-interpreter (planning _) + "Interpret PLANNING element as Org syntax." (mapconcat - 'identity + #'identity (delq nil (list (let ((deadline (org-element-property :deadline planning))) (when deadline @@ -2398,20 +2445,28 @@ Assume point is at the beginning of the block." (language (progn (looking-at - (concat "^[ \t]*#\\+BEGIN_SRC" - "\\(?: +\\(\\S-+\\)\\)?" - "\\(\\(?: +\\(?:-l \".*?\"\\|[-+][A-Za-z]\\)\\)+\\)?" - "\\(.*\\)[ \t]*$")) - (org-match-string-no-properties 1))) + "^[ \t]*#\\+BEGIN_SRC\ +\\(?: +\\(\\S-+\\)\\)?\ +\\(\\(?: +\\(?:-\\(?:l \".+\"\\|[ikr]\\)\\|[-+]n\\(?: *[0-9]+\\)?\\)\\)+\\)?\ +\\(.*\\)[ \t]*$") + (match-string-no-properties 1))) ;; Get switches. - (switches (org-match-string-no-properties 2)) + (switches (match-string-no-properties 2)) ;; Get parameters. - (parameters (org-match-string-no-properties 3)) - ;; Switches analysis + (parameters (match-string-no-properties 3)) + ;; Switches analysis. (number-lines - (cond ((not switches) nil) - ((string-match "-n\\>" switches) 'new) - ((string-match "+n\\>" switches) 'continued))) + (and switches + (string-match "\\([-+]\\)n\\(?: *\\([0-9]+\\)\\)?\\>" + switches) + (cons + (if (equal (match-string 1 switches) "-") + 'new + 'continued) + (if (not (match-end 2)) 0 + ;; Subtract 1 to give number of lines before + ;; first line. + (1- (string-to-number (match-string 2 switches))))))) (preserve-indent (and switches (string-match "-i\\>" switches))) (label-fmt @@ -2430,14 +2485,10 @@ Assume point is at the beginning of the block." (or (not switches) (and retain-labels (not (string-match "-k\\>" switches))))) - ;; Indentation. - (block-ind (progn (skip-chars-forward " \t") (current-column))) ;; Retrieve code. - (value (org-element-remove-indentation - (org-unescape-code-in-string - (buffer-substring-no-properties - (progn (forward-line) (point)) contents-end)) - block-ind)) + (value (org-unescape-code-in-string + (buffer-substring-no-properties + (line-beginning-position 2) contents-end))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -2463,9 +2514,8 @@ Assume point is at the beginning of the block." :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-src-block-interpreter (src-block contents) - "Interpret SRC-BLOCK element as Org syntax. -CONTENTS is nil." +(defun org-element-src-block-interpreter (src-block _) + "Interpret SRC-BLOCK element as Org syntax." (let ((lang (org-element-property :language src-block)) (switches (org-element-property :switches src-block)) (params (org-element-property :parameters src-block)) @@ -2475,11 +2525,12 @@ CONTENTS is nil." ((or org-src-preserve-indentation (org-element-property :preserve-indent src-block)) val) - ((zerop org-edit-src-content-indentation) val) + ((zerop 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-" ind val nil nil 1))))))) + "^" ind (org-remove-indentation val)))))))) (concat (format "#+BEGIN_SRC%s\n" (concat (and lang (concat " " lang)) (and switches (concat " " switches)) @@ -2517,7 +2568,7 @@ Assume point is at the beginning of the table." (point))) (tblfm (let (acc) (while (looking-at "[ \t]*#\\+TBLFM: +\\(.*\\)[ \t]*$") - (push (org-match-string-no-properties 1) acc) + (push (match-string-no-properties 1) acc) (forward-line)) acc)) (pos-before-blank (point)) @@ -2556,11 +2607,9 @@ CONTENTS is a string, if table's type is `org', or nil." ;;;; Table Row -(defun org-element-table-row-parser (limit) +(defun org-element-table-row-parser (_) "Parse table row at point. -LIMIT bounds the search. - Return a list whose CAR is `table-row' and CDR is a plist containing `:begin', `:end', `:contents-begin', `:contents-end', `:type', `:post-blank' and `:post-affiliated' keywords." @@ -2569,9 +2618,7 @@ containing `:begin', `:end', `:contents-begin', `:contents-end', (begin (point)) ;; A table rule has no contents. In that case, ensure ;; CONTENTS-BEGIN matches CONTENTS-END. - (contents-begin (and (eq type 'standard) - (search-forward "|") - (point))) + (contents-begin (and (eq type 'standard) (search-forward "|"))) (contents-end (and (eq type 'standard) (progn (end-of-line) @@ -2591,7 +2638,7 @@ containing `:begin', `:end', `:contents-begin', `:contents-end', "Interpret TABLE-ROW element as Org syntax. CONTENTS is the contents of the table row." (if (eq (org-element-property :type table-row) 'rule) "|-" - (concat "| " contents))) + (concat "|" contents))) ;;;; Verse Block @@ -2634,8 +2681,8 @@ Assume point is at beginning of the block." :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-verse-block-interpreter (verse-block contents) - "Interpret VERSE-BLOCK element as Org syntax. +(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)) @@ -2683,8 +2730,8 @@ Assume point is at the first star marker." :contents-end contents-end :post-blank post-blank)))))) -(defun org-element-bold-interpreter (bold contents) - "Interpret BOLD object as Org syntax. +(defun org-element-bold-interpreter (_ contents) + "Interpret bold object as Org syntax. CONTENTS is the contents of the object." (format "*%s*" contents)) @@ -2703,7 +2750,7 @@ Assume point is at the first tilde marker." (unless (bolp) (backward-char 1)) (when (looking-at org-emph-re) (let ((begin (match-beginning 2)) - (value (org-match-string-no-properties 4)) + (value (match-string-no-properties 4)) (post-blank (progn (goto-char (match-end 2)) (skip-chars-forward " \t"))) (end (point))) @@ -2713,9 +2760,8 @@ Assume point is at the first tilde marker." :end end :post-blank post-blank)))))) -(defun org-element-code-interpreter (code contents) - "Interpret CODE object as Org syntax. -CONTENTS is nil." +(defun org-element-code-interpreter (code _) + "Interpret CODE object as Org syntax." (format "~%s~" (org-element-property :value code))) @@ -2754,9 +2800,8 @@ Assume point is at the beginning of the entity." :use-brackets-p bracketsp :post-blank post-blank))))))) -(defun org-element-entity-interpreter (entity contents) - "Interpret ENTITY object as Org syntax. -CONTENTS is nil." +(defun org-element-entity-interpreter (entity _) + "Interpret ENTITY object as Org syntax." (concat "\\" (org-element-property :name entity) (when (org-element-property :use-brackets-p entity) "{}"))) @@ -2781,7 +2826,7 @@ Assume point is at the beginning of the snippet." (re-search-forward "@@" nil t) (match-beginning 0)))) (let* ((begin (match-beginning 0)) - (back-end (org-match-string-no-properties 1)) + (back-end (match-string-no-properties 1)) (value (buffer-substring-no-properties (match-end 0) contents-end)) (post-blank (skip-chars-forward " \t")) @@ -2793,9 +2838,8 @@ Assume point is at the beginning of the snippet." :end end :post-blank post-blank))))))) -(defun org-element-export-snippet-interpreter (export-snippet contents) - "Interpret EXPORT-SNIPPET object as Org syntax. -CONTENTS is nil." +(defun org-element-export-snippet-interpreter (export-snippet _) + "Interpret EXPORT-SNIPPET object as Org syntax." (format "@@%s:%s@@" (org-element-property :back-end export-snippet) (org-element-property :value export-snippet))) @@ -2816,14 +2860,10 @@ When at a footnote reference, return a list whose car is (when closing (save-excursion (let* ((begin (point)) - (label - (or (org-match-string-no-properties 2) - (org-match-string-no-properties 3) - (and (match-string 1) - (concat "fn:" (org-match-string-no-properties 1))))) - (type (if (or (not label) (match-string 1)) 'inline 'standard)) + (label (match-string-no-properties 1)) (inner-begin (match-end 0)) (inner-end (1- closing)) + (type (if (match-end 2) 'inline 'standard)) (post-blank (progn (goto-char closing) (skip-chars-forward " \t"))) (end (point))) @@ -2839,9 +2879,9 @@ When at a footnote reference, return a list whose car is (defun org-element-footnote-reference-interpreter (footnote-reference contents) "Interpret FOOTNOTE-REFERENCE object as Org syntax. CONTENTS is its definition, when inline, or nil." - (format "[%s]" - (concat (or (org-element-property :label footnote-reference) "fn:") - (and contents (concat ":" contents))))) + (format "[fn:%s%s]" + (or (org-element-property :label footnote-reference) "") + (if contents (concat ":" contents) ""))) ;;;; Inline Babel Call @@ -2856,31 +2896,39 @@ When at an inline babel call, return a list whose car is Assume point is at the beginning of the babel call." (save-excursion - (unless (bolp) (backward-char)) - (when (let ((case-fold-search t)) - (looking-at org-babel-inline-lob-one-liner-regexp)) - (let ((begin (match-end 1)) - (call (org-match-string-no-properties 2)) - (inside-header (org-string-nw-p (org-match-string-no-properties 4))) - (arguments (org-string-nw-p (org-match-string-no-properties 6))) - (end-header (org-string-nw-p (org-match-string-no-properties 8))) - (value (buffer-substring-no-properties (match-end 1) (match-end 0))) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'inline-babel-call - (list :call call - :inside-header inside-header - :arguments arguments - :end-header end-header - :begin begin - :end end - :value value - :post-blank post-blank)))))) + (catch :no-object + (when (let ((case-fold-search nil)) + (looking-at "\\<call_\\([^ \t\n[(]+\\)[([]")) + (goto-char (match-end 1)) + (let* ((begin (match-beginning 0)) + (call (match-string-no-properties 1)) + (inside-header + (let ((p (org-element--parse-paired-brackets ?\[))) + (and (org-string-nw-p p) + (replace-regexp-in-string "\n[ \t]*" " " (org-trim p))))) + (arguments (org-string-nw-p + (or (org-element--parse-paired-brackets ?\() + ;; Parenthesis are mandatory. + (throw :no-object nil)))) + (end-header + (let ((p (org-element--parse-paired-brackets ?\[))) + (and (org-string-nw-p p) + (replace-regexp-in-string "\n[ \t]*" " " (org-trim p))))) + (value (buffer-substring-no-properties begin (point))) + (post-blank (skip-chars-forward " \t")) + (end (point))) + (list 'inline-babel-call + (list :call call + :inside-header inside-header + :arguments arguments + :end-header end-header + :begin begin + :end end + :value value + :post-blank post-blank))))))) -(defun org-element-inline-babel-call-interpreter (inline-babel-call contents) - "Interpret INLINE-BABEL-CALL object as Org syntax. -CONTENTS is nil." +(defun org-element-inline-babel-call-interpreter (inline-babel-call _) + "Interpret INLINE-BABEL-CALL object as Org syntax." (concat "call_" (org-element-property :call inline-babel-call) (let ((h (org-element-property :inside-header inline-babel-call))) @@ -2902,26 +2950,29 @@ keywords. Otherwise, return nil. Assume point is at the beginning of the inline src block." (save-excursion - (unless (bolp) (backward-char)) - (when (looking-at org-babel-inline-src-block-regexp) - (let ((begin (match-beginning 1)) - (language (org-match-string-no-properties 2)) - (parameters (org-match-string-no-properties 4)) - (value (org-match-string-no-properties 5)) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'inline-src-block - (list :language language - :value value - :parameters parameters - :begin begin - :end end - :post-blank post-blank)))))) + (catch :no-object + (when (let ((case-fold-search nil)) + (looking-at "\\<src_\\([^ \t\n[{]+\\)[{[]")) + (goto-char (match-end 1)) + (let ((begin (match-beginning 0)) + (language (match-string-no-properties 1)) + (parameters + (let ((p (org-element--parse-paired-brackets ?\[))) + (and (org-string-nw-p p) + (replace-regexp-in-string "\n[ \t]*" " " (org-trim p))))) + (value (or (org-element--parse-paired-brackets ?\{) + (throw :no-object nil))) + (post-blank (skip-chars-forward " \t"))) + (list 'inline-src-block + (list :language language + :value value + :parameters parameters + :begin begin + :end (point) + :post-blank post-blank))))))) -(defun org-element-inline-src-block-interpreter (inline-src-block contents) - "Interpret INLINE-SRC-BLOCK object as Org syntax. -CONTENTS is nil." +(defun org-element-inline-src-block-interpreter (inline-src-block _) + "Interpret INLINE-SRC-BLOCK object as Org syntax." (let ((language (org-element-property :language inline-src-block)) (arguments (org-element-property :parameters inline-src-block)) (body (org-element-property :value inline-src-block))) @@ -2957,8 +3008,8 @@ Assume point is at the first slash marker." :contents-end contents-end :post-blank post-blank)))))) -(defun org-element-italic-interpreter (italic contents) - "Interpret ITALIC object as Org syntax. +(defun org-element-italic-interpreter (_ contents) + "Interpret italic object as Org syntax. CONTENTS is the contents of the object." (format "/%s/" contents)) @@ -2986,12 +3037,13 @@ Assume point is at the beginning of the LaTeX fragment." '(?\s ?\t ?\n ?, ?.))) (looking-at "\\(\\s.\\|\\s-\\|\\s(\\|\\s)\\|\\s\"\\|$\\)") (point))) - (case (char-after (1+ (point))) + (pcase (char-after (1+ (point))) (?\( (search-forward "\\)" nil t)) (?\[ (search-forward "\\]" nil t)) - (otherwise + (_ ;; Macro. - (and (looking-at "\\\\[a-zA-Z]+\\*?\\(\\(\\[[^][\n{}]*\\]\\)\\|\\({[^{}\n]*}\\)\\)*") + (and (looking-at "\\\\[a-zA-Z]+\\*?\\(\\(\\[[^][\n{}]*\\]\\)\ +\\|\\({[^{}\n]*}\\)\\)*") (match-end 0)))))) (post-blank (if (not after-fragment) (throw 'no-object nil) (goto-char after-fragment) @@ -3003,9 +3055,8 @@ Assume point is at the beginning of the LaTeX fragment." :end end :post-blank post-blank)))))) -(defun org-element-latex-fragment-interpreter (latex-fragment contents) - "Interpret LATEX-FRAGMENT object as Org syntax. -CONTENTS is nil." +(defun org-element-latex-fragment-interpreter (latex-fragment _) + "Interpret LATEX-FRAGMENT object as Org syntax." (org-element-property :value latex-fragment)) ;;;; Line Break @@ -3018,16 +3069,15 @@ and cdr a plist with `:begin', `:end' and `:post-blank' keywords. Otherwise, return nil. Assume point is at the beginning of the line break." - (when (and (org-looking-at-p "\\\\\\\\[ \t]*$") + (when (and (looking-at-p "\\\\\\\\[ \t]*$") (not (eq (char-before) ?\\))) (list 'line-break (list :begin (point) :end (line-beginning-position 2) :post-blank 0)))) -(defun org-element-line-break-interpreter (line-break contents) - "Interpret LINE-BREAK object as Org syntax. -CONTENTS is nil." +(defun org-element-line-break-interpreter (&rest _) + "Interpret LINE-BREAK object as Org syntax." "\\\\\n") @@ -3037,7 +3087,7 @@ CONTENTS is nil." "Parse link at point, if any. When at a link, return a list whose car is `link' and cdr a plist -with `:type', `:path', `:raw-link', `:application', +with `:type', `:path', `:format', `:raw-link', `:application', `:search-option', `:begin', `:end', `:contents-begin', `:contents-end' and `:post-blank' as keywords. Otherwise, return nil. @@ -3045,20 +3095,22 @@ nil. Assume point is at the beginning of the link." (catch 'no-object (let ((begin (point)) - end contents-begin contents-end link-end post-blank path type - raw-link link search-option application) + end contents-begin contents-end link-end post-blank path type format + raw-link search-option application) (cond ;; Type 1: Text targeted from a radio target. ((and org-target-link-regexp (save-excursion (or (bolp) (backward-char)) (looking-at org-target-link-regexp))) - (setq type "radio" - link-end (match-end 1) - path (org-match-string-no-properties 1) - contents-begin (match-beginning 1) - contents-end (match-end 1))) + (setq type "radio") + (setq format 'plain) + (setq link-end (match-end 1)) + (setq path (match-string-no-properties 1)) + (setq contents-begin (match-beginning 1)) + (setq contents-end (match-end 1))) ;; Type 2: Standard link, i.e. [[http://orgmode.org][homepage]] ((looking-at org-bracket-link-regexp) + (setq format 'bracket) (setq contents-begin (match-beginning 3)) (setq contents-end (match-end 3)) (setq link-end (match-end 0)) @@ -3076,7 +3128,7 @@ Assume point is at the beginning of the link." (setq raw-link (org-link-expand-abbrev (replace-regexp-in-string "[ \t]*\n[ \t]*" " " - (org-match-string-no-properties 1)))) + (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. @@ -3086,16 +3138,13 @@ Assume point is at the beginning of the link." (string-match "\\`\\.\\.?/" raw-link)) (setq type "file") (setq path raw-link)) - ;; Explicit type (http, irc, bbdb...). See `org-link-types'. + ;; Explicit type (http, irc, bbdb...). ((string-match org-link-types-re raw-link) (setq type (match-string 1 raw-link)) (setq path (substring raw-link (match-end 0)))) - ;; Id type: PATH is the id. - ((string-match "\\`id:\\([-a-f0-9]+\\)\\'" raw-link) - (setq type "id" path (match-string 1 raw-link))) ;; Code-ref type: PATH is the name of the reference. - ((and (org-string-match-p "\\`(" raw-link) - (org-string-match-p ")\\'" raw-link)) + ((and (string-match-p "\\`(" raw-link) + (string-match-p ")\\'" raw-link)) (setq type "coderef") (setq path (substring raw-link 1 -1))) ;; Custom-id type: PATH is the name of the custom id. @@ -3110,21 +3159,23 @@ Assume point is at the beginning of the link." (setq path raw-link)))) ;; Type 3: Plain link, e.g., http://orgmode.org ((looking-at org-plain-link-re) - (setq raw-link (org-match-string-no-properties 0) - type (org-match-string-no-properties 1) - link-end (match-end 0) - path (org-match-string-no-properties 2))) + (setq format 'plain) + (setq raw-link (match-string-no-properties 0)) + (setq type (match-string-no-properties 1)) + (setq link-end (match-end 0)) + (setq path (match-string-no-properties 2))) ;; Type 4: Angular link, e.g., <http://orgmode.org>. Unlike to ;; bracket links, follow RFC 3986 and remove any extra ;; whitespace in URI. ((looking-at org-angle-link-re) - (setq type (org-match-string-no-properties 1)) + (setq format 'angle) + (setq type (match-string-no-properties 1)) (setq link-end (match-end 0)) (setq raw-link (buffer-substring-no-properties (match-beginning 1) (match-end 2))) (setq path (replace-regexp-in-string - "[ \t]*\n[ \t]*" "" (org-match-string-no-properties 2)))) + "[ \t]*\n[ \t]*" "" (match-string-no-properties 2)))) (t (throw 'no-object nil))) ;; In any case, deduce end point after trailing white space from ;; LINK-END variable. @@ -3149,6 +3200,7 @@ Assume point is at the beginning of the link." (list 'link (list :type type :path path + :format format :raw-link (or raw-link path) :application application :search-option search-option @@ -3164,18 +3216,38 @@ CONTENTS is the contents of the object, or nil." (let ((type (org-element-property :type link)) (path (org-element-property :path link))) (if (string= type "radio") path - (format "[[%s]%s]" - (cond ((string= type "coderef") (format "(%s)" path)) - ((string= type "custom-id") (concat "#" path)) - ((string= type "file") - (let ((app (org-element-property :application link)) - (opt (org-element-property :search-option link))) - (concat type (and app (concat "+" app)) ":" - path - (and opt (concat "::" opt))))) - ((string= type "fuzzy") path) - (t (concat type ":" path))) - (if contents (format "[%s]" contents) ""))))) + (let ((fmt (pcase (org-element-property :format link) + ;; Links with contents and internal links have to + ;; use bracket syntax. Ignore `:format' in these + ;; cases. This is also the default syntax when the + ;; property is not defined, e.g., when the object + ;; was crafted by the user. + ((guard contents) + (format "[[%%s][%s]]" + ;; Since this is going to be used as + ;; a format string, escape percent signs + ;; in description. + (replace-regexp-in-string "%" "%%" contents))) + ((or `bracket + `nil + (guard (member type '("coderef" "custom-id" "fuzzy")))) + "[[%s]]") + ;; Otherwise, just obey to `:format'. + (`angle "<%s>") + (`plain "%s") + (f (error "Wrong `:format' value: %s" f))))) + (format fmt + (pcase type + ("coderef" (format "(%s)" path)) + ("custom-id" (concat "#" path)) + ("file" + (let ((app (org-element-property :application link)) + (opt (org-element-property :search-option link))) + (concat type (and app (concat "+" app)) ":" + path + (and opt (concat "::" opt))))) + ("fuzzy" path) + (_ (concat type ":" path)))))))) ;;;; Macro @@ -3191,12 +3263,12 @@ Assume point is at the macro." (save-excursion (when (looking-at "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\(([ \t\n]*\\([^\000]*?\\))\\)?}}}") (let ((begin (point)) - (key (downcase (org-match-string-no-properties 1))) - (value (org-match-string-no-properties 0)) + (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 (org-match-string-no-properties 3))) + (args (let ((args (match-string-no-properties 3))) (and args (org-macro-extract-arguments args))))) (list 'macro (list :key key @@ -3206,9 +3278,8 @@ Assume point is at the macro." :end end :post-blank post-blank)))))) -(defun org-element-macro-interpreter (macro contents) - "Interpret MACRO object as Org syntax. -CONTENTS is nil." +(defun org-element-macro-interpreter (macro _) + "Interpret MACRO object as Org syntax." (org-element-property :value macro)) @@ -3228,7 +3299,7 @@ Assume point is at the radio target." (let ((begin (point)) (contents-begin (match-beginning 1)) (contents-end (match-end 1)) - (value (org-match-string-no-properties 1)) + (value (match-string-no-properties 1)) (post-blank (progn (goto-char (match-end 0)) (skip-chars-forward " \t"))) (end (point))) @@ -3240,8 +3311,8 @@ Assume point is at the radio target." :post-blank post-blank :value value)))))) -(defun org-element-radio-target-interpreter (target contents) - "Interpret TARGET object as Org syntax. +(defun org-element-radio-target-interpreter (_ contents) + "Interpret target object as Org syntax. CONTENTS is the contents of the object." (concat "<<<" contents ">>>")) @@ -3270,9 +3341,8 @@ Assume point is at the beginning of the statistics-cookie." :value value :post-blank post-blank)))))) -(defun org-element-statistics-cookie-interpreter (statistics-cookie contents) - "Interpret STATISTICS-COOKIE object as Org syntax. -CONTENTS is nil." +(defun org-element-statistics-cookie-interpreter (statistics-cookie _) + "Interpret STATISTICS-COOKIE object as Org syntax." (org-element-property :value statistics-cookie)) @@ -3303,8 +3373,8 @@ Assume point is at the first plus sign marker." :contents-end contents-end :post-blank post-blank)))))) -(defun org-element-strike-through-interpreter (strike-through contents) - "Interpret STRIKE-THROUGH object as Org syntax. +(defun org-element-strike-through-interpreter (_ contents) + "Interpret strike-through object as Org syntax. CONTENTS is the contents of the object." (format "+%s+" contents)) @@ -3404,8 +3474,8 @@ and `:post-blank' keywords." :contents-end contents-end :post-blank 0)))) -(defun org-element-table-cell-interpreter (table-cell contents) - "Interpret TABLE-CELL element as Org syntax. +(defun org-element-table-cell-interpreter (_ contents) + "Interpret table-cell element as Org syntax. CONTENTS is the contents of the cell, or nil." (concat " " contents " |")) @@ -3423,7 +3493,7 @@ Assume point is at the target." (save-excursion (when (looking-at org-target-regexp) (let ((begin (point)) - (value (org-match-string-no-properties 1)) + (value (match-string-no-properties 1)) (post-blank (progn (goto-char (match-end 0)) (skip-chars-forward " \t"))) (end (point))) @@ -3433,9 +3503,8 @@ Assume point is at the target." :value value :post-blank post-blank)))))) -(defun org-element-target-interpreter (target contents) - "Interpret TARGET object as Org syntax. -CONTENTS is nil." +(defun org-element-target-interpreter (target _) + "Interpret TARGET object as Org syntax." (format "<<%s>>" (org-element-property :value target))) @@ -3462,7 +3531,7 @@ cdr a plist with `:type', `:raw-value', `:year-start', Otherwise, return nil. Assume point is at the beginning of the timestamp." - (when (org-looking-at-p org-element--timestamp-regexp) + (when (looking-at-p org-element--timestamp-regexp) (save-excursion (let* ((begin (point)) (activep (eq (char-after) ?<)) @@ -3500,8 +3569,8 @@ Assume point is at the beginning of the timestamp." (t 'cumulate))) :repeater-value (string-to-number (match-string 2 raw-value)) :repeater-unit - (case (string-to-char (match-string 3 raw-value)) - (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (t 'year))))) + (pcase (string-to-char (match-string 3 raw-value)) + (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year))))) (warning-props (and (not diaryp) (string-match "\\(-\\)?-\\([0-9]+\\)\\([hdwmy]\\)" raw-value) @@ -3509,8 +3578,8 @@ Assume point is at the beginning of the timestamp." :warning-type (if (match-string 1 raw-value) 'first 'all) :warning-value (string-to-number (match-string 2 raw-value)) :warning-unit - (case (string-to-char (match-string 3 raw-value)) - (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (t 'year))))) + (pcase (string-to-char (match-string 3 raw-value)) + (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year))))) year-start month-start day-start hour-start minute-start year-end month-end day-end hour-end minute-end) ;; Parse date-start. @@ -3550,26 +3619,24 @@ Assume point is at the beginning of the timestamp." repeater-props warning-props)))))) -(defun org-element-timestamp-interpreter (timestamp contents) - "Interpret TIMESTAMP object as Org syntax. -CONTENTS is nil." +(defun org-element-timestamp-interpreter (timestamp _) + "Interpret TIMESTAMP object as Org syntax." (let* ((repeat-string (concat - (case (org-element-property :repeater-type timestamp) - (cumulate "+") (catch-up "++") (restart ".+")) + (pcase (org-element-property :repeater-type timestamp) + (`cumulate "+") (`catch-up "++") (`restart ".+")) (let ((val (org-element-property :repeater-value timestamp))) (and val (number-to-string val))) - (case (org-element-property :repeater-unit timestamp) - (hour "h") (day "d") (week "w") (month "m") (year "y")))) + (pcase (org-element-property :repeater-unit timestamp) + (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) (warning-string (concat - (case (org-element-property :warning-type timestamp) - (first "--") - (all "-")) + (pcase (org-element-property :warning-type timestamp) + (`first "--") (`all "-")) (let ((val (org-element-property :warning-value timestamp))) (and val (number-to-string val))) - (case (org-element-property :warning-unit timestamp) - (hour "h") (day "d") (week "w") (month "m") (year "y")))) + (pcase (org-element-property :warning-unit timestamp) + (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) (build-ts-string ;; Build an Org timestamp string from TIME. ACTIVEP is ;; non-nil when time stamp is active. If WITH-TIME-P is @@ -3578,7 +3645,7 @@ CONTENTS is nil." ;; the repeater string, if any. (lambda (time activep &optional with-time-p hour-end minute-end) (let ((ts (format-time-string - (funcall (if with-time-p 'cdr 'car) + (funcall (if with-time-p #'cdr #'car) org-time-stamp-formats) time))) (when (and hour-end minute-end) @@ -3597,8 +3664,8 @@ CONTENTS is nil." ;; Return value. ts))) (type (org-element-property :type timestamp))) - (case type - ((active inactive) + (pcase type + ((or `active `inactive) (let* ((minute-start (org-element-property :minute-start timestamp)) (minute-end (org-element-property :minute-end timestamp)) (hour-start (org-element-property :hour-start timestamp)) @@ -3618,7 +3685,7 @@ CONTENTS is nil." (and hour-start minute-start) (and time-range-p hour-end) (and time-range-p minute-end)))) - ((active-range inactive-range) + ((or `active-range `inactive-range) (let ((minute-start (org-element-property :minute-start timestamp)) (minute-end (org-element-property :minute-end timestamp)) (hour-start (org-element-property :hour-start timestamp)) @@ -3644,7 +3711,7 @@ CONTENTS is nil." (org-element-property :year-end timestamp)) (eq type 'active-range) (and hour-end minute-end))))) - (otherwise (org-element-property :raw-value timestamp))))) + (_ (org-element-property :raw-value timestamp))))) ;;;; Underline @@ -3674,8 +3741,8 @@ Assume point is at the first underscore marker." :contents-end contents-end :post-blank post-blank)))))) -(defun org-element-underline-interpreter (underline contents) - "Interpret UNDERLINE object as Org syntax. +(defun org-element-underline-interpreter (_ contents) + "Interpret underline object as Org syntax. CONTENTS is the contents of the object." (format "_%s_" contents)) @@ -3694,7 +3761,7 @@ Assume point is at the first equal sign marker." (unless (bolp) (backward-char 1)) (when (looking-at org-emph-re) (let ((begin (match-beginning 2)) - (value (org-match-string-no-properties 4)) + (value (match-string-no-properties 4)) (post-blank (progn (goto-char (match-end 2)) (skip-chars-forward " \t"))) (end (point))) @@ -3704,9 +3771,8 @@ Assume point is at the first equal sign marker." :end end :post-blank post-blank)))))) -(defun org-element-verbatim-interpreter (verbatim contents) - "Interpret VERBATIM object as Org syntax. -CONTENTS is nil." +(defun org-element-verbatim-interpreter (verbatim _) + "Interpret VERBATIM object as Org syntax." (format "=%s=" (org-element-property :value verbatim))) @@ -3808,27 +3874,35 @@ element it has to parse." ;; Keywords. ((looking-at "[ \t]*#") (goto-char (match-end 0)) - (cond ((looking-at "\\(?: \\|$\\)") - (beginning-of-line) - (org-element-comment-parser limit affiliated)) - ((looking-at "\\+BEGIN_\\(\\S-+\\)") - (beginning-of-line) - (let ((parser (assoc (upcase (match-string 1)) - org-element-block-name-alist))) - (if parser (funcall (cdr parser) limit affiliated) - (org-element-special-block-parser limit affiliated)))) - ((looking-at "\\+CALL:") - (beginning-of-line) - (org-element-babel-call-parser limit affiliated)) - ((looking-at "\\+BEGIN:? ") - (beginning-of-line) - (org-element-dynamic-block-parser limit affiliated)) - ((looking-at "\\+\\S-+:") - (beginning-of-line) - (org-element-keyword-parser limit affiliated)) - (t - (beginning-of-line) - (org-element-paragraph-parser limit affiliated)))) + (cond + ((looking-at "\\(?: \\|$\\)") + (beginning-of-line) + (org-element-comment-parser limit affiliated)) + ((looking-at "\\+BEGIN_\\(\\S-+\\)") + (beginning-of-line) + (funcall (pcase (upcase (match-string 1)) + ("CENTER" #'org-element-center-block-parser) + ("COMMENT" #'org-element-comment-block-parser) + ("EXAMPLE" #'org-element-example-block-parser) + ("EXPORT" #'org-element-export-block-parser) + ("QUOTE" #'org-element-quote-block-parser) + ("SRC" #'org-element-src-block-parser) + ("VERSE" #'org-element-verse-block-parser) + (_ #'org-element-special-block-parser)) + limit + affiliated)) + ((looking-at "\\+CALL:") + (beginning-of-line) + (org-element-babel-call-parser limit affiliated)) + ((looking-at "\\+BEGIN:? ") + (beginning-of-line) + (org-element-dynamic-block-parser limit affiliated)) + ((looking-at "\\+\\S-+:") + (beginning-of-line) + (org-element-keyword-parser limit affiliated)) + (t + (beginning-of-line) + (org-element-paragraph-parser limit affiliated)))) ;; Footnote Definition. ((looking-at org-footnote-definition-re) (org-element-footnote-definition-parser limit affiliated)) @@ -3893,7 +3967,7 @@ position of point and CDR is nil." (dualp (member kwd org-element-dual-keywords)) (dual-value (and dualp - (let ((sec (org-match-string-no-properties 2))) + (let ((sec (match-string-no-properties 2))) (if (or (not sec) (not parsedp)) sec (save-match-data (org-element--parse-objects @@ -4010,14 +4084,11 @@ If STRING is the empty string or nil, return nil." (dolist (v local-variables) (ignore-errors (if (symbolp v) (makunbound v) - (org-set-local (car v) (cdr v))))) + (set (make-local-variable (car v)) (cdr v))))) (insert string) (restore-buffer-modified-p nil) - (let ((data (org-element--parse-objects - (point-min) (point-max) nil restriction))) - (when parent - (dolist (o data) (org-element-put-property o :parent parent))) - data)))))) + (org-element--parse-objects + (point-min) (point-max) nil restriction parent)))))) (defun org-element-map (data types fun &optional info first-match no-recursion with-affiliated) @@ -4087,94 +4158,93 @@ looking into captions: (list no-recursion))) ;; Recursion depth is determined by --CATEGORY. (--category - (catch 'found + (catch :--found (let ((category 'greater-elements) (all-objects (cons 'plain-text org-element-all-objects))) (dolist (type types category) (cond ((memq type all-objects) - ;; If one object is found, the function has to - ;; recurse into every object. - (throw 'found 'objects)) + ;; If one object is found, the function has + ;; to recurse into every object. + (throw :--found 'objects)) ((not (memq type org-element-greater-elements)) ;; If one regular element is found, the ;; function has to recurse, at least, into ;; every element it encounters. (and (not (eq category 'elements)) (setq category 'elements)))))))) - --acc - --walk-tree - (--walk-tree - (lambda (--data) - ;; Recursively walk DATA. INFO, if non-nil, is a plist - ;; holding contextual information. - (let ((--type (org-element-type --data))) - (cond - ((not --data)) - ;; Ignored element in an export context. - ((and info (memq --data (plist-get info :ignore-list)))) - ;; List of elements or objects. - ((not --type) (mapc --walk-tree --data)) - ;; Unconditionally enter parse trees. - ((eq --type 'org-data) - (mapc --walk-tree (org-element-contents --data))) - (t - ;; Check if TYPE is matching among TYPES. If so, - ;; apply FUN to --DATA and accumulate return value - ;; into --ACC (or exit if FIRST-MATCH is non-nil). - (when (memq --type types) - (let ((result (funcall fun --data))) - (cond ((not result)) - (first-match (throw '--map-first-match result)) - (t (push result --acc))))) - ;; If --DATA has a secondary string that can contain - ;; objects with their type among TYPES, look into it. - (when (and (eq --category 'objects) (not (stringp --data))) - (dolist (p (cdr (assq --type - org-element-secondary-value-alist))) - (funcall --walk-tree (org-element-property p --data)))) - ;; If --DATA has any parsed affiliated keywords and - ;; WITH-AFFILIATED is non-nil, look for objects in - ;; them. - (when (and with-affiliated - (eq --category 'objects) - (memq --type org-element-all-elements)) - (dolist (kwd-pair org-element--parsed-properties-alist) - (let ((kwd (car kwd-pair)) - (value (org-element-property (cdr kwd-pair) --data))) - ;; Pay attention to the type of parsed keyword. - ;; In particular, preserve order for multiple - ;; keywords. - (cond - ((not value)) - ((member kwd org-element-dual-keywords) - (if (member kwd org-element-multiple-keywords) - (dolist (line (reverse value)) - (funcall --walk-tree (cdr line)) - (funcall --walk-tree (car line))) - (funcall --walk-tree (cdr value)) - (funcall --walk-tree (car value)))) - ((member kwd org-element-multiple-keywords) - (mapc --walk-tree (reverse value))) - (t (funcall --walk-tree value)))))) - ;; Determine if a recursion into --DATA is possible. - (cond - ;; --TYPE is explicitly removed from recursion. - ((memq --type no-recursion)) - ;; --DATA has no contents. - ((not (org-element-contents --data))) - ;; Looking for greater elements but --DATA is simply - ;; an element or an object. - ((and (eq --category 'greater-elements) - (not (memq --type org-element-greater-elements)))) - ;; Looking for elements but --DATA is an object. - ((and (eq --category 'elements) - (memq --type org-element-all-objects))) - ;; In any other case, map contents. - (t (mapc --walk-tree (org-element-contents --data)))))))))) - (catch '--map-first-match - (funcall --walk-tree data) - ;; Return value in a proper order. - (nreverse --acc)))) + --acc) + (letrec ((--walk-tree + (lambda (--data) + ;; Recursively walk DATA. INFO, if non-nil, is a plist + ;; holding contextual information. + (let ((--type (org-element-type --data))) + (cond + ((not --data)) + ;; Ignored element in an export context. + ((and info (memq --data (plist-get info :ignore-list)))) + ;; List of elements or objects. + ((not --type) (mapc --walk-tree --data)) + ;; Unconditionally enter parse trees. + ((eq --type 'org-data) + (mapc --walk-tree (org-element-contents --data))) + (t + ;; Check if TYPE is matching among TYPES. If so, + ;; apply FUN to --DATA and accumulate return value + ;; into --ACC (or exit if FIRST-MATCH is non-nil). + (when (memq --type types) + (let ((result (funcall fun --data))) + (cond ((not result)) + (first-match (throw :--map-first-match result)) + (t (push result --acc))))) + ;; If --DATA has a secondary string that can contain + ;; objects with their type among TYPES, look inside. + (when (and (eq --category 'objects) (not (stringp --data))) + (dolist (p (cdr (assq --type + org-element-secondary-value-alist))) + (funcall --walk-tree (org-element-property p --data)))) + ;; If --DATA has any parsed affiliated keywords and + ;; WITH-AFFILIATED is non-nil, look for objects in + ;; them. + (when (and with-affiliated + (eq --category 'objects) + (eq (org-element-class --data) 'element)) + (dolist (kwd-pair org-element--parsed-properties-alist) + (let ((kwd (car kwd-pair)) + (value (org-element-property (cdr kwd-pair) --data))) + ;; Pay attention to the type of parsed + ;; keyword. In particular, preserve order for + ;; multiple keywords. + (cond + ((not value)) + ((member kwd org-element-dual-keywords) + (if (member kwd org-element-multiple-keywords) + (dolist (line (reverse value)) + (funcall --walk-tree (cdr line)) + (funcall --walk-tree (car line))) + (funcall --walk-tree (cdr value)) + (funcall --walk-tree (car value)))) + ((member kwd org-element-multiple-keywords) + (mapc --walk-tree (reverse value))) + (t (funcall --walk-tree value)))))) + ;; Determine if a recursion into --DATA is possible. + (cond + ;; --TYPE is explicitly removed from recursion. + ((memq --type no-recursion)) + ;; --DATA has no contents. + ((not (org-element-contents --data))) + ;; Looking for greater elements but --DATA is + ;; simply an element or an object. + ((and (eq --category 'greater-elements) + (not (memq --type org-element-greater-elements)))) + ;; Looking for elements but --DATA is an object. + ((and (eq --category 'elements) + (eq (org-element-class --data) 'object))) + ;; In any other case, map contents. + (t (mapc --walk-tree (org-element-contents --data)))))))))) + (catch :--map-first-match + (funcall --walk-tree data) + ;; Return value in a proper order. + (nreverse --acc))))) (put 'org-element-map 'lisp-indent-function 2) ;; The following functions are internal parts of the parser. @@ -4195,21 +4265,21 @@ otherwise. Modes can be either `first-section', `item', `node-property', `planning', `property-drawer', `section', `table-row' or nil." (if parentp - (case type - (headline 'section) - (inlinetask 'planning) - (plain-list 'item) - (property-drawer 'node-property) - (section 'planning) - (table 'table-row)) - (case type - (item 'item) - (node-property 'node-property) - (planning 'property-drawer) - (table-row 'table-row)))) + (pcase type + (`headline 'section) + (`inlinetask 'planning) + (`plain-list 'item) + (`property-drawer 'node-property) + (`section 'planning) + (`table 'table-row)) + (pcase type + (`item 'item) + (`node-property 'node-property) + (`planning 'property-drawer) + (`table-row 'table-row)))) (defun org-element--parse-elements - (beg end mode structure granularity visible-only acc) + (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 @@ -4235,49 +4305,49 @@ Elements are accumulated into ACC." ;; 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))) - ;; Main loop start. - (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)))) - (org-element-adopt-elements acc element) - ;; Update mode. - (setq mode (org-element--next-mode type nil)))) - ;; Return result. - acc)) + (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)))) + ;; Return result. + (apply #'org-element-set-contents acc (nreverse elements))))) (defun org-element--object-lex (restriction) "Return next object in current buffer or nil. @@ -4285,27 +4355,41 @@ RESTRICTION is a list of object types, as symbols, that should be looked after. This function assumes that the buffer is narrowed to an appropriate container (e.g., a paragraph)." (if (memq 'table-cell restriction) (org-element-table-cell-parser) - (save-excursion - (let ((limit (and org-target-link-regexp - (save-excursion - (or (bolp) (backward-char)) - (re-search-forward org-target-link-regexp nil t)) - (match-beginning 1))) - found) + (let* ((start (point)) + (limit + (save-excursion + (cond ((not org-target-link-regexp) nil) + ((not (memq 'link restriction)) nil) + ((progn + (unless (bolp) (forward-char -1)) + (not (re-search-forward org-target-link-regexp nil t))) + nil) + ;; Since we moved backward, we do not want to + ;; match again an hypothetical 1-character long + ;; radio link before us. Realizing that this can + ;; only happen if such a radio link starts at + ;; beginning of line, we prevent this here. + ((and (= start (1+ (line-beginning-position))) + (= start (match-end 1))) + (and (re-search-forward org-target-link-regexp nil t) + (match-beginning 1))) + (t (match-beginning 1))))) + found) + (save-excursion (while (and (not found) - (re-search-forward org-element--object-regexp limit t)) + (re-search-forward org-element--object-regexp limit 'move)) (goto-char (match-beginning 0)) (let ((result (match-string 0))) (setq found (cond - ((eq (compare-strings result nil nil "call_" nil nil t) t) + ((string-prefix-p "call_" result t) (and (memq 'inline-babel-call restriction) (org-element-inline-babel-call-parser))) - ((eq (compare-strings result nil nil "src_" nil nil t) t) + ((string-prefix-p "src_" result t) (and (memq 'inline-src-block restriction) (org-element-inline-src-block-parser))) (t - (case (char-after) + (pcase (char-after) (?^ (and (memq 'superscript restriction) (org-element-superscript-parser))) (?_ (or (and (memq 'subscript restriction) @@ -4336,7 +4420,8 @@ to an appropriate container (e.g., a paragraph)." (org-element-target-parser))) (or (and (memq 'timestamp restriction) (org-element-timestamp-parser)) - (and (memq 'link restriction) + (and (or (memq 'link restriction) + (memq 'simple-link restriction)) (org-element-link-parser))))) (?\\ (if (eq (aref result 1) ?\\) @@ -4357,60 +4442,63 @@ to an appropriate container (e.g., a paragraph)." (and (memq 'statistics-cookie restriction) (org-element-statistics-cookie-parser))))) ;; This is probably a plain link. - (otherwise (and (or (memq 'link restriction) - (memq 'plain-link restriction)) - (org-element-link-parser))))))) + (_ (and (or (memq 'link restriction) + (memq 'simple-link restriction)) + (org-element-link-parser))))))) (or (eobp) (forward-char)))) (cond (found) - ;; Radio link. - ((and limit (memq 'link restriction)) - (goto-char limit) (org-element-link-parser))))))) + (limit (org-element-link-parser)) ;radio link + (t nil)))))) -(defun org-element--parse-objects (beg end acc restriction) +(defun org-element--parse-objects (beg end acc restriction &optional parent) "Parse objects between BEG and END and return recursive structure. -Objects are accumulated in ACC. +Objects are accumulated in ACC. RESTRICTION is a list of object +successors which are allowed in the current object. -RESTRICTION is a list of object successors which are allowed in -the current object." +ACC becomes the parent for all parsed objects. However, if ACC +is nil (i.e., a secondary string is being parsed) and optional +argument PARENT is non-nil, use it as the parent for all objects. +Eventually, if both ACC and PARENT are nil, the common parent is +the list of objects itself." (save-excursion (save-restriction (narrow-to-region beg end) (goto-char (point-min)) - (let (next-object) + (let (next-object contents) (while (and (not (eobp)) (setq next-object (org-element--object-lex restriction))) - ;; 1. Text before any object. Untabify it. + ;; Text before any object. (let ((obj-beg (org-element-property :begin next-object))) (unless (= (point) obj-beg) - (setq acc - (org-element-adopt-elements - acc - (replace-regexp-in-string - "\t" (make-string tab-width ? ) - (buffer-substring-no-properties (point) obj-beg)))))) - ;; 2. Object... + (let ((text (buffer-substring-no-properties (point) obj-beg))) + (push (if acc (org-element-put-property text :parent acc) text) + contents)))) + ;; Object... (let ((obj-end (org-element-property :end next-object)) (cont-beg (org-element-property :contents-begin next-object))) - ;; Fill contents of NEXT-OBJECT by side-effect, if it has - ;; a recursive type. - (when (and cont-beg - (memq (car next-object) org-element-recursive-objects)) - (org-element--parse-objects - cont-beg (org-element-property :contents-end next-object) - next-object (org-element-restriction next-object))) - (setq acc (org-element-adopt-elements acc next-object)) - (goto-char obj-end)))) - ;; 3. Text after last object. Untabify it. - (unless (eobp) - (setq acc - (org-element-adopt-elements - acc - (replace-regexp-in-string - "\t" (make-string tab-width ? ) - (buffer-substring-no-properties (point) end))))) - ;; Result. - acc))) + (when acc (org-element-put-property next-object :parent acc)) + (push (if cont-beg + ;; Fill contents of NEXT-OBJECT if possible. + (org-element--parse-objects + cont-beg + (org-element-property :contents-end next-object) + next-object + (org-element-restriction next-object)) + next-object) + contents) + (goto-char obj-end))) + ;; Text after last object. + (unless (eobp) + (let ((text (buffer-substring-no-properties (point) end))) + (push (if acc (org-element-put-property text :parent acc) text) + contents))) + ;; Result. Set appropriate parent. + (if acc (apply #'org-element-set-contents acc (nreverse contents)) + (let* ((contents (nreverse contents)) + (parent (or parent contents))) + (dolist (datum contents contents) + (org-element-put-property datum :parent parent)))))))) @@ -4429,73 +4517,70 @@ the current object." "Interpret DATA as Org syntax. DATA is a parse tree, an element, an object or a secondary string to interpret. Return Org syntax as a string." - (org-element--interpret-data-1 data nil)) - -(defun org-element--interpret-data-1 (data parent) - "Interpret DATA as Org syntax. - -DATA is a parse tree, an element, an object or a secondary string -to interpret. PARENT is used for recursive calls. It contains -the element or object containing data, or nil. - -Return Org syntax as a string." - (let* ((type (org-element-type data)) - ;; Find interpreter for current object or element. If it - ;; doesn't exist (e.g. this is a pseudo object or element), - ;; return contents, if any. - (interpret - (let ((fun (intern (format "org-element-%s-interpreter" type)))) - (if (fboundp fun) fun (lambda (data contents) contents)))) - (results - (cond - ;; Secondary string. - ((not type) - (mapconcat - (lambda (obj) (org-element--interpret-data-1 obj parent)) data "")) - ;; Full Org document. - ((eq type 'org-data) - (mapconcat (lambda (obj) (org-element--interpret-data-1 obj parent)) - (org-element-contents data) "")) - ;; Plain text: return it. - ((stringp data) data) - ;; Element or object without contents. - ((not (org-element-contents data)) (funcall interpret data nil)) - ;; Element or object with contents. - (t - (funcall interpret data - ;; Recursively interpret contents. - (mapconcat - (lambda (obj) (org-element--interpret-data-1 obj data)) - (org-element-contents - (if (not (memq type '(paragraph verse-block))) - data - ;; Fix indentation of elements containing - ;; objects. We ignore `table-row' elements - ;; as they are one line long anyway. - (org-element-normalize-contents - data - ;; When normalizing first paragraph of an - ;; item or a footnote-definition, ignore - ;; first line's indentation. - (and (eq type 'paragraph) - (equal data (car (org-element-contents parent))) - (memq (org-element-type parent) - '(footnote-definition item)))))) - "")))))) - (if (memq type '(org-data plain-text nil)) results - ;; Build white spaces. If no `:post-blank' property is - ;; specified, assume its value is 0. - (let ((post-blank (or (org-element-property :post-blank data) 0))) - (if (or (memq type org-element-all-objects) - (and parent - (let ((type (org-element-type parent))) - (or (not type) - (memq type org-element-object-containers))))) - (concat results (make-string post-blank ?\s)) - (concat - (org-element--interpret-affiliated-keywords data) - (org-element-normalize-string results) - (make-string post-blank ?\n))))))) + (letrec ((fun + (lambda (data parent) + (let* ((type (org-element-type data)) + ;; Find interpreter for current object or + ;; element. If it doesn't exist (e.g. this is + ;; a pseudo object or element), return contents, + ;; if any. + (interpret + (let ((fun (intern + (format "org-element-%s-interpreter" type)))) + (if (fboundp fun) fun (lambda (_ contents) contents)))) + (results + (cond + ;; Secondary string. + ((not type) + (mapconcat (lambda (obj) (funcall fun obj parent)) + data + "")) + ;; Full Org document. + ((eq type 'org-data) + (mapconcat (lambda (obj) (funcall fun obj parent)) + (org-element-contents data) + "")) + ;; Plain text: return it. + ((stringp data) data) + ;; Element or object without contents. + ((not (org-element-contents data)) + (funcall interpret data nil)) + ;; Element or object with contents. + (t + (funcall + interpret + data + ;; Recursively interpret contents. + (mapconcat + (lambda (datum) (funcall fun datum data)) + (org-element-contents + (if (not (memq type '(paragraph verse-block))) + data + ;; Fix indentation of elements containing + ;; objects. We ignore `table-row' + ;; elements as they are one line long + ;; anyway. + (org-element-normalize-contents + data + ;; When normalizing first paragraph of + ;; an item or a footnote-definition, + ;; ignore first line's indentation. + (and (eq type 'paragraph) + (memq (org-element-type parent) + '(footnote-definition item)) + (eq data + (car (org-element-contents parent))))))) + "")))))) + (if (memq type '(org-data plain-text nil)) results + ;; Build white spaces. If no `:post-blank' property + ;; is specified, assume its value is 0. + (let ((blank (or (org-element-property :post-blank data) 0))) + (if (eq (org-element-class data parent) 'object) + (concat results (make-string blank ?\s)) + (concat (org-element--interpret-affiliated-keywords data) + (org-element-normalize-string results) + (make-string blank ?\n))))))))) + (funcall fun data nil))) (defun org-element--interpret-affiliated-keywords (element) "Return ELEMENT's affiliated keywords as Org syntax. @@ -4529,14 +4614,14 @@ If there is no affiliated keyword, return the empty string." ;; List all ELEMENT's properties matching an attribute line or an ;; affiliated keyword, but ignore translated keywords since they ;; cannot belong to the property list. - (loop for prop in (nth 1 element) by 'cddr - when (let ((keyword (upcase (substring (symbol-name prop) 1)))) - (or (string-match "^ATTR_" keyword) - (and - (member keyword org-element-affiliated-keywords) - (not (assoc keyword - org-element-keyword-translation-alist))))) - collect prop) + (cl-loop for prop in (nth 1 element) by 'cddr + when (let ((keyword (upcase (substring (symbol-name prop) 1)))) + (or (string-match "^ATTR_" keyword) + (and + (member keyword org-element-affiliated-keywords) + (not (assoc keyword + org-element-keyword-translation-alist))))) + collect prop) ""))) ;; Because interpretation of the parse tree must return the same @@ -4572,75 +4657,89 @@ If optional argument IGNORE-FIRST is non-nil, ignore first line's indentation to compute maximal common indentation. Return the normalized element that is element with global -indentation removed from its contents. The function assumes that -indentation is not done with TAB characters." - (let* ((min-ind most-positive-fixnum) - find-min-ind ; For byte-compiler. - (find-min-ind - ;; Return minimal common indentation within BLOB. This is - ;; done by walking recursively BLOB and updating MIN-IND - ;; along the way. FIRST-FLAG is non-nil when the next - ;; object is expected to be a string that doesn't start with - ;; a newline character. It happens for strings at the - ;; beginnings of the contents or right after a line break. - (lambda (blob first-flag) - (dolist (object (org-element-contents blob)) - (when first-flag - (setq first-flag nil) - ;; Objects cannot start with spaces: in this case, - ;; indentation is 0. - (if (not (stringp object)) (throw 'zero (setq min-ind 0)) - (string-match "\\` *" object) - (let ((len (match-end 0))) - ;; An indentation of zero means no string will be - ;; modified. Quit the process. - (if (zerop len) (throw 'zero (setq min-ind 0)) - (setq min-ind (min len min-ind)))))) - (cond - ((stringp object) - (dolist (line (cdr (org-split-string object " *\n"))) - (unless (string= line "") - (setq min-ind (min (org-get-indentation line) min-ind))))) - ((eq (org-element-type object) 'line-break) (setq first-flag t)) - ((memq (org-element-type object) org-element-recursive-objects) - (funcall find-min-ind object first-flag))))))) - ;; Find minimal indentation in ELEMENT. - (catch 'zero (funcall find-min-ind element (not ignore-first))) +indentation removed from its contents." + (letrec ((find-min-ind + ;; Return minimal common indentation within BLOB. This is + ;; done by walking recursively BLOB and updating MIN-IND + ;; along the way. FIRST-FLAG is non-nil when the next + ;; object is expected to be a string that doesn't start + ;; with a newline character. It happens for strings at + ;; the beginnings of the contents or right after a line + ;; break. + (lambda (blob first-flag min-ind) + (catch 'zero + (dolist (datum (org-element-contents blob) min-ind) + (when first-flag + (setq first-flag nil) + (cond + ;; Objects cannot start with spaces: in this + ;; case, indentation is 0. + ((not (stringp datum)) (throw 'zero 0)) + ((not (string-match + "\\`\\([ \t]+\\)\\([^ \t\n]\\|\n\\|\\'\\)" datum)) + (throw 'zero 0)) + ((equal (match-string 2 datum) "\n") + (put-text-property + (match-beginning 1) (match-end 1) 'org-ind 'empty datum)) + (t + (let ((i (string-width (match-string 1 datum)))) + (put-text-property + (match-beginning 1) (match-end 1) 'org-ind i datum) + (setq min-ind (min i min-ind)))))) + (cond + ((stringp datum) + (let ((s 0)) + (while (string-match + "\n\\([ \t]+\\)\\([^ \t\n]\\|\n\\|\\'\\)" datum s) + (setq s (match-end 1)) + (if (equal (match-string 2 datum) "\n") + (put-text-property + (match-beginning 1) (match-end 1) + 'org-ind 'empty + datum) + (let ((i (string-width (match-string 1 datum)))) + (put-text-property + (match-beginning 1) (match-end 1) 'org-ind i datum) + (setq min-ind (min i min-ind))))))) + ((eq (org-element-type datum) 'line-break) + (setq first-flag t)) + ((memq (org-element-type datum) org-element-recursive-objects) + (setq min-ind + (funcall find-min-ind datum first-flag min-ind)))))))) + (min-ind (funcall find-min-ind + element (not ignore-first) most-positive-fixnum))) (if (or (zerop min-ind) (= min-ind most-positive-fixnum)) element ;; Build ELEMENT back, replacing each string with the same ;; string minus common indentation. - (let* (build ; For byte compiler. - (build - (lambda (blob first-flag) - ;; Return BLOB with all its strings indentation - ;; shortened from MIN-IND white spaces. FIRST-FLAG is - ;; non-nil when the next object is expected to be - ;; a string that doesn't start with a newline - ;; character. - (setcdr (cdr blob) - (mapcar - (lambda (object) - (when first-flag - (setq first-flag nil) - (when (stringp object) - (setq object - (replace-regexp-in-string - (format "\\` \\{%d\\}" min-ind) - "" object)))) - (cond - ((stringp object) - (replace-regexp-in-string - (format "\n \\{%d\\}" min-ind) "\n" object)) - ((memq (org-element-type object) - org-element-recursive-objects) - (funcall build object first-flag)) - ((eq (org-element-type object) 'line-break) - (setq first-flag t) - object) - (t object))) - (org-element-contents blob))) - blob))) - (funcall build element (not ignore-first)))))) + (letrec ((build + (lambda (datum) + ;; Return DATUM with all its strings indentation + ;; shortened from MIN-IND white spaces. + (setcdr + (cdr datum) + (mapcar + (lambda (object) + (cond + ((stringp object) + (with-temp-buffer + (insert object) + (let ((s (point-min))) + (while (setq s (text-property-not-all + s (point-max) 'org-ind nil)) + (goto-char s) + (let ((i (get-text-property s 'org-ind))) + (delete-region s (progn + (skip-chars-forward " \t") + (point))) + (when (integerp i) (indent-to (- i min-ind)))))) + (buffer-string))) + ((memq (org-element-type object) + org-element-recursive-objects) + (funcall build object)) + (t object))) + (org-element-contents datum))) + datum))) + (funcall build element))))) @@ -4722,7 +4821,7 @@ with `org-element--cache-compare'. This cache is used in Key is an element, as returned by `org-element-at-point', and value is an alist where each association is: - \(PARENT COMPLETEP . OBJECTS) + (PARENT COMPLETEP . OBJECTS) where PARENT is an element or object, COMPLETEP is a boolean, non-nil when all direct children of parent are already cached and @@ -4736,12 +4835,12 @@ contained within a paragraph If the paragraph is completely parsed, OBJECTS-DATA will be - \((PARAGRAPH t BOLD-OBJECT ENTITY-OBJECT) - \(BOLD-OBJECT t ENTITY-OBJECT)) + ((PARAGRAPH t BOLD-OBJECT ENTITY-OBJECT) + (BOLD-OBJECT t ENTITY-OBJECT)) whereas in a partially parsed paragraph, it could be - \((PARAGRAPH nil ENTITY-OBJECT)) + ((PARAGRAPH nil ENTITY-OBJECT)) This cache is used in `org-element-context'.") @@ -4830,16 +4929,16 @@ the following rules: gets a new level. Its value is the mean between LOWER and UPPER: - \(1 2) + (1 4) --> (1 3) + (1 2) + (1 4) --> (1 3) - If LOWER has no value to compare with, it is assumed that its value is `most-negative-fixnum'. E.g., - \(1 1) + (1 1 2) + (1 1) + (1 1 2) is equivalent to - \(1 1 m) + (1 1 2) + (1 1 m) + (1 1 2) where m is `most-negative-fixnum'. Likewise, if UPPER is short of levels, the current value is `most-positive-fixnum'. @@ -4847,18 +4946,18 @@ the following rules: - If they differ from only one, the new key inherits from current LOWER level and fork it at the next level. E.g., - \(2 1) + (3 3) + (2 1) + (3 3) is equivalent to - \(2 1) + (2 M) + (2 1) + (2 M) where M is `most-positive-fixnum'. - If the key is only one level long, it is returned as an integer: - \(1 2) + (3 2) --> 2 + (1 2) + (3 2) --> 2 When they are not equals, the function assumes that LOWER is lesser than UPPER, per `org-element--cache-key-less-p'." @@ -4976,10 +5075,10 @@ the cache." (setq node nil lower element upper element))))) - (case side - (both (cons lower upper)) - ((nil) lower) - (otherwise upper)))) + (pcase side + (`both (cons lower upper)) + (`nil lower) + (_ upper)))) (defun org-element--cache-put (element &optional data) "Store ELEMENT in current buffer's cache, if allowed. @@ -5053,8 +5152,8 @@ Properties are modified by side-effect." (not (eq (org-element-type (plist-get properties :parent)) 'item))) (dolist (item (plist-get properties :structure)) - (incf (car item) offset) - (incf (nth 6 item) offset))) + (cl-incf (car item) offset) + (cl-incf (nth 6 item) offset))) (dolist (key '(:begin :contents-begin :contents-end :end :post-affiliated)) (let ((value (and (or (not props) (memq key props)) (plist-get properties key)))) @@ -5093,7 +5192,7 @@ updated before current modification are actually submitted." ;; Request processed. Merge current and next offsets and ;; transfer ending position. (when next - (incf (aref next 3) (aref request 3)) + (cl-incf (aref next 3) (aref request 3)) (aset next 2 (aref request 2))) (setq org-element--cache-sync-requests (cdr org-element--cache-sync-requests)))) @@ -5483,12 +5582,12 @@ that range. See `after-change-functions' for more information." ;; to both previous and current state. We make a special ;; case for headline editing: if a headline is modified but ;; not removed, do not extend. - (when (case org-element--cache-change-warning - ((t) t) - (headline + (when (pcase org-element--cache-change-warning + (`t t) + (`headline (not (and (org-with-limited-levels (org-at-heading-p)) (= (line-end-position) bottom)))) - (otherwise + (_ (let ((case-fold-search t)) (re-search-forward org-element--cache-sensitive-re bottom t)))) @@ -5569,7 +5668,7 @@ change, as an integer." ;; Current changes can be merged with first sync request: we ;; can save a partial cache synchronization. (progn - (incf (aref next 3) offset) + (cl-incf (aref next 3) offset) ;; If last change happened within area to be removed, extend ;; boundaries of robust parents, if any. Otherwise, find ;; first element to remove and update request accordingly. @@ -5619,7 +5718,8 @@ change, as an integer." ;; No element to remove. No need to re-parent either. ;; Simply shift additional elements, if any, by OFFSET. (when org-element--cache-sync-requests - (incf (aref (car org-element--cache-sync-requests) 3) offset))))))) + (cl-incf (aref (car org-element--cache-sync-requests) 3) + offset))))))) ;;;; Public Functions @@ -5633,14 +5733,14 @@ buffers." (dolist (buffer (if all (buffer-list) (list (current-buffer)))) (with-current-buffer buffer (when (and org-element-use-cache (derived-mode-p 'org-mode)) - (org-set-local 'org-element--cache - (avl-tree-create #'org-element--cache-compare)) - (org-set-local 'org-element--cache-objects (make-hash-table :test #'eq)) - (org-set-local 'org-element--cache-sync-keys - (make-hash-table :weakness 'key :test #'eq)) - (org-set-local 'org-element--cache-change-warning nil) - (org-set-local 'org-element--cache-sync-requests nil) - (org-set-local 'org-element--cache-sync-timer nil) + (setq-local org-element--cache + (avl-tree-create #'org-element--cache-compare)) + (setq-local org-element--cache-objects (make-hash-table :test #'eq)) + (setq-local org-element--cache-sync-keys + (make-hash-table :weakness 'key :test #'eq)) + (setq-local org-element--cache-change-warning nil) + (setq-local org-element--cache-sync-requests nil) + (setq-local org-element--cache-sync-timer nil) (add-hook 'before-change-functions #'org-element--cache-before-change nil t) (add-hook 'after-change-functions @@ -5772,15 +5872,16 @@ Providing it allows for quicker computation." (throw 'objects-forbidden element))))) ;; At an headline or inlinetask, objects are in title. ((memq type '(headline inlinetask)) - (goto-char (org-element-property :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 (let (case-fold-search) (looking-at org-comment-string)) - (goto-char (match-end 0))) - (if (>= (point) end) (throw 'objects-forbidden element) - (narrow-to-region (point) end))))) + (let ((case-fold-search nil)) + (goto-char (org-element-property :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-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)) @@ -5988,7 +6089,7 @@ end of ELEM-A." (goto-char beg-B) (when specialp (setq body-B (replace-regexp-in-string "\\`[ \t]*" "" body-B)) - (org-indent-to-column ind-B)) + (indent-to-column ind-B)) (insert body-A) ;; Restore ex ELEM-A overlays. (let ((offset (- beg-B beg-A))) @@ -6002,36 +6103,6 @@ end of ELEM-A." (move-overlay (car o) (- (nth 1 o) offset) (- (nth 2 o) offset)))) (goto-char (org-element-property :end elem-B))))) -(defun org-element-remove-indentation (s &optional n) - "Remove maximum common indentation in string S and return it. -When optional argument N is a positive integer, remove exactly -that much characters from indentation, if possible, or return -S as-is otherwise. Unlike to `org-remove-indentation', this -function doesn't call `untabify' on S." - (catch 'exit - (with-temp-buffer - (insert s) - (goto-char (point-min)) - ;; Find maximum common indentation, if not specified. - (setq n (or n - (let ((min-ind (point-max))) - (save-excursion - (while (re-search-forward "^[ \t]*\\S-" nil t) - (let ((ind (1- (current-column)))) - (if (zerop ind) (throw 'exit s) - (setq min-ind (min min-ind ind)))))) - min-ind))) - (if (zerop n) s - ;; Remove exactly N indentation, but give up if not possible. - (while (not (eobp)) - (let ((ind (progn (skip-chars-forward " \t") (current-column)))) - (cond ((eolp) (delete-region (line-beginning-position) (point))) - ((< ind n) (throw 'exit s)) - (t (org-indent-line-to (- ind n)))) - (forward-line))) - (buffer-string))))) - - (provide 'org-element) |