diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index c58135dcd..509b6c28d 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -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