;;; org-footnote.el --- Footnote support in Org and elsewhere
;;
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 2009-2016 Free Software Foundation, Inc.
;;
;; Author: Carsten Dominik <carsten at orgmode dot org>
;; Keywords: outlines, hypermedia, calendar, wp
;; Homepage: http://orgmode.org
-;; Version: 7.7
;;
;; This file is part of GNU Emacs.
;;
(require 'org-macs)
(require 'org-compat)
+(declare-function message-point-in-header-p "message" ())
+(declare-function org-back-over-empty-lines "org" ())
+(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-combine-plists "org" (&rest plists))
+(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
+(declare-function org-fill-paragraph "org" (&optional justify))
+(declare-function org-icompleting-read "org" (&rest args))
+(declare-function org-id-uuid "org-id" ())
+(declare-function org-in-block-p "org" (names))
(declare-function org-in-commented-line "org" ())
(declare-function org-in-indented-comment-line "org" ())
(declare-function org-in-regexp "org" (re &optional nlines visually))
-(declare-function org-in-block-p "org" (names))
-(declare-function org-mark-ring-push "org" (&optional pos buffer))
-(declare-function outline-next-heading "outline")
-(declare-function org-trim "org" (s))
-(declare-function org-show-context "org" (&optional key))
-(declare-function org-back-to-heading "org" (&optional invisible-ok))
-(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(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-id-uuid "org" ())
-(declare-function org-fill-paragraph "org" (&optional justify))
-(declare-function org-export-preprocess-string "org-exp"
- (string &rest parameters))
+(declare-function org-mark-ring-push "org" (&optional pos buffer))
+(declare-function org-show-context "org" (&optional key))
+(declare-function org-trim "org" (s))
+(declare-function org-skip-whitespace "org" ())
+(declare-function outline-next-heading "outline")
+(declare-function org-skip-whitespace "org" ())
-(defvar org-outline-regexp-bol) ; defined in org.el
-(defvar org-odd-levels-only) ;; defined in org.el
-(defvar org-bracket-link-regexp) ; defined in org.el
-(defvar message-signature-separator) ;; defined in message.el
+(defvar org-outline-regexp-bol) ; defined in org.el
+(defvar org-odd-levels-only) ; defined in org.el
+(defvar org-bracket-link-regexp) ; defined in org.el
+(defvar message-cite-prefix-regexp) ; defined in message.el
+(defvar message-signature-separator) ; defined in message.el
(defconst org-footnote-re
;; Only [1]-like footnotes are closed in this regexp, as footnotes
;; their definition.
;;
;; `org-re' is used for regexp compatibility with XEmacs.
- (org-re (concat "\\[\\(?:"
- ;; Match inline footnotes.
- "fn:\\([-_[:word:]]+\\)?:\\|"
- ;; Match other footnotes.
- "\\(?:\\([0-9]+\\)\\]\\)\\|"
- "\\(fn:[-_[:word:]]+\\)"
- "\\)"))
+ (concat "\\[\\(?:"
+ ;; Match inline footnotes.
+ (org-re "fn:\\([-_[:word:]]+\\)?:\\|")
+ ;; Match other footnotes.
+ "\\(?:\\([0-9]+\\)\\]\\)\\|"
+ (org-re "\\(fn:[-_[:word:]]+\\)")
+ "\\)")
"Regular expression for matching footnotes.")
(defconst org-footnote-definition-re
- (org-re "^\\(\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]\\)")
+ (org-re "^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]")
"Regular expression matching the definition of a footnote.")
-(defvar org-footnote-forbidden-blocks '("example" "verse" "src" "ascii" "beamer"
- "docbook" "html" "latex" "odt")
+(defconst org-footnote-forbidden-blocks
+ '("ascii" "beamer" "comment" "example" "html" "latex" "odt" "src")
"Names of blocks where footnotes are not allowed.")
(defgroup org-footnote nil
:group 'org)
(defcustom org-footnote-section "Footnotes"
- "Outline heading containing footnote definitions before export.
-This can be nil, to place footnotes locally at the end of the current
-outline node. If can also be the name of a special outline heading
-under which footnotes should be put.
+ "Outline heading containing footnote definitions.
+
+This can be nil, to place footnotes locally at the end of the
+current outline node. If can also be 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 removed after extracting footnote definitions."
+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."
:group 'org-footnote
:type '(choice
(string :tag "Collect footnotes under heading")
(defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:"
"Tag marking the beginning of footnote section.
-The Org-mode footnote engine can be used in arbitrary text files as well
-as in Org-mode. Outside Org-mode, new footnotes are always placed at
+The Org footnote engine can be used in arbitrary text files as well
+as in Org-mode. Outside Org mode, new footnotes are always placed at
the end of the file. When you normalize the notes, any line containing
only this tag will be removed, a new one will be inserted at the end
-of the file, followed by the collected and normalized footnotes."
+of the file, followed by the collected and normalized footnotes.
+
+If you don't want any tag in such buffers, set this variable to nil."
:group 'org-footnote
- :type 'string)
+ :type '(choice
+ (string :tag "Collect footnotes under tag")
+ (const :tag "Don't use a tag" nil)))
(defcustom org-footnote-define-inline nil
"Non-nil means define footnotes inline, at reference location.
"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], ...
-confirm like t, but let the user edit the created value. In particular,
- the label can be removed from the minibuffer, to create
+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.
-plain Automatically create plain number labels like [1]"
+plain Automatically create plain number labels like [1]."
:group 'org-footnote
:type '(choice
(const :tag "Prompt for label" nil)
#+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)))
(save-match-data
(not (or (org-in-commented-line)
(org-in-indented-comment-line)
- (org-in-verbatim-emphasis)
+ (org-inside-LaTeX-fragment-p)
;; Avoid literal example.
+ (org-in-verbatim-emphasis)
(save-excursion
(beginning-of-line)
(looking-at "[ \t]*:[ \t]+"))
(or (looking-at org-footnote-re)
(org-in-regexp org-footnote-re)
(save-excursion (re-search-backward org-footnote-re nil t)))
- ;; Only inline footnotes can start at bol.
- (or (eq (char-before (match-end 0)) 58)
- (/= (match-beginning 0) (point-at-bol))))
+ (/= (match-beginning 0) (point-at-bol)))
(let* ((beg (match-beginning 0))
- (label (or (match-string 2) (match-string 3)
+ (label (or (org-match-string-no-properties 2)
+ (org-match-string-no-properties 3)
;; Anonymous footnotes don't have labels
- (and (match-string 1) (concat "fn:" (match-string 1)))))
+ (and (match-string 1)
+ (concat "fn:" (org-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
(org-in-regexp org-bracket-link-regexp))))
(and linkp (< (point) (cdr linkp))))))
;; Verify point doesn't belong to a LaTeX macro.
- ;; Beware though, when two footnotes are side by
- ;; side, once the first one is changed into LaTeX,
- ;; the second one might then be considered as an
- ;; optional argument of the command. Thus, check
- ;; the `org-protected' property of that command.
- (or (not (org-inside-latex-macro-p))
- (and (get-text-property (1- beg) 'org-protected)
- (not (get-text-property beg 'org-protected)))))
+ (not (org-inside-latex-macro-p)))
(list label beg end
;; Definition: ensure this is an inline footnote first.
(and (or (not label) (match-string 1))
- (org-trim (buffer-substring (match-end 0) (1- end)))))))))
+ (org-trim (buffer-substring-no-properties
+ (match-end 0) (1- end)))))))))
(defun org-footnote-at-definition-p ()
- "Is the cursor at a footnote definition?
+ "Is point within a footnote definition?
-This matches only pure definitions like [1] or [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.
+This matches only pure definitions like [1] or [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 a list with
-label, start, end and definition of the footnote otherwise."
- (when (org-footnote-in-valid-context-p)
+The return value will be 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)
- (let ((lim (save-excursion (re-search-backward
- (concat org-outline-regexp-bol
- "\\|^[ \t]*$") nil t))))
+ ;; 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)
- (end-of-line)
- (list (match-string 2)
- (match-beginning 0)
- (save-match-data
- ;; In a message, limit search to signature.
- (let ((bound (and (derived-mode-p 'message-mode)
- (save-excursion
- (goto-char (point-max))
- (re-search-backward
- message-signature-separator nil t)))))
- (or (and (re-search-forward
- (org-re
- (concat "^[ \t]*$" "\\|"
- org-outline-regexp-bol
- "\\|"
- "^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]"))
- bound 'move)
- (progn (skip-chars-forward " \t\n") (point-at-bol)))
- (point))))
- (org-trim (buffer-substring (match-end 0) (point)))))))))
+ (let ((label (org-match-string-no-properties 1))
+ (beg (match-beginning 0))
+ (beg-def (match-end 0))
+ ;; In message-mode, do not search after signature.
+ (end (let ((bound (and (derived-mode-p 'message-mode)
+ (save-excursion
+ (goto-char (point-max))
+ (re-search-backward
+ message-signature-separator nil t)))))
+ (if (progn
+ (end-of-line)
+ (re-search-forward
+ (concat org-outline-regexp-bol "\\|"
+ org-footnote-definition-re "\\|"
+ "^\\([ \t]*\n\\)\\{2,\\}") bound 'move))
+ (match-beginning 0)
+ (point)))))
+ (list label beg end
+ (org-trim (buffer-substring-no-properties beg-def end)))))))))
(defun org-footnote-get-next-reference (&optional label backward limit)
"Return complete reference of the next footnote.
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)
+ (let* (ref (origin (point)))
(catch 'exit
(while t
(unless (re-search-forward org-footnote-re limit t)
+ (goto-char origin)
(throw 'exit nil))
;; Beware: with [1]-like footnotes point will be just after
;; the closing square bracket.
(re (format "^\\[%s\\]\\|.\\[%s:" label label))
pos)
(save-excursion
- (when (or (re-search-forward re nil t)
- (and (goto-char (point-min))
- (re-search-forward re nil t))
- (and (progn (widen) t)
- (goto-char (point-min))
- (re-search-forward re nil t)))
- (let ((refp (org-footnote-at-reference-p)))
- (cond
- ((and (nth 3 refp) refp))
- ((org-footnote-at-definition-p))))))))
+ (save-restriction
+ (when (or (re-search-forward re nil t)
+ (and (goto-char (point-min))
+ (re-search-forward re nil t))
+ (and (progn (widen) t)
+ (goto-char (point-min))
+ (re-search-forward re nil t)))
+ (let ((refp (org-footnote-at-reference-p)))
+ (cond
+ ((and (nth 3 refp) refp))
+ ((org-footnote-at-definition-p)))))))))
(defun org-footnote-goto-definition (label)
- "Move point to the definition of the footnote LABEL."
+ "Move point to the definition of the footnote LABEL.
+Return a non-nil value when a definition has been found."
(interactive "sLabel: ")
(org-mark-ring-push)
(let ((def (org-footnote-get-definition label)))
(looking-at (format "\\[%s\\]\\|\\[%s:" label label))
(goto-char (match-end 0))
(org-show-context 'link-search)
- (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'."))))
+ (when (derived-mode-p 'org-mode)
+ (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'."))
+ t)))
(defun org-footnote-goto-previous-reference (label)
"Find the first closest (to point) reference of footnote with label LABEL."
(defun org-footnote-unique-label (&optional current)
"Return a new unique footnote label.
-The returns the firsts fn:N labels that is currently not used."
+
+The function returns the first \"fn:N\" or \"N\" label that is
+currently not used.
+
+Optional argument CURRENT is the list of labels active in the
+buffer."
(unless current (setq current (org-footnote-all-labels)))
(let ((fmt (if (eq org-footnote-auto-label 'plain) "%d" "fn:%d"))
(cnt 1))
(incf cnt))
(format fmt cnt)))
-(defvar org-footnote-label-history nil
- "History of footnote labels entered in current buffer.")
-(make-variable-buffer-local 'org-footnote-label-history)
-
(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 (and (not (bolp)) (org-footnote-in-valid-context-p))
+ (unless (org-footnote-in-valid-context-p)
(error "Cannot insert a footnote here"))
- (let* ((labels (and (not (equal org-footnote-auto-label 'random))
- (org-footnote-all-labels)))
- (propose (org-footnote-unique-label labels))
+ (let* ((lbls (and (not (equal org-footnote-auto-label 'random))
+ (org-footnote-all-labels)))
+ (propose (and (not (equal org-footnote-auto-label 'random))
+ (org-footnote-unique-label lbls)))
(label
(org-footnote-normalize-label
(cond
(require 'org-id)
(substring (org-id-uuid) 0 8))
(t
- (completing-read
+ (org-icompleting-read
"Label (leave empty for anonymous): "
- (mapcar 'list labels) nil nil
- (if (eq org-footnote-auto-label 'confirm) propose nil)
- 'org-footnote-label-history))))))
+ (mapcar 'list lbls) nil nil
+ (if (eq org-footnote-auto-label 'confirm) propose nil)))))))
(cond
+ ((bolp) (error "Cannot create a footnote reference at left margin"))
((not label)
(insert "[fn:: ]")
(backward-char 1))
- ((member label labels)
+ ((member label lbls)
(insert "[" label "]")
(message "New reference to existing note"))
(org-footnote-define-inline
(org-footnote-create-definition label)
(org-footnote-auto-adjust-maybe)))))
+(defvar org-blank-before-new-entry) ; silence byte-compiler
(defun org-footnote-create-definition (label)
"Start the definition of a footnote with label LABEL."
(interactive "sLabel: ")
- (let ((label (org-footnote-normalize-label label)))
+ (let ((label (org-footnote-normalize-label label))
+ electric-indent-mode) ;; Prevent wrong indentation
(cond
- ((org-mode-p)
- ;; No section, put footnote into the current outline node Try to
- ;; find or make the special node
+ ;; In an Org file.
+ ((derived-mode-p 'org-mode)
+ ;; If `org-footnote-section' is defined, find it, or create it
+ ;; at the end of the buffer.
(when org-footnote-section
(goto-char (point-min))
(let ((re (concat "^\\*+[ \t]+" org-footnote-section "[ \t]*$")))
(unless (or (re-search-forward re nil t)
(and (progn (widen) t)
(re-search-forward re nil t)))
- (goto-char (point-max))
- (insert "\n\n* " org-footnote-section "\n"))))
- ;; Now go to the end of this entry and insert there.
+ (goto-char (point-max))
+ (skip-chars-backward " \t\r\n")
+ (unless (bolp) (newline))
+ ;; Insert new section. Separate it from the previous one
+ ;; with a blank line, unless `org-blank-before-new-entry'
+ ;; explicitly says no.
+ (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"))))
+ ;; Move to the end of this entry (which may be
+ ;; `org-footnote-section' or the current one).
(org-footnote-goto-local-insertion-point)
(org-show-context 'link-search))
(t
;; In a non-Org file. Search for footnote tag, or create it if
- ;; necessary (at the end of buffer, or before a signature if in
+ ;; specified (at the end of buffer, or before signature if in
;; Message mode). Set point after any definition already there.
- (let ((tag (concat "^" org-footnote-tag-for-non-org-mode-files "[ \t]*$"))
- (max (save-excursion
- (if (and (derived-mode-p 'message-mode)
- (re-search-forward
- message-signature-separator nil t))
- (copy-marker (point-at-bol) t)
- (copy-marker (point-max) t)))))
+ (let ((tag (and org-footnote-tag-for-non-org-mode-files
+ (concat "^" (regexp-quote
+ org-footnote-tag-for-non-org-mode-files)
+ "[ \t]*$")))
+ (max (if (and (derived-mode-p 'message-mode)
+ (goto-char (point-max))
+ (re-search-backward
+ message-signature-separator nil t))
+ (progn
+ ;; Ensure one blank line separates last
+ ;; footnote from signature.
+ (beginning-of-line)
+ (open-line 2)
+ (point-marker))
+ (point-max-marker))))
+ (set-marker-insertion-type max t)
(goto-char max)
- (unless (re-search-backward tag nil t)
+ ;; Check if the footnote tag is defined but missing. In this
+ ;; case, insert it, before any footnote or one blank line
+ ;; after any previous text.
+ (when (and tag (not (re-search-backward tag nil t)))
(skip-chars-backward " \t\r\n")
- (delete-region (point) max)
- (insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n"))
- ;; Skip existing footnotes.
- (while (re-search-forward org-footnote-definition-re max t))
- (let ((def (org-footnote-at-definition-p)))
- (when def (goto-char (nth 2 def))))
+ (while (re-search-backward org-footnote-definition-re nil t))
+ (unless (bolp) (newline 2))
+ (insert org-footnote-tag-for-non-org-mode-files "\n\n"))
+ ;; Remove superfluous white space and clear marker.
+ (goto-char max)
+ (skip-chars-backward " \t\r\n")
+ (delete-region (point) max)
+ (unless (bolp) (newline))
(set-marker max nil))))
- ;; Insert footnote label, position point and notify user.
- (unless (bolp) (insert "\n"))
- (insert "\n[" label "] \n")
+ ;; Insert footnote label.
+ (when (zerop (org-back-over-empty-lines)) (newline))
+ (insert "[" label "] \n")
(backward-char)
- (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")))
+ ;; Only notify user about next possible action when in an Org
+ ;; buffer, as the bindings may have different meanings otherwise.
+ (when (derived-mode-p 'org-mode)
+ (message
+ "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'."))))
;;;###autoload
(defun org-footnote-action (&optional special)
(org-footnote-goto-previous-reference (car tmp)))
(t (org-footnote-new)))))
-(defvar org-footnote-insert-pos-for-preprocessor 'point-max
- "See `org-footnote-normalize'.")
-
-(defvar org-export-footnotes-seen nil) ; silence byte-compiler
-(defvar org-export-footnotes-data nil) ; silence byte-compiler
-
;;;###autoload
-(defun org-footnote-normalize (&optional sort-only export-props)
+(defun org-footnote-normalize (&optional sort-only)
"Collect the footnotes in various formats and normalize them.
This finds the different sorts of footnotes allowed in Org, and
-normalizes them to the usual [N] format that is understood by the
-Org-mode exporters.
+normalizes them to the usual [N] format.
When SORT-ONLY is set, only sort the footnote definitions into the
-referenced sequence.
-
-If Org is amidst an export process, EXPORT-PROPS will hold the
-export properties of the buffer.
-
-When EXPORT-PROPS is non-nil, the default action is to insert
-normalized footnotes towards the end of the pre-processing buffer.
-Some exporters like docbook, odt, etc. expect that footnote
-definitions be available before any references to them. Such
-exporters can let bind `org-footnote-insert-pos-for-preprocessor' to
-symbol 'point-min to achieve the desired behaviour.
-
-Additional note on `org-footnote-insert-pos-for-preprocessor':
-1. This variable has not effect when FOR-PREPROCESSOR is nil.
-2. This variable (potentially) obviates the need for extra scan
- of pre-processor buffer as witnessed in
- `org-export-docbook-get-footnotes'."
+referenced sequence."
;; This is based on Paul's function, but rewritten.
;;
;; Re-create `org-with-limited-levels', but not limited to Org
org-inlinetask-min-level
(1- org-inlinetask-min-level)))
(nstars (and limit-level
- (if org-odd-levels-only
- (and limit-level (1- (* limit-level 2)))
+ (if org-odd-levels-only (1- (* limit-level 2))
limit-level)))
(org-outline-regexp
(concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ ")))
- ;; Determine the highest marker used so far.
- (ref-table (when export-props org-export-footnotes-seen))
- (count (if (and export-props ref-table)
- (apply 'max (mapcar (lambda (e) (nth 1 e)) ref-table))
- 0))
- ins-point ref)
+ (count 0)
+ ins-point ref ref-table)
(save-excursion
;; 1. Find every footnote reference, extract the definition, and
;; collect that data in REF-TABLE. If SORT-ONLY is nil, also
(goto-char (point-min))
(while (setq ref (org-footnote-get-next-reference))
(let* ((lbl (car ref))
+ (pos (nth 1 ref))
;; When footnote isn't anonymous, check if it's label
;; (REF) is already stored in REF-TABLE. In that case,
;; extract number used to identify it (MARKER). If
;; Replace footnote reference with [MARKER]. Maybe fill
;; paragraph once done. If SORT-ONLY is non-nil, only move
;; to the end of reference found to avoid matching it twice.
- ;; If EXPORT-PROPS isn't nil, also add `org-footnote'
- ;; property to it, so it can be easily recognized by
- ;; exporters.
- (if sort-only
- (goto-char (nth 2 ref))
+ (if sort-only (goto-char (nth 2 ref))
(delete-region (nth 1 ref) (nth 2 ref))
(goto-char (nth 1 ref))
- (let ((new-ref (format "[%d]" marker)))
- (when export-props (org-add-props new-ref '(org-footnote t)))
- (insert new-ref))
+ (insert (format "[%d]" marker))
(and inlinep
org-footnote-fill-after-inline-note-extraction
(org-fill-paragraph)))
- ;; Add label (REF), identifier (MARKER) and definition (DEF)
- ;; to REF-TABLE if data was unknown.
+ ;; Add label (REF), identifier (MARKER), definition (DEF)
+ ;; type (INLINEP) and position (POS) to REF-TABLE if data
+ ;; was unknown.
(unless a
- (let ((def (or (nth 3 ref) ; inline
- (and export-props
- (cdr (assoc lbl org-export-footnotes-data)))
+ (let ((def (or (nth 3 ref) ; Inline definition.
(nth 3 (org-footnote-get-definition lbl)))))
- (push (list lbl marker
- ;; When exporting, each definition goes
- ;; through `org-export-preprocess-string' so
- ;; it is ready to insert in the
- ;; backend-specific buffer.
- (if export-props
- (let ((parameters
- (org-combine-plists
- export-props
- '(:todo-keywords t :tags t :priority t))))
- (org-export-preprocess-string def parameters))
- def)
- inlinep) ref-table)))
- ;; Remove definition of non-inlined footnotes.
- (unless inlinep (org-footnote-delete-definitions lbl))))
+ (push (list lbl marker def
+ ;; Reference beginning position is a marker
+ ;; to preserve it during further buffer
+ ;; modifications.
+ inlinep (copy-marker pos)) ref-table)))))
;; 2. Find and remove the footnote section, if any. Also
;; determine where footnotes shall be inserted (INS-POINT).
- (goto-char (point-min))
(cond
- ((org-mode-p)
- (if (and org-footnote-section
- (re-search-forward
- (concat "^\\*[ \t]+" (regexp-quote org-footnote-section)
- "[ \t]*$")
- nil t))
- (progn
- (setq ins-point (match-beginning 0))
- (delete-region (match-beginning 0) (org-end-of-subtree t)))
- (setq ins-point (point-max))))
+ ((and org-footnote-section (derived-mode-p 'org-mode))
+ (goto-char (point-min))
+ (if (re-search-forward
+ (concat "^\\*[ \t]+" (regexp-quote org-footnote-section)
+ "[ \t]*$") nil t)
+ (delete-region (match-beginning 0) (org-end-of-subtree t t)))
+ ;; A new footnote section is inserted by default at the end of
+ ;; the buffer.
+ (goto-char (point-max))
+ (skip-chars-backward " \r\t\n")
+ (forward-line)
+ (unless (bolp) (newline)))
+ ;; No footnote section set: Footnotes will be added at the end
+ ;; of the section containing their first reference.
+ ((derived-mode-p 'org-mode))
(t
- (when (re-search-forward
- (concat "^"
- (regexp-quote org-footnote-tag-for-non-org-mode-files)
- "[ \t]*$")
- nil t)
- (replace-match ""))
- ;; In message-mode, ensure footnotes are inserted before the
+ ;; Remove any left-over tag in the buffer, if one is set up.
+ (when org-footnote-tag-for-non-org-mode-files
+ (let ((tag (concat "^" (regexp-quote
+ org-footnote-tag-for-non-org-mode-files)
+ "[ \t]*$")))
+ (goto-char (point-min))
+ (while (re-search-forward tag nil t)
+ (replace-match "")
+ (delete-region (point) (progn (forward-line) (point))))))
+ ;; In Message mode, ensure footnotes are inserted before the
;; signature.
- (let ((pt-max
- (or (and (derived-mode-p 'message-mode)
- (save-excursion
- (goto-char (point-max))
- (re-search-backward
- message-signature-separator nil t)
- (1- (point))))
- (point-max))))
- (goto-char pt-max)
- (skip-chars-backward " \t\n\r")
- (forward-line)
- (delete-region (point) pt-max))
- (setq ins-point (point))))
+ (if (and (derived-mode-p 'message-mode)
+ (goto-char (point-max))
+ (re-search-backward message-signature-separator nil t))
+ (beginning-of-line)
+ (goto-char (point-max)))))
+ (setq ins-point (point-marker))
;; 3. Clean-up REF-TABLE.
(setq ref-table
(delq nil
(lambda (x)
(cond
;; When only sorting, ignore inline footnotes.
- ((and sort-only (nth 3 x)) nil)
+ ;; Also clear position marker.
+ ((and sort-only (nth 3 x))
+ (set-marker (nth 4 x) nil) nil)
;; No definition available: provide one.
((not (nth 2 x))
- (append (butlast x 2)
- (list (format "DEFINITION NOT FOUND: %s" (car x))
- (nth 3 x))))
+ (append
+ (list (car x) (nth 1 x)
+ (format "DEFINITION NOT FOUND: %s" (car x)))
+ (nthcdr 3 x)))
(t x)))
ref-table)))
(setq ref-table (nreverse ref-table))
- ;; 4. Insert the footnotes again in the buffer, at the
+ ;; 4. Remove left-over definitions in the buffer.
+ (mapc (lambda (x)
+ (unless (nth 3 x) (org-footnote-delete-definitions (car x))))
+ ref-table)
+ ;; 5. Insert the footnotes again in the buffer, at the
;; appropriate spot.
- (goto-char (or
- (and export-props
- (eq org-footnote-insert-pos-for-preprocessor 'point-min)
- (point-min))
- ins-point
- (point-max)))
+ (goto-char ins-point)
(cond
;; No footnote: exit.
((not ref-table))
;; Cases when footnotes should be inserted in one place.
- ((or (not (org-mode-p))
- org-footnote-section
- (not sort-only))
- ;; Insert again the section title.
- (cond
- ((not (org-mode-p))
- (insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n"))
- ((and org-footnote-section (not export-props))
- (or (bolp) (insert "\n"))
- (insert "* " org-footnote-section "\n")))
- ;; Insert the footnotes.
- (insert "\n"
- (mapconcat (lambda (x) (format "[%s] %s"
- (nth (if sort-only 0 1) x) (nth 2 x)))
- ref-table "\n\n")
- "\n\n")
- ;; When exporting, add newly inserted markers along with their
- ;; associated definition to `org-export-footnotes-seen'.
- (when export-props
- (setq org-export-footnotes-seen ref-table)))
- ;; Else, insert each definition at the end of the section
- ;; containing their first reference. Happens only in Org files
- ;; with no special footnote section, and only when doing
- ;; sorting.
- (t (mapc 'org-insert-footnote-reference-near-definition
- ref-table))))))
-
-(defun org-insert-footnote-reference-near-definition (entry)
- "Find first reference of footnote ENTRY and insert the definition there.
-ENTRY is (fn-label num-mark definition)."
- (when (car entry)
- (goto-char (point-min))
- (let ((ref (org-footnote-get-next-reference (car entry))))
- (when ref
- (goto-char (nth 2 ref))
- (org-footnote-goto-local-insertion-point)
- (insert (format "\n[%s] %s\n" (car entry) (nth 2 entry)))))))
+ ((or (not (derived-mode-p 'org-mode)) org-footnote-section)
+ ;; Insert again the section title, if any. Ensure that title,
+ ;; or the subsequent footnotes, will be separated by a blank
+ ;; lines from the rest of the document. In an Org buffer,
+ ;; separate section with a blank line, unless explicitly
+ ;; stated in `org-blank-before-new-entry'.
+ (if (not (derived-mode-p 'org-mode))
+ (progn (skip-chars-backward " \t\n\r")
+ (delete-region (point) ins-point)
+ (unless (bolp) (newline))
+ (when org-footnote-tag-for-non-org-mode-files
+ (insert "\n" org-footnote-tag-for-non-org-mode-files "\n")))
+ (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"))
+ (set-marker ins-point nil)
+ ;; Insert the footnotes, separated by a blank line.
+ (insert
+ (mapconcat
+ (lambda (x)
+ ;; Clean markers.
+ (set-marker (nth 4 x) nil)
+ (format "\n[%s] %s" (nth (if sort-only 0 1) x) (nth 2 x)))
+ ref-table "\n"))
+ (unless (eobp) (insert "\n\n")))
+ ;; Each footnote definition has to be inserted at the end of
+ ;; the section where its first reference belongs.
+ (t
+ (mapc
+ (lambda (x)
+ (let ((pos (nth 4 x)))
+ (goto-char pos)
+ ;; Clean marker.
+ (set-marker pos nil))
+ (org-footnote-goto-local-insertion-point)
+ (insert (format "\n[%s] %s\n"
+ (if sort-only (car x) (nth 1 x))
+ (nth 2 x))))
+ ref-table))))))
(defun org-footnote-goto-local-insertion-point ()
"Find insertion point for footnote, just before next outline heading."
(beginning-of-line 0)
(while (and (not (bobp)) (= (char-after) ?#))
(beginning-of-line 0))
- (if (looking-at "[ \t]*#\\+TBLFM:") (beginning-of-line 2))
+ (if (let ((case-fold-search t)) (looking-at "[ \t]*#\\+tblfm:")) (beginning-of-line 2))
(end-of-line 1)
(skip-chars-backward "\n\r\t ")
(forward-line))
(ndef 0))
(while (re-search-forward def-re nil t)
(let ((full-def (org-footnote-at-definition-p)))
- (delete-region (nth 1 full-def) (nth 2 full-def)))
- (incf ndef))
+ (when full-def
+ ;; Remove the footnote, and all blank lines before it.
+ (goto-char (nth 1 full-def))
+ (skip-chars-backward " \r\t\n")
+ (unless (bolp) (forward-line))
+ (delete-region (point) (nth 2 full-def))
+ (incf ndef))))
ndef)))
(defun org-footnote-delete (&optional label)
(label (cond
;; LABEL is provided as argument.
(label)
- ;; Footnote reference at point. If the footnote is
+ ;; Footnote reference at point. If the footnote is
;; anonymous, delete it and exit instead.
((setq x (org-footnote-at-reference-p))
(or (car x)
(defun org-footnote-renumber-fn:N ()
"Renumber the simple footnotes like fn:17 into a sequence in the document."
(interactive)
- (let (map i (n 0))
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (while (re-search-forward "\\[fn:\\([0-9]+\\)[]:]" nil t)
- (setq i (string-to-number (match-string 1)))
- (when (and (string-match "\\S-" (buffer-substring
- (point-at-bol) (match-beginning 0)))
- (not (assq i map)))
- (push (cons i (number-to-string (incf n))) map)))
- (goto-char (point-min))
- (while (re-search-forward "\\(\\[fn:\\)\\([0-9]+\\)\\([]:]\\)" nil t)
- (replace-match (concat "\\1" (cdr (assq (string-to-number (match-string 2)) map)) "\\3")))))))
+ (let (map (n 0))
+ (org-with-wide-buffer
+ (goto-char (point-min))
+ (while (re-search-forward "\\[fn:\\([0-9]+\\)[]:]" nil t)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ ;; Ensure match is a footnote reference or definition.
+ (when (save-match-data (if (bolp)
+ (org-footnote-at-definition-p)
+ (org-footnote-at-reference-p)))
+ (let ((new-val (or (cdr (assoc (match-string 1) map))
+ (number-to-string (incf n)))))
+ (unless (assoc (match-string 1) map)
+ (push (cons (match-string 1) new-val) map))
+ (replace-match new-val nil nil nil 1))))))))
(defun org-footnote-auto-adjust-maybe ()
"Renumber and/or sort footnotes according to user settings."
(provide 'org-footnote)
-
+;; Local variables:
+;; generated-autoload-file: "org-loaddefs.el"
+;; End:
;;; org-footnote.el ends here