;;; org-footnote.el --- Footnote support in Org and elsewhere
;;
-;; Copyright (C) 2009 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
;;
;; Author: Carsten Dominik <carsten at orgmode dot org>
;; Keywords: outlines, hypermedia, calendar, wp
;; Homepage: http://orgmode.org
-;; Version: 6.19e
+;; Version: 6.33x
;;
;; This file is part of GNU Emacs.
;;
(require 'org-macs)
(require 'org-compat)
+(declare-function org-in-commented-line "org" ())
(declare-function org-in-regexp "org" (re &optional nlines visually))
(declare-function org-mark-ring-push "org" (&optional pos buffer))
(declare-function outline-next-heading "outline")
(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" ())
+(defvar org-odd-levels-only) ;; defined in org.el
(defconst org-footnote-re
(concat "[^][\n]" ; to make sure it is not at the beginning of a line
"\\]")
"Regular expression for matching footnotes.")
-(defconst org-footnote-definition-re
+(defconst org-footnote-definition-re
(org-re "^\\(\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]\\)")
"Regular expression matching the definition of a footnote.")
heading will be removed after extracting footnote definitions."
:group 'org-footnotes
:type '(choice
- (string :tag "Collect fotnotes under heading")
+ (string :tag "Collect footnotes under heading")
(const :tag "Define footnotes locally" nil)))
(defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:"
plain Automatically create plain number labels like [1]"
:group 'org-footnote
:type '(choice
- (const :tag "Frompt for label" nil)
+ (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 automatic [N]" plain)))
+(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 "Renumber" renumber)
+ (const :tag "Sort" sort)
+ (const :tag "Renumber and Sort" t)))
+
(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.
If yes, return the beginning position, the label, and the definition, if local."
(when (org-in-regexp org-footnote-re 15)
(list (match-beginning 0)
- (or (match-string 1)
+ (or (match-string 1)
(if (equal (match-string 2) "fn:") nil (match-string 2)))
(match-string 4))))
This matches only pure definitions like [1] or [fn:name] at the beginning
of a line. It does not a references like [fn:name:definition], where the
footnote text is included and defined locally.
-The return value will be nil if not at a foornote definition, and a list
+The return value will be nil if not at a footnote definition, and a list
with start and label of the footnote if there is a definition at point."
(save-excursion
(end-of-line 1)
(message "New reference to existing note"))
(org-footnote-define-inline
(insert "[" label ": ]")
- (backward-char 1))
+ (backward-char 1)
+ (org-footnote-auto-adjust-maybe))
(t
(insert "[" label "]")
- (org-footnote-create-definition label)))))
+ (org-footnote-create-definition label)
+ (org-footnote-auto-adjust-maybe)))))
(defun org-footnote-create-definition (label)
"Start the definition of a footnote with label LABEL."
(goto-char (point-max))
(insert "\n\n* " org-footnote-section "\n")))
;; Now go to the end of this entry and insert there.
- (org-footnote-goto-local-insertion-point))
+ (org-footnote-goto-local-insertion-point)
+ (org-show-context 'link-search))
(t
(setq re (concat "^" org-footnote-tag-for-non-org-mode-files "[ \t]*$"))
(unless (re-search-forward re nil t)
;;;###autoload
(defun org-footnote-action (&optional special)
"Do the right thing for footnotes.
-When at a foornote reference, jump to the definition. When at a definition,
-jump to the refernces. When neither at definition or reference,
+When at a footnote reference, jump to the definition. When at a definition,
+jump to the references. When neither at definition or reference,
create a new footnote, interactively.
With prefix arg SPECIAL, offer additional commands in a menu."
(interactive "P")
(let (tmp c)
(cond
(special
- (message "Footnotes: [s]ort | convert to [n]umeric | [d]elete")
+ (message "Footnotes: [s]ort | [r]enumber fn:N | [S]=r+s |->[n]umeric | [d]elete")
(setq c (read-char-exclusive))
(cond
((equal c ?s)
(org-footnote-normalize 'sort))
+ ((equal c ?r)
+ (org-footnote-renumber-fn:N))
+ ((equal c ?S)
+ (org-footnote-renumber-fn:N)
+ (org-footnote-normalize 'sort))
((equal c ?n)
(org-footnote-normalize))
((equal c ?d)
;;;###autoload
(defun org-footnote-normalize (&optional sort-only for-preprocessor)
"Collect the footnotes in various formats and normalize them.
-This find the different sorts of footnotes allowed in Org, and
+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.
When SORT-ONLY is set, only sort the footnote definitions into the
referenced sequence."
;; This is based on Paul's function, but rewritten.
- (let ((count 0) ref def idef ref-table beg beg1 marker a before
- ins-point)
+ (let* ((limit-level
+ (and (boundp 'org-inlinetask-min-level)
+ 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)))
+ limit-level)))
+ (outline-regexp
+ (concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ ")))
+ (count 0)
+ ref def idef ref-table beg beg1 marker a before ins-point)
(save-excursion
;; Now find footnote references, and extract the definitions
(goto-char (point-min))
(while (re-search-forward org-footnote-re nil t)
- (org-if-unprotected
- (setq def (match-string 4)
- idef def
- ref (or (match-string 1) (match-string 2))
- before (char-to-string (char-after (match-beginning 0))))
- (if (equal ref "fn:") (setq ref nil))
- (if (and ref (setq a (assoc ref ref-table)))
- (progn
- (setq marker (nth 1 a))
- (unless (nth 2 a) (setf (caddr a) def)))
- (setq marker (number-to-string (incf count))))
- (save-match-data
- (if def
- (setq def (org-trim def))
- (save-excursion
- (goto-char (point-min))
- (if (not (re-search-forward (concat "^\\[" (regexp-quote ref)
- "\\]") nil t))
- (setq def nil)
- (setq beg (match-beginning 0))
- (setq beg1 (match-end 0))
- (re-search-forward
- (org-re "^[ \t]*$\\|^\\*+ \\|^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]")
- nil 'move)
- (setq def (buffer-substring beg1 (or (match-beginning 0)
- (point-max))))
- (goto-char beg)
- (skip-chars-backward " \t\n\t")
- (delete-region (1+ (point)) (match-beginning 0))))))
- (unless sort-only
- (replace-match (concat before "[" marker "]"))
- (and idef
- org-footnote-fill-after-inline-note-extraction
- (fill-paragraph)))
- (if (not a) (push (list ref marker def) ref-table))))
+ (unless (or (org-in-commented-line) (org-in-verbatim-emphasis))
+ (org-if-unprotected
+ (setq def (match-string 4)
+ idef def
+ ref (or (match-string 1) (match-string 2))
+ before (char-to-string (char-after (match-beginning 0))))
+ (if (equal ref "fn:") (setq ref nil))
+ (if (and ref (setq a (assoc ref ref-table)))
+ (progn
+ (setq marker (nth 1 a))
+ (unless (nth 2 a) (setf (caddr a) def)))
+ (setq marker (number-to-string (incf count))))
+ (save-match-data
+ (if def
+ (setq def (org-trim def))
+ (save-excursion
+ (goto-char (point-min))
+ (if (not (re-search-forward (concat "^\\[" (regexp-quote ref)
+ "\\]") nil t))
+ (setq def nil)
+ (setq beg (match-beginning 0))
+ (setq beg1 (match-end 0))
+ (re-search-forward
+ (org-re "^[ \t]*$\\|^\\*+ \\|^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]")
+ nil 'move)
+ (setq def (buffer-substring beg1 (or (match-beginning 0)
+ (point-max))))
+ (goto-char beg)
+ (skip-chars-backward " \t\n\t")
+ (delete-region (1+ (point)) (match-beginning 0))))))
+ (unless sort-only
+ (replace-match (concat before "[" marker "]"))
+ (and idef
+ org-footnote-fill-after-inline-note-extraction
+ (fill-paragraph)))
+ (if (not a) (push (list ref marker def (if idef t nil))
+ ref-table)))))
;; First find and remove the footnote section
(goto-char (point-min))
(insert "* " org-footnote-section "\n")
(setq ins-point (point))))))
(t
- (if (re-search-forward
+ (if (re-search-forward
(concat "^"
(regexp-quote org-footnote-tag-for-non-org-mode-files)
"[ \t]*$")
(delete-region (point) (point-max))
(insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n")
(setq ins-point (point))))
-
+
;; Insert the footnotes again
(goto-char (or ins-point (point-max)))
(setq ref-table (reverse ref-table))
(when sort-only
- ;; remove anonymous fotnotes from the list
+ ;; remove anonymous and inline footnotes from the list
(setq ref-table
(delq nil (mapcar
(lambda (x) (and (car x)
(not (equal (car x) "fn:"))
+ (not (nth 3 x))
x))
ref-table))))
;; Make sure each footnote has a description, or an error message.
(defun org-footnote-goto-local-insertion-point ()
"Find insertion point for footnote, just before next outline heading."
- (outline-next-heading)
+ (org-with-limited-levels (outline-next-heading))
(or (bolp) (newline))
(beginning-of-line 0)
(while (and (not (bobp)) (= (char-after) ?#))
(beginning-of-line 0))
- (if (looking-at "#\\+TBLFM:") (beginning-of-line 2))
+ (if (looking-at "[ \t]*#\\+TBLFM:") (beginning-of-line 2))
(end-of-line 1)
(skip-chars-backward "\n\r\t "))
(goto-char (point-max)))
(delete-region beg (point))
(incf ndef))))
+ (org-footnote-auto-adjust-maybe)
(message "%d definition(s) of and %d reference(s) of footnote %s removed"
ndef nref label))))
+(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")))))))
+
+(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 (nth 1 (org-footnote-at-definition-p))))
+ (org-footnote-normalize 'sort)
+ (when label
+ (goto-char (point-min))
+ (and (re-search-forward (concat "^\\[" (regexp-quote label) "\\]")
+ nil t)
+ (progn (insert " ")
+ (just-one-space)))))))
+
(provide 'org-footnote)
;; arch-tag: 1b5954df-fb5d-4da5-8709-78d944dbfc37