Subject: bug#59943: 30.0.50; ERC 5.5+: Add visual indicator to ERC keep-place

From: "J.P." <[email-masked]>
Date: Fri, 16 Dec 2022 06:26:31 -0800

v2. Show indicator in server buffers (kind of annoying IMO). Don't
update indicator when buffer appears in active frame.

>From 0f2350b9dee5b1c9902de7891c88d53b36d01b4d Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Fri, 16 Dec 2022 06:31:41 -0800
Subject: [PATCH 0/1] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (1):
  Add option to show visual erc-keep-place indicator

 etc/ERC-NEWS            |  10 ++++
 lisp/erc/erc-goodies.el | 117 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 125 insertions(+), 2 deletions(-)

Interdiff:
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 5b73760741..a994a37d83 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -16,10 +16,10 @@ GNU Emacs since Emacs version 22.1.
 
 ** Module 'keep-place' now offers a visual indicator.
 
-Remember your place in a target buffer a bit more easily while also
-having the freedom to roam.  Optionally sync the indicator manually if
-you've made progress but still haven't caught up to the live stream.
-See new option 'erc-keep-place-indicator'.
+Remember your place in ERC buffers a bit more easily while also having
+the freedom to look around.  Optionally sync the indicator to any
+progress made when you haven't yet caught up to the live stream.  See
+new option 'erc-keep-place-indicator' and friends.
 
 
 * Changes in ERC 5.5
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 18e9f665bd..4efb88139f 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -47,6 +47,9 @@ erc-log-p
 (declare-function erc-extract-command-from-line "erc" (line))
 (declare-function erc-beg-of-input-line "erc" nil)
 (declare-function erc-default-target "erc" nil)
