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 ()
"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)))))

View file

@ -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)
@ -222,7 +222,7 @@ savehist file."
(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

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
`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)

View file

@ -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))))))

View file

@ -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)

View file

@ -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))

View file

@ -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))

View file

@ -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."

View file

@ -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)