l;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ; Footnote behavior customization ; + left-align footnote text to first character of first paragraph, ; not to left margin. ; + Instructions: ; + toggle the feature using 'mode-map-prefix M-q' (usually 'C-c ! M-q') ; + whenever you run `auto-fill' (M-q), it will act ; + if you set visual-line-mode, it acts automatically (require 'footnote) (defcustom Footnote-align-to-fn-text t "Nil if footnote text is to be aligned flush left with left side of the footnote number. Non-nil if footnote text is to be aligned left with the first character of footnote text." :type 'boolean :group 'footnote) (make-variable-buffer-local 'Footnote-align-to-fn-text) (defvar Footnote--body-auto-fill-prefix nil "When Footnmote mode modifies variable `fill-prefix', store the prior value here, so that it can be restored when leaving the footnote area.") (make-variable-buffer-local 'Footnote--body-auto-fill-prefix) (defun Footnote--calc-fn-alignment-column() "Calculate the left alignment for footnote text." (+ footnote-body-tag-spacing (length (concat footnote-start-tag footnote-end-tag (Footnote-index-to-string (caar (last footnote-text-marker-alist))))))) (defun Footnote--align-to-fn() "For the purpose of this feature proposal (only), this function is an advice :after function `Footnote-add-footnote'. For implementation within emacs, the code would be included at the end of that function." ; TODO: If the length has changed, eg. when adding footnote [10] after having ; footnote [9], refresh all footnotes with new alignment. (when Footnote-align-to-fn-text (setq Footnote--body-auto-fill-prefix (or fill-prefix "")) (setq fill-prefix (make-string (Footnote--calc-fn-alignment-column) 32)))) (defun Footnote--align-to-body() (when (not Footnote-align-to-fn-text) (setq fill-prefix Footnote--body-auto-fill-prefix) (setq Footnote--body-auto-fill-prefix nil))) (defun Footnote--return-to-body(arg) "For the purpose of this feature proposal (only), this function is an advice :after function `Footnote-back-to-message'. For implementation within emacs, the code would be included at the end of that function." (setq fill-prefix Footnote--body-auto-fill-prefix) (setq Footnote--body-auto-fill-prefix nil)) (defun Footnote--point-in-body-p() "Return `t' if point is in the buffer text area, ie. before the beginning of the footnote area." ; NOTE: This code is shared with a patch I've proposed for emacs-bug#29756 (let ((p (point)) (q (if (not footnote-text-marker-alist) (point-max) (if (string-equal footnote-section-tag "") (cdr (first footnote-text-marker-alist)) (goto-char (cdr (first footnote-text-marker-alist))) (if (re-search-backward (concat "^" footnote-section-tag-regexp) nil t) (match-beginning 0) ; 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. (cdr (first footnote-text-marker-alist)) ))))) (goto-char p) ; undo `re-search-backward' side-effect (if (< p q) t nil))) (defun Footnote--fill-paragraph-in-body(orig-function &optional justify region) "For the purpose of this feature proposal (only), this function is an advice :before function `fill-paragraph' to ensure that variable `fill-prefix' is correctly restored and maintained when dealing with footnotes potentially possessing a different `fill-prefix' value. This is meant to cover cases of the user manually returning to the buffer text from the footnote area instead of using the function `Footnote-back-to-message'. For the purpose of implementation within emacs, this function would be a remap of `fill-paragraph', specific to the `Footnote-mode' minor mode." (when (and footnote-mode (Footnote--point-in-body-p) Footnote--body-auto-fill-prefix) (setq fill-prefix Footnote--body-auto-fill-prefix) (setq Footnote--body-auto-fill-prefix nil))) (defun Footnote-toggle-alignment() "Change whether footnote text is aligned flush left with the left of a footnote's number, or flush left with the first character of footnote text on the footnote's first line." (interactive) (setq Footnote-align-to-fn-text (not Footnote-align-to-fn-text)) (when footnote-text-marker-alist (if (>= (point) (cdr (first footnote-text-marker-alist))) (if Footnote-align-to-fn-text (Footnote--align-to-fn) (Footnote--align-to-body)))) (if Footnote-align-to-fn-text (message "Footnotes will left-align to footnote text") (message "Footnotes will left-align to body text"))) (define-key footnote-mode-map (kbd "M-q") 'Footnote-toggle-alignment) (advice-add 'Footnote-add-footnote :after #'Footnote--align-to-fn) (advice-add 'Footnote-back-to-message :after #'Footnote--return-to-body) (advice-add 'fill-paragraph :before #'Footnote--fill-paragraph-in-body) ;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━