aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorF. Jason Park2022-05-21 03:04:04 -0700
committerF. Jason Park2023-04-08 14:23:51 -0700
commit0e4c07dc7448aafd2aa5f6e101d7b7aac23d8a6b (patch)
treefd847c1ff8426dbadfe88831c833264c305c9a06
parentc104e90888a03b4879cd91bf5d130288ac880d66 (diff)
downloademacs-0e4c07dc7448aafd2aa5f6e101d7b7aac23d8a6b.tar.gz
emacs-0e4c07dc7448aafd2aa5f6e101d7b7aac23d8a6b.zip
Allow erc-reuse-frames to favor connections
* lisp/erc/erc.el (erc-reuse-frames): Add alternate value to favor existing frames already displaying buffers from the same connection. (erc--setup-buffer-first-window, erc--display-buffer-use-some-frame): Add helpers to support 'display' variant of `erc-resuse-frames' * test/lisp/erc/erc-tests.el (erc-tests--run-in-term, erc-tests--servars, erc-reuse-frames, erc-tests--erc-reuse-frames, erc-tests--erc-reuse-frames--t, erc-resuse-frames--t, erc-tests--erc-reuse-frames--displayed-single, erc-reuse-frames--displayed-single, erc-tests--assert-server-split, erc-tests--erc-reuse-frames--displayed-double, erc-reuse-frames--displayed-double, erc-tests--erc-reuse-frames--displayed-full, erc-reuse-frames--displayed-full): Add test case and supporting fixtures. (Bug#55540.)
-rw-r--r--lisp/erc/erc.el61
-rw-r--r--test/lisp/erc/erc-tests.el303
2 files changed, 357 insertions, 7 deletions
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 5aa460241cd..284990e2d43 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1573,11 +1573,23 @@ This only has effect when `erc-join-buffer' is set to `frame'."
1573 1573
1574(defcustom erc-reuse-frames t 1574(defcustom erc-reuse-frames t
1575 "Determines whether new frames are always created. 1575 "Determines whether new frames are always created.
1576Non-nil means that a new frame is not created to display an ERC 1576
1577buffer if there is already a window displaying it. This only has 1577A value of t means only create a frame for undisplayed buffers.
1578effect when `erc-join-buffer' is set to `frame'." 1578`displayed' means use any existing, potentially hidden frame
1579already displaying a buffer from the same network context or,
1580failing that, a frame showing any ERC buffer. As a last resort,
1581`displayed' defaults to the selected frame, except for brand new
1582connections, for which the invoking frame is always used. When
1583this option is nil, a new frame is always created.
1584
1585Regardless of its value, this option is ignored unless
1586`erc-join-buffer' is set to `frame'. And like most options in
1587the `erc-buffer' customize group, this has no effect on server
1588buffers while reconnecting because those are always buried."
1589 :package-version '(ERC . "5.6") ; FIXME sync on release
1579 :group 'erc-buffers 1590 :group 'erc-buffers
1580 :type 'boolean) 1591 :type '(choice boolean
1592 (const displayed)))
1581 1593
1582(defun erc-channel-p (channel) 1594(defun erc-channel-p (channel)
1583 "Return non-nil if CHANNEL seems to be an IRC channel name." 1595 "Return non-nil if CHANNEL seems to be an IRC channel name."
@@ -2003,6 +2015,35 @@ Except ignore all local modules, which were introduced in ERC 5.5."
2003 (push mode local-modes)) 2015 (push mode local-modes))
2004 (error "`%s' is not a known ERC module" module))))) 2016 (error "`%s' is not a known ERC module" module)))))
2005 2017
2018(defun erc--setup-buffer-first-window (frame a b)
2019 (catch 'found
2020 (walk-window-tree
2021 (lambda (w)
2022 (when (cond ((functionp a) (with-current-buffer (window-buffer w)
2023 (funcall a b)))
2024 (t (eq (buffer-local-value a (window-buffer w)) b)))
2025 (throw 'found t)))
2026 frame nil 0)))
2027
2028(defun erc--display-buffer-use-some-frame (buffer alist)
2029 "Maybe display BUFFER in an existing frame for the same connection.
2030If performed, return window used; otherwise, return nil. Forward ALIST
2031to display-buffer machinery."
2032 (when-let*
2033 ((idp (lambda (value)
2034 (and erc-networks--id
2035 (erc-networks--id-equal-p erc-networks--id value))))
2036 (procp (lambda (frame)
2037 (erc--setup-buffer-first-window frame idp erc-networks--id)))
2038 (ercp (lambda (frame)
2039 (erc--setup-buffer-first-window frame 'major-mode 'erc-mode)))
2040 ((or (cdr (frame-list)) (funcall ercp (selected-frame)))))
2041 ;; Workaround to avoid calling `window--display-buffer' directly
2042 (or (display-buffer-use-some-frame buffer
2043 `((frame-predicate . ,procp) ,@alist))
2044 (display-buffer-use-some-frame buffer
2045 `((frame-predicate . ,ercp) ,@alist)))))
2046
2006(defun erc-setup-buffer (buffer) 2047(defun erc-setup-buffer (buffer)
2007 "Consults `erc-join-buffer' to find out how to display `BUFFER'." 2048 "Consults `erc-join-buffer' to find out how to display `BUFFER'."
2008 (pcase (if (zerop (erc-with-server-buffer 2049 (pcase (if (zerop (erc-with-server-buffer
@@ -2018,15 +2059,21 @@ Except ignore all local modules, which were introduced in ERC 5.5."
2018 ('bury 2059 ('bury
2019 nil) 2060 nil)
2020 ('frame 2061 ('frame
2021 (when (or (not erc-reuse-frames) 2062 (cond
2022 (not (get-buffer-window buffer t))) 2063 ((and (eq erc-reuse-frames 'displayed)
2064 (not (get-buffer-window buffer t)))
2065 (display-buffer buffer '((erc--display-buffer-use-some-frame)
2066 (inhibit-switch-frame . t)
2067 (inhibit-same-window . t))))
2068 ((or (not erc-reuse-frames)
2069 (not (get-buffer-window buffer t)))
2023 (let ((frame (make-frame (or erc-frame-alist 2070 (let ((frame (make-frame (or erc-frame-alist
2024 default-frame-alist)))) 2071 default-frame-alist))))
2025 (raise-frame frame) 2072 (raise-frame frame)
2026 (select-frame frame)) 2073 (select-frame frame))
2027 (switch-to-buffer buffer) 2074 (switch-to-buffer buffer)
2028 (when erc-frame-dedicated-flag 2075 (when erc-frame-dedicated-flag
2029 (set-window-dedicated-p (selected-window) t)))) 2076 (set-window-dedicated-p (selected-window) t)))))
2030 (_ 2077 (_
2031 (if (active-minibuffer-window) 2078 (if (active-minibuffer-window)
2032 (display-buffer buffer) 2079 (display-buffer buffer)
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 43a5b54dcc7..29bda7e742d 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -399,6 +399,309 @@
399 (dolist (b '("server" "other" "#chan" "#foo" "#fake")) 399 (dolist (b '("server" "other" "#chan" "#foo" "#fake"))
400 (kill-buffer b)))) 400 (kill-buffer b))))
401 401
402(defun erc-tests--run-in-term (&optional debug)
403 (let* ((default-directory (getenv "EMACS_TEST_DIRECTORY"))
404 (emacs (expand-file-name invocation-name invocation-directory))
405 (process-environment (cons "ERC_TESTS_SUBPROCESS=1"
406 process-environment))
407 (name (ert-test-name (ert-running-test)))
408 (temp-file (make-temp-file "erc-term-test-"))
409 (cmd `(let ((stats 1))
410 (setq enable-dir-local-variables nil)
411 (unwind-protect
412 (setq stats (ert-run-tests-batch ',name))
413 (unless ',debug
414 (let ((buf (with-current-buffer (messages-buffer)
415 (buffer-string))))
416 (with-temp-file ,temp-file
417 (insert buf)))
418 (kill-emacs (ert-stats-completed-unexpected stats))))))
419 ;; `ert-test' object in Emacs 29 has a `file-name' field
420 (file-name (symbol-file name 'ert--test))
421 (default-directory (expand-file-name (file-name-directory file-name)))
422 (package (if-let* ((found (getenv "ERC_PACKAGE_NAME"))
423 ((string-prefix-p "erc-" found)))
424 (intern found)
425 'erc))
426 (setup (and (featurep 'compat)
427 `(progn
428 (require 'package)
429 (let ((package-load-list '((compat t) (,package t))))
430 (package-initialize)))))
431 ;; Make subprocess terminal bigger than controlling.
432 (buf (cl-letf (((symbol-function 'window-screen-lines)
433 (lambda () 20))
434 ((symbol-function 'window-max-chars-per-line)
435 (lambda () 40)))
436 (make-term (symbol-name name) emacs nil "-Q" "-nw"
437 "-eval" (prin1-to-string setup)
438 "-l" file-name "-eval" (format "%S" cmd))))
439 (proc (get-buffer-process buf))
440 (err (lambda ()
441 (with-temp-buffer
442 (insert-file-contents temp-file)
443 (message "Subprocess: %s" (buffer-string))
444 (delete-file temp-file)))))
445 (with-current-buffer buf
446 (set-process-query-on-exit-flag proc nil)
447 (with-timeout (10 (funcall err) (error "Timed out awaiting result"))
448 (while (process-live-p proc)
449 (accept-process-output proc 0.1)))
450 (while (accept-process-output proc))
451 (goto-char (point-min))
452 ;; Otherwise gives process exited abnormally with exit-code >0
453 (unless (search-forward (format "Process %s finished" name) nil t)
454 (funcall err)
455 (ert-fail (when (search-forward "exited" nil t)
456 (buffer-substring-no-properties (line-beginning-position)
457 (line-end-position)))))
458 (delete-file temp-file)
459 (when noninteractive
460 (kill-buffer)))))
461
462(defun erc-tests--servars (source &rest vars)
463 (unless (bufferp source)
464 (setq source (get-buffer source)))
465 (dolist (var vars)
466 (should (local-variable-if-set-p var))
467 (set var (buffer-local-value var source))))
468
469(defun erc-tests--erc-reuse-frames (test &optional debug)
470 (if (and (or debug noninteractive) (not (getenv "ERC_TESTS_SUBPROCESS")))
471 (progn
472 (when (memq system-type '(windows-nt ms-dos))
473 (ert-skip "System must be UNIX"))
474 (erc-tests--run-in-term debug))
475 (should-not erc-frame-dedicated-flag)
476 (should (eq erc-reuse-frames t))
477 (let ((erc-join-buffer 'frame)
478 (erc-reuse-frames t)
479 (erc-frame-alist nil)
480 (orig-frame (selected-frame))
481 erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
482 (delete-other-frames)
483 (delete-other-windows)
484 (set-window-buffer (selected-window) "*scratch*")
485 (funcall test orig-frame)
486 (delete-other-frames orig-frame)
487 (delete-other-windows))))
488
489;; TODO add cases for frame-display behavior while reconnecting
490
491(defun erc-tests--erc-reuse-frames--t (_)
492 (ert-info ("New server buffer creates and raises second frame")
493 (with-current-buffer (generate-new-buffer "server")
494 (erc-mode)
495 (setq erc-server-process (start-process "server"
496 (current-buffer) "sleep" "10")
497 erc-frame-alist (cons '(name . "server") default-frame-alist)
498 erc-network 'foonet
499 erc-networks--id (erc-networks--id-create nil)
500 erc--server-last-reconnect-count 0)
501 (set-process-buffer erc-server-process (current-buffer))
502 (set-process-query-on-exit-flag erc-server-process nil)
503 (should-not (get-buffer-window (current-buffer) t))
504 (erc-setup-buffer (current-buffer))
505 (should (equal "server" (frame-parameter (window-frame) 'name)))
506 (should (get-buffer-window (current-buffer) t))))
507
508 (ert-info ("New channel creates and raises third frame")
509 (with-current-buffer (generate-new-buffer "#chan")
510 (erc-mode)
511 (erc-tests--servars "server" 'erc-server-process 'erc-networks--id
512 'erc-network)
513 (setq erc-frame-alist (cons '(name . "#chan") default-frame-alist)
514 erc-default-recipients '("#chan"))
515 (should-not (get-buffer-window (current-buffer) t))
516 (erc-setup-buffer (current-buffer))
517 (should (equal "#chan" (frame-parameter (window-frame) 'name)))
518 (should (get-buffer-window (current-buffer) t))
519 (should (cddr (frame-list))))))
520
521(ert-deftest erc-reuse-frames--t ()
522 :tags '(:unstable :expensive-test)
523 (erc-tests--erc-reuse-frames
524 (lambda (orig-frame)
525 (erc-tests--erc-reuse-frames--t orig-frame)
526 (dolist (b '("server" "#chan"))
527 (kill-buffer b)))))
528
529(defun erc-tests--erc-reuse-frames--displayed-single (_ server-name chan-name)
530
531 (should (eq erc-reuse-frames 'displayed))
532
533 (ert-info ("New server buffer shown in existing frame")
534 (with-current-buffer (generate-new-buffer server-name)
535 (erc-mode)
536 (setq erc-server-process (start-process server-name (current-buffer)
537 "sleep" "10")
538 erc-frame-alist (cons `(name . ,server-name) default-frame-alist)
539 erc-network (make-symbol server-name)
540 erc-server-current-nick "tester"
541 erc-networks--id (erc-networks--id-create nil)
542 erc--server-last-reconnect-count 0)
543 (set-process-buffer erc-server-process (current-buffer))
544 (set-process-query-on-exit-flag erc-server-process nil)
545 (should-not (get-buffer-window (current-buffer) t))
546 (erc-setup-buffer (current-buffer))
547 (should-not (equal server-name (frame-parameter (window-frame) 'name)))
548 ;; New server buffer window appears in split below ERT/scratch
549 (should (get-buffer-window (current-buffer) t))))
550
551 (ert-info ("New channel shown in existing frame")
552 (with-current-buffer (generate-new-buffer chan-name)
553 (erc-mode)
554 (erc-tests--servars server-name 'erc-server-process 'erc-networks--id
555 'erc-network)
556 (setq erc-frame-alist (cons `(name . ,chan-name) default-frame-alist)
557 erc-default-recipients (list chan-name))
558 (should-not (get-buffer-window (current-buffer) t))
559 (erc-setup-buffer (current-buffer))
560 (should-not (equal chan-name (frame-parameter (window-frame) 'name)))
561 ;; New channel buffer replaces server in lower window
562 (should (get-buffer-window (current-buffer) t))
563 (should-not (get-buffer-window server-name t)))))
564
565(ert-deftest erc-reuse-frames--displayed-single ()
566 :tags '(:unstable :expensive-test)
567 (erc-tests--erc-reuse-frames
568 (lambda (orig-frame)
569 (let ((erc-reuse-frames 'displayed))
570 (erc-tests--erc-reuse-frames--displayed-single orig-frame
571 "server" "#chan")
572 (should-not (cdr (frame-list))))
573 (dolist (b '("server" "#chan"))
574 (kill-buffer b)))))
575
576(defun erc-tests--assert-server-split (buffer-or-name frame-name)
577 ;; Assert current buffer resides on one side of a horizontal split
578 ;; in the "server" frame but is not selected.
579 (let* ((buffer-window (get-buffer-window buffer-or-name t))
580 (buffer-frame (window-frame buffer-window)))
581 (should (equal frame-name (frame-parameter buffer-frame 'name)))
582 (should (memq buffer-window (car-safe (window-tree buffer-frame))))
583 (should-not (eq buffer-window (frame-selected-window)))
584 buffer-frame))
585
586(defun erc-tests--erc-reuse-frames--displayed-double (_)
587 (should (eq erc-reuse-frames 'displayed))
588
589 (make-frame '((name . "other")))
590 (select-frame (make-frame '((name . "server"))) 'no-record)
591 (set-window-buffer (selected-window) "*scratch*") ; invokes `erc'
592
593 ;; A user invokes an entry point and switches immediately to a new
594 ;; frame before autojoin kicks in (bug#55540).
595
596 (ert-info ("New server buffer shown in selected frame")
597 (with-current-buffer (generate-new-buffer "server")
598 (erc-mode)
599 (setq erc-server-process (start-process "server" (current-buffer)
600 "sleep" "10")
601 erc-network 'foonet
602 erc-server-current-nick "tester"
603 erc-networks--id (erc-networks--id-create nil)
604 erc--server-last-reconnect-count 0)
605 (set-process-buffer erc-server-process (current-buffer))
606 (set-process-query-on-exit-flag erc-server-process nil)
607 (should-not (get-buffer-window (current-buffer) t))
608 (erc-setup-buffer (current-buffer))
609 (should (equal "server" (frame-parameter (window-frame) 'name)))
610 (should (get-buffer-window (current-buffer) t))))
611
612 (select-frame-by-name "other")
613
614 (ert-info ("New channel shown in dedicated frame")
615 (with-current-buffer (generate-new-buffer "#chan")
616 (erc-mode)
617 (erc-tests--servars "server" 'erc-server-process 'erc-networks--id
618 'erc-network)
619 (setq erc-frame-alist (cons '(name . "#chan") default-frame-alist)
620 erc-default-recipients '("#chan"))
621 (should-not (get-buffer-window (current-buffer) t))
622 (erc-setup-buffer (current-buffer))
623 (erc-tests--assert-server-split (current-buffer) "server")
624 ;; New channel buffer replaces server in lower window of other frame
625 (should-not (get-buffer-window "server" t)))))
626
627(ert-deftest erc-reuse-frames--displayed-double ()
628 :tags '(:unstable :expensive-test)
629 (erc-tests--erc-reuse-frames
630 (lambda (orig-frame)
631 (let ((erc-reuse-frames 'displayed))
632 (erc-tests--erc-reuse-frames--displayed-double orig-frame))
633 (dolist (b '("server" "#chan"))
634 (kill-buffer b)))))
635
636;; If a frame showing ERC buffers exists among other frames, new,
637;; additional connections will use the existing IRC frame. However,
638;; if two or more frames exist with ERC buffers unique to a particular
639;; connection, the correct frame will be found.
640
641(defun erc-tests--erc-reuse-frames--displayed-full (orig-frame)
642 (erc-tests--erc-reuse-frames--displayed-double orig-frame)
643 ;; Server buffer is not displayed because #chan has replaced it in
644 ;; the "server" frame, which is not selected.
645 (should (equal "other" (frame-parameter (window-frame) 'name)))
646 (erc-tests--erc-reuse-frames--displayed-single orig-frame "ircd" "#spam")
647 (should (equal "other" (frame-parameter (window-frame) 'name)))
648
649 ;; Buffer "#spam" has replaced "ircd", which earlier replaced
650 ;; "#chan" in frame "server". But this is confusing, so...
651 (ert-info ("Arrange windows for second connection in other frame")
652 (set-window-buffer (selected-window) "ircd")
653 (split-window-below)
654 (set-window-buffer (next-window) "#spam")
655 (should (equal (cddar (window-tree))
656 (list (get-buffer-window "ircd" t)
657 (get-buffer-window "#spam" t)))))
658
659 (ert-info ("Arrange windows for first connection in server frame")
660 (select-frame-by-name "server")
661 (set-window-buffer (selected-window) "server")
662 (set-window-buffer (next-window) "#chan")
663 (should (equal (cddar (window-tree))
664 (list (get-buffer-window "server" t)
665 (get-buffer-window "#chan" t)))))
666
667 ;; Select original ERT frame
668 (ert-info ("New target for connection server finds appropriate frame")
669 (select-frame orig-frame 'no-record)
670 (with-current-buffer (window-buffer (selected-window))
671 (should (member (buffer-name) '("*ert*" "*scratch*")))
672 (with-current-buffer (generate-new-buffer "alice")
673 (erc-mode)
674 (erc-tests--servars "server" 'erc-server-process 'erc-networks--id)
675 (setq erc-default-recipients '("alice"))
676 (should-not (get-buffer-window (current-buffer) t))
677 (erc-setup-buffer (current-buffer))
678 ;; Window created in frame "server"
679 (should (eq (selected-frame) orig-frame))
680 (erc-tests--assert-server-split (current-buffer) "server"))))
681
682 (ert-info ("New target for connection ircd finds appropriate frame")
683 (select-frame orig-frame 'no-record)
684 (with-current-buffer (window-buffer (selected-window))
685 (should (member (buffer-name) '("*ert*" "*scratch*")))
686 (with-current-buffer (generate-new-buffer "bob")
687 (erc-mode)
688 (erc-tests--servars "ircd" 'erc-server-process 'erc-networks--id)
689 (setq erc-default-recipients '("bob"))
690 (should-not (get-buffer-window (current-buffer) t))
691 (erc-setup-buffer (current-buffer))
692 ;; Window created in frame "other"
693 (should (eq (selected-frame) orig-frame))
694 (erc-tests--assert-server-split (current-buffer) "other")))))
695
696(ert-deftest erc-reuse-frames--displayed-full ()
697 :tags '(:unstable :expensive-test)
698 (erc-tests--erc-reuse-frames
699 (lambda (orig-frame)
700 (let ((erc-reuse-frames 'displayed))
701 (erc-tests--erc-reuse-frames--displayed-full orig-frame))
702 (dolist (b '("server" "ircd" "bob" "alice" "#spam" "#chan"))
703 (kill-buffer b)))))
704
402(ert-deftest erc-lurker-maybe-trim () 705(ert-deftest erc-lurker-maybe-trim ()
403 (let (erc-lurker-trim-nicks 706 (let (erc-lurker-trim-nicks
404 (erc-lurker-ignore-chars "_`")) 707 (erc-lurker-ignore-chars "_`"))