diff --git a/CHANGELOG.org b/CHANGELOG.org index 466d1fd5f..7e424a4ea 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -69,6 +69,7 @@ + =app/irc= -- Emacs as an IRC client, using circe (contributed by [[https://github.com/bandresen][brandresen]]). + =core= + New variable: ~doom-host-dir~, as a base path for ~doom-etc-dir~ and ~doom-cache-dir~. + + New hooks: ~doom-init-hook~ and ~doom-post-init-hook~, which are run on ~emacs-startup-hook~. This is meant to simplify post-Emacs initialization hooks (~after-init-hook~, ~emacs-startup-hook~ and ~window-setup-hook~) into two unambiguous ones. + =core-ui= + Add quit confirmation when trying to close a frame that contains real buffers. + Fix quit confirmations for clients connected to ~emacs --daemon~ with ~emacsclient~. @@ -76,6 +77,10 @@ + Change what files recentf will ignore: everything in ~doom-host-dir~ is now ignored and anything else in ~doom-local-dir~ won't be. + =core-packages= + When a quelpa package is deleted, its build repo (in ~quelpa-build-dir~) is now deleted with it. + + New hook: ~doom-reload-hook~ (sort of). This has been around for a while, but now it is defined and documented. It runs when ~doom/reload~ is called (which gets called remotely if you run package management while an Emacs session is active). ++ =ui= + + =ui/doom= + + Vastly improve daemon and terminal support for doom-themes by reloading the theme when a new client is attached, or new terminal/daemon frame is created. This prevents incorrect colors from bleeding across face class barriers. + =feature= + =feature/evil= + Remove =goto-last-change=, which conflicts with =goto-chg=, which is a dependency of evil (that does the exact same thing, but is what evil uses). diff --git a/core/core-editor.el b/core/core-editor.el index 032849d55..78b7cbdfe 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -107,7 +107,7 @@ fundamental-mode) for performance sake." savehist-autosave-interval nil ; save on kill only savehist-additional-variables '(kill-ring search-ring regexp-search-ring) save-place-file (concat doom-cache-dir "saveplace")) -(add-hook! 'emacs-startup-hook #'(savehist-mode save-place-mode)) +(add-hook! 'doom-init-hook #'(savehist-mode save-place-mode)) ;; Keep track of recently opened files (def-package! recentf @@ -151,7 +151,7 @@ fundamental-mode) for performance sake." (t (error "%s is an invalid action for :editorconfig" action))))) :config - (add-hook 'emacs-startup-hook #'editorconfig-mode) + (add-hook 'doom-init-hook #'editorconfig-mode) (defun doom|editorconfig-whitespace-mode-maybe (&rest _) "Show whitespace-mode when file uses TABS (ew)." @@ -169,7 +169,7 @@ fundamental-mode) for performance sake." sp-max-pair-length 3) :config - (add-hook 'emacs-startup-hook #'smartparens-global-mode) + (add-hook 'doom-init-hook #'smartparens-global-mode) (require 'smartparens-config) ;; Smartparens interferes with Replace mode (add-hook 'evil-replace-state-entry-hook #'turn-off-smartparens-mode) diff --git a/core/core-keybinds.el b/core/core-keybinds.el index d7df7c25b..7ac1ddf95 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -33,7 +33,7 @@ ;; embolden local bindings (set-face-attribute 'which-key-local-map-description-face nil :weight 'bold) (which-key-setup-side-window-bottom) - (add-hook 'window-setup-hook #'which-key-mode)) + (add-hook 'doom-init-hook #'which-key-mode)) ;; diff --git a/core/core-packages.el b/core/core-packages.el index f33b0fc31..86053e16f 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -40,8 +40,13 @@ ;; See core/autoload/packages.el for more functions. (defvar doom-init-p nil - "Non-nil if doom's package system has been initialized (by `doom-initialize'). -This will be nil if you have byte-compiled your configuration (as intended).") + "Non-nil if doom is done initializing (once `doom-post-init-hook' is done). If +this is nil after Emacs has started something is wrong.") + +(defvar doom-package-init-p nil + "If non-nil, doom's package system has been initialized (by +`doom-initialize'). This will be nill if you byte-compile your configuration (as +intended).") (defvar doom-init-time nil "The time it took, in seconds, for DOOM Emacs to initialize.") @@ -62,6 +67,9 @@ missing) and shouldn't be deleted.") (defvar doom-disabled-packages () "A list of packages that should be ignored by `def-package!'.") +(defvar doom-reload-hook nil + "A list of hooks to run when `doom/reload' is called.") + (defvar doom--site-load-path load-path "The load path of built in Emacs libraries.") @@ -123,7 +131,7 @@ base by `doom!' and for calculating how many packages exist.") are installed. If you byte-compile core/core.el, this function will be avoided to speed up startup." ;; Called early during initialization; only use native functions! - (when (or (not doom-init-p) force-p) + (when (or (not doom-package-init-p) force-p) (unless noninteractive (message "Doom initialized")) @@ -162,7 +170,7 @@ to speed up startup." (load "quelpa" nil t) (load "use-package" nil t) - (setq doom-init-p t))) + (setq doom-package-init-p t))) (defun doom-initialize-autoloads () "Ensures that `doom-autoload-file' exists and is loaded. Otherwise run @@ -297,7 +305,7 @@ byte-compilation." (unless (server-running-p) (server-start))) - (add-hook 'emacs-startup-hook #'doom--display-benchmark t)))) + (add-hook 'doom-post-init-hook #'doom--display-benchmark)))) (defmacro def-package! (name &rest plist) "A thin wrapper around `use-package'." diff --git a/core/core-popups.el b/core/core-popups.el index 312e43df9..6aef44f2a 100644 --- a/core/core-popups.el +++ b/core/core-popups.el @@ -90,8 +90,7 @@ is enabled/disabled.'") ("^ \\*" :regexp t :size 12 :noselect t :autokill t :autoclose t))) :config - (add-transient-hook! 'after-make-frame-functions (shackle-mode +1)) - (add-hook 'window-setup-hook #'shackle-mode) + (add-hook 'doom-init-hook #'shackle-mode) (defun doom*shackle-always-align (plist) "Ensure popups are always aligned and selected by default. Eliminates the need @@ -540,7 +539,7 @@ you came from." ;; Ensure these settings are attached to org-load-hook as late as possible, ;; giving other modules a chance to add their own hooks. -(add-hook! 'emacs-startup-hook +(defun doom|init-org-popups () (add-hook! 'org-load-hook (set! :popup '("*Calendar*" :size 0.4 :noselect t) @@ -601,6 +600,7 @@ you came from." (let ((map org-agenda-mode-map)) (define-key map "q" 'org-agenda-Quit) (define-key map "Q" 'org-agenda-Quit))))) +(add-hook 'doom-init-hook #'doom|init-org-popups) (provide 'core-popups) ;;; core-popups.el ends here diff --git a/core/core-projects.el b/core/core-projects.el index ca45a2f58..d203027aa 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -21,7 +21,7 @@ state are passed in.") "build.gradle")) :config - (add-hook 'after-init-hook #'projectile-mode) + (add-hook 'doom-init-hook #'projectile-mode) (setq projectile-other-file-alist (append '(("less" "css") diff --git a/core/core-ui.el b/core/core-ui.el index ad96d340b..407648943 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -95,14 +95,14 @@ local value, whether or not it's permanent-local. Therefore, we cycle ;; undo/redo changes to Emacs' window layout (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself -(require 'winner) -(add-hook 'window-setup-hook #'winner-mode) +(autoload 'winner-mode "winner" nil t) +(add-hook 'doom-init-hook #'winner-mode) ;; highlight matching delimiters (setq show-paren-delay 0.1 show-paren-highlight-openparen t show-paren-when-point-inside-paren t) -(add-hook 'window-setup-hook #'show-paren-mode) +(add-hook 'doom-init-hook #'show-paren-mode) ;;; More reliable inter-window border ;; The native border "consumes" a pixel of the fringe on righter-most splits, @@ -110,7 +110,7 @@ local value, whether or not it's permanent-local. Therefore, we cycle (setq-default window-divider-default-places t window-divider-default-bottom-width 1 window-divider-default-right-width 1) -(add-hook 'window-setup-hook #'window-divider-mode) +(add-hook 'doom-init-hook #'window-divider-mode) ;; like diminish, but for major-modes. [pedantry intensifies] (defvar doom-ui-mode-names @@ -132,24 +132,27 @@ mode is detected.") (global-set-key [remap delete-frame] #'doom/delete-frame) -;; auto-enabled in Emacs 25+; I'd rather enable it manually -(global-eldoc-mode -1) +(global-eldoc-mode -1) ; auto-enabled in Emacs 25+; I'll do it myself +(blink-cursor-mode +1) ; a good indicator that Emacs isn't frozen ;; draw me like one of your French editors (tooltip-mode -1) ; relegate tooltips to echo area only (menu-bar-mode -1) (when (fboundp 'tool-bar-mode) (tool-bar-mode -1)) -(when (display-graphic-p) - (scroll-bar-mode -1) - ;; buffer name in frame title - (setq-default frame-title-format '("DOOM Emacs")) - ;; standardize fringe width - (push (cons 'left-fringe doom-ui-fringe-size) default-frame-alist) - (push (cons 'right-fringe doom-ui-fringe-size) default-frame-alist) - ;; no fringe in the minibuffer - (add-hook! '(emacs-startup-hook minibuffer-setup-hook) - (set-window-fringes (minibuffer-window) 0 0 nil))) +(when (fboundp 'scroll-bar-mode) + (scroll-bar-mode -1)) + +;; buffer name in frame title +(setq-default frame-title-format '("DOOM Emacs")) +;; standardize fringe width +(push (cons 'left-fringe doom-ui-fringe-size) default-frame-alist) +(push (cons 'right-fringe doom-ui-fringe-size) default-frame-alist) +;; no fringe in the minibuffer +(defun doom|no-fringes-in-minibuffer () + (set-window-fringes (minibuffer-window) 0 0 nil)) +(add-hook! '(doom-init-hook minibuffer-setup-hook) + #'doom|no-fringes-in-minibuffer) ;; @@ -320,9 +323,8 @@ file." ;; indicators for empty lines past EOF (def-package! vi-tilde-fringe - :when (display-graphic-p) :commands global-vi-tilde-fringe-mode - :init (add-hook 'window-setup-hook #'global-vi-tilde-fringe-mode)) + :init (add-hook 'doom-init-hook #'global-vi-tilde-fringe-mode)) ;; For a distractions-free-like UI, that dynamically resizes margets and can ;; center a buffer. diff --git a/core/core.el b/core/core.el index 96bbfce6b..b99624253 100644 --- a/core/core.el +++ b/core/core.el @@ -124,8 +124,15 @@ melodramatic ex-vimmer disappointed with the text-editor status quo." initial-major-mode 'fundamental-mode initial-scratch-message nil) +;; Custom init hooks; clearer than `after-init-hook', `emacs-startup-hook', and +;; `window-setup-hook'. +(defvar doom-init-hook nil + "A list of hooks run when DOOM is initialized, before `doom-post-init-hook'.") + +(defvar doom-post-init-hook nil + "A list of hooks run after DOOM initialization is complete, and after +`doom-init-hook'.") -;;; ;; Automatic minor modes (defvar doom-auto-minor-mode-alist '() "Alist mapping filename patterns to corresponding minor mode functions, like @@ -153,7 +160,7 @@ enable multiple minor modes for the same regexp.") ;;; -;; Bootstrap +;; Initialize (eval-and-compile (defvar doom--file-name-handler-alist file-name-handler-alist) (setq gc-cons-threshold 402653184 @@ -170,7 +177,25 @@ enable multiple minor modes for the same regexp.") (setq load-path (eval-when-compile load-path) doom--package-load-path (eval-when-compile doom--package-load-path)) -;;; Let 'er rip +(defun doom|finalize () + (unless doom-init-p + (run-hooks 'doom-init-hook) + (run-hooks 'doom-post-init-hook) + + ;; Don't keep gc-cons-threshold too high. It helps to stave off the GC while + ;; Emacs starts up, but afterwards it causes stuttering and random freezes. + ;; So reset it to a reasonable default. + (setq gc-cons-threshold 16777216 + gc-cons-percentage 0.1 + file-name-handler-alist doom--file-name-handler-alist + doom-init-p t))) + +(add-hook! '(emacs-startup-hook doom-reload-hook) + #'doom|finalize) + + +;;; +;; Bootstrap (load! core-os) ; consistent behavior across Oses (with-demoted-errors "AUTOLOAD ERROR: %s" (require 'autoloads doom-autoload-file t)) @@ -182,16 +207,5 @@ enable multiple minor modes for the same regexp.") (load! core-projects) ; making Emacs project-aware (load! core-keybinds)) ; centralized keybind system + which-key -(defun doom|init () - ;; Don't keep gc-cons-threshold too high. It helps to stave off the GC while - ;; Emacs starts up, but afterwards it causes stuttering and random freezes. So - ;; reset it to a reasonable default. - (setq gc-cons-threshold 16777216 - gc-cons-percentage 0.1 - file-name-handler-alist doom--file-name-handler-alist)) - -(add-hook! '(emacs-startup-hook doom-reload-hook) - #'doom|init) - (provide 'core) ;;; core.el ends here diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index 55adb1d6c..db13f2595 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -33,7 +33,7 @@ :config (load "helm-autoloads" nil t) - (add-hook 'emacs-startup-hook #'helm-mode) + (add-hook 'doom-init-hook #'helm-mode) (defvar helm-projectile-find-file-map (make-sparse-keymap)) (require 'helm-projectile) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index 053def28d..46fd19e2a 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -40,7 +40,7 @@ session)." (after! magit (setq magit-completing-read-function #'ivy-completing-read)) (after! yasnippet (push #'+ivy-yas-prompt yas-prompt-functions)) - (add-hook 'emacs-startup-hook #'ivy-mode) + (add-hook 'doom-init-hook #'ivy-mode) (map! :map ivy-mode-map [remap apropos] #'counsel-apropos diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index f7962aa3b..557953bc2 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -44,7 +44,7 @@ shift-select-mode nil) :config - (add-hook 'emacs-startup-hook #'evil-mode) + (add-hook 'doom-init-hook #'evil-mode) (evil-select-search-module 'evil-search-module 'evil-search) (set! :popup @@ -242,7 +242,7 @@ across windows." evil-escape-excluded-major-modes '(neotree-mode) evil-escape-key-sequence "jk" evil-escape-delay 0.25) - (add-hook 'emacs-startup-hook #'evil-escape-mode) + (add-hook 'doom-post-init-hook #'evil-escape-mode) :config ;; no `evil-escape' in minibuffer (cl-pushnew #'minibufferp evil-escape-inhibit-functions) @@ -333,7 +333,7 @@ the new algorithm is confusing, like in python or ruby." (?\] "[]})]") (?\; "[;:]"))) :config - (add-hook 'emacs-startup-hook #'evil-snipe-override-mode)) + (add-hook 'doom-post-init-hook #'evil-snipe-override-mode)) (def-package! evil-surround @@ -349,7 +349,7 @@ the new algorithm is confusing, like in python or ruby." :init (setq vimish-fold-dir (concat doom-cache-dir "vimish-fold/") vimish-fold-indication-mode 'right-fringe) - (add-hook 'emacs-startup-hook #'evil-vimish-fold-mode t)) + (add-hook 'doom-post-init-hook #'evil-vimish-fold-mode t)) ;; Without `evil-visualstar', * and # grab the word at point and search, no diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index ac95e0286..a22da879f 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -43,21 +43,24 @@ renamed.") ;; auto-save on kill persp-auto-save-opt 1) - (defun +workspaces|init (&rest _) - (unless persp-mode - (persp-mode +1) + (add-hook 'doom-init-hook #'+workspaces|init t) + (add-hook 'after-make-frame-functions #'+workspaces|init) + + (defun +workspaces|init (&optional frame) + (let ((frame (or frame (selected-frame)))) + (unless persp-mode + (persp-mode +1)) ;; The default perspective persp-mode makes (defined by `persp-nil-name') ;; is special and doesn't actually represent a real persp object, so ;; buffers can't really be assigned to it, among other quirks. We create a ;; *real* main workspace to fill this role. - (persp-add-new +workspaces-main) + (unless (persp-with-name-exists-p +workspaces-main) + (persp-add-new +workspaces-main)) ;; Switch to it if we aren't auto-loading the last session - (when (or (= persp-auto-resume-time -1) - (equal (safe-persp-name (get-current-persp)) persp-nil-name)) - (persp-frame-switch +workspaces-main)))) - - (add-hook 'emacs-startup-hook #'+workspaces|init) - (add-hook 'after-make-frame-functions #'+workspaces|init) + (when (or (equal (safe-persp-name (get-current-persp)) persp-nil-name) + (and (one-window-p) + (eq (window-buffer (selected-window)) (doom-fallback-buffer)))) + (persp-frame-switch +workspaces-main frame)))) (define-key persp-mode-map [remap delete-window] #'+workspace/close-window-or-workspace) diff --git a/modules/private/hlissner/+bindings.el b/modules/private/hlissner/+bindings.el index f31765ff7..61fe11755 100644 --- a/modules/private/hlissner/+bindings.el +++ b/modules/private/hlissner/+bindings.el @@ -58,7 +58,7 @@ "M-b" #'+eval/build "M-a" #'mark-whole-buffer "M-c" #'evil-yank - "M-q" #'save-buffers-kill-emacs + "M-q" (if (daemonp) #'delete-frame #'save-buffers-kill-emacs) "M-s" #'save-buffer "M-v" #'clipboard-yank "M-f" #'swiper diff --git a/modules/ui/doom/autoload/doom.el b/modules/ui/doom/autoload/doom.el index 99b6456d7..5cefe3ab4 100644 --- a/modules/ui/doom/autoload/doom.el +++ b/modules/ui/doom/autoload/doom.el @@ -8,7 +8,7 @@ (when theme (mapc #'disable-theme custom-enabled-themes)) (load "doom-themes-common.el" nil t) - (load-theme theme t) + (+doom|init) (+doom|refresh-bright-buffers))) ;;;###autoload diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el index 4cb058c9c..9a2f7f35f 100644 --- a/modules/ui/doom/config.el +++ b/modules/ui/doom/config.el @@ -16,17 +16,33 @@ "Fallback font for unicode glyphs. Is ignored if :feature unicode is active.") -;;; Set fonts -(when (display-graphic-p) +;; Getting themes to remain consistent across GUI Emacs, terminal Emacs and +;; daemon Emacs is hairy. +;; +;; + Running `+doom|init' directly sorts out the initial GUI frame. +;; + Attaching it to `after-make-frame-functions' sorts out daemon Emacs. +;; + Terminal Emacs is a bit of a wildcard. +(defun +doom|init (&optional frame) + "Set the theme and load the font, in that order." + (load-theme +doom-theme t) + (with-demoted-errors "FONT ERROR: %s" - (set-frame-font +doom-font t t) + (set-frame-font +doom-font nil (if frame (list frame) t)) ;; Fallback to `doom-unicode-font' for Unicode characters (unless (featurep! :ui unicode) (when +doom-unicode-font - (set-fontset-font t 'unicode +doom-unicode-font))) + (set-fontset-font t 'unicode +doom-unicode-font frame))) ;; ...and for variable-pitch mode (when +doom-variable-pitch-font - (set-face-attribute 'variable-pitch nil :font +doom-variable-pitch-font)))) + (set-face-attribute 'variable-pitch frame :font +doom-variable-pitch-font)))) + +(defun +doom|init-daemon (frame) + (when (or (daemonp) (not (display-graphic-p))) + (with-selected-frame frame + (run-with-timer 0 nil #'+doom|init)))) + +(add-hook 'after-make-frame-functions #'+doom|init) +(add-hook 'after-make-frame-functions #'+doom|init-daemon) ;; doom-one: gives Emacs a look inspired by Dark One in Atom. @@ -35,13 +51,11 @@ :load-path "~/work/plugins/emacs-doom-themes/" :demand t :config - (load-theme +doom-theme t) - + (+doom|init) ;; blink mode-line on errors - (add-hook 'emacs-startup-hook #'doom-themes-visual-bell-config t) - + (add-hook 'doom-post-init-hook #'doom-themes-visual-bell-config) ;; Add file icons to doom-neotree - (add-hook 'emacs-startup-hook #'doom-themes-neotree-config t) + (add-hook 'doom-post-init-hook #'doom-themes-neotree-config) (setq doom-neotree-enable-variable-pitch t doom-neotree-file-icons 'simple doom-neotree-line-spacing 2) @@ -52,11 +66,6 @@ :foreground "#ffffff" :background (doom-color 'current-line)) - ;; Dark frames by default - (when (display-graphic-p) - (push (cons 'background-color (doom-color 'bg)) initial-frame-alist) - (push (cons 'foreground-color (doom-color 'fg)) initial-frame-alist)) - (after! neotree (defun +doom|neotree-fix-popup () "Ensure the fringe settings are maintained on popup restore." diff --git a/modules/ui/evil-goggles/config.el b/modules/ui/evil-goggles/config.el index 39355da40..77a99cd02 100644 --- a/modules/ui/evil-goggles/config.el +++ b/modules/ui/evil-goggles/config.el @@ -6,4 +6,4 @@ :init (setq evil-goggles-duration 0.1 evil-goggles-enable-delete nil) - (add-hook 'emacs-startup-hook #'evil-goggles-mode t)) + (add-hook 'doom-post-init-hook #'evil-goggles-mode t)) diff --git a/test/run.el b/test/run.el index 421cf1bc2..a12170082 100644 --- a/test/run.el +++ b/test/run.el @@ -2,5 +2,5 @@ (setq-default debug-on-error nil) -(run-hooks 'after-init-hook 'emacs-startup-hook 'window-setup-hook) +(run-hooks 'emacs-startup-hook) (ert-run-tests-batch-and-exit)