diff --git a/core/core-cli.el b/core/core-cli.el index 65a818ca9..0b5e21931 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -592,8 +592,7 @@ modified." (print! (green "✓ Clean up autoloads"))) ;; Byte compile it to give the file a chance to reveal errors. (doom--byte-compile-file doom-autoload-file) - (when (and noninteractive (not (daemonp))) - (doom--server-load doom-autoload-file)) + (doom--server-load doom-autoload-file) t))) @@ -668,8 +667,7 @@ This should be run whenever your `doom!' block or update your packages." (doom--cleanup-package-autoloads) (print! (green "✓ Removed load-path/auto-mode-alist entries")))) (doom--byte-compile-file doom-package-autoload-file) - (when (and noninteractive (not (daemonp))) - (doom--server-load doom-package-autoload-file)) + (doom--server-load doom-package-autoload-file) t)) diff --git a/core/core-lib.el b/core/core-lib.el index 09f897c86..d87a170f7 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -282,13 +282,14 @@ compilation. This will no-op on features that have been disabled by the user." (save-silently t)) ,@forms)))) -(defmacro add-transient-hook! (hook &rest forms) - "Attaches transient forms to a HOOK. +(defmacro add-transient-hook! (hook-or-function &rest forms) + "Attaches a self-removing function to HOOK-OR-FUNCTION. -This means FORMS will be evaluated once when that function/hook is first -invoked, then never again. +FORMS are evaluated once when that function/hook is first invoked, then never +again. -HOOK can be a quoted hook or a sharp-quoted function (which will be advised)." +HOOK-OR-FUNCTION can be a quoted hook or a sharp-quoted function (which will be +advised)." (declare (indent 1)) (let ((append (if (eq (car forms) :after) (pop forms))) (fn (if (symbolp (car forms)) @@ -298,14 +299,14 @@ HOOK can be a quoted hook or a sharp-quoted function (which will be advised)." (fset ',fn (lambda (&rest _) ,@forms - (cond ((functionp ,hook) (advice-remove ,hook #',fn)) - ((symbolp ,hook) (remove-hook ,hook #',fn))) + (cond ((functionp ,hook-or-function) (advice-remove ,hook-or-function #',fn)) + ((symbolp ,hook-or-function) (remove-hook ,hook-or-function #',fn))) (unintern ',fn nil))) - (cond ((functionp ,hook) - (advice-add ,hook ,(if append :after :before) #',fn)) - ((symbolp ,hook) + (cond ((functionp ,hook-or-function) + (advice-add ,hook-or-function ,(if append :after :before) #',fn)) + ((symbolp ,hook-or-function) (put ',fn 'permanent-local-hook t) - (add-hook ,hook #',fn ,append)))))) + (add-hook ,hook-or-function #',fn ,append)))))) (defmacro add-hook! (&rest args) "A convenience macro for `add-hook'. Takes, in order: diff --git a/core/core-ui.el b/core/core-ui.el index 865a6accf..c67888738 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -287,8 +287,7 @@ DEFAULT is non-nil, set the default mode-line for all buffers." (1- (line-end-position))) ((or (eobp) (save-excursion (forward-line) (eobp))) (line-end-position)) - (t - (line-beginning-position 2))))) + ((line-beginning-position 2))))) (setq hl-line-range-function #'doom--line-range)) (after! evil diff --git a/core/core.el b/core/core.el index 14dabc350..83a882928 100644 --- a/core/core.el +++ b/core/core.el @@ -341,30 +341,31 @@ Module load order is determined by your `doom!' block. See `doom-modules-dirs' for a list of all recognized module trees. Order defines precedence (from most to least)." (when (or force-p (not doom-init-p)) - ;; Set this to prevent infinite recursive calls to `doom-initialize' - (setq doom-init-p t) + (setq doom-init-p t) ; Prevent infinite recursion + ;; `doom-autoload-file' tells Emacs where to load all its autoloaded ;; functions from. This includes everything in core/autoload/*.el and all ;; the autoload files in your enabled modules. (when (or force-p (not (doom-initialize-autoloads doom-autoload-file))) (doom-ensure-core-directories) (doom-ensure-same-emacs-version-p) - ;; Ensure packages are set up and initialized + (require 'core-packages) (doom-ensure-packages-initialized force-p) (doom-ensure-core-packages) - ;; Regenerate `doom-autoload-file', which tells Doom where to find all its - ;; module autoloaded functions. + (unless (or force-p noninteractive) (user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them"))) - ;; Loads `doom-package-autoload-file', which caches `load-path', - ;; `auto-mode-alist', `Info-directory-list', `doom-disabled-packages' and + + ;; Loads `doom-package-autoload-file', which loads a concatenated package + ;; autoloads file and caches `load-path', `auto-mode-alist', + ;; `Info-directory-list', `doom-disabled-packages' and ;; `package-activated-list'. A big reduction in startup time. (unless (or force-p (doom-initialize-autoloads doom-package-autoload-file) noninteractive) (user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them"))) - ;; Initialize Doom core + (require 'core-os) (unless noninteractive (add-hook! 'emacs-startup-hook @@ -375,7 +376,8 @@ to least)." (require 'core-keybinds))) (defun doom-initialize-autoloads (file) - "Tries to load FILE (an autoloads file). Return t on success, nil otherwise." + "Tries to load FILE (an autoloads file). Return t on success, throws an error +in interactive sessions, nil otherwise (but logs a warning)." (condition-case e (load (file-name-sans-extension file) 'noerror 'nomessage) ((debug error) diff --git a/modules/feature/snippets/config.el b/modules/feature/snippets/config.el index 80276db41..bbc3a4eeb 100644 --- a/modules/feature/snippets/config.el +++ b/modules/feature/snippets/config.el @@ -24,17 +24,22 @@ yas-also-auto-indent-first-line t yas-prompt-functions (delq #'yas-dropdown-prompt yas-prompt-functions) yas-triggers-in-field t) ; Allow nested snippets + (add-to-list 'yas-snippet-dirs '+snippets-dir nil #'eq) + ;; Register `def-project-mode!' modes with yasnippet. This enables project ;; specific snippet libraries (e.g. for Laravel, React or Jekyll projects). (add-hook 'doom-project-hook #'+snippets|enable-project-modes) + ;; Exit snippets on ESC from normal mode (add-hook 'doom-escape-hook #'yas-abort-snippet) - ;; Fix an error caused by smartparens interfering with yasnippet bindings + (after! smartparens + ;; tell smartparens overlays not to interfere with yasnippet keybinds (advice-add #'yas-expand :before #'sp-remove-active-pair-overlay)) - ;; Better `yas-insert-snippet' for evil users + (when (featurep! :feature evil) + ;; evil visual-mode integration for `yas-insert-snippet' (define-key yas-minor-mode-map [remap yas-insert-snippet] #'+snippets/expand-on-region))) diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index 8875de6ab..76ff118e9 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -13,8 +13,7 @@ ;; NOTE persp-mode requires `workgroups' for file persistence in Emacs 24.4. (defvar +workspaces-main "main" - "The name of the primary and initial workspace, which cannot be deleted or -renamed.") + "The name of the primary and initial workspace, which cannot be deleted.") (defvar +workspaces-switch-project-function #'doom-project-find-file "The function to run after `projectile-switch-project' or @@ -27,7 +26,7 @@ new project directory.") stored in `persp-save-dir'.") ;; If emacs is passed --restore, restore the last session on startup. This is -;; particularly useful for the `+workspace/restart-emacs-then-restore' command. +;; used by the `+workspace/restart-emacs-then-restore' command. (defun +workspaces-restore-last-session (&rest _) (add-hook 'emacs-startup-hook #'+workspace/load-session :append)) (add-to-list 'command-switch-alist (cons "--restore" #'+workspaces-restore-last-session)) @@ -51,7 +50,7 @@ stored in `persp-save-dir'.") (+workspaces|init-frame (selected-frame)))) (defun +workspaces|init-frame (frame) - "Make sure a main workspace exists and is switched to, if FRAME isn't in any + "Ensure a main workspace exists and is switched to, if FRAME isn't in any workspace. Also ensures that the *Warnings* buffer will be visible in main. Uses `+workspaces-main' to determine the name of the main workspace." @@ -60,10 +59,10 @@ Uses `+workspaces-main' to determine the name of the main workspace." (unless noninteractive (let (persp-before-switch-functions persp-activated-functions) (with-selected-frame frame - ;; 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. + ;; The default perspective persp-mode creates (`persp-nil-name') is + ;; special and doesn't 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. (unless (persp-get-by-name +workspaces-main) (persp-add-new +workspaces-main)) ;; Switch to it if we aren't auto-loading the last session @@ -73,9 +72,8 @@ Uses `+workspaces-main' to determine the name of the main workspace." ;; We want to know where we are in every new daemon frame (when (daemonp) (run-at-time 0.1 nil #'+workspace/display)) - ;; The warnings buffer gets swallowed by creating - ;; `+workspaces-main', so we display it manually, if it exists (fix - ;; #319). + ;; Fix #319: the warnings buffer gets swallowed by creating + ;; `+workspaces-main', so we display it manually, if it exists. (when-let* ((warnings (get-buffer "*Warnings*"))) (save-excursion (display-buffer-in-side-window @@ -93,21 +91,22 @@ Uses `+workspaces-main' to determine the name of the main workspace." persp-auto-resume-time -1 ; Don't auto-load on startup persp-auto-save-opt (if noninteractive 0 1)) ; auto-save on kill + (advice-add #'persp-asave-on-exit :around #'+workspaces*autosave-real-buffers) + + (add-hook 'doom-cleanup-hook #'+workspaces|cleanup-unassociated-buffers) + ;; Ensure buffers we've opened/switched to are auto-added to the current ;; perspective (setq persp-add-buffer-on-find-file t persp-add-buffer-on-after-change-major-mode t) (add-hook 'persp-add-buffer-on-after-change-major-mode-filter-functions #'doom-unreal-buffer-p) - ;; bootstrap (defun +workspaces|init-persp-mode () (cond (persp-mode - ;; Ensure `persp-kill-buffer-query-function' is last in - ;; kill-buffer-query-functions + ;; `persp-kill-buffer-query-function' must be last (remove-hook 'kill-buffer-query-functions 'persp-kill-buffer-query-function) (add-hook 'kill-buffer-query-functions 'persp-kill-buffer-query-function t) - ;; Remap `buffer-list' to current workspace's buffers in - ;; `doom-buffer-list' + ;; Restrict buffer list to workspace (advice-add #'doom-buffer-list :override #'+workspace-buffer-list)) ((advice-remove #'doom-buffer-list #'+workspace-buffer-list)))) (add-hook 'persp-mode-hook #'+workspaces|init-persp-mode) @@ -120,22 +119,16 @@ Uses `+workspaces-main' to determine the name of the main workspace." 'auto-create))) (add-hook 'persp-after-load-state-functions #'+workspaces|leave-nil-perspective) - ;; Modify `delete-window' to close the workspace if used on the last window + ;; Delete the current workspace if closing the last open window (define-key! persp-mode-map - [remap restart-emacs] #'+workspace/restart-emacs-then-restore [remap delete-window] #'+workspace/close-window-or-workspace [remap evil-delete-window] #'+workspace/close-window-or-workspace) - ;; only auto-save when real buffers are present - (advice-add #'persp-asave-on-exit :around #'+workspaces*autosave-real-buffers) - ;; On `doom/cleanup-session', delete buffers associated with no perspectives - (add-hook 'doom-cleanup-hook #'+workspaces|cleanup-unassociated-buffers) ;; per-frame workspaces (setq persp-init-frame-behaviour t persp-init-new-frame-behaviour-override nil persp-interactive-init-frame-behaviour-override #'+workspaces|associate-frame persp-emacsclient-init-frame-behaviour-override #'+workspaces|associate-frame) - ;; delete frame associated with workspace, if it exists (add-hook 'delete-frame-functions #'+workspaces|delete-associated-workspace) ;; per-project workspaces, but reuse current workspace if empty @@ -163,8 +156,8 @@ Uses `+workspaces-main' to determine the name of the main workspace." (add-hook 'projectile-after-switch-project-hook #'+workspaces|switch-to-project) - ;; In some scenarios, persp-mode throws error an error when Emacs tries to - ;; die, preventing its death. + ;; In some scenarios, persp-mode throws error when Emacs tries to die, + ;; preventing its death and trapping us in Emacs. (defun +workspaces*ignore-errors-on-kill-emacs (orig-fn) (ignore-errors (funcall orig-fn))) (advice-add #'persp-kill-emacs-h :around #'+workspaces*ignore-errors-on-kill-emacs) diff --git a/modules/lang/org/+attach.el b/modules/lang/org/+attach.el index ac9160886..09ed06832 100644 --- a/modules/lang/org/+attach.el +++ b/modules/lang/org/+attach.el @@ -31,11 +31,9 @@ (def-package! org-download :commands (org-download-dnd org-download-dnd-base64) :init - ;; Add these myself, so that org-download is lazy-loaded... - (setq dnd-protocol-alist - `(("^\\(https?\\|ftp\\|file\\|nfs\\):" . +org-attach-download-dnd) - ("^data:" . org-download-dnd-base64) - ,@dnd-protocol-alist)) + ;; Add these manually so that org-download is lazy-loaded... + (add-to-list 'dnd-protocol-alist '("^\\(https?\\|ftp\\|file\\|nfs\\):" . +org-attach-download-dnd)) + (add-to-list 'dnd-protocol-alist '("^data:" . org-download-dnd-base64)) (advice-add #'org-download-enable :override #'ignore) :config @@ -78,8 +76,7 @@ (setq org-attach-directory (expand-file-name +org-attach-dir org-directory)) ;; A shorter link to attachments - (push (cons "attach" (abbreviate-file-name org-attach-directory)) - org-link-abbrev-alist) + (add-to-list 'org-link-abbrev-alist (cons "attach" (abbreviate-file-name org-attach-directory))) (org-link-set-parameters "attach" @@ -93,10 +90,9 @@ 'error))) (after! projectile - (push (car (last (split-string +org-attach-dir "/" t))) - projectile-globally-ignored-directories)) + (add-to-list 'projectile-globally-ignored-directories + (car (last (split-string +org-attach-dir "/" t))))) (after! recentf - (push (format "%s.+$" (regexp-quote org-attach-directory)) - recentf-exclude))) + (add-to-list 'recentf-exclude (format "%s.+$" (regexp-quote org-attach-directory))))) diff --git a/modules/lang/org/+babel.el b/modules/lang/org/+babel.el index ab151c4bd..0d4ec79ca 100644 --- a/modules/lang/org/+babel.el +++ b/modules/lang/org/+babel.el @@ -40,12 +40,11 @@ string). Stops at the first function to return non-nil.") t))) (advice-add #'org-babel-confirm-evaluate :around #'+org*babel-lazy-load-library) - ;; I prefer C-c C-c for confirming over the default C-c ' + ;; I prefer C-c C-c over C-c ' (define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit) - ;; In a recent update, `org-babel-get-header' was removed from org-mode, which - ;; is something a fair number of babel plugins use. So until those plugins - ;; update, this polyfill will do: + ;; `org-babel-get-header' was removed from org in 9.0. Quite a few babel + ;; plugins use it, so until those plugins update, this polyfill will do: (defun org-babel-get-header (params key &optional others) (cl-loop with fn = (if others #'not #'identity) for p in params diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el index 9fca302b7..343e341b4 100644 --- a/modules/lang/org/autoload/org.el +++ b/modules/lang/org/autoload/org.el @@ -268,7 +268,7 @@ wrong places)." (org-toggle-checkbox '(4))) ;;;###autoload -(defalias #'+org/toggle-fold #'+org|toggle-only-current-fold) +(defalias #'+org/toggle-fold #'+org|cycle-only-current-subtree) ;;;###autoload (defun +org/open-fold () @@ -319,7 +319,7 @@ another level of headings on each invocation." ;; ;;;###autoload -(defun +org|delete-backward-char () +(defun +org|delete-backward-char-and-realign-table-maybe () "TODO" (when (eq major-mode 'org-mode) (org-check-before-invisible-edit 'delete-backward) @@ -388,7 +388,7 @@ another level of headings on each invocation." t)) ;;;###autoload -(defun +org|toggle-only-current-fold (&optional arg) +(defun +org|cycle-only-current-subtree (&optional arg) "Toggle the local fold at the point (as opposed to cycling through all levels with `org-cycle')." (interactive "P") @@ -416,7 +416,7 @@ with `org-cycle')." ;; ;;;###autoload -(defun +org*return-indent-in-src-blocks () +(defun +org*fix-newline-and-indent-in-src-blocks () "Try to mimic `newline-and-indent' with correct indentation in src blocks." (when (org-in-src-block-p t) (org-babel-do-in-edit-buffer diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index a5690b240..1cada2d6e 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -30,7 +30,7 @@ (add-hook 'org-load-hook #'+org|setup-evil) (add-hook 'evil-org-mode-hook #'evil-normalize-keymaps) :config - ;; only support the `evil-org-key-theme' workflow + ;; change `evil-org-key-theme' instead (advice-add #'evil-org-set-key-theme :override #'ignore) (def-package! evil-org-agenda :after org-agenda @@ -44,7 +44,7 @@ (add-hook! 'org-load-hook #'(org-crypt-use-before-save-magic +org|setup-ui - +org|setup-popups-rules + +org|setup-popup-rules +org|setup-agenda +org|setup-keybinds +org|setup-hacks @@ -132,7 +132,7 @@ unfold to point on startup." org-agenda-start-on-weekday nil org-agenda-start-day "-3d")) -(defun +org|setup-popups-rules () +(defun +org|setup-popup-rules () "Defines popup rules for org-mode (does nothing if :ui popup is disabled)." (set-popup-rules! '(("^\\*Org Links" :slot -1 :vslot -1 :size 2 :ttl 0) @@ -193,7 +193,7 @@ unfold to point on startup." ;; Previews are usually rendered with light backgrounds, so ensure their ;; background (and foreground) match the current theme. - (defun +org|update-latex-faces () + (defun +org|update-latex-preview-background-color () (setq-default org-format-latex-options (plist-put org-format-latex-options @@ -201,7 +201,7 @@ unfold to point on startup." (face-attribute (or (cadr (assq 'default face-remapping-alist)) 'default) :background nil t)))) - (add-hook 'doom-load-theme-hook #'+org|update-latex-faces) + (add-hook 'doom-load-theme-hook #'+org|update-latex-preview-background-color) ;; Custom links (setq org-link-abbrev-alist @@ -246,18 +246,18 @@ unfold to point on startup." "Sets up org-mode and evil keybindings. Tries to fix the idiosyncrasies between the two." (add-hook 'doom-escape-hook #'+org|remove-occur-highlights) + ;; C-a & C-e act like `doom/backward-to-bol-or-indent' and ;; `doom/forward-to-last-non-comment-or-eol', but with more org awareness. (setq org-special-ctrl-a/e t) - ;; Try indenting normally or expanding snippets on TAB - (add-hook! 'org-tab-first-hook #'(+org|indent-maybe +org|yas-expand-maybe)) - ;; Tell `doom/delete-backward-char' to respect org tables - (add-hook 'doom-delete-backward-functions #'+org|delete-backward-char) - ;; Don't split current tree on M-RET + (setq org-M-RET-may-split-line nil ;; insert new headings after current subtree rather than inside it org-insert-heading-respect-content t) - ;; Custom keybinds + + (add-hook! 'org-tab-first-hook #'(+org|indent-maybe +org|yas-expand-maybe)) + (add-hook 'doom-delete-backward-functions #'+org|delete-backward-char-and-realign-table-maybe) + (define-key! org-mode-map (kbd "C-c C-S-l") #'+org/remove-link (kbd "C-c C-i") #'org-toggle-inline-images @@ -268,20 +268,20 @@ between the two." ;; In case this hook is used in an advice on `evil-org-set-key-theme', this ;; prevents recursive requires. (unless args (require 'evil-org)) - ;; By default, TAB cycles the visibility of all children under the current - ;; tree between three states. I want to toggle the tree between two states, - ;; without affecting its children. - (add-hook 'org-tab-first-hook #'+org|toggle-only-current-fold t) - ;; Fix newline-and-indent behavior in src blocks - (advice-add #'org-return-indent :after #'+org*return-indent-in-src-blocks) + + (add-hook 'org-tab-first-hook #'+org|cycle-only-current-subtree t) + (advice-add #'org-return-indent :after #'+org*fix-newline-and-indent-in-src-blocks) + ;; Fix o/O creating new list items in the middle of nested plain lists. Only ;; has an effect when `evil-org-special-o/O' has `item' in it (not the ;; default). (advice-add #'evil-org-open-below :around #'+org*evil-org-open-below) - ;; Undo `evil-collection-outline' + + ;; Undo keybinds in `evil-collection-outline' (evil-define-key* 'normal outline-mode-map "^" nil [backtab] nil + "\M-j" nil "\M-k" nil "\C-j" nil "\C-k" nil "]" nil "[" nil) (evil-define-key* 'insert evil-org-mode-map @@ -346,6 +346,7 @@ between the two." "Getting org to behave." ;; Don't open separate windows (setf (alist-get 'file org-link-frame-setup) #'find-file) + ;; Fix variable height org-level-N faces in the eldoc string (defun +org*fix-font-size-variation-in-eldoc (orig-fn) (cl-letf (((symbol-function 'org-format-outline-path) @@ -360,7 +361,7 @@ between the two." separator)))) (funcall orig-fn))) (advice-add #'org-eldoc-get-breadcrumb :around #'+org*fix-font-size-variation-in-eldoc) - ;; Let OS decide what to do with files when opened + (setq org-file-apps `(("pdf" . default) ("\\.x?html?\\'" . default) @@ -368,7 +369,7 @@ between the two." (directory . emacs) (t . ,(cond (IS-MAC "open -R \"%s\"") (IS-LINUX "xdg-open \"%s\""))))) - ;; Don't clobber recentf or current workspace with agenda files + (defun +org|exclude-agenda-buffers-from-workspace () (when org-agenda-new-buffers (let (persp-autokill-buffer-on-remove) diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index a1631d1f8..86e895cfa 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -29,12 +29,12 @@ load everything.") magit-popup-display-buffer-action '((display-buffer-in-side-window))) (set-popup-rule! "^\\(?:\\*magit\\|magit:\\)" :ignore t) - ;; Consider magit buffers real (so they can switched to) + ;; so magit buffers can be switched to (add-hook 'magit-mode-hook #'doom|mark-buffer-as-real) - ;; no mode-line in magit popups + ;; modeline isn't helpful in magit (add-hook! '(magit-mode-hook magit-popup-mode-hook) #'hide-mode-line-mode) - ;; Clean up after magit by properly killing buffers + ;; properly kill leftover magit buffers on quit (define-key magit-status-mode-map [remap magit-mode-bury-buffer] #'+magit/quit))