Rewrite switch-{buffer,window} hooks

+ Add doom-switch-frame-hook
+ Replace doom-{enter,exit}-{buffer,window}-hook with
  doom-switch-{buffer,window}-hook
+ New switch-buffer hooks run on buffer-list-update-hook rather than
  in select-window advice.
+ Blank our buffer-list-update-hook in some places to reduce how many
  times it gets triggered.
This commit is contained in:
Henrik Lissner 2019-03-07 00:26:51 -05:00
parent 8f2fb07948
commit a05b1877be
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
9 changed files with 93 additions and 104 deletions

View file

@ -49,7 +49,8 @@ BUF should be skipped over by functions like `next-buffer' and `other-buffer'."
(defun doom-fallback-buffer () (defun doom-fallback-buffer ()
"Returns the fallback buffer, creating it if necessary. By default this is the "Returns the fallback buffer, creating it if necessary. By default this is the
scratch buffer. See `doom-fallback-buffer-name' to change this." 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 ;;;###autoload
(defalias 'doom-buffer-list #'buffer-list) (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)))) (format "Buffer %s is modified; kill anyway?" buf))))
(message "Aborted") (message "Aborted")
(set-buffer-modified-p nil) (set-buffer-modified-p nil)
(when (or ;; if there aren't more real buffers than visible buffers, (let (buffer-list-update-hook)
;; then there are no real, non-visible buffers left. (when (or ;; if there aren't more real buffers than visible buffers,
(not (cl-set-difference (doom-real-buffer-list) ;; then there are no real, non-visible buffers left.
(doom-visible-buffers))) (not (cl-set-difference (doom-real-buffer-list)
;; if we end up back where we start (or previous-buffer (doom-visible-buffers)))
;; returns nil), we have nowhere left to go ;; if we end up back where we start (or previous-buffer
(memq (previous-buffer) (list buf 'nil))) ;; returns nil), we have nowhere left to go
(switch-to-buffer (doom-fallback-buffer))) (memq (previous-buffer) (list buf 'nil)))
(switch-to-buffer (doom-fallback-buffer))))
(kill-buffer buf))) (kill-buffer buf)))
((funcall orig-fn))))) ((funcall orig-fn)))))

View file

@ -147,7 +147,7 @@ savehist file."
(def-package! smartparens (def-package! smartparens
;; Auto-close delimiters and blocks as you type. It's more powerful than that, ;; Auto-close delimiters and blocks as you type. It's more powerful than that,
;; but that is all Doom uses it for. ;; 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) :commands (sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string)
:config :config
(require 'smartparens-config) (require 'smartparens-config)
@ -222,7 +222,7 @@ savehist file."
(def-package! undo-tree (def-package! undo-tree
;; Branching & persistent undo ;; Branching & persistent undo
:after-call (doom-exit-buffer-hook after-find-file) :after-call (doom-switch-buffer-hook after-find-file)
:config :config
(setq undo-tree-auto-save-history t (setq undo-tree-auto-save-history t
;; undo-in-region is known to cause undo history corruption, which can ;; undo-in-region is known to cause undo history corruption, which can

View file

@ -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 "Hook run after the theme is loaded with `load-theme' or reloaded with
`doom/reload-theme'.") `doom/reload-theme'.")
(defvar doom-exit-window-hook nil (defvar doom-switch-buffer-hook nil
"Hook run before `switch-window' or `switch-frame' are called. "TODO")
Also see `doom-enter-window-hook'.") (defvar doom-switch-window-hook nil
"TODO")
(defvar doom-enter-window-hook nil (defvar doom-switch-frame-hook nil
"Hook run after `switch-window' or `switch-frame' are called. "TODO")
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-inhibit-switch-buffer-hooks nil (defvar doom-inhibit-switch-buffer-hooks nil
"Letvar for inhibiting `doom-enter-buffer-hook' and `doom-exit-buffer-hook'. "Letvar for inhibiting `doom-switch-buffer-hook'. Do not set this directly.")
Do not set this directly.")
(defvar doom-inhibit-switch-window-hooks nil (defvar doom-inhibit-switch-window-hooks nil
"Letvar for inhibiting `doom-enter-window-hook' and `doom-exit-window-hook'. "Letvar for inhibiting `doom-switch-window-hook'. Do not set this directly.")
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) (defvar doom--last-window nil)
(if (or doom-inhibit-switch-window-hooks (defvar doom--last-frame nil)
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)))))
(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 (if (or doom-inhibit-switch-buffer-hooks
(null buffer-or-name) (if (eq orig-fn 'switch-to-buffer)
(if (eq orig-fn 'switch-to-buffer) (car args)) (car args) ; norecord
(if (eq orig-fn 'pop-to-buffer) (nth 1 args)) (eq (get-buffer buffer-or-name) (current-buffer))))
(eq (get-buffer buffer-or-name) (current-buffer)))
(apply orig-fn buffer-or-name args) (apply orig-fn buffer-or-name args)
(let ((doom-inhibit-switch-buffer-hooks t)) (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) (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) (defun doom*run-load-theme-hooks (theme &optional _no-confirm no-enable)
(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 _)
"Set up `doom-load-theme-hook' to run after `load-theme' is called." "Set up `doom-load-theme-hook' to run after `load-theme' is called."
(setq doom-theme theme) (unless no-enable
(run-hooks 'doom-load-theme-hook)) (setq doom-theme theme)
(run-hooks 'doom-load-theme-hook)))
(defun doom|protect-visible-buffer () (defun doom|protect-visible-buffer ()
"Don't kill the current buffer if it is visible in another window (bury it "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 (def-package! winner
;; undo/redo changes to Emacs' window layout ;; undo/redo changes to Emacs' window layout
:hook (doom-exit-window . winner-mode) :after-call (after-find-file doom-switch-window-hook)
:preface (defvar winner-dont-bind-my-keys t)) ; I'll bind keys myself :preface (defvar winner-dont-bind-my-keys t)
:config (winner-mode +1)) ; I'll bind keys myself
(def-package! paren (def-package! paren
;; highlight matching delimiters ;; highlight matching delimiters
:after-call (after-find-file doom-exit-buffer-hook) :after-call (after-find-file doom-switch-buffer-hook)
:init :init
(defun doom|disable-show-paren-mode () (defun doom|disable-show-paren-mode ()
"Turn off `show-paren-mode' buffer-locally." "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-make-frame-functions #'doom|reload-theme-in-frame-maybe)
(add-hook 'after-delete-frame-functions #'doom|reload-theme-maybe) (add-hook 'after-delete-frame-functions #'doom|reload-theme-maybe)
;; Set up `doom-enter-buffer-hook', `doom-exit-buffer-hook', ;; Initialize custom switch-{buffer,window,frame} hooks:
;; `doom-enter-window-hook' and `doom-exit-window-hook' ;; + `doom-switch-buffer-hook'
(doom-init-switch-hooks)) ;; + `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 ;; Set fonts
(add-hook 'doom-init-ui-hook #'doom|init-fonts) (add-hook 'doom-init-ui-hook #'doom|init-fonts)

View file

@ -61,43 +61,39 @@
(before-each (before-each
(setq a (switch-to-buffer (get-buffer-create "a")) (setq a (switch-to-buffer (get-buffer-create "a"))
b (get-buffer-create "b")) b (get-buffer-create "b"))
(spy-on 'before-hook) (spy-on 'hook)
(spy-on 'after-hook) (add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks)
(doom-init-switch-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 (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 a)
(kill-buffer b)) (kill-buffer b))
(describe "switch-buffer" (describe "switch-buffer"
:var (doom-exit-buffer-hook :var (doom-switch-buffer-hook)
doom-enter-buffer-hook)
(before-each (before-each
(setq doom-exit-buffer-hook '(before-hook) (setq doom-switch-buffer-hook '(hook)))
doom-enter-buffer-hook '(after-hook)))
(after-each (after-each
(setq doom-exit-buffer-hook nil (setq doom-switch-buffer-hook nil))
doom-enter-buffer-hook nil))
(it "should trigger when switching buffers" (it "should trigger when switching buffers"
(switch-to-buffer b) (switch-to-buffer b)
(switch-to-buffer a) (switch-to-buffer a)
(switch-to-buffer b) (switch-to-buffer b)
(expect 'before-hook :to-have-been-called-times 3) (expect 'hook :to-have-been-called-times 3))
(expect 'after-hook :to-have-been-called-times 3))
(it "should trigger only once on the same buffer" (it "should trigger only once on the same buffer"
(switch-to-buffer b) (switch-to-buffer b)
(switch-to-buffer b) (switch-to-buffer b)
(switch-to-buffer a) (switch-to-buffer a)
(expect 'before-hook :to-have-been-called-times 2) (expect 'hook :to-have-been-called-times 2)))
(expect 'after-hook :to-have-been-called-times 2)))
(describe "switch-window" (describe "switch-window"
:var (doom-exit-window-hook :var (doom-switch-window-hook x y)
doom-enter-window-hook
x y)
(before-each (before-each
(delete-other-windows) (delete-other-windows)
(setq x (get-buffer-window a) (setq x (get-buffer-window a)
@ -105,21 +101,17 @@
(with-selected-window y (with-selected-window y
(switch-to-buffer b)) (switch-to-buffer b))
(select-window x) (select-window x)
(spy-calls-reset 'before-hook) (spy-calls-reset 'hook)
(spy-calls-reset 'after-hook) (setq doom-switch-window-hook '(hook)))
(setq doom-exit-window-hook '(before-hook)
doom-enter-window-hook '(after-hook)))
(it "should trigger when switching windows" (it "should trigger when switching windows"
(select-window y) (select-window y)
(select-window x) (select-window x)
(select-window y) (select-window y)
(expect 'before-hook :to-have-been-called-times 3) (expect 'hook :to-have-been-called-times 3))
(expect 'after-hook :to-have-been-called-times 3))
(it "should trigger only once on the same window" (it "should trigger only once on the same window"
(select-window y) (select-window y)
(select-window y) (select-window y)
(select-window x) (select-window x)
(expect 'before-hook :to-have-been-called-times 2) (expect 'hook :to-have-been-called-times 2))))))
(expect 'after-hook :to-have-been-called-times 2))))))

View file

@ -18,8 +18,7 @@
;; Handles whitespace (tabs/spaces) settings externally. This way projects can ;; Handles whitespace (tabs/spaces) settings externally. This way projects can
;; specify their own formatting rules. ;; specify their own formatting rules.
(def-package! editorconfig (def-package! editorconfig
:defer 3 :after-call (doom-switch-buffer-hook after-find-file)
:after-call (doom-enter-buffer-hook after-find-file)
:config :config
;; Register missing indent variables ;; Register missing indent variables
(unless (assq 'mips-mode editorconfig-indentation-alist) (unless (assq 'mips-mode editorconfig-indentation-alist)

View file

@ -9,7 +9,7 @@
(def-package! flycheck (def-package! flycheck
:commands (flycheck-list-errors flycheck-buffer) :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 :config
;; Emacs feels snappier without checks on newline ;; Emacs feels snappier without checks on newline
(setq flycheck-check-syntax-automatically (delq 'new-line flycheck-check-syntax-automatically)) (setq flycheck-check-syntax-automatically (delq 'new-line flycheck-check-syntax-automatically))

View file

@ -44,14 +44,14 @@ warning)."
(make-directory +wakatime-home t))) (make-directory +wakatime-home t)))
(global-wakatime-mode +1)) (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)) (advice-remove 'after-find-file #'+wakatime|autostart))
;;;###autoload ;;;###autoload
(defun +wakatime|delayed-autostart (&rest _) (defun +wakatime|delayed-autostart (&rest _)
"Lazily initialize `wakatime-mode' until the next time you switch buffers or "Lazily initialize `wakatime-mode' until the next time you switch buffers or
open a file." 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 ;; this is necessary in case the user opens emacs with file arguments
(advice-add 'after-find-file :before #'+wakatime|autostart)) (advice-add 'after-find-file :before #'+wakatime|autostart))

View file

@ -196,7 +196,7 @@ PLIST can have the following properties:
(add-hook 'window-configuration-change-hook #'+doom-dashboard|resize) (add-hook 'window-configuration-change-hook #'+doom-dashboard|resize)
(add-hook 'window-size-change-functions #'+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 '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 ;; `persp-mode' integration: update `default-directory' when switching
(add-hook 'persp-created-functions #'+doom-dashboard|record-project) (add-hook 'persp-created-functions #'+doom-dashboard|record-project)
(add-hook 'persp-activated-functions #'+doom-dashboard|detect-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 _) (defun +doom-dashboard|resize (&rest _)
"Recenter the dashboard, and reset its margins and fringes." "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) (dolist (win windows)
(set-window-start win 0) (set-window-start win 0)
(set-window-fringes win 0 0) (set-window-fringes win 0 0)
@ -274,7 +275,8 @@ project (which may be different across perspective)."
(defun +doom-dashboard-initial-buffer () (defun +doom-dashboard-initial-buffer ()
"Returns buffer to display on startup. Designed for `initial-buffer-choice'." "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) (defun +doom-dashboard-p (buffer)
"Returns t if BUFFER is the dashboard buffer." "Returns t if BUFFER is the dashboard buffer."

View file

@ -11,7 +11,7 @@
;; NOTE In :feature lookup `recenter' is hooked to a bunch of jumping ;; NOTE In :feature lookup `recenter' is hooked to a bunch of jumping
;; commands, which will trigger nav-flash. ;; commands, which will trigger nav-flash.
(add-hook! (add-hook!
'(doom-enter-window-hook '(doom-switch-window-hook
imenu-after-jump-hook evil-jumps-post-jump-hook imenu-after-jump-hook evil-jumps-post-jump-hook
counsel-grep-post-action-hook dumb-jump-after-jump-hook) counsel-grep-post-action-hook dumb-jump-after-jump-hook)
#'+nav-flash|blink-cursor-maybe) #'+nav-flash|blink-cursor-maybe)