From unknown Wed Jun 25 02:09:23 2025 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Mailer: MIME-tools 5.509 (Entity 5.509) Content-Type: text/plain; charset=utf-8 From: bug#64855 <64855@debbugs.gnu.org> To: bug#64855 <64855@debbugs.gnu.org> Subject: Status: 30.0.50; ERC 5.6: Make scrolltobottom less erratic Reply-To: bug#64855 <64855@debbugs.gnu.org> Date: Wed, 25 Jun 2025 09:09:23 +0000 retitle 64855 30.0.50; ERC 5.6: Make scrolltobottom less erratic reassign 64855 emacs submitter 64855 "J.P." severity 64855 normal tag 64855 patch thanks From debbugs-submit-bounces@debbugs.gnu.org Tue Jul 25 09:40:50 2023 Received: (at submit) by debbugs.gnu.org; 25 Jul 2023 13:40:51 +0000 Received: from localhost ([127.0.0.1]:44928 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qOIHS-000656-4m for submit@debbugs.gnu.org; Tue, 25 Jul 2023 09:40:50 -0400 Received: from lists.gnu.org ([2001:470:142::17]:33076) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qOIHQ-00064f-0P for submit@debbugs.gnu.org; Tue, 25 Jul 2023 09:40:49 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qOIHD-0003Vr-1a for bug-gnu-emacs@gnu.org; Tue, 25 Jul 2023 09:40:35 -0400 Received: from mail-108-mta165.mxroute.com ([136.175.108.165]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qOIHA-00007y-3y for bug-gnu-emacs@gnu.org; Tue, 25 Jul 2023 09:40:34 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta165.mxroute.com (ZoneMTA) with ESMTPSA id 1898d4797990004cef.001 for (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Tue, 25 Jul 2023 13:40:26 +0000 X-Zone-Loop: d5927c8f1f7dc4dc93ca417af3184c151e8dcff41f7b X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:Subject:To:From:Sender: Reply-To:Cc:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=GiYaGJH8MGm2qdCy6kPIQpDQEBXdrRm+/SUZilJtgn0=; b=GxE58F4u9vvOG8y1sl/cbhvBRj ezEa6qobw+D2rV/jB9wtIAmbBk5XzNlvQWzlGzJ4RUWG/ZC3QgDpjOX0cohbrB8PrqtGS160MUaOj CSjMdD04LpZutmzhdv/7yY4B98nXvAJhWSSw9k+fSC6tA9fC3OjXIbkDsDsMS2v4jAVKq2e+mfs9A f2C3lCGjABJX3gQ7ZYE3IH/MOgfaozpcFuz0VG29Vh4/yFsW7oLeeCGTQE/pWrroOKynM6zOzLnpl u//XrfGMOBUWIb3AKb9abDCw+BsVpv3ItQ35yCRdPajJQeagG8Wa4eWHGAGRNYQCsuv4GgPJXZMcY JiwjbhUw==; From: "J.P." To: bug-gnu-emacs@gnu.org Subject: 30.0.50; ERC 5.6: Make scrolltobottom less erratic X-Debbugs-CC: emacs-erc@gnu.org Date: Tue, 25 Jul 2023 06:40:24 -0700 Message-ID: <87h6psyurb.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me Received-SPF: pass client-ip=136.175.108.165; envelope-from=jp@neverwas.me; helo=mail-108-mta165.mxroute.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: 0.9 (/) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.1 (/) --=-=-= Content-Type: text/plain Tags: patch A minor bug fix involving the `scrolltobottom' module (and originally lumped in with the mostly unrelated bug#63595) eventually materialized on HEAD as [1]. While it did fix the bug, it didn't really address any of the related foundational issues that have haunted `scrolltobottom' since the beginning. The attached patch is meant to make the module more predictable and consistent overall, while specifically addressing - the effect only being applied in the selected window - the effect not responding to changes in window size - the gradual scrolling of point toward the centermost line when away from the prompt (on graphical displays) People miffed by this module's rough edges are encouraged to try these changes and give feedback. Thanks. [1] commit e51e43b7046b56c58310854182a1d589ee4c770c Author: F. Jason Park Date: Wed May 17 19:48:02 2023 -0700 Fix buffer-mismatch bug in erc-scroll-to-bottom * lisp/erc/erc-goodies.el (erc-scroll-to-bottom): Only `recenter' when the selected window's buffer is current. Previously, the module `scrolltobottom' signaled an "Error in `post-command-hook'" when a user clicked a channel indicator in the mode line from a window showing another ERC buffer. [...] (Bug#63595) lisp/erc/erc-goodies.el | 1 + lisp/erc/erc-track.el | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.38, cairo version 1.17.6) of 2023-07-25 built on localhost Repository revision: 89558533683a100ca7946c4a35bf4ef50463efef Repository branch: master Windowing system distributor 'The X.Org Foundation', version 11.0.12014000 System Description: Fedora Linux 37 (Workstation Edition) Configured using: 'configure --enable-check-lisp-object-type --enable-checking=yes,glyphs 'CFLAGS=-O0 -g3' PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig' Configured features: ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB Important settings: value of $LANG: en_US.UTF-8 value of $XMODIFIERS: @im=ibus locale-coding-system: utf-8-unix Major mode: Lisp Interaction Minor modes in effect: tooltip-mode: t global-eldoc-mode: t eldoc-mode: t show-paren-mode: t electric-indent-mode: t mouse-wheel-mode: t tool-bar-mode: t menu-bar-mode: t file-name-shadow-mode: t global-font-lock-mode: t font-lock-mode: t blink-cursor-mode: t line-number-mode: t indent-tabs-mode: t transient-mark-mode: t auto-composition-mode: t auto-encryption-mode: t auto-compression-mode: t Load-path shadows: None found. Features: (shadow sort mail-extr emacsbug message mailcap yank-media puny dired dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config gnus-util text-property-search time-date mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils erc auth-source cl-seq eieio eieio-core cl-macs password-cache json subr-x map format-spec cl-loaddefs cl-lib erc-backend erc-networks byte-opt gv bytecomp byte-compile erc-common erc-compat erc-loaddefs rmc iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic indonesian philippine cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech european ethiopic indian cyrillic chinese composite emoji-zwj charscript charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp files window text-properties overlay sha1 md5 base64 format env code-pages mule custom widget keymap hashtable-print-readable backquote threads dbusbind inotify lcms2 dynamic-setting system-font-setting font-render-setting cairo gtk x-toolkit xinput2 x multi-tty move-toolbar make-network-process emacs) Memory information: ((conses 16 64796 9162) (symbols 48 8637 0) (strings 32 23386 1972) (string-bytes 1 680274) (vectors 16 15042) (vector-slots 8 207499 8948) (floats 8 24 41) (intervals 56 225 0) (buffers 976 10)) --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Consider-all-windows-in-erc-scrolltobottom-mode.patch >From 5ce39aa8adfefc85208448ff1d254766f15892e1 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sat, 22 Jul 2023 00:46:44 -0700 Subject: [PATCH] [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'. Call `erc-add-scroll-to-bottom' for teardown as well. (erc--scroll-to-bottom-debounce-expire): New variable. (erc-possibly-scroll-to-bottom): Don't bother scrolling when user has done so recently. (erc--scroll-to-bottom-all): New function to scroll in all windows of 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'. --- lisp/erc/erc-goodies.el | 81 +++++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index d9ededa8e68..af44b98e0bf 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -52,34 +52,93 @@ 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--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--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.") + +(defvar-local erc--scroll-to-bottom-last-window-start nil + "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. +Skip when `erc--scroll-to-bottom-debounce-expire' has not yet +arrived." + (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))))) + +(defun erc--scroll-to-bottom-all (&rest _) + "Run `erc-scroll-to-bottom' in all windows showing current buffer." + (dolist (window (get-buffer-window-list nil nil 'visible)) + (with-selected-window window + (erc--scroll-to-bottom)))) + (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))) + +(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))))) + +(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." + (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))))))))) (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 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Wed Jul 26 09:28:10 2023 Received: (at 64855) by debbugs.gnu.org; 26 Jul 2023 13:28:11 +0000 Received: from localhost ([127.0.0.1]:47723 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qOeYk-00085x-14 for submit@debbugs.gnu.org; Wed, 26 Jul 2023 09:28:10 -0400 Received: from mail-108-mta74.mxroute.com ([136.175.108.74]:46257) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qOeYh-00085i-Mh for 64855@debbugs.gnu.org; Wed, 26 Jul 2023 09:28:09 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta74.mxroute.com (ZoneMTA) with ESMTPSA id 189926295400004cef.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Wed, 26 Jul 2023 13:28:01 +0000 X-Zone-Loop: 9655449b70fb01798a690afc88204fd2d73ff57bb88a X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=fdH8WGFL0syyAMwhMx+kvQrFMdIJsr0x8N+3o07csec=; b=FHY79HJlyb2zs990ykLdf3cWq7 fHX0/SJ6sgminKZpSAc4Wj/prCNYtL/O1DfEW4y35rI/ZhwD73Zwvu9PHFdDGE5JMB9vnZjONjpn/ HYM3eczZGNBVJevDWLJ3v/W/mVYEjUE+GpMYTCj7l3YdVN45jFb+h64a9cYZNQzAXjI0D0XO+z7dx 0oG3CFxCUoG8zUgx9dJrd325Pu5rVwcuR1foFEYlnq6b6qFuDYgUKw0nFRNJA0EA3CzFI8LLxuLv3 UT/FnckohduwW/ApvFHpkfHOTm3D/n5/MtBLd4ucaiHlIt8LKNPn56E1mpe+AiOgiH4pALxNxmR6S R+F9lZPw==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87h6psyurb.fsf@neverwas.me> (J. P.'s message of "Tue, 25 Jul 2023 06:40:24 -0700") References: <87h6psyurb.fsf@neverwas.me> Date: Wed, 26 Jul 2023 06:27:58 -0700 Message-ID: <87351ax0o1.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain v2. Stash and restore `window-start' for all windows when inserting messages. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0000-v1-v2.diff >From 49e66fee68a4ad69418dae329e24e5611ea8de6e Mon Sep 17 00:00:00 2001 From: "F. Jason Park" 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Consider-all-windows-in-erc-scrolltobottom-mode.patch >From 49e66fee68a4ad69418dae329e24e5611ea8de6e Mon Sep 17 00:00:00 2001 From: "F. Jason Park" 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 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Wed Aug 09 11:00:18 2023 Received: (at 64855) by debbugs.gnu.org; 9 Aug 2023 15:00:18 +0000 Received: from localhost ([127.0.0.1]:40332 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qTkfY-0002Qn-S0 for submit@debbugs.gnu.org; Wed, 09 Aug 2023 11:00:18 -0400 Received: from mail-108-mta42.mxroute.com ([136.175.108.42]:42841) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qTkfT-0002QY-Jp for 64855@debbugs.gnu.org; Wed, 09 Aug 2023 11:00:15 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta42.mxroute.com (ZoneMTA) with ESMTPSA id 189dacffa9900023b6.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Wed, 09 Aug 2023 15:00:10 +0000 X-Zone-Loop: 639962acff67ea37c5938d93fefc9e4a6b5b62a08517 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=KUI2a9YxndEMJYaasoNPj/eSiQS0hWbvk2aKRI9q+6k=; b=cGef5PmOwiuNhWL9SvVqL/H/I7 /wsXnUNtFEMxFtpCenkmhBJ9fblxedPb1tzSnBY7iqAD2R9cl9QFPnX14+j7aPhkKs6RMTIP5EMr2 r/QKW70FyHWN23/muJ8wkg5IIUziedbxXKpM2KW5SrfD6aRlBr+9H9SYLNdEf1504b+p79oi9mt9Z MlBU2lS+kjiBWuYaItBzsl8HZdft4yzwN28BUi91Wlo/LzRlB2HgVZqBMgmDW4mccWd7byX8Be9Gu CBslXYPtKr6FftnFicN2f+fup8QI9DlH1GkrCN4nSXmhdczIegr6aJjuYIK+xd6YarUJPIPe8c7gh 5qyRUxVg==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87h6psyurb.fsf@neverwas.me> (J. P.'s message of "Tue, 25 Jul 2023 06:40:24 -0700") References: <87h6psyurb.fsf@neverwas.me> Date: Wed, 09 Aug 2023 08:00:07 -0700 Message-ID: <87zg30l0qw.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain v3. Add new option to forgo forcing prompt to window's bottom. Remove debouncing mechanism. Prefer setting window-start instead of `recenter'ing when possible. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0000-v2-v3.diff >From 81fcee1a241f4efdf0c80fa9ee8a647cfbad794b Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 9 Aug 2023 06:31:29 -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 | 222 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 209 insertions(+), 13 deletions(-) Interdiff: diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index e82dc73f82f..129e17fb5ec 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -44,58 +44,143 @@ erc-input-line-position This should be an integer specifying the line of the buffer on which the input line should stay. A value of \"-1\" would keep the input line positioned on the last line in the buffer. This is passed as an -argument to `recenter'." +argument to `recenter', unless `erc-scrolltobottom-relaxed' is +non-nil, in which case, ERC interprets it as additional lines to +scroll down by per message insertion (minus one for the prompt)." :group 'erc-display :type '(choice integer (const nil))) +(defcustom erc-scrolltobottom-relaxed nil + "Whether to forgo forcing prompt to the bottom of the window. +When non-nil, and point is at the prompt, ERC scrolls the window +up when inserting messages, making the prompt appear stationary. +Users who find this effect too \"stagnant\" can adjust the option +`erc-input-line-position', which ERC borrows to express a scroll +offset when this option is non-nil. Setting that value to zero +lets the prompt drift toward the bottom by one line per message, +which is generally slow enough not to distract while composing +input. Of course, this doesn't apply when receiving a large +influx of messages, such as after typing \"/msg NickServ help\". +Note that ERC only considers this option when initializing the +`scrolltobottom' module and enabling `erc-scrolltobottom-mode'." + :group 'erc-display + :package-version '(ERC . "5.6") ; FIXME sync on release + :type 'boolean) + ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t) (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--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) + (add-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert) + (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-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--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) + (remove-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-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 _) +(defvar-local erc--scrolltobottom-relaxed-commands '(end-of-buffer) + "Commands triggering a force scroll to prompt. +Only applies with `erc-scrolltobottom-relaxed' when away from prompt.") + +(defvar-local erc--scrolltobottom-window-info nil + "Alist with windows as keys and lists of window-related info as values. +Values are lists containing the last window start position and +the last \"window line\" of point. The \"window line\", which +may be nil, is the number of lines between `window-start' and +`window-point', inclusive.") + +(defvar erc--scrolltobottom-post-force-commands + '(electric-newline-and-maybe-indent) + "Commands that force a scroll after execution at prompt.") + +(defvar erc--scrolltobottom-relaxed-skip-commands '(recenter-top-bottom)) + +(defun erc--scrolltobottom-on-pre-command () + (cl-assert (not erc-scrolltobottom-relaxed)) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (when (> (point) erc-input-marker) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point))))))) + +(defun erc--scrolltobottom-on-post-command () + "Restore window start or scroll to prompt and recenter. +When `erc--scrolltobottom-window-info' is non-nil and its first +item is associated with the selected window, restore start of +window so long as prompt hasn't moved. Expect buffer to be +unnarrowed." + (cl-assert (not erc-scrolltobottom-relaxed)) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (if-let (((not (input-pending-p))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window))) + ((not (memq this-command erc--scrolltobottom-post-force-commands))) + ((= (nth 2 found) (count-screen-lines (window-start) (point))))) + (set-window-start (selected-window) (nth 1 found)) + (erc--scrolltobottom-confirm)) + (setq erc--scrolltobottom-window-info nil)) + +(defun erc--scrolltobottom-on-pre-command-relaxed () + "Maybe scroll to bottom when away from prompt in an unnarrowed buffer. +When `erc-scrolltobottom-relaxed' is active, only scroll when +prompt is past window's end and the command is `end-of-buffer' or +`self-insert-command' (assuming `move-to-prompt' is active). +When at prompt and current command is not `recenter-top-bottom', +stash `erc--scrolltobottom-window-info' for the selected window." + (cl-assert erc-scrolltobottom-relaxed) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (when (and (not (input-pending-p)) + (< (point) erc-input-marker) + (memq this-command erc--scrolltobottom-relaxed-commands) + (< (window-end nil t) erc-input-marker)) + (save-excursion + (goto-char (point-max)) + (recenter (or erc-input-line-position -1)))) + (when (and (> (point) erc-input-marker) + (not (memq this-command + erc--scrolltobottom-relaxed-skip-commands))) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point-max))))))) + +(defun erc--scrolltobottom-on-post-command-relaxed () + "Set window start or scroll when data was captured on pre-command." + (cl-assert erc-scrolltobottom-relaxed) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (when-let ((erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window)))) + (if (and (not (memq this-command erc--scrolltobottom-post-force-commands)) + (= (nth 2 found) + (count-screen-lines (window-start) (point-max)))) + (set-window-start (selected-window) (nth 1 found)) + (recenter (nth 2 found))) + (setq erc--scrolltobottom-window-info nil))) + +;; FIXME this is currently of little value because it doesn't restore +;; the relative position of window point after changing dimensions. +;; It would be preferable to instead stash the previous ratio of +;; window line to body height and later recenter proportionally. But +;; that may not be worth the added bookkeeping. +(defun erc--scrolltobottom-away-from-prompt () + "Scroll to bottom unless at prompt." + (unless (input-pending-p) + (erc--scrolltobottom-confirm))) + +(defun erc--scrolltobottom-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, @@ -103,17 +188,17 @@ erc--scroll-to-bottom-all (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' + ((erc--scrolltobottom-window-info) + (found (assq window erc--scrolltobottom-window-info)) + ((not (erc--scrolltobottom-confirm (nth 2 found))))) + (setf (window-start window) (cadr found))))) + ;; Necessary unless we're sure `erc--scrolltobottom-on-pre-insert' ;; always runs between calls to this function. - (setq erc--scroll-to-bottom-last-window-start nil)) + (setq erc--scrolltobottom-window-info nil)) (defun erc-add-scroll-to-bottom () - "Arrange for `scrolltobottom' to refresh on window configuration changes. -Undo that arrangement when `erc-scrolltobottom-mode' is disabled. + "Arrange for scrolling to bottom on window configuration changes. +Undo that arrangement when disabling `erc-scrolltobottom-mode'. If you find that ERC hangs when using this function, try customizing the value of `erc-input-line-position'. @@ -124,29 +209,57 @@ erc-add-scroll-to-bottom (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)) + #'erc--scrolltobottom-away-from-prompt nil t) + (if erc-scrolltobottom-relaxed + (progn + (when (or (bound-and-true-p erc-move-to-prompt-mode) + (memq 'move-to-prompt erc-modules)) + (cl-pushnew 'self-insert-command + erc--scrolltobottom-relaxed-commands)) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed -90 t) + (add-hook 'pre-command-hook ; preempt `move-to-prompt' + #'erc--scrolltobottom-on-pre-command-relaxed -90 t)) + (add-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command nil t) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command nil t))) (remove-hook 'window-configuration-change-hook - #'erc--possibly-scroll-to-bottom t) + #'erc--scrolltobottom-away-from-prompt t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command-relaxed t) (remove-hook 'post-command-hook - #'erc--possibly-scroll-to-bottom t))) + #'erc--scrolltobottom-on-post-command-relaxed t) + (kill-local-variable 'erc--scrolltobottom-relaxed-commands) + (kill-local-variable 'erc--scrolltobottom-window-info))) -(cl-defmethod erc--scroll-to-bottom-on-pre-insert (_input-or-string) +(cl-defmethod erc--scrolltobottom-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))) + (setq erc--scrolltobottom-window-info + (mapcar (lambda (w) + (list w + (window-start w) + (and-let* + ((erc-scrolltobottom-relaxed) + (c (count-screen-lines (window-start w) + (window-point w) nil w))) + (if (= ?\n (char-before (point-max))) (1+ c) c)))) (get-buffer-window-list nil nil 'visible)))) -(cl-defmethod erc--scroll-to-bottom-on-pre-insert ((input erc-input)) +(cl-defmethod erc--scrolltobottom-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 () +(defun erc--scrolltobottom-confirm (&optional scroll-to) "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." +one. Scroll to SCROLL-TO (or 0) lines from the window's top. +Return non-nil when recentering has occurred." (when erc-insert-marker (let ((resize-mini-windows nil)) (save-restriction @@ -154,7 +267,7 @@ erc--scroll-to-bottom (when (>= (window-point) erc-input-marker) (save-excursion (goto-char (point-max)) - (recenter (or erc-input-line-position -1)) + (recenter (+ (or scroll-to 0) (or erc-input-line-position -1))) t)))))) (defun erc-scroll-to-bottom () @@ -293,12 +406,15 @@ erc--keep-place-indicator-setup (add-hook 'window-configuration-change-hook #'erc--keep-place-indicator-on-window-configuration-change nil t) (when-let* (((memq erc-keep-place-indicator-style '(t arrow))) + (ov-property (if (zerop (fringe-columns 'left)) + 'after-string + 'before-string)) (display (if (zerop (fringe-columns 'left)) `((margin left-margin) ,overlay-arrow-string) '(left-fringe right-triangle erc-keep-place-indicator-arrow))) (bef (propertize " " 'display display))) - (overlay-put erc--keep-place-indicator-overlay 'before-string bef)) + (overlay-put erc--keep-place-indicator-overlay ov-property bef)) (when (memq erc-keep-place-indicator-style '(t face)) (overlay-put erc--keep-place-indicator-overlay 'face 'erc-keep-place-indicator-line))) -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Consider-all-windows-in-erc-scrolltobottom-mode.patch >From 81fcee1a241f4efdf0c80fa9ee8a647cfbad794b Mon Sep 17 00:00:00 2001 From: "F. Jason Park" 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-input-line-position): Mention secondary role when new option `erc-scroll-to-bottom-relaxed' is non-nil. (erc-scrolltobottom-relaxed): New option to leave the prompt stationary instead of forcing it to the bottom of the window. (erc-scrolltobottom-mode, erc-scrolltobottom-enable, erc-scrolltobottom-disable): Use `erc--scrolltobottom-all' instead of `erc-possibly-scroll-to-bottom' for `erc-insert-done-hook' and now also `erc-send-completed-hook'. Add `erc--scrolltobottom-on-pre-insert' to `erc-insert-pre-hook' and `erc-pre-send-functions'. Call `erc-add-scroll-to-bottom' for teardown as well. (erc--scrolltobottom-relaxed-commands, erc--scrolltobottom-window-info, erc--scrolltobottom-post-force-commands, erc--scrolltobottom-relaxed-skip-commands): New internal variables. (erc--scrolltobottom-on-pre-command erc--scrolltobottom-on-post-command): New functions resembling `erc-possibly-scroll-to-bottom' that try to avoid scrolling repeatedly for no reason. (erc--scrolltobottom-on-pre-command-relaxed, erc--scrolltobottom-on-post-command-relaxed): New commands to help implement `erc-scroll-to-bottom-relaxed'. (erc--scrolltobottom-away-from-prompt): New function to scroll to bottom on window configuration changes. (erc--scrolltobottom-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 scroll on `window-configuration-changed-hook'. (erc--scrolltobottom-on-pre-insert): New generic function that remembers the last `window-start' and maybe the current line before inserting a message in order to restore it afterward. (erc--scrolltobottom-confirm): New function, a replacement for `erc-scroll-to-bottom', that returns non-nil when it's actually recentered the window. (erc--keep-place-indicator-setup): Add overlay arrow `after-string' in non-graphical settings in case users have time stamps or other content occupying the left margin. (Bug#64855) --- lisp/erc/erc-goodies.el | 222 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 209 insertions(+), 13 deletions(-) diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index d9ededa8e68..129e17fb5ec 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -44,42 +44,235 @@ erc-input-line-position This should be an integer specifying the line of the buffer on which the input line should stay. A value of \"-1\" would keep the input line positioned on the last line in the buffer. This is passed as an -argument to `recenter'." +argument to `recenter', unless `erc-scrolltobottom-relaxed' is +non-nil, in which case, ERC interprets it as additional lines to +scroll down by per message insertion (minus one for the prompt)." :group 'erc-display :type '(choice integer (const nil))) +(defcustom erc-scrolltobottom-relaxed nil + "Whether to forgo forcing prompt to the bottom of the window. +When non-nil, and point is at the prompt, ERC scrolls the window +up when inserting messages, making the prompt appear stationary. +Users who find this effect too \"stagnant\" can adjust the option +`erc-input-line-position', which ERC borrows to express a scroll +offset when this option is non-nil. Setting that value to zero +lets the prompt drift toward the bottom by one line per message, +which is generally slow enough not to distract while composing +input. Of course, this doesn't apply when receiving a large +influx of messages, such as after typing \"/msg NickServ help\". +Note that ERC only considers this option when initializing the +`scrolltobottom' module and enabling `erc-scrolltobottom-mode'." + :group 'erc-display + :package-version '(ERC . "5.6") ; FIXME sync on release + :type 'boolean) + ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t) (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--scrolltobottom-on-pre-insert) + (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-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--scrolltobottom-on-pre-insert) + (remove-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) + (erc-buffer-do #'erc-add-scroll-to-bottom))) (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))) +(defvar-local erc--scrolltobottom-relaxed-commands '(end-of-buffer) + "Commands triggering a force scroll to prompt. +Only applies with `erc-scrolltobottom-relaxed' when away from prompt.") + +(defvar-local erc--scrolltobottom-window-info nil + "Alist with windows as keys and lists of window-related info as values. +Values are lists containing the last window start position and +the last \"window line\" of point. The \"window line\", which +may be nil, is the number of lines between `window-start' and +`window-point', inclusive.") + +(defvar erc--scrolltobottom-post-force-commands + '(electric-newline-and-maybe-indent) + "Commands that force a scroll after execution at prompt.") + +(defvar erc--scrolltobottom-relaxed-skip-commands '(recenter-top-bottom)) + +(defun erc--scrolltobottom-on-pre-command () + (cl-assert (not erc-scrolltobottom-relaxed)) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (when (> (point) erc-input-marker) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point))))))) + +(defun erc--scrolltobottom-on-post-command () + "Restore window start or scroll to prompt and recenter. +When `erc--scrolltobottom-window-info' is non-nil and its first +item is associated with the selected window, restore start of +window so long as prompt hasn't moved. Expect buffer to be +unnarrowed." + (cl-assert (not erc-scrolltobottom-relaxed)) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (if-let (((not (input-pending-p))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window))) + ((not (memq this-command erc--scrolltobottom-post-force-commands))) + ((= (nth 2 found) (count-screen-lines (window-start) (point))))) + (set-window-start (selected-window) (nth 1 found)) + (erc--scrolltobottom-confirm)) + (setq erc--scrolltobottom-window-info nil)) + +(defun erc--scrolltobottom-on-pre-command-relaxed () + "Maybe scroll to bottom when away from prompt in an unnarrowed buffer. +When `erc-scrolltobottom-relaxed' is active, only scroll when +prompt is past window's end and the command is `end-of-buffer' or +`self-insert-command' (assuming `move-to-prompt' is active). +When at prompt and current command is not `recenter-top-bottom', +stash `erc--scrolltobottom-window-info' for the selected window." + (cl-assert erc-scrolltobottom-relaxed) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (when (and (not (input-pending-p)) + (< (point) erc-input-marker) + (memq this-command erc--scrolltobottom-relaxed-commands) + (< (window-end nil t) erc-input-marker)) + (save-excursion + (goto-char (point-max)) + (recenter (or erc-input-line-position -1)))) + (when (and (> (point) erc-input-marker) + (not (memq this-command + erc--scrolltobottom-relaxed-skip-commands))) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point-max))))))) + +(defun erc--scrolltobottom-on-post-command-relaxed () + "Set window start or scroll when data was captured on pre-command." + (cl-assert erc-scrolltobottom-relaxed) ; delete me + (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me + (when-let ((erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window)))) + (if (and (not (memq this-command erc--scrolltobottom-post-force-commands)) + (= (nth 2 found) + (count-screen-lines (window-start) (point-max)))) + (set-window-start (selected-window) (nth 1 found)) + (recenter (nth 2 found))) + (setq erc--scrolltobottom-window-info nil))) + +;; FIXME this is currently of little value because it doesn't restore +;; the relative position of window point after changing dimensions. +;; It would be preferable to instead stash the previous ratio of +;; window line to body height and later recenter proportionally. But +;; that may not be worth the added bookkeeping. +(defun erc--scrolltobottom-away-from-prompt () + "Scroll to bottom unless at prompt." + (unless (input-pending-p) + (erc--scrolltobottom-confirm))) + +(defun erc--scrolltobottom-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 + ((erc--scrolltobottom-window-info) + (found (assq window erc--scrolltobottom-window-info)) + ((not (erc--scrolltobottom-confirm (nth 2 found))))) + (setf (window-start window) (cadr found))))) + ;; Necessary unless we're sure `erc--scrolltobottom-on-pre-insert' + ;; always runs between calls to this function. + (setq erc--scrolltobottom-window-info nil)) + (defun erc-add-scroll-to-bottom () - "A hook function for `erc-mode-hook' to recenter output at bottom of window. + "Arrange for scrolling to bottom on window configuration changes. +Undo that arrangement when disabling `erc-scrolltobottom-mode'. 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--scrolltobottom-away-from-prompt nil t) + (if erc-scrolltobottom-relaxed + (progn + (when (or (bound-and-true-p erc-move-to-prompt-mode) + (memq 'move-to-prompt erc-modules)) + (cl-pushnew 'self-insert-command + erc--scrolltobottom-relaxed-commands)) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed -90 t) + (add-hook 'pre-command-hook ; preempt `move-to-prompt' + #'erc--scrolltobottom-on-pre-command-relaxed -90 t)) + (add-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command nil t) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command nil t))) + (remove-hook 'window-configuration-change-hook + #'erc--scrolltobottom-away-from-prompt t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command-relaxed t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed t) + (kill-local-variable 'erc--scrolltobottom-relaxed-commands) + (kill-local-variable 'erc--scrolltobottom-window-info))) + +(cl-defmethod erc--scrolltobottom-on-pre-insert (_input-or-string) + "Remember the `window-start' before inserting a message." + (setq erc--scrolltobottom-window-info + (mapcar (lambda (w) + (list w + (window-start w) + (and-let* + ((erc-scrolltobottom-relaxed) + (c (count-screen-lines (window-start w) + (window-point w) nil w))) + (if (= ?\n (char-before (point-max))) (1+ c) c)))) + (get-buffer-window-list nil nil 'visible)))) + +(cl-defmethod erc--scrolltobottom-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--scrolltobottom-confirm (&optional scroll-to) + "Like `erc-scroll-to-bottom', but use `window-point'. +Expect to run in some window, not necessarily the user-selected +one. Scroll to SCROLL-TO (or 0) lines from the window's top. +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 scroll-to 0) (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 @@ -213,12 +406,15 @@ erc--keep-place-indicator-setup (add-hook 'window-configuration-change-hook #'erc--keep-place-indicator-on-window-configuration-change nil t) (when-let* (((memq erc-keep-place-indicator-style '(t arrow))) + (ov-property (if (zerop (fringe-columns 'left)) + 'after-string + 'before-string)) (display (if (zerop (fringe-columns 'left)) `((margin left-margin) ,overlay-arrow-string) '(left-fringe right-triangle erc-keep-place-indicator-arrow))) (bef (propertize " " 'display display))) - (overlay-put erc--keep-place-indicator-overlay 'before-string bef)) + (overlay-put erc--keep-place-indicator-overlay ov-property bef)) (when (memq erc-keep-place-indicator-style '(t face)) (overlay-put erc--keep-place-indicator-overlay 'face 'erc-keep-place-indicator-line))) -- 2.41.0 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Fri Aug 18 09:50:28 2023 Received: (at 64855) by debbugs.gnu.org; 18 Aug 2023 13:50:28 +0000 Received: from localhost ([127.0.0.1]:46410 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qWzru-0000zo-O2 for submit@debbugs.gnu.org; Fri, 18 Aug 2023 09:50:28 -0400 Received: from mail-108-mta0.mxroute.com ([136.175.108.0]:44211) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qWzrr-0000z9-Iz for 64855@debbugs.gnu.org; Fri, 18 Aug 2023 09:50:25 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta0.mxroute.com (ZoneMTA) with ESMTPSA id 18a08e94b20000d7b6.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Fri, 18 Aug 2023 13:50:21 +0000 X-Zone-Loop: 0b48f5115b7732b60a41c090c15dd5ebd15ba462b123 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=qHYWEZ7dHv2CS/Y5TwBoYYRyUTMjNaoB/aoiib43ZXU=; b=e2uefYUHsfeJhPuRxBR158ovzL qoo6Sn8pkDLpU4E0leKW4Z7fjLZh2Vq6As0LqsOLlmxB9st2e6Ifb10vwGGQ+9+JOM7Zt1dFe0WsM seFkr57Ezxuvnazgdzb5avHA2DC8PCJJObKygTdsb7JNwVabhQ53GZXJ2szT9Ahrd/AW/z6En1SPi tQ3AyS4QbXGmhNJps0lxSwb5qQuc+ijpBAizfORjSrSUD6sn9JLmPoVBHleIYSCb0YhkVhcyJ+4pI pJl83SvJJ7bQXz1kaPuijNz54rWxVyL7BXrflWj5QtL3dXEp6gx5NqfklIBw+C2YWqAfn8fpZGZn/ fn5GJJlg==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87h6psyurb.fsf@neverwas.me> (J. P.'s message of "Tue, 25 Jul 2023 06:40:24 -0700") References: <87h6psyurb.fsf@neverwas.me> Date: Fri, 18 Aug 2023 06:50:18 -0700 Message-ID: <877cpscvdx.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain v4. Fix bug involving new option `erc-scrolltobottom-relaxed'. Specify explicit hook depths for `erc--scrolltobottom-on-pre-command{,-relaxed}' and `erc-move-to-prompt' in `pre-command-hook'. Remove assertions. This still needs tests and a news entry. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0000-v3-v4.diff >From 78e5fa32620b386b283f16383f16c713e628a0ff Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Fri, 18 Aug 2023 06:36:44 -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 | 222 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 208 insertions(+), 14 deletions(-) Interdiff: diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index 129e17fb5ec..891641f77f7 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -100,15 +100,15 @@ erc--scrolltobottom-window-info `window-point', inclusive.") (defvar erc--scrolltobottom-post-force-commands - '(electric-newline-and-maybe-indent) + '(electric-newline-and-maybe-indent default-indent-new-line) "Commands that force a scroll after execution at prompt.") -(defvar erc--scrolltobottom-relaxed-skip-commands '(recenter-top-bottom)) +(defvar erc--scrolltobottom-relaxed-skip-commands + '(recenter-top-bottom scroll-down-command)) (defun erc--scrolltobottom-on-pre-command () - (cl-assert (not erc-scrolltobottom-relaxed)) ; delete me - (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me - (when (> (point) erc-input-marker) + (when (and (eq (selected-window) (get-buffer-window)) + (>= (point) erc-input-marker)) (setq erc--scrolltobottom-window-info (list (list (selected-window) (window-start) @@ -120,17 +120,17 @@ erc--scrolltobottom-on-post-command item is associated with the selected window, restore start of window so long as prompt hasn't moved. Expect buffer to be unnarrowed." - (cl-assert (not erc-scrolltobottom-relaxed)) ; delete me - (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me - (if-let (((not (input-pending-p))) - (erc--scrolltobottom-window-info) - (found (car erc--scrolltobottom-window-info)) - ((eq (car found) (selected-window))) - ((not (memq this-command erc--scrolltobottom-post-force-commands))) - ((= (nth 2 found) (count-screen-lines (window-start) (point))))) - (set-window-start (selected-window) (nth 1 found)) - (erc--scrolltobottom-confirm)) - (setq erc--scrolltobottom-window-info nil)) + (when (eq (selected-window) (get-buffer-window)) + (if-let (((not (input-pending-p))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window))) + ((not (memq this-command + erc--scrolltobottom-post-force-commands))) + ((= (nth 2 found) (count-screen-lines (window-start) (point))))) + (set-window-start (selected-window) (nth 1 found)) + (erc--scrolltobottom-confirm)) + (setq erc--scrolltobottom-window-info nil))) (defun erc--scrolltobottom-on-pre-command-relaxed () "Maybe scroll to bottom when away from prompt in an unnarrowed buffer. @@ -139,28 +139,26 @@ erc--scrolltobottom-on-pre-command-relaxed `self-insert-command' (assuming `move-to-prompt' is active). When at prompt and current command is not `recenter-top-bottom', stash `erc--scrolltobottom-window-info' for the selected window." - (cl-assert erc-scrolltobottom-relaxed) ; delete me - (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me - (when (and (not (input-pending-p)) - (< (point) erc-input-marker) - (memq this-command erc--scrolltobottom-relaxed-commands) - (< (window-end nil t) erc-input-marker)) - (save-excursion - (goto-char (point-max)) - (recenter (or erc-input-line-position -1)))) - (when (and (> (point) erc-input-marker) - (not (memq this-command - erc--scrolltobottom-relaxed-skip-commands))) - (setq erc--scrolltobottom-window-info - (list (list (selected-window) - (window-start) - (count-screen-lines (window-start) (point-max))))))) + (when (eq (selected-window) (get-buffer-window)) + (when (and (not (input-pending-p)) + (< (point) erc-input-marker) + (memq this-command erc--scrolltobottom-relaxed-commands) + (< (window-end nil t) erc-input-marker)) + (save-excursion + (goto-char (point-max)) + (recenter (or erc-input-line-position -1)))) + (when (and (>= (point) erc-input-marker) + (not (memq this-command + erc--scrolltobottom-relaxed-skip-commands))) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point-max)))))))) (defun erc--scrolltobottom-on-post-command-relaxed () "Set window start or scroll when data was captured on pre-command." - (cl-assert erc-scrolltobottom-relaxed) ; delete me - (cl-assert (eq (selected-window) (get-buffer-window))) ; delete me - (when-let ((erc--scrolltobottom-window-info) + (when-let (((eq (selected-window) (get-buffer-window))) + (erc--scrolltobottom-window-info) (found (car erc--scrolltobottom-window-info)) ((eq (car found) (selected-window)))) (if (and (not (memq this-command erc--scrolltobottom-post-force-commands)) @@ -217,13 +215,13 @@ erc-add-scroll-to-bottom (cl-pushnew 'self-insert-command erc--scrolltobottom-relaxed-commands)) (add-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command-relaxed -90 t) + #'erc--scrolltobottom-on-post-command-relaxed 60 t) (add-hook 'pre-command-hook ; preempt `move-to-prompt' - #'erc--scrolltobottom-on-pre-command-relaxed -90 t)) + #'erc--scrolltobottom-on-pre-command-relaxed 60 t)) (add-hook 'pre-command-hook - #'erc--scrolltobottom-on-pre-command nil t) + #'erc--scrolltobottom-on-pre-command 60 t) (add-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command nil t))) + #'erc--scrolltobottom-on-post-command 60 t))) (remove-hook 'window-configuration-change-hook #'erc--scrolltobottom-away-from-prompt t) (remove-hook 'pre-command-hook @@ -246,7 +244,7 @@ erc--scrolltobottom-on-pre-insert (and-let* ((erc-scrolltobottom-relaxed) (c (count-screen-lines (window-start w) - (window-point w) nil w))) + (point-max) nil w))) (if (= ?\n (char-before (point-max))) (1+ c) c)))) (get-buffer-window-list nil nil 'visible)))) @@ -328,7 +326,7 @@ erc-move-to-prompt (defun erc-move-to-prompt-setup () "Initialize the move-to-prompt module." - (add-hook 'pre-command-hook #'erc-move-to-prompt nil t)) + (add-hook 'pre-command-hook #'erc-move-to-prompt 70 t)) ;;; Keep place in unvisited channels ;;;###autoload(autoload 'erc-keep-place-mode "erc-goodies" nil t) -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Consider-all-windows-in-erc-scrolltobottom-mode.patch >From 78e5fa32620b386b283f16383f16c713e628a0ff Mon Sep 17 00:00:00 2001 From: "F. Jason Park" 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-input-line-position): Mention secondary role when new option `erc-scroll-to-bottom-relaxed' is non-nil. (erc-scrolltobottom-relaxed): New option to leave the prompt stationary instead of forcing it to the bottom of the window. (erc-scrolltobottom-mode, erc-scrolltobottom-enable, erc-scrolltobottom-disable): Use `erc--scrolltobottom-all' instead of `erc-possibly-scroll-to-bottom' for `erc-insert-done-hook' and now also `erc-send-completed-hook'. Add `erc--scrolltobottom-on-pre-insert' to `erc-insert-pre-hook' and `erc-pre-send-functions'. Call `erc-add-scroll-to-bottom' for teardown as well. (erc--scrolltobottom-relaxed-commands, erc--scrolltobottom-window-info, erc--scrolltobottom-post-force-commands, erc--scrolltobottom-relaxed-skip-commands): New internal variables. (erc--scrolltobottom-on-pre-command erc--scrolltobottom-on-post-command): New functions resembling `erc-possibly-scroll-to-bottom' that try to avoid scrolling repeatedly for no reason. (erc--scrolltobottom-on-pre-command-relaxed, erc--scrolltobottom-on-post-command-relaxed): New commands to help implement `erc-scroll-to-bottom-relaxed'. (erc--scrolltobottom-away-from-prompt): New function to scroll to bottom on window configuration changes. (erc--scrolltobottom-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 scroll on `window-configuration-changed-hook'. (erc--scrolltobottom-on-pre-insert): New generic function that remembers the last `window-start' and maybe the current line before inserting a message in order to restore it afterward. (erc--scrolltobottom-confirm): New function, a replacement for `erc-scroll-to-bottom', that returns non-nil when it's actually recentered the window. (erc-move-to-prompt-setup): Add `erc-move-to-prompt' to `pre-command-hook' at a depth of 70. (erc--keep-place-indicator-setup): Add overlay arrow `after-string' in non-graphical settings in case users have time stamps or other content occupying the left margin. (Bug#64855) --- lisp/erc/erc-goodies.el | 222 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 208 insertions(+), 14 deletions(-) diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index d9ededa8e68..891641f77f7 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -44,42 +44,233 @@ erc-input-line-position This should be an integer specifying the line of the buffer on which the input line should stay. A value of \"-1\" would keep the input line positioned on the last line in the buffer. This is passed as an -argument to `recenter'." +argument to `recenter', unless `erc-scrolltobottom-relaxed' is +non-nil, in which case, ERC interprets it as additional lines to +scroll down by per message insertion (minus one for the prompt)." :group 'erc-display :type '(choice integer (const nil))) +(defcustom erc-scrolltobottom-relaxed nil + "Whether to forgo forcing prompt to the bottom of the window. +When non-nil, and point is at the prompt, ERC scrolls the window +up when inserting messages, making the prompt appear stationary. +Users who find this effect too \"stagnant\" can adjust the option +`erc-input-line-position', which ERC borrows to express a scroll +offset when this option is non-nil. Setting that value to zero +lets the prompt drift toward the bottom by one line per message, +which is generally slow enough not to distract while composing +input. Of course, this doesn't apply when receiving a large +influx of messages, such as after typing \"/msg NickServ help\". +Note that ERC only considers this option when initializing the +`scrolltobottom' module and enabling `erc-scrolltobottom-mode'." + :group 'erc-display + :package-version '(ERC . "5.6") ; FIXME sync on release + :type 'boolean) + ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t) (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--scrolltobottom-on-pre-insert) + (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-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--scrolltobottom-on-pre-insert) + (remove-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) + (erc-buffer-do #'erc-add-scroll-to-bottom))) (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))) +(defvar-local erc--scrolltobottom-relaxed-commands '(end-of-buffer) + "Commands triggering a force scroll to prompt. +Only applies with `erc-scrolltobottom-relaxed' when away from prompt.") + +(defvar-local erc--scrolltobottom-window-info nil + "Alist with windows as keys and lists of window-related info as values. +Values are lists containing the last window start position and +the last \"window line\" of point. The \"window line\", which +may be nil, is the number of lines between `window-start' and +`window-point', inclusive.") + +(defvar erc--scrolltobottom-post-force-commands + '(electric-newline-and-maybe-indent default-indent-new-line) + "Commands that force a scroll after execution at prompt.") + +(defvar erc--scrolltobottom-relaxed-skip-commands + '(recenter-top-bottom scroll-down-command)) + +(defun erc--scrolltobottom-on-pre-command () + (when (and (eq (selected-window) (get-buffer-window)) + (>= (point) erc-input-marker)) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point))))))) + +(defun erc--scrolltobottom-on-post-command () + "Restore window start or scroll to prompt and recenter. +When `erc--scrolltobottom-window-info' is non-nil and its first +item is associated with the selected window, restore start of +window so long as prompt hasn't moved. Expect buffer to be +unnarrowed." + (when (eq (selected-window) (get-buffer-window)) + (if-let (((not (input-pending-p))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window))) + ((not (memq this-command + erc--scrolltobottom-post-force-commands))) + ((= (nth 2 found) (count-screen-lines (window-start) (point))))) + (set-window-start (selected-window) (nth 1 found)) + (erc--scrolltobottom-confirm)) + (setq erc--scrolltobottom-window-info nil))) + +(defun erc--scrolltobottom-on-pre-command-relaxed () + "Maybe scroll to bottom when away from prompt in an unnarrowed buffer. +When `erc-scrolltobottom-relaxed' is active, only scroll when +prompt is past window's end and the command is `end-of-buffer' or +`self-insert-command' (assuming `move-to-prompt' is active). +When at prompt and current command is not `recenter-top-bottom', +stash `erc--scrolltobottom-window-info' for the selected window." + (when (eq (selected-window) (get-buffer-window)) + (when (and (not (input-pending-p)) + (< (point) erc-input-marker) + (memq this-command erc--scrolltobottom-relaxed-commands) + (< (window-end nil t) erc-input-marker)) + (save-excursion + (goto-char (point-max)) + (recenter (or erc-input-line-position -1)))) + (when (and (>= (point) erc-input-marker) + (not (memq this-command + erc--scrolltobottom-relaxed-skip-commands))) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point-max)))))))) + +(defun erc--scrolltobottom-on-post-command-relaxed () + "Set window start or scroll when data was captured on pre-command." + (when-let (((eq (selected-window) (get-buffer-window))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window)))) + (if (and (not (memq this-command erc--scrolltobottom-post-force-commands)) + (= (nth 2 found) + (count-screen-lines (window-start) (point-max)))) + (set-window-start (selected-window) (nth 1 found)) + (recenter (nth 2 found))) + (setq erc--scrolltobottom-window-info nil))) + +;; FIXME this is currently of little value because it doesn't restore +;; the relative position of window point after changing dimensions. +;; It would be preferable to instead stash the previous ratio of +;; window line to body height and later recenter proportionally. But +;; that may not be worth the added bookkeeping. +(defun erc--scrolltobottom-away-from-prompt () + "Scroll to bottom unless at prompt." + (unless (input-pending-p) + (erc--scrolltobottom-confirm))) + +(defun erc--scrolltobottom-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 + ((erc--scrolltobottom-window-info) + (found (assq window erc--scrolltobottom-window-info)) + ((not (erc--scrolltobottom-confirm (nth 2 found))))) + (setf (window-start window) (cadr found))))) + ;; Necessary unless we're sure `erc--scrolltobottom-on-pre-insert' + ;; always runs between calls to this function. + (setq erc--scrolltobottom-window-info nil)) + (defun erc-add-scroll-to-bottom () - "A hook function for `erc-mode-hook' to recenter output at bottom of window. + "Arrange for scrolling to bottom on window configuration changes. +Undo that arrangement when disabling `erc-scrolltobottom-mode'. 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--scrolltobottom-away-from-prompt nil t) + (if erc-scrolltobottom-relaxed + (progn + (when (or (bound-and-true-p erc-move-to-prompt-mode) + (memq 'move-to-prompt erc-modules)) + (cl-pushnew 'self-insert-command + erc--scrolltobottom-relaxed-commands)) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed 60 t) + (add-hook 'pre-command-hook ; preempt `move-to-prompt' + #'erc--scrolltobottom-on-pre-command-relaxed 60 t)) + (add-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command 60 t) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command 60 t))) + (remove-hook 'window-configuration-change-hook + #'erc--scrolltobottom-away-from-prompt t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command-relaxed t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed t) + (kill-local-variable 'erc--scrolltobottom-relaxed-commands) + (kill-local-variable 'erc--scrolltobottom-window-info))) + +(cl-defmethod erc--scrolltobottom-on-pre-insert (_input-or-string) + "Remember the `window-start' before inserting a message." + (setq erc--scrolltobottom-window-info + (mapcar (lambda (w) + (list w + (window-start w) + (and-let* + ((erc-scrolltobottom-relaxed) + (c (count-screen-lines (window-start w) + (point-max) nil w))) + (if (= ?\n (char-before (point-max))) (1+ c) c)))) + (get-buffer-window-list nil nil 'visible)))) + +(cl-defmethod erc--scrolltobottom-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--scrolltobottom-confirm (&optional scroll-to) + "Like `erc-scroll-to-bottom', but use `window-point'. +Expect to run in some window, not necessarily the user-selected +one. Scroll to SCROLL-TO (or 0) lines from the window's top. +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 scroll-to 0) (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 @@ -135,7 +326,7 @@ erc-move-to-prompt (defun erc-move-to-prompt-setup () "Initialize the move-to-prompt module." - (add-hook 'pre-command-hook #'erc-move-to-prompt nil t)) + (add-hook 'pre-command-hook #'erc-move-to-prompt 70 t)) ;;; Keep place in unvisited channels ;;;###autoload(autoload 'erc-keep-place-mode "erc-goodies" nil t) @@ -213,12 +404,15 @@ erc--keep-place-indicator-setup (add-hook 'window-configuration-change-hook #'erc--keep-place-indicator-on-window-configuration-change nil t) (when-let* (((memq erc-keep-place-indicator-style '(t arrow))) + (ov-property (if (zerop (fringe-columns 'left)) + 'after-string + 'before-string)) (display (if (zerop (fringe-columns 'left)) `((margin left-margin) ,overlay-arrow-string) '(left-fringe right-triangle erc-keep-place-indicator-arrow))) (bef (propertize " " 'display display))) - (overlay-put erc--keep-place-indicator-overlay 'before-string bef)) + (overlay-put erc--keep-place-indicator-overlay ov-property bef)) (when (memq erc-keep-place-indicator-style '(t face)) (overlay-put erc--keep-place-indicator-overlay 'face 'erc-keep-place-indicator-line))) -- 2.41.0 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 24 10:11:47 2023 Received: (at 64855) by debbugs.gnu.org; 24 Aug 2023 14:11:47 +0000 Received: from localhost ([127.0.0.1]:38354 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qZB3r-0007Rh-M8 for submit@debbugs.gnu.org; Thu, 24 Aug 2023 10:11:47 -0400 Received: from mail-108-mta158.mxroute.com ([136.175.108.158]:42039) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qZB3n-0007RQ-E3 for 64855@debbugs.gnu.org; Thu, 24 Aug 2023 10:11:44 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta158.mxroute.com (ZoneMTA) with ESMTPSA id 18a27e2de67000d7b6.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Thu, 24 Aug 2023 14:11:34 +0000 X-Zone-Loop: c1b0e6ecca1d408880e84f66afbe3acdd61f821f8a89 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=Xfx2plXf4cfhnEjGIDtviG54GHuqFTYL2/zCsKUgzoc=; b=G3Kbp8Z26PYbBlnWHYj2o8ON18 v+rmwcm8VDntzu2pL75mkvyKq4suT1dXojbrCPjS8nIdnExXJYgAhvZ6busYAl2YxvfdmOOS6/MF5 NFUY+/giMkeJe/5w2abEjsCmdGkonbOHjWfXpxmEw1ZgKM04mpTWx/uFJXIuJQ/KI6sxps1AJrLGP 1swlfyGN+VOUitliEw7ptRn3oZ7VCPb8eAkbSdbtwF3SV8UnY6EA7o1jlUWdmP46r8jBZLOte2plw zBTleX6vuvYPCCP5FvCk0U0nJHB2KOJ910wmZMGLNqXq+RXYdtN5gP91iDyGGs1lokyD8vcC/V5Sr r7STZKOg==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87h6psyurb.fsf@neverwas.me> (J. P.'s message of "Tue, 25 Jul 2023 06:40:24 -0700") References: <87h6psyurb.fsf@neverwas.me> Date: Thu, 24 Aug 2023 07:11:26 -0700 Message-ID: <87il948r8x.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" --=-=-= Content-Type: text/plain v5. Make new behavior hinge on new option, disabled by default. Assign some module functions explicit hook depths. Add news and tests. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0000-v4-v5.diff >From 3ed5a748d76989ec2ed79311e3550da5c6d1aaa1 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Thu, 24 Aug 2023 06:49:46 -0700 Subject: [PATCH 0/1] *** NOT A PATCH *** *** BLURB HERE *** F. Jason Park (1): [5.6] Consider all windows in erc-scrolltobottom-mode etc/ERC-NEWS | 19 +- lisp/erc/erc-backend.el | 2 +- lisp/erc/erc-goodies.el | 267 ++++++++++++++++-- .../erc/erc-scenarios-scrolltobottom-all.el | 48 ++++ .../erc-scenarios-scrolltobottom-relaxed.el | 140 +++++++++ test/lisp/erc/erc-scenarios-scrolltobottom.el | 44 +++ .../erc/resources/base/assoc/bumped/again.eld | 10 +- .../resources/base/assoc/bumped/foisted.eld | 10 +- .../resources/base/assoc/bumped/refoisted.eld | 8 +- .../resources/base/netid/bouncer/barnet.eld | 2 +- .../resources/base/netid/bouncer/foonet.eld | 2 +- test/lisp/erc/resources/erc-d/erc-d-t.el | 7 +- .../erc/resources/erc-scenarios-common.el | 196 +++++++++++++ .../erc/resources/scrolltobottom/help.eld | 46 +++ 14 files changed, 761 insertions(+), 40 deletions(-) create mode 100644 test/lisp/erc/erc-scenarios-scrolltobottom-all.el create mode 100644 test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el create mode 100644 test/lisp/erc/erc-scenarios-scrolltobottom.el create mode 100644 test/lisp/erc/resources/scrolltobottom/help.eld Interdiff: diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index 94452514e6d..72b768b02ec 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -178,6 +178,15 @@ been restored with a slightly revised role contingent on a few assumptions explained in its doc string. For clarity, it has been renamed 'erc-ensure-target-buffer-on-privmsg'. +** Module 'scrolltobottom' attempts to behave more sensibly. +Enabling the experimental option 'erc-scrolltobottom-all' tells +'scrolltobottom' to be more vigilant about recentering and to do so in +all ERC windows. The dependent option 'erc-scrolltobottom-relaxed', +likewise experimental, makes ERC's prompt stationary wherever it +resides instead of forcing it to the bottom of the window. That is, +new input appears above the prompt, scrolling existing messages upward +to compensate. + ** Subtle changes in two fundamental faces. Users of the default theme may notice that 'erc-action-face' and 'erc-notice-face' now appear slightly less bold on systems supporting @@ -232,9 +241,9 @@ property of the same name has been retained and now has a value of Built-in and third-party modules rely on certain hooks for adjusting incoming and outgoing messages upon insertion. And some modules only want to do so after others have done their damage. Traditionally, -this required various hacks and finagling to achieve. And while this -release makes an effort to load modules in a more consistent order, -that alone isn't enough to ensure similar predictability among +this has required various hacks and finagling to achieve. And while +this release makes an effort to load modules in a more consistent +order, that alone isn't enough to ensure similar predictability among essential members of important hooks. Luckily, ERC now leverages a feature introduced in Emacs 27, "hook @@ -248,6 +257,10 @@ the first two, 'erc-button-add-buttons' and 'erc-fill', which have been swapped with respect to their previous places in recent ERC versions. +The same depth interval is now also provisionally reserved for +'erc-insert-pre-hook' and for the non-ERC hooks 'pre-command-hook' and +'post-command-hook', but only locally, in ERC buffers. + *** ERC now manages timestamp-related properties a bit differently. For starters, the 'cursor-sensor-functions' property no longer contains unique closures and thus no longer proves effective for diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index eb3ec39fedd..9e121ec1e92 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -1089,7 +1089,7 @@ erc--hide-prompt (put-text-property erc-insert-marker (1- erc-input-marker) 'erc-prompt 'hidden) (erc--conceal-prompt)) - (add-hook 'pre-command-hook #'erc--unhide-prompt-on-self-insert 91 t)))) + (add-hook 'pre-command-hook #'erc--unhide-prompt-on-self-insert 80 t)))) (defun erc-process-sentinel (cproc event) "Sentinel function for ERC process." diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index 30089915c19..07a93b06f2c 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -50,19 +50,31 @@ erc-input-line-position :group 'erc-display :type '(choice integer (const nil))) +(defcustom erc-scrolltobottom-all nil + "Whether to scroll all windows or just the selected one. +A value of nil preserves pre-5.6 behavior, in which scrolling +only affects the selected window. Users should consider its +non-nil behavior experimental for the time being. Note also that +ERC expects this option to be configured before module +initialization." + :group 'erc-display + :package-version '(ERC . "5.6") ; FIXME sync on release + :type 'boolean) + (defcustom erc-scrolltobottom-relaxed nil "Whether to forgo forcing prompt to the bottom of the window. When non-nil, and point is at the prompt, ERC scrolls the window up when inserting messages, making the prompt appear stationary. Users who find this effect too \"stagnant\" can adjust the option `erc-input-line-position', which ERC borrows to express a scroll -offset when this option is non-nil. Setting that value to zero -lets the prompt drift toward the bottom by one line per message, -which is generally slow enough not to distract while composing -input. Of course, this doesn't apply when receiving a large -influx of messages, such as after typing \"/msg NickServ help\". -Note that ERC only considers this option when initializing the -`scrolltobottom' module and enabling `erc-scrolltobottom-mode'." +step offset when this option is non-nil. Setting that value to +zero lets the prompt drift toward the bottom by one line per +message, which is generally slow enough not to distract while +composing input. Of course, this doesn't apply when receiving a +large influx of messages, such as after typing \"/msg NickServ +help\". Note that ERC only considers this option when the +experimental companion option `erc-scrolltobottom-all' is enabled +and, only then, during module setup." :group 'erc-display :package-version '(ERC . "5.6") ; FIXME sync on release :type 'boolean) @@ -70,18 +82,25 @@ erc-scrolltobottom-relaxed ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t) (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--scrolltobottom-on-pre-insert) - (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) - (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) - (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-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--scrolltobottom-on-pre-insert) - (remove-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) - (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) - (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) - (erc-buffer-do #'erc-add-scroll-to-bottom))) + ((add-hook 'erc-mode-hook #'erc--scrolltobottom-setup) + (unless erc--updating-modules-p (erc-buffer-do #'erc--scrolltobottom-setup)) + (if erc-scrolltobottom-all + (progn + (add-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert 25) + (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-all)) + (add-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom))) + ((remove-hook 'erc-mode-hook #'erc--scrolltobottom-setup) + (erc-buffer-do #'erc--scrolltobottom-setup) + (if erc-scrolltobottom-all + (progn + (remove-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-pre-send-functions + #'erc--scrolltobottom-on-pre-insert)) + (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)))) (defun erc-possibly-scroll-to-bottom () "Like `erc-add-scroll-to-bottom', but only if window is selected." @@ -100,8 +119,12 @@ erc--scrolltobottom-window-info `window-point', inclusive.") (defvar erc--scrolltobottom-post-force-commands - '(electric-newline-and-maybe-indent default-indent-new-line) - "Commands that force a scroll after execution at prompt.") + '(beginning-of-buffer + electric-newline-and-maybe-indent + default-indent-new-line) + "Commands that force a scroll after execution at prompt. +That is, ERC recalculates the window's start instead of blindly +restoring it.") (defvar erc--scrolltobottom-relaxed-skip-commands '(recenter-top-bottom scroll-down-command)) @@ -112,7 +135,7 @@ erc--scrolltobottom-on-pre-command (setq erc--scrolltobottom-window-info (list (list (selected-window) (window-start) - (count-screen-lines (window-start) (point))))))) + (count-screen-lines (window-start) (point-max))))))) (defun erc--scrolltobottom-on-post-command () "Restore window start or scroll to prompt and recenter. @@ -127,7 +150,8 @@ erc--scrolltobottom-on-post-command ((eq (car found) (selected-window))) ((not (memq this-command erc--scrolltobottom-post-force-commands))) - ((= (nth 2 found) (count-screen-lines (window-start) (point))))) + ((= (nth 2 found) + (count-screen-lines (window-start) (point-max))))) (set-window-start (selected-window) (nth 1 found)) (erc--scrolltobottom-confirm)) (setq erc--scrolltobottom-window-info nil))) @@ -137,8 +161,9 @@ erc--scrolltobottom-on-pre-command-relaxed When `erc-scrolltobottom-relaxed' is active, only scroll when prompt is past window's end and the command is `end-of-buffer' or `self-insert-command' (assuming `move-to-prompt' is active). -When at prompt and current command is not `recenter-top-bottom', -stash `erc--scrolltobottom-window-info' for the selected window." +When at prompt and current command does not appear in +`erc--scrolltobottom-relaxed-skip-commands', stash +`erc--scrolltobottom-window-info' for the selected window." (when (eq (selected-window) (get-buffer-window)) (when (and (not (input-pending-p)) (< (point) erc-input-marker) @@ -175,7 +200,9 @@ erc--scrolltobottom-on-post-command-relaxed ;; that may not be worth the added bookkeeping. (defun erc--scrolltobottom-away-from-prompt () "Scroll to bottom unless at prompt." - (unless (input-pending-p) + (unless (or (input-pending-p) + (minibuffer-window-active-p (minibuffer-window)) + (eq (old-selected-window) (minibuffer-window))) (erc--scrolltobottom-confirm))) (defun erc--scrolltobottom-all (&rest _) @@ -195,8 +222,7 @@ erc--scrolltobottom-all (setq erc--scrolltobottom-window-info nil)) (defun erc-add-scroll-to-bottom () - "Arrange for scrolling to bottom on window configuration changes. -Undo that arrangement when disabling `erc-scrolltobottom-mode'. + "A hook function for `erc-mode-hook' to recenter output at bottom of window. If you find that ERC hangs when using this function, try customizing the value of `erc-input-line-position'. @@ -204,24 +230,35 @@ erc-add-scroll-to-bottom 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." + (declare (obsolete erc--scrolltobottom-setup "30.1")) + (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t)) + +(cl-defgeneric erc--scrolltobottom-setup () + "Arrange for scrolling to bottom on window configuration changes. +Undo that arrangement when disabling `erc-scrolltobottom-mode'." (if erc-scrolltobottom-mode - (progn + (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t) + (remove-hook 'post-command-hook #'erc-scroll-to-bottom t))) + +(cl-defmethod erc--scrolltobottom-setup (&context + (erc-scrolltobottom-all (eql t))) + (if erc-scrolltobottom-mode + (if erc-scrolltobottom-relaxed + (progn + (when (or (bound-and-true-p erc-move-to-prompt-mode) + (memq 'move-to-prompt erc-modules)) + (cl-pushnew 'self-insert-command + erc--scrolltobottom-relaxed-commands)) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed 60 t) + (add-hook 'pre-command-hook ; preempt `move-to-prompt' + #'erc--scrolltobottom-on-pre-command-relaxed 60 t)) (add-hook 'window-configuration-change-hook #'erc--scrolltobottom-away-from-prompt nil t) - (if erc-scrolltobottom-relaxed - (progn - (when (or (bound-and-true-p erc-move-to-prompt-mode) - (memq 'move-to-prompt erc-modules)) - (cl-pushnew 'self-insert-command - erc--scrolltobottom-relaxed-commands)) - (add-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command-relaxed 60 t) - (add-hook 'pre-command-hook ; preempt `move-to-prompt' - #'erc--scrolltobottom-on-pre-command-relaxed 60 t)) - (add-hook 'pre-command-hook - #'erc--scrolltobottom-on-pre-command 60 t) - (add-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command 60 t))) + (add-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command 60 t) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command 60 t)) (remove-hook 'window-configuration-change-hook #'erc--scrolltobottom-away-from-prompt t) (remove-hook 'pre-command-hook @@ -255,9 +292,9 @@ erc--scrolltobottom-on-pre-insert (defun erc--scrolltobottom-confirm (&optional scroll-to) "Like `erc-scroll-to-bottom', but use `window-point'. -Expect to run in some window, not necessarily the user-selected -one. Scroll to SCROLL-TO (or 0) lines from the window's top. -Return non-nil when recentering has occurred." +Position current line (with `recenter') SCROLL-TO lines below +window's top. Return nil if point is not in prompt area or if +prompt isn't ready." (when erc-insert-marker (let ((resize-mini-windows nil)) (save-restriction @@ -332,7 +369,7 @@ erc-move-to-prompt-setup ;;;###autoload(autoload 'erc-keep-place-mode "erc-goodies" nil t) (define-erc-module keep-place nil "Leave point above un-viewed text in other channels." - ((add-hook 'erc-insert-pre-hook #'erc-keep-place)) + ((add-hook 'erc-insert-pre-hook #'erc-keep-place 85)) ((remove-hook 'erc-insert-pre-hook #'erc-keep-place))) (defcustom erc-keep-place-indicator-style t @@ -427,7 +464,7 @@ keep-place-indicator ((memq 'keep-place erc-modules) (erc-keep-place-mode +1)) ;; Enable a local version of `keep-place-mode'. - (t (add-hook 'erc-insert-pre-hook #'erc-keep-place 90 t))) + (t (add-hook 'erc-insert-pre-hook #'erc-keep-place 85 t))) (if (pcase erc-keep-place-indicator-buffer-type ('target erc--target) ('server (not erc--target)) @@ -450,7 +487,7 @@ erc--keep-place-indicator-on-global-module global one." (if erc-keep-place-mode (remove-hook 'erc-insert-pre-hook #'erc-keep-place t) - (add-hook 'erc-insert-pre-hook #'erc-keep-place 90 t))) + (add-hook 'erc-insert-pre-hook #'erc-keep-place 85 t))) (defun erc-keep-place-move (pos) "Move keep-place indicator to current line or POS. diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-all.el b/test/lisp/erc/erc-scenarios-scrolltobottom-all.el new file mode 100644 index 00000000000..33f232e64d9 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom-all.el @@ -0,0 +1,48 @@ +;;; erc-scenarios-scrolltobottom-all.el --- erc-scrolltobottom-all test -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--all () + :tags '(:expensive-test) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (let ((erc-scrolltobottom-all t)) + + (erc-scenarios-scrolltobottom--normal + (lambda () + (ert-info ("New insertion anchors prompt in other window") + (let ((w (next-window))) + ;; We're at prompt and aligned to bottom. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (erc-scenarios-common--at-win-end-p w)) + (erc-d-t-ensure-for 0.5 + (erc-scenarios-common--at-win-end-p w)))))))) + +;;; erc-scenarios-scrolltobottom-all.el ends here diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el new file mode 100644 index 00000000000..7d256bf711b --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el @@ -0,0 +1,140 @@ +;;; erc-scenarios-scrolltobottom-relaxed.el --- erc-scrolltobottom-relaxed -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;; TODO assert behavior of prompt input spanning multiple lines, with +;; and without line endings. + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--relaxed () + :tags '(:expensive-test) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "scrolltobottom") + (dumb-server (erc-d-run "localhost" t 'help)) + (port (process-contact dumb-server :service)) + (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) + (erc-scrolltobottom-all t) + (erc-scrolltobottom-relaxed t) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter)) + lower upper) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer "foonet" + (should (looking-at " and")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below 15) + (recenter 0) + + (ert-info ("Moving into prompt does not trigger scroll") + (with-selected-window (next-window) + (should-not (erc-scenarios-common--at-win-end-p)) + (recenter 0) + (goto-char (1- erc-insert-marker)) + (execute-kbd-macro "\C-n") + (should-not (erc-scenarios-common--at-win-end-p)) + (should (= (point) (point-max))) + (setq lower (count-screen-lines (window-start) (window-point))))) + + (ert-info ("Module `move-to-prompt' still works") + ;; Prompt is somewhere in the middle of the window. + (should (erc-scenarios-common--above-win-end-p)) + (should-not (= (point-max) (point))) + ;; Hitting a self-insert key triggers `move-to-prompt' but not + ;; a scroll (to bottom). + (execute-kbd-macro "hi") + ;; Prompt and input appear on same line. + (should (= (point-max) (point))) + (setq upper (count-screen-lines (window-start) (window-point))) + (should-not (= upper (window-body-height)))) + + (ert-info ("Command `recenter-top-bottom' allowed at prompt") + ;; Hitting C-l recenters the window. + (should (= upper (count-screen-lines (window-start) (window-point)))) + (let ((lines (list upper))) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (setq lines (delete-dups lines)) + (should (= (length lines) 4)))) + + (ert-info ("Command `beginning-of-buffer' allowed at prompt") + ;; Hitting C-< goes to beginning of buffer. + (execute-kbd-macro "\M-<") + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New message doesn't trigger scroll when away from prompt") + ;; Arriving insertions don't trigger a scroll when away from the + ;; prompt. New output not seen. + (erc-cmd-MSG "NickServ help register") + (save-excursion (erc-d-t-search-for 10 "End of NickServ")) + (should (= 1 (point))) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New insertion keeps prompt stationary in other window") + (let ((w (next-window))) + ;; We're at prompt and completely stationary. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (= lower (count-screen-lines (window-start w) (window-point w)))) + (erc-d-t-ensure-for 0.5 + (= lower (count-screen-lines (window-start w) + (window-point w)))))) + + (should (= 2 (length (window-list)))) + (ert-info ("New message does not trigger a scroll when at prompt") + ;; Recenter so prompt is above rather than at window's end. + (funcall expect 10 "End of NickServ HELP") + (recenter 0) + (set-window-point nil (point-max)) + (setq upper (count-screen-lines (window-start) (window-point))) + ;; Prompt is somewhere in the middle of the window. + (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p)) + (erc-scenarios-common-say "/msg NickServ help identify") + ;; New arriving messages don't move prompt. + (erc-d-t-ensure-for 1 + (= upper (count-screen-lines (window-start) (window-point)))) + (funcall expect 10 "IDENTIFY lets you login"))))) + +;;; erc-scenarios-scrolltobottom-relaxed.el ends here diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom.el b/test/lisp/erc/erc-scenarios-scrolltobottom.el new file mode 100644 index 00000000000..44e64204fb1 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom.el @@ -0,0 +1,44 @@ +;;; erc-scenarios-scrolltobottom.el --- erc-scrolltobottom-mode -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--normal () + :tags '(:expensive-test) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (erc-scenarios-scrolltobottom--normal + (lambda () + (ert-info ("New insertion doesn't anchor prompt in other window") + (let ((w (next-window))) + ;; We're at prompt but not aligned to bottom. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (not (erc-scenarios-common--at-win-end-p w)))))))) + +;;; erc-scenarios-scrolltobottom.el ends here diff --git a/test/lisp/erc/resources/base/assoc/bumped/again.eld b/test/lisp/erc/resources/base/assoc/bumped/again.eld index ab3c7b06214..aef164b6237 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/again.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/again.eld @@ -1,10 +1,10 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0.0 ":irc.foonet.org 433 * tester :Nickname is reserved by a different account") (0.0 ":irc.foonet.org FAIL NICK NICKNAME_RESERVED tester :Nickname is reserved by a different account")) -((nick 3 "NICK tester`") +((nick 10 "NICK tester`") (0.1 ":irc.foonet.org 001 tester` :Welcome to the foonet IRC Network tester`") (0.0 ":irc.foonet.org 002 tester` :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") (0.0 ":irc.foonet.org 003 tester` :This server was created Fri, 24 Sep 2021 01:38:36 UTC") @@ -21,10 +21,10 @@ (0.2 ":irc.foonet.org 266 tester` 3 3 :Current global users 3, max 3") (0.0 ":irc.foonet.org 422 tester` :MOTD File is missing")) -((mode-user 3.2 "MODE tester` +i") +((mode-user 10 "MODE tester` +i") (0.0 ":irc.foonet.org 221 tester` +i") (0.0 ":irc.foonet.org NOTICE tester` :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) -((privmsg 42.6 "PRIVMSG NickServ :IDENTIFY tester changeme") +((privmsg 10 "PRIVMSG NickServ :IDENTIFY tester changeme") (0.01 ":tester`!~u@rpaau95je67ci.irc NICK tester") (0.0 ":NickServ!NickServ@localhost NOTICE tester :You're now logged in as tester")) diff --git a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld index 5c36e58d9d3..0f7aadac564 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") (0.0 ":irc.foonet.org 003 tester :This server was created Fri, 24 Sep 2021 01:38:36 UTC") @@ -17,14 +17,14 @@ (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") (0.0 ":irc.foonet.org 422 tester :MOTD File is missing")) -((mode-user 1.2 "MODE tester +i") +((mode-user 10 "MODE tester +i") (0.0 ":irc.foonet.org 221 tester +i") (0.0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) -((privmsg 17.21 "PRIVMSG bob :hi") +((privmsg 10 "PRIVMSG bob :hi") (0.02 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :hola") (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :how r u?")) -((quit 18.19 "QUIT :" quit) +((quit 10 "QUIT :" quit) (0.01 ":tester!~u@rpaau95je67ci.irc QUIT :Quit: " quit)) ((drop 1 DROP)) diff --git a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld index 33e4168ac46..63366d3f576 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0.1 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy") (0.0 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") (0.0 ":irc.foonet.org 003 dummy :This server was created Fri, 24 Sep 2021 01:38:36 UTC") @@ -22,10 +22,10 @@ (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG dummy :back?") ) -((mode-user 1.2 "MODE dummy +i") +((mode-user 10 "MODE dummy +i") (0.0 ":irc.foonet.org 221 dummy +i") (0.0 ":irc.foonet.org NOTICE dummy :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) -((renick 42.6 "NICK tester") +((renick 10 "NICK tester") (0.01 ":dummy!~u@rpaau95je67ci.irc NICK tester") (0.0 ":NickServ!NickServ@localhost NOTICE dummy :You're now logged in as tester")) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld index 204d01fef77..596383c2699 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld @@ -38,4 +38,4 @@ (0.05 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: As he regards his aged father's life.") (0.05 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonor in doing it.")) -((linger 1 LINGER)) +((linger 2 LINGER)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld index 4445350ca0c..2e1a3ac27da 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld @@ -43,4 +43,4 @@ (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Orlando, my liege; the youngest son of Sir Rowland de Boys.") (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: The ape is dead, and I must conjure him.")) -((linger 1 LINGER)) +((linger 2 LINGER)) diff --git a/test/lisp/erc/resources/erc-d/erc-d-t.el b/test/lisp/erc/resources/erc-d/erc-d-t.el index 7b2adf4f07b..cf869fb3c70 100644 --- a/test/lisp/erc/resources/erc-d/erc-d-t.el +++ b/test/lisp/erc/resources/erc-d/erc-d-t.el @@ -83,6 +83,8 @@ erc-d-t-with-cleanup (ignore-errors (kill-buffer buf))))) (sleep-for erc-d-t-cleanup-sleep-secs))))) +(defvar erc-d-t--wait-message-prefix "Awaiting: ") + (defmacro erc-d-t-wait-for (max-secs msg &rest body) "Wait for BODY to become non-nil. Or signal error with MSG after MAX-SECS. When MAX-SECS is negative, @@ -99,7 +101,7 @@ erc-d-t-wait-for (let ((inverted (make-symbol "inverted")) (time-out (make-symbol "time-out")) (result (make-symbol "result"))) - `(ert-info ((concat "Awaiting: " ,msg)) + `(ert-info ((concat erc-d-t--wait-message-prefix ,msg)) (let ((,time-out (abs ,max-secs)) (,inverted (< ,max-secs 0)) (,result ',result)) @@ -120,7 +122,8 @@ erc-d-t-ensure-for (unless (or (stringp msg) (memq (car-safe msg) '(format concat))) (push msg body) (setq msg (prin1-to-string body))) - `(erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body)))) + `(let ((erc-d-t--wait-message-prefix "Sustaining: ")) + (erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body))))) (defun erc-d-t-search-for (timeout text &optional from on-success) "Wait for TEXT to appear in current buffer before TIMEOUT secs. diff --git a/test/lisp/erc/resources/erc-scenarios-common.el b/test/lisp/erc/resources/erc-scenarios-common.el index 972faa5c73f..b92acdd81e8 100644 --- a/test/lisp/erc/resources/erc-scenarios-common.el +++ b/test/lisp/erc/resources/erc-scenarios-common.el @@ -183,6 +183,103 @@ erc-scenarios-common-with-cleanup ,@body))) +(defvar erc-scenarios-common--term-size '(34 . 80)) +(declare-function term-char-mode "term" nil) +(declare-function term-line-mode "term" nil) + +(defun erc-scenarios-common--run-in-term (&optional debug) + (require 'term) + (let* ((default-directory (getenv "EMACS_TEST_DIRECTORY")) + (emacs (expand-file-name invocation-name invocation-directory)) + (process-environment (cons "HOME=/nonexistent" + (cons "ERC_TESTS_SUBPROCESS=1" + process-environment))) + (name (ert-test-name (ert-running-test))) + (temp-file (make-temp-file "erc-term-test-")) + (cmd `(let ((stats 1)) + (setq enable-dir-local-variables nil) + (unwind-protect + (setq stats (ert-run-tests-batch ',name)) + (unless ',debug + (let ((buf (with-current-buffer (messages-buffer) + (buffer-string)))) + (with-temp-file ,temp-file + (insert buf))) + (kill-emacs (ert-stats-completed-unexpected stats)))))) + ;; The `ert-test' object in Emacs 29 has a `file-name' field. + (file-name (symbol-file name 'ert--test)) + (default-directory (expand-file-name (file-name-directory file-name))) + (package (if-let* ((found (getenv "ERC_PACKAGE_NAME")) + ((string-prefix-p "erc-" found))) + (intern found) + 'erc)) + ;; This is for testing ERC's ELPA-package on older Emacsen we + ;; still support. It won't run inside the emacs.git tree. + (setup (and (featurep 'compat) + `(progn + (require 'package) + (let ((package-load-list '((compat t) (,package t)))) + (package-initialize))))) + ;; Make subprocess terminal bigger than controlling. + (buf (cl-letf (((symbol-function 'window-screen-lines) + (lambda () (car erc-scenarios-common--term-size))) + ((symbol-function 'window-max-chars-per-line) + (lambda () (cdr erc-scenarios-common--term-size)))) + (make-term (symbol-name name) emacs nil "-Q" "-nw" + "-eval" (prin1-to-string setup) + "-l" file-name "-eval" (format "%S" cmd)))) + (proc (get-buffer-process buf)) + (err (lambda () + (with-temp-buffer + (insert-file-contents temp-file) + (message "Subprocess: %s" (buffer-string)) + (delete-file temp-file))))) + (set-window-buffer (selected-window) buf) + (delete-other-windows) + (with-current-buffer buf + (set-process-query-on-exit-flag proc nil) + (unless noninteractive (term-char-mode)) + (with-timeout (30 (funcall err) (error "Timed out awaiting result")) + (while (process-live-p proc) + (accept-process-output proc 0.1) + (unless noninteractive + (redisplay)))) + (while (accept-process-output proc)) + (term-line-mode) + (goto-char (point-min)) + ;; Otherwise gives process exited abnormally with exit-code >0 + (unless (search-forward (format "Process %s finished" name) nil t) + (funcall err) + (ert-fail (when (search-forward "exited" nil t) + (buffer-substring-no-properties (line-beginning-position) + (line-end-position))))) + (delete-file temp-file) + (when noninteractive + (kill-buffer))))) + +(defvar erc-scenarios-common-interactive-debug-term-p nil + "When non-nil, run interactive ") + +(defmacro erc-scenarios-common-with-noninteractive-in-term (&rest body) + "Run BODY via `erc-scenarios-common-with-cleanup' in a `term' subprocess. +Also do so when `erc-scenarios-common-interactive-debug-term-p' +is non-nil. When debugging, leave the `term-mode' buffer around +for inspection and name it after the test, bounded by asterisks. +When debugging, ensure the test always fails, as a reminder to +disable `erc-scenarios-common-interactive-debug-term-p'. + +See Info node `(emacs) Term Mode' for the various commands." + (declare (indent 1)) + `(if (and (or erc-scenarios-common-interactive-debug-term-p + noninteractive) + (not (getenv "ERC_TESTS_SUBPROCESS"))) + (progn + (when (memq system-type '(windows-nt ms-dos)) + (ert-skip "System must be UNIX")) + (erc-scenarios-common--run-in-term + erc-scenarios-common-interactive-debug-term-p)) + (erc-scenarios-common-with-cleanup ,@body))) + (defun erc-scenarios-common-assert-initial-buf-name (id port) ;; Assert no limbo period when explicit ID given (should (string= (if id @@ -209,9 +306,108 @@ erc-scenarios-common-say (insert str) (erc-send-current-line))) +(defun erc-scenarios-common--at-win-end-p (&optional window) + (= (window-body-height window) + (count-screen-lines (window-start window) (point-max) nil window))) + +(defun erc-scenarios-common--above-win-end-p (&optional window) + (> (window-body-height window) + (count-screen-lines (window-start window) (point-max)))) + +(defun erc-scenarios-common--prompt-past-win-end-p (&optional window) + (< (window-body-height window) + (count-screen-lines (window-start window) (point-max)))) + +(defun erc-scenarios-common--recenter-top-bottom-around (orig &rest args) + (let (this-command last-command) (apply orig args))) + +(defun erc-scenarios-common--recenter-top-bottom () + (advice-add 'recenter-top-bottom + :around #'erc-scenarios-common--recenter-top-bottom-around) + (execute-kbd-macro "\C-l") + (advice-remove 'recenter-top-bottom + #'erc-scenarios-common--recenter-top-bottom-around)) + ;;;; Fixtures +(defun erc-scenarios-scrolltobottom--normal (test) + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "scrolltobottom") + (dumb-server (erc-d-run "localhost" t 'help)) + (port (process-contact dumb-server :service)) + (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer "foonet" + (should (looking-at " and")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below 15) + (recenter 0) + + (ert-info ("Moving into prompt in other window triggers scroll") + (with-selected-window (next-window) + (should-not (erc-scenarios-common--at-win-end-p)) + (goto-char (1- erc-insert-marker)) + (execute-kbd-macro "\C-n") + ;; Ensure point is at prompt and aligned to bottom. + (should (erc-scenarios-common--at-win-end-p)))) + + (ert-info ("Module `move-to-prompt' still works") + ;; Prompt is somewhere in the middle of the window. + (should (erc-scenarios-common--above-win-end-p)) + ;; Hitting a self-insert key triggers `move-to-prompt' as well + ;; as a scroll (to bottom). + (execute-kbd-macro "hi") + ;; Prompt and input appear on last line of window. + (should (erc-scenarios-common--at-win-end-p))) + + (ert-info ("Command `recenter-top-bottom' disallowed at prompt") + ;; Hitting C-l does not recenter the window. + (erc-scenarios-common--recenter-top-bottom) + (should (erc-scenarios-common--at-win-end-p)) + (erc-scenarios-common--recenter-top-bottom) + (should (erc-scenarios-common--at-win-end-p))) + + (ert-info ("Command `beginning-of-buffer' allowed at prompt") + ;; Hitting C-< goes to beginning of buffer. + (call-interactively #'beginning-of-buffer) + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New message doesn't trigger scroll when away from prompt") + ;; Arriving insertions don't trigger a scroll when away from the + ;; prompt. New output not seen. + (erc-cmd-MSG "NickServ help register") + (save-excursion (erc-d-t-search-for 10 "End of NickServ")) + (should (= 1 (point))) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (funcall test) + + (ert-info ("New message does trigger a scroll when at prompt") + ;; Recenter so prompt is above rather than at window's end. + (funcall expect 10 "If you are currently logged in") + (recenter 0) + ;; Prompt is somewhere in the middle of the window. + (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p)) + (erc-scenarios-common-say "/msg NickServ help identify") + ;; New arriving messages trigger a snap when inserted. + (erc-d-t-wait-for 10 (erc-scenarios-common--at-win-end-p)) + (funcall expect 10 "IDENTIFY lets you login"))))) + (cl-defun erc-scenarios-common--base-network-id-bouncer ((&key autop foo-id bar-id after &aux diff --git a/test/lisp/erc/resources/scrolltobottom/help.eld b/test/lisp/erc/resources/scrolltobottom/help.eld new file mode 100644 index 00000000000..ba44a0def39 --- /dev/null +++ b/test/lisp/erc/resources/scrolltobottom/help.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Mon, 21 Aug 2023 06:18:36 UTC") + (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.01 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.01 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.01 ":irc.foonet.org 254 tester 2 :channels formed") + (0.01 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.01 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0.01 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0.01 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0.02 ":irc.foonet.org 221 tester +i")) + +((privmsg-help-register 10 "PRIVMSG NickServ :help register") + (0.05 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2REGISTER [email]\2") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :REGISTER lets you register your current nickname as a user account. If the") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :server allows anonymous registration, you can omit the e-mail address.") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :If you are currently logged in with a TLS client certificate and wish to use") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :it instead of a password to log in, send * as the password.") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ HELP\2 ***")) + +((privmsg-help-identify 20 "PRIVMSG NickServ :help identify") + (0.06 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2IDENTIFY [password]\2") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :IDENTIFY lets you login to the given username using either password auth, or") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :certfp (your client certificate) if a password is not given.") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ HELP\2 ***")) + +((quit 10 "QUIT :\2ERC\2 ") + (0.07 ":tester!~u@26axz8nh8zaag.irc QUIT :Quit: \2ERC\2") + (0.02 "ERROR :Quit: \2ERC\2")) -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Consider-all-windows-in-erc-scrolltobottom-mode.patch >From 3ed5a748d76989ec2ed79311e3550da5c6d1aaa1 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sat, 22 Jul 2023 00:46:44 -0700 Subject: [PATCH 1/1] [5.6] Consider all windows in erc-scrolltobottom-mode * etc/ERC-NEWS: Add entry for option `erc-scrolltobottom-all'. Also mention explicit hook-depth intervals reserved by ERC. * lisp/erc/erc-backend.el (erc--hide-prompt): Change hook depth on `pre-command-hook' from 91 to 80. * lisp/erc/erc-goodies.el (erc-input-line-position): Mention secondary role when new option `erc-scroll-to-bottom-relaxed' is non-nil. (erc-scrolltobottom-all): New option that decides whether module `scrolltobottom' affects all windows or just the selected one, as it always has. (erc-scrolltobottom-relaxed): New option to leave the prompt stationary instead of forcing it to the bottom of the window. (erc-scrolltobottom-mode, erc-scrolltobottom-enable, erc-scrolltobottom-disable): Use `erc--scrolltobottom-setup' instead of `erc-add-scroll-to-bottom' for adding and removing local hooks, instead of ranging over buffers when removing them. Also add and remove new hook members when `erc-scrolltobottom-all' is non-nil. (erc--scrolltobottom-relaxed-commands, erc--scrolltobottom-window-info, erc--scrolltobottom-post-force-commands, erc--scrolltobottom-relaxed-skip-commands): New internal variables. (erc--scrolltobottom-on-pre-command erc--scrolltobottom-on-post-command): New functions resembling `erc-possibly-scroll-to-bottom' that try to avoid scrolling repeatedly for no reason. (erc--scrolltobottom-on-pre-command-relaxed, erc--scrolltobottom-on-post-command-relaxed): New commands to help implement `erc-scroll-to-bottom-relaxed'. (erc--scrolltobottom-away-from-prompt): New function to scroll to bottom on window configuration changes. (erc--scrolltobottom-all): New function to scroll all windows displaying the current buffer. (erc-add-scroll-to-bottom): Deprecate this function because it is now unused. (erc--scrolltobottom-setup): New generic function to perform teardown as well as setup, depending on module mode var. Add scrolling on `window-configuration-changed-hook' when `erc-scrolltobottom-all' is non-nil and `erc-scrolltobottom-relaxed' is nil. (erc--scrolltobottom-on-pre-insert): New generic function that remembers the last `window-start' and maybe the current screen line before inserting a message, in order to restore it afterward. (erc--scrolltobottom-confirm): New function, a replacement for `erc-scroll-to-bottom', that returns non-nil when it's actually recentered the window. (erc-move-to-prompt-setup): Add `erc-move-to-prompt' to `pre-command-hook' at a depth of 70. (erc-keep-place-mode, erc-keep-place-enable): Change hook depth from 0 to 85. (erc--keep-place-indicator-setup): Add overlay arrow `after-string' in non-graphical settings in case users have time stamps or other content occupying the left margin. (erc-keep-place-indicator-mode, erc-keep-place-indicator-enable): Change hook depth from 90 to 85 so as not to conflict with t. (erc--keep-place-indicator-on-global-module): Change hook depth from 90 to 85. * test/lisp/erc/erc-scenarios-scrolltobottom.el: New file. * test/lisp/erc/erc-scenarios-scrolltobottom-all.el: New file. * test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el: New file. * test/lisp/erc/resources/base/assoc/bumped/again.eld: Adjust timeouts. * test/lisp/erc/resources/base/assoc/bumped/foisted.eld: Adjust timeouts. * test/lisp/erc/resources/base/assoc/bumped/refoisted.eld: Adjust timeouts. * test/lisp/erc/resources/base/netid/bouncer/barnet.eld: Adjust timeouts. * test/lisp/erc/resources/base/netid/bouncer/foonet.eld: Adjust timeouts. * test/lisp/erc/resources/erc-d/erc-d-t.el (erc-d-t--wait-message-prefix, erc-d-t-wait-for, erc-d-t-ensure-for): Add and use new variable to make `ert-info' message prefix adjustable. * test/lisp/erc/resources/erc-scenarios-common.el (erc-scenarios-common--term-size, erc-scenarios-common--run-in-term, erc-scenarios-common-interactive-debug-term-p, erc-scenarios-common-with-noninteractive-in-term): New test macro and supporting helper function and variables to facilitate running scenario-based tests in a subprocess. (erc-scenarios-common--at-win-end-p, erc-scenarios-common--above-win-end-p, erc-scenarios-common--prompt-past-win-end-p, erc-scenarios-common--recenter-top-bottom-around, erc-scenarios-common--recenter-top-bottom, erc-scenarios-scrolltobottom--normal): New test fixture and assertion helper functions. * test/lisp/erc/resources/scrolltobottom/help.eld: New file. (Bug#64855) --- etc/ERC-NEWS | 19 +- lisp/erc/erc-backend.el | 2 +- lisp/erc/erc-goodies.el | 267 ++++++++++++++++-- .../erc/erc-scenarios-scrolltobottom-all.el | 48 ++++ .../erc-scenarios-scrolltobottom-relaxed.el | 140 +++++++++ test/lisp/erc/erc-scenarios-scrolltobottom.el | 44 +++ .../erc/resources/base/assoc/bumped/again.eld | 10 +- .../resources/base/assoc/bumped/foisted.eld | 10 +- .../resources/base/assoc/bumped/refoisted.eld | 8 +- .../resources/base/netid/bouncer/barnet.eld | 2 +- .../resources/base/netid/bouncer/foonet.eld | 2 +- test/lisp/erc/resources/erc-d/erc-d-t.el | 7 +- .../erc/resources/erc-scenarios-common.el | 196 +++++++++++++ .../erc/resources/scrolltobottom/help.eld | 46 +++ 14 files changed, 761 insertions(+), 40 deletions(-) create mode 100644 test/lisp/erc/erc-scenarios-scrolltobottom-all.el create mode 100644 test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el create mode 100644 test/lisp/erc/erc-scenarios-scrolltobottom.el create mode 100644 test/lisp/erc/resources/scrolltobottom/help.eld diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index 94452514e6d..72b768b02ec 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -178,6 +178,15 @@ been restored with a slightly revised role contingent on a few assumptions explained in its doc string. For clarity, it has been renamed 'erc-ensure-target-buffer-on-privmsg'. +** Module 'scrolltobottom' attempts to behave more sensibly. +Enabling the experimental option 'erc-scrolltobottom-all' tells +'scrolltobottom' to be more vigilant about recentering and to do so in +all ERC windows. The dependent option 'erc-scrolltobottom-relaxed', +likewise experimental, makes ERC's prompt stationary wherever it +resides instead of forcing it to the bottom of the window. That is, +new input appears above the prompt, scrolling existing messages upward +to compensate. + ** Subtle changes in two fundamental faces. Users of the default theme may notice that 'erc-action-face' and 'erc-notice-face' now appear slightly less bold on systems supporting @@ -232,9 +241,9 @@ property of the same name has been retained and now has a value of Built-in and third-party modules rely on certain hooks for adjusting incoming and outgoing messages upon insertion. And some modules only want to do so after others have done their damage. Traditionally, -this required various hacks and finagling to achieve. And while this -release makes an effort to load modules in a more consistent order, -that alone isn't enough to ensure similar predictability among +this has required various hacks and finagling to achieve. And while +this release makes an effort to load modules in a more consistent +order, that alone isn't enough to ensure similar predictability among essential members of important hooks. Luckily, ERC now leverages a feature introduced in Emacs 27, "hook @@ -248,6 +257,10 @@ the first two, 'erc-button-add-buttons' and 'erc-fill', which have been swapped with respect to their previous places in recent ERC versions. +The same depth interval is now also provisionally reserved for +'erc-insert-pre-hook' and for the non-ERC hooks 'pre-command-hook' and +'post-command-hook', but only locally, in ERC buffers. + *** ERC now manages timestamp-related properties a bit differently. For starters, the 'cursor-sensor-functions' property no longer contains unique closures and thus no longer proves effective for diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index eb3ec39fedd..9e121ec1e92 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -1089,7 +1089,7 @@ erc--hide-prompt (put-text-property erc-insert-marker (1- erc-input-marker) 'erc-prompt 'hidden) (erc--conceal-prompt)) - (add-hook 'pre-command-hook #'erc--unhide-prompt-on-self-insert 91 t)))) + (add-hook 'pre-command-hook #'erc--unhide-prompt-on-self-insert 80 t)))) (defun erc-process-sentinel (cproc event) "Sentinel function for ERC process." diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index b37855cbecc..07a93b06f2c 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -44,42 +44,270 @@ erc-input-line-position This should be an integer specifying the line of the buffer on which the input line should stay. A value of \"-1\" would keep the input line positioned on the last line in the buffer. This is passed as an -argument to `recenter'." +argument to `recenter', unless `erc-scrolltobottom-relaxed' is +non-nil, in which case, ERC interprets it as additional lines to +scroll down by per message insertion (minus one for the prompt)." :group 'erc-display :type '(choice integer (const nil))) +(defcustom erc-scrolltobottom-all nil + "Whether to scroll all windows or just the selected one. +A value of nil preserves pre-5.6 behavior, in which scrolling +only affects the selected window. Users should consider its +non-nil behavior experimental for the time being. Note also that +ERC expects this option to be configured before module +initialization." + :group 'erc-display + :package-version '(ERC . "5.6") ; FIXME sync on release + :type 'boolean) + +(defcustom erc-scrolltobottom-relaxed nil + "Whether to forgo forcing prompt to the bottom of the window. +When non-nil, and point is at the prompt, ERC scrolls the window +up when inserting messages, making the prompt appear stationary. +Users who find this effect too \"stagnant\" can adjust the option +`erc-input-line-position', which ERC borrows to express a scroll +step offset when this option is non-nil. Setting that value to +zero lets the prompt drift toward the bottom by one line per +message, which is generally slow enough not to distract while +composing input. Of course, this doesn't apply when receiving a +large influx of messages, such as after typing \"/msg NickServ +help\". Note that ERC only considers this option when the +experimental companion option `erc-scrolltobottom-all' is enabled +and, only then, during module setup." + :group 'erc-display + :package-version '(ERC . "5.6") ; FIXME sync on release + :type 'boolean) + ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t) (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) - (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))))) + ((add-hook 'erc-mode-hook #'erc--scrolltobottom-setup) + (unless erc--updating-modules-p (erc-buffer-do #'erc--scrolltobottom-setup)) + (if erc-scrolltobottom-all + (progn + (add-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert 25) + (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-all)) + (add-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom))) + ((remove-hook 'erc-mode-hook #'erc--scrolltobottom-setup) + (erc-buffer-do #'erc--scrolltobottom-setup) + (if erc-scrolltobottom-all + (progn + (remove-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-pre-send-functions + #'erc--scrolltobottom-on-pre-insert)) + (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)))) (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))) +(defvar-local erc--scrolltobottom-relaxed-commands '(end-of-buffer) + "Commands triggering a force scroll to prompt. +Only applies with `erc-scrolltobottom-relaxed' when away from prompt.") + +(defvar-local erc--scrolltobottom-window-info nil + "Alist with windows as keys and lists of window-related info as values. +Values are lists containing the last window start position and +the last \"window line\" of point. The \"window line\", which +may be nil, is the number of lines between `window-start' and +`window-point', inclusive.") + +(defvar erc--scrolltobottom-post-force-commands + '(beginning-of-buffer + electric-newline-and-maybe-indent + default-indent-new-line) + "Commands that force a scroll after execution at prompt. +That is, ERC recalculates the window's start instead of blindly +restoring it.") + +(defvar erc--scrolltobottom-relaxed-skip-commands + '(recenter-top-bottom scroll-down-command)) + +(defun erc--scrolltobottom-on-pre-command () + (when (and (eq (selected-window) (get-buffer-window)) + (>= (point) erc-input-marker)) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point-max))))))) + +(defun erc--scrolltobottom-on-post-command () + "Restore window start or scroll to prompt and recenter. +When `erc--scrolltobottom-window-info' is non-nil and its first +item is associated with the selected window, restore start of +window so long as prompt hasn't moved. Expect buffer to be +unnarrowed." + (when (eq (selected-window) (get-buffer-window)) + (if-let (((not (input-pending-p))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window))) + ((not (memq this-command + erc--scrolltobottom-post-force-commands))) + ((= (nth 2 found) + (count-screen-lines (window-start) (point-max))))) + (set-window-start (selected-window) (nth 1 found)) + (erc--scrolltobottom-confirm)) + (setq erc--scrolltobottom-window-info nil))) + +(defun erc--scrolltobottom-on-pre-command-relaxed () + "Maybe scroll to bottom when away from prompt in an unnarrowed buffer. +When `erc-scrolltobottom-relaxed' is active, only scroll when +prompt is past window's end and the command is `end-of-buffer' or +`self-insert-command' (assuming `move-to-prompt' is active). +When at prompt and current command does not appear in +`erc--scrolltobottom-relaxed-skip-commands', stash +`erc--scrolltobottom-window-info' for the selected window." + (when (eq (selected-window) (get-buffer-window)) + (when (and (not (input-pending-p)) + (< (point) erc-input-marker) + (memq this-command erc--scrolltobottom-relaxed-commands) + (< (window-end nil t) erc-input-marker)) + (save-excursion + (goto-char (point-max)) + (recenter (or erc-input-line-position -1)))) + (when (and (>= (point) erc-input-marker) + (not (memq this-command + erc--scrolltobottom-relaxed-skip-commands))) + (setq erc--scrolltobottom-window-info + (list (list (selected-window) + (window-start) + (count-screen-lines (window-start) (point-max)))))))) + +(defun erc--scrolltobottom-on-post-command-relaxed () + "Set window start or scroll when data was captured on pre-command." + (when-let (((eq (selected-window) (get-buffer-window))) + (erc--scrolltobottom-window-info) + (found (car erc--scrolltobottom-window-info)) + ((eq (car found) (selected-window)))) + (if (and (not (memq this-command erc--scrolltobottom-post-force-commands)) + (= (nth 2 found) + (count-screen-lines (window-start) (point-max)))) + (set-window-start (selected-window) (nth 1 found)) + (recenter (nth 2 found))) + (setq erc--scrolltobottom-window-info nil))) + +;; FIXME this is currently of little value because it doesn't restore +;; the relative position of window point after changing dimensions. +;; It would be preferable to instead stash the previous ratio of +;; window line to body height and later recenter proportionally. But +;; that may not be worth the added bookkeeping. +(defun erc--scrolltobottom-away-from-prompt () + "Scroll to bottom unless at prompt." + (unless (or (input-pending-p) + (minibuffer-window-active-p (minibuffer-window)) + (eq (old-selected-window) (minibuffer-window))) + (erc--scrolltobottom-confirm))) + +(defun erc--scrolltobottom-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 + ((erc--scrolltobottom-window-info) + (found (assq window erc--scrolltobottom-window-info)) + ((not (erc--scrolltobottom-confirm (nth 2 found))))) + (setf (window-start window) (cadr found))))) + ;; Necessary unless we're sure `erc--scrolltobottom-on-pre-insert' + ;; always runs between calls to this function. + (setq erc--scrolltobottom-window-info nil)) + (defun erc-add-scroll-to-bottom () "A hook function for `erc-mode-hook' to recenter output at bottom of window. 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'." +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." + (declare (obsolete erc--scrolltobottom-setup "30.1")) (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t)) +(cl-defgeneric erc--scrolltobottom-setup () + "Arrange for scrolling to bottom on window configuration changes. +Undo that arrangement when disabling `erc-scrolltobottom-mode'." + (if erc-scrolltobottom-mode + (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t) + (remove-hook 'post-command-hook #'erc-scroll-to-bottom t))) + +(cl-defmethod erc--scrolltobottom-setup (&context + (erc-scrolltobottom-all (eql t))) + (if erc-scrolltobottom-mode + (if erc-scrolltobottom-relaxed + (progn + (when (or (bound-and-true-p erc-move-to-prompt-mode) + (memq 'move-to-prompt erc-modules)) + (cl-pushnew 'self-insert-command + erc--scrolltobottom-relaxed-commands)) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed 60 t) + (add-hook 'pre-command-hook ; preempt `move-to-prompt' + #'erc--scrolltobottom-on-pre-command-relaxed 60 t)) + (add-hook 'window-configuration-change-hook + #'erc--scrolltobottom-away-from-prompt nil t) + (add-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command 60 t) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command 60 t)) + (remove-hook 'window-configuration-change-hook + #'erc--scrolltobottom-away-from-prompt t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command t) + (remove-hook 'pre-command-hook + #'erc--scrolltobottom-on-pre-command-relaxed t) + (remove-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command-relaxed t) + (kill-local-variable 'erc--scrolltobottom-relaxed-commands) + (kill-local-variable 'erc--scrolltobottom-window-info))) + +(cl-defmethod erc--scrolltobottom-on-pre-insert (_input-or-string) + "Remember the `window-start' before inserting a message." + (setq erc--scrolltobottom-window-info + (mapcar (lambda (w) + (list w + (window-start w) + (and-let* + ((erc-scrolltobottom-relaxed) + (c (count-screen-lines (window-start w) + (point-max) nil w))) + (if (= ?\n (char-before (point-max))) (1+ c) c)))) + (get-buffer-window-list nil nil 'visible)))) + +(cl-defmethod erc--scrolltobottom-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--scrolltobottom-confirm (&optional scroll-to) + "Like `erc-scroll-to-bottom', but use `window-point'. +Position current line (with `recenter') SCROLL-TO lines below +window's top. Return nil if point is not in prompt area or if +prompt isn't ready." + (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 scroll-to 0) (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 @@ -135,13 +363,13 @@ erc-move-to-prompt (defun erc-move-to-prompt-setup () "Initialize the move-to-prompt module." - (add-hook 'pre-command-hook #'erc-move-to-prompt nil t)) + (add-hook 'pre-command-hook #'erc-move-to-prompt 70 t)) ;;; Keep place in unvisited channels ;;;###autoload(autoload 'erc-keep-place-mode "erc-goodies" nil t) (define-erc-module keep-place nil "Leave point above un-viewed text in other channels." - ((add-hook 'erc-insert-pre-hook #'erc-keep-place)) + ((add-hook 'erc-insert-pre-hook #'erc-keep-place 85)) ((remove-hook 'erc-insert-pre-hook #'erc-keep-place))) (defcustom erc-keep-place-indicator-style t @@ -213,12 +441,15 @@ erc--keep-place-indicator-setup (add-hook 'window-configuration-change-hook #'erc--keep-place-indicator-on-window-configuration-change nil t) (when-let* (((memq erc-keep-place-indicator-style '(t arrow))) + (ov-property (if (zerop (fringe-columns 'left)) + 'after-string + 'before-string)) (display (if (zerop (fringe-columns 'left)) `((margin left-margin) ,overlay-arrow-string) '(left-fringe right-triangle erc-keep-place-indicator-arrow))) (bef (propertize " " 'display display))) - (overlay-put erc--keep-place-indicator-overlay 'before-string bef)) + (overlay-put erc--keep-place-indicator-overlay ov-property bef)) (when (memq erc-keep-place-indicator-style '(t face)) (overlay-put erc--keep-place-indicator-overlay 'face 'erc-keep-place-indicator-line))) @@ -233,7 +464,7 @@ keep-place-indicator ((memq 'keep-place erc-modules) (erc-keep-place-mode +1)) ;; Enable a local version of `keep-place-mode'. - (t (add-hook 'erc-insert-pre-hook #'erc-keep-place 90 t))) + (t (add-hook 'erc-insert-pre-hook #'erc-keep-place 85 t))) (if (pcase erc-keep-place-indicator-buffer-type ('target erc--target) ('server (not erc--target)) @@ -256,7 +487,7 @@ erc--keep-place-indicator-on-global-module global one." (if erc-keep-place-mode (remove-hook 'erc-insert-pre-hook #'erc-keep-place t) - (add-hook 'erc-insert-pre-hook #'erc-keep-place 90 t))) + (add-hook 'erc-insert-pre-hook #'erc-keep-place 85 t))) (defun erc-keep-place-move (pos) "Move keep-place indicator to current line or POS. diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-all.el b/test/lisp/erc/erc-scenarios-scrolltobottom-all.el new file mode 100644 index 00000000000..33f232e64d9 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom-all.el @@ -0,0 +1,48 @@ +;;; erc-scenarios-scrolltobottom-all.el --- erc-scrolltobottom-all test -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--all () + :tags '(:expensive-test) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (let ((erc-scrolltobottom-all t)) + + (erc-scenarios-scrolltobottom--normal + (lambda () + (ert-info ("New insertion anchors prompt in other window") + (let ((w (next-window))) + ;; We're at prompt and aligned to bottom. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (erc-scenarios-common--at-win-end-p w)) + (erc-d-t-ensure-for 0.5 + (erc-scenarios-common--at-win-end-p w)))))))) + +;;; erc-scenarios-scrolltobottom-all.el ends here diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el new file mode 100644 index 00000000000..7d256bf711b --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el @@ -0,0 +1,140 @@ +;;; erc-scenarios-scrolltobottom-relaxed.el --- erc-scrolltobottom-relaxed -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;; TODO assert behavior of prompt input spanning multiple lines, with +;; and without line endings. + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--relaxed () + :tags '(:expensive-test) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "scrolltobottom") + (dumb-server (erc-d-run "localhost" t 'help)) + (port (process-contact dumb-server :service)) + (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) + (erc-scrolltobottom-all t) + (erc-scrolltobottom-relaxed t) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter)) + lower upper) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer "foonet" + (should (looking-at " and")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below 15) + (recenter 0) + + (ert-info ("Moving into prompt does not trigger scroll") + (with-selected-window (next-window) + (should-not (erc-scenarios-common--at-win-end-p)) + (recenter 0) + (goto-char (1- erc-insert-marker)) + (execute-kbd-macro "\C-n") + (should-not (erc-scenarios-common--at-win-end-p)) + (should (= (point) (point-max))) + (setq lower (count-screen-lines (window-start) (window-point))))) + + (ert-info ("Module `move-to-prompt' still works") + ;; Prompt is somewhere in the middle of the window. + (should (erc-scenarios-common--above-win-end-p)) + (should-not (= (point-max) (point))) + ;; Hitting a self-insert key triggers `move-to-prompt' but not + ;; a scroll (to bottom). + (execute-kbd-macro "hi") + ;; Prompt and input appear on same line. + (should (= (point-max) (point))) + (setq upper (count-screen-lines (window-start) (window-point))) + (should-not (= upper (window-body-height)))) + + (ert-info ("Command `recenter-top-bottom' allowed at prompt") + ;; Hitting C-l recenters the window. + (should (= upper (count-screen-lines (window-start) (window-point)))) + (let ((lines (list upper))) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (setq lines (delete-dups lines)) + (should (= (length lines) 4)))) + + (ert-info ("Command `beginning-of-buffer' allowed at prompt") + ;; Hitting C-< goes to beginning of buffer. + (execute-kbd-macro "\M-<") + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New message doesn't trigger scroll when away from prompt") + ;; Arriving insertions don't trigger a scroll when away from the + ;; prompt. New output not seen. + (erc-cmd-MSG "NickServ help register") + (save-excursion (erc-d-t-search-for 10 "End of NickServ")) + (should (= 1 (point))) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New insertion keeps prompt stationary in other window") + (let ((w (next-window))) + ;; We're at prompt and completely stationary. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (= lower (count-screen-lines (window-start w) (window-point w)))) + (erc-d-t-ensure-for 0.5 + (= lower (count-screen-lines (window-start w) + (window-point w)))))) + + (should (= 2 (length (window-list)))) + (ert-info ("New message does not trigger a scroll when at prompt") + ;; Recenter so prompt is above rather than at window's end. + (funcall expect 10 "End of NickServ HELP") + (recenter 0) + (set-window-point nil (point-max)) + (setq upper (count-screen-lines (window-start) (window-point))) + ;; Prompt is somewhere in the middle of the window. + (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p)) + (erc-scenarios-common-say "/msg NickServ help identify") + ;; New arriving messages don't move prompt. + (erc-d-t-ensure-for 1 + (= upper (count-screen-lines (window-start) (window-point)))) + (funcall expect 10 "IDENTIFY lets you login"))))) + +;;; erc-scenarios-scrolltobottom-relaxed.el ends here diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom.el b/test/lisp/erc/erc-scenarios-scrolltobottom.el new file mode 100644 index 00000000000..44e64204fb1 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom.el @@ -0,0 +1,44 @@ +;;; erc-scenarios-scrolltobottom.el --- erc-scrolltobottom-mode -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--normal () + :tags '(:expensive-test) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (erc-scenarios-scrolltobottom--normal + (lambda () + (ert-info ("New insertion doesn't anchor prompt in other window") + (let ((w (next-window))) + ;; We're at prompt but not aligned to bottom. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (not (erc-scenarios-common--at-win-end-p w)))))))) + +;;; erc-scenarios-scrolltobottom.el ends here diff --git a/test/lisp/erc/resources/base/assoc/bumped/again.eld b/test/lisp/erc/resources/base/assoc/bumped/again.eld index ab3c7b06214..aef164b6237 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/again.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/again.eld @@ -1,10 +1,10 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0.0 ":irc.foonet.org 433 * tester :Nickname is reserved by a different account") (0.0 ":irc.foonet.org FAIL NICK NICKNAME_RESERVED tester :Nickname is reserved by a different account")) -((nick 3 "NICK tester`") +((nick 10 "NICK tester`") (0.1 ":irc.foonet.org 001 tester` :Welcome to the foonet IRC Network tester`") (0.0 ":irc.foonet.org 002 tester` :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") (0.0 ":irc.foonet.org 003 tester` :This server was created Fri, 24 Sep 2021 01:38:36 UTC") @@ -21,10 +21,10 @@ (0.2 ":irc.foonet.org 266 tester` 3 3 :Current global users 3, max 3") (0.0 ":irc.foonet.org 422 tester` :MOTD File is missing")) -((mode-user 3.2 "MODE tester` +i") +((mode-user 10 "MODE tester` +i") (0.0 ":irc.foonet.org 221 tester` +i") (0.0 ":irc.foonet.org NOTICE tester` :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) -((privmsg 42.6 "PRIVMSG NickServ :IDENTIFY tester changeme") +((privmsg 10 "PRIVMSG NickServ :IDENTIFY tester changeme") (0.01 ":tester`!~u@rpaau95je67ci.irc NICK tester") (0.0 ":NickServ!NickServ@localhost NOTICE tester :You're now logged in as tester")) diff --git a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld index 5c36e58d9d3..0f7aadac564 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") (0.0 ":irc.foonet.org 003 tester :This server was created Fri, 24 Sep 2021 01:38:36 UTC") @@ -17,14 +17,14 @@ (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") (0.0 ":irc.foonet.org 422 tester :MOTD File is missing")) -((mode-user 1.2 "MODE tester +i") +((mode-user 10 "MODE tester +i") (0.0 ":irc.foonet.org 221 tester +i") (0.0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) -((privmsg 17.21 "PRIVMSG bob :hi") +((privmsg 10 "PRIVMSG bob :hi") (0.02 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :hola") (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :how r u?")) -((quit 18.19 "QUIT :" quit) +((quit 10 "QUIT :" quit) (0.01 ":tester!~u@rpaau95je67ci.irc QUIT :Quit: " quit)) ((drop 1 DROP)) diff --git a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld index 33e4168ac46..63366d3f576 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0.1 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy") (0.0 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") (0.0 ":irc.foonet.org 003 dummy :This server was created Fri, 24 Sep 2021 01:38:36 UTC") @@ -22,10 +22,10 @@ (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG dummy :back?") ) -((mode-user 1.2 "MODE dummy +i") +((mode-user 10 "MODE dummy +i") (0.0 ":irc.foonet.org 221 dummy +i") (0.0 ":irc.foonet.org NOTICE dummy :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) -((renick 42.6 "NICK tester") +((renick 10 "NICK tester") (0.01 ":dummy!~u@rpaau95je67ci.irc NICK tester") (0.0 ":NickServ!NickServ@localhost NOTICE dummy :You're now logged in as tester")) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld index 204d01fef77..596383c2699 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld @@ -38,4 +38,4 @@ (0.05 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: As he regards his aged father's life.") (0.05 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonor in doing it.")) -((linger 1 LINGER)) +((linger 2 LINGER)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld index 4445350ca0c..2e1a3ac27da 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld @@ -43,4 +43,4 @@ (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Orlando, my liege; the youngest son of Sir Rowland de Boys.") (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: The ape is dead, and I must conjure him.")) -((linger 1 LINGER)) +((linger 2 LINGER)) diff --git a/test/lisp/erc/resources/erc-d/erc-d-t.el b/test/lisp/erc/resources/erc-d/erc-d-t.el index 7b2adf4f07b..cf869fb3c70 100644 --- a/test/lisp/erc/resources/erc-d/erc-d-t.el +++ b/test/lisp/erc/resources/erc-d/erc-d-t.el @@ -83,6 +83,8 @@ erc-d-t-with-cleanup (ignore-errors (kill-buffer buf))))) (sleep-for erc-d-t-cleanup-sleep-secs))))) +(defvar erc-d-t--wait-message-prefix "Awaiting: ") + (defmacro erc-d-t-wait-for (max-secs msg &rest body) "Wait for BODY to become non-nil. Or signal error with MSG after MAX-SECS. When MAX-SECS is negative, @@ -99,7 +101,7 @@ erc-d-t-wait-for (let ((inverted (make-symbol "inverted")) (time-out (make-symbol "time-out")) (result (make-symbol "result"))) - `(ert-info ((concat "Awaiting: " ,msg)) + `(ert-info ((concat erc-d-t--wait-message-prefix ,msg)) (let ((,time-out (abs ,max-secs)) (,inverted (< ,max-secs 0)) (,result ',result)) @@ -120,7 +122,8 @@ erc-d-t-ensure-for (unless (or (stringp msg) (memq (car-safe msg) '(format concat))) (push msg body) (setq msg (prin1-to-string body))) - `(erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body)))) + `(let ((erc-d-t--wait-message-prefix "Sustaining: ")) + (erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body))))) (defun erc-d-t-search-for (timeout text &optional from on-success) "Wait for TEXT to appear in current buffer before TIMEOUT secs. diff --git a/test/lisp/erc/resources/erc-scenarios-common.el b/test/lisp/erc/resources/erc-scenarios-common.el index 972faa5c73f..b92acdd81e8 100644 --- a/test/lisp/erc/resources/erc-scenarios-common.el +++ b/test/lisp/erc/resources/erc-scenarios-common.el @@ -183,6 +183,103 @@ erc-scenarios-common-with-cleanup ,@body))) +(defvar erc-scenarios-common--term-size '(34 . 80)) +(declare-function term-char-mode "term" nil) +(declare-function term-line-mode "term" nil) + +(defun erc-scenarios-common--run-in-term (&optional debug) + (require 'term) + (let* ((default-directory (getenv "EMACS_TEST_DIRECTORY")) + (emacs (expand-file-name invocation-name invocation-directory)) + (process-environment (cons "HOME=/nonexistent" + (cons "ERC_TESTS_SUBPROCESS=1" + process-environment))) + (name (ert-test-name (ert-running-test))) + (temp-file (make-temp-file "erc-term-test-")) + (cmd `(let ((stats 1)) + (setq enable-dir-local-variables nil) + (unwind-protect + (setq stats (ert-run-tests-batch ',name)) + (unless ',debug + (let ((buf (with-current-buffer (messages-buffer) + (buffer-string)))) + (with-temp-file ,temp-file + (insert buf))) + (kill-emacs (ert-stats-completed-unexpected stats)))))) + ;; The `ert-test' object in Emacs 29 has a `file-name' field. + (file-name (symbol-file name 'ert--test)) + (default-directory (expand-file-name (file-name-directory file-name))) + (package (if-let* ((found (getenv "ERC_PACKAGE_NAME")) + ((string-prefix-p "erc-" found))) + (intern found) + 'erc)) + ;; This is for testing ERC's ELPA-package on older Emacsen we + ;; still support. It won't run inside the emacs.git tree. + (setup (and (featurep 'compat) + `(progn + (require 'package) + (let ((package-load-list '((compat t) (,package t)))) + (package-initialize))))) + ;; Make subprocess terminal bigger than controlling. + (buf (cl-letf (((symbol-function 'window-screen-lines) + (lambda () (car erc-scenarios-common--term-size))) + ((symbol-function 'window-max-chars-per-line) + (lambda () (cdr erc-scenarios-common--term-size)))) + (make-term (symbol-name name) emacs nil "-Q" "-nw" + "-eval" (prin1-to-string setup) + "-l" file-name "-eval" (format "%S" cmd)))) + (proc (get-buffer-process buf)) + (err (lambda () + (with-temp-buffer + (insert-file-contents temp-file) + (message "Subprocess: %s" (buffer-string)) + (delete-file temp-file))))) + (set-window-buffer (selected-window) buf) + (delete-other-windows) + (with-current-buffer buf + (set-process-query-on-exit-flag proc nil) + (unless noninteractive (term-char-mode)) + (with-timeout (30 (funcall err) (error "Timed out awaiting result")) + (while (process-live-p proc) + (accept-process-output proc 0.1) + (unless noninteractive + (redisplay)))) + (while (accept-process-output proc)) + (term-line-mode) + (goto-char (point-min)) + ;; Otherwise gives process exited abnormally with exit-code >0 + (unless (search-forward (format "Process %s finished" name) nil t) + (funcall err) + (ert-fail (when (search-forward "exited" nil t) + (buffer-substring-no-properties (line-beginning-position) + (line-end-position))))) + (delete-file temp-file) + (when noninteractive + (kill-buffer))))) + +(defvar erc-scenarios-common-interactive-debug-term-p nil + "When non-nil, run interactive ") + +(defmacro erc-scenarios-common-with-noninteractive-in-term (&rest body) + "Run BODY via `erc-scenarios-common-with-cleanup' in a `term' subprocess. +Also do so when `erc-scenarios-common-interactive-debug-term-p' +is non-nil. When debugging, leave the `term-mode' buffer around +for inspection and name it after the test, bounded by asterisks. +When debugging, ensure the test always fails, as a reminder to +disable `erc-scenarios-common-interactive-debug-term-p'. + +See Info node `(emacs) Term Mode' for the various commands." + (declare (indent 1)) + `(if (and (or erc-scenarios-common-interactive-debug-term-p + noninteractive) + (not (getenv "ERC_TESTS_SUBPROCESS"))) + (progn + (when (memq system-type '(windows-nt ms-dos)) + (ert-skip "System must be UNIX")) + (erc-scenarios-common--run-in-term + erc-scenarios-common-interactive-debug-term-p)) + (erc-scenarios-common-with-cleanup ,@body))) + (defun erc-scenarios-common-assert-initial-buf-name (id port) ;; Assert no limbo period when explicit ID given (should (string= (if id @@ -209,9 +306,108 @@ erc-scenarios-common-say (insert str) (erc-send-current-line))) +(defun erc-scenarios-common--at-win-end-p (&optional window) + (= (window-body-height window) + (count-screen-lines (window-start window) (point-max) nil window))) + +(defun erc-scenarios-common--above-win-end-p (&optional window) + (> (window-body-height window) + (count-screen-lines (window-start window) (point-max)))) + +(defun erc-scenarios-common--prompt-past-win-end-p (&optional window) + (< (window-body-height window) + (count-screen-lines (window-start window) (point-max)))) + +(defun erc-scenarios-common--recenter-top-bottom-around (orig &rest args) + (let (this-command last-command) (apply orig args))) + +(defun erc-scenarios-common--recenter-top-bottom () + (advice-add 'recenter-top-bottom + :around #'erc-scenarios-common--recenter-top-bottom-around) + (execute-kbd-macro "\C-l") + (advice-remove 'recenter-top-bottom + #'erc-scenarios-common--recenter-top-bottom-around)) + ;;;; Fixtures +(defun erc-scenarios-scrolltobottom--normal (test) + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "scrolltobottom") + (dumb-server (erc-d-run "localhost" t 'help)) + (port (process-contact dumb-server :service)) + (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer "foonet" + (should (looking-at " and")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below 15) + (recenter 0) + + (ert-info ("Moving into prompt in other window triggers scroll") + (with-selected-window (next-window) + (should-not (erc-scenarios-common--at-win-end-p)) + (goto-char (1- erc-insert-marker)) + (execute-kbd-macro "\C-n") + ;; Ensure point is at prompt and aligned to bottom. + (should (erc-scenarios-common--at-win-end-p)))) + + (ert-info ("Module `move-to-prompt' still works") + ;; Prompt is somewhere in the middle of the window. + (should (erc-scenarios-common--above-win-end-p)) + ;; Hitting a self-insert key triggers `move-to-prompt' as well + ;; as a scroll (to bottom). + (execute-kbd-macro "hi") + ;; Prompt and input appear on last line of window. + (should (erc-scenarios-common--at-win-end-p))) + + (ert-info ("Command `recenter-top-bottom' disallowed at prompt") + ;; Hitting C-l does not recenter the window. + (erc-scenarios-common--recenter-top-bottom) + (should (erc-scenarios-common--at-win-end-p)) + (erc-scenarios-common--recenter-top-bottom) + (should (erc-scenarios-common--at-win-end-p))) + + (ert-info ("Command `beginning-of-buffer' allowed at prompt") + ;; Hitting C-< goes to beginning of buffer. + (call-interactively #'beginning-of-buffer) + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New message doesn't trigger scroll when away from prompt") + ;; Arriving insertions don't trigger a scroll when away from the + ;; prompt. New output not seen. + (erc-cmd-MSG "NickServ help register") + (save-excursion (erc-d-t-search-for 10 "End of NickServ")) + (should (= 1 (point))) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (funcall test) + + (ert-info ("New message does trigger a scroll when at prompt") + ;; Recenter so prompt is above rather than at window's end. + (funcall expect 10 "If you are currently logged in") + (recenter 0) + ;; Prompt is somewhere in the middle of the window. + (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p)) + (erc-scenarios-common-say "/msg NickServ help identify") + ;; New arriving messages trigger a snap when inserted. + (erc-d-t-wait-for 10 (erc-scenarios-common--at-win-end-p)) + (funcall expect 10 "IDENTIFY lets you login"))))) + (cl-defun erc-scenarios-common--base-network-id-bouncer ((&key autop foo-id bar-id after &aux diff --git a/test/lisp/erc/resources/scrolltobottom/help.eld b/test/lisp/erc/resources/scrolltobottom/help.eld new file mode 100644 index 00000000000..ba44a0def39 --- /dev/null +++ b/test/lisp/erc/resources/scrolltobottom/help.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Mon, 21 Aug 2023 06:18:36 UTC") + (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.01 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.01 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.01 ":irc.foonet.org 254 tester 2 :channels formed") + (0.01 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.01 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0.01 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0.01 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0.02 ":irc.foonet.org 221 tester +i")) + +((privmsg-help-register 10 "PRIVMSG NickServ :help register") + (0.05 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2REGISTER [email]\2") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :REGISTER lets you register your current nickname as a user account. If the") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :server allows anonymous registration, you can omit the e-mail address.") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :If you are currently logged in with a TLS client certificate and wish to use") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :it instead of a password to log in, send * as the password.") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ HELP\2 ***")) + +((privmsg-help-identify 20 "PRIVMSG NickServ :help identify") + (0.06 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2IDENTIFY [password]\2") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :IDENTIFY lets you login to the given username using either password auth, or") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :certfp (your client certificate) if a password is not given.") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ HELP\2 ***")) + +((quit 10 "QUIT :\2ERC\2 ") + (0.07 ":tester!~u@26axz8nh8zaag.irc QUIT :Quit: \2ERC\2") + (0.02 "ERROR :Quit: \2ERC\2")) -- 2.41.0 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Wed Sep 13 10:06:12 2023 Received: (at 64855) by debbugs.gnu.org; 13 Sep 2023 14:06:12 +0000 Received: from localhost ([127.0.0.1]:35479 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qgQVQ-0000qU-0P for submit@debbugs.gnu.org; Wed, 13 Sep 2023 10:06:12 -0400 Received: from mail-108-mta23.mxroute.com ([136.175.108.23]:40197) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qgQVK-0000qF-Dg for 64855@debbugs.gnu.org; Wed, 13 Sep 2023 10:06:10 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta23.mxroute.com (ZoneMTA) with ESMTPSA id 18a8edce637000d7b6.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Wed, 13 Sep 2023 14:05:56 +0000 X-Zone-Loop: 90c5c59a52b3539093201b1140068d7c047dbf1ee633 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=SjhFyF60ZXs0WYxlMMA2d4FDRJsOX7k/koUMUi36278=; b=be+oCtlgTEXZr+SZsDUSgBhwcO TMcrmqYeFv4RTUpnP1HdrUWScc4oqwunbqDa2soMBtScgCs32/VRA1+PXdPDTZgqIvRCfk3jxHh21 pJeNDBihJskAWx38b38IEyrb/ku/1eYtlKdL0y7Le0WnkOGB2sweS0r5ahUI0DO+B99JNkgsiveQe XrPd0UbAvuwqGvbLp+gUjQNvIG2bkTcPeJS6Jy2IQwaB37Knd6AW7hXbZxjQM/gCXS+lq9VHBL+mI oD9yTaFqhTWalDyERKXm/osNAFSOcfYDdWasVoYza7v1SVIAqARzpjFd56B7jBs2UfIcG8NMPrzNX RNz4oNSQ==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87il948r8x.fsf@neverwas.me> (J. P.'s message of "Thu, 24 Aug 2023 07:11:26 -0700") References: <87h6psyurb.fsf@neverwas.me> <87il948r8x.fsf@neverwas.me> Date: Wed, 13 Sep 2023 07:05:52 -0700 Message-ID: <871qf2183j.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain "J.P." writes: > v5. Make new behavior hinge on new option, disabled by default. Assign > some module functions explicit hook depths. Add news and tests. I've added a version of this as commit 617ddb808999a71c925b68f5369d77aebfcd9254 Consider all windows in erc-scrolltobottom-mode But it comes with a few known problems. The first can be observed during the rapid insertion of newly arriving messages, like you get with history playback or large swaths of help text. Basically, when point is away from the prompt and you issue a command that changes `window-start' significantly (e.g., M-<), point hardly moves at all, maybe half a screenful at most. Another issue is only noticeable if you have code running that suppresses message insertion after prompt submissions (see bug#49860's `echo-message' for one example). Basically, if you've got `erc-scrolltobottom-relaxed' enabled and you stare long enough, you'll notice that the prompt drifts downward with every submitted round of multi-line input. Attached is a patch that attempts to address both of these issues, along with a couple more to get at some unrelated odds and ends. I'm also sort of thinking we ought to temporarily change the default of the new option `erc-scrolltobottom-all' to t for a few weeks to help flush out any other glaring bugs introduced by this feature. If anyone thinks that's a bad idea, please say so. Thanks. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-doc-misc-erc.texi-Fix-display-buffer-example.patch >From 88430ddf866031366ccda5439d007fef6b579d8b Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sun, 10 Sep 2023 22:55:16 -0700 Subject: [PATCH 1/4] ; * doc/misc/erc.texi: Fix display-buffer example. --- doc/misc/erc.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index 6d7785a9b54..1cbecfa891d 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi @@ -1821,7 +1821,7 @@ display-buffer (defun my-erc-disp-chan-p (_ action) (or (assq 'erc-autojoin-mode action) - (and (memq (cdr (assq 'erc-buffer-display alist)) 'JOIN) + (and (eq (cdr (assq 'erc-buffer-display action)) 'JOIN) (member (erc-default-target) '("#emacs" "#fsf"))))) @end lisp -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-5.6-Simplify-erc-fill-module-docstring.patch >From 118e2e4aea66d0c54f0966d5e658086654e9807a Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 13 Sep 2023 02:50:28 -0700 Subject: [PATCH 2/4] [5.6] Simplify erc--fill-module-docstring * lisp/erc/erc-common.el (erc--fill-module-docstring): Don't run hooks for major mode when filling. Prefer `lisp-data-mode' to `emacs-lisp-mode'. --- lisp/erc/erc-common.el | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index 85971797c2f..67c2cf8535b 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -270,18 +270,20 @@ erc--prepare-custom-module-type " above.")))))) (defun erc--fill-module-docstring (&rest strings) + "Concatenate STRINGS and fill as a doc string." + ;; Perhaps it's better to mimic `internal--format-docstring-line' + ;; and use basic filling instead of applying a major mode? (with-temp-buffer - (emacs-lisp-mode) - (insert "(defun foo ()\n" - (format "%S" (apply #'concat strings)) - "\n(ignore))") + (delay-mode-hooks + (if (fboundp 'lisp-data-mode) (lisp-data-mode) (emacs-lisp-mode))) + (insert (format "%S" (apply #'concat strings))) (goto-char (point-min)) - (forward-line 2) - (let ((emacs-lisp-docstring-fill-column 65) + (forward-line) + (let ((fill-column 65) (sentence-end-double-space t)) (fill-paragraph)) (goto-char (point-min)) - (nth 3 (read (current-buffer))))) + (read (current-buffer)))) (defmacro erc--find-feature (name alias) `(pcase (erc--find-group ',name ,(and alias (list 'quote alias))) -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0003-5.6-Skip-erc-ignored-user-p-when-erc-ignore-list-is-.patch >From 801306861920124a4ca68d925829f957d748aa37 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Sep 2023 21:21:42 -0700 Subject: [PATCH 3/4] [5.6] Skip erc-ignored-user-p when erc-ignore-list is empty * lisp/erc/erc-backend.el (erc-server-PRIVMSG): Don't bother with `erc-ignored-user-p' and `erc-ignored-reply-p' when their associated options are null. * lisp/erc/erc-common.el (erc-get-server-user): Rearrange so `erc-with-server-buffer' doesn't have to switch to the server buffer because `erc-downcase' can run in channels as well. * lisp/erc/erc.el (erc-ignored-user-p): Add comment. * test/lisp/erc/erc-scenarios-base-buffer-display.el (erc-scenarios-base-buffer-display--defwin-recbury-intbuf): Extend timeout. --- lisp/erc/erc-backend.el | 7 ++++--- lisp/erc/erc-common.el | 5 +++-- lisp/erc/erc.el | 2 ++ test/lisp/erc/erc-scenarios-base-buffer-display.el | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 9e121ec1e92..596e504b39f 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -113,6 +113,8 @@ erc-ensure-target-buffer-on-privmsg (defvar erc-format-nick-function) (defvar erc-format-query-as-channel-p) (defvar erc-hide-prompt) +(defvar erc-ignore-list) +(defvar erc-ignore-reply-list) (defvar erc-input-marker) (defvar erc-insert-marker) (defvar erc-invitation) @@ -1902,8 +1904,8 @@ erc--server-determine-join-display-context (cmd (erc-response.command parsed)) (tgt (car (erc-response.command-args parsed))) (msg (erc-response.contents parsed))) - (if (or (erc-ignored-user-p sender-spec) - (erc-ignored-reply-p msg tgt proc)) + (if (or (and erc-ignore-list (erc-ignored-user-p sender-spec)) + (and erc-ignore-reply-list (erc-ignored-reply-p msg tgt proc))) (when erc-minibuffer-ignored (message "Ignored %s from %s to %s" cmd sender-spec tgt)) (let* ((sndr (erc-parse-user sender-spec)) @@ -1918,7 +1920,6 @@ erc--server-determine-join-display-context ,@erc--display-context)) s buffer fnick) - (setf (erc-response.contents parsed) msg) (setq buffer (erc-get-buffer (if privp nick tgt) proc)) ;; Even worth checking for empty target here? (invalid anyway) (unless (or buffer noticep (string-empty-p tgt) (eq ?$ (aref tgt 0)) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index 67c2cf8535b..e680666156b 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -472,8 +472,9 @@ erc-get-channel-user (define-inline erc-get-server-user (nick) "Find NICK in the current server's `erc-server-users' hash table." (inline-letevals (nick) - (inline-quote (erc-with-server-buffer - (gethash (erc-downcase ,nick) erc-server-users))))) + (inline-quote + (gethash (erc-downcase ,nick) + (erc-with-server-buffer erc-server-users))))) (defmacro erc--with-dependent-type-match (type &rest features) "Massage Custom :type TYPE with :match function that pre-loads FEATURES." diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 07ba32d1cca..a65739cf861 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -6832,6 +6832,8 @@ erc-delete-query (setq erc-default-recipients d2) (error "Current target is not a QUERY")))) +;; FIXME spin this ignore stuff off into its own module, enabled by +;; default until some major version change. (defun erc-ignored-user-p (spec) "Return non-nil if SPEC matches something in `erc-ignore-list'. diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el index df292a8c113..ef544b4dcd0 100644 --- a/test/lisp/erc/erc-scenarios-base-buffer-display.el +++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el @@ -109,7 +109,7 @@ erc-scenarios-base-buffer-display--defwin-recbury-intbuf (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam")) (should (eq (window-buffer) (get-buffer "#spam"))) ;; Option `buffer' replaces entire window (no split) - (erc-d-t-wait-for 5 (frame-root-window-p (selected-window))))))))) + (erc-d-t-wait-for 10 (frame-root-window-p (selected-window))))))))) (ert-deftest erc-scenarios-base-buffer-display--defwino-recbury-intbuf () :tags '(:expensive-test) -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0004-5.6-Run-erc-scrolltobottom-on-pre-insert-uncondition.patch >From 88681f79c2a6fcfb9343c85e2d8ba58ed8090680 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 13 Sep 2023 05:42:24 -0700 Subject: [PATCH 4/4] [5.6] Run erc--scrolltobottom-on-pre-insert unconditionally * lisp/erc/erc-goodies.el (erc--scrolltobottom-all): Pass `no-force' argument to `set-window-start'. (erc--scrolltobottom-on-pre-insert): Convert function from generic to normal and drop `erc-input' method completely. A non-nil `insertp' slot means a message is marked for insertion in the read-only portion of the buffer, above the prompt. But conditioning the restoring of window parameters on the latter is not enough: the window still needs adjusting whenever input is typed, regardless of whether it's erased or "inserted." (Bug#64855) --- lisp/erc/erc-goodies.el | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index 6353b813805..6eb015fdd64 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -223,7 +223,7 @@ erc--scrolltobottom-all ((erc--scrolltobottom-window-info) (found (assq window erc--scrolltobottom-window-info)) ((not (erc--scrolltobottom-confirm (nth 2 found))))) - (setf (window-start window) (cadr found))))) + (set-window-start window (cadr found) 'no-force)))) ;; Necessary unless we're sure `erc--scrolltobottom-on-pre-insert' ;; always runs between calls to this function. (setq erc--scrolltobottom-window-info nil)) @@ -280,7 +280,7 @@ erc--scrolltobottom-setup (kill-local-variable 'erc--scrolltobottom-relaxed-commands) (kill-local-variable 'erc--scrolltobottom-window-info))) -(cl-defmethod erc--scrolltobottom-on-pre-insert (_input-or-string) +(defun erc--scrolltobottom-on-pre-insert (_) "Remember the `window-start' before inserting a message." (setq erc--scrolltobottom-window-info (mapcar (lambda (w) @@ -293,11 +293,6 @@ erc--scrolltobottom-on-pre-insert (if (= ?\n (char-before (point-max))) (1+ c) c)))) (get-buffer-window-list nil nil 'visible)))) -(cl-defmethod erc--scrolltobottom-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--scrolltobottom-confirm (&optional scroll-to) "Like `erc-scroll-to-bottom', but use `window-point'. Position current line (with `recenter') SCROLL-TO lines below -- 2.41.0 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Tue Sep 19 09:39:13 2023 Received: (at 64855-done) by debbugs.gnu.org; 19 Sep 2023 13:39:13 +0000 Received: from localhost ([127.0.0.1]:55641 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qiawa-0006lu-NQ for submit@debbugs.gnu.org; Tue, 19 Sep 2023 09:39:13 -0400 Received: from mail-108-mta167.mxroute.com ([136.175.108.167]:36373) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qiawW-0006li-9n for 64855-done@debbugs.gnu.org; Tue, 19 Sep 2023 09:39:10 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta167.mxroute.com (ZoneMTA) with ESMTPSA id 18aadaa51c6000d7b6.001 for <64855-done@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Tue, 19 Sep 2023 13:38:55 +0000 X-Zone-Loop: 8463e75b1b1197af586ab18aff1f47ba2acd95ef487e X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=QHX1paYPwD3RKVvHv1cJOp+ZV6B9uer72YmjDcPLIww=; b=jBAN2Fp5k9oRM87tPqOhuG4ZnX evtE9N71ZmAnP8IImzKrJlaVhU7N/Mu/wPCXAiLav0mtyw4/NSPTkeBaEpuSAbaobyqz7J/j56GiO ef+cV9iUHhz2qU1b1elTJbQUDCG0LcWvL7CbFuL3+POLDdxyGxSIGr0akxNneojTgz9hxdHZGaMJS RpvTS4Oz7x41cvCM2oOel99Uqyz8lncIaghVGbTNK76GvH+j4FovcswAOpg6VZ2lew/CEYnauoOqI Cyjc8OTJ0E3prn5zdWHi0+/cVC3JLO+DUOQdtuW4jITAvoWp0za5Kc7fjn7Oy4MO4pc8HMz//j9jb 8MduqmkQ==; From: "J.P." To: 64855-done@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <871qf2183j.fsf@neverwas.me> (J. P.'s message of "Wed, 13 Sep 2023 07:05:52 -0700") References: <87h6psyurb.fsf@neverwas.me> <87il948r8x.fsf@neverwas.me> <871qf2183j.fsf@neverwas.me> Date: Tue, 19 Sep 2023 06:38:52 -0700 Message-ID: <87zg1icmfn.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855-done Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) "J.P." writes: > But it comes with a few known problems. > > The first can be observed during the rapid insertion of newly arriving > messages, like you get with history playback or large swaths of help > text. Basically, when point is away from the prompt and you issue a > command that changes `window-start' significantly (e.g., M-<), point > hardly moves at all, maybe half a screenful at most. > > Another issue is only noticeable if you have code running that > suppresses message insertion after prompt submissions (see bug#49860's > `echo-message' for one example). Basically, if you've got > `erc-scrolltobottom-relaxed' enabled and you stare long enough, you'll > notice that the prompt drifts downward with every submitted round of > multi-line input. > > Attached is a patch that attempts to address both of these issues, along > with a couple more to get at some unrelated odds and ends. I'm also sort > of thinking we ought to temporarily change the default of the new option > `erc-scrolltobottom-all' to t for a few weeks to help flush out any > other glaring bugs introduced by this feature. If anyone thinks that's a > bad idea, please say so. Thanks. I've installed this as 69a1546 Run erc--scrolltobottom-on-pre-insert unconditionally along with two others from that set. However, I left out [3/4] [5.6] Skip erc-ignored-user-p when erc-ignore-list is empty because even a minor change like this to such a widely used feature should probably be accompanied by tests and extra scrutiny, especially since I myself am not really familiar with it. The optimization itself seems simple enough, but one of those options is buffer-local and set via the /IGNORE command, which complicates matters. I'm thinking we may end up folding that patch into a larger initiative aimed at moving everything related to message skipping and hiding to another module. Indeed, there seems to be a good deal of overlap in functionality among the lurker, hide-list, and /IGNORE features (and also the fools stuff in erc-match). It'd be nice to get all that out of erc.el because it can mostly be implemented using hooks, which should help with maintenance and maybe save uninterested users some cycles. From debbugs-submit-bounces@debbugs.gnu.org Tue Oct 10 22:54:35 2023 Received: (at 64855) by debbugs.gnu.org; 11 Oct 2023 02:54:35 +0000 Received: from localhost ([127.0.0.1]:37263 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qqPMo-00047I-7I for submit@debbugs.gnu.org; Tue, 10 Oct 2023 22:54:35 -0400 Received: from mail-108-mta56.mxroute.com ([136.175.108.56]:38305) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qqPMl-000479-Kp for 64855@debbugs.gnu.org; Tue, 10 Oct 2023 22:54:32 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta56.mxroute.com (ZoneMTA) with ESMTPSA id 18b1ca7db23000ff68.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Wed, 11 Oct 2023 02:54:04 +0000 X-Zone-Loop: b734edc21247807e2a4dff68a4194c924b60d8672976 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=0wXNFDg/X/HyBd0fElBT7yOLqwlnFLE2sWs/ZDrKuyg=; b=R8GgYLrQz9rzjeJNfwL5BKP8Nd zTXizOpdH3jWVpTjNedXNgefxBi9tC0q2S/M7Ug9t2bAmVSdn21agmccmHgAfyWow9/NqWUYv/jPl jqZCRzLXAmNOWtfUqJKceOh183Uwd73svcOI5pUwgvv1sbRxkldG4C19hvODzigNu3wAkKS18K7qE t5hK5NrUfQTAegYrrzTm1KIglAQYzMRjwWkRKWTqF0L8j6bCIaIJnQ3/9+MbVTzLi1eL8zHDznBrj 3eD7wqowpmDQgDQXnSjL3Ogs0AHlqYCXTQ6Te5+3h0Hf5AZOViMsJtxFTRc5drf7jXaseUNEtE6XH rnCgwLBA==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87h6psyurb.fsf@neverwas.me> (J. P.'s message of "Tue, 25 Jul 2023 06:40:24 -0700") References: <87h6psyurb.fsf@neverwas.me> Date: Tue, 10 Oct 2023 19:53:43 -0700 Message-ID: <87o7h5euo8.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Due to various outstanding issues related to this feature, I've decided to abandon almost all window twiddling in `post-command-hook' because blacklisting and whitelisting various commands was becoming a sad game of whack-a-mole. Rather, I think it's best if we restrict this option's availability to Emacs 28+ and leverage `read-minibuffer-restore-windows' to keep windows stable when executing extended commands. I also think it makes sense to consolidate knobs and have `erc-scrolltobottom-all' subsume the do-little `erc-scrolltobottom-relaxed' as a third value state (boolean + the symbol `relaxed'). Please see attached changes. Thanks. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Skip-restore-post-minibuf-read-in-erc-scrolltobo.patch >From f39edd3d8f223be7a8f9574f37cd0f16abdeb26a Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Tue, 10 Oct 2023 18:14:53 -0700 Subject: [PATCH] [5.6] Skip restore post minibuf read in erc-scrolltobottom * etc/ERC-NEWS: Remove mention of `erc-scrolltobottom-relaxed' in entry for module `scrolltobottom'. * lisp/erc/erc-goodies.el (erc-input-line-position): Fix mention of subsumed option `erc-scrolltobottom-relaxed'. (erc-scrolltobottom-all): Subsume option `erc-scrolltobottom-relaxed', which depended on this option being non-nil anyway. (erc-scrolltobottom-relaxed): Remove redundant option, which was new in ERC 5.6. (erc-scrolltobottom-enable, erc-scrolltobottom-mode): Warn if user attempts to enable `erc-scrolltobottom-all' on Emacs 27, which is not supported. (erc--scrolltobottom-relaxed-commands, erc--scrolltobottom-post-force-commands, erc--scrolltobottom-relaxed-skip-commands): Remove unused variables. (erc--scrolltobottom-on-pre-command, erc--scrolltobottom-on-pre-command-relaxed, erc--scrolltobottom-on-post-command-relaxed): Remove unused functions. (erc--scrolltobottom-on-post-command): Remove conditional branch for dealing with a non-nil `erc--scrolltobottom-window-info'. (erc--scrolltobottom-setup): Convert from generic to normal function and remove setup and teardown for unused hooks. Set variable `read-minibuffer-restore-windows' locally when option `erc-scrolltobottom-all' is non-nil. (erc--scrolltobottom-on-pre-insert): Replace reference to subsumed option `erc-scrolltobottom-relaxed' with new value `relaxed' for existing option `erc-scrolltobottom-all'. * test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el (erc-scenarios-scrolltobottom--relaxed): Replace subsumed option `erc-scrolltobottom-relaxed' with new value `relaxed' for `erc-scrolltobottom-all'. (Bug#64855) --- etc/ERC-NEWS | 13 +- lisp/erc/erc-goodies.el | 201 +++++------------- .../erc-scenarios-scrolltobottom-relaxed.el | 5 +- 3 files changed, 60 insertions(+), 159 deletions(-) diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index fadd97b65df..1fd516dcacf 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -179,13 +179,12 @@ assumptions explained in its doc string. For clarity, it has been renamed 'erc-ensure-target-buffer-on-privmsg'. ** Module 'scrolltobottom' can attempt to be more aggressive. -Enabling the experimental option 'erc-scrolltobottom-all' tells -'scrolltobottom' to be more vigilant about staking down the input area -and to do so in all ERC windows. The dependent option -'erc-scrolltobottom-relaxed', also experimental, makes ERC's prompt -stationary wherever it happens to reside instead of forcing it to the -bottom of a window. That is, new input appears above the prompt, -scrolling existing messages upward to compensate. +Enabling the experimental option 'erc-scrolltobottom-all' makes ERC +more vigilant about staking down the input area in all ERC windows. +And the option's 'relaxed' variant makes ERC's prompt stationary +wherever it happens to reside, instead of forcing it to the bottom of +a window. That is, new input appears above the prompt, scrolling +existing messages upward to compensate. ** Subtle changes in two fundamental faces. Users of the default theme may notice that 'erc-action-face' and diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index b77176d8ac7..863429de202 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -44,45 +44,46 @@ erc-input-line-position This should be an integer specifying the line of the buffer on which the input line should stay. A value of \"-1\" would keep the input line positioned on the last line in the buffer. This is passed as an -argument to `recenter', unless `erc-scrolltobottom-relaxed' is -non-nil, in which case, ERC interprets it as additional lines to -scroll down by per message insertion (minus one for the prompt)." +argument to `recenter', unless `erc-scrolltobottom-all' is +`relaxed', in which case, ERC interprets it as additional lines +to scroll down by per message insertion (minus one for the +prompt)." :group 'erc-display :type '(choice integer (const nil))) (defcustom erc-scrolltobottom-all nil "Whether to scroll all windows or just the selected one. -A value of nil preserves pre-5.6 behavior, in which scrolling -only affects the selected window. Users should consider its -non-nil behavior experimental for the time being. Note also that ERC expects this option to be configured before module -initialization." +initialization. A value of nil preserves pre-5.6 behavior, in +which scrolling only affects the selected window. A value of t +means ERC attempts to recenter all visible windows whose point +resides in the input area. + +A value of `relaxed' tells ERC to forgo forcing prompt to the +bottom of the window. When point is at the prompt, ERC scrolls +the window up when inserting messages, making the prompt appear +stationary. Users who find this effect too \"stagnant\" can +adjust the option `erc-input-line-position', borrowed here to +express a scroll step offset. Setting that value to zero lets +the prompt drift toward the bottom by one line per message, which +is generally slow enough not to distract while composing input. +Of course, this doesn't apply when receiving a large influx of +messages, such as after typing \"/msg NickServ help\". + +Note that users should consider this option's non-nil behavior to +be experimental. It currently only works with Emacs 28+." :group 'erc-display :package-version '(ERC . "5.6") ; FIXME sync on release - :type 'boolean) - -(defcustom erc-scrolltobottom-relaxed nil - "Whether to forgo forcing prompt to the bottom of the window. -When non-nil, and point is at the prompt, ERC scrolls the window -up when inserting messages, making the prompt appear stationary. -Users who find this effect too \"stagnant\" can adjust the option -`erc-input-line-position', which ERC borrows to express a scroll -step offset when this option is non-nil. Setting that value to -zero lets the prompt drift toward the bottom by one line per -message, which is generally slow enough not to distract while -composing input. Of course, this doesn't apply when receiving a -large influx of messages, such as after typing \"/msg NickServ -help\". Note that ERC only considers this option when the -experimental companion option `erc-scrolltobottom-all' is enabled -and, only then, during module setup." - :group 'erc-display - :package-version '(ERC . "5.6") ; FIXME sync on release - :type 'boolean) + :type '(choice boolean (const relaxed))) ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t) (define-erc-module scrolltobottom nil "This mode causes the prompt to stay at the end of the window." ((add-hook 'erc-mode-hook #'erc--scrolltobottom-setup) + (when (and erc-scrolltobottom-all (< emacs-major-version 28)) + (erc-button--display-error-notice-with-keys + "Option `erc-scrolltobottom-all' requires Emacs 28+. Disabling.") + (setopt erc-scrolltobottom-all nil)) (unless erc--updating-modules-p (erc-buffer-do #'erc--scrolltobottom-setup)) (if erc-scrolltobottom-all (progn @@ -93,25 +94,17 @@ scrolltobottom (add-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom))) ((remove-hook 'erc-mode-hook #'erc--scrolltobottom-setup) (erc-buffer-do #'erc--scrolltobottom-setup) - (if erc-scrolltobottom-all - (progn - (remove-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert) - (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) - (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) - (remove-hook 'erc-pre-send-functions - #'erc--scrolltobottom-on-pre-insert)) - (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)))) + (remove-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all) + (remove-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert) + (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom))) (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))) -(defvar-local erc--scrolltobottom-relaxed-commands '(end-of-buffer) - "Commands triggering a forced scroll to prompt. -Only applies with `erc-scrolltobottom-relaxed' while away from -prompt.") - (defvar-local erc--scrolltobottom-window-info nil "Alist with windows as keys and lists of window-related info as values. Values are lists containing the last window start position and @@ -119,34 +112,12 @@ erc--scrolltobottom-window-info may be nil, is the number of lines between `window-start' and `window-point', inclusive.") -(defvar erc--scrolltobottom-post-force-commands - '(beginning-of-buffer - electric-newline-and-maybe-indent - newline - default-indent-new-line) - "Commands that force a scroll after execution at prompt. -That is, ERC recalculates the window's start instead of blindly -restoring it.") - -;; Unfortunately, this doesn't work when `erc-scrolltobottom-relaxed' -;; is enabled (scaling up still moves the prompt). +;; FIXME treat `end-of-buffer' specially and always recenter -1. +;; FIXME make this work when `erc-scrolltobottom-all' is set to +;; `relaxed'. (defvar erc--scrolltobottom-post-ignore-commands '(text-scale-adjust) "Commands to skip instead of force-scroll on `post-command-hook'.") -(defvar erc--scrolltobottom-relaxed-skip-commands - '(recenter-top-bottom scroll-down-command) - "Commands exempt from triggering a stash and restore of `window-start'. -Only applies with `erc-scrolltobottom-relaxed' while in the input -area.") - -(defun erc--scrolltobottom-on-pre-command () - (when (and (eq (selected-window) (get-buffer-window)) - (>= (point) erc-input-marker)) - (setq erc--scrolltobottom-window-info - (list (list (selected-window) - (window-start) - (count-screen-lines (window-start) (point-max))))))) - (defun erc--scrolltobottom-on-post-command () "Restore window start or scroll to prompt and recenter. When `erc--scrolltobottom-window-info' is non-nil and its first @@ -154,55 +125,8 @@ erc--scrolltobottom-on-post-command window so long as prompt hasn't moved. Expect buffer to be unnarrowed." (when (eq (selected-window) (get-buffer-window)) - (if-let (((not (input-pending-p))) - (erc--scrolltobottom-window-info) - (found (car erc--scrolltobottom-window-info)) - ((eq (car found) (selected-window))) - ((not (memq this-command - erc--scrolltobottom-post-force-commands))) - ((= (nth 2 found) - (count-screen-lines (window-start) (point-max))))) - (set-window-start (selected-window) (nth 1 found)) - (unless (memq this-command erc--scrolltobottom-post-ignore-commands) - (erc--scrolltobottom-confirm))) - (setq erc--scrolltobottom-window-info nil))) - -(defun erc--scrolltobottom-on-pre-command-relaxed () - "Maybe scroll to bottom when away from prompt. -When `erc-scrolltobottom-relaxed' is active, only scroll when -prompt is past window's end and the command is `end-of-buffer' or -`self-insert-command' (assuming `move-to-prompt' is active). -When at prompt and current command does not appear in -`erc--scrolltobottom-relaxed-skip-commands', stash -`erc--scrolltobottom-window-info' for the selected window. -Assume an unnarrowed buffer." - (when (eq (selected-window) (get-buffer-window)) - (when (and (not (input-pending-p)) - (< (point) erc-input-marker) - (memq this-command erc--scrolltobottom-relaxed-commands) - (< (window-end nil t) erc-input-marker)) - (save-excursion - (goto-char (point-max)) - (recenter (or erc-input-line-position -1)))) - (when (and (>= (point) erc-input-marker) - (not (memq this-command - erc--scrolltobottom-relaxed-skip-commands))) - (setq erc--scrolltobottom-window-info - (list (list (selected-window) - (window-start) - (count-screen-lines (window-start) (point-max)))))))) - -(defun erc--scrolltobottom-on-post-command-relaxed () - "Set window start or scroll when data was captured on pre-command." - (when-let (((eq (selected-window) (get-buffer-window))) - (erc--scrolltobottom-window-info) - (found (car erc--scrolltobottom-window-info)) - ((eq (car found) (selected-window)))) - (if (and (not (memq this-command erc--scrolltobottom-post-force-commands)) - (= (nth 2 found) - (count-screen-lines (window-start) (point-max)))) - (set-window-start (selected-window) (nth 1 found)) - (recenter (nth 2 found))) + (unless (memq this-command erc--scrolltobottom-post-ignore-commands) + (erc--scrolltobottom-confirm)) (setq erc--scrolltobottom-window-info nil))) ;; It may be desirable to also restore the relative line position of @@ -246,54 +170,33 @@ erc-add-scroll-to-bottom (declare (obsolete erc--scrolltobottom-setup "30.1")) (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t)) -(cl-defgeneric erc--scrolltobottom-setup () - "Arrange for scrolling to bottom on window configuration changes. -Undo that arrangement when disabling `erc-scrolltobottom-mode'." - (if erc-scrolltobottom-mode - (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t) - (remove-hook 'post-command-hook #'erc-scroll-to-bottom t))) - -(cl-defmethod erc--scrolltobottom-setup (&context - (erc-scrolltobottom-all (eql t))) - "Add and remove local hooks specific to `erc-scrolltobottom-all'." +(defun erc--scrolltobottom-setup () + "Perform buffer-local setup for module `scrolltobottom'." (if erc-scrolltobottom-mode - (if erc-scrolltobottom-relaxed + (if erc-scrolltobottom-all (progn - (when (or (bound-and-true-p erc-move-to-prompt-mode) - (memq 'move-to-prompt erc-modules)) - (cl-pushnew 'self-insert-command - erc--scrolltobottom-relaxed-commands)) - (add-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command-relaxed 60 t) - (add-hook 'pre-command-hook ; preempt `move-to-prompt' - #'erc--scrolltobottom-on-pre-command-relaxed 60 t)) - (add-hook 'window-configuration-change-hook - #'erc--scrolltobottom-at-prompt-minibuffer-active nil t) - (add-hook 'pre-command-hook - #'erc--scrolltobottom-on-pre-command 60 t) - (add-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command 60 t)) + (setq-local read-minibuffer-restore-windows nil) + (unless (eq erc-scrolltobottom-all 'relaxed) + (add-hook 'window-configuration-change-hook + #'erc--scrolltobottom-at-prompt-minibuffer-active 50 t) + (add-hook 'post-command-hook + #'erc--scrolltobottom-on-post-command 50 t))) + (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t)) + (remove-hook 'post-command-hook #'erc-scroll-to-bottom t) + (remove-hook 'post-command-hook #'erc--scrolltobottom-on-post-command t) (remove-hook 'window-configuration-change-hook #'erc--scrolltobottom-at-prompt-minibuffer-active t) - (remove-hook 'pre-command-hook - #'erc--scrolltobottom-on-pre-command t) - (remove-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command t) - (remove-hook 'pre-command-hook - #'erc--scrolltobottom-on-pre-command-relaxed t) - (remove-hook 'post-command-hook - #'erc--scrolltobottom-on-post-command-relaxed t) - (kill-local-variable 'erc--scrolltobottom-relaxed-commands) + (kill-local-variable 'read-minibuffer-restore-windows) (kill-local-variable 'erc--scrolltobottom-window-info))) (defun erc--scrolltobottom-on-pre-insert (_) - "Remember the `window-start' before inserting a message." + "Remember `window-start' before inserting a message." (setq erc--scrolltobottom-window-info (mapcar (lambda (w) (list w (window-start w) (and-let* - ((erc-scrolltobottom-relaxed) + (((eq erc-scrolltobottom-all 'relaxed)) (c (count-screen-lines (window-start w) (point-max) nil w))) (if (= ?\n (char-before (point-max))) (1+ c) c)))) diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el index 7d256bf711b..68ea0b1b070 100644 --- a/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el +++ b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el @@ -1,4 +1,4 @@ -;;; erc-scenarios-scrolltobottom-relaxed.el --- erc-scrolltobottom-relaxed -*- lexical-binding: t -*- +;;; erc-scenarios-scrolltobottom-relaxed.el --- erc-scrolltobottom-all relaxed -*- lexical-binding: t -*- ;; Copyright (C) 2023 Free Software Foundation, Inc. @@ -40,8 +40,7 @@ erc-scenarios-scrolltobottom--relaxed (dumb-server (erc-d-run "localhost" t 'help)) (port (process-contact dumb-server :service)) (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) - (erc-scrolltobottom-all t) - (erc-scrolltobottom-relaxed t) + (erc-scrolltobottom-all 'relaxed) (erc-server-flood-penalty 0.1) (expect (erc-d-t-make-expecter)) lower upper) -- 2.41.0 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Fri Oct 13 20:30:26 2023 Received: (at 64855) by debbugs.gnu.org; 14 Oct 2023 00:30:26 +0000 Received: from localhost ([127.0.0.1]:47587 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qrSXx-0007JU-Lb for submit@debbugs.gnu.org; Fri, 13 Oct 2023 20:30:25 -0400 Received: from mail-108-mta245.mxroute.com ([136.175.108.245]:44157) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qrSXu-0007JB-PS for 64855@debbugs.gnu.org; Fri, 13 Oct 2023 20:30:23 -0400 Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta245.mxroute.com (ZoneMTA) with ESMTPSA id 18b2b96fb29000ff68.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Sat, 14 Oct 2023 00:29:56 +0000 X-Zone-Loop: 78ea21470b9e8546ac87b1f4eb92e614b28a15399a28 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=aQ6Yw+BqYZBSEohxm/aA09VDDRQ6SPJagdhEo0JgQ84=; b=j2iGlt5UxF5OMu4bdNOk+GTxcr UOn5p+qIh2QWBbPp83gyNEcH29+HS68TvZcJaF3BP3pG0PKBtwLRAF8MdbbkwXVDCtjtxIhPsIoFA 8SOVE3x8lPECbzPU9PlUEZTvF+lBtJ5gnPztxE0enSl7Syzh6bB9XTcQsWSC/xYJ7nJpzdBUDVuvL Czko9ZUOMXunlh6IIT7YzH2HI6OqgPiHouBwUWp1FCUaLob5f1CAhO4mZuckmwYmiPW2pjP9fXjFB +n+AMsnnkNxC77vVMPO3MqF0m9fUfSkuoPKF/D6dkxQC4MEnKXfYToVQftzDIygY34sirixtAaS8R fwbynTUA==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87o7h5euo8.fsf@neverwas.me> (J. P.'s message of "Tue, 10 Oct 2023 19:53:43 -0700") References: <87h6psyurb.fsf@neverwas.me> <87o7h5euo8.fsf@neverwas.me> Date: Fri, 13 Oct 2023 17:29:52 -0700 Message-ID: <871qdy9hbz.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) I've added these changes as https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=328a9856 This bug has already been closed. Thanks. From debbugs-submit-bounces@debbugs.gnu.org Tue Oct 24 22:16:38 2023 Received: (at 64855) by debbugs.gnu.org; 25 Oct 2023 02:16:38 +0000 Received: from localhost ([127.0.0.1]:56977 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qvTRl-0007LP-Nm for submit@debbugs.gnu.org; Tue, 24 Oct 2023 22:16:38 -0400 Received: from mail-108-mta225.mxroute.com ([136.175.108.225]:45483) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qvTRg-0007L5-Fb for 64855@debbugs.gnu.org; Tue, 24 Oct 2023 22:16:36 -0400 Received: from filter006.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta225.mxroute.com (ZoneMTA) with ESMTPSA id 18b649e05b20008912.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Wed, 25 Oct 2023 02:15:59 +0000 X-Zone-Loop: 2e88826f8941f3e7e217a21df330e4a3c9d7b8b789ea X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=fVolk0yPnD4juLWsYnsTQWmVDjGFJ6nyXClSvqMftNM=; b=RsjX7TCpPK36uBWSiSU6KewrnS I93d5MSG4xcx189YHMx9u5TOUINIlLEsm6vLinWdMZxaL/1NqKnmiSYkRiSGFku1Glto43beZENpi GxONe+ElqpM7IU0EwvpL2ouut92UpYvonT1i4gjA5M4Ihp7LQqiTJmI/xB3NRNisjxdJBbyyOMLxH wTzvW0Tz1AmAKgy3M2P+Q+S2JH0DobMzAy1obCgyqvkAQUtJ2r3iUL/24dVxOuJ/HoQAT3+phpL5s 8fq1LafvcmQXcK1OndFXPa2SrwzfM/LDYYOAnQBQNAfMJC4WE4O3reyHYh5rQgWQbARJ74Q+549JE Asfdc6SQ==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <871qdy9hbz.fsf@neverwas.me> (J. P.'s message of "Fri, 13 Oct 2023 17:29:52 -0700") References: <87h6psyurb.fsf@neverwas.me> <87o7h5euo8.fsf@neverwas.me> <871qdy9hbz.fsf@neverwas.me> Date: Tue, 24 Oct 2023 19:15:55 -0700 Message-ID: <87r0lja1lw.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain "J.P." writes: > I've added these changes as > > https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=328a9856 > > This bug has already been closed. Thanks. But wait, there's more! (There's always more.) It seems I went a little overboard in reigning in the frequency of scroll attempts. This has been walked back slightly in the attached changes. There's also a subtle quirk that's come to light involving `point-max' and (recenter -1) where Emacs calculates point as having drifted off screen. We could try tackling this with something kludgey, like decrementing point temporarily during `recenter' attempts. But that fails if the current input ends in a newline. I've instead decided to address this by taking a page from other Emacs libraries and setting `scroll-step' locally to 1 if a user hasn't customized `scroll-conservatively'. See attached. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Be-slightly-more-aggressive-in-erc-scrolltobotto.patch >From 3e538317bade00780bb54a31aafd58689ee9eb42 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Tue, 24 Oct 2023 18:18:50 -0700 Subject: [PATCH] [5.6] Be slightly more aggressive in erc-scrolltobottom-all * lisp/erc/erc-goodies.el (erc--scrolltobottom-on-post-command): Redo obsolete doc string. (erc--scrolltobottom-at-prompt-minibuffer-active, erc--scrolltobottom-on-win-conf-change): Rename former to latter to better reflect actual role. Remove conditional guard so this always runs. (erc--scrolltobottom-setup): Set `scroll-step' locally when a user hasn't customized `scroll-conservatively'. Update hook member names. (Bug#64855) --- lisp/erc/erc-goodies.el | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index 9d70c644429..e854d063cdd 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -119,28 +119,20 @@ erc--scrolltobottom-post-ignore-commands "Commands to skip instead of force-scroll on `post-command-hook'.") (defun erc--scrolltobottom-on-post-command () - "Restore window start or scroll to prompt and recenter. -When `erc--scrolltobottom-window-info' is non-nil and its first -item is associated with the selected window, restore start of -window so long as prompt hasn't moved. Expect buffer to be -unnarrowed." + "Scroll selected window unless `this-command' is exempted." (when (eq (selected-window) (get-buffer-window)) (unless (memq this-command erc--scrolltobottom-post-ignore-commands) - (erc--scrolltobottom-confirm)) - (setq erc--scrolltobottom-window-info nil))) + (setq erc--scrolltobottom-window-info nil) + (erc--scrolltobottom-confirm)))) ;; It may be desirable to also restore the relative line position of ;; window point after changing dimensions. Perhaps stashing the ;; previous ratio of window line to body height and later recentering ;; proportionally would achieve this. -(defun erc--scrolltobottom-at-prompt-minibuffer-active () +(defun erc--scrolltobottom-on-win-conf-change () "Scroll window to bottom when at prompt and using the minibuffer." - ;; This is redundant or ineffective in the selected window if at - ;; prompt or if only one window exists. - (unless (or (input-pending-p) - (and (minibuffer-window-active-p (minibuffer-window)) - (eq (old-selected-window) (minibuffer-window)))) - (erc--scrolltobottom-confirm))) + (setq erc--scrolltobottom-window-info nil) + (erc--scrolltobottom-confirm)) (defun erc--scrolltobottom-all (&rest _) "Maybe put prompt on last line in all windows displaying current buffer. @@ -176,16 +168,18 @@ erc--scrolltobottom-setup (if erc-scrolltobottom-all (progn (setq-local read-minibuffer-restore-windows nil) + (when (zerop scroll-conservatively) + (setq-local scroll-step 1)) (unless (eq erc-scrolltobottom-all 'relaxed) (add-hook 'window-configuration-change-hook - #'erc--scrolltobottom-at-prompt-minibuffer-active 50 t) + #'erc--scrolltobottom-on-win-conf-change 50 t) (add-hook 'post-command-hook #'erc--scrolltobottom-on-post-command 50 t))) (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t)) (remove-hook 'post-command-hook #'erc-scroll-to-bottom t) (remove-hook 'post-command-hook #'erc--scrolltobottom-on-post-command t) (remove-hook 'window-configuration-change-hook - #'erc--scrolltobottom-at-prompt-minibuffer-active t) + #'erc--scrolltobottom-on-win-conf-change t) (kill-local-variable 'read-minibuffer-restore-windows) (kill-local-variable 'erc--scrolltobottom-window-info))) -- 2.41.0 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Mon Oct 30 09:47:11 2023 Received: (at 64855) by debbugs.gnu.org; 30 Oct 2023 13:47:11 +0000 Received: from localhost ([127.0.0.1]:44579 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qxSbm-0004qe-Tf for submit@debbugs.gnu.org; Mon, 30 Oct 2023 09:47:11 -0400 Received: from mail-108-mta234.mxroute.com ([136.175.108.234]:41887) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qxSbl-0004qW-6V for 64855@debbugs.gnu.org; Mon, 30 Oct 2023 09:47:09 -0400 Received: from filter006.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta234.mxroute.com (ZoneMTA) with ESMTPSA id 18b80d60655000190b.001 for <64855@debbugs.gnu.org> (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Mon, 30 Oct 2023 13:46:31 +0000 X-Zone-Loop: 1e6f6bcbc27f5fda24c42ad34dbbd215e9a22a744f57 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=Wh06acddRaiyoPAEkLJXZlYaM0X9KnPMl94MiaIQoxQ=; b=WVZiDWrl6hAYIOgWAgswnfWaT2 F6bm5Wp45ZO53zAT7LTMbnRSkYZyA0ZCAKQa6oXIMOk0OFdOukjJiFIEWv43meJ/GKlgVSLmgo9fp GL5X3tcsvWSsYd2/nYYssZXILNTjZj9n0xLd8k9YiL/iDRMarMjD+AD8ZIw1eCwdwObbqGHdYuupA yPXil4txKxp1GVHkFexifCFz8I9ZnRvNkPuZrEK4cK6+E27ErexN3xmdZXtD4R+hhuYQlo30yGgyj ha1LI0RRw6Cx9DF9BfKzoR/jPrkbBAOEwLrYjQD8Fcwt9DK9oeyV3HxXPqlvu25tEfPwVhBMqzjO3 xEKe193Q==; From: "J.P." To: 64855@debbugs.gnu.org Subject: Re: bug#64855: 30.0.50; ERC 5.6: Make scrolltobottom less erratic In-Reply-To: <87r0lja1lw.fsf@neverwas.me> (J. P.'s message of "Tue, 24 Oct 2023 19:15:55 -0700") References: <87h6psyurb.fsf@neverwas.me> <87o7h5euo8.fsf@neverwas.me> <871qdy9hbz.fsf@neverwas.me> <87r0lja1lw.fsf@neverwas.me> Date: Mon, 30 Oct 2023 06:46:28 -0700 Message-ID: <87lebkussr.fsf@neverwas.me> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain X-Authenticated-Id: masked@neverwas.me X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 64855 Cc: emacs-erc@gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) "J.P." writes: > It seems I went a little overboard in reigning in the frequency of > scroll attempts. This has been walked back slightly in the attached > changes. > > There's also a subtle quirk that's come to light involving `point-max' > and (recenter -1) where Emacs calculates point as having drifted off > screen. We could try tackling this with something kludgey, like > decrementing point temporarily during `recenter' attempts. But that > fails if the current input ends in a newline. I've instead decided to > address this by taking a page from other Emacs libraries and setting > `scroll-step' locally to 1 if a user hasn't customized > `scroll-conservatively'. See attached. This followup was installed as e9205323e15 * Be slightly more aggressive with erc-scrolltobottom-all The bug is already closed. From unknown Wed Jun 25 02:09:23 2025 Received: (at fakecontrol) by fakecontrolmessage; To: internal_control@debbugs.gnu.org From: Debbugs Internal Request Subject: Internal Control Message-Id: bug archived. Date: Tue, 28 Nov 2023 12:24:06 +0000 User-Agent: Fakemail v42.6.9 # This is a fake control message. # # The action: # bug archived. thanks # This fakemail brought to you by your local debbugs # administrator