diff --git a/core/autoload/buffers.el b/core/autoload/buffers.el index f077ccb65..2d0002714 100644 --- a/core/autoload/buffers.el +++ b/core/autoload/buffers.el @@ -49,7 +49,8 @@ BUF should be skipped over by functions like `next-buffer' and `other-buffer'." (defun doom-fallback-buffer () "Returns the fallback buffer, creating it if necessary. By default this is the scratch buffer. See `doom-fallback-buffer-name' to change this." - (get-buffer-create doom-fallback-buffer-name)) + (let (buffer-list-update-hook) + (get-buffer-create doom-fallback-buffer-name))) ;;;###autoload (defalias 'doom-buffer-list #'buffer-list) @@ -219,14 +220,15 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (format "Buffer %s is modified; kill anyway?" buf)))) (message "Aborted") (set-buffer-modified-p nil) - (when (or ;; if there aren't more real buffers than visible buffers, - ;; then there are no real, non-visible buffers left. - (not (cl-set-difference (doom-real-buffer-list) - (doom-visible-buffers))) - ;; if we end up back where we start (or previous-buffer - ;; returns nil), we have nowhere left to go - (memq (previous-buffer) (list buf 'nil))) - (switch-to-buffer (doom-fallback-buffer))) + (let (buffer-list-update-hook) + (when (or ;; if there aren't more real buffers than visible buffers, + ;; then there are no real, non-visible buffers left. + (not (cl-set-difference (doom-real-buffer-list) + (doom-visible-buffers))) + ;; if we end up back where we start (or previous-buffer + ;; returns nil), we have nowhere left to go + (memq (previous-buffer) (list buf 'nil))) + (switch-to-buffer (doom-fallback-buffer)))) (kill-buffer buf))) ((funcall orig-fn))))) diff --git a/core/core-editor.el b/core/core-editor.el index e758f814d..0972e1856 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -147,7 +147,7 @@ savehist file." (def-package! smartparens ;; Auto-close delimiters and blocks as you type. It's more powerful than that, ;; but that is all Doom uses it for. - :after-call (doom-exit-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :commands (sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string) :config (require 'smartparens-config) @@ -220,9 +220,9 @@ savehist file." (advice-add #'dtrt-indent-mode :around #'doom*fix-broken-smie-modes)) -(def-package! undo-tree +(def-package! undo-tree ;; Branching & persistent undo - :after-call (doom-exit-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :config (setq undo-tree-auto-save-history t ;; undo-in-region is known to cause undo history corruption, which can diff --git a/core/core-ui.el b/core/core-ui.el index 691e679d7..a32ffe90a 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -66,71 +66,60 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") "Hook run after the theme is loaded with `load-theme' or reloaded with `doom/reload-theme'.") -(defvar doom-exit-window-hook nil - "Hook run before `switch-window' or `switch-frame' are called. +(defvar doom-switch-buffer-hook nil + "TODO") -Also see `doom-enter-window-hook'.") +(defvar doom-switch-window-hook nil + "TODO") -(defvar doom-enter-window-hook nil - "Hook run after `switch-window' or `switch-frame' are called. - -Also see `doom-exit-window-hook'.") - -(defvar doom-exit-buffer-hook nil - "Hook run after `switch-to-buffer', `pop-to-buffer' or `display-buffer' are -called. The buffer to be switched to is current when these hooks run. - -Also see `doom-enter-buffer-hook'.") - -(defvar doom-enter-buffer-hook nil - "Hook run before `switch-to-buffer', `pop-to-buffer' or `display-buffer' are -called. The buffer to be switched to is current when these hooks run. - -Also see `doom-exit-buffer-hook'.") +(defvar doom-switch-frame-hook nil + "TODO") (defvar doom-inhibit-switch-buffer-hooks nil - "Letvar for inhibiting `doom-enter-buffer-hook' and `doom-exit-buffer-hook'. -Do not set this directly.") + "Letvar for inhibiting `doom-switch-buffer-hook'. Do not set this directly.") (defvar doom-inhibit-switch-window-hooks nil - "Letvar for inhibiting `doom-enter-window-hook' and `doom-exit-window-hook'. -Do not set this directly.") + "Letvar for inhibiting `doom-switch-window-hook'. Do not set this directly.") +(defvar doom-inhibit-switch-frame-hooks nil + "Letvar for inhibiting `doom-switch-frame-hook'. Do not set this directly.") -(defun doom*switch-window-hooks (orig-fn window &optional norecord) - (if (or doom-inhibit-switch-window-hooks - norecord - (eq window (selected-window)) - (window-minibuffer-p window)) - (funcall orig-fn window norecord) - (let ((doom-inhibit-switch-window-hooks t)) - (run-hooks 'doom-exit-window-hook) - (prog1 (funcall orig-fn window norecord) - (run-hooks 'doom-enter-window-hook))))) +(defvar doom--last-window nil) +(defvar doom--last-frame nil) -(defun doom*switch-buffer-hooks (orig-fn buffer-or-name &rest args) +(defun doom|run-switch-window-hooks () + (unless (or doom-inhibit-switch-buffer-hooks + (eq doom--last-window (selected-window)) + (minibufferp)) + (let ((doom-inhibit-switch-buffer-hooks t)) + (run-hooks 'doom-switch-window-hook) + (doom-log "Window switched to %s" (selected-window)) + (setq doom--last-window (selected-window))))) + +(defun doom|run-switch-frame-hooks (&rest _) + (let ((selected-frame (selected-frame))) + (unless (or doom-inhibit-switch-frame-hooks + (eq doom--last-frame (selected-frame)) + (frame-parameter nil 'parent-frame)) + (let ((doom-inhibit-switch-frame-hooks t)) + (run-hooks 'doom-switch-frame-hook) + (doom-log "Frame switched to %s" (selected-frame)) + (setq doom--last-frame (selected-frame)))))) + +(defun doom*run-switch-buffer-hooks (orig-fn buffer-or-name &rest args) (if (or doom-inhibit-switch-buffer-hooks - (null buffer-or-name) - (if (eq orig-fn 'switch-to-buffer) (car args)) - (if (eq orig-fn 'pop-to-buffer) (nth 1 args)) - (eq (get-buffer buffer-or-name) (current-buffer))) + (if (eq orig-fn 'switch-to-buffer) + (car args) ; norecord + (eq (get-buffer buffer-or-name) (current-buffer)))) (apply orig-fn buffer-or-name args) (let ((doom-inhibit-switch-buffer-hooks t)) - (run-hooks 'doom-exit-buffer-hook) + (doom-log "Buffer switched in %s" (selected-window)) (prog1 (apply orig-fn buffer-or-name args) - (run-hooks 'doom-enter-buffer-hook))))) + (run-hooks 'doom-switch-buffer-hook))))) -(defun doom-init-switch-hooks (&optional disable) - (dolist (spec '((select-window . doom*switch-window-hooks) - (switch-to-buffer . doom*switch-buffer-hooks) - (display-buffer . doom*switch-buffer-hooks) - (pop-to-buffer . doom*switch-buffer-hooks))) - (if disable - (advice-remove (car spec) (cdr spec)) - (advice-add (car spec) :around (cdr spec))))) - -(defun doom*load-theme-hooks (theme &rest _) +(defun doom*run-load-theme-hooks (theme &optional _no-confirm no-enable) "Set up `doom-load-theme-hook' to run after `load-theme' is called." - (setq doom-theme theme) - (run-hooks 'doom-load-theme-hook)) + (unless no-enable + (setq doom-theme theme) + (run-hooks 'doom-load-theme-hook))) (defun doom|protect-visible-buffer () "Don't kill the current buffer if it is visible in another window (bury it @@ -292,13 +281,14 @@ read-only or not file-visiting." (def-package! winner ;; undo/redo changes to Emacs' window layout - :hook (doom-exit-window . winner-mode) - :preface (defvar winner-dont-bind-my-keys t)) ; I'll bind keys myself + :after-call (after-find-file doom-switch-window-hook) + :preface (defvar winner-dont-bind-my-keys t) + :config (winner-mode +1)) ; I'll bind keys myself (def-package! paren ;; highlight matching delimiters - :after-call (after-find-file doom-exit-buffer-hook) + :after-call (after-find-file doom-switch-buffer-hook) :init (defun doom|disable-show-paren-mode () "Turn off `show-paren-mode' buffer-locally." @@ -539,9 +529,13 @@ frame's window-system, the theme will be reloaded.") (add-hook 'after-make-frame-functions #'doom|reload-theme-in-frame-maybe) (add-hook 'after-delete-frame-functions #'doom|reload-theme-maybe) - ;; Set up `doom-enter-buffer-hook', `doom-exit-buffer-hook', - ;; `doom-enter-window-hook' and `doom-exit-window-hook' - (doom-init-switch-hooks)) + ;; Initialize custom switch-{buffer,window,frame} hooks: + ;; + `doom-switch-buffer-hook' + ;; + `doom-switch-window-hook' + ;; + `doom-switch-frame-hook' + (add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) + (add-hook 'focus-in-hook #'doom|run-switch-frame-hooks) + (advice-add! '(switch-to-buffer display-buffer) :around #'doom*run-switch-buffer-hooks)) ;; Set fonts (add-hook 'doom-init-ui-hook #'doom|init-fonts) diff --git a/core/test/test-core.el b/core/test/test-core.el index ec920105e..1a3fc80fc 100644 --- a/core/test/test-core.el +++ b/core/test/test-core.el @@ -61,43 +61,39 @@ (before-each (setq a (switch-to-buffer (get-buffer-create "a")) b (get-buffer-create "b")) - (spy-on 'before-hook) - (spy-on 'after-hook) - (doom-init-switch-hooks)) + (spy-on 'hook) + (add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) + (add-hook 'focus-in-hook #'doom|run-switch-frame-hooks) + (advice-add! '(switch-to-buffer display-buffer) :around #'doom*run-switch-buffer-hooks)) (after-each - (doom-init-switch-hooks 'disable) + (remove-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) + (remove-hook 'focus-in-hook #'doom|run-switch-frame-hooks) + (advice-remove! '(switch-to-buffer display-buffer) #'doom*run-switch-buffer-hooks) (kill-buffer a) (kill-buffer b)) (describe "switch-buffer" - :var (doom-exit-buffer-hook - doom-enter-buffer-hook) + :var (doom-switch-buffer-hook) (before-each - (setq doom-exit-buffer-hook '(before-hook) - doom-enter-buffer-hook '(after-hook))) + (setq doom-switch-buffer-hook '(hook))) (after-each - (setq doom-exit-buffer-hook nil - doom-enter-buffer-hook nil)) + (setq doom-switch-buffer-hook nil)) (it "should trigger when switching buffers" (switch-to-buffer b) (switch-to-buffer a) (switch-to-buffer b) - (expect 'before-hook :to-have-been-called-times 3) - (expect 'after-hook :to-have-been-called-times 3)) + (expect 'hook :to-have-been-called-times 3)) (it "should trigger only once on the same buffer" (switch-to-buffer b) (switch-to-buffer b) (switch-to-buffer a) - (expect 'before-hook :to-have-been-called-times 2) - (expect 'after-hook :to-have-been-called-times 2))) + (expect 'hook :to-have-been-called-times 2))) (describe "switch-window" - :var (doom-exit-window-hook - doom-enter-window-hook - x y) + :var (doom-switch-window-hook x y) (before-each (delete-other-windows) (setq x (get-buffer-window a) @@ -105,21 +101,17 @@ (with-selected-window y (switch-to-buffer b)) (select-window x) - (spy-calls-reset 'before-hook) - (spy-calls-reset 'after-hook) - (setq doom-exit-window-hook '(before-hook) - doom-enter-window-hook '(after-hook))) + (spy-calls-reset 'hook) + (setq doom-switch-window-hook '(hook))) (it "should trigger when switching windows" (select-window y) (select-window x) (select-window y) - (expect 'before-hook :to-have-been-called-times 3) - (expect 'after-hook :to-have-been-called-times 3)) + (expect 'hook :to-have-been-called-times 3)) (it "should trigger only once on the same window" (select-window y) (select-window y) (select-window x) - (expect 'before-hook :to-have-been-called-times 2) - (expect 'after-hook :to-have-been-called-times 2)))))) + (expect 'hook :to-have-been-called-times 2)))))) diff --git a/modules/tools/editorconfig/config.el b/modules/tools/editorconfig/config.el index b1d18a55f..cff636d44 100644 --- a/modules/tools/editorconfig/config.el +++ b/modules/tools/editorconfig/config.el @@ -18,8 +18,7 @@ ;; Handles whitespace (tabs/spaces) settings externally. This way projects can ;; specify their own formatting rules. (def-package! editorconfig - :defer 3 - :after-call (doom-enter-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :config ;; Register missing indent variables (unless (assq 'mips-mode editorconfig-indentation-alist) diff --git a/modules/tools/flycheck/config.el b/modules/tools/flycheck/config.el index adc034cb4..57c98bfda 100644 --- a/modules/tools/flycheck/config.el +++ b/modules/tools/flycheck/config.el @@ -9,7 +9,7 @@ (def-package! flycheck :commands (flycheck-list-errors flycheck-buffer) - :after-call (doom-enter-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :config ;; Emacs feels snappier without checks on newline (setq flycheck-check-syntax-automatically (delq 'new-line flycheck-check-syntax-automatically)) diff --git a/modules/tools/wakatime/autoload.el b/modules/tools/wakatime/autoload.el index 73aaae385..e07702ab8 100644 --- a/modules/tools/wakatime/autoload.el +++ b/modules/tools/wakatime/autoload.el @@ -44,14 +44,14 @@ warning)." (make-directory +wakatime-home t))) (global-wakatime-mode +1)) ;; - (remove-hook 'doom-exit-buffer-hook #'+wakatime|autostart) + (remove-hook 'doom-switch-buffer-hook #'+wakatime|autostart) (advice-remove 'after-find-file #'+wakatime|autostart)) ;;;###autoload (defun +wakatime|delayed-autostart (&rest _) "Lazily initialize `wakatime-mode' until the next time you switch buffers or open a file." - (add-hook 'doom-exit-buffer-hook #'+wakatime|autostart) + (add-hook 'doom-switch-buffer-hook #'+wakatime|autostart) ;; this is necessary in case the user opens emacs with file arguments (advice-add 'after-find-file :before #'+wakatime|autostart)) diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index 8bbbe9267..a0be1f422 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -196,7 +196,7 @@ PLIST can have the following properties: (add-hook 'window-configuration-change-hook #'+doom-dashboard|resize) (add-hook 'window-size-change-functions #'+doom-dashboard|resize) (add-hook 'kill-buffer-query-functions #'+doom-dashboard|reload-on-kill) - (add-hook 'doom-enter-buffer-hook #'+doom-dashboard|reload-on-kill) + (add-hook 'doom-switch-buffer-hook #'+doom-dashboard|reload-on-kill) ;; `persp-mode' integration: update `default-directory' when switching (add-hook 'persp-created-functions #'+doom-dashboard|record-project) (add-hook 'persp-activated-functions #'+doom-dashboard|detect-project) @@ -228,7 +228,8 @@ whose dimensions may not be fully initialized by the time this is run." (defun +doom-dashboard|resize (&rest _) "Recenter the dashboard, and reset its margins and fringes." - (let ((windows (get-buffer-window-list (doom-fallback-buffer) nil t))) + (let ((windows (get-buffer-window-list (doom-fallback-buffer) nil t)) + buffer-list-update-hook) (dolist (win windows) (set-window-start win 0) (set-window-fringes win 0 0) @@ -274,7 +275,8 @@ project (which may be different across perspective)." (defun +doom-dashboard-initial-buffer () "Returns buffer to display on startup. Designed for `initial-buffer-choice'." - (get-buffer-create +doom-dashboard-name)) + (let (buffer-list-update-hook) + (get-buffer-create +doom-dashboard-name))) (defun +doom-dashboard-p (buffer) "Returns t if BUFFER is the dashboard buffer." diff --git a/modules/ui/nav-flash/config.el b/modules/ui/nav-flash/config.el index d59b7e2be..e84837bbb 100644 --- a/modules/ui/nav-flash/config.el +++ b/modules/ui/nav-flash/config.el @@ -11,7 +11,7 @@ ;; NOTE In :feature lookup `recenter' is hooked to a bunch of jumping ;; commands, which will trigger nav-flash. (add-hook! - '(doom-enter-window-hook + '(doom-switch-window-hook imenu-after-jump-hook evil-jumps-post-jump-hook counsel-grep-post-action-hook dumb-jump-after-jump-hook) #'+nav-flash|blink-cursor-maybe)