unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: "J.P." <jp@neverwas.me>
To: 64855@debbugs.gnu.org
Cc: emacs-erc@gnu.org
Subject: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic
Date: Wed, 26 Jul 2023 06:27:58 -0700	[thread overview]
Message-ID: <87351ax0o1.fsf__40818.3792418399$1690394229$gmane$org@neverwas.me> (raw)
In-Reply-To: <87h6psyurb.fsf@neverwas.me> (J. P.'s message of "Tue, 25 Jul 2023 06:40:24 -0700")

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

v2. Stash and restore `window-start' for all windows when inserting
messages.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v1-v2.diff --]
[-- Type: text/x-patch, Size: 6649 bytes --]

From 49e66fee68a4ad69418dae329e24e5611ea8de6e Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Wed, 26 Jul 2023 05:33:54 -0700
Subject: [PATCH 0/1] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (1):
  [5.6] Consider all windows in erc-scrolltobottom-mode

 lisp/erc/erc-goodies.el | 102 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 91 insertions(+), 11 deletions(-)

Interdiff:
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index af44b98e0bf..e82dc73f82f 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -52,22 +52,26 @@ erc-input-line-position
 (define-erc-module scrolltobottom nil
   "This mode causes the prompt to stay at the end of the window."
   ((add-hook 'erc-mode-hook #'erc-add-scroll-to-bottom)
-   (add-hook 'erc-insert-pre-hook #'erc--on-pre-insert)
+   (add-hook 'erc-insert-pre-hook #'erc--scroll-to-bottom-on-pre-insert)
+   (add-hook 'erc-send-pre-functions #'erc--scroll-to-bottom-on-pre-insert)
    (add-hook 'erc-insert-done-hook #'erc--scroll-to-bottom-all)
    (add-hook 'erc-send-completed-hook #'erc--scroll-to-bottom-all)
    (unless erc--updating-modules-p (erc-buffer-do #'erc-add-scroll-to-bottom)))
   ((remove-hook 'erc-mode-hook #'erc-add-scroll-to-bottom)
-   (remove-hook 'erc-insert-pre-hook #'erc--on-pre-insert)
+   (remove-hook 'erc-insert-pre-hook #'erc--scroll-to-bottom-on-pre-insert)
+   (remove-hook 'erc-send-pre-functions #'erc--scroll-to-bottom-on-pre-insert)
    (remove-hook 'erc-insert-done-hook #'erc--scroll-to-bottom-all)
    (remove-hook 'erc-send-completed-hook #'erc--scroll-to-bottom-all)
    (erc-buffer-do #'erc-add-scroll-to-bottom)))
 
 (defvar-local erc--scroll-to-bottom-debounce-expire nil
-  "Time after which `scrolltobottom' is allowed to run.
-Set to a fraction of a second in the future on every refresh.")
+  "A cons of some window and the expiration time in seconds.
+The CAR is a window in whose buffer this variable is local.  The
+CDR is a half second after the last attempted refresh of such a
+window.")
 
 (defvar-local erc--scroll-to-bottom-last-window-start nil
-  "A cons of a window and a starting position.")
+  "Alist whose members are a cons of a window and a starting position.")
 
 (defun erc-possibly-scroll-to-bottom ()
   "Like `erc-add-scroll-to-bottom', but only if window is selected."
@@ -76,18 +80,36 @@ erc-possibly-scroll-to-bottom
 
 (defun erc--possibly-scroll-to-bottom ()
   "Call `erc-scroll-to-bottom' when buffer occupies selected window.
-Skip when `erc--scroll-to-bottom-debounce-expire' has not yet
-arrived."
+Expect narrowing not to be in effect.  Skip when
+`erc--scroll-to-bottom-debounce-expire' has not yet transpired."
   (when (eq (selected-window) (get-buffer-window))
-    (unless (eq this-command 'recenter-top-bottom)
-      (let (erc--scroll-to-bottom-last-window-start)
-        (erc--scroll-to-bottom)))))
+    (unless (or (eq this-command 'recenter-top-bottom) (input-pending-p))
+      (let ((now (erc-current-time))
+            (at-prompt-p (>= (point) erc-input-marker)))
+        (unless (and at-prompt-p
+                     erc--scroll-to-bottom-debounce-expire
+                     (eq (car erc--scroll-to-bottom-debounce-expire)
+                         (selected-window))
+                     (< now (cdr erc--scroll-to-bottom-debounce-expire)))
+          (erc--scroll-to-bottom))
+        (setq erc--scroll-to-bottom-debounce-expire
+              (and at-prompt-p (cons (selected-window) (+ now 0.5))))))))
 
 (defun erc--scroll-to-bottom-all (&rest _)
-  "Run `erc-scroll-to-bottom' in all windows showing current buffer."
+  "Maybe put prompt on last line in all windows displaying current buffer.
+Expect to run when narrowing is in effect, such as on insertion
+or send-related hooks.  When recentering has not been performed,
+attempt to restore last `window-start', if known."
   (dolist (window (get-buffer-window-list nil nil 'visible))
     (with-selected-window window
-      (erc--scroll-to-bottom))))
+      (when-let
+          (((not (erc--scroll-to-bottom)))
+           (erc--scroll-to-bottom-last-window-start)
+           (found (assq window erc--scroll-to-bottom-last-window-start)))
+        (setf (window-start window) (cdr found)))))
+  ;; Necessary unless we're sure `erc--scroll-to-bottom-on-pre-insert'
+  ;; always runs between calls to this function.
+  (setq erc--scroll-to-bottom-last-window-start nil))
 
 (defun erc-add-scroll-to-bottom ()
   "Arrange for `scrolltobottom' to refresh on window configuration changes.
@@ -110,31 +132,30 @@ erc-add-scroll-to-bottom
     (remove-hook 'post-command-hook
                  #'erc--possibly-scroll-to-bottom t)))
 
-(defun erc--on-pre-insert (&rest _)
-  (when (eq (selected-window) (get-buffer-window))
-    (setq erc--scroll-to-bottom-last-window-start
-          (cons (selected-window) (window-start)))))
+(cl-defmethod erc--scroll-to-bottom-on-pre-insert (_input-or-string)
+  "Remember the `window-start' before inserting a message."
+  (setq erc--scroll-to-bottom-last-window-start
+        (mapcar (lambda (w) (cons w (window-start w)))
+                (get-buffer-window-list nil nil 'visible))))
+
+(cl-defmethod erc--scroll-to-bottom-on-pre-insert ((input erc-input))
+  "Remember the `window-start' before inserting a message."
+  (when (erc-input-insertp input)
+    (cl-call-next-method)))
 
 (defun erc--scroll-to-bottom ()
   "Like `erc-scroll-to-bottom', but use `window-point'.
 Expect to run in some window, not necessarily the user-selected
-one."
+one.  Return non-nil when recentering has occurred."
   (when erc-insert-marker
     (let ((resize-mini-windows nil))
       (save-restriction
         (widen)
-        (if (>= (window-point) erc-input-marker)
-            (save-excursion
-              (goto-char (point-max))
-              (recenter (or erc-input-line-position -1)))
-          (when (and erc--scroll-to-bottom-last-window-start
-                     ;; `selected-window' means the one being visited.
-                     (eq (selected-window)
-                         (car erc--scroll-to-bottom-last-window-start)))
-            (save-excursion
-              (goto-char (cdr erc--scroll-to-bottom-last-window-start))
-              (let ((recenter-positions '(top)))
-                (recenter-top-bottom)))))))))
+        (when (>= (window-point) erc-input-marker)
+          (save-excursion
+            (goto-char (point-max))
+            (recenter (or erc-input-line-position -1))
+            t))))))
 
 (defun erc-scroll-to-bottom ()
   "Recenter WINDOW so that `point' is on the last line.
-- 
2.41.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-5.6-Consider-all-windows-in-erc-scrolltobottom-mode.patch --]
[-- Type: text/x-patch, Size: 8150 bytes --]

From 49e66fee68a4ad69418dae329e24e5611ea8de6e Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Sat, 22 Jul 2023 00:46:44 -0700
Subject: [PATCH 1/1] [5.6] Consider all windows in erc-scrolltobottom-mode

* lisp/erc/erc-goodies.el (erc-scrolltobottom-mode,
erc-scrolltobottom-enable, erc-scrolltobottom-disable): Use
`erc--scroll-to-bottom-all' instead of `erc-possibly-scroll-to-bottom'
for `erc-insert-done-hook' and now also `erc-send-completed-hook'.
Add `erc--scroll-to-bottom-on-pre-insert' to `erc-insert-pre-hook' and
`erc-send-pre-functions'.
Call `erc-add-scroll-to-bottom' for teardown as well.
(erc--scroll-to-bottom-debounce-expire): New variable to help ERC
refrain from calling `recenter' when the user is typing at the prompt.
(erc--scroll-to-bottom-last-window-start): New variable used by
`erc--scroll-to-bottom-on-pre-insert' and `erc--scroll-to-bottom' for
stashing and restoring the `window-start' of all windows displaying
the buffer.
(erc--possibly-scroll-to-bottom): New function resembling
`erc-possibly-scroll-to-bottom' that tries to avoid scrolling again
when user has done so recently.
(erc--scroll-to-bottom-all): New function to scroll all windows
displaying the current buffer.
(erc-add-scroll-to-bottom): Perform teardown as well when mode is
disabled.  Also run on `window-configuration-changed-hook'.
(erc-scroll-to-bottom): Use `window-point' instead of `point'.
(erc--scroll-to-bottom-on-pre-insert): New generic function that
remembers the `window-start' before inserting a message in order to
restore it afterward.
(erc--scroll-to-bottom): New function, a replacement for
`erc-scroll-to-bottom', that takes returns non-nil when it's actually
performed a recentering.  (Bug#64855)
---
 lisp/erc/erc-goodies.el | 102 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 91 insertions(+), 11 deletions(-)

diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index d9ededa8e68..e82dc73f82f 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -52,34 +52,114 @@ erc-input-line-position
 (define-erc-module scrolltobottom nil
   "This mode causes the prompt to stay at the end of the window."
   ((add-hook 'erc-mode-hook #'erc-add-scroll-to-bottom)
-   (add-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)
+   (add-hook 'erc-insert-pre-hook #'erc--scroll-to-bottom-on-pre-insert)
+   (add-hook 'erc-send-pre-functions #'erc--scroll-to-bottom-on-pre-insert)
+   (add-hook 'erc-insert-done-hook #'erc--scroll-to-bottom-all)
+   (add-hook 'erc-send-completed-hook #'erc--scroll-to-bottom-all)
    (unless erc--updating-modules-p (erc-buffer-do #'erc-add-scroll-to-bottom)))
   ((remove-hook 'erc-mode-hook #'erc-add-scroll-to-bottom)
-   (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)
-   (dolist (buffer (erc-buffer-list))
-     (with-current-buffer buffer
-       (remove-hook 'post-command-hook #'erc-scroll-to-bottom t)))))
+   (remove-hook 'erc-insert-pre-hook #'erc--scroll-to-bottom-on-pre-insert)
+   (remove-hook 'erc-send-pre-functions #'erc--scroll-to-bottom-on-pre-insert)
+   (remove-hook 'erc-insert-done-hook #'erc--scroll-to-bottom-all)
+   (remove-hook 'erc-send-completed-hook #'erc--scroll-to-bottom-all)
+   (erc-buffer-do #'erc-add-scroll-to-bottom)))
+
+(defvar-local erc--scroll-to-bottom-debounce-expire nil
+  "A cons of some window and the expiration time in seconds.
+The CAR is a window in whose buffer this variable is local.  The
+CDR is a half second after the last attempted refresh of such a
+window.")
+
+(defvar-local erc--scroll-to-bottom-last-window-start nil
+  "Alist whose members are a cons of a window and a starting position.")
 
 (defun erc-possibly-scroll-to-bottom ()
   "Like `erc-add-scroll-to-bottom', but only if window is selected."
   (when (eq (selected-window) (get-buffer-window))
     (erc-scroll-to-bottom)))
 
+(defun erc--possibly-scroll-to-bottom ()
+  "Call `erc-scroll-to-bottom' when buffer occupies selected window.
+Expect narrowing not to be in effect.  Skip when
+`erc--scroll-to-bottom-debounce-expire' has not yet transpired."
+  (when (eq (selected-window) (get-buffer-window))
+    (unless (or (eq this-command 'recenter-top-bottom) (input-pending-p))
+      (let ((now (erc-current-time))
+            (at-prompt-p (>= (point) erc-input-marker)))
+        (unless (and at-prompt-p
+                     erc--scroll-to-bottom-debounce-expire
+                     (eq (car erc--scroll-to-bottom-debounce-expire)
+                         (selected-window))
+                     (< now (cdr erc--scroll-to-bottom-debounce-expire)))
+          (erc--scroll-to-bottom))
+        (setq erc--scroll-to-bottom-debounce-expire
+              (and at-prompt-p (cons (selected-window) (+ now 0.5))))))))
+
+(defun erc--scroll-to-bottom-all (&rest _)
+  "Maybe put prompt on last line in all windows displaying current buffer.
+Expect to run when narrowing is in effect, such as on insertion
+or send-related hooks.  When recentering has not been performed,
+attempt to restore last `window-start', if known."
+  (dolist (window (get-buffer-window-list nil nil 'visible))
+    (with-selected-window window
+      (when-let
+          (((not (erc--scroll-to-bottom)))
+           (erc--scroll-to-bottom-last-window-start)
+           (found (assq window erc--scroll-to-bottom-last-window-start)))
+        (setf (window-start window) (cdr found)))))
+  ;; Necessary unless we're sure `erc--scroll-to-bottom-on-pre-insert'
+  ;; always runs between calls to this function.
+  (setq erc--scroll-to-bottom-last-window-start nil))
+
 (defun erc-add-scroll-to-bottom ()
-  "A hook function for `erc-mode-hook' to recenter output at bottom of window.
+  "Arrange for `scrolltobottom' to refresh on window configuration changes.
+Undo that arrangement when `erc-scrolltobottom-mode' is disabled.
 
 If you find that ERC hangs when using this function, try customizing
 the value of `erc-input-line-position'.
 
-This works whenever scrolling happens, so it's added to
-`window-scroll-functions' rather than `erc-insert-post-hook'."
-  (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t))
+Note that the prior suggestion comes from a time when this
+function used `window-scroll-functions', which was replaced by
+`post-command-hook' in ERC 5.3."
+  (if erc-scrolltobottom-mode
+      (progn
+        (add-hook 'window-configuration-change-hook
+                  #'erc--possibly-scroll-to-bottom nil t)
+        (add-hook 'post-command-hook
+                  #'erc--possibly-scroll-to-bottom nil t))
+    (remove-hook 'window-configuration-change-hook
+                 #'erc--possibly-scroll-to-bottom t)
+    (remove-hook 'post-command-hook
+                 #'erc--possibly-scroll-to-bottom t)))
+
+(cl-defmethod erc--scroll-to-bottom-on-pre-insert (_input-or-string)
+  "Remember the `window-start' before inserting a message."
+  (setq erc--scroll-to-bottom-last-window-start
+        (mapcar (lambda (w) (cons w (window-start w)))
+                (get-buffer-window-list nil nil 'visible))))
+
+(cl-defmethod erc--scroll-to-bottom-on-pre-insert ((input erc-input))
+  "Remember the `window-start' before inserting a message."
+  (when (erc-input-insertp input)
+    (cl-call-next-method)))
+
+(defun erc--scroll-to-bottom ()
+  "Like `erc-scroll-to-bottom', but use `window-point'.
+Expect to run in some window, not necessarily the user-selected
+one.  Return non-nil when recentering has occurred."
+  (when erc-insert-marker
+    (let ((resize-mini-windows nil))
+      (save-restriction
+        (widen)
+        (when (>= (window-point) erc-input-marker)
+          (save-excursion
+            (goto-char (point-max))
+            (recenter (or erc-input-line-position -1))
+            t))))))
 
 (defun erc-scroll-to-bottom ()
   "Recenter WINDOW so that `point' is on the last line.
 
-This is added to `window-scroll-functions' by `erc-add-scroll-to-bottom'.
-
 You can control which line is recentered to by customizing the
 variable `erc-input-line-position'."
       ;; Temporarily bind resize-mini-windows to nil so that users who have it
-- 
2.41.0


  reply	other threads:[~2023-07-26 13:27 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-25 13:40 bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic J.P.
2023-07-26 13:27 ` J.P. [this message]
2023-08-09 15:00 ` J.P.
2023-08-18 13:50 ` J.P.
2023-08-24 14:11 ` J.P.
     [not found] ` <87il948r8x.fsf@neverwas.me>
2023-09-13 14:05   ` J.P.
     [not found]   ` <871qf2183j.fsf@neverwas.me>
2023-09-19 13:38     ` J.P.
2023-10-11  2:53 ` J.P.
     [not found] ` <87o7h5euo8.fsf@neverwas.me>
2023-10-14  0:29   ` J.P.
     [not found]   ` <871qdy9hbz.fsf@neverwas.me>
2023-10-25  2:15     ` J.P.
     [not found]     ` <87r0lja1lw.fsf@neverwas.me>
2023-10-30 13:46       ` J.P.

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='87351ax0o1.fsf__40818.3792418399$1690394229$gmane$org@neverwas.me' \
    --to=jp@neverwas.me \
    --cc=64855@debbugs.gnu.org \
    --cc=emacs-erc@gnu.org \
    /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).