1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-02 08:22:22 +00:00

Prevent unwanted recursion in erc-nickbar-disable

* lisp/erc/erc-speedbar.el (erc-status-sidebar-mode--unhook): Remove
forward declaration.
(erc-speedbar--toggle-nicknames-sidebar): Inline
`erc-speedbar-close-nicknames-window'.  Don't call
`erc-speedbar-browser' and thus avoid adding excess timers.
(erc-speedbar--ensure): Inline `speedbar-enable-update' to avoid
unneeded call to `speedbar-set-timer', and ensure it only runs in
`speedbar-buffer'.
(erc-speedbar--shutting-down-p): New flag variable to avoid recursive
calls to `dframe-close-frame' and friends.
(erc-nickbar-mode, erc-nickbar-enable, erc-nickbar-disable): Move
logic formerly performed by `speedbar-disable-update' to
`erc-speedbar--toggle-nicknames-sidebar'.  When disabling, guard
against recursive calls to `dframe-close-frame' and friends.
(erc-speedbar--get-timers): New utility function, also for use in
testing.
(erc-speedbar--dframe-controlled): Bind
`erc-speedbar--shutting-down-p' flag non-nil around call to
`erc-nickbar-mode'.  Remove excess timer left behind due to
incompatible behavior from `dframe-close-frame'.  Let caller kill
buffer.
(erc-speedbar-close-nicknames-window): Remove unused command, new in
ERC 5.6 and Emacs 30.
* test/lisp/erc/erc-scenarios-status-sidebar.el
(erc-speedbar-close-nicknames-window): Remove forward declaration.
(erc-speedbar--get-timers): Add forward declaration.
(erc-scenarios-status-sidebar--nickbar): Fix faulty expectations of
desired behavior when disabling module.  Ensure timers canceled.
* test/lisp/erc/resources/erc-scenarios-common.el
(erc-scenarios-common--make-bindings): Shadow `timer-idle-list' to
avoid polluting global test environment with stray timers.
(Bug#63595)
This commit is contained in:
F. Jason Park 2023-08-15 21:14:07 -07:00
parent b93757029c
commit f8b710e54f
3 changed files with 79 additions and 47 deletions

View File

@ -439,7 +439,6 @@ The INDENT level is ignored."
(defvar erc-status-sidebar-buffer-name)
(declare-function erc-status-sidebar-set-window-preserve-size
"erc-status-sidebar" nil)
(declare-function erc-status-sidebar-mode--unhook "erc-status-sidebar" nil)
(defvar erc-speedbar--buffer-options
'((speedbar-update-flag . t)
@ -490,36 +489,64 @@ The INDENT level is ignored."
(cl-assert (buffer-live-p speedbar-buffer))
(if (or (and force (< arg 0))
(and (not force) (get-buffer-window speedbar-buffer nil)))
(erc-speedbar-close-nicknames-window nil)
;; Close associated windows and stop updating but leave timer.
(progn
(dolist (window (get-buffer-window-list speedbar-buffer nil t))
(unless (frame-root-window-p window)
(when erc-speedbar--hidden-speedbar-frame
(cl-assert
(not (eq (window-frame window)
erc-speedbar--hidden-speedbar-frame))))
(delete-window window)))
(with-current-buffer speedbar-buffer
(setq speedbar-update-flag nil)
(speedbar-set-mode-line-format)))
(when (or (not force) (>= arg 0))
(with-selected-frame speedbar-frame
(erc-speedbar--emulate-sidebar-set-window-preserve-size)))))
(when (or (not force) (>= arg 0))
(let ((speedbar-frame-parameters (backquote-list*
'(visibility . nil)
'(no-other-frame . t)
speedbar-frame-parameters))
(speedbar-after-create-hook #'erc-speedbar--emulate-sidebar))
(erc-speedbar-browser)
;; If we put the remaining parts in the "create hook" along
;; with everything else, the frame with `window-main-window'
;; gets raised and steals focus if you've switched away from
;; Emacs in the meantime.
(make-frame-invisible speedbar-frame)
(select-frame (setq speedbar-frame (previous-frame)))
(erc-speedbar--emulate-sidebar-set-window-preserve-size))))))
(when-let (((or (not force) (>= arg 0)))
(speedbar-frame-parameters (backquote-list*
'(visibility . nil)
'(no-other-frame . t)
speedbar-frame-parameters))
(speedbar-after-create-hook #'erc-speedbar--emulate-sidebar))
(erc-install-speedbar-variables)
;; Run before toggling mode to prevent timer from being
;; created twice.
(speedbar-change-initial-expansion-list "ERC")
(speedbar-frame-mode 1)
;; If we put the remaining parts in the "create hook" along
;; with everything else, the frame with `window-main-window'
;; gets raised and steals focus if you've switched away from
;; Emacs in the meantime.
(make-frame-invisible speedbar-frame)
(select-frame (setq speedbar-frame (previous-frame)))
(erc-speedbar--emulate-sidebar-set-window-preserve-size))))
(cl-assert (not (cdr (erc-speedbar--get-timers))) t))
(defun erc-speedbar--ensure (&optional force)
(when (or (erc-server-buffer) force)
(when erc-track-mode
(cl-pushnew '(derived-mode . speedbar-mode)
erc-track--switch-fallback-blockers :test #'equal))
(unless speedbar-update-flag
(erc-button--display-error-notice-with-keys
(erc-server-buffer)
"Module `nickbar' needs `speedbar-update-flag' to be non-nil"
(and (not (display-graphic-p)) " in text terminals")
". Setting to t for the current Emacs session."
" Customize it permanently to avoid this message.")
(setq speedbar-update-flag t))
(erc-speedbar--toggle-nicknames-sidebar +1)
(speedbar-enable-update)))
(with-current-buffer speedbar-buffer
(setq speedbar-update-flag t)
(speedbar-set-mode-line-format))))
(defvar erc-speedbar--shutting-down-p nil)
;;;###autoload(autoload 'erc-nickbar-mode "erc-speedbar" nil t)
(define-erc-module nickbar nil
"Show nicknames in a side window.
"Show nicknames for current target buffer in a side window.
When enabling, create a speedbar session if one doesn't exist and
show its buffer in an `erc-status-sidebar' window instead of a
separate frame. When disabling, close the window or, with a
@ -527,8 +554,8 @@ negative prefix arg, destroy the session.
WARNING: this module may produce unwanted side effects, like the
raising of frames or the stealing of input focus. If you witness
such an occurrence, and can reproduce it, please file a bug
report with \\[erc-bug]."
such a thing and can reproduce it, please file a bug report with
\\[erc-bug]."
((add-hook 'erc--setup-buffer-hook #'erc-speedbar--ensure)
(erc-speedbar--ensure)
(unless (or erc--updating-modules-p
@ -542,31 +569,44 @@ report with \\[erc-bug]."
(erc-error "Not initializing `erc-nickbar-mode' in %s"
(current-buffer))))))
((remove-hook 'erc--setup-buffer-hook #'erc-speedbar--ensure)
(speedbar-disable-update)
(when erc-track-mode
(setq erc-track--switch-fallback-blockers
(remove '(derived-mode . speedbar-mode)
erc-track--switch-fallback-blockers)))
(erc-speedbar--toggle-nicknames-sidebar -1)
(when-let ((arg erc--module-toggle-prefix-arg)
(when-let (((not erc-speedbar--shutting-down-p))
(arg erc--module-toggle-prefix-arg)
((numberp arg))
((< arg 0)))
(erc-speedbar-close-nicknames-window 'kill))))
(with-current-buffer speedbar-buffer
(dframe-close-frame)
(setq erc-speedbar--hidden-speedbar-frame nil)))))
(defun erc-speedbar--get-timers ()
(cl-remove #'dframe-timer-fn timer-idle-list
:key #'timer--function
:test-not #'eq))
(defun erc-speedbar--dframe-controlled (arg)
(when speedbar-buffer
(cl-assert (eq speedbar-buffer (current-buffer))))
(when (and erc-speedbar--hidden-speedbar-frame (numberp arg) (< arg 0))
(when erc-nickbar-mode
(erc-nickbar-mode -1))
(let ((erc-speedbar--shutting-down-p t))
(erc-nickbar-mode -1)))
(setq speedbar-frame erc-speedbar--hidden-speedbar-frame
erc-speedbar--hidden-speedbar-frame nil)
;; It's unknown whether leaving the frame invisible interferes
;; with the upstream teardown procedure.
;; with the upstream teardown sequence.
(when (display-graphic-p)
(make-frame-visible speedbar-frame))
(speedbar-frame-mode arg)
(when speedbar-buffer
(kill-buffer speedbar-buffer)
(setq speedbar-buffer nil))))
(speedbar-frame-mode arg) ; -1
;; As of Emacs 29, `dframe-set-timer' can't remove `dframe-timer'.
(cl-assert (= 1 (length (erc-speedbar--get-timers))) t)
(cancel-function-timers #'dframe-timer-fn)
;; `dframe-close-frame' kills the buffer but no function in
;; erc-speedbar.el resets this to nil.
(setq speedbar-buffer nil)))
(defun erc-speedbar-toggle-nicknames-window-lock ()
"Toggle whether nicknames window is selectable with \\[other-window]."
@ -578,20 +618,6 @@ report with \\[erc-bug]."
(set-window-parameter window 'no-other-window (not val))
(message "nick-window: %s" (if val "selectable" "protected")))))
(defun erc-speedbar-close-nicknames-window (kill)
(interactive "P")
(if kill
(with-current-buffer speedbar-buffer
(dframe-close-frame)
(cl-assert (not erc-nickbar-mode))
(setq erc-speedbar--hidden-speedbar-frame nil))
(dolist (window (get-buffer-window-list speedbar-buffer nil t))
(unless (frame-root-window-p window)
(when erc-speedbar--hidden-speedbar-frame
(cl-assert (not (eq (window-frame window)
erc-speedbar--hidden-speedbar-frame))))
(delete-window window)))))
;;;; Nicks integration

View File

@ -94,7 +94,7 @@
;; terminal, and we lack a fixture for that. Please try running this
;; test interactively with both graphical Emacs and non.
(declare-function erc-nickbar-mode "erc-speedbar" (arg))
(declare-function erc-speedbar-close-nicknames-window "erc-speedbar" (kill))
(declare-function erc-speedbar--get-timers "erc-speedbar" nil)
(declare-function speedbar-timer-fn "speedbar" nil)
(defvar erc-nickbar-mode)
(defvar speedbar-buffer)
@ -154,16 +154,21 @@
(ert-info ("Core toggle and kill commands work")
;; Avoid using API, e.g., `erc-status-sidebar-buffer-exists-p',
;; etc. for testing commands that call those same functions.
(erc-nickbar-mode -1)
(call-interactively #'erc-nickbar-mode)
(should-not erc-nickbar-mode)
(should-not (and speedbar-buffer
(get-buffer-window speedbar-buffer)))
(should speedbar-buffer)
(erc-nickbar-mode +1)
(should (and speedbar-buffer
(get-buffer-window speedbar-buffer)))
(should (get-buffer " SPEEDBAR"))
(erc-speedbar-close-nicknames-window 'kill)
(erc-nickbar-mode -1)
(should-not (get-buffer " SPEEDBAR"))
(should-not erc-nickbar-mode)
(should-not (cdr (frame-list)))))))
(should-not (cdr (frame-list)))))
(should-not (erc-speedbar--get-timers))))
;;; erc-scenarios-status-sidebar.el ends here

View File

@ -122,6 +122,7 @@
(inhibit-interaction t)
(auth-source-do-cache nil)
(timer-list (copy-sequence timer-list))
(timer-idle-list (copy-sequence timer-idle-list))
(erc-auth-source-parameters-join-function nil)
(erc-autojoin-channels-alist nil)
(erc-server-auto-reconnect nil)