unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [Footnote-mode]: alignment option [CODE included]
@ 2017-12-07  6:18 Boruch Baum
  2017-12-08 16:50 ` Stefan Monnier
  0 siblings, 1 reply; 3+ messages in thread
From: Boruch Baum @ 2017-12-07  6:18 UTC (permalink / raw)
  To: emacs-devel

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

Hi. I'm not a member of the list, but I wanted to share some code that I
think would be useful for emacs users...

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.

Once the code is evaluated and one has footnote-mode enabled, `C-c ! q'
toggles the feature. Then, whenever one performs an auto-fill, `M-q'',
feature acts. Of course, if one has visual-line-mode enabled, the
auto-fill acts automatically.

The current code is set up as defadvice-s (that's how long I've ended up
sitting on it), but if emacs wants it integrated into the mode, there's
no need for that.



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

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

;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
; Footnote behavior customization
; + left-align footnote text to first character of first paragraph,
;   not to left margin.
; + Instructions:
;   + toggle the feature using 'mode-map-prexic q' (usually 'C-c ! q')
;   + whenever you run `auto-fill' (M-q), it will act
;   + if you set visual-line-mode, it acts automatically
(require 'footnote)

(setq Footnote-align-to-fn-text t
      body-auto-fill-prefix nil)

(defun Footnote-calc-fn-alignment-column()
  (+ 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()
  (when Footnote-align-to-fn-text
    (setq body-auto-fill-prefix fill-prefix
          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 body-auto-fill-prefix)))

(defun Footnote-toggle-alignment()
  (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 "q") 'Footnote-toggle-alignment)

(defadvice Footnote-add-footnote (after update-auto-fill-prefix activate)
  (interactive)
  (Footnote-align-to-fn))

(defadvice Footnote-back-to-message (after restore-auto-fill-prefix
                                     activate)
  (interactive)
  (setq fill-prefix body-auto-fill-prefix))

(defadvice Footnote-add-footnote (around abort-when-in-fn-area activate)
  (interactive)
  (if (or
        (not footnote-text-marker-alist)
        (< (point) (cdr (first footnote-text-marker-alist))))
    ad-do-it
   (message "Add footnotes only while in text body")))
;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Footnote-mode]: alignment option [CODE included]
  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>
  0 siblings, 1 reply; 3+ messages in thread
From: Stefan Monnier @ 2017-12-08 16:50 UTC (permalink / raw)
  To: emacs-devel

> 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?

> 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)?

> The current code is set up as defadvice-s (that's how long I've ended up
> sitting on it), but if emacs wants it integrated into the mode, there's
> no need for that.

Of course.  Some comments about the code:

> (defun Footnote-align-to-fn()
>   (when Footnote-align-to-fn-text
>     (setq body-auto-fill-prefix fill-prefix
>           fill-prefix (make-string (Footnote-calc-fn-alignment-column) 32))))

IIUC 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).

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.

> (defun Footnote-toggle-alignment()
>   (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")))

I suggest you make this into a minor mode:

    (define-minor-mode Footnote-align-to-text
      "When enabled, align footnote to the text rather than to the margin."
      :lighter nil
      (when footnote-text-marker-alist
        (if (>= (point) (cdr (first footnote-text-marker-alist)))
            (if Footnote-align-to-text
                (Footnote-align-to-fn)
              (Footnote-align-to-body)))))

> (defadvice Footnote-add-footnote (around abort-when-in-fn-area activate)
>   (interactive)
>   (if (or
>         (not footnote-text-marker-alist)
>         (< (point) (cdr (first footnote-text-marker-alist))))
>     ad-do-it
>    (message "Add footnotes only while in text body")))

I don't see how this relates to this new alignment feature.


        Stefan




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Footnote-mode]: alignment option [CODE included]
       [not found]   ` <jwvbmiylnmn.fsf-monnier+gmane.emacs.devel@gnu.org>
@ 2017-12-18  0:13     ` Boruch Baum
  0 siblings, 0 replies; 3+ messages in thread
From: Boruch Baum @ 2017-12-18  0:13 UTC (permalink / raw)
  To: Stefan Monnier, Emacs-Devel List

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

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2017-12-18  0:13 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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

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).