forked from mirrors/org-mode
contrib/lisp/org-export: Improve footnote API
* contrib/lisp/org-export.el (org-export-initial-options): Rename `:footnotes-labels-alist' property into `:footnote-definition-alist' for consistency. Add comments to code. (org-export-persistent-properties-list): Apply previous renaming. (org-export-update-info): Rename `:seen-footnote-labels' into `footnote-seen-labels'. (org-export-collect-footnote-definitions, org-export-footnote-first-reference-p, org-export-get-footnote-definition, org-export-get-footnote-number): New functions.
This commit is contained in:
parent
314e2647d7
commit
27480a2ee7
|
@ -579,12 +579,25 @@ while every other back-end will ignore it."
|
||||||
;; - category :: option
|
;; - category :: option
|
||||||
;; - type :: list of strings
|
;; - type :: list of strings
|
||||||
|
|
||||||
;; + `footnotes-labels-alist' :: Alist between footnote labels and
|
;; + `footnote-definition-alist' :: Alist between footnote labels and
|
||||||
;; their definition, as parsed data. Once retrieved, the
|
;; their definition, as parsed data. Only non-inlined footnotes
|
||||||
;; definition should be exported with `org-export-data'.
|
;; are represented in this alist. Also, every definition isn't
|
||||||
|
;; guaranteed to be referenced in the parse tree. The purpose of
|
||||||
|
;; this property is to preserve definitions from oblivion
|
||||||
|
;; (i.e. when the parse tree comes from a part of the original
|
||||||
|
;; buffer), it isn't meant for direct use in a back-end. To
|
||||||
|
;; retrieve a definition relative to a reference, use
|
||||||
|
;; `org-export-get-footnote-definition' instead.
|
||||||
;; - category :: option
|
;; - category :: option
|
||||||
;; - type :: alist (STRING . LIST)
|
;; - type :: alist (STRING . LIST)
|
||||||
|
|
||||||
|
;; + `footnote-seen-labels' :: List of already transcoded footnote
|
||||||
|
;; labels. It is used to know when a reference appears for the
|
||||||
|
;; first time. (cf. `org-export-footnote-first-reference-p').
|
||||||
|
;; - category :: persistent
|
||||||
|
;; - type :: list of strings
|
||||||
|
;; - update :: `org-export-update-info'
|
||||||
|
|
||||||
;; + `genealogy' :: List of current element's parents types.
|
;; + `genealogy' :: List of current element's parents types.
|
||||||
;; - category :: local
|
;; - category :: local
|
||||||
;; - type :: list of symbols
|
;; - type :: list of symbols
|
||||||
|
@ -673,12 +686,6 @@ while every other back-end will ignore it."
|
||||||
;; - category :: option
|
;; - category :: option
|
||||||
;; - type :: symbol (nil, t)
|
;; - type :: symbol (nil, t)
|
||||||
|
|
||||||
;; + `seen-footnote-labels' :: List of already transcoded footnote
|
|
||||||
;; labels.
|
|
||||||
;; - category :: persistent
|
|
||||||
;; - type :: list of strings
|
|
||||||
;; - update :: `org-export-update-info'
|
|
||||||
|
|
||||||
;; + `select-tags' :: List of tags enforcing inclusion of sub-trees in
|
;; + `select-tags' :: List of tags enforcing inclusion of sub-trees in
|
||||||
;; transcoding. When such a tag is present,
|
;; transcoding. When such a tag is present,
|
||||||
;; subtrees without it are de facto excluded from
|
;; subtrees without it are de facto excluded from
|
||||||
|
@ -1030,6 +1037,10 @@ BACKEND is a symbol specifying which back-end should be used."
|
||||||
"Return a plist with non-optional properties.
|
"Return a plist with non-optional properties.
|
||||||
OPTIONS is the export options plist computed so far."
|
OPTIONS is the export options plist computed so far."
|
||||||
(list
|
(list
|
||||||
|
;; `:macro-date', `:macro-time' and `:macro-property' could as well
|
||||||
|
;; be initialized as persistent properties, since they don't depend
|
||||||
|
;; on initial environment. Though, it may be more logical to keep
|
||||||
|
;; them close to other ":macro-" properties.
|
||||||
:macro-date "(eval (format-time-string \"$1\"))"
|
:macro-date "(eval (format-time-string \"$1\"))"
|
||||||
:macro-time "(eval (format-time-string \"$1\"))"
|
:macro-time "(eval (format-time-string \"$1\"))"
|
||||||
:macro-property "(eval (org-entry-get nil \"$1\" 'selective))"
|
:macro-property "(eval (org-entry-get nil \"$1\" 'selective))"
|
||||||
|
@ -1041,18 +1052,22 @@ OPTIONS is the export options plist computed so far."
|
||||||
"))"))
|
"))"))
|
||||||
:macro-input-file (and (buffer-file-name)
|
:macro-input-file (and (buffer-file-name)
|
||||||
(file-name-nondirectory (buffer-file-name)))
|
(file-name-nondirectory (buffer-file-name)))
|
||||||
:footnotes-labels-alist
|
;; Footnotes definitions must be collected in the original buffer,
|
||||||
|
;; as there's no insurance that they will still be in the parse
|
||||||
|
;; tree, due to some narrowing.
|
||||||
|
:footnote-definition-alist
|
||||||
(let (alist)
|
(let (alist)
|
||||||
(org-with-wide-buffer
|
(org-with-wide-buffer
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (re-search-forward org-footnote-definition-re nil t)
|
(while (re-search-forward org-footnote-definition-re nil t)
|
||||||
(let ((def (org-footnote-at-definition-p)))
|
(let ((def (org-footnote-at-definition-p)))
|
||||||
|
(when def
|
||||||
(org-skip-whitespace)
|
(org-skip-whitespace)
|
||||||
(push (cons (car def)
|
(push (cons (car def)
|
||||||
(save-restriction
|
(save-restriction
|
||||||
(narrow-to-region (point) (nth 2 def))
|
(narrow-to-region (point) (nth 2 def))
|
||||||
(org-element-parse-buffer)))
|
(org-element-parse-buffer)))
|
||||||
alist)))
|
alist))))
|
||||||
alist))))
|
alist))))
|
||||||
|
|
||||||
(defvar org-export-allow-BIND-local nil)
|
(defvar org-export-allow-BIND-local nil)
|
||||||
|
@ -1102,7 +1117,7 @@ retrieved."
|
||||||
|
|
||||||
(defconst org-export-persistent-properties-list
|
(defconst org-export-persistent-properties-list
|
||||||
'(:back-end :code-refs :headline-alist :headline-numbering :headline-offset
|
'(:back-end :code-refs :headline-alist :headline-numbering :headline-offset
|
||||||
:parse-tree :point-max :seen-footnote-labels :target-list
|
:parse-tree :point-max :footnote-seen-labels :target-list
|
||||||
:total-loc :use-select-tags)
|
:total-loc :use-select-tags)
|
||||||
"List of persistent properties.")
|
"List of persistent properties.")
|
||||||
|
|
||||||
|
@ -1278,6 +1293,8 @@ When RECURSEP is non-nil, assume the following element or object
|
||||||
will be inside the current one.
|
will be inside the current one.
|
||||||
|
|
||||||
The following properties are updated:
|
The following properties are updated:
|
||||||
|
`footnote-seen-labels' List of already parsed footnote
|
||||||
|
labels (string list)
|
||||||
`genealogy' List of current element's parents
|
`genealogy' List of current element's parents
|
||||||
(symbol list).
|
(symbol list).
|
||||||
`inherited-properties' List of inherited properties from
|
`inherited-properties' List of inherited properties from
|
||||||
|
@ -1286,8 +1303,6 @@ The following properties are updated:
|
||||||
(plist).
|
(plist).
|
||||||
`previous-element' Previous element's type (symbol).
|
`previous-element' Previous element's type (symbol).
|
||||||
`previous-object' Previous object's type (symbol).
|
`previous-object' Previous object's type (symbol).
|
||||||
`seen-footnote-labels' List of already parsed footnote
|
|
||||||
labels (string list)
|
|
||||||
|
|
||||||
Return the property list."
|
Return the property list."
|
||||||
(let* ((type (and (not (stringp blob)) (car blob))))
|
(let* ((type (and (not (stringp blob)) (car blob))))
|
||||||
|
@ -1317,12 +1332,12 @@ Return the property list."
|
||||||
(when (eq type 'footnote-reference)
|
(when (eq type 'footnote-reference)
|
||||||
(let ((label (org-element-get-property :label blob))
|
(let ((label (org-element-get-property :label blob))
|
||||||
(seen-labels (plist-get org-export-persistent-properties
|
(seen-labels (plist-get org-export-persistent-properties
|
||||||
:seen-footnote-labels)))
|
:footnote-seen-labels)))
|
||||||
;; Store anonymous footnotes (nil label) without checking if
|
;; Store anonymous footnotes (nil label) without checking if
|
||||||
;; another anonymous footnote was seen before.
|
;; another anonymous footnote was seen before.
|
||||||
(unless (and label (member label seen-labels))
|
(unless (and label (member label seen-labels))
|
||||||
(setq info (org-export-set-property
|
(setq info (org-export-set-property
|
||||||
info :seen-footnote-labels (push label seen-labels))))))
|
info :footnote-seen-labels (push label seen-labels))))))
|
||||||
;; Set `:previous-element' or `:previous-object' according to
|
;; Set `:previous-element' or `:previous-object' according to
|
||||||
;; BLOB.
|
;; BLOB.
|
||||||
(setq info (cond ((not type)
|
(setq info (cond ((not type)
|
||||||
|
@ -2074,9 +2089,80 @@ Point is at buffer's beginning when BODY is applied."
|
||||||
;; function general enough to have its use across many back-ends
|
;; function general enough to have its use across many back-ends
|
||||||
;; should be added here.
|
;; should be added here.
|
||||||
|
|
||||||
;; As of now, functions operating on headlines, include keywords,
|
;; As of now, functions operating on footnotes, headlines, include
|
||||||
;; links, macros, references, src-blocks, tables and tables of
|
;; keywords, links, macros, references, src-blocks, tables and tables
|
||||||
;; contents are implemented.
|
;; of contents are implemented.
|
||||||
|
|
||||||
|
;;;; For Footnotes
|
||||||
|
|
||||||
|
;; `org-export-collect-footnote-definitions' is a tool to list
|
||||||
|
;; actually used footnotes definitions in the whole parse tree, or in
|
||||||
|
;; an headline, in order to add footnote listings throughout the
|
||||||
|
;; transcoded data.
|
||||||
|
|
||||||
|
;; `org-export-footnote-first-reference-p' is a predicate used by some
|
||||||
|
;; back-ends, when they need to attach the footnote definition only to
|
||||||
|
;; the first occurrence of the corresponding label.
|
||||||
|
|
||||||
|
;; `org-export-get-footnote-definition' and
|
||||||
|
;; `org-export-get-footnote-number' provide easier access to
|
||||||
|
;; additional information relative to a footnote reference.
|
||||||
|
|
||||||
|
(defun org-export-collect-footnote-definitions (data info)
|
||||||
|
"Return an alist between footnote label and its definition.
|
||||||
|
|
||||||
|
DATA is the parse tree from which definitions are collected.
|
||||||
|
INFO is the plist used as a communication channel.
|
||||||
|
|
||||||
|
As anonymous footnotes have no label, the key used is that case
|
||||||
|
is their beginning position.
|
||||||
|
|
||||||
|
Definitions are sorted by order of references. They either
|
||||||
|
appear as Org data \(transcoded with `org-export-data'\) or as
|
||||||
|
a secondary string for inlined footnotes \(transcoded with
|
||||||
|
`org-export-secondary-string'\). Unreferenced definitions are
|
||||||
|
ignored."
|
||||||
|
(org-element-map
|
||||||
|
data 'footnote-reference
|
||||||
|
(lambda (footnote local)
|
||||||
|
(cond
|
||||||
|
;; Definition already collected.
|
||||||
|
((not (org-export-footnote-first-reference-p footnote local)) nil)
|
||||||
|
;; Reference has a label: Use it as a key, and get the
|
||||||
|
;; corresponding definition.
|
||||||
|
((org-element-get-property :label footnote)
|
||||||
|
(cons (org-element-get-property :label footnote)
|
||||||
|
(org-export-get-footnote-definition footnote local)))
|
||||||
|
;; No label: This is an anonymous footnote. Use beginning
|
||||||
|
;; position as the key and inline definition (a secondary
|
||||||
|
;; string) as its value.
|
||||||
|
(t (cons (org-element-get-property :begin footnote)
|
||||||
|
(org-element-get-property :inline-definition footnote)))))
|
||||||
|
info))
|
||||||
|
|
||||||
|
(defun org-export-footnote-first-reference-p (footnote-reference info)
|
||||||
|
"Non-nil when a footnote reference is the first one for its label.
|
||||||
|
|
||||||
|
FOOTNOTE-REFERENCE is the footnote reference being considered.
|
||||||
|
INFO is the plist used as a communication channel."
|
||||||
|
(let ((label (org-element-get-property :label footnote-reference)))
|
||||||
|
(not (and label (member label (plist-get info :footnote-seen-labels))))))
|
||||||
|
|
||||||
|
(defun org-export-get-footnote-definition (footnote-reference info)
|
||||||
|
"Return definition of FOOTNOTE-REFERENCE as parsed data.
|
||||||
|
INFO is the plist used as a communication channel."
|
||||||
|
(let ((label (org-element-get-property :label footnote-reference)))
|
||||||
|
(or (org-element-get-property :inline-definition footnote-reference)
|
||||||
|
(cdr (assoc label (plist-get info :footnote-definition-alist))))))
|
||||||
|
|
||||||
|
(defun org-export-get-footnote-number (footnote-reference info)
|
||||||
|
"Return footnote number associated to FOOTNOTE-REFERENCE.
|
||||||
|
INFO is the plist used as a communication channel."
|
||||||
|
(let* ((all-seen (plist-get info :footnote-seen-labels))
|
||||||
|
(label (org-element-get-property :label footnote-reference))
|
||||||
|
;; Anonymous footnotes are always new footnotes.
|
||||||
|
(seenp (and label (member label all-seen))))
|
||||||
|
(if seenp (length seenp) (1+ (length all-seen)))))
|
||||||
|
|
||||||
|
|
||||||
;;;; For Headlines
|
;;;; For Headlines
|
||||||
|
|
Loading…
Reference in New Issue