aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorF. Jason Park2023-05-04 00:01:11 -0700
committerF. Jason Park2023-07-13 18:45:31 -0700
commitded35c2da4da52641ec99927347cd50b736b9577 (patch)
treeb8f865ed50f352031da634170d3244d41e427bf6 /lisp
parent3c70e85d362262d096301e7663a11ca8c392f526 (diff)
downloademacs-ded35c2da4da52641ec99927347cd50b736b9577.tar.gz
emacs-ded35c2da4da52641ec99927347cd50b736b9577.zip
Add erc-status-sidebar integration to erc-speedbar
* lisp/erc/erc-speedbar.el: Require `erc-button' atop file and don't bother loading `dframe', which `speedbar' handles for us. (erc-speedbar): Explain that `nickbar' is the module for this group and library for the benefit of those who run M-x customize-group. (erc-speedbar-nicknames-window-width): New option. (erc-speedbar-hide-mode-topic): New option determining whether to hide the mode and topic. (erc-speedbar-my-nick-face): New option for determining face to use when displaying user's current nick. (erc-speedbar-browser): Call `erc-install-speedbar-variables' explicitly and remove top-level `with-eval-after-load'. (erc-speedbar-insert-target): Add parenthesized channel count after channel name in server and channel views. (erc-speedbar-expand-channel): Hide mode and topic depending on option `erc-speedbar-hide-mode-topic' and pass buffer to `erc-speedbar-insert-user'. (erc-speedbar--nick-face-function): New internal function-valued variable. (erc-speedbar--highlight-self-and-ops): New function to serve as default value for `erc-speedbar--nick-face-function'. (erc-speedbar--on-click): Dispatch `erc-nick-popup' after trimming status chars. (erc-speedbar-insert-user): Revise doc string. Call `erc-speedbar--nick-face-function' to determine face. Change token for both expansion and on-click text props. Assign `erc-speedbar--on-click' as the mouse handler for nick items. (erc-speedbar--buffer-options): Variable to override options locally in speedbar buffer. (erc-speedbar--hidden-speedbar-frame): Add variable to hold original `speedbar-frame' before spoofing by setting to selected frame containing window showing ERC buffer. (erc-speedbar--emulate-sidebar-set-window-preserve-size, erc-speedbar--status-sidebar-mode--unhook): Add function to ensure status sidebar is showing correctly and helper to unregister from hook on teardown. (erc-speedbar--emulate-sidebar): Add function to control sidebar nicknames setup. (erc-speedbar--toggle-nicknames-sidebar): Add toggle function for speedbar or emulated sidebar. (erc-speedbar--ensure): Add helper function to show speedbar if it's hidden or create one if none exists. (erc-nickbar-mode, erc-nickbar-enable, erc-nickbar-disable): Add new mini module. (erc-speedbar--dframe-controlled) Add function to overwrite `speedbar-frame-mode' as `dframe-controlled' in speedbar buffer. (erc-speedbar-toggle-nicknames-window-lock, erc-speedbar-close-nicknames-window): Add commands to close speedbar window and toggle its cyclability. (erc-speedbar--compose-nicks-face): Add helper for nicks integration. * test/lisp/erc/erc-scenarios-status-sidebar.el (erc-scenarios-status-sidebar--nickbar): New test. (Bug#63595)
Diffstat (limited to 'lisp')
-rw-r--r--lisp/erc/erc-speedbar.el287
1 files changed, 269 insertions, 18 deletions
diff --git a/lisp/erc/erc-speedbar.el b/lisp/erc/erc-speedbar.el
index a9443e0ea17..f5fbaac767d 100644
--- a/lisp/erc/erc-speedbar.el
+++ b/lisp/erc/erc-speedbar.el
@@ -32,20 +32,31 @@
32;; update-channel, update-nick, remove-nick-from-channel, ... 32;; update-channel, update-nick, remove-nick-from-channel, ...
33;; * Use indicator-strings for op/voice 33;; * Use indicator-strings for op/voice
34;; * Extract/convert face notes field from bbdb if available 34;; * Extract/convert face notes field from bbdb if available
35;; * Write tests that run in a term-mode subprocess
35;; 36;;
36;;; Code: 37;;; Code:
37 38
38(require 'erc) 39(require 'erc)
39(require 'erc-goodies) 40(require 'erc-goodies)
41(require 'erc-button)
40(require 'speedbar) 42(require 'speedbar)
41(condition-case nil (require 'dframe) (error nil))
42 43
43;;; Customization: 44;;; Customization:
44 45
45(defgroup erc-speedbar nil 46(defgroup erc-speedbar nil
46 "Integration of ERC in the Speedbar." 47 "Speedbar integration for ERC.
48To open an ERC-flavored speedbar in a separate frame, run the
49command `erc-speedbar-browser'. To use a window-based proxy
50instead, run \\[erc-nickbar-mode] in a connected ERC buffer or
51put `nickbar' in `erc-modules' before connecting. See Info
52node `(speedbar) Top' for more about the underlying integration."
47 :group 'erc) 53 :group 'erc)
48 54
55(defcustom erc-speedbar-nicknames-window-width 18
56 "Default width of the nicknames sidebar (in columns)."
57 :package-version '(ERC . "5.6") ; FIXME sync on release
58 :type 'integer)
59
49(defcustom erc-speedbar-sort-users-type 'activity 60(defcustom erc-speedbar-sort-users-type 'activity
50 "How channel nicknames are sorted. 61 "How channel nicknames are sorted.
51 62
@@ -56,6 +67,23 @@ nil - Do not sort users"
56 (const :tag "Sort users alphabetically" alphabetical) 67 (const :tag "Sort users alphabetically" alphabetical)
57 (const :tag "Do not sort users" nil))) 68 (const :tag "Do not sort users" nil)))
58 69
70(defcustom erc-speedbar-hide-mode-topic 'headerline
71 "Hide mode and topic lines."
72 :package-version '(ERC . "5.6") ; FIXME sync on release
73 :type '(choice (const :tag "Always show" nil)
74 (const :tag "Always hide" t)
75 (const :tag "Omit when headerline visible" headerline)))
76
77(defcustom erc-speedbar-my-nick-face t
78 "A face to use for your nickname.
79When the value is t, ERC uses `erc-current-nick-face' if
80`erc-match' has been loaded and `erc-my-nick-face' otherwise.
81When using the `nicks' module, you can see your nick as it
82appears to others by coordinating with the option
83`erc-nicks-skip-faces'."
84 :package-version '(ERC . "5.6") ; FIXME sync on release
85 :type '(choice face (const :tag "Current nick or own speaker face" t)))
86
59(defvar erc-speedbar-key-map nil 87(defvar erc-speedbar-key-map nil
60 "Keymap used when in erc display mode.") 88 "Keymap used when in erc display mode.")
61 89
@@ -88,10 +116,6 @@ nil - Do not sort users"
88 (looking-at "[0-9]+: *.-. "))]) 116 (looking-at "[0-9]+: *.-. "))])
89 "Additional menu-items to add to speedbar frame.") 117 "Additional menu-items to add to speedbar frame.")
90 118
91;; Make sure our special speedbar major mode is loaded
92(with-eval-after-load 'speedbar
93 (erc-install-speedbar-variables))
94
95;;; ERC hierarchy display method 119;;; ERC hierarchy display method
96;;;###autoload 120;;;###autoload
97(defun erc-speedbar-browser () 121(defun erc-speedbar-browser ()
@@ -99,6 +123,7 @@ nil - Do not sort users"
99This will add a speedbar major display mode." 123This will add a speedbar major display mode."
100 (interactive) 124 (interactive)
101 (require 'speedbar) 125 (require 'speedbar)
126 (erc-install-speedbar-variables)
102 ;; Make sure that speedbar is active 127 ;; Make sure that speedbar is active
103 (speedbar-frame-mode 1) 128 (speedbar-frame-mode 1)
104 ;; Now, throw us into Info mode on speedbar. 129 ;; Now, throw us into Info mode on speedbar.
@@ -169,12 +194,18 @@ This will add a speedbar major display mode."
169 t))))) 194 t)))))
170 195
171(defun erc-speedbar-insert-target (buffer depth) 196(defun erc-speedbar-insert-target (buffer depth)
172 (if (with-current-buffer buffer 197 (if (erc--target-channel-p (buffer-local-value 'erc--target buffer))
173 (erc-channel-p (erc-default-target))) 198 (progn
174 (speedbar-make-tag-line 199 (speedbar-make-tag-line
175 'bracket ?+ 'erc-speedbar-expand-channel buffer 200 'bracket ?+ 'erc-speedbar-expand-channel buffer
176 (buffer-name buffer) 'erc-speedbar-goto-buffer buffer nil 201 (erc--target-string (buffer-local-value 'erc--target buffer))
177 depth) 202 'erc-speedbar-goto-buffer buffer nil
203 depth)
204 (save-excursion
205 (forward-line -1)
206 (let ((table (buffer-local-value 'erc-channel-users buffer)))
207 (speedbar-add-indicator (format "(%d)" (hash-table-count table)))
208 (rx "(" (+ (any "0-9")) ")"))))
178 ;; Query target 209 ;; Query target
179 (speedbar-make-tag-line 210 (speedbar-make-tag-line
180 nil nil nil nil 211 nil nil nil nil
@@ -220,6 +251,13 @@ INDENT is the current indentation level."
220 'angle ?i nil nil 251 'angle ?i nil nil
221 (concat "Topic: " topic) nil nil nil 252 (concat "Topic: " topic) nil nil nil
222 (1+ indent))) 253 (1+ indent)))
254 (unless (pcase erc-speedbar-hide-mode-topic
255 ('nil 'show)
256 ('headerline (null erc-header-line-format)))
257 (save-excursion
258 (goto-char (point-max))
259 (forward-line (if (string= topic "") -1 -2))
260 (put-text-property (pos-bol) (point-max) 'invisible t)))
223 (let ((names (cond ((eq erc-speedbar-sort-users-type 'alphabetical) 261 (let ((names (cond ((eq erc-speedbar-sort-users-type 'alphabetical)
224 (erc-sort-channel-users-alphabetically 262 (erc-sort-channel-users-alphabetically
225 (with-current-buffer channel 263 (with-current-buffer channel
@@ -233,17 +271,52 @@ INDENT is the current indentation level."
233 (when names 271 (when names
234 (speedbar-with-writable 272 (speedbar-with-writable
235 (dolist (entry names) 273 (dolist (entry names)
236 (erc-speedbar-insert-user entry ?+ (1+ indent)))))))))) 274 (erc-speedbar-insert-user entry ?+ (1+ indent) channel)))))))))
237 ((string-search "-" text) 275 ((string-search "-" text)
238 (speedbar-change-expand-button-char ?+) 276 (speedbar-change-expand-button-char ?+)
239 (speedbar-delete-subblock indent)) 277 (speedbar-delete-subblock indent))
240 (t (error "Ooops... not sure what to do"))) 278 (t (error "Ooops... not sure what to do")))
241 (speedbar-center-buffer-smartly)) 279 (speedbar-center-buffer-smartly))
242 280
243(defun erc-speedbar-insert-user (entry exp-char indent) 281(defvar erc-speedbar--nick-face-function #'erc-speedbar--highlight-self-and-ops
282 "Function called when finding a face for fontifying nicks.
283Called with the proposed nick, the `erc-server-user', and the
284`erc-channel-user'. Should return any valid face, possibly
285composed or anonymous, or nil.")
286
287(defun erc-speedbar--highlight-self-and-ops (buffer user cuser)
288 "Highlight own nick and op'd users in the speedbar."
289 (with-current-buffer buffer
290 (if (erc-current-nick-p (erc-server-user-nickname user))
291 (pcase erc-speedbar-my-nick-face
292 ('t (if (facep 'erc-current-nick-face)
293 'erc-current-nick-face
294 'erc-my-nick-face))
295 (v v))
296 ;; FIXME overload `erc-channel-user-owner-p' and friends to
297 ;; accept an `erc-channel-user' object and replace this unrolled
298 ;; stuff with a single call to `erc-get-user-mode-prefix'.
299 (and cuser (or (erc-channel-user-owner cuser)
300 (erc-channel-user-admin cuser)
301 (erc-channel-user-op cuser)
302 (erc-channel-user-halfop cuser)
303 (erc-channel-user-voice cuser))
304 erc-button-nickname-face))))
305
306(defun erc-speedbar--on-click (nick sbtoken _indent)
307 ;; 0: finger, 1: name, 2: info, 3: buffer-name
308 (with-current-buffer (nth 3 sbtoken)
309 (erc-nick-popup (string-trim-left nick "[~&@%+]+"))))
310
311(defun erc-speedbar-insert-user (entry exp-char indent &optional buffer)
244 "Insert one user based on the channel member list ENTRY. 312 "Insert one user based on the channel member list ENTRY.
245EXP-CHAR is the expansion character to use. 313Expect EXP-CHAR to be the expansion character to use, INDENT the
246INDENT is the current indentation level." 314current indentation level, and BUFFER the associated channel or
315query buffer. Set the `speedbar-function' text property to
316`erc-speedbar--on-click', which is called with the formatted
317nick, a so-called \"token\", and the indent level. The token is
318a list of four items: the userhost, the GECOS, the current
319`erc-server-user' info slot, and the associated buffer."
247 (let* ((user (car entry)) 320 (let* ((user (car entry))
248 (cuser (cdr entry)) 321 (cuser (cdr entry))
249 (nick (erc-server-user-nickname user)) 322 (nick (erc-server-user-nickname user))
@@ -255,11 +328,12 @@ INDENT is the current indentation level."
255 (op (and cuser (erc-channel-user-op cuser))) 328 (op (and cuser (erc-channel-user-op cuser)))
256 (nick-str (concat (if op "@" "") (if voice "+" "") nick)) 329 (nick-str (concat (if op "@" "") (if voice "+" "") nick))
257 (finger (concat login (when (or login host) "@") host)) 330 (finger (concat login (when (or login host) "@") host))
258 (sbtoken (list finger name info))) 331 (sbtoken (list finger name info (buffer-name buffer))))
259 (if (or login host name info) ; we want to be expandable 332 (if (or login host name info) ; we want to be expandable
260 (speedbar-make-tag-line 333 (speedbar-make-tag-line
261 'bracket ?+ 'erc-speedbar-expand-user sbtoken 334 'bracket ?+ 'erc-speedbar-expand-user sbtoken
262 nick-str nil sbtoken nil 335 nick-str #'erc-speedbar--on-click sbtoken
336 (funcall erc-speedbar--nick-face-function buffer user cuser)
263 indent) 337 indent)
264 (when (equal exp-char ?-) 338 (when (equal exp-char ?-)
265 (forward-line -1) 339 (forward-line -1)
@@ -357,6 +431,183 @@ The INDENT level is ignored."
357 (t 431 (t
358 (message "%s" txt))))) 432 (message "%s" txt)))))
359 433
434
435;;;; Status-sidebar integration
436
437(defvar erc-track-mode)
438(defvar erc-track--switch-fallback-blockers)
439(defvar erc-status-sidebar-buffer-name)
440(declare-function erc-status-sidebar-set-window-preserve-size
441 "erc-status-sidebar" nil)
442(declare-function erc-status-sidebar-mode--unhook "erc-status-sidebar" nil)
443
444(defvar erc-speedbar--buffer-options
445 '((speedbar-update-flag . t)
446 (speedbar-use-images . nil)
447 (speedbar-hide-button-brackets-flag . t)))
448
449(defvar erc-speedbar--hidden-speedbar-frame nil)
450
451(defun erc-speedbar--emulate-sidebar-set-window-preserve-size ()
452 (let ((erc-status-sidebar-buffer-name (buffer-name speedbar-buffer))
453 (display-buffer-overriding-action
454 `(display-buffer-in-side-window
455 . ((side . right)
456 (window-width . ,erc-speedbar-nicknames-window-width)))))
457 (erc-status-sidebar-set-window-preserve-size)
458 (when-let ((window (get-buffer-window speedbar-buffer)))
459 (set-window-parameter window 'no-other-window nil)
460 (internal-show-cursor window t))))
461
462(defun erc-speedbar--status-sidebar-mode--unhook ()
463 "Remove hooks installed by `erc-status-sidebar-mode'."
464 (remove-hook 'window-configuration-change-hook
465 #'erc-speedbar--emulate-sidebar-set-window-preserve-size))
466
467(defun erc-speedbar--emulate-sidebar ()
468 (require 'erc-status-sidebar)
469 (cl-assert speedbar-frame)
470 (cl-assert (eq speedbar-buffer (current-buffer)))
471 (cl-assert (eq speedbar-frame (selected-frame)))
472 (setq erc-speedbar--hidden-speedbar-frame speedbar-frame
473 dframe-controlled #'erc-speedbar--dframe-controlled)
474 (add-hook 'window-configuration-change-hook
475 #'erc-speedbar--emulate-sidebar-set-window-preserve-size nil t)
476 (add-hook 'kill-buffer-hook
477 #'erc-speedbar--status-sidebar-mode--unhook nil t)
478 (with-current-buffer speedbar-buffer
479 (pcase-dolist (`(,var . ,val) erc-speedbar--buffer-options)
480 (set (make-local-variable var) val)))
481 (when (memq 'nicks erc-modules)
482 (with-current-buffer speedbar-buffer
483 (add-function :around (local 'erc-speedbar--nick-face-function)
484 #'erc-speedbar--compose-nicks-face))))
485
486(defun erc-speedbar--toggle-nicknames-sidebar (arg)
487 (let ((force (numberp arg)))
488 (if speedbar-buffer
489 (progn
490 (cl-assert (buffer-live-p speedbar-buffer))
491 (if (or (and force (< arg 0))
492 (and (not force) (get-buffer-window speedbar-buffer nil)))
493 (erc-speedbar-close-nicknames-window nil)
494 (when (or (not force) (>= arg 0))
495 (with-selected-frame speedbar-frame
496 (erc-speedbar--emulate-sidebar-set-window-preserve-size)))))
497 (when (or (not force) (>= arg 0))
498 (let ((speedbar-frame-parameters (backquote-list*
499 '(visibility . nil)
500 '(no-other-frame . t)
501 speedbar-frame-parameters))
502 (speedbar-after-create-hook #'erc-speedbar--emulate-sidebar))
503 (erc-speedbar-browser)
504 ;; If we put the remaining parts in the "create hook" along
505 ;; with everything else, the frame with `window-main-window'
506 ;; gets raised and steals focus if you've switched away from
507 ;; Emacs in the meantime.
508 (make-frame-invisible speedbar-frame)
509 (select-frame (setq speedbar-frame (previous-frame)))
510 (erc-speedbar--emulate-sidebar-set-window-preserve-size))))))
511
512(defun erc-speedbar--ensure (&optional force)
513 (when (or (erc-server-buffer) force)
514 (when erc-track-mode
515 (cl-pushnew '(derived-mode . speedbar-mode)
516 erc-track--switch-fallback-blockers :test #'equal))
517 (erc-speedbar--toggle-nicknames-sidebar +1)
518 (speedbar-enable-update)))
519
520;;;###autoload(autoload 'erc-nickbar-mode "erc-speedbar" nil t)
521(define-erc-module nickbar nil
522 "Show nicknames in a side window.
523When enabling, create a speedbar session if one doesn't exist and
524show its buffer in an `erc-status-sidebar' window instead of a
525separate frame. When disabling, close the window or, with a
526negative prefix arg, destroy the session.
527
528WARNING: this module may produce unwanted side effects, like the
529raising of frames or the stealing of input focus. If you witness
530such an occurrence, and can reproduce it, please file a bug
531report with \\[erc-bug]."
532 ((add-hook 'erc--setup-buffer-hook #'erc-speedbar--ensure)
533 (erc-speedbar--ensure)
534 (unless (or erc--updating-modules-p
535 (and-let* ((speedbar-buffer)
536 (win (get-buffer-window speedbar-buffer 'all-frames))
537 ((eq speedbar-frame (window-frame win))))))
538 (if speedbar-buffer
539 (erc-speedbar--ensure 'force)
540 (setq erc-nickbar-mode nil)
541 (when (derived-mode-p 'erc-mode)
542 (erc-error "Not initializing `erc-nickbar-mode' in %s"
543 (current-buffer))))))
544 ((remove-hook 'erc--setup-buffer-hook #'erc-speedbar--ensure)
545 (speedbar-disable-update)
546 (when erc-track-mode
547 (setq erc-track--switch-fallback-blockers
548 (remove '(derived-mode . speedbar-mode)
549 erc-track--switch-fallback-blockers)))
550 (erc-speedbar--toggle-nicknames-sidebar -1)
551 (when-let ((arg erc--module-toggle-prefix-arg)
552 ((numberp arg))
553 ((< arg 0)))
554 (erc-speedbar-close-nicknames-window 'kill))))
555
556(defun erc-speedbar--dframe-controlled (arg)
557 (when (and erc-speedbar--hidden-speedbar-frame (numberp arg) (< arg 0))
558 (when erc-nickbar-mode
559 (erc-nickbar-mode -1))
560 (setq speedbar-frame erc-speedbar--hidden-speedbar-frame
561 erc-speedbar--hidden-speedbar-frame nil)
562 ;; It's unknown whether leaving the frame invisible interferes
563 ;; with the upstream teardown procedure.
564 (when (display-graphic-p)
565 (make-frame-visible speedbar-frame))
566 (speedbar-frame-mode arg)
567 (when speedbar-buffer
568 (kill-buffer speedbar-buffer)
569 (setq speedbar-buffer nil))))
570
571(defun erc-speedbar-toggle-nicknames-window-lock ()
572 "Toggle whether nicknames window is selectable with \\[other-window]."
573 (interactive)
574 (unless erc-nickbar-mode
575 (user-error "`erc-nickbar-mode' inactive"))
576 (when-let ((window (get-buffer-window speedbar-buffer)))
577 (let ((val (window-parameter window 'no-other-window)))
578 (set-window-parameter window 'no-other-window (not val))
579 (message "nick-window: %s" (if val "selectable" "protected")))))
580
581(defun erc-speedbar-close-nicknames-window (kill)
582 (interactive "P")
583 (if kill
584 (with-current-buffer speedbar-buffer
585 (dframe-close-frame)
586 (cl-assert (not erc-nickbar-mode))
587 (setq erc-speedbar--hidden-speedbar-frame nil))
588 (dolist (window (get-buffer-window-list speedbar-buffer nil t))
589 (unless (frame-root-window-p window)
590 (when erc-speedbar--hidden-speedbar-frame
591 (cl-assert (not (eq (window-frame window)
592 erc-speedbar--hidden-speedbar-frame))))
593 (delete-window window)))))
594
595
596;;;; Nicks integration
597
598(declare-function erc-nicks--highlight "erc-nicks" (nickname &optional face))
599
600(defun erc-speedbar--compose-nicks-face (orig buffer user cuser)
601 (require 'erc-nicks)
602 (let ((rv (funcall orig buffer user cuser)))
603 (if-let ((nick (erc-server-user-nickname user))
604 (face (with-current-buffer buffer
605 (erc-nicks--highlight nick rv)))
606 ((not (eq face erc-button-nickname-face))))
607 (cons face (ensure-list rv))
608 rv)))
609
610
360(provide 'erc-speedbar) 611(provide 'erc-speedbar)
361;;; erc-speedbar.el ends here 612;;; erc-speedbar.el ends here
362;; 613;;