diff options
Diffstat (limited to 'lisp/mail/footnote.el')
-rw-r--r-- | lisp/mail/footnote.el | 466 |
1 files changed, 316 insertions, 150 deletions
diff --git a/lisp/mail/footnote.el b/lisp/mail/footnote.el index 5a04eea25ac..f5d280ae1ea 100644 --- a/lisp/mail/footnote.el +++ b/lisp/mail/footnote.el @@ -1,8 +1,9 @@ -;;; footnote.el --- footnote support for message mode +;;; footnote.el --- footnote support for message mode -*- lexical-binding:t -*- ;; Copyright (C) 1997, 2000-2018 Free Software Foundation, Inc. -;; Author: Steven L Baur <steve@xemacs.org> +;; Author: Steven L Baur <steve@xemacs.org> (1997-2011) +;; Boruch Baum <boruch_baum@gmx.com> (2017-) ;; Keywords: mail, news ;; Version: 0.19 @@ -29,9 +30,36 @@ ;; [1] Footnotes look something like this. Along with some decorative ;; stuff. -;; TODO: -;; Reasonable Undo support. -;; more language styles. +;;;; TODO: +;; + Reasonable Undo support. +;; - could use an `apply' entry in the buffer-undo-list to be warned when +;; a footnote we inserted is removed via undo. +;; - should try to handle the more general problem of deleting/removing +;; footnotes via standard editing commands rather than via footnote +;; commands. +;; + more language styles. +;; + The key sequence 'C-c ! a C-y C-c ! b' should auto-fill the +;; footnote in adaptive fill mode. This does not seem to be a bug in +;; `adaptive-fill' because it behaves that way on all point movements +;; + Handle footmode mode elegantly in all modes, even if that means refuses to +;; accept the burden. For example, in a programming language mode, footnotes +;; should be commented. +;; + Manually autofilling the a first footnote should not cause it to +;; wrap into the footnote section tag +;; + Current solution adds a second newline after the section tag, so it is +;; clearly a separate paragraph. There may be stylistic objections to this. +;; + Footnotes with multiple paragraphs should not have their first +;; line out-dented. +;; + Upon leaving footnote area, perform an auto-fill on an entire +;; footnote (including multiple paragraphs), or on entire footnote area. +;; + fill-paragraph takes arg REGION, but seemingly only when called +;; interactively. +;; + At some point, it became necessary to change `footnote-section-tag-regexp' +;; to remove its trailing space. (Adaptive fill side-effect?) +;; + useful for lazy testing +;; (setq footnote-narrow-to-footnotes-when-editing t) +;; (setq footnote-section-tag "Footnotes: ") +;; (setq footnote-section-tag-regexp "Footnotes\\(\\[.\\]\\)?:") ;;; Code: @@ -92,20 +120,25 @@ After that, changing the prefix key requires manipulating keymaps." ;;; Interface variables that probably shouldn't be changed -(defcustom footnote-section-tag "Footnotes: " +(defcustom footnote-section-tag "Footnotes:" "Tag inserted at beginning of footnote section. If you set this to the empty string, no tag is inserted and the value of `footnote-section-tag-regexp' is ignored. Customizing this variable has no effect on buffers already displaying footnotes." + :version "27.1" :type 'string :group 'footnote) -(defcustom footnote-section-tag-regexp "Footnotes\\(\\[.\\]\\)?: " +(defcustom footnote-section-tag-regexp + ;; Even if `footnote-section-tag' has a trailing space, let's not require it + ;; here, since it might be trimmed by various commands. + "Footnotes\\(\\[.\\]\\)?:" "Regexp which indicates the start of a footnote section. This variable is disregarded when `footnote-section-tag' is the empty string. Customizing this variable has no effect on buffers already displaying footnotes." + :version "27.1" :type 'regexp :group 'footnote) @@ -124,13 +157,21 @@ has no effect on buffers already displaying footnotes." :type 'string :group 'footnote) -(defcustom footnote-signature-separator (if (boundp 'message-signature-separator) - message-signature-separator - "^-- $") +(defcustom footnote-signature-separator + (if (boundp 'message-signature-separator) + message-signature-separator + "^-- $") "Regexp used by Footnote mode to recognize signatures." :type 'regexp :group 'footnote) +(defcustom footnote-align-to-fn-text t + "How to left-align footnote text. +If nil, footnote text is to be aligned flush left with left side +of the footnote number. If non-nil, footnote text is to be aligned +left with the first character of footnote text." + :type 'boolean) + ;;; Private variables (defvar footnote-style-number nil @@ -148,12 +189,14 @@ has no effect on buffers already displaying footnotes." (defvar footnote-mouse-highlight 'highlight "Text property name to enable mouse over highlight.") +(defvar footnote-mode) + ;;; Default styles ;;; NUMERIC (defconst footnote-numeric-regexp "[0-9]+" "Regexp for digits.") -(defun Footnote-numeric (n) +(defun footnote--numeric (n) "Numeric footnote style. Use Arabic numerals for footnoting." (int-to-string n)) @@ -165,7 +208,7 @@ Use Arabic numerals for footnoting." (defconst footnote-english-upper-regexp "[A-Z]+" "Regexp for upper case English alphabet.") -(defun Footnote-english-upper (n) +(defun footnote--english-upper (n) "Upper case English footnoting. Wrapping around the alphabet implies successive repetitions of letters." (let* ((ltr (mod (1- n) (length footnote-english-upper))) @@ -184,7 +227,7 @@ Wrapping around the alphabet implies successive repetitions of letters." (defconst footnote-english-lower-regexp "[a-z]+" "Regexp of lower case English alphabet.") -(defun Footnote-english-lower (n) +(defun footnote--english-lower (n) "Lower case English footnoting. Wrapping around the alphabet implies successive repetitions of letters." (let* ((ltr (mod (1- n) (length footnote-english-lower))) @@ -202,27 +245,28 @@ Wrapping around the alphabet implies successive repetitions of letters." (50 . "l") (100 . "c") (500 . "d") (1000 . "m")) "List of roman numerals with their values.") -(defconst footnote-roman-lower-regexp "[ivxlcdm]+" +(defconst footnote-roman-lower-regexp + (concat "[" (mapconcat #'cdr footnote-roman-lower-list "") "]+") "Regexp of roman numerals.") -(defun Footnote-roman-lower (n) +(defun footnote--roman-lower (n) "Generic Roman number footnoting." - (Footnote-roman-common n footnote-roman-lower-list)) + (footnote--roman-common n footnote-roman-lower-list)) ;;; ROMAN UPPER (defconst footnote-roman-upper-list - '((1 . "I") (5 . "V") (10 . "X") - (50 . "L") (100 . "C") (500 . "D") (1000 . "M")) + (mapcar (lambda (x) (cons (car x) (upcase (cdr x)))) + footnote-roman-lower-list) "List of roman numerals with their values.") -(defconst footnote-roman-upper-regexp "[IVXLCDM]+" +(defconst footnote-roman-upper-regexp (upcase footnote-roman-lower-regexp) "Regexp of roman numerals. Not complete") -(defun Footnote-roman-upper (n) +(defun footnote--roman-upper (n) "Generic Roman number footnoting." - (Footnote-roman-common n footnote-roman-upper-list)) + (footnote--roman-common n footnote-roman-upper-list)) -(defun Footnote-roman-common (n footnote-roman-list) +(defun footnote--roman-common (n footnote-roman-list) "Lower case Roman footnoting." (let* ((our-list footnote-roman-list) (rom-lngth (length our-list)) @@ -257,22 +301,22 @@ Wrapping around the alphabet implies successive repetitions of letters." ;; (message "pairs are: rom-low: %S, rom-high: %S, rom-div: %S" ;; rom-low-pair rom-high-pair rom-div-pair) (cond - ((< n 0) (error "Footnote-roman-common called with n < 0")) + ((< n 0) (error "footnote--roman-common called with n < 0")) ((= n 0) "") ((= n (car rom-low-pair)) (cdr rom-low-pair)) ((= n (car rom-high-pair)) (cdr rom-high-pair)) ((= (car rom-low-pair) (car rom-high-pair)) (concat (cdr rom-low-pair) - (Footnote-roman-common + (footnote--roman-common (- n (car rom-low-pair)) footnote-roman-list))) ((>= rom-div 0) (concat (cdr rom-div-pair) (cdr rom-high-pair) - (Footnote-roman-common + (footnote--roman-common (- n (- (car rom-high-pair) (car rom-div-pair))) footnote-roman-list))) (t (concat (cdr rom-low-pair) - (Footnote-roman-common + (footnote--roman-common (- n (car rom-low-pair)) footnote-roman-list))))))) @@ -285,7 +329,7 @@ Wrapping around the alphabet implies successive repetitions of letters." (defconst footnote-latin-regexp (concat "[" footnote-latin-string "]") "Regexp for Latin-1 footnoting characters.") -(defun Footnote-latin (n) +(defun footnote--latin (n) "Latin-1 footnote style. Use a range of Latin-1 non-ASCII characters for footnoting." (string (aref footnote-latin-string @@ -299,7 +343,7 @@ Use a range of Latin-1 non-ASCII characters for footnoting." (defconst footnote-unicode-regexp (concat "[" footnote-unicode-string "]+") "Regexp for Unicode footnoting characters.") -(defun Footnote-unicode (n) +(defun footnote--unicode (n) "Unicode footnote style. Use Unicode characters for footnoting." (let (modulus result done) @@ -310,18 +354,70 @@ Use Unicode characters for footnoting." (push (aref footnote-unicode-string modulus) result)) (apply #'string result))) +;; Hebrew + +(defconst footnote-hebrew-numeric + '( + ("א" "ב" "ג" "ד" "ה" "ו" "ז" "ח" "ט") + ("י" "כ" "ל" "מ" "נ" "ס" "ע" "פ" "צ") + ("ק" "ר" "ש" "ת" "תק" "תר" "תש" "תת" "תתק"))) + +(defconst footnote-hebrew-numeric-regex + (concat "[" (apply #'concat (apply #'append footnote-hebrew-numeric)) "']+")) +;; (defconst footnote-hebrew-numeric-regex "\\([אבגדהוזחט]'\\)?\\(ת\\)?\\(ת\\)?\\([קרשת]\\)?\\([טיכלמנסעפצ]\\)?\\([אבגדהוזחט]\\)?") + +(defun footnote--hebrew-numeric (n) + "Supports 9999 footnotes, then rolls over." + (let* ((n (+ (mod n 10000) (/ n 10000))) + (thousands (/ n 1000)) + (hundreds (/ (mod n 1000) 100)) + (tens (/ (mod n 100) 10)) + (units (mod n 10)) + (special (cond + ((not (= tens 1)) nil) + ((= units 5) "טו") + ((= units 6) "טז")))) + (concat + (when (/= 0 thousands) + (concat (nth (1- thousands) (nth 0 footnote-hebrew-numeric)) "'")) + (when (/= 0 hundreds) + (nth (1- hundreds) (nth 2 footnote-hebrew-numeric))) + (or special + (concat + (when (/= 0 tens) (nth (1- tens) (nth 1 footnote-hebrew-numeric))) + (when (/= 0 units) (nth (1- units) (nth 0 footnote-hebrew-numeric)))))))) + +(defconst footnote-hebrew-symbolic + '( + "א" "ב" "ג" "ד" "ה" "ו" "ז" "ח" "ט" "י" "כ" "ל" "מ" "נ" "ס" "ע" "פ" "צ" "ק" "ר" "ש" "ת")) + +(defconst footnote-hebrew-symbolic-regex + (concat "[" (apply #'concat footnote-hebrew-symbolic) "]")) + +(defun footnote--hebrew-symbolic (n) + "Only 22 elements, per the style of eg. 'פירוש שפתי חכמים על רש״י'. +Proceeds from `י' to `כ', from `צ' to `ק'. After `ת', rolls over to `א'." + (nth (mod (1- n) 22) footnote-hebrew-symbolic)) + ;;; list of all footnote styles (defvar footnote-style-alist - `((numeric Footnote-numeric ,footnote-numeric-regexp) - (english-lower Footnote-english-lower ,footnote-english-lower-regexp) - (english-upper Footnote-english-upper ,footnote-english-upper-regexp) - (roman-lower Footnote-roman-lower ,footnote-roman-lower-regexp) - (roman-upper Footnote-roman-upper ,footnote-roman-upper-regexp) - (latin Footnote-latin ,footnote-latin-regexp) - (unicode Footnote-unicode ,footnote-unicode-regexp)) + `((numeric footnote--numeric ,footnote-numeric-regexp) + (english-lower footnote--english-lower ,footnote-english-lower-regexp) + (english-upper footnote--english-upper ,footnote-english-upper-regexp) + (roman-lower footnote--roman-lower ,footnote-roman-lower-regexp) + (roman-upper footnote--roman-upper ,footnote-roman-upper-regexp) + (latin footnote--latin ,footnote-latin-regexp) + (unicode footnote--unicode ,footnote-unicode-regexp) + (hebrew-numeric footnote--hebrew-numeric ,footnote-hebrew-numeric-regex) + (hebrew-symbolic footnote--hebrew-symbolic ,footnote-hebrew-symbolic-regex)) "Styles of footnote tags available. -By default only boring Arabic numbers, English letters and Roman Numerals -are available.") +By default, Arabic numbers, English letters, Roman Numerals, +Latin and Unicode superscript characters, and Hebrew numerals +are available. +Each element of the list should be of the form (NAME FUNCTION REGEXP) +where NAME is a symbol, FUNCTION takes a footnote number and +returns the corresponding representation in that style as a string, +and REGEXP should be a regexp that matches any output of FUNCTION.") (defcustom footnote-style 'numeric "Default style used for footnoting. @@ -332,6 +428,8 @@ roman-lower == i, ii, iii, iv, v, ... roman-upper == I, II, III, IV, V, ... latin == ¹ ² ³ º ª § ¶ unicode == ¹, ², ³, ... +hebrew-numeric == א, ב, ..., יא, ..., תקא... +hebrew-symbolic == א, ב, ..., י, כ, ..., צ, ק, ..., ת, א See also variables `footnote-start-tag' and `footnote-end-tag'. Note: some characters in the unicode style may not show up @@ -339,36 +437,36 @@ properly if the default font does not contain those characters. Customizing this variable has no effect on buffers already displaying footnotes. To change the style of footnotes in such a -buffer use the command `Footnote-set-style'." +buffer use the command `footnote-set-style'." :type (cons 'choice (mapcar (lambda (x) (list 'const (car x))) footnote-style-alist)) :group 'footnote) ;;; Style utilities & functions -(defun Footnote-style-p (style) +(defun footnote--style-p (style) "Return non-nil if style is a valid style known to `footnote-mode'." (assq style footnote-style-alist)) -(defun Footnote-index-to-string (index) +(defun footnote--index-to-string (index) "Convert a binary index into a string to display as a footnote. Conversion is done based upon the current selected style." - (let ((alist (if (Footnote-style-p footnote-style) + (let ((alist (if (footnote--style-p footnote-style) (assq footnote-style footnote-style-alist) (nth 0 footnote-style-alist)))) (funcall (nth 1 alist) index))) -(defun Footnote-current-regexp () +(defun footnote--current-regexp () "Return the regexp of the index of the current style." (concat (nth 2 (or (assq footnote-style footnote-style-alist) (nth 0 footnote-style-alist))) "*")) -(defun Footnote-refresh-footnotes (&optional index-regexp) +(defun footnote--refresh-footnotes (&optional index-regexp) "Redraw all footnotes. You must call this or arrange to have this called after changing footnote styles." (unless index-regexp - (setq index-regexp (Footnote-current-regexp))) + (setq index-regexp (footnote--current-regexp))) (save-excursion ;; Take care of the pointers first (let ((i 0) locn alist) @@ -387,7 +485,7 @@ styles." (propertize (concat footnote-start-tag - (Footnote-index-to-string (1+ i)) + (footnote--index-to-string (1+ i)) footnote-end-tag) 'footnote-number (1+ i) footnote-mouse-highlight t) nil "\\1")) @@ -406,13 +504,13 @@ styles." (propertize (concat footnote-start-tag - (Footnote-index-to-string (1+ i)) + (footnote--index-to-string (1+ i)) footnote-end-tag) 'footnote-number (1+ i)) nil "\\1")) (setq i (1+ i)))))) -(defun Footnote-assoc-index (key alist) +(defun footnote--assoc-index (key alist) "Give index of key in alist." (let ((i 0) (max (length alist)) rc) (while (and (null rc) @@ -422,33 +520,33 @@ styles." (setq i (1+ i))) rc)) -(defun Footnote-cycle-style () +(defun footnote-cycle-style () "Select next defined footnote style." (interactive) - (let ((old (Footnote-assoc-index footnote-style footnote-style-alist)) + (let ((old (footnote--assoc-index footnote-style footnote-style-alist)) (max (length footnote-style-alist)) idx) (setq idx (1+ old)) (when (>= idx max) (setq idx 0)) (setq footnote-style (car (nth idx footnote-style-alist))) - (Footnote-refresh-footnotes (nth 2 (nth old footnote-style-alist))))) + (footnote--refresh-footnotes (nth 2 (nth old footnote-style-alist))))) -(defun Footnote-set-style (&optional style) +(defun footnote-set-style (&optional style) "Select a specific style." (interactive (list (intern (completing-read "Footnote Style: " - obarray #'Footnote-style-p 'require-match)))) - (let ((old (Footnote-assoc-index footnote-style footnote-style-alist))) + obarray #'footnote--style-p 'require-match)))) + (let ((old (footnote--assoc-index footnote-style footnote-style-alist))) (setq footnote-style style) - (Footnote-refresh-footnotes (nth 2 (nth old footnote-style-alist))))) + (footnote--refresh-footnotes (nth 2 (nth old footnote-style-alist))))) ;; Internal functions -(defun Footnote-insert-numbered-footnote (arg &optional mousable) +(defun footnote--insert-numbered-footnote (arg &optional mousable) "Insert numbered footnote at (point)." (let ((string (concat footnote-start-tag - (Footnote-index-to-string arg) + (footnote--index-to-string arg) footnote-end-tag))) (insert-before-markers (if mousable @@ -456,7 +554,7 @@ styles." string 'footnote-number arg footnote-mouse-highlight t) (propertize string 'footnote-number arg))))) -(defun Footnote-renumber (from to pointer-alist text-alist) +(defun footnote--renumber (_from to pointer-alist text-alist) "Renumber a single footnote." (let* ((posn-list (cdr pointer-alist))) (setcar pointer-alist to) @@ -464,49 +562,40 @@ styles." (while posn-list (goto-char (car posn-list)) (when (looking-back (concat (regexp-quote footnote-start-tag) - (Footnote-current-regexp) + (footnote--current-regexp) (regexp-quote footnote-end-tag)) (line-beginning-position)) (replace-match (propertize (concat footnote-start-tag - (Footnote-index-to-string to) + (footnote--index-to-string to) footnote-end-tag) 'footnote-number to footnote-mouse-highlight t))) (setq posn-list (cdr posn-list))) (goto-char (cdr text-alist)) (when (looking-at (concat (regexp-quote footnote-start-tag) - (Footnote-current-regexp) + (footnote--current-regexp) (regexp-quote footnote-end-tag))) (replace-match (propertize (concat footnote-start-tag - (Footnote-index-to-string to) + (footnote--index-to-string to) footnote-end-tag) 'footnote-number to))))) -;; Not needed? -(defun Footnote-narrow-to-footnotes () +(defun footnote--narrow-to-footnotes () "Restrict text in buffer to show only text of footnotes." - (interactive) ; testing - (goto-char (point-max)) - (when (re-search-backward footnote-signature-separator nil t) - (let ((end (point))) - (cond - ((and (not (string-equal footnote-section-tag "")) - (re-search-backward - (concat "^" footnote-section-tag-regexp) nil t)) - (narrow-to-region (point) end)) - (footnote-text-marker-alist - (narrow-to-region (cdar footnote-text-marker-alist) end)))))) + (interactive) ; testing + (narrow-to-region (footnote--get-area-point-min) + (footnote--get-area-point-max))) -(defun Footnote-goto-char-point-max () +(defun footnote--goto-char-point-max () "Move to end of buffer or prior to start of .signature." (goto-char (point-max)) (or (re-search-backward footnote-signature-separator nil t) (point))) -(defun Footnote-insert-text-marker (arg locn) +(defun footnote--insert-text-marker (arg locn) "Insert a marker pointing to footnote ARG, at buffer location LOCN." (let ((marker (make-marker))) (unless (assq arg footnote-text-marker-alist) @@ -514,9 +603,9 @@ styles." (setq footnote-text-marker-alist (cons (cons arg marker) footnote-text-marker-alist)) (setq footnote-text-marker-alist - (Footnote-sort footnote-text-marker-alist))))) + (footnote--sort footnote-text-marker-alist))))) -(defun Footnote-insert-pointer-marker (arg locn) +(defun footnote--insert-pointer-marker (arg locn) "Insert a marker pointing to footnote ARG, at buffer location LOCN." (let ((marker (make-marker)) alist) @@ -527,14 +616,14 @@ styles." (setq footnote-pointer-marker-alist (cons (cons arg (list marker)) footnote-pointer-marker-alist)) (setq footnote-pointer-marker-alist - (Footnote-sort footnote-pointer-marker-alist))))) + (footnote--sort footnote-pointer-marker-alist))))) -(defun Footnote-insert-footnote (arg) +(defun footnote--insert-footnote (arg) "Insert a footnote numbered ARG, at (point)." (push-mark) - (Footnote-insert-pointer-marker arg (point)) - (Footnote-insert-numbered-footnote arg t) - (Footnote-goto-char-point-max) + (footnote--insert-pointer-marker arg (point)) + (footnote--insert-numbered-footnote arg t) + (footnote--goto-char-point-max) (if (cond ((not (string-equal footnote-section-tag "")) (re-search-backward (concat "^" footnote-section-tag-regexp) nil t)) @@ -542,8 +631,8 @@ styles." (goto-char (cdar footnote-text-marker-alist)))) (save-restriction (when footnote-narrow-to-footnotes-when-editing - (Footnote-narrow-to-footnotes)) - (Footnote-goto-footnote (1- arg)) ; evil, FIXME (less evil now) + (footnote--narrow-to-footnotes)) + (footnote-goto-footnote (1- arg)) ; evil, FIXME (less evil now) ;; (message "Inserting footnote %d" arg) (unless (or (eq arg 1) @@ -552,11 +641,11 @@ styles." "\n\n" (concat "\n" (regexp-quote footnote-start-tag) - (Footnote-current-regexp) + (footnote--current-regexp) (regexp-quote footnote-end-tag))) nil t) (unless (beginning-of-line) t)) - (Footnote-goto-char-point-max) + (footnote--goto-char-point-max) (cond ((not (string-equal footnote-section-tag "")) (re-search-backward @@ -570,46 +659,115 @@ styles." (unless (string-equal footnote-section-tag "") (insert footnote-section-tag "\n"))) (let ((old-point (point))) - (Footnote-insert-numbered-footnote arg nil) - (Footnote-insert-text-marker arg old-point))) + (footnote--insert-numbered-footnote arg nil) + (footnote--insert-text-marker arg old-point))) -(defun Footnote-sort (list) +(defun footnote--sort (list) (sort list (lambda (e1 e2) (< (car e1) (car e2))))) -(defun Footnote-text-under-cursor () - "Return the number of footnote if in footnote text. +(defun footnote--text-under-cursor () + "Return the number of the current footnote if in footnote text. Return nil if the cursor is not positioned over the text of a footnote." - (when (and (let ((old-point (point))) - (save-excursion - (save-restriction - (Footnote-narrow-to-footnotes) - (and (>= old-point (point-min)) - (<= old-point (point-max)))))) - footnote-text-marker-alist - (>= (point) (cdar footnote-text-marker-alist))) - (let ((i 1) - alist-txt rc) + (when (and footnote-text-marker-alist + (<= (footnote--get-area-point-min) + (point) + (footnote--get-area-point-max))) + (let ((i 1) alist-txt result) (while (and (setq alist-txt (nth i footnote-text-marker-alist)) - (null rc)) - (when (< (point) (cdr alist-txt)) - (setq rc (car (nth (1- i) footnote-text-marker-alist)))) - (setq i (1+ i))) - (when (and (null rc) - (null alist-txt)) - (setq rc (car (nth (1- i) footnote-text-marker-alist)))) - rc))) - -(defun Footnote-under-cursor () + (null result)) + (when (< (point) (cdr alist-txt)) + (setq result (car (nth (1- i) footnote-text-marker-alist)))) + (setq i (1+ i))) + (when (and (null result) (null alist-txt)) + (setq result (car (nth (1- i) footnote-text-marker-alist)))) + result))) + +(defun footnote--under-cursor () "Return the number of the footnote underneath the cursor. Return nil if the cursor is not over a footnote." (or (get-text-property (point) 'footnote-number) - (Footnote-text-under-cursor))) + (footnote--text-under-cursor))) + +(defun footnote--calc-fn-alignment-column () + "Calculate the left alignment for footnote text." + ;; FIXME: Maybe it would be better to go to the footnote's beginning and + ;; see at which column it starts. + (+ footnote-body-tag-spacing + (string-width + (concat footnote-start-tag footnote-end-tag + (footnote--index-to-string + (caar (last footnote-text-marker-alist))))))) + +(defun footnote--fill-prefix-string () + "Return the fill prefix to be used by footnote mode." + ;; TODO: Prefix to this value other prefix strings, such as those + ;; designating a comment line, a message response, or a boxquote. + (make-string (footnote--calc-fn-alignment-column) ?\s)) + +(defun footnote--point-in-body-p () + "Return non-nil if point is in the buffer text area, +i.e. before the beginning of the footnote area." + (< (point) (footnote--get-area-point-min))) + +(defun footnote--get-area-point-min (&optional before-tag) + "Return start of the first footnote. +If there is no footnote area, returns `point-max'. +With optional arg BEFORE-TAG, return position of the `footnote-section-tag' +instead, if applicable." + (cond + ;; FIXME: Shouldn't we use `footnote--get-area-point-max' instead? + ((not footnote-text-marker-alist) (point-max)) + ((not before-tag) (cdr (car footnote-text-marker-alist))) + ((string-equal footnote-section-tag "") + (cdr (car footnote-text-marker-alist))) + (t + (save-excursion + (goto-char (cdr (car footnote-text-marker-alist))) + (if (re-search-backward (concat "^" footnote-section-tag-regexp) nil t) + (match-beginning 0) + (message "Footnote section tag not found!") + ;; This `else' should never happen, and indicates an error, + ;; ie. footnotes already exist and a footnote-section-tag is defined, + ;; but the section tag hasn't been found. We choose to assume that the + ;; user deleted it intentionally and wants us to behave in this buffer + ;; as if the section tag was set "", so we do that, now. + ;;(setq footnote-section-tag "") + ;; + ;; HOWEVER: The rest of footnote mode does not currently honor or + ;; account for this. + ;; + ;; To illustrate the difference in behavior, create a few footnotes, + ;; delete the section tag, and create another footnote. Then undo, + ;; comment the above line (that sets the tag to ""), re-evaluate this + ;; function, and repeat. + ;; + ;; TODO: integrate sanity checks at reasonable operational points. + (cdr (car footnote-text-marker-alist))))))) + +(defun footnote--get-area-point-max () + "Return the end of footnote area. +This is either `point-max' or the start of a `.signature' string, as +defined by variable `footnote-signature-separator'. If there is no +footnote area, returns `point-max'." + (save-excursion (footnote--goto-char-point-max))) + +(defun footnote--adaptive-fill-function (orig-fun) + (or + (and + footnote-mode + footnote-align-to-fn-text + (footnote--text-under-cursor) + ;; (not (footnote--point-in-body-p)) + ;; (< (point) (footnote--signature-area-start-point)) + (footnote--fill-prefix-string)) + ;; If not within a footnote's text, fallback to the default. + (funcall orig-fun))) ;;; User functions -(defun Footnote-make-hole () +(defun footnote--make-hole () (save-excursion (let ((i 0) (notes (length footnote-pointer-marker-alist)) @@ -622,32 +780,32 @@ Return nil if the cursor is not over a footnote." (setq rc (car alist-ptr))) (save-excursion (message "Renumbering from %s to %s" - (Footnote-index-to-string (car alist-ptr)) - (Footnote-index-to-string + (footnote--index-to-string (car alist-ptr)) + (footnote--index-to-string (1+ (car alist-ptr)))) - (Footnote-renumber (car alist-ptr) + (footnote--renumber (car alist-ptr) (1+ (car alist-ptr)) alist-ptr alist-txt))) (setq i (1+ i))) rc))) -(defun Footnote-add-footnote (&optional arg) +(defun footnote-add-footnote () "Add a numbered footnote. The number the footnote receives is dependent upon the relative location of any other previously existing footnotes. If the variable `footnote-narrow-to-footnotes-when-editing' is set, the buffer is narrowed to the footnote body. The restriction is removed -by using `Footnote-back-to-message'." - (interactive "*P") +by using `footnote-back-to-message'." + (interactive "*") (let ((num (if footnote-text-marker-alist (if (< (point) (cl-cadar (last footnote-pointer-marker-alist))) - (Footnote-make-hole) + (footnote--make-hole) (1+ (caar (last footnote-text-marker-alist)))) 1))) (message "Adding footnote %d" num) - (Footnote-insert-footnote num) + (footnote--insert-footnote num) (insert-before-markers (make-string footnote-body-tag-spacing ? )) (let ((opoint (point))) (save-excursion @@ -656,18 +814,18 @@ by using `Footnote-back-to-message'." "\n\n" "\n")) (when footnote-narrow-to-footnotes-when-editing - (Footnote-narrow-to-footnotes))) + (footnote--narrow-to-footnotes))) ;; Emacs/XEmacs bug? save-excursion doesn't restore point when using ;; insert-before-markers. (goto-char opoint)))) -(defun Footnote-delete-footnote (&optional arg) +(defun footnote-delete-footnote (&optional arg) "Delete a numbered footnote. With no parameter, delete the footnote under (point). With ARG specified, delete the footnote with that number." (interactive "*P") (unless arg - (setq arg (Footnote-under-cursor))) + (setq arg (footnote--under-cursor))) (when (and arg (or (not footnote-prompt-before-deletion) (y-or-n-p (format "Really delete footnote %d?" arg)))) @@ -681,7 +839,7 @@ delete the footnote with that number." (save-excursion (goto-char (car locn)) (when (looking-back (concat (regexp-quote footnote-start-tag) - (Footnote-current-regexp) + (footnote--current-regexp) (regexp-quote footnote-end-tag)) (line-beginning-position)) (delete-region (match-beginning 0) (match-end 0)))) @@ -692,20 +850,20 @@ delete the footnote with that number." (point) (if footnote-spaced-footnotes (search-forward "\n\n" nil t) - (save-restriction + (save-restriction ; <= 2017-12 Boruch: WHY?? I see no narrowing / widening here. (end-of-line) (next-single-char-property-change - (point) 'footnote-number nil (Footnote-goto-char-point-max)))))) + (point) 'footnote-number nil (footnote--goto-char-point-max)))))) (setq footnote-pointer-marker-alist (delq alist-ptr footnote-pointer-marker-alist)) (setq footnote-text-marker-alist (delq alist-txt footnote-text-marker-alist)) - (Footnote-renumber-footnotes) + (footnote-renumber-footnotes) (when (and (null footnote-text-marker-alist) (null footnote-pointer-marker-alist)) (save-excursion (if (not (string-equal footnote-section-tag "")) - (let* ((end (Footnote-goto-char-point-max)) + (let* ((end (footnote--goto-char-point-max)) (start (1- (re-search-backward (concat "^" footnote-section-tag-regexp) nil t)))) @@ -715,13 +873,13 @@ delete the footnote with that number." (delete-region start (if (< end (point-max)) end (point-max)))) - (Footnote-goto-char-point-max) + (footnote--goto-char-point-max) (when (looking-back "\n\n" (- (point) 2)) (kill-line -1)))))))) -(defun Footnote-renumber-footnotes (&optional arg) +(defun footnote-renumber-footnotes () "Renumber footnotes, starting from 1." - (interactive "*P") + (interactive "*") (save-excursion (let ((i 0) (notes (length footnote-pointer-marker-alist)) @@ -730,16 +888,16 @@ delete the footnote with that number." (setq alist-ptr (nth i footnote-pointer-marker-alist)) (setq alist-txt (nth i footnote-text-marker-alist)) (unless (= (1+ i) (car alist-ptr)) - (Footnote-renumber (car alist-ptr) (1+ i) alist-ptr alist-txt)) + (footnote--renumber (car alist-ptr) (1+ i) alist-ptr alist-txt)) (setq i (1+ i)))))) -(defun Footnote-goto-footnote (&optional arg) +(defun footnote-goto-footnote (&optional arg) "Jump to the text of a footnote. With no parameter, jump to the text of the footnote under (point). With ARG specified, jump to the text of that footnote." (interactive "P") (unless arg - (setq arg (Footnote-under-cursor))) + (setq arg (footnote--under-cursor))) (let ((footnote (assq arg footnote-text-marker-alist))) (cond (footnote @@ -755,13 +913,13 @@ specified, jump to the text of that footnote." (t (error "I don't see a footnote here"))))) -(defun Footnote-back-to-message (&optional arg) +(defun footnote-back-to-message () "Move cursor back to footnote referent. If the cursor is not over the text of a footnote, point is not changed. If the buffer was narrowed due to `footnote-narrow-to-footnotes-when-editing' being set it is automatically widened." - (interactive "P") - (let ((note (Footnote-text-under-cursor))) + (interactive) + (let ((note (footnote--text-under-cursor))) (when note (when footnote-narrow-to-footnotes-when-editing (widen)) @@ -769,13 +927,13 @@ being set it is automatically widened." (defvar footnote-mode-map (let ((map (make-sparse-keymap))) - (define-key map "a" 'Footnote-add-footnote) - (define-key map "b" 'Footnote-back-to-message) - (define-key map "c" 'Footnote-cycle-style) - (define-key map "d" 'Footnote-delete-footnote) - (define-key map "g" 'Footnote-goto-footnote) - (define-key map "r" 'Footnote-renumber-footnotes) - (define-key map "s" 'Footnote-set-style) + (define-key map "a" 'footnote-add-footnote) + (define-key map "b" 'footnote-back-to-message) + (define-key map "c" 'footnote-cycle-style) + (define-key map "d" 'footnote-delete-footnote) + (define-key map "g" 'footnote-goto-footnote) + (define-key map "r" 'footnote-renumber-footnotes) + (define-key map "s" 'footnote-set-style) map)) (defvar footnote-minor-mode-map @@ -787,9 +945,6 @@ being set it is automatically widened." ;;;###autoload (define-minor-mode footnote-mode "Toggle Footnote mode. -With a prefix argument ARG, enable Footnote mode if ARG is -positive, and disable it otherwise. If called from Lisp, enable -the mode if ARG is omitted or nil. Footnote mode is a buffer-local minor mode. If enabled, it provides footnote support for `message-mode'. To get started, @@ -798,8 +953,14 @@ play around with the following keys: :lighter footnote-mode-line-string :keymap footnote-minor-mode-map ;; (filladapt-mode t) + (unless adaptive-fill-function + ;; nil and `ignore' have the same semantics for adaptive-fill-function, + ;; but only `ignore' behaves correctly with add/remove-function. + (setq adaptive-fill-function #'ignore)) + (remove-function (local 'adaptive-fill-function) + #'footnote--adaptive-fill-function) (when footnote-mode - ;; (Footnote-setup-keybindings) + ;; (footnote-setup-keybindings) (make-local-variable 'footnote-style) (make-local-variable 'footnote-body-tag-spacing) (make-local-variable 'footnote-spaced-footnotes) @@ -807,7 +968,12 @@ play around with the following keys: (make-local-variable 'footnote-section-tag-regexp) (make-local-variable 'footnote-start-tag) (make-local-variable 'footnote-end-tag) + (make-local-variable 'adaptive-fill-function) + (add-function :around (local 'adaptive-fill-function) + #'footnote--adaptive-fill-function) + ;; filladapt is an XEmacs package which AFAIK has never been ported + ;; to Emacs. (when (boundp 'filladapt-token-table) ;; add tokens to filladapt to match footnotes ;; 1] xxxxxxxxxxx x x x or [1] x x x x x x x |