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:
Nicolas Goaziou 2011-12-23 14:46:20 +01:00
parent 314e2647d7
commit 27480a2ee7
1 changed files with 110 additions and 24 deletions

View File

@ -579,12 +579,25 @@ while every other back-end will ignore it."
;; - category :: option
;; - type :: list of strings
;; + `footnotes-labels-alist' :: Alist between footnote labels and
;; their definition, as parsed data. Once retrieved, the
;; definition should be exported with `org-export-data'.
;; + `footnote-definition-alist' :: Alist between footnote labels and
;; their definition, as parsed data. Only non-inlined footnotes
;; 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
;; - 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.
;; - category :: local
;; - type :: list of symbols
@ -673,12 +686,6 @@ while every other back-end will ignore it."
;; - category :: option
;; - 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
;; transcoding. When such a tag is present,
;; 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.
OPTIONS is the export options plist computed so far."
(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-time "(eval (format-time-string \"$1\"))"
: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)
(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)
(org-with-wide-buffer
(goto-char (point-min))
(while (re-search-forward org-footnote-definition-re nil t)
(let ((def (org-footnote-at-definition-p)))
(org-skip-whitespace)
(push (cons (car def)
(save-restriction
(narrow-to-region (point) (nth 2 def))
(org-element-parse-buffer)))
alist)))
(when def
(org-skip-whitespace)
(push (cons (car def)
(save-restriction
(narrow-to-region (point) (nth 2 def))
(org-element-parse-buffer)))
alist))))
alist))))
(defvar org-export-allow-BIND-local nil)
@ -1102,7 +1117,7 @@ retrieved."
(defconst org-export-persistent-properties-list
'(: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)
"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.
The following properties are updated:
`footnote-seen-labels' List of already parsed footnote
labels (string list)
`genealogy' List of current element's parents
(symbol list).
`inherited-properties' List of inherited properties from
@ -1286,8 +1303,6 @@ The following properties are updated:
(plist).
`previous-element' Previous element'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."
(let* ((type (and (not (stringp blob)) (car blob))))
@ -1317,12 +1332,12 @@ Return the property list."
(when (eq type 'footnote-reference)
(let ((label (org-element-get-property :label blob))
(seen-labels (plist-get org-export-persistent-properties
:seen-footnote-labels)))
:footnote-seen-labels)))
;; Store anonymous footnotes (nil label) without checking if
;; another anonymous footnote was seen before.
(unless (and label (member label seen-labels))
(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
;; BLOB.
(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
;; should be added here.
;; As of now, functions operating on headlines, include keywords,
;; links, macros, references, src-blocks, tables and tables of
;; contents are implemented.
;; As of now, functions operating on footnotes, headlines, include
;; keywords, links, macros, references, src-blocks, tables and tables
;; 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