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

From: "J.P." <[email-masked]>
Date: Sat, 10 Dec 2022 07:52:14 -0800

Tags: patch

Hi people,

There's been some recent talk regarding ERC's lack of a visual element
to demarcate the boundary between seen and unseen messages in a target
buffer. This approach leverages the existing `keep-place' module located
in erc-goodies. (But perhaps there's a smarter way?) Improvements or
replacements welcome, as always.

Thanks,
J.P.

In GNU Emacs 30.0.50 (build 2, x86_64-pc-linux-gnu, GTK+ Version
 3.24.35, cairo version 1.17.6) of 2022-12-10 built on localhost
Repository revision: 9c670695b91deb3424a74cd2b0f6a37356a4efcc
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: Fedora Linux 36 (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 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 iso8601 time-date
auth-source cl-seq eieio eieio-core cl-macs password-cache json subr-x
map thingatpt pp format-spec cl-loaddefs cl-lib erc-backend erc-goodies
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 move-toolbar gtk x-toolkit xinput2 x multi-tty
make-network-process emacs)

Memory information:
((conses 16 64369 5963)
 (symbols 48 8607 0)
 (strings 32 23642 2119)
 (string-bytes 1 684025)
 (vectors 16 15217)
 (vector-slots 8 208839 8628)
 (floats 8 24 27)
 (intervals 56 227 0)
 (buffers 984 11))

>From 685e151fa4565970683cd76eb111afd308d1b531 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Fri, 9 Dec 2022 22:00:59 -0800
Subject: [PATCH] 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 | 102 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index d0d84d0a98..af1cfcacea 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 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'.
+
 
 * Changes in ERC 5.5
 
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 1af83b58ba..18e9f665bd 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -46,6 +46,7 @@ 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)
 
 (defun erc-imenu-setup ()
   "Setup Imenu support in an ERC buffer."
@@ -154,11 +155,106 @@ 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'.")
+
+(defun erc--keep-place-on-window-configuration-change ()
+  "Maybe sync `erc--keep-place-overlay'."
+  (when erc-keep-place-indicator-follow
+    (unless (minibuffer-window-active-p (minibuffer-window))
+      (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)
+    (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))))
+
 ;;; 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)
+   (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))))
+  ((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))))))
+
+(defun erc-keep-place-move (&optional pos)
+  "Move keep-place indicator to the current line."
+  (interactive "P")
+  (save-excursion
+    (let ((inhibit-field-text-motion t))
+      (when (numberp 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))
 
 (defun erc-keep-place (_ignored)
   "Move point away from the last line in a non-selected ERC buffer."
@@ -168,6 +264,8 @@ erc-keep-place
     (deactivate-mark)
     (goto-char (erc-beg-of-input-line))
     (forward-line -1)
+    (when erc-keep-place-indicator
+      (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