summaryrefslogtreecommitdiff
path: root/lisp/org/org-element.el
diff options
context:
space:
mode:
authorKyle Meyer <kyle@kyleam.com>2021-09-29 18:48:59 -0400
committerKyle Meyer <kyle@kyleam.com>2021-09-29 23:21:21 -0400
commitbf9ec3d91a79414deac039f7bf83352a9b0a9a85 (patch)
tree5e636992801ca408a26f7b7532c666d24c80020e /lisp/org/org-element.el
parentdc94ca7b2b878c9a88be72fea118bf6557259ffd (diff)
downloademacs-bf9ec3d91a79414deac039f7bf83352a9b0a9a85.tar.gz
Update to Org 9.5
Diffstat (limited to 'lisp/org/org-element.el')
-rw-r--r--lisp/org/org-element.el268
1 files changed, 222 insertions, 46 deletions
diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el
index 31f5f78eae0..1c971b2f8a4 100644
--- a/lisp/org/org-element.el
+++ b/lisp/org/org-element.el
@@ -117,6 +117,19 @@
;; `org-element-update-syntax' builds proper syntax regexps according
;; to current setup.
+(defconst org-element-citation-key-re
+ (rx "@" (group (one-or-more (any word "-.:?!`'/*@+|(){}<>&_^$#%&~"))))
+ "Regexp matching a citation key.
+Key is located in match group 1.")
+
+(defconst org-element-citation-prefix-re
+ (rx "[cite"
+ (opt "/" (group (one-or-more (any "/_-" alnum)))) ;style
+ ":"
+ (zero-or-more (any "\t\n ")))
+ "Regexp matching a citation prefix.
+Style, if any, is located in match group 1.")
+
(defvar org-element-paragraph-separate nil
"Regexp to separate paragraphs in an Org buffer.
In the case of lines starting with \"#\" and \":\", this regexp
@@ -182,15 +195,17 @@ specially in `org-element--object-lex'.")
(nth 2 org-emphasis-regexp-components)))
;; Plain links.
(concat "\\<" link-types ":")
- ;; Objects starting with "[": regular link,
+ ;; Objects starting with "[": citations,
;; footnote reference, statistics cookie,
- ;; timestamp (inactive).
- (concat "\\[\\(?:"
- "fn:" "\\|"
- "\\[" "\\|"
- "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" "\\|"
- "[0-9]*\\(?:%\\|/[0-9]*\\)\\]"
- "\\)")
+ ;; timestamp (inactive) and regular link.
+ (format "\\[\\(?:%s\\)"
+ (mapconcat
+ #'identity
+ (list "cite[:/]"
+ "fn:"
+ "\\(?:[0-9]\\|\\(?:%\\|/[0-9]*\\)\\]\\)"
+ "\\[")
+ "\\|"))
;; Objects starting with "@": export snippets.
"@@"
;; Objects starting with "{": macro.
@@ -234,15 +249,15 @@ specially in `org-element--object-lex'.")
"List of recursive element types aka Greater Elements.")
(defconst org-element-all-objects
- '(bold code entity export-snippet footnote-reference inline-babel-call
- inline-src-block italic line-break latex-fragment link macro
- radio-target statistics-cookie strike-through subscript superscript
- table-cell target timestamp underline verbatim)
+ '(bold citation citation-reference code entity export-snippet
+ footnote-reference inline-babel-call inline-src-block italic line-break
+ latex-fragment link macro radio-target statistics-cookie strike-through
+ subscript superscript table-cell target timestamp underline verbatim)
"Complete list of object types.")
(defconst org-element-recursive-objects
- '(bold footnote-reference italic link subscript radio-target strike-through
- superscript table-cell underline)
+ '(bold citation footnote-reference italic link subscript radio-target
+ strike-through superscript table-cell underline)
"List of recursive object types.")
(defconst org-element-object-containers
@@ -331,9 +346,12 @@ Don't modify it, set `org-element-affiliated-keywords' instead.")
(defconst org-element-object-restrictions
(let* ((minimal-set '(bold code entity italic latex-fragment strike-through
subscript superscript underline verbatim))
- (standard-set (remq 'table-cell org-element-all-objects))
+ (standard-set
+ (remq 'citation-reference (remq 'table-cell org-element-all-objects)))
(standard-set-no-line-break (remq 'line-break standard-set)))
`((bold ,@standard-set)
+ (citation citation-reference)
+ (citation-reference ,@minimal-set)
(footnote-reference ,@standard-set)
(headline ,@standard-set-no-line-break)
(inlinetask ,@standard-set-no-line-break)
@@ -354,8 +372,8 @@ Don't modify it, set `org-element-affiliated-keywords' instead.")
;; Ignore inline babel call and inline source block as formulas
;; are possible. Also ignore line breaks and statistics
;; cookies.
- (table-cell export-snippet footnote-reference link macro radio-target
- target timestamp ,@minimal-set)
+ (table-cell citation export-snippet footnote-reference link macro
+ radio-target target timestamp ,@minimal-set)
(table-row table-cell)
(underline ,@standard-set)
(verse-block ,@standard-set)))
@@ -370,9 +388,11 @@ This alist also applies to secondary string. For example, an
still has an entry since one of its properties (`:title') does.")
(defconst org-element-secondary-value-alist
- '((headline :title)
+ '((citation :prefix :suffix)
+ (headline :title)
(inlinetask :title)
- (item :tag))
+ (item :tag)
+ (citation-reference :prefix :suffix))
"Alist between element types and locations of secondary values.")
(defconst org-element--pair-round-table
@@ -737,7 +757,9 @@ Return a list whose CAR is `drawer' and CDR is a plist containing
Assume point is at beginning of drawer."
(let ((case-fold-search t))
- (if (not (save-excursion (re-search-forward "^[ \t]*:END:[ \t]*$" limit t)))
+ (if (not (save-excursion
+ (goto-char (min limit (line-end-position)))
+ (re-search-forward "^[ \t]*:END:[ \t]*$" limit t)))
;; Incomplete drawer: parse it as a paragraph.
(org-element-paragraph-parser limit affiliated)
(save-excursion
@@ -999,7 +1021,10 @@ Assume point is at beginning of the headline."
(commentedp
(and (let (case-fold-search) (looking-at org-comment-string))
(goto-char (match-end 0))))
- (title-start (point))
+ (title-start (prog1 (point)
+ (unless (or todo priority commentedp)
+ ;; Headline like "* :tag:"
+ (skip-syntax-backward " \t"))))
(tags (when (re-search-forward
"[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$"
(line-end-position)
@@ -2751,6 +2776,129 @@ CONTENTS is the contents of the object."
(format "*%s*" contents))
+;;;; Citation
+
+(defun org-element-citation-parser ()
+ "Parse citation object at point, if any.
+
+When at a citation object, return a list whose car is `citation'
+and cdr is a plist with `:style', `:prefix', `:suffix', `:begin',
+`:end', `:contents-begin', `:contents-end', and `:post-blank'
+keywords. Otherwise, return nil.
+
+Assume point is at the beginning of the citation."
+ (when (looking-at org-element-citation-prefix-re)
+ (let* ((begin (point))
+ (style (and (match-end 1)
+ (match-string-no-properties 1)))
+ ;; Ignore blanks between cite type and prefix or key.
+ (start (match-end 0))
+ (closing (with-syntax-table org-element--pair-square-table
+ (ignore-errors (scan-lists begin 1 0)))))
+ (save-excursion
+ (when (and closing
+ (re-search-forward org-element-citation-key-re closing t))
+ ;; Find prefix, if any.
+ (let ((first-key-end (match-end 0))
+ (types (org-element-restriction 'citation-reference))
+ (cite
+ (list 'citation
+ (list :style style
+ :begin begin
+ :post-blank (progn
+ (goto-char closing)
+ (skip-chars-forward " \t"))
+ :end (point)))))
+ ;; `:contents-begin' depends on the presence of
+ ;; a non-empty common prefix.
+ (goto-char first-key-end)
+ (if (not (search-backward ";" start t))
+ (org-element-put-property cite :contents-begin start)
+ (when (< start (point))
+ (org-element-put-property
+ cite :prefix
+ (org-element--parse-objects start (point) nil types cite)))
+ (forward-char)
+ (org-element-put-property cite :contents-begin (point)))
+ ;; `:contents-end' depends on the presence of a non-empty
+ ;; common suffix.
+ (goto-char (1- closing))
+ (skip-chars-backward " \r\t\n")
+ (let ((end (point)))
+ (if (or (not (search-backward ";" first-key-end t))
+ (re-search-forward org-element-citation-key-re end t))
+ (org-element-put-property cite :contents-end end)
+ (forward-char)
+ (when (< (point) end)
+ (org-element-put-property
+ cite :suffix
+ (org-element--parse-objects (point) end nil types cite)))
+ (org-element-put-property cite :contents-end (point))))
+ cite))))))
+
+(defun org-element-citation-interpreter (citation contents)
+ "Interpret CITATION object as Org syntax.
+CONTENTS is the contents of the object, as a string."
+ (let ((prefix (org-element-property :prefix citation))
+ (suffix (org-element-property :suffix citation))
+ (style (org-element-property :style citation)))
+ (concat "[cite"
+ (and style (concat "/" style))
+ ":"
+ (and prefix (concat (org-element-interpret-data prefix) ";"))
+ (if suffix
+ (concat contents (org-element-interpret-data suffix))
+ ;; Remove spurious semicolon.
+ (substring contents nil -1))
+ "]")))
+
+
+;;;; Citation Reference
+
+(defun org-element-citation-reference-parser ()
+ "Parse citation reference object at point, if any.
+
+When at a reference, return a list whose car is
+`citation-reference', and cdr is a plist with `:key',
+`:prefix', `:suffix', `:begin', `:end', and `:post-blank' keywords.
+
+Assume point is at the beginning of the reference."
+ (save-excursion
+ (let ((begin (point)))
+ (when (re-search-forward org-element-citation-key-re nil t)
+ (let* ((key (match-string-no-properties 1))
+ (key-start (match-beginning 0))
+ (key-end (match-end 0))
+ (separator (search-forward ";" nil t))
+ (end (or separator (point-max)))
+ (suffix-end (if separator (1- end) end))
+ (types (org-element-restriction 'citation-reference))
+ (reference
+ (list 'citation-reference
+ (list :key key
+ :begin begin
+ :end end
+ :post-blank 0))))
+ (when (< begin key-start)
+ (org-element-put-property
+ reference :prefix
+ (org-element--parse-objects begin key-start nil types reference)))
+ (when (< key-end suffix-end)
+ (org-element-put-property
+ reference :suffix
+ (org-element--parse-objects key-end suffix-end nil types reference)))
+ reference)))))
+
+(defun org-element-citation-reference-interpreter (citation-reference _)
+ "Interpret CITATION-REFERENCE object as Org syntax."
+ (concat (org-element-interpret-data
+ (org-element-property :prefix citation-reference))
+ "@" (org-element-property :key citation-reference)
+ (org-element-interpret-data
+ (org-element-property :suffix citation-reference))
+ ";"))
+
+
;;;; Code
(defun org-element-code-parser ()
@@ -3951,14 +4099,36 @@ element it has to parse."
;; There is no strict definition of a table.el
;; table. Try to prevent false positive while being
;; quick.
- (let ((rule-regexp "[ \t]*\\+\\(-+\\+\\)+[ \t]*$")
+ (let ((rule-regexp
+ (rx (zero-or-more (any " \t"))
+ "+"
+ (one-or-more (one-or-more "-") "+")
+ (zero-or-more (any " \t"))
+ eol))
+ (non-table.el-line
+ (rx bol
+ (zero-or-more (any " \t"))
+ (or eol (not (any "+| \t")))))
(next (line-beginning-position 2)))
- (and (looking-at rule-regexp)
- (save-excursion
- (forward-line)
- (re-search-forward "^[ \t]*\\($\\|[^|]\\)" limit t)
- (and (> (line-beginning-position) next)
- (org-match-line rule-regexp))))))
+ ;; Start with a full rule.
+ (and
+ (looking-at rule-regexp)
+ (< next limit) ;no room for a table.el table
+ (save-excursion
+ (end-of-line)
+ (cond
+ ;; Must end with a full rule.
+ ((not (re-search-forward non-table.el-line limit 'move))
+ (if (bolp) (forward-line -1) (beginning-of-line))
+ (looking-at rule-regexp))
+ ;; Ignore pseudo-tables with a single
+ ;; rule.
+ ((= next (line-beginning-position))
+ nil)
+ ;; Must end with a full rule.
+ (t
+ (forward-line -1)
+ (looking-at rule-regexp)))))))
(org-element-table-parser limit affiliated))
;; List.
((looking-at (org-item-re))
@@ -4322,7 +4492,7 @@ element or object. Meaningful values are `first-section',
TYPE is the type of the current element or object.
If PARENT? is non-nil, assume the next element or object will be
-located inside the current one. "
+located inside the current one."
(if parent?
(pcase type
(`headline 'section)
@@ -4413,7 +4583,11 @@ Elements are accumulated into ACC."
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)
+ (cond
+ ((memq 'table-cell restriction) (org-element-table-cell-parser))
+ ((memq 'citation-reference restriction)
+ (org-element-citation-reference-parser))
+ (t
(let* ((start (point))
(limit
;; Object regexp sometimes needs to have a peek at
@@ -4501,6 +4675,9 @@ to an appropriate container (e.g., a paragraph)."
((and ?f
(guard (memq 'footnote-reference restriction)))
(org-element-footnote-reference-parser))
+ ((and ?c
+ (guard (memq 'citation restriction)))
+ (org-element-citation-parser))
((and (or ?% ?/)
(guard (memq 'statistics-cookie restriction)))
(org-element-statistics-cookie-parser))
@@ -4515,8 +4692,8 @@ to an appropriate container (e.g., a paragraph)."
(or (eobp) (forward-char))))
(cond (found)
(limit (forward-char -1)
- (org-element-link-parser)) ;radio link
- (t nil))))))
+ (org-element-link-parser)) ;radio link
+ (t nil)))))))
(defun org-element--parse-objects (beg end acc restriction &optional parent)
"Parse objects between BEG and END and return recursive structure.
@@ -4640,7 +4817,7 @@ to interpret. Return Org syntax as a string."
(eq (org-element-property :pre-blank parent)
0)))))
""))))))
- (if (memq type '(org-data plain-text nil)) results
+ (if (memq type '(org-data 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)))
@@ -4655,19 +4832,18 @@ to interpret. Return Org syntax as a string."
"Return ELEMENT's affiliated keywords as Org syntax.
If there is no affiliated keyword, return the empty string."
(let ((keyword-to-org
- (function
- (lambda (key value)
- (let (dual)
- (when (member key org-element-dual-keywords)
- (setq dual (cdr value) value (car value)))
- (concat "#+" (downcase key)
- (and dual
- (format "[%s]" (org-element-interpret-data dual)))
- ": "
- (if (member key org-element-parsed-keywords)
- (org-element-interpret-data value)
- value)
- "\n"))))))
+ (lambda (key value)
+ (let (dual)
+ (when (member key org-element-dual-keywords)
+ (setq dual (cdr value) value (car value)))
+ (concat "#+" (downcase key)
+ (and dual
+ (format "[%s]" (org-element-interpret-data dual)))
+ ": "
+ (if (member key org-element-parsed-keywords)
+ (org-element-interpret-data value)
+ value)
+ "\n")))))
(mapconcat
(lambda (prop)
(let ((value (org-element-property prop element))