| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023 | 
							- ;;; org-footnote.el --- Footnote support in Org      -*- lexical-binding: t; -*-
 
- ;;
 
- ;; Copyright (C) 2009-2022 Free Software Foundation, Inc.
 
- ;;
 
- ;; Author: Carsten Dominik <carsten.dominik@gmail.com>
 
- ;; Keywords: outlines, hypermedia, calendar, wp
 
- ;; URL: https://orgmode.org
 
- ;;
 
- ;; This file is part of GNU Emacs.
 
- ;;
 
- ;; GNU Emacs is free software: you can redistribute it and/or modify
 
- ;; it under the terms of the GNU General Public License as published by
 
- ;; the Free Software Foundation, either version 3 of the License, or
 
- ;; (at your option) any later version.
 
- ;; GNU Emacs is distributed in the hope that it will be useful,
 
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 
- ;; GNU General Public License for more details.
 
- ;; You should have received a copy of the GNU General Public License
 
- ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
- ;;
 
- ;;; Commentary:
 
- ;; This file contains the code dealing with footnotes in Org mode.
 
- ;;; Code:
 
- ;;;; Declarations
 
- (require 'cl-lib)
 
- (require 'org-macs)
 
- (require 'org-compat)
 
- (declare-function org-at-comment-p "org" ())
 
- (declare-function org-at-heading-p "org" (&optional ignored))
 
- (declare-function org-back-over-empty-lines "org" ())
 
- (declare-function org-end-of-meta-data "org" (&optional full))
 
- (declare-function org-edit-footnote-reference "org-src" ())
 
- (declare-function org-element-at-point "org-element" (&optional pom cached-only))
 
- (declare-function org-element-class "org-element" (datum &optional parent))
 
- (declare-function org-element-context "org-element" (&optional element))
 
- (declare-function org-element-lineage "org-element" (blob &optional types with-self))
 
- (declare-function org-element-property "org-element" (property element))
 
- (declare-function org-element-type "org-element" (element))
 
- (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-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-fold-show-context "org-fold" (&optional key))
 
- (declare-function outline-next-heading "outline")
 
- (defvar electric-indent-mode)
 
- (defvar org-blank-before-new-entry)	; 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
 
- (defvar org-outline-regexp-bol)		; defined in org.el
 
- ;;;; Constants
 
- (defconst org-footnote-re
 
-   "\\[fn:\\(?:\\(?1:[-_[:word:]]+\\)?\\(:\\)\\|\\(?1:[-_[:word:]]+\\)\\]\\)"
 
-   "Regular expression for matching footnotes.
 
- Match group 1 contains footnote's label.  It is nil for anonymous
 
- footnotes.  Match group 2 is non-nil only when footnote is
 
- inline, i.e., it contains its own definition.")
 
- (defconst org-footnote-definition-re "^\\[fn:\\([-_[:word:]]+\\)\\]"
 
-   "Regular expression matching the definition of a footnote.
 
- Match group 1 contains definition's label.")
 
- (defconst org-footnote-forbidden-blocks '("comment" "example" "export" "src")
 
-   "Names of blocks where footnotes are not allowed.")
 
- ;;;; Customization
 
- (defgroup org-footnote nil
 
-   "Footnotes in Org mode."
 
-   :tag "Org Footnote"
 
-   :group 'org)
 
- (defcustom org-footnote-section "Footnotes"
 
-   "Outline heading containing footnote definitions.
 
- This can be nil, to place footnotes locally at the end of the current
 
- outline node.  It can also be a string representing the name of a
 
- special outline heading under which footnotes should be put.
 
- This variable defines the place where Org puts the definition
 
- automatically, i.e. when creating the footnote, and when sorting
 
- the notes.  However, by hand, you may place definitions
 
- *anywhere*.
 
- If this is a string, during export, all subtrees starting with
 
- this heading will be ignored.
 
- If you don't use the customize interface to change this variable,
 
- you will need to run the following command after the change:
 
-   `\\[universal-argument] \\[org-element-cache-reset]'"
 
-   :group 'org-footnote
 
-   :initialize 'custom-initialize-default
 
-   :set (lambda (var val)
 
- 	 (set-default-toplevel-value var val)
 
- 	 (when (fboundp 'org-element-cache-reset)
 
- 	   (org-element-cache-reset 'all)))
 
-   :type '(choice
 
- 	  (string :tag "Collect footnotes under heading")
 
- 	  (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.
 
- 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
 
-   :safe #'booleanp)
 
- (defcustom org-footnote-auto-label t
 
-   "Non-nil means define automatically new labels for footnotes.
 
- Possible values are:
 
- nil        Prompt the user for each label.
 
- t          Create unique labels of the form [fn:1], [fn:2], etc.
 
- confirm    Like t, but let the user edit the created value.
 
-            The label can be removed from the minibuffer to create
 
-            an anonymous footnote.
 
- random	   Automatically generate a unique, random label."
 
-   :group 'org-footnote
 
-   :type '(choice
 
- 	  (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))
 
-   :safe #'symbolp)
 
- (defcustom org-footnote-auto-adjust nil
 
-   "Non-nil means automatically adjust footnotes after insert/delete.
 
- When this is t, after each insertion or deletion of a footnote,
 
- simple fn:N footnotes will be renumbered, and all footnotes will be sorted.
 
- If you want to have just sorting or just renumbering, set this variable
 
- to `sort' or `renumber'.
 
- The main values of this variable can be set with in-buffer options:
 
- #+STARTUP: fnadjust
 
- #+STARTUP: nofnadjust"
 
-   :group 'org-footnote
 
-   :type '(choice
 
- 	  (const :tag "No adjustment" nil)
 
- 	  (const :tag "Renumber" renumber)
 
- 	  (const :tag "Sort" sort)
 
- 	  (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.
 
- 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
 
-   :safe #'booleanp)
 
- ;;;; Predicates
 
- (defun org-footnote-in-valid-context-p ()
 
-   "Is point in a context where footnotes are allowed?"
 
-   (save-match-data
 
-     (not (or (org-at-comment-p)
 
- 	     (org-inside-LaTeX-fragment-p)
 
- 	     ;; Avoid literal example.
 
- 	     (org-in-verbatim-emphasis)
 
- 	     (save-excursion
 
- 	       (beginning-of-line)
 
- 	       (looking-at "[ \t]*:[ \t]+"))
 
- 	     ;; Avoid forbidden blocks.
 
- 	     (org-in-block-p org-footnote-forbidden-blocks)))))
 
- (defun org-footnote-at-reference-p ()
 
-   "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 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 ()
 
-   "Non-nil if point is within a footnote definition.
 
- 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 is nil if not at a footnote definition, and
 
- a list with label, start, end and definition of the footnote
 
- otherwise."
 
-   (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
 
- (defun org-footnote--allow-reference-p ()
 
-   "Non-nil when a footnote reference can be inserted at point."
 
-   ;; XXX: This is similar to `org-footnote-in-valid-context-p' but
 
-   ;; more accurate and usually faster, except in some corner cases.
 
-   ;; It may replace it after doing proper benchmarks as it would be
 
-   ;; used in fontification.
 
-   (unless (bolp)
 
-     (let* ((context (org-element-context))
 
- 	   (type (org-element-type context)))
 
-       (cond
 
-        ;; No footnote reference in attributes.
 
-        ((let ((post (org-element-property :post-affiliated context)))
 
- 	  (and post (< (point) post)))
 
- 	nil)
 
-        ;; Paragraphs and blank lines at top of document are fine.
 
-        ((memq type '(nil paragraph)))
 
-        ;; So are contents of verse blocks.
 
-        ((eq type 'verse-block)
 
- 	(and (>= (point) (org-element-property :contents-begin context))
 
- 	     (< (point) (org-element-property :contents-end context))))
 
-        ;; In an headline or inlinetask, point must be either on the
 
-        ;; heading itself or on the blank lines below.
 
-        ((memq type '(headline inlinetask))
 
- 	(or (not (org-at-heading-p))
 
- 	    (and (save-excursion
 
- 		   (beginning-of-line)
 
- 		   (and (let ((case-fold-search t))
 
- 			  (not (looking-at-p "\\*+ END[ \t]*$")))
 
- 			(let ((case-fold-search nil))
 
- 			  (looking-at org-complex-heading-regexp))))
 
- 		 (match-beginning 4)
 
- 		 (>= (point) (match-beginning 4))
 
- 		 (or (not (match-beginning 5))
 
- 		     (< (point) (match-beginning 5))))))
 
-        ;; White spaces after an object or blank lines after an element
 
-        ;; are OK.
 
-        ((>= (point)
 
- 	    (save-excursion (goto-char (org-element-property :end context))
 
- 			    (skip-chars-backward " \r\t\n")
 
- 			    (if (eq (org-element-class context) 'object) (point)
 
- 			      (line-beginning-position 2)))))
 
-        ;; At the beginning of a footnote definition, right after the
 
-        ;; label, is OK.
 
-        ((eq type 'footnote-definition) (looking-at (rx space)))
 
-        ;; Other elements are invalid.
 
-        ((eq (org-element-class context) 'element) nil)
 
-        ;; Just before object is fine.
 
-        ((= (point) (org-element-property :begin context)))
 
-        ;; Within recursive object too, but not in a link.
 
-        ((eq type 'link) nil)
 
-        ((eq type 'table-cell)
 
-         ;; :contents-begin is not reliable on empty cells, so special
 
-         ;; case it.
 
-         (<= (save-excursion (skip-chars-backward " \t") (point))
 
-             (org-element-property :contents-end context)))
 
-        ((let ((cbeg (org-element-property :contents-begin context))
 
- 	      (cend (org-element-property :contents-end context)))
 
- 	  (and cbeg (>= (point) cbeg) (<= (point) cend))))))))
 
- (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.  Leave point
 
- within the new section."
 
-   (when org-footnote-section
 
-     (goto-char (point-min))
 
-     (let ((regexp (format "^\\*+ +%s[ \t]*$"
 
- 			  (regexp-quote org-footnote-section))))
 
-       (while (re-search-forward regexp nil t)
 
- 	(delete-region
 
- 	 (match-beginning 0)
 
- 	 (org-end-of-subtree t t))))
 
-     (goto-char (point-max))
 
-     ;; 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"))
 
-     (insert "* " org-footnote-section "\n")))
 
- (defun org-footnote--set-label (label)
 
-   "Set label of footnote at point to string LABEL.
 
- Assume point is at the beginning of the reference or definition
 
- to rename."
 
-   (forward-char 4)
 
-   (cond ((eq (char-after) ?:) (insert label))
 
- 	((looking-at "\\([-_[:word:]]+\\)") (replace-match label nil nil nil 1))
 
- 	(t nil)))
 
- (defun org-footnote--collect-references (&optional anonymous)
 
-   "Collect all labeled footnote references in current buffer.
 
- Return an alist where associations follow the pattern
 
-   (LABEL MARKER TOP-LEVEL SIZE)
 
- with
 
-   LABEL     the label of the of the definition,
 
-   MARKER    a marker pointing to its beginning,
 
-   TOP-LEVEL a boolean, nil when the footnote is contained within
 
-             another one,
 
-   SIZE      the length of the inline definition, in characters,
 
-             or nil for non-inline references.
 
- When optional ANONYMOUS is non-nil, also collect anonymous
 
- references.  In such cases, LABEL is nil.
 
- References are sorted according to a deep-reading order."
 
-   (org-with-wide-buffer
 
-    (goto-char (point-min))
 
-    (let ((regexp (if anonymous org-footnote-re "\\[fn:[-_[:word:]]+[]:]"))
 
- 	 references nested)
 
-      (save-excursion
 
-        (while (re-search-forward regexp nil t)
 
- 	 ;; Ignore definitions.
 
- 	 (unless (and (eq (char-before) ?\])
 
- 		      (= (line-beginning-position) (match-beginning 0)))
 
- 	   ;; Ensure point is within the reference before parsing it.
 
- 	   (backward-char)
 
- 	   (let ((object (org-element-context)))
 
- 	     (when (eq (org-element-type object) 'footnote-reference)
 
- 	       (let* ((label (org-element-property :label object))
 
- 		      (begin (org-element-property :begin object))
 
- 		      (size
 
- 		       (and (eq (org-element-property :type object) 'inline)
 
- 			    (- (org-element-property :contents-end object)
 
- 			       (org-element-property :contents-begin object)))))
 
- 		 (let ((d (org-element-lineage object '(footnote-definition))))
 
- 		   (push (list label (copy-marker begin) (not d) size)
 
- 			 references)
 
- 		   (when d
 
- 		     ;; Nested references are stored in alist NESTED.
 
- 		     ;; Associations there follow the pattern
 
- 		     ;;
 
- 		     ;;   (DEFINITION-LABEL . REFERENCES)
 
- 		     (let* ((def-label (org-element-property :label d))
 
- 			    (labels (assoc def-label nested)))
 
- 		       (if labels (push label (cdr labels))
 
- 			 (push (list def-label label) nested)))))))))))
 
-      ;; Sort the list of references.  Nested footnotes have priority
 
-      ;; over top-level ones.
 
-      (letrec ((ordered nil)
 
- 	      (add-reference
 
- 	       (lambda (ref allow-nested)
 
- 		 (when (or allow-nested (nth 2 ref))
 
- 		   (push ref ordered)
 
- 		   (dolist (r (mapcar (lambda (l) (assoc l references))
 
- 				      (reverse
 
- 				       (cdr (assoc (nth 0 ref) nested)))))
 
- 		     (funcall add-reference r t))))))
 
-        (dolist (r (reverse references) (nreverse ordered))
 
- 	 (funcall add-reference r nil))))))
 
- (defun org-footnote--collect-definitions (&optional delete)
 
-   "Collect all footnote definitions in current buffer.
 
- Return an alist where associations follow the pattern
 
-   (LABEL . DEFINITION)
 
- with LABEL and DEFINITION being, respectively, the label and the
 
- definition of the footnote, as strings.
 
- When optional argument DELETE is non-nil, delete the definition
 
- while collecting them."
 
-   (org-with-wide-buffer
 
-    (goto-char (point-min))
 
-    (let (definitions seen)
 
-      (while (re-search-forward org-footnote-definition-re nil t)
 
-        (backward-char)
 
-        (let ((element (org-element-at-point)))
 
- 	 (let ((label (org-element-property :label element)))
 
- 	   (when (and (eq (org-element-type element) 'footnote-definition)
 
- 		      (not (member label seen)))
 
- 	     (push label seen)
 
- 	     (let* ((beg (progn
 
- 			   (goto-char (org-element-property :begin element))
 
- 			   (skip-chars-backward " \r\t\n")
 
- 			   (if (bobp) (point) (line-beginning-position 2))))
 
- 		    (end (progn
 
- 			   (goto-char (org-element-property :end element))
 
- 			   (skip-chars-backward " \r\t\n")
 
- 			   (line-beginning-position 2)))
 
- 		    (def (org-trim (buffer-substring-no-properties beg end))))
 
- 	       (push (cons label def) definitions)
 
- 	       (when delete (delete-region beg end)))))))
 
-      definitions)))
 
- (defun org-footnote--goto-local-insertion-point ()
 
-   "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-chars-backward " \t\n")
 
-   (unless (bobp) (forward-line))
 
-   (unless (bolp) (insert "\n")))
 
- ;;;; Navigation
 
- (defun org-footnote-get-next-reference (&optional label backward limit)
 
-   "Return complete reference of the next footnote.
 
- If LABEL is provided, get the next reference of that footnote.  If
 
- BACKWARD is non-nil, find previous reference instead.  LIMIT is
 
- 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."
 
-   (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))
 
- 	  (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.
 
- 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.
 
- 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)
 
- 	  (goto-char origin)
 
- 	  (throw 'exit nil))
 
- 	;; Beware: with non-inline footnotes point will be just after
 
- 	;; the closing square bracket.
 
- 	(backward-char)
 
- 	(cond
 
- 	 ((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.
 
- 	 ((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.
 
- LOCATION, when non-nil specifies the buffer position of the
 
- definition.
 
- Throw an error if there is no definition or if it cannot be
 
- reached from current narrowed part of buffer.  Return a non-nil
 
- value if point was successfully moved."
 
-   (interactive "sLabel: ")
 
-   (let* ((label (org-footnote-normalize-label label))
 
- 	 (def-start (or location (nth 1 (org-footnote-get-definition label)))))
 
-     (cond
 
-      ((not def-start)
 
-       (user-error "Cannot find definition of footnote %s" label))
 
-      ((or (> def-start (point-max)) (< def-start (point-min)))
 
-       (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)))
 
-     (goto-char (match-end 0))
 
-     (org-fold-show-context 'link-search)
 
-     (when (derived-mode-p 'org-mode)
 
-       (message "%s" (substitute-command-keys
 
- 		     "Edit definition and go back with \
 
- `\\[org-mark-ring-goto]' or, if unique, with `\\[org-ctrl-c-ctrl-c]'.")))
 
-     t))
 
- (defun org-footnote-goto-previous-reference (label)
 
-   "Find the first closest (to point) reference of footnote with label LABEL."
 
-   (interactive "sLabel: ")
 
-   (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-fold-show-context 'link-search)))
 
- ;;;; Getters
 
- (defun org-footnote-normalize-label (label)
 
-   "Return LABEL without \"fn:\" prefix.
 
- If LABEL is the empty string or constituted of white spaces only,
 
- return nil instead."
 
-   (pcase (org-trim label)
 
-     ("" nil)
 
-     ((pred (string-prefix-p "fn:")) (substring label 3))
 
-     (_ label)))
 
- (defun org-footnote-get-definition (label)
 
-   "Return label, boundaries and definition of the footnote LABEL."
 
-   (let* ((label (regexp-quote (org-footnote-normalize-label label)))
 
- 	 (re (format "^\\[fn:%s\\]\\|.\\[fn:%s:" label label)))
 
-     (org-with-wide-buffer
 
-      (goto-char (point-min))
 
-      (catch 'found
 
-        (while (re-search-forward re nil t)
 
- 	 (let* ((datum (progn (backward-char) (org-element-context)))
 
- 		(type (org-element-type datum)))
 
- 	   (when (memq type '(footnote-definition footnote-reference))
 
- 	     (throw 'found
 
- 		    (list
 
- 		     label
 
- 		     (org-element-property :begin datum)
 
- 		     (org-element-property :end datum)
 
- 		     (let ((cbeg (org-element-property :contents-begin datum)))
 
- 		       (if (not cbeg) ""
 
- 			 (replace-regexp-in-string
 
- 			  "[ \t\n]*\\'"
 
- 			  ""
 
- 			  (buffer-substring-no-properties
 
- 			   cbeg
 
- 			   (org-element-property :contents-end datum))))))))))
 
-        nil))))
 
- (defun org-footnote-all-labels ()
 
-   "List all defined footnote labels used throughout the buffer.
 
- This function ignores narrowing, if any."
 
-   (org-with-wide-buffer
 
-    (goto-char (point-min))
 
-    (let (all)
 
-      (while (re-search-forward org-footnote-re nil t)
 
-        (backward-char)
 
-        (let ((context (org-element-context)))
 
- 	 (when (memq (org-element-type context)
 
- 		     '(footnote-definition footnote-reference))
 
- 	   (let ((label (org-element-property :label context)))
 
- 	     (when label (cl-pushnew label all :test #'equal))))))
 
-      all)))
 
- (defun org-footnote-unique-label (&optional current)
 
-   "Return a new unique footnote label.
 
- The function returns the first numeric label currently unused.
 
- Optional argument CURRENT is the list of labels active in the
 
- buffer."
 
-   (let ((current (or current (org-footnote-all-labels))))
 
-     (let ((count 1))
 
-       (while (member (number-to-string count) current)
 
- 	(cl-incf count))
 
-       (number-to-string count))))
 
- ;;;; Adding, Deleting Footnotes
 
- (defun org-footnote-new ()
 
-   "Insert a new footnote.
 
- This command prompts for a label.  If this is a label referencing an
 
- existing label, only insert the label.  If the footnote label is empty
 
- or new, let the user edit the definition of the footnote."
 
-   (interactive)
 
-   (unless (org-footnote--allow-reference-p)
 
-     (user-error "Cannot insert a footnote here"))
 
-   (let* ((all (org-footnote-all-labels))
 
- 	 (label
 
- 	  (if (eq org-footnote-auto-label 'random)
 
- 	      (format "%x" (abs (random)))
 
- 	    (org-footnote-normalize-label
 
- 	     (let ((propose (org-footnote-unique-label all)))
 
- 	       (if (eq org-footnote-auto-label t) propose
 
- 		 (completing-read
 
- 		  "Label (leave empty for anonymous): "
 
- 		  (mapcar #'list all) nil nil
 
- 		  (and (eq org-footnote-auto-label 'confirm) propose))))))))
 
-     (cond ((not label)
 
- 	   (insert "[fn::]")
 
- 	   (backward-char 1))
 
- 	  ((member label all)
 
- 	   (insert "[fn:" label "]")
 
- 	   (message "New reference to existing note"))
 
- 	  (org-footnote-define-inline
 
- 	   (insert "[fn:" label ":]")
 
- 	   (backward-char 1)
 
- 	   (org-footnote-auto-adjust-maybe))
 
- 	  (t
 
- 	   (insert "[fn:" label "]")
 
- 	   (let ((p (org-footnote-create-definition label)))
 
- 	     ;; `org-footnote-goto-definition' needs to be called
 
- 	     ;; after `org-footnote-auto-adjust-maybe'.  Otherwise
 
- 	     ;; both label and location of the definition are lost.
 
- 	     ;; On the contrary, it needs to be called before
 
- 	     ;; `org-edit-footnote-reference' so that the remote
 
- 	     ;; editing buffer can display the correct label.
 
- 	     (if (ignore-errors (org-footnote-goto-definition label p))
 
- 		 (org-footnote-auto-adjust-maybe)
 
- 	       ;; Definition was created outside current scope: edit
 
- 	       ;; it remotely.
 
- 	       (org-footnote-auto-adjust-maybe)
 
- 	       (org-edit-footnote-reference)))))))
 
- (defun org-footnote-create-definition (label)
 
-   "Start the definition of a footnote with label LABEL.
 
- 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-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))
 
-         (org-end-of-meta-data t)
 
- 	(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.
 
- Return the number of footnotes removed."
 
-   (save-excursion
 
-     (goto-char (point-min))
 
-     (let (ref (nref 0))
 
-       (while (setq ref (org-footnote-get-next-reference label))
 
- 	(goto-char (nth 1 ref))
 
- 	(delete-region (nth 1 ref) (nth 2 ref))
 
- 	(cl-incf nref))
 
-       nref)))
 
- (defun org-footnote-delete-definitions (label)
 
-   "Delete every definition of the footnote LABEL.
 
- Return the number of footnotes removed."
 
-   (save-excursion
 
-     (goto-char (point-min))
 
-     (let ((def-re (format "^\\[fn:%s\\]" (regexp-quote label)))
 
- 	  (ndef 0))
 
-       (while (re-search-forward def-re nil t)
 
- 	(pcase (org-footnote-at-definition-p)
 
- 	  (`(,_ ,start ,end ,_)
 
- 	   ;; Remove the footnote, and all blank lines before it.
 
- 	   (delete-region (progn
 
- 			    (goto-char start)
 
- 			    (skip-chars-backward " \r\t\n")
 
- 			    (if (bobp) (point) (line-beginning-position 2)))
 
- 			  (progn
 
- 			    (goto-char end)
 
- 			    (skip-chars-backward " \r\t\n")
 
- 			    (if (bobp) (point) (line-beginning-position 2))))
 
- 	   (cl-incf ndef))))
 
-       ndef)))
 
- (defun org-footnote-delete (&optional label)
 
-   "Delete the footnote at point.
 
- This will remove the definition (even multiple definitions if they exist)
 
- and all references of a footnote label.
 
- If LABEL is non-nil, delete that footnote instead."
 
-   (catch 'done
 
-     (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
 
- (defun org-footnote-renumber-fn:N ()
 
-   "Order numbered footnotes into a sequence in the document."
 
-   (interactive)
 
-   (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.
 
- Sort footnote definitions so they match order of footnote
 
- 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)))
 
-     (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)
 
-   (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."
 
-   (when (memq org-footnote-auto-adjust '(t renumber))
 
-     (org-footnote-renumber-fn:N))
 
-   (when (memq org-footnote-auto-adjust '(t sort))
 
-     (let ((label (car (org-footnote-at-definition-p))))
 
-       (org-footnote-sort)
 
-       (when label
 
- 	(goto-char (point-min))
 
- 	(and (re-search-forward (format "^\\[fn:%s\\]" (regexp-quote label))
 
- 				nil t)
 
- 	     (progn (insert " ")
 
- 		    (just-one-space)))))))
 
- ;;;; End-user interface
 
- ;;;###autoload
 
- (defun org-footnote-action (&optional special)
 
-   "Do the right thing for footnotes.
 
- When at a footnote reference, jump to the definition.
 
- When at a definition, jump to the references if they exist, offer
 
- to create them otherwise.
 
- When neither at definition or reference, create a new footnote,
 
- interactively if possible.
 
- With prefix arg SPECIAL, or when no footnote can be created,
 
- offer additional commands in a menu."
 
-   (interactive "P")
 
-   (let* ((context (and (not special) (org-element-context)))
 
- 	 (type (org-element-type context)))
 
-     (cond
 
-      ;; On white space after element, insert a new footnote.
 
-      ((and context
 
- 	   (> (point)
 
- 	      (save-excursion
 
- 		(goto-char (org-element-property :end context))
 
- 		(skip-chars-backward " \t")
 
- 		(point))))
 
-       (org-footnote-new))
 
-      ((eq type 'footnote-reference)
 
-       (let ((label (org-element-property :label context)))
 
- 	(cond
 
- 	 ;; Anonymous footnote: move point at the beginning of its
 
- 	 ;; definition.
 
- 	 ((not label)
 
- 	  (goto-char (org-element-property :contents-begin context)))
 
- 	 ;; Check if a definition exists: then move to it.
 
- 	 ((let ((p (nth 1 (org-footnote-get-definition label))))
 
- 	    (when p (org-footnote-goto-definition label p))))
 
- 	 ;; No definition exists: offer to create it.
 
- 	 ((yes-or-no-p (format "No definition for %s.  Create one? " label))
 
- 	  (let ((p (org-footnote-create-definition label)))
 
- 	    (or (ignore-errors (org-footnote-goto-definition label p))
 
- 		;; Since definition was created outside current scope,
 
- 		;; edit it remotely.
 
- 		(org-edit-footnote-reference)))))))
 
-      ((eq type 'footnote-definition)
 
-       (org-footnote-goto-previous-reference
 
-        (org-element-property :label context)))
 
-      ((or special (not (org-footnote--allow-reference-p)))
 
-       (message "Footnotes: [s]ort | [r]enumber fn:N | [S]=r+s | [n]ormalize | \
 
- \[d]elete")
 
-       (pcase (read-char-exclusive)
 
- 	(?s (org-footnote-sort))
 
- 	(?r (org-footnote-renumber-fn:N))
 
- 	(?S (org-footnote-renumber-fn:N)
 
- 	    (org-footnote-sort))
 
- 	(?n (org-footnote-normalize))
 
- 	(?d (org-footnote-delete))
 
- 	(char (error "No such footnote command %c" char))))
 
-      (t (org-footnote-new)))))
 
- (provide 'org-footnote)
 
- ;; Local variables:
 
- ;; generated-autoload-file: "org-loaddefs.el"
 
- ;; End:
 
- ;;; org-footnote.el ends here
 
 
  |