;;; footnotes.el v 0.2 ;;; Functions for automatically inserting footnotes. ;;; Use [C-c f] to start a new footnote, [C-c e] to end it. These ;;; keys are set at load-time for text-mode-map, and may be set for ;;; other modes with (footnote-set-keys ). ;;; Footnotes may be recursive, and are marked with [n] in the text, ;;; and are placed on a line starting with [n] at the bottom of the mail ;;; (excluding signature, if footnote-below-signature is not t). ;;; If end-footnote ([C-c e]) is given a prefix argument, it will not end ;;; the editing of the last footnote, but instead try to figure out the ;;; footnote the point is in, and move to its mark in the text. ;;; TODO: Be a bit more intelligent about signatures when starting ;;; a footnote. Should make proper room for it. ;;; Version 0.2 ;;; Can now search for existing footnotes using prefix arg to start-footnote ;;; This program is copyright (c) 1998 Lars R. Clausen (elascurn@daimi.aau.dk) ;;; This code is freely distributable. ;;; You may modify this code and distribute modified versions, as long as ;;; you don't remove or alter the copyright notice. (defvar footnote-below-signature nil "*If this variable is t, footnotes are inserted after the signature. Otherwise, they are inserted just before it.") (defvar footnote-stack nil "The stack of footnotes being written in each buffer.") (make-variable-buffer-local 'footnote-stack) (defvar footnote-next-marker nil "The function for getting the next footnote marker. Must return a string. If nil, normal numbers are used. The function is given the previously used string as an argument." ) (defvar footnote-is-marker nil "The function to determine if a text is really a footnote marker. If nil, returns true if the text is a number." ) (defun first-footnote-place () (if footnote-below-signature (point-max-marker) (save-excursion (goto-char (point-max)) (if (re-search-backward "^-- ?$" nil t) (progn (forward-line -1) (point-marker) ) (point-max-marker))))) (defun find-next-footnote-number () ;; Try to find the last footnote (save-excursion (goto-char (point-max)) (if (re-search-backward "^\\[\\([0-9]+\\)\\] " nil t) (let ((nr (string-to-int (buffer-substring (match-beginning 1) (match-end 1))))) (end-of-paragraph-text) (forward-line 1) (cons (1+ nr) (point-marker))) (cons 1 (first-footnote-place)) ))) (defun footnote-find-mark-here () ;; Find the nearest footnote mark on the same line (let* ((start-place (point)) (start-line (progn (beginning-of-line) (point)))) (goto-char start-place) (if (looking-at "\\[[^]]*\\]") (point) (if (not (re-search-backward "\\[[^]]*\\=" start-line t)) (re-search-backward "\\[[^]]*\\]\\=" start-line t)) (if (looking-at "\\[[^]]*\\]") (point))))) (defun start-footnote (jump) "Start writing a footnote. With prefix argument, just jump to the footnote found at point." (interactive "P") (if jump (let ((start-place (point)) (footnote-at (footnote-find-mark-here))) (if (not footnote-at) (progn (goto-char start-place) (message (concat "No footnote found here."))) (goto-char footnote-at) (looking-at "\\[[^]]*\\]") (goto-char (match-end 0)) (let ((footnote-marker (buffer-substring (match-beginning 0) (match-end 0)))) (forward-char 1) (if (search-forward footnote-marker nil t) (setq footnote-stack (cons (set-marker (make-marker) start-place) footnote-stack)) (goto-char start-place) (message (concat "No footnote found for " footnote-marker ".")))))) (let ((foot (find-next-footnote-number))) (let ((nr (car foot)) (place (cdr foot))) (if (eq (marker-position place) (point)) (progn (insert "\n\n") (setq place (point-marker)) (backward-char 2)) (insert (concat "[" (int-to-string nr) "]")) (setq footnote-stack (cons (point-marker) footnote-stack)) (goto-char (marker-position place)) (insert (concat "\n[" (int-to-string nr) "] \n")) (backward-char 1)))))) (defun footnote-find-here () "Find the footnote # for the footnote here." (save-excursion (if (re-search-backward "^\\[\\([0-9]+\\)\\] " nil t) (string-to-int (buffer-substring (match-beginning 1) (match-end 1))) nil))) (defun footnote-find-start (nr) "Find the footnote marker for the given nr." (let ((marker-found nil)) (save-excursion (let ((marks footnote-stack) (search-string (concat "\\[" (int-to-string nr) "\\]\\="))) (while marks (goto-char (marker-position (car marks))) (if (re-search-backward search-string nil t) (setq marker-found (car marks))) (setq marks (cdr marks))))) marker-found)) (defun end-footnote (here) "End writing a footnote. If argument is non-nil, end writing the footnote at the cursor position, otherwise end writing the last one started." (interactive "P") (if (null footnote-stack) (message "Not in a footnote.") (if here (let ((current (footnote-find-here))) (if (not current) (message "No footnote found hereabouts") (let ((start-place (footnote-find-start current))) (if (not start-place) (message (concat "Sorry, I couldn't find the footnote mark for footnote " (int-to-string current))) (goto-char (marker-position start-place)))))) (goto-char (marker-position (car footnote-stack))) (setq footnote-stack (cdr footnote-stack))))) (defun footnote-set-keys (mode-map) "Set [C-c f] and [C-c e] to be footnote-keys." (define-key mode-map "\C-cf" 'start-footnote) (define-key mode-map "\C-ce" 'end-footnote)) (footnote-set-keys text-mode-map)