aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorF. Jason Park2022-12-09 22:00:59 -0800
committerF. Jason Park2023-04-08 14:23:51 -0700
commite7992d2adbc50ba8a3b0fb18b9afe22a2a539b1d (patch)
treee61cf2ee00dd7d568326a366e81ac1f0a00ea903
parentba7fe88b782ad516b4cbb5e99fb108f57a9235e2 (diff)
downloademacs-e7992d2adbc50ba8a3b0fb18b9afe22a2a539b1d.tar.gz
emacs-e7992d2adbc50ba8a3b0fb18b9afe22a2a539b1d.zip
Add option to show visual erc-keep-place indicator
* lisp/erc/erc-goodies.el (erc-keep-place-indicator-style, erc-keep-place-indicator-buffer-type, 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-indicator-overlay): New internal variable. (erc--keep-place-indicator-on-window-configuration-change): New function to subscribe to `window-configuration-change-hook' and maybe update kept-place indicator. (erc--keep-place-indicator-setup): New function to initialize buffer for local module `keep-place-indicator'. (erc-keep-place-indicator-mode, erc-keep-place-indicator-enable, erc-keep-place-indicator-disable): New local ERC module. Depends on "parent" module `keep-place'. Like `fill-wrap', this is (for now) also deliberately left out of the widget menu for `erc-modules'. (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. * test/lisp/erc/erc-goodies-tests.el (erc-keep-place-indicator-mode): Add test. (Bug#59943.)
-rw-r--r--lisp/erc/erc-goodies.el157
-rw-r--r--test/lisp/erc/erc-goodies-tests.el81
2 files changed, 238 insertions, 0 deletions
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 7ea6c42ec65..6235de5f1c0 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -32,6 +32,10 @@
32(eval-when-compile (require 'cl-lib)) 32(eval-when-compile (require 'cl-lib))
33(require 'erc) 33(require 'erc)
34 34
35(declare-function fringe-columns "fringe" (side &optional real))
36(declare-function pulse-available-p "pulse" nil)
37(declare-function pulse-momentary-highlight-overlay "pulse" (o &optional face))
38
35 39
36;;; Automatically scroll to bottom 40;;; Automatically scroll to bottom
37(defcustom erc-input-line-position nil 41(defcustom erc-input-line-position nil
@@ -143,6 +147,154 @@ Put this function on `erc-insert-post-hook' and/or `erc-send-post-hook'."
143 ((add-hook 'erc-insert-pre-hook #'erc-keep-place)) 147 ((add-hook 'erc-insert-pre-hook #'erc-keep-place))
144 ((remove-hook 'erc-insert-pre-hook #'erc-keep-place))) 148 ((remove-hook 'erc-insert-pre-hook #'erc-keep-place)))
145 149
150(defcustom erc-keep-place-indicator-style t
151 "Flavor of visual indicator applied to kept place.
152For use with the `keep-place-indicator' module. A value of `arrow'
153displays an arrow in the left fringe or margin. When it's
154`face', ERC adds the face `erc-keep-place-indicator-line' to the
155appropriate line. A value of t does both."
156 :group 'erc
157 :package-version '(ERC . "5.6")
158 :type '(choice (const t) (const server) (const target)))
159
160(defcustom erc-keep-place-indicator-buffer-type t
161 "ERC buffer type in which to display `keep-place-indicator'.
162A value of t means \"all\" ERC buffers."
163 :group 'erc
164 :package-version '(ERC . "5.6")
165 :type '(choice (const t) (const server) (const target)))
166
167(defcustom erc-keep-place-indicator-follow nil
168 "Whether to sync visual kept place to window's top when reading.
169For use with `erc-keep-place-indicator-mode'."
170 :group 'erc
171 :package-version '(ERC . "5.6")
172 :type 'boolean)
173
174(defface erc-keep-place-indicator-line
175 '((((class color) (min-colors 88) (background light)
176 (supports :underline (:style wave)))
177 (:underline (:color "PaleGreen3" :style wave)))
178 (((class color) (min-colors 88) (background dark)
179 (supports :underline (:style wave)))
180 (:underline (:color "PaleGreen1" :style wave)))
181 (t :underline t))
182 "Face for option `erc-keep-place-indicator-style'."
183 :group 'erc-faces)
184
185(defface erc-keep-place-indicator-arrow
186 '((((class color) (min-colors 88) (background light))
187 (:foreground "PaleGreen3"))
188 (((class color) (min-colors 88) (background dark))
189 (:foreground "PaleGreen1"))
190 (t :inherit fringe))
191 "Face for arrow value of option `erc-keep-place-indicator-style'."
192 :group 'erc-faces)
193
194(defvar-local erc--keep-place-indicator-overlay nil
195 "Overlay for `erc-keep-place-indicator-mode'.")
196
197(defun erc--keep-place-indicator-on-window-configuration-change ()
198 "Maybe sync `erc--keep-place-indicator-overlay'.
199Specifically, do so unless switching to or from another window in
200the active frame."
201 (when erc-keep-place-indicator-follow
202 (unless (or (minibuffer-window-active-p (minibuffer-window))
203 (eq (window-old-buffer) (current-buffer)))
204 (when (< (overlay-end erc--keep-place-indicator-overlay)
205 (window-start)
206 erc-insert-marker)
207 (erc-keep-place-move (window-start))))))
208
209(defun erc--keep-place-indicator-setup ()
210 "Initialize buffer for maintaining `erc--keep-place-indicator-overlay'."
211 (require 'fringe)
212 (setq erc--keep-place-indicator-overlay
213 (if-let* ((vars (or erc--server-reconnecting erc--target-priors))
214 ((alist-get 'erc-keep-place-indicator-mode vars)))
215 (alist-get 'erc--keep-place-indicator-overlay vars)
216 (make-overlay 0 0)))
217 (add-hook 'window-configuration-change-hook
218 #'erc--keep-place-indicator-on-window-configuration-change nil t)
219 (when-let* (((memq erc-keep-place-indicator-style '(t arrow)))
220 (display (if (zerop (fringe-columns 'left))
221 `((margin left-margin) ,overlay-arrow-string)
222 '(left-fringe right-triangle
223 erc-keep-place-indicator-arrow)))
224 (bef (propertize " " 'display display)))
225 (overlay-put erc--keep-place-indicator-overlay 'before-string bef))
226 (when (memq erc-keep-place-indicator-style '(t face))
227 (overlay-put erc--keep-place-indicator-overlay 'face
228 'erc-keep-place-indicator-line)))
229
230;;;###autoload(put 'keep-place-indicator 'erc--feature 'erc-goodies)
231(define-erc-module keep-place-indicator nil
232 "`keep-place' with a fringe arrow and/or highlighted face."
233 ((unless erc-keep-place-mode
234 (unless (memq 'keep-place erc-modules)
235 ;; FIXME use `erc-button--display-error-notice-with-keys'
236 ;; to display this message when bug#60933 is ready.
237 (erc-display-error-notice
238 nil (concat
239 "Local module `keep-place-indicator' needs module `keep-place'."
240 " Enabling now. This will affect \C-]all\C-] ERC sessions."
241 " Add `keep-place' to `erc-modules' to silence this message.")))
242 (erc-keep-place-mode +1))
243 (if (pcase erc-keep-place-indicator-buffer-type
244 ('target erc--target)
245 ('server (not erc--target))
246 ('t t))
247 (erc--keep-place-indicator-setup)
248 (setq erc-keep-place-indicator-mode nil)))
249 ((when erc--keep-place-indicator-overlay
250 (delete-overlay erc--keep-place-indicator-overlay)
251 (remove-hook 'window-configuration-change-hook
252 #'erc--keep-place-indicator-on-window-configuration-change t)
253 (kill-local-variable 'erc--keep-place-indicator-overlay)))
254 'local)
255
256(defun erc-keep-place-move (pos)
257 "Move keep-place indicator to current line or POS.
258For use with `keep-place-indicator' module. When called
259interactively, interpret POS as an offset. Specifically, when
260POS is a raw prefix arg, like (4), move the indicator to the
261window's last line. When it's the minus sign, put it on the
262window's first line. Interpret an integer as an offset in lines."
263 (interactive
264 (progn
265 (unless erc-keep-place-indicator-mode
266 (user-error "`erc-keep-place-indicator-mode' not enabled"))
267 (list (pcase current-prefix-arg
268 ((and (pred integerp) v)
269 (save-excursion
270 (let ((inhibit-field-text-motion t))
271 (forward-line v)
272 (point))))
273 (`(,_) (1- (min erc-insert-marker (window-end))))
274 ('- (min (1- erc-insert-marker) (window-start)))))))
275 (save-excursion
276 (let ((inhibit-field-text-motion t))
277 (when pos
278 (goto-char pos))
279 (move-overlay erc--keep-place-indicator-overlay
280 (line-beginning-position)
281 (line-end-position)))))
282
283(defun erc-keep-place-goto ()
284 "Jump to keep-place indicator.
285For use with `keep-place-indicator' module."
286 (interactive
287 (prog1 nil
288 (unless erc-keep-place-indicator-mode
289 (user-error "`erc-keep-place-indicator-mode' not enabled"))
290 (deactivate-mark)
291 (push-mark)))
292 (goto-char (overlay-start erc--keep-place-indicator-overlay))
293 (recenter (truncate (* (window-height) 0.25)) t)
294 (require 'pulse)
295 (when (pulse-available-p)
296 (pulse-momentary-highlight-overlay erc--keep-place-indicator-overlay)))
297
146(defun erc-keep-place (_ignored) 298(defun erc-keep-place (_ignored)
147 "Move point away from the last line in a non-selected ERC buffer." 299 "Move point away from the last line in a non-selected ERC buffer."
148 (when (and (not (eq (window-buffer (selected-window)) 300 (when (and (not (eq (window-buffer (selected-window))
@@ -151,6 +303,11 @@ Put this function on `erc-insert-post-hook' and/or `erc-send-post-hook'."
151 (deactivate-mark) 303 (deactivate-mark)
152 (goto-char (erc-beg-of-input-line)) 304 (goto-char (erc-beg-of-input-line))
153 (forward-line -1) 305 (forward-line -1)
306 (when erc-keep-place-indicator-mode
307 (unless (or (minibuffer-window-active-p (selected-window))
308 (and (frame-visible-p (selected-frame))
309 (get-buffer-window (current-buffer) (selected-frame))))
310 (erc-keep-place-move nil)))
154 ;; if `switch-to-buffer-preserve-window-point' is set, 311 ;; if `switch-to-buffer-preserve-window-point' is set,
155 ;; we cannot rely on point being saved, and must commit 312 ;; we cannot rely on point being saved, and must commit
156 ;; it to window-prev-buffers. 313 ;; it to window-prev-buffers.
diff --git a/test/lisp/erc/erc-goodies-tests.el b/test/lisp/erc/erc-goodies-tests.el
index 46fcf82401b..a1f53c5bf88 100644
--- a/test/lisp/erc/erc-goodies-tests.el
+++ b/test/lisp/erc/erc-goodies-tests.el
@@ -250,4 +250,85 @@
250 (when noninteractive 250 (when noninteractive
251 (kill-buffer))))) 251 (kill-buffer)))))
252 252
253
254;; Among other things, this test also asserts that a local module's
255;; minor-mode toggle is allowed to disable its mode variable as
256;; needed.
257
258(ert-deftest erc-keep-place-indicator-mode ()
259 ;; FIXME remove after adding
260 (unless (fboundp 'erc--initialize-markers)
261 (ert-skip "Missing required function"))
262 (with-current-buffer (get-buffer-create "*erc-keep-place-indicator-mode*")
263 (erc-mode)
264 (erc--initialize-markers (point) nil)
265 (let ((assert-off
266 (lambda ()
267 (should-not erc-keep-place-indicator-mode)
268 (should-not (local-variable-p 'window-configuration-change-hook))
269 (should-not erc--keep-place-indicator-overlay)))
270 (assert-on
271 (lambda ()
272 (should erc--keep-place-indicator-overlay)
273 (should (local-variable-p 'window-configuration-change-hook))
274 (should window-configuration-change-hook)
275 (should erc-keep-place-mode)))
276 ;;
277 erc-insert-pre-hook
278 erc-modules)
279
280 (funcall assert-off)
281
282 (ert-info ("Value t")
283 (should (eq erc-keep-place-indicator-buffer-type t))
284 (erc-keep-place-indicator-mode +1)
285 (funcall assert-on)
286 (goto-char (point-min))
287 (should (search-forward "Enabling" nil t))
288 (should (memq 'keep-place erc-modules)))
289
290 (erc-keep-place-indicator-mode -1)
291 (funcall assert-off)
292
293 (ert-info ("Value `target'")
294 (let ((erc-keep-place-indicator-buffer-type 'target))
295 (erc-keep-place-indicator-mode +1)
296 (funcall assert-off)
297 (setq erc--target (erc--target-from-string "#chan"))
298 (erc-keep-place-indicator-mode +1)
299 (funcall assert-on)))
300
301 (erc-keep-place-indicator-mode -1)
302 (funcall assert-off)
303
304 (ert-info ("Value `server'")
305 (let ((erc-keep-place-indicator-buffer-type 'server))
306 (erc-keep-place-indicator-mode +1)
307 (funcall assert-off)
308 (setq erc--target nil)
309 (erc-keep-place-indicator-mode +1)
310 (funcall assert-on)))
311
312 ;; Populate buffer
313 (erc-display-message nil 'notice (current-buffer)
314 "This buffer is for text that is not saved")
315 (erc-display-message nil 'notice (current-buffer)
316 "and for lisp evaluation")
317 (should (search-forward "saved" nil t))
318 (erc-keep-place-move nil)
319 (goto-char erc-input-marker)
320
321 (ert-info ("Indicator survives reconnect")
322 (let ((erc--server-reconnecting (buffer-local-variables)))
323 (cl-letf (((symbol-function 'erc-server-connect) #'ignore))
324 (erc-open "localhost" 6667 "tester" "Tester" 'connect
325 nil nil nil nil nil "tester" nil)))
326 (funcall assert-on)
327 (should (= (point) erc-input-marker))
328 (goto-char (overlay-start erc--keep-place-indicator-overlay))
329 (should (looking-at (rx "*** This buffer is for text")))))
330
331 (when noninteractive
332 (kill-buffer))))
333
253;;; erc-goodies-tests.el ends here 334;;; erc-goodies-tests.el ends here