summaryrefslogtreecommitdiff
path: root/lisp/org/org-footnote.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/org/org-footnote.el')
-rw-r--r--lisp/org/org-footnote.el611
1 files changed, 310 insertions, 301 deletions
diff --git a/lisp/org/org-footnote.el b/lisp/org/org-footnote.el
index f8963184654..0fe382819de 100644
--- a/lisp/org/org-footnote.el
+++ b/lisp/org/org-footnote.el
@@ -47,18 +47,16 @@
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(declare-function org-fill-paragraph "org" (&optional justify region))
(declare-function org-in-block-p "org" (names))
-(declare-function org-in-regexp "org" (re &optional nlines visually))
(declare-function org-in-verbatim-emphasis "org" ())
(declare-function org-inside-LaTeX-fragment-p "org" ())
(declare-function org-inside-latex-macro-p "org" ())
(declare-function org-mark-ring-push "org" (&optional pos buffer))
(declare-function org-show-context "org" (&optional key))
-(declare-function org-trim "org" (s &optional keep-lead))
(declare-function outline-next-heading "outline")
(defvar electric-indent-mode)
(defvar org-blank-before-new-entry) ; defined in org.el
-(defvar org-bracket-link-regexp) ; defined in org.el
+(defvar org-link-bracket-re) ; defined in org.el
(defvar org-complex-heading-regexp) ; defined in org.el
(defvar org-odd-levels-only) ; defined in org.el
(defvar org-outline-regexp) ; defined in org.el
@@ -116,7 +114,8 @@ you will need to run the following command after the change:
(org-element-cache-reset 'all)))
:type '(choice
(string :tag "Collect footnotes under heading")
- (const :tag "Define footnotes locally" nil)))
+ (const :tag "Define footnotes locally" nil))
+ :safe #'string-or-null-p)
(defcustom org-footnote-define-inline nil
"Non-nil means define footnotes inline, at reference location.
@@ -124,7 +123,8 @@ When nil, footnotes will be defined in a special section near
the end of the document. When t, the [fn:label:definition] notation
will be used to define the footnote at the reference position."
:group 'org-footnote
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-footnote-auto-label t
"Non-nil means define automatically new labels for footnotes.
@@ -141,7 +141,8 @@ random Automatically generate a unique, random label."
(const :tag "Prompt for label" nil)
(const :tag "Create automatic [fn:N]" t)
(const :tag "Offer automatic [fn:N] for editing" confirm)
- (const :tag "Create a random label" random)))
+ (const :tag "Create a random label" random))
+ :safe #'symbolp)
(defcustom org-footnote-auto-adjust nil
"Non-nil means automatically adjust footnotes after insert/delete.
@@ -159,7 +160,8 @@ The main values of this variable can be set with in-buffer options:
(const :tag "No adjustment" nil)
(const :tag "Renumber" renumber)
(const :tag "Sort" sort)
- (const :tag "Renumber and Sort" t)))
+ (const :tag "Renumber and Sort" t))
+ :safe #'symbolp)
(defcustom org-footnote-fill-after-inline-note-extraction nil
"Non-nil means fill paragraphs after extracting footnotes.
@@ -167,7 +169,8 @@ When extracting inline footnotes, the lengths of lines can change a lot.
When this option is set, paragraphs from which an inline footnote has been
extracted will be filled again."
:group 'org-footnote
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
;;;; Predicates
@@ -186,76 +189,53 @@ extracted will be filled again."
(org-in-block-p org-footnote-forbidden-blocks)))))
(defun org-footnote-at-reference-p ()
- "Is the cursor at a footnote reference?
-
+ "Non-nil if point is at a footnote reference.
If so, return a list containing its label, beginning and ending
-positions, and the definition, when inlined."
- (when (and (org-footnote-in-valid-context-p)
- (or (looking-at org-footnote-re)
- (org-in-regexp org-footnote-re)
- (save-excursion (re-search-backward org-footnote-re nil t)))
- (/= (match-beginning 0) (line-beginning-position)))
- (let* ((beg (match-beginning 0))
- (label (match-string-no-properties 1))
- ;; Inline footnotes don't end at (match-end 0) as
- ;; `org-footnote-re' stops just after the second colon.
- ;; Find the real ending with `scan-sexps', so Org doesn't
- ;; get fooled by unrelated closing square brackets.
- (end (ignore-errors (scan-sexps beg 1))))
- ;; Point is really at a reference if it's located before true
- ;; ending of the footnote.
- (when (and end
- (< (point) end)
- ;; Verify match isn't a part of a link.
- (not (save-excursion
- (goto-char beg)
- (let ((linkp
- (save-match-data
- (org-in-regexp org-bracket-link-regexp))))
- (and linkp (< (point) (cdr linkp))))))
- ;; Verify point doesn't belong to a LaTeX macro.
- (not (org-inside-latex-macro-p)))
- (list label beg end
- ;; Definition: ensure this is an inline footnote first.
- (and (match-end 2)
- (org-trim
- (buffer-substring-no-properties
- (match-end 0) (1- end)))))))))
+positions, and the definition, when inline."
+ (let ((reference (org-element-context)))
+ (when (eq 'footnote-reference (org-element-type reference))
+ (let ((end (save-excursion
+ (goto-char (org-element-property :end reference))
+ (skip-chars-backward " \t")
+ (point))))
+ (when (< (point) end)
+ (list (org-element-property :label reference)
+ (org-element-property :begin reference)
+ end
+ (and (eq 'inline (org-element-property :type reference))
+ (buffer-substring-no-properties
+ (org-element-property :contents-begin reference)
+ (org-element-property :contents-end
+ reference)))))))))
(defun org-footnote-at-definition-p ()
- "Is point within a footnote definition?
+ "Non-nil if point is within a footnote definition.
-This matches only pure definitions like [1] or [fn:name] at the
+This matches only pure definitions like [fn:name] at the
beginning of a line. It does not match references like
\[fn:name:definition], where the footnote text is included and
defined locally.
-The return value will be nil if not at a footnote definition, and
+The return value is nil if not at a footnote definition, and
a list with label, start, end and definition of the footnote
otherwise."
- (when (save-excursion (beginning-of-line) (org-footnote-in-valid-context-p))
- (save-excursion
- (end-of-line)
- ;; Footnotes definitions are separated by new headlines, another
- ;; footnote definition or 2 blank lines.
- (let ((lim (save-excursion
- (re-search-backward
- (concat org-outline-regexp-bol
- "\\|^\\([ \t]*\n\\)\\{2,\\}") nil t))))
- (when (re-search-backward org-footnote-definition-re lim t)
- (let ((label (match-string-no-properties 1))
- (beg (match-beginning 0))
- (beg-def (match-end 0))
- (end (if (progn
- (end-of-line)
- (re-search-forward
- (concat org-outline-regexp-bol "\\|"
- org-footnote-definition-re "\\|"
- "^\\([ \t]*\n\\)\\{2,\\}") nil 'move))
- (match-beginning 0)
- (point))))
- (list label beg end
- (org-trim (buffer-substring-no-properties beg-def end)))))))))
+ (pcase (org-element-lineage (org-element-at-point) '(footnote-definition) t)
+ (`nil nil)
+ (definition
+ (let* ((label (org-element-property :label definition))
+ (begin (org-element-property :post-affiliated definition))
+ (end (save-excursion
+ (goto-char (org-element-property :end definition))
+ (skip-chars-backward " \r\t\n")
+ (line-beginning-position 2)))
+ (contents-begin (org-element-property :contents-begin definition))
+ (contents-end (org-element-property :contents-end definition))
+ (contents
+ (if (not contents-begin) ""
+ (org-trim
+ (buffer-substring-no-properties contents-begin
+ contents-end)))))
+ (list label begin end contents)))))
;;;; Internal functions
@@ -313,23 +293,23 @@ otherwise."
(defun org-footnote--clear-footnote-section ()
"Remove all footnote sections in buffer and create a new one.
-New section is created at the end of the buffer, before any file
-local variable definition. Leave point within the new section."
+New section is created at the end of the buffer. Leave point
+within the new section."
(when org-footnote-section
(goto-char (point-min))
- (let ((regexp
- (format "^\\*+ +%s[ \t]*$"
- (regexp-quote org-footnote-section))))
+ (let ((regexp (format "^\\*+ +%s[ \t]*$"
+ (regexp-quote org-footnote-section))))
(while (re-search-forward regexp nil t)
(delete-region
(match-beginning 0)
- (progn (org-end-of-subtree t t)
- (if (not (eobp)) (point)
- (org-footnote--goto-local-insertion-point)
- (skip-chars-forward " \t\n")
- (if (eobp) (point) (line-beginning-position)))))))
+ (org-end-of-subtree t t))))
(goto-char (point-max))
- (org-footnote--goto-local-insertion-point)
+ ;; Clean-up blank lines at the end of the buffer.
+ (skip-chars-backward " \r\t\n")
+ (unless (bobp)
+ (forward-line)
+ (when (eolp) (insert "\n")))
+ (delete-region (point) (point-max))
(when (and (cdr (assq 'heading org-blank-before-new-entry))
(zerop (save-excursion (org-back-over-empty-lines))))
(insert "\n"))
@@ -448,14 +428,8 @@ while collecting them."
"Find insertion point for footnote, just before next outline heading.
Assume insertion point is within currently accessible part of the buffer."
(org-with-limited-levels (outline-next-heading))
- ;; Skip file local variables. See `modify-file-local-variable'.
- (when (eobp)
- (let ((case-fold-search t))
- (re-search-backward "^[ \t]*# +Local Variables:"
- (max (- (point-max) 3000) (point-min))
- t)))
(skip-chars-backward " \t\n")
- (forward-line)
+ (unless (bobp) (forward-line))
(unless (bolp) (insert "\n")))
@@ -470,16 +444,15 @@ the buffer position bounding the search.
Return value is a list like those provided by `org-footnote-at-reference-p'.
If no footnote is found, return nil."
- (save-excursion
- (let* ((label-fmt (if label (format "\\[fn:%s[]:]" label) org-footnote-re)))
- (catch 'exit
- (while t
- (unless (funcall (if backward #'re-search-backward #'re-search-forward)
- label-fmt limit t)
- (throw 'exit nil))
+ (let ((label-regexp (if label (format "\\[fn:%s[]:]" label) org-footnote-re)))
+ (catch :exit
+ (save-excursion
+ (while (funcall (if backward #'re-search-backward #'re-search-forward)
+ label-regexp limit t)
(unless backward (backward-char))
- (let ((ref (org-footnote-at-reference-p)))
- (when ref (throw 'exit ref))))))))
+ (pcase (org-footnote-at-reference-p)
+ (`nil nil)
+ (reference (throw :exit reference))))))))
(defun org-footnote-next-reference-or-definition (limit)
"Move point to next footnote reference or definition.
@@ -488,8 +461,10 @@ LIMIT is the buffer position bounding the search.
Return value is a list like those provided by
`org-footnote-at-reference-p' or `org-footnote-at-definition-p'.
-If no footnote is found, return nil."
- (let* (ref (origin (point)))
+If no footnote is found, return nil.
+
+This function is meant to be used for fontification only."
+ (let ((origin (point)))
(catch 'exit
(while t
(unless (re-search-forward org-footnote-re limit t)
@@ -499,15 +474,56 @@ If no footnote is found, return nil."
;; the closing square bracket.
(backward-char)
(cond
- ((setq ref (org-footnote-at-reference-p))
- (throw 'exit ref))
+ ((and (/= (match-beginning 0) (line-beginning-position))
+ (let* ((beg (match-beginning 0))
+ (label (match-string-no-properties 1))
+ ;; Inline footnotes don't end at (match-end 0)
+ ;; as `org-footnote-re' stops just after the
+ ;; second colon. Find the real ending with
+ ;; `scan-sexps', so Org doesn't get fooled by
+ ;; unrelated closing square brackets.
+ (end (ignore-errors (scan-sexps beg 1))))
+ (and end
+ ;; Verify match isn't a part of a link.
+ (not (save-excursion
+ (goto-char beg)
+ (let ((linkp
+ (save-match-data
+ (org-in-regexp org-link-bracket-re))))
+ (and linkp (< (point) (cdr linkp))))))
+ ;; Verify point doesn't belong to a LaTeX macro.
+ (not (org-inside-latex-macro-p))
+ (throw 'exit
+ (list label beg end
+ ;; Definition: ensure this is an
+ ;; inline footnote first.
+ (and (match-end 2)
+ (org-trim
+ (buffer-substring-no-properties
+ (match-end 0) (1- end))))))))))
;; Definition: also grab the last square bracket, matched in
;; `org-footnote-re' for non-inline footnotes.
- ((save-match-data (org-footnote-at-definition-p))
- (let ((end (match-end 0)))
- (throw 'exit
- (list nil (match-beginning 0)
- (if (eq (char-before end) ?\]) end (1+ end)))))))))))
+ ((and (save-excursion
+ (beginning-of-line)
+ (save-match-data (org-footnote-in-valid-context-p)))
+ (save-excursion
+ (end-of-line)
+ ;; Footnotes definitions are separated by new
+ ;; headlines, another footnote definition or 2 blank
+ ;; lines.
+ (let ((end (match-end 0))
+ (lim (save-excursion
+ (re-search-backward
+ (concat org-outline-regexp-bol
+ "\\|^\\([ \t]*\n\\)\\{2,\\}")
+ nil t))))
+ (and (re-search-backward org-footnote-definition-re lim t)
+ (throw 'exit
+ (list nil
+ (match-beginning 0)
+ (if (eq (char-before end) ?\]) end
+ (1+ end)))))))))
+ (t nil))))))
(defun org-footnote-goto-definition (label &optional location)
"Move point to the definition of the footnote LABEL.
@@ -528,7 +544,7 @@ value if point was successfully moved."
(user-error "Definition is outside narrowed part of buffer")))
(org-mark-ring-push)
(goto-char def-start)
- (looking-at (format "\\[fn:%s[]:] ?" (regexp-quote label)))
+ (looking-at (format "\\[fn:%s[]:]" (regexp-quote label)))
(goto-char (match-end 0))
(org-show-context 'link-search)
(when (derived-mode-p 'org-mode)
@@ -540,21 +556,23 @@ value if point was successfully moved."
(defun org-footnote-goto-previous-reference (label)
"Find the first closest (to point) reference of footnote with label LABEL."
(interactive "sLabel: ")
- (org-mark-ring-push)
- (let ((label (org-footnote-normalize-label label))
- ref)
- (save-excursion
- (setq ref (or (org-footnote-get-next-reference label t)
- (org-footnote-get-next-reference label)
- (save-restriction
- (widen)
- (or
- (org-footnote-get-next-reference label t)
- (org-footnote-get-next-reference label))))))
- (if (not ref)
- (error "Cannot find reference of footnote %s" label)
- (goto-char (nth 1 ref))
- (org-show-context 'link-search))))
+ (let* ((label (org-footnote-normalize-label label))
+ (reference
+ (save-excursion
+ (or (org-footnote-get-next-reference label t)
+ (org-footnote-get-next-reference label)
+ (and (buffer-narrowed-p)
+ (org-with-wide-buffer
+ (or (org-footnote-get-next-reference label t)
+ (org-footnote-get-next-reference label)))))))
+ (start (nth 1 reference)))
+ (cond ((not reference)
+ (user-error "Cannot find reference of footnote %S" label))
+ ((or (> start (point-max)) (< start (point-min)))
+ (user-error "Reference is outside narrowed part of buffer")))
+ (org-mark-ring-push)
+ (goto-char start)
+ (org-show-context 'link-search)))
;;;; Getters
@@ -676,21 +694,22 @@ Return buffer position at the beginning of the definition. This
function doesn't move point."
(let ((label (org-footnote-normalize-label label))
electric-indent-mode) ; Prevent wrong indentation.
- (org-with-wide-buffer
- (cond
- ((not org-footnote-section) (org-footnote--goto-local-insertion-point))
- ((save-excursion
- (goto-char (point-min))
- (re-search-forward
- (concat "^\\*+[ \t]+" (regexp-quote org-footnote-section) "[ \t]*$")
- nil t))
- (goto-char (match-end 0))
- (forward-line)
- (unless (bolp) (insert "\n")))
- (t (org-footnote--clear-footnote-section)))
- (when (zerop (org-back-over-empty-lines)) (insert "\n"))
- (insert "[fn:" label "] \n")
- (line-beginning-position 0))))
+ (org-preserve-local-variables
+ (org-with-wide-buffer
+ (cond
+ ((not org-footnote-section) (org-footnote--goto-local-insertion-point))
+ ((save-excursion
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^\\*+[ \t]+" (regexp-quote org-footnote-section) "[ \t]*$")
+ nil t))
+ (goto-char (match-end 0))
+ (forward-line)
+ (unless (bolp) (insert "\n")))
+ (t (org-footnote--clear-footnote-section)))
+ (when (zerop (org-back-over-empty-lines)) (insert "\n"))
+ (insert "[fn:" label "] \n")
+ (line-beginning-position 0)))))
(defun org-footnote-delete-references (label)
"Delete every reference to footnote LABEL.
@@ -733,31 +752,32 @@ and all references of a footnote label.
If LABEL is non-nil, delete that footnote instead."
(catch 'done
- (let* ((nref 0) (ndef 0) x
- ;; 1. Determine LABEL of footnote at point.
- (label (cond
- ;; LABEL is provided as argument.
- (label)
- ;; Footnote reference at point. If the footnote is
- ;; anonymous, delete it and exit instead.
- ((setq x (org-footnote-at-reference-p))
- (or (car x)
- (progn
- (delete-region (nth 1 x) (nth 2 x))
- (message "Anonymous footnote removed")
- (throw 'done t))))
- ;; Footnote definition at point.
- ((setq x (org-footnote-at-definition-p))
- (car x))
- (t (error "Don't know which footnote to remove")))))
- ;; 2. Now that LABEL is non-nil, find every reference and every
- ;; definition, and delete them.
- (setq nref (org-footnote-delete-references label)
- ndef (org-footnote-delete-definitions label))
- ;; 3. Verify consistency of footnotes and notify user.
- (org-footnote-auto-adjust-maybe)
- (message "%d definition(s) of and %d reference(s) of footnote %s removed"
- ndef nref label))))
+ (org-preserve-local-variables
+ (let* ((nref 0) (ndef 0) x
+ ;; 1. Determine LABEL of footnote at point.
+ (label (cond
+ ;; LABEL is provided as argument.
+ (label)
+ ;; Footnote reference at point. If the footnote is
+ ;; anonymous, delete it and exit instead.
+ ((setq x (org-footnote-at-reference-p))
+ (or (car x)
+ (progn
+ (delete-region (nth 1 x) (nth 2 x))
+ (message "Anonymous footnote removed")
+ (throw 'done t))))
+ ;; Footnote definition at point.
+ ((setq x (org-footnote-at-definition-p))
+ (car x))
+ (t (error "Don't know which footnote to remove")))))
+ ;; 2. Now that LABEL is non-nil, find every reference and every
+ ;; definition, and delete them.
+ (setq nref (org-footnote-delete-references label)
+ ndef (org-footnote-delete-definitions label))
+ ;; 3. Verify consistency of footnotes and notify user.
+ (org-footnote-auto-adjust-maybe)
+ (message "%d definition(s) of and %d reference(s) of footnote %s removed"
+ ndef nref label)))))
;;;; Sorting, Renumbering, Normalizing
@@ -765,28 +785,25 @@ If LABEL is non-nil, delete that footnote instead."
(defun org-footnote-renumber-fn:N ()
"Order numbered footnotes into a sequence in the document."
(interactive)
- (let ((references (org-footnote--collect-references)))
- (unwind-protect
- (let* ((c 0)
- (references (cl-remove-if-not
- (lambda (r) (string-match-p "\\`[0-9]+\\'" (car r)))
- references))
- (alist (mapcar (lambda (l) (cons l (number-to-string (cl-incf c))))
- (delete-dups (mapcar #'car references)))))
- (org-with-wide-buffer
- ;; Re-number references.
- (dolist (ref references)
- (goto-char (nth 1 ref))
- (org-footnote--set-label (cdr (assoc (nth 0 ref) alist))))
- ;; Re-number definitions.
- (goto-char (point-min))
- (while (re-search-forward "^\\[fn:\\([0-9]+\\)\\]" nil t)
- (replace-match (or (cdr (assoc (match-string 1) alist))
- ;; Un-referenced definitions get
- ;; higher numbers.
- (number-to-string (cl-incf c)))
- nil nil nil 1))))
- (dolist (r references) (set-marker (nth 1 r) nil)))))
+ (let* ((c 0)
+ (references (cl-remove-if-not
+ (lambda (r) (string-match-p "\\`[0-9]+\\'" (car r)))
+ (org-footnote--collect-references)))
+ (alist (mapcar (lambda (l) (cons l (number-to-string (cl-incf c))))
+ (delete-dups (mapcar #'car references)))))
+ (org-with-wide-buffer
+ ;; Re-number references.
+ (dolist (ref references)
+ (goto-char (nth 1 ref))
+ (org-footnote--set-label (cdr (assoc (nth 0 ref) alist))))
+ ;; Re-number definitions.
+ (goto-char (point-min))
+ (while (re-search-forward "^\\[fn:\\([0-9]+\\)\\]" nil t)
+ (replace-match (or (cdr (assoc (match-string 1) alist))
+ ;; Un-referenced definitions get higher
+ ;; numbers.
+ (number-to-string (cl-incf c)))
+ nil nil nil 1)))))
(defun org-footnote-sort ()
"Rearrange footnote definitions in the current buffer.
@@ -795,129 +812,121 @@ references. Also relocate definitions at the end of their
relative section or within a single footnote section, according
to `org-footnote-section'. Inline definitions are ignored."
(let ((references (org-footnote--collect-references)))
- (unwind-protect
- (let ((definitions (org-footnote--collect-definitions 'delete)))
- (org-with-wide-buffer
- (org-footnote--clear-footnote-section)
- ;; Insert footnote definitions at the appropriate location,
- ;; separated by a blank line. Each definition is inserted
- ;; only once throughout the buffer.
- (let (inserted)
- (dolist (cell references)
- (let ((label (car cell))
- (nested (not (nth 2 cell)))
- (inline (nth 3 cell)))
- (unless (or (member label inserted) inline)
- (push label inserted)
- (unless (or org-footnote-section nested)
- ;; If `org-footnote-section' is non-nil, or
- ;; reference is nested, point is already at the
- ;; correct position. Otherwise, move at the
- ;; appropriate location within the section
- ;; containing the reference.
- (goto-char (nth 1 cell))
- (org-footnote--goto-local-insertion-point))
- (insert "\n"
- (or (cdr (assoc label definitions))
- (format "[fn:%s] DEFINITION NOT FOUND." label))
- "\n"))))
- ;; Insert un-referenced footnote definitions at the end.
- (let ((unreferenced
- (cl-remove-if (lambda (d) (member (car d) inserted))
- definitions)))
- (dolist (d unreferenced) (insert "\n" (cdr d) "\n"))))))
- ;; Clear dangling markers in the buffer.
- (dolist (r references) (set-marker (nth 1 r) nil)))))
+ (org-preserve-local-variables
+ (let ((definitions (org-footnote--collect-definitions 'delete)))
+ (org-with-wide-buffer
+ (org-footnote--clear-footnote-section)
+ ;; Insert footnote definitions at the appropriate location,
+ ;; separated by a blank line. Each definition is inserted
+ ;; only once throughout the buffer.
+ (let (inserted)
+ (dolist (cell references)
+ (let ((label (car cell))
+ (nested (not (nth 2 cell)))
+ (inline (nth 3 cell)))
+ (unless (or (member label inserted) inline)
+ (push label inserted)
+ (unless (or org-footnote-section nested)
+ ;; If `org-footnote-section' is non-nil, or
+ ;; reference is nested, point is already at the
+ ;; correct position. Otherwise, move at the
+ ;; appropriate location within the section
+ ;; containing the reference.
+ (goto-char (nth 1 cell))
+ (org-footnote--goto-local-insertion-point))
+ (insert "\n"
+ (or (cdr (assoc label definitions))
+ (format "[fn:%s] DEFINITION NOT FOUND." label))
+ "\n"))))
+ ;; Insert un-referenced footnote definitions at the end.
+ (pcase-dolist (`(,label . ,definition) definitions)
+ (unless (member label inserted)
+ (insert "\n" definition "\n")))))))))
(defun org-footnote-normalize ()
"Turn every footnote in buffer into a numbered one."
(interactive)
- (let ((references (org-footnote--collect-references 'anonymous)))
- (unwind-protect
- (let ((n 0)
- (translations nil)
- (definitions nil))
- (org-with-wide-buffer
- ;; Update label for reference. We need to do this before
- ;; clearing definitions in order to rename nested footnotes
- ;; before they are deleted.
- (dolist (cell references)
- (let* ((label (car cell))
- (anonymous (not label))
- (new
- (cond
- ;; In order to differentiate anonymous
- ;; references from regular ones, set their
- ;; labels to integers, not strings.
- (anonymous (setcar cell (cl-incf n)))
- ((cdr (assoc label translations)))
- (t (let ((l (number-to-string (cl-incf n))))
- (push (cons label l) translations)
- l)))))
- (goto-char (nth 1 cell)) ; Move to reference's start.
- (org-footnote--set-label
- (if anonymous (number-to-string new) new))
- (let ((size (nth 3 cell)))
- ;; Transform inline footnotes into regular references
- ;; and retain their definition for later insertion as
- ;; a regular footnote definition.
- (when size
- (let ((def (concat
- (format "[fn:%s] " new)
- (org-trim
- (substring
- (delete-and-extract-region
- (point) (+ (point) size 1))
- 1)))))
- (push (cons (if anonymous new label) def) definitions)
- (when org-footnote-fill-after-inline-note-extraction
- (org-fill-paragraph)))))))
- ;; Collect definitions. Update labels according to ALIST.
- (let ((definitions
- (nconc definitions
- (org-footnote--collect-definitions 'delete)))
- (inserted))
- (org-footnote--clear-footnote-section)
- (dolist (cell references)
- (let* ((label (car cell))
- (anonymous (integerp label))
- (pos (nth 1 cell)))
- ;; Move to appropriate location, if required. When
- ;; there is a footnote section or reference is
- ;; nested, point is already at the expected location.
- (unless (or org-footnote-section (not (nth 2 cell)))
- (goto-char pos)
- (org-footnote--goto-local-insertion-point))
- ;; Insert new definition once label is updated.
- (unless (member label inserted)
- (push label inserted)
- (let ((stored (cdr (assoc label definitions)))
- ;; Anonymous footnotes' label is already
- ;; up-to-date.
- (new (if anonymous label
- (cdr (assoc label translations)))))
- (insert "\n"
- (cond
- ((not stored)
- (format "[fn:%s] DEFINITION NOT FOUND." new))
- (anonymous stored)
- (t
- (replace-regexp-in-string
- "\\`\\[fn:\\(.*?\\)\\]" new stored nil nil 1)))
- "\n")))))
- ;; Insert un-referenced footnote definitions at the end.
- (let ((unreferenced
- (cl-remove-if (lambda (d) (member (car d) inserted))
- definitions)))
- (dolist (d unreferenced)
- (insert "\n"
- (replace-regexp-in-string
- org-footnote-definition-re
- (format "[fn:%d]" (cl-incf n))
- (cdr d))
- "\n"))))))
- ;; Clear dangling markers.
- (dolist (r references) (set-marker (nth 1 r) nil)))))
+ (org-preserve-local-variables
+ (let ((n 0)
+ (translations nil)
+ (definitions nil)
+ (references (org-footnote--collect-references 'anonymous)))
+ (org-with-wide-buffer
+ ;; Update label for reference. We need to do this before
+ ;; clearing definitions in order to rename nested footnotes
+ ;; before they are deleted.
+ (dolist (cell references)
+ (let* ((label (car cell))
+ (anonymous (not label))
+ (new
+ (cond
+ ;; In order to differentiate anonymous references
+ ;; from regular ones, set their labels to integers,
+ ;; not strings.
+ (anonymous (setcar cell (cl-incf n)))
+ ((cdr (assoc label translations)))
+ (t (let ((l (number-to-string (cl-incf n))))
+ (push (cons label l) translations)
+ l)))))
+ (goto-char (nth 1 cell)) ; Move to reference's start.
+ (org-footnote--set-label
+ (if anonymous (number-to-string new) new))
+ (let ((size (nth 3 cell)))
+ ;; Transform inline footnotes into regular references and
+ ;; retain their definition for later insertion as
+ ;; a regular footnote definition.
+ (when size
+ (let ((def (concat
+ (format "[fn:%s] " new)
+ (org-trim
+ (substring
+ (delete-and-extract-region
+ (point) (+ (point) size 1))
+ 1)))))
+ (push (cons (if anonymous new label) def) definitions)
+ (when org-footnote-fill-after-inline-note-extraction
+ (org-fill-paragraph)))))))
+ ;; Collect definitions. Update labels according to ALIST.
+ (let ((definitions
+ (nconc definitions
+ (org-footnote--collect-definitions 'delete)))
+ (inserted))
+ (org-footnote--clear-footnote-section)
+ (dolist (cell references)
+ (let* ((label (car cell))
+ (anonymous (integerp label))
+ (pos (nth 1 cell)))
+ ;; Move to appropriate location, if required. When there
+ ;; is a footnote section or reference is nested, point is
+ ;; already at the expected location.
+ (unless (or org-footnote-section (not (nth 2 cell)))
+ (goto-char pos)
+ (org-footnote--goto-local-insertion-point))
+ ;; Insert new definition once label is updated.
+ (unless (member label inserted)
+ (push label inserted)
+ (let ((stored (cdr (assoc label definitions)))
+ ;; Anonymous footnotes' label is already
+ ;; up-to-date.
+ (new (if anonymous label
+ (cdr (assoc label translations)))))
+ (insert "\n"
+ (cond
+ ((not stored)
+ (format "[fn:%s] DEFINITION NOT FOUND." new))
+ (anonymous stored)
+ (t
+ (replace-regexp-in-string
+ "\\`\\[fn:\\(.*?\\)\\]" new stored nil nil 1)))
+ "\n")))))
+ ;; Insert un-referenced footnote definitions at the end.
+ (pcase-dolist (`(,label . ,definition) definitions)
+ (unless (member label inserted)
+ (insert "\n"
+ (replace-regexp-in-string org-footnote-definition-re
+ (format "[fn:%d]" (cl-incf n))
+ definition)
+ "\n"))))))))
(defun org-footnote-auto-adjust-maybe ()
"Renumber and/or sort footnotes according to user settings."