unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Boruch Baum <boruch_baum@gmx.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>,
	Emacs-Devel List <emacs-devel@gnu.org>
Subject: Re: [Footnote-mode]: alignment option [CODE included]
Date: Sun, 17 Dec 2017 19:13:06 -0500	[thread overview]
Message-ID: <20171218001306.mqycrkyhva7h5ivi@E15-2016.optimum.net> (raw)
In-Reply-To: <jwvbmiylnmn.fsf-monnier+gmane.emacs.devel@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 2434 bytes --]

On 2017-12-16 10:05, Stefan Monnier wrote:
> Have you seen my reply last week in emacs-devel?
No. Thanks for contacting me off-list about it.

>> The attached code allows one to left-justify footnote text from the
>> first column of text, instead of from the left margin.  I find this
>> aesthetically preferable, especially when I have long footnotes.

> Could you give some example of the difference?

Sure. The current behavior is:

[1] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam nisl,

The proposed optional alternative is:

[1] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
    hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam

>> Of course, if one has visual-line-mode enabled, the auto-fill acts
>> automatically.

> Hmm... how does visual-line-mode pay attention to
> Footnote-align-to-fn-text (and know if it's inside a footnote)?

I double-checked, and I wrote incorrectly.

> Some comments about the code:
>
> body-auto-fill-prefix is just a var into which we temporarily save
> the normal fill-prefix.  So this should be named with a "[Ff]ootnote-"
> prefix (arguably with a "--" somewhere to make it clear it's an
> internal variable), and it should be made buffer-local (so there's no
> cross-buffer pollution).

Done. Note that the file `footnote.el' currently has all its `Private
variables' labeled without the "--" convention.

> The more serious problem is that `fill-prefix' is set with no guarantee
> it will be reset to its proper value later.  E.g. if the user returns to
> the body "manually" rather than via Footnote-back-to-message.

Yes. Corrected.

> I suggest you make this into a minor mode:

Why a minor mode just for a small option of another minor mode?

>> (defadvice Footnote-add-footnote (around abort-when-in-fn-area activate)
> I don't see how this relates to this new alignment feature.

You're correct. It was part of my personal configuration to address a
bug that I had neglected to report, now emacs-bug#29756.

UPDATE:
=======

The updated code is attached, in two forms: 1) As a patch to the 25.2
version of footnote.el (ie. fully integrated into footnote mode without
any need for advising, and; 2) As a snippet, with the feature's
functions advised around the mode's current functions.



-- 
hkp://keys.gnupg.net
CA45 09B5 5351 7C11 A9D1  7286 0036 9E45 1595 8BC0

[-- Attachment #2: footnote.patch --]
[-- Type: text/x-diff, Size: 5682 bytes --]

--- footnote.el	2017-12-17 18:40:22.940312994 -0500
+++ footnote.el_new	2017-12-17 19:00:27.713722813 -0500
@@ -131,6 +131,14 @@ has no effect on buffers already display
   :type 'regexp
   :group '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)
+
 ;;; Private variables
 
 (defvar footnote-style-number nil
@@ -148,6 +156,12 @@ has no effect on buffers already display
 (defvar footnote-mouse-highlight 'highlight
   "Text property name to enable mouse over highlight.")
 
+(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)
+
 ;;; Default styles
 ;;; NUMERIC
 (defconst footnote-numeric-regexp "[0-9]+"
@@ -609,8 +623,72 @@ Return nil if the cursor is not over a f
   (or (get-text-property (point) 'footnote-number)
       (Footnote-text-under-cursor)))
 
+(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-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--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)))
+
 ;;; User functions
 
+(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")))
+
+(defun Footnote-fill-paragraph(&optional justify region)
+  (when (and (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))
+  (fill-paragraph justify region))
+
 (defun Footnote-make-hole ()
   (save-excursion
     (let ((i 0)
@@ -661,7 +739,10 @@ by using `Footnote-back-to-message'."
 	  (Footnote-narrow-to-footnotes)))
       ;; Emacs/XEmacs bug?  save-excursion doesn't restore point when using
       ;; insert-before-markers.
-      (goto-char opoint))))
+      (goto-char opoint))
+    (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-delete-footnote (&optional arg)
   "Delete a numbered footnote.
@@ -767,7 +848,9 @@ being set it is automatically widened."
     (when note
       (when footnote-narrow-to-footnotes-when-editing
 	(widen))
-      (goto-char (cadr (assq note footnote-pointer-marker-alist))))))
+      (goto-char (cadr (assq note footnote-pointer-marker-alist)))
+   (setq fill-prefix Footnote--body-auto-fill-prefix)
+   (setq Footnote--body-auto-fill-prefix nil))))
 
 (defvar footnote-mode-map
   (let ((map (make-sparse-keymap)))
@@ -778,6 +861,8 @@ being set it is automatically widened."
     (define-key map "g" 'Footnote-goto-footnote)
     (define-key map "r" 'Footnote-renumber-footnotes)
     (define-key map "s" 'Footnote-set-style)
+    (define-key map (kbd "M-q") 'Footnote-toggle-alignment)
+    (define-key map [remap fill-paragraph] 'Footnote-fill-paragraph)
     map))
 
 (defvar footnote-minor-mode-map

[-- Attachment #3: footnote-snippet.el --]
[-- Type: text/plain, Size: 6030 bytes --]

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)
;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

      parent reply	other threads:[~2017-12-18  0:13 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-07  6:18 [Footnote-mode]: alignment option [CODE included] Boruch Baum
2017-12-08 16:50 ` Stefan Monnier
     [not found]   ` <jwvbmiylnmn.fsf-monnier+gmane.emacs.devel@gnu.org>
2017-12-18  0:13     ` Boruch Baum [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171218001306.mqycrkyhva7h5ivi@E15-2016.optimum.net \
    --to=boruch_baum@gmx.com \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).