+(declare-function erc-buffer-filter "erc" (predicate &optional proc))
+(declare-function pulse-available-p "pulse" nil)
+(declare-function pulse-momentary-highlight-overlay "pulse" (o &optional face))
 
 (defun erc-imenu-setup ()
   "Setup Imenu support in an ERC buffer."
@@ -194,57 +197,63 @@ erc-keep-place-indicator-arrow
 (defvar-local erc--keep-place-overlay nil
   "Overlay for option `erc-keep-place-indicator'.")
 
+;; Replace this with whatever mechanism is devised for persisting
+;; a target buffer's variables (if not limited to local modules)
+(put 'erc--keep-place-overlay 'permanent-local t)
+
 (defun erc--keep-place-on-window-configuration-change ()
-  "Maybe sync `erc--keep-place-overlay'."
+  "Maybe sync `erc--keep-place-overlay'.
+Specifically, do so unless switching to or from another window in
+the active frame."
   (when erc-keep-place-indicator-follow
-    (unless (minibuffer-window-active-p (minibuffer-window))
+    (unless (or (minibuffer-window-active-p (minibuffer-window))
+                (eq (window-old-buffer) (current-buffer)))
       (when (< (overlay-end erc--keep-place-overlay)
                (window-start)
                erc-insert-marker)
         (erc-keep-place-move (window-start))))))
 
 (defun erc--keep-place-setup-overlay ()
-  (when (erc-default-target)
+  (when erc-keep-place-indicator
     (add-hook 'window-configuration-change-hook
               #'erc--keep-place-on-window-configuration-change nil t)
-    (setq erc--keep-place-overlay (make-overlay 0 0))
-    (when (memq erc-keep-place-indicator '(t arrow))
-      (overlay-put erc--keep-place-overlay 'before-string
-                   (propertize
-                    " "
-                    'display
-                    (if (zerop (fringe-columns 'left))
-                        `((margin left-margin) ,overlay-arrow-string)
-                      '(left-fringe right-triangle
-                                    erc-keep-place-indicator-arrow)))))
-    (when (memq erc-keep-place-indicator '(t face))
-      (overlay-put erc--keep-place-overlay 'face
-                   'erc-keep-place-indicator-line))))
+    (unless erc--keep-place-overlay
+      (setq erc--keep-place-overlay (make-overlay 0 0))
+      (when (memq erc-keep-place-indicator '(t arrow))
+        (overlay-put erc--keep-place-overlay 'before-string
+                     (propertize
+                      " "
+                      'display
+                      (if (zerop (fringe-columns 'left))
+                          `((margin left-margin) ,overlay-arrow-string)
+                        '(left-fringe right-triangle
+                                      erc-keep-place-indicator-arrow)))))
+      (when (memq erc-keep-place-indicator '(t face))
+        (overlay-put erc--keep-place-overlay 'face
+                     'erc-keep-place-indicator-line)))))
 
 ;;; Keep place in unvisited channels
 (define-erc-module keep-place nil
   "Leave point above un-viewed text in other channels."
   ((add-hook 'erc-insert-pre-hook  #'erc-keep-place)
-   (when erc-keep-place-indicator
-     (if (derived-mode-p 'erc-mode)
-         (erc--keep-place-setup-overlay)
-       (add-hook 'erc-mode-hook #'erc--keep-place-setup-overlay))))
+   (add-hook 'erc-mode-hook #'erc--keep-place-setup-overlay)
+   (erc-with-all-buffers-of-server erc-server-process nil
+     (erc--keep-place-setup-overlay)))
   ((remove-hook 'erc-insert-pre-hook  #'erc-keep-place)
-   (when erc-keep-place-indicator
-     (remove-hook 'erc-mode-hook #'erc--keep-place-setup-overlay)
-     (erc-with-all-buffers-of-server erc-server-process nil
-       (when erc--keep-place-overlay
-         (delete-overlay erc--keep-place-overlay)
-         (remove-hook 'window-configuration-change-hook
-                      #'erc--keep-place-on-window-configuration-change t)
-         (kill-local-variable 'erc--keep-place-overlay))))))
+   (remove-hook 'erc-mode-hook #'erc--keep-place-setup-overlay)
+   (erc-with-all-buffers-of-server erc-server-process nil
+     (when erc--keep-place-overlay
+       (delete-overlay erc--keep-place-overlay)
+       (remove-hook 'window-configuration-change-hook
+                    #'erc--keep-place-on-window-configuration-change t)
+       (kill-local-variable 'erc--keep-place-overlay)))))
 
 (defun erc-keep-place-move (&optional pos)
-  "Move keep-place indicator to the current line."
-  (interactive "P")
+  "Move keep-place indicator to the current line or POS."
+  (interactive)
   (save-excursion
     (let ((inhibit-field-text-motion t))
-      (when (numberp pos)
+      (when pos
         (goto-char pos))
       (move-overlay erc--keep-place-overlay
                     (line-beginning-position)
@@ -254,7 +263,10 @@ erc-keep-place-goto
   "Jump to keep-place indicator."
   (interactive)
   (goto-char (overlay-start erc--keep-place-overlay))
-  (recenter (truncate (* (window-height) 0.25)) t))
+  (recenter (truncate (* (window-height) 0.25)) t)
+  (require 'pulse)
+  (when (pulse-available-p)
+    (pulse-momentary-highlight-overlay erc--keep-place-overlay)))
 
 (defun erc-keep-place (_ignored)
   "Move point away from the last line in a non-selected ERC buffer."
@@ -265,7 +277,10 @@ erc-keep-place
     (goto-char (erc-beg-of-input-line))
     (forward-line -1)
     (when erc-keep-place-indicator
-      (erc-keep-place-move))
+      (unless (or (minibuffer-window-active-p (selected-window))
+                  (and (frame-visible-p (selected-frame))
+                       (get-buffer-window (current-buffer) (selected-frame))))
+        (erc-keep-place-move)))
     ;; if `switch-to-buffer-preserve-window-point' is set,
     ;; we cannot rely on point being saved, and must commit
     ;; it to window-prev-buffers.
-- 
2.38.1

>From 0f2350b9dee5b1c9902de7891c88d53b36d01b4d Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Fri, 9 Dec 2022 22:00:59 -0800
Subject: [PATCH 1/1] Add option to show visual erc-keep-place indicator

* etc/ERC-NEWS: Create new section for version 5.6 and mention
keep-place indicator.
* lisp/erc/erc-goodies.el (erc-keep-place-indicator,
erc-keep-place-indicator-follow): New options for anchoring kept place
visually.
(erc-keep-place-indicator-line, erc-keep-place-indicator-arrow): New
faces.
(erc--keep-place-overlay): New internal local state var.
(erc--keep-place-on-window-configuration-change): New function to
subscribe to `window-configuration-change-hook' and maybe update
keep-place indicator.
(erc--keep-place-setup-overlay): New function to initialize buffer for
option `erc-keep-place-indicator'.
(erc-keep-place-enable, erc-keep-place-disable): Add setup and
teardown to support `erc-keep-place-indicator'.
(erc-keep-place-move, erc-keep-place-goto): Add new commands for
manually updating and jumping to keep-place indicator.
(erc-keep-place): Move `erc--keep-place-overlay' when applicable.
---
 etc/ERC-NEWS            |  10 ++++
 lisp/erc/erc-goodies.el | 117 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 125 insertions(+), 2 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 76439f1d06..a994a37d83 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -11,6 +11,16 @@ This file is about changes in ERC, the powerful, modular, and
 extensible IRC (Internet Relay Chat) client distributed with
 GNU Emacs since Emacs version 22.1.
 
+
+* Changes in ERC 5.6
+
+** Module 'keep-place' now offers a visual indicator.
+
+Remember your place in ERC buffers a bit more easily while also having
+the freedom to look around.  Optionally sync the indicator to any
+progress made when you haven't yet caught up to the live stream.  See
+new option 'erc-keep-place-indicator' and friends.
+
 
 * Changes in ERC 5.5
 
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 1af83b58ba..4efb88139f 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -46,6 +46,10 @@ erc-log-p
 (declare-function erc-error "erc" (&rest args))
 (declare-function erc-extract-command-from-line "erc" (line))
 (declare-function erc-beg-of-input-line "erc" nil)
+(declare-function erc-default-target "erc" nil)
+(declare-function erc-buffer-filter "erc" (predicate &optional proc))
+(declare-function pulse-available-p "pulse" nil)
+(declare-function pulse-momentary-highlight-overlay "pulse" (o &optional face))
 
 (defun erc-imenu-setup ()
   "Setup Imenu support in an ERC buffer."
@@ -154,11 +158,115 @@ erc-move-to-prompt-setup
   "Initialize the move-to-prompt module."
   (add-hook 'pre-command-hook #'erc-move-to-prompt nil t))
 
+(defcustom erc-keep-place-indicator nil
+  "Show kept place with visual indicator in target buffers.
+For use with the `keep-place' module.  A value of `arrow'
+displays an arrow in the left fringe or margin.  A value of
+`face' applies `erc-keep-place-indicator-line' to the appropriate
+line.  A value of t does both.  A value of nil does neither."
+  :group 'erc
+  :package-version '(ERC . "5.6")
+  :type '(choice (const nil) (const t) (const face) (const arrow)))
+
+(defcustom erc-keep-place-indicator-follow nil
+  "Whether to sync visual kept place to window's top when reading."
+  :group 'erc
+  :package-version '(ERC . "5.6")
+  :type 'boolean)
+
+(defface erc-keep-place-indicator-line
+  '((((class color) (min-colors 88) (background light)
+      (supports :underline (:style wave)))
+     (:underline (:color "PaleGreen3" :style wave)))
+    (((class color) (min-colors 88) (background dark)
+      (supports :underline (:style wave)))
+     (:underline (:color "PaleGreen1" :style wave)))
+    (t :underline t))
+  "Face for option `erc-keep-place-indicator'."
+  :group 'erc-faces)
+
+(defface erc-keep-place-indicator-arrow
+  '((((class color) (min-colors 88) (background light))
+     (:foreground "PaleGreen3"))
+    (((class color) (min-colors 88) (background dark))
+     (:foreground "PaleGreen1"))
+    (t :inherit fringe))
+  "Face for arrow value of option `erc-keep-place-indicator'."
+  :group 'erc-faces)
+
+(defvar-local erc--keep-place-overlay nil
+  "Overlay for option `erc-keep-place-indicator'.")
+
+;; Replace this with whatever mechanism is devised for persisting
+;; a target buffer's variables (if not limited to local modules)
+(put 'erc--keep-place-overlay 'permanent-local t)
+
+(defun erc--keep-place-on-window-configuration-change ()
+  "Maybe sync `erc--keep-place-overlay'.
+Specifically, do so unless switching to or from another window in
+the active frame."
+  (when erc-keep-place-indicator-follow
+    (unless (or (minibuffer-window-active-p (minibuffer-window))
+                (eq (window-old-buffer) (current-buffer)))
+      (when (< (overlay-end erc--keep-place-overlay)
+               (window-start)
+               erc-insert-marker)
+        (erc-keep-place-move (window-start))))))
+
+(defun erc--keep-place-setup-overlay ()
+  (when erc-keep-place-indicator
+    (add-hook 'window-configuration-change-hook
+              #'erc--keep-place-on-window-configuration-change nil t)
+    (unless erc--keep-place-overlay
+      (setq erc--keep-place-overlay (make-overlay 0 0))
+      (when (memq erc-keep-place-indicator '(t arrow))
+        (overlay-put erc--keep-place-overlay 'before-string
+                     (propertize
+                      " "
+                      'display
+                      (if (zerop (fringe-columns 'left))
+                          `((margin left-margin) ,overlay-arrow-string)
+                        '(left-fringe right-triangle
+                                      erc-keep-place-indicator-arrow)))))
+      (when (memq erc-keep-place-indicator '(t face))
+        (overlay-put erc--keep-place-overlay 'face
+                     'erc-keep-place-indicator-line)))))
+
 ;;; Keep place in unvisited channels
 (define-erc-module keep-place nil
   "Leave point above un-viewed text in other channels."
-  ((add-hook 'erc-insert-pre-hook  #'erc-keep-place))
-  ((remove-hook 'erc-insert-pre-hook  #'erc-keep-place)))
+  ((add-hook 'erc-insert-pre-hook  #'erc-keep-place)
+   (add-hook 'erc-mode-hook #'erc--keep-place-setup-overlay)
+   (erc-with-all-buffers-of-server erc-server-process nil
+     (erc--keep-place-setup-overlay)))
+  ((remove-hook 'erc-insert-pre-hook  #'erc-keep-place)
+   (remove-hook 'erc-mode-hook #'erc--keep-place-setup-overlay)
+   (erc-with-all-buffers-of-server erc-server-process nil
+     (when erc--keep-place-overlay
+       (delete-overlay erc--keep-place-overlay)
+       (remove-hook 'window-configuration-change-hook
+                    #'erc--keep-place-on-window-configuration-change t)
+       (kill-local-variable 'erc--keep-place-overlay)))))
+
+(defun erc-keep-place-move (&optional pos)
+  "Move keep-place indicator to the current line or POS."
+  (interactive)
+  (save-excursion
+    (let ((inhibit-field-text-motion t))
+      (when pos
+        (goto-char pos))
+      (move-overlay erc--keep-place-overlay
+                    (line-beginning-position)
+                    (line-end-position)))))
+
+(defun erc-keep-place-goto ()
+  "Jump to keep-place indicator."
+  (interactive)
+  (goto-char (overlay-start erc--keep-place-overlay))
+  (recenter (truncate (* (window-height) 0.25)) t)
+  (require 'pulse)
+  (when (pulse-available-p)
+    (pulse-momentary-highlight-overlay erc--keep-place-overlay)))
 
 (defun erc-keep-place (_ignored)
   "Move point away from the last line in a non-selected ERC buffer."
@@ -168,6 +276,11 @@ erc-keep-place
     (deactivate-mark)
     (goto-char (erc-beg-of-input-line))
     (forward-line -1)
+    (when erc-keep-place-indicator
+      (unless (or (minibuffer-window-active-p (selected-window))
+                  (and (frame-visible-p (selected-frame))
+                       (get-buffer-window (current-buffer) (selected-frame))))
+        (erc-keep-place-move)))
     ;; if `switch-to-buffer-preserve-window-point' is set,
     ;; we cannot rely on point being saved, and must commit
     ;; it to window-prev-buffers.
-- 
2.38.1