diff --git a/Makefile b/Makefile index ca088d921..5bc6eb0f5 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,18 @@ DOOM = "bin/doom" MODULES = $(patsubst modules/%/, %, $(sort $(dir $(wildcard modules/*/ modules/*/*/)))) -all: +all: deprecated @$(DOOM) refresh +deprecated: + @echo "Using make to manage your Doom config is deprecated" + @echo + @echo "Use the 'bin/doom' script instead. The equivalent of 'make' is 'doom refresh'." + @echo + @echo "See 'doom help' for a list of commands" + @echo + @read -p "Press enter to continue" + ## Shortcuts a: autoloads i: install @@ -16,34 +25,33 @@ cp: compile-plugins re: recompile d: doctor -quickstart: - @$(DOOM) quickstart +quickstart: install ## Package management -install: +install: deprecated @$(DOOM) install -update: +update: deprecated @$(DOOM) update -autoremove: +autoremove: deprecated @$(DOOM) autoremove -autoloads: +autoloads: deprecated @$(DOOM) autoloads -upgrade: +upgrade: deprecated @$(DOOM) upgrade ## Byte compilation -compile: +compile: deprecated @$(DOOM) compile -compile-core: +compile-core: deprecated @$(DOOM) compile :core -compile-private: +compile-private: deprecated @$(DOOM) compile :private -compile-plugins: - @$(DOOM) compile :plugins -recompile: +compile-plugins: deprecated + @$(DOOM) build +recompile: deprecated @$(DOOM) recompile -clean: +clean: deprecated @$(DOOM) clean # compile-module # compile-module/submodule diff --git a/README.md b/README.md index dd499db2e..2ca9060d1 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ **Quick start** ```bash git clone https://github.com/hlissner/doom-emacs ~/.emacs.d -~/.emacs.d/bin/doom quickstart +~/.emacs.d/bin/doom install ``` **Table of Contents** @@ -109,7 +109,7 @@ Feature Highlights support for a variety of languages. - A jump-to-definition/references implementation for all languages that tries to "just work," resorting to mode-specific functionality, before falling back on - [dump-jump][url:dump-jump]. + [dumb-jump][url:dumb-jump]. Troubleshooting @@ -178,7 +178,7 @@ contributions: [url:company-mode]: https://github.com/company-mode/company-mode [url:doom-themes]: https://github.com/hlissner/emacs-doom-themes -[url:dump-jump]: https://github.com/jacktasia/dumb-jump +[url:dumb-jump]: https://github.com/jacktasia/dumb-jump [url:editorconfig]: http://editorconfig.org/ [url:evil-mode]: https://github.com/emacs-evil/evil [url:helm]: https://github.com/emacs-helm/helm diff --git a/bin/doom b/bin/doom index 571a84a3e..416c861e7 100755 --- a/bin/doom +++ b/bin/doom @@ -10,6 +10,11 @@ ":"; exec $EMACS --script "$0" -- "$@" ":"; exit 0 +(setq user-emacs-directory + (or (getenv "EMACSDIR") + (expand-file-name "../" (file-name-directory (file-truename load-file-name))))) + + (defun usage () (with-temp-buffer (insert (format! "%s %s [COMMAND] [ARGS...]\n" @@ -35,15 +40,14 @@ " -d --debug\t\tTurns on doom-debug-mode (and debug-on-error)\n" " -e --emacsd DIR\tUse the emacs config at DIR (e.g. ~/.emacs.d)\n" " -i --insecure\t\tDisable TLS/SSL validation (not recommended)\n" + " -l --local DIR\tUse DIR as your local storage directory\n" " -p --private DIR\tUse the private module at DIR (e.g. ~/.doom.d)\n" " -y --yes\t\tAuto-accept all confirmation prompts\n\n") (princ (buffer-string))) (doom--dispatch-help)) ;; -(let ((args (cdr (cdr (cdr command-line-args)))) - (emacs-dir (or (getenv "EMACSDIR") - (expand-file-name "../" (file-name-directory (file-truename load-file-name)))))) +(let ((args (cdr (cdr (cdr command-line-args))))) ;; Parse options (while (ignore-errors (string-prefix-p "-" (car args))) (pcase (pop args) @@ -62,47 +66,57 @@ (or (file-directory-p doom-private-dir) (message "Warning: %s does not exist" (abbreviate-file-name doom-private-dir)))) + ((or "-l" "--local") + (setq doom-local-dir (expand-file-name (concat (pop args) "/"))) + (setenv "DOOMLOCALDIR" doom-local-dir) + (message "DOOMLOCALDIR changed to %s" doom-local-dir)) ((or "-e" "--emacsd") - (setq emacs-dir (expand-file-name (concat (pop args) "/"))) - (message "Emacs directory changed to %s" emacs-dir)) + (setq user-emacs-directory (expand-file-name (concat (pop args) "/"))) + (message "Emacs directory changed to %s" user-emacs-directory)) ((or "-y" "--yes") (setenv "YES" "1") (message "Auto-yes mode on")))) - (or (file-directory-p emacs-dir) - (error "%s does not exist" emacs-dir)) + (unless (file-directory-p user-emacs-directory) + (error "%s does not exist" user-emacs-directory)) ;; Bootstrap Doom - (load (expand-file-name "init" emacs-dir) - nil 'nomessage) + (if (not noninteractive) + (progn + (load (expand-file-name "init.el" user-emacs-directory) + nil 'nomessage) + (doom-run-all-startup-hooks-h)) + (load (expand-file-name "core/core.el" user-emacs-directory) + nil 'nomessage) + (doom-initialize 'force-p) + (doom-initialize-modules) - (cond ((not noninteractive) - (doom|run-all-startup-hooks)) - ((and (not (cdr args)) - (member (car args) '("help" "h"))) - (usage)) - ((not args) - (message "No command detected, aborting!\n\nRun %s help for documentation." - (file-name-nondirectory load-file-name))) - ((let ((default-directory emacs-dir)) - (setq argv nil - noninteractive 'doom) - (condition-case e - (doom-dispatch (car args) (cdr args)) - (user-error - (signal (car e) (cdr e))) - ((debug error) - (message "--------------------------------------------------\n") - (message "There was an unexpected error:") - (message " %s (%s)" (get (car e) 'error-message) (car e)) - (dolist (item (cdr e)) - (message " %s" item)) - (unless debug-on-error - (message - (concat "\nRun the command again with the -d (or --debug) option to enable debug\n" - "mode and, hopefully, generate a stack trace. If you decide to file a bug\n" - "report, please include it!\n\n" - "Emacs outputs to standard error, so you'll need to redirect stderr to\n" - "stdout to pipe this to a file or clipboard!\n\n" - " e.g. doom -d install 2>&1 | clipboard-program")) - (signal 'doom-error e)))))))) + (cond ((and (not (cdr args)) + (member (car args) '("help" "h"))) + (usage)) + ((not args) + (print! (error "No command detected.\n")) + (usage)) + ((require 'core-cli) + (let ((default-directory user-emacs-directory)) + (setq argv nil) + (condition-case e + (doom-dispatch (car args) (cdr args)) + (user-error + (print! (error "%s\n") (error-message-string e)) + (print! (yellow "See 'doom help %s' for documentation on this command.") (car args))) + ((debug error) + (message "--------------------------------------------------\n") + (message "There was an unexpected error:") + (message " %s (%s)" (get (car e) 'error-message) (car e)) + (dolist (item (cdr e)) + (message " %s" item)) + (unless debug-on-error + (message + (concat "\nRun the command again with the -d (or --debug) option to enable debug\n" + "mode and, hopefully, generate a stack trace. If you decide to file a bug\n" + "report, please include it!\n\n" + "Emacs outputs to standard error, so you'll need to redirect stderr to\n" + "stdout to pipe this to a file or clipboard!\n\n" + " e.g. doom -d install 2>&1 | clipboard-program")) + (signal 'doom-error e))))))))) diff --git a/bin/doom-doctor b/bin/doom-doctor index 8560bd086..a3f54e98a 100755 --- a/bin/doom-doctor +++ b/bin/doom-doctor @@ -18,7 +18,8 @@ ;; specified by the EMACSDIR envvar) (setq user-emacs-directory (or (getenv "EMACSDIR") - (expand-file-name "../" (file-name-directory load-file-name)))) + (expand-file-name "../" (file-name-directory (file-truename load-file-name)))) + default-directory user-emacs-directory) (unless (file-directory-p user-emacs-directory) (error "Couldn't find a Doom config!")) @@ -27,8 +28,9 @@ (when (getenv "DEBUG") (setq debug-on-error t)) +(require 'subr-x) (require 'pp) -(load (expand-file-name "core/autoload/message" user-emacs-directory) nil t) +(load (expand-file-name "core/autoload/format" user-emacs-directory) nil t) (defvar doom-init-p nil) @@ -147,7 +149,7 @@ emacs-version) (explain! "Byte-code compiled in one version of Emacs may not work in another version." "It is recommended that you reinstall your plugins or recompile them with" - "`bin/doom compile :plugins'."))) + "`bin/doom rebuild'."))) (section! "Checking for Emacs config conflicts...") (when (file-exists-p "~/.emacs") @@ -182,109 +184,7 @@ ;; on windows? (when (memq system-type '(windows-nt ms-dos cygwin)) (warn! "Warning: Windows detected") - (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")) - - ;; gnutls-cli & openssl - (section! "Checking gnutls/openssl...") - (cond ((executable-find "gnutls-cli")) - ((executable-find "openssl") - (let* ((output (sh "openssl ciphers -v")) - (protocols - (let (protos) - (mapcar (lambda (row) - (add-to-list 'protos (cadr (split-string row " " t)))) - (split-string (sh "openssl ciphers -v") "\n")) - (delq nil protos)))) - (unless (or (member "TLSv1.1" protocols) - (member "TLSv1.2" protocols)) - (let ((version (cadr (split-string (sh "openssl version") " " t)))) - (warn! "Warning: couldn't find gnutls-cli, and OpenSSL is out-of-date (v%s)" version) - (explain! - "This may not affect your Emacs experience, but there are security " - "vulnerabilities in the SSL2/3 & TLS1.0 protocols. You should use " - "TLS 1.1+, which wasn't introduced until OpenSSL v1.0.1.\n\n" - - "Please consider updating (or install gnutls-cli, which is preferred)."))))) - (t - (error! "Important: couldn't find either gnutls-cli nor openssl") - (explain! - "You may not be able to install/update packages because Emacs won't be able to " - "verify HTTPS ELPA sources. Install gnutls-cli or openssl v1.0.0+. If for some " - "reason you can't, you can bypass this verification with the INSECURE flag:\n\n" - - " INSECURE=1 make install\n\n" - - "Or change `package-archives' to use non-https sources.\n\n" - - "But remember that you're leaving your security in the hands of your " - "network, provider, government, neckbearded mother-in-laws, geeky roommates, " - "or just about anyone who knows more about computers than you do!"))) - - ;; are certificates validated properly? - (section! "Testing your root certificates...") - (cond ((not (ignore-errors (gnutls-available-p))) - (warn! "Warning: Emacs wasn't installed with gnutls support") - (explain! - "This may cause 'pecular error' errors with the Doom doctor, and is likely to " - "interfere with package management. Your mileage may vary." - (when (eq system-type 'darwin) - (concat "\nMacOS users are advised to install Emacs via homebrew with one of the following:\n" - " brew install emacs --with-gnutls" - " or" - " brew tap d12frosted/emacs-plus" - " brew install emacs-plus")))) - - ((not (fboundp 'url-retrieve-synchronously)) - (error! "Can't find url-retrieve-synchronously function. Are you sure you're on Emacs 24+?")) - - ((or (executable-find "gnutls-cli") - (executable-find "openssl")) - (let ((tls-checktrust t) - (gnutls-verify-error t)) - (dolist (url '("https://elpa.gnu.org" "https://melpa.org")) - (pcase (condition-case-unless-debug e - (unless (let ((inhibit-message t)) (url-retrieve-synchronously url)) - 'empty) - ('timed-out 'timeout) - ('error e)) - (`nil nil) - (`empty (error! "Couldn't reach %s" url)) - (`timeout (error! "Timed out trying to contact %s" ex)) - (it - (error! "Failed to validate %s" url) - (explain! (pp-to-string it))))) - (dolist (url '("https://self-signed.badssl.com" - "https://wrong.host.badssl.com/")) - (pcase (condition-case-unless-debug e - (if (let ((inhibit-message t)) (url-retrieve-synchronously url)) - t - 'empty) - ('timed-out 'timeout) - ('error)) - (`nil nil) - (`empty (error! "Couldn't reach %s" url)) - (`timeout (error! "Timed out trying to contact %s" ex)) - (_ - (error! "Validated %s (this shouldn't happen!)" url))))))) - - ;; which variant of tar is on your system? bsd or gnu tar? - (section! "Checking for GNU/BSD tar...") - (let ((tar-bin (or (executable-find "gtar") - (executable-find "tar")))) - (if tar-bin - (unless (string-match-p "(GNU tar)" (sh "%s --version" tar-bin)) - (warn! "Warning: BSD tar detected") - (explain! - "QUELPA (through package-build) uses the system tar to build plugins, but it " - "expects GNU tar. BSD tar *could* cause errors during package installation or " - "updating from non-ELPA sources." - (when (eq system-type 'darwin) - (concat "\nMacOS users can install gnu-tar via homebrew:\n" - " brew install gnu-tar")))) - (error! "Important: Couldn't find tar") - (explain! - "This is required by package.el and QUELPA to build packages and will " - "prevent you from installing & updating packages.")))) + (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!"))) ;; @@ -292,24 +192,17 @@ (condition-case-unless-debug ex (let ((after-init-time (current-time)) - (doom-message-backend 'ansi) + (doom-format-backend 'ansi) noninteractive) (section! "Checking DOOM Emacs...") (load (concat user-emacs-directory "core/core.el") nil t) (unless (file-directory-p doom-private-dir) - (error "No DOOMDIR was found, did you run `doom quickstart` yet?")) + (error "No DOOMDIR was found, did you run `doom install` yet?")) (let ((indent 2)) - ;; Make sure everything is loaded - (require 'core-cli) - (require 'core-keybinds) - (require 'core-ui) - (require 'core-projects) - (require 'core-editor) - (require 'core-packages) - - ;; ...and initialized - (doom-initialize) + ;; Make sure Doom is initialized and loaded + (doom-initialize 'force) + (doom-initialize-core) (success! "Initialized Doom Emacs %s" doom-version) (doom-initialize-modules) @@ -332,26 +225,23 @@ (when doom-modules (section! "Checking your enabled modules...") (let ((indent (+ indent 2))) - (advice-add #'require :around #'doom*shut-up) + (advice-add #'require :around #'doom-shut-up-a) (maphash (lambda (key plist) (let ((prefix (format! (bold "(%s %s) " (car key) (cdr key))))) (condition-case-unless-debug ex (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el")) (packages-file (doom-module-path (car key) (cdr key) "packages.el"))) - (cl-loop with doom--stage = 'packages - for name in (let (doom-packages + (cl-loop for name in (let (doom-packages doom-disabled-packages) (load packages-file 'noerror 'nomessage) (mapcar #'car doom-packages)) - for name = (doom-package-true-name name) - unless (or (doom-package-prop name :disable) - (eval (doom-package-prop name :ignore)) - (package-built-in-p name) - (package-installed-p name)) + unless (or (doom-package-get name :disable) + (eval (doom-package-get name :ignore)) + (doom-package-built-in-p name) + (doom-package-installed-p name)) do (error! "%s is not installed" name)) - (let ((doom--stage 'doctor)) - (load doctor-file 'noerror 'nomessage))) + (load doctor-file 'noerror 'nomessage)) (file-missing (error! "%s" (error-message-string ex))) (error (error! "Syntax error: %s" ex))))) doom-modules))))) diff --git a/core/autoload/buffers.el b/core/autoload/buffers.el index 1074a5645..4e7b74969 100644 --- a/core/autoload/buffers.el +++ b/core/autoload/buffers.el @@ -250,46 +250,11 @@ regex PATTERN. Returns the number of killed buffers." ;; Hooks ;;;###autoload -(defun doom|mark-buffer-as-real () +(defun doom-mark-buffer-as-real-h () "Hook function that marks the current buffer as real." (doom-set-buffer-real (current-buffer) t)) -;; -;; Advice - -;;;###autoload -(defun doom*switch-to-fallback-buffer-maybe (orig-fn) - "Advice for `kill-current-buffer'. If in a dedicated window, delete it. If there -are no real buffers left OR if all remaining buffers are visible in other -windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original -`kill-current-buffer'." - (let ((buf (current-buffer))) - (cond ((window-dedicated-p) - (delete-window)) - ((eq buf (doom-fallback-buffer)) - (message "Can't kill the fallback buffer.")) - ((doom-real-buffer-p buf) - (if (and buffer-file-name - (buffer-modified-p buf) - (not (y-or-n-p - (format "Buffer %s is modified; kill anyway?" buf)))) - (message "Aborted") - (set-buffer-modified-p nil) - (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 (switch-to-prev-buffer nil t) (list buf 'nil))) - (switch-to-buffer (doom-fallback-buffer))) - (unless (delq (selected-window) (get-buffer-window-list buf nil t)) - (kill-buffer buf))))) - ((funcall orig-fn))))) - - ;; ;; Interactive commands diff --git a/core/autoload/cache.el b/core/autoload/cache.el index 235f6243a..79db071dd 100644 --- a/core/autoload/cache.el +++ b/core/autoload/cache.el @@ -1,4 +1,4 @@ -;;; ../core/autoload/cache.el -*- lexical-binding: t; -*- +;;; core/autoload/cache.el -*- lexical-binding: t; -*- ;; This little library thinly wraps around persistent-soft (which is a pcache ;; wrapper, how about that). It has three purposes: @@ -21,7 +21,7 @@ to persist across Emacs sessions.") name under `pcache-directory' (by default a subdirectory under `doom-cache-dir'). One file may contain multiple cache entries.") -(defun doom|save-persistent-cache () +(defun doom-save-persistent-cache-h () "Hook to run when an Emacs session is killed. Saves all persisted variables listed in `doom-cache-alists' to files." (dolist (alist (butlast doom-cache-alists 1)) @@ -29,7 +29,7 @@ listed in `doom-cache-alists' to files." for var in (cdr alist) if (symbol-value var) do (doom-cache-set var it nil key)))) -(add-hook 'kill-emacs-hook #'doom|save-persistent-cache) +(add-hook 'kill-emacs-hook #'doom-save-persistent-cache-h) ;; diff --git a/core/autoload/cli.el b/core/autoload/cli.el index 693a31250..9eb3f8406 100644 --- a/core/autoload/cli.el +++ b/core/autoload/cli.el @@ -1,15 +1,18 @@ ;;; core/autoload/cli.el -*- lexical-binding: t; -*- +;; Externs +(defvar evil-collection-mode-list) + (require 'core-cli) ;;;###autoload -(defun doom-cli-run (command &rest _args) +(defun doom--cli-run (command &rest _args) (when (featurep 'general) (general-auto-unbind-keys)) (let* ((evil-collection-mode-list nil) (default-directory doom-emacs-dir) (buf (get-buffer-create " *bin/doom*")) - (doom-message-backend 'ansi) + (doom-format-backend 'ansi) (ignore-window-parameters t) (noninteractive t) (standard-output @@ -39,21 +42,21 @@ "TODO" (interactive "P") (let ((doom-auto-accept yes)) - (doom-cli-run "autoloads"))) + (doom--cli-run "autoloads"))) ;;;###autoload (defun doom//update (&optional yes) "TODO" (interactive "P") (let ((doom-auto-accept yes)) - (doom-cli-run "update"))) + (doom--cli-run "update"))) ;;;###autoload (defun doom//upgrade (&optional yes) "TODO" (interactive "P") (let ((doom-auto-accept yes)) - (doom-cli-run "upgrade")) + (doom--cli-run "upgrade")) (when (y-or-n-p "You must restart Emacs for the upgrade to take effect. Restart?") (doom/restart-and-restore))) @@ -62,18 +65,18 @@ "TODO" (interactive "P") (let ((doom-auto-accept yes)) - (doom-cli-run "install"))) + (doom--cli-run "install"))) ;;;###autoload (defun doom//autoremove (&optional yes) "TODO" (interactive "P") (let ((doom-auto-accept yes)) - (doom-cli-run "autoremove"))) + (doom--cli-run "autoremove"))) ;;;###autoload (defun doom//refresh (&optional yes) "TODO" (interactive "P") (let ((doom-auto-accept yes)) - (doom-cli-run "refresh"))) + (doom--cli-run "refresh"))) diff --git a/core/autoload/config.el b/core/autoload/config.el index e4b3548ec..9439bc96c 100644 --- a/core/autoload/config.el +++ b/core/autoload/config.el @@ -1,5 +1,9 @@ ;;; core/autoload/config.el -*- lexical-binding: t; -*- +;;;###autoload +(defvar doom-reload-hook nil + "A list of hooks to run when `doom/reload' is called.") + ;;;###autoload (defvar doom-reloading-p nil "TODO") @@ -19,7 +23,7 @@ (doom-project-find-file doom-private-dir)) ;;;###autoload -(defun doom/reload (&optional force-p) +(defun doom/reload () "Reloads your private config. This is experimental! It will try to do as `bin/doom refresh' does, but from @@ -27,26 +31,29 @@ within this Emacs session. i.e. it reload autoloads files (if necessary), reloads your package list, and lastly, reloads your private config.el. Runs `doom-reload-hook' afterwards." - (interactive "P") + (interactive) + (or (y-or-n-p + (concat "You are about to reload your Doom config from within Emacs. This " + "is highly experimental and may cause issues. It is recommended you " + "use 'bin/doom refresh' on the command line instead.\n\n" + "Reload anyway?")) + (user-error "Aborted")) (require 'core-cli) - (general-auto-unbind-keys) (let ((doom-reloading-p t)) - (when (getenv "DOOMENV") - (doom-reload-env-file 'force)) - (doom-reload-autoloads force-p) - (let (doom-init-p) - (doom-initialize)) + (compile (format "%s/bin/doom refresh -f" doom-emacs-dir)) + (while compilation-in-progress + (sit-for 1)) + (doom-initialize 'force) (with-demoted-errors "PRIVATE CONFIG ERROR: %s" - (let (doom-init-modules-p) - (doom-initialize-modules))) - (when (bound-and-true-p doom-packages) - (doom/reload-packages)) + (general-auto-unbind-keys) + (unwind-protect + (doom-initialize-modules 'force) + (general-auto-unbind-keys t))) (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook)) - (general-auto-unbind-keys t) (message "Finished!")) ;;;###autoload -(defun doom/reload-autoloads (&optional force-p) +(defun doom/reload-autoloads () "Reload only `doom-autoload-file' and `doom-package-autoload-file'. This is much faster and safer than `doom/reload', but not as comprehensive. This @@ -55,9 +62,11 @@ not reload your private config. It is useful to only pull in changes performed by 'doom refresh' on the command line." - (interactive "P") - (doom-initialize-autoloads doom-autoload-file) - (doom-initialize-autoloads doom-package-autoload-file)) + (interactive) + (require 'core-cli) + (require 'core-packages) + (doom-initialize-packages) + (doom-reload-autoloads nil 'force)) ;;;###autoload (defun doom/reload-env () @@ -70,26 +79,4 @@ Uses the same mechanism as 'bin/doom env reload'." (sit-for 1)) (unless (file-readable-p doom-env-file) (error "Failed to generate env file")) - (doom-load-env-vars doom-env-file)) - -;;;###autoload -(defun doom/reload-font () - "Reload your fonts, if they're set. -See `doom|init-fonts'." - (interactive) - (when doom-font - (set-frame-font doom-font t)) - (doom|init-fonts) - (mapc #'doom|init-emoji-fonts (frame-list))) - -;;;###autoload -(defun doom/reload-theme () - "Reload the current color theme." - (interactive) - (let ((theme (or (car-safe custom-enabled-themes) doom-theme))) - (when theme - (mapc #'disable-theme custom-enabled-themes)) - (when (and doom-theme (not (memq doom-theme custom-enabled-themes))) - (let (doom--prefer-theme-elc) - (load-theme doom-theme t))) - (doom|init-fonts))) + (doom-load-envvars-file doom-env-file)) diff --git a/core/autoload/debug.el b/core/autoload/debug.el index bab04a0c6..bad9910a6 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -1,5 +1,19 @@ ;;; core/autoload/debug.el -*- lexical-binding: t; -*- +;;;###autoload +(defun doom-run-all-startup-hooks-h () + "Run all startup Emacs hooks. Meant to be executed after starting Emacs with +-q or -Q, for example: + + emacs -Q -l init.el -f doom-run-all-startup-hooks-h" + (run-hook-wrapped 'after-init-hook #'doom-try-run-hook) + (setq after-init-time (current-time)) + (dolist (hook (list 'delayed-warnings-hook + 'emacs-startup-hook 'term-setup-hook + 'window-setup-hook)) + (run-hook-wrapped hook #'doom-try-run-hook))) + + ;; ;;; Helpers @@ -16,65 +30,67 @@ ready to be pasted in a bug report on github." (require 'vc-git) (let ((default-directory doom-emacs-dir) (doom-modules (doom-modules))) - (format - (concat "- OS: %s (%s)\n" - "- Shell: %s\n" - "- Emacs: %s (%s)\n" - "- Doom: %s (%s)\n" - "- Graphic display: %s (daemon: %s)\n" - "- System features: %s\n" - "- Details:\n" - " ```elisp\n" - " env bootstrapper: %s\n" - " elc count: %s\n" - " uname -a: %s\n" - " modules: %s\n" - " packages: %s\n" - " exec-path: %s\n" - " ```") - system-type system-configuration - shell-file-name - emacs-version (format-time-string "%b %d, %Y" emacs-build-time) - doom-version - (or (string-trim (shell-command-to-string "git log -1 --format=\"%D %h %ci\"")) - "n/a") - (display-graphic-p) (daemonp) - (bound-and-true-p system-configuration-features) - (cond ((file-exists-p doom-env-file) 'envvar-file) - ((featurep 'exec-path-from-shell) 'exec-path-from-shell)) - ;; details - (length (doom-files-in `(,@doom-modules-dirs - ,doom-core-dir - ,doom-private-dir) - :type 'files :match "\\.elc$" :sort nil)) - (if IS-WINDOWS - "n/a" - (with-temp-buffer - (unless (zerop (call-process "uname" nil t nil "-msrv")) - (insert (format "%s" system-type))) - (string-trim (buffer-string)))) - (or (cl-loop with cat = nil - for key being the hash-keys of doom-modules - if (or (not cat) (not (eq cat (car key)))) - do (setq cat (car key)) - and collect cat - and collect (cdr key) - else collect - (let ((flags (doom-module-get cat (cdr key) :flags))) - (if flags - `(,(cdr key) ,@flags) - (cdr key)))) - "n/a") - (or (ignore-errors - (require 'use-package) - (cl-loop for (name . plist) in (doom-find-packages :private t) - if (use-package-plist-delete (copy-sequence plist) :modules) - collect (format "%s" (cons name it)) - else - collect (symbol-name name))) - "n/a") - ;; abbreviate $HOME to hide username - (mapcar #'abbreviate-file-name exec-path)))) + (cl-letf + (((symbol-function 'sh) + (lambda (format) + (string-trim + (shell-command-to-string format))))) + `((emacs + (version . ,emacs-version) + (features ,@system-configuration-features) + (build . ,(format-time-string "%b %d, %Y" emacs-build-time)) + (buildopts ,system-configuration-options)) + (doom + (version . ,doom-version) + (build . ,(sh "git log -1 --format=\"%D %h %ci\""))) + (system + (type . ,system-type) + (config . ,system-configuration) + (shell . ,shell-file-name) + (uname . ,(if IS-WINDOWS + "n/a" + (sh "uname -msrv"))) + (path . ,(mapcar #'abbreviate-file-name exec-path))) + (config + (envfile + . ,(cond ((file-exists-p doom-env-file) 'envvar-file) + ((featurep 'exec-path-from-shell) 'exec-path-from-shell))) + (elc-files + . ,(length (doom-files-in `(,@doom-modules-dirs + ,doom-core-dir + ,doom-private-dir) + :type 'files :match "\\.elc$"))) + (modules + ,@(or (cl-loop with cat = nil + for key being the hash-keys of doom-modules + if (or (not cat) (not (eq cat (car key)))) + do (setq cat (car key)) + and collect cat + and collect (cdr key) + else collect + (let ((flags (doom-module-get cat (cdr key) :flags))) + (if flags + `(,(cdr key) ,@flags) + (cdr key)))) + '("n/a"))) + (packages + ,@(or (ignore-errors + (require 'core-packages) + (doom-initialize-packages) + (cl-loop for (name . plist) in doom-packages + if (doom-package-private-p name) + collect + (format + "%s" (if-let (splist (doom-plist-delete (copy-sequence plist) + :modules)) + (cons name splist) + name)))) + '("n/a"))) + (elpa-packages + ,@(or (ignore-errors + (cl-loop for (name . _) in package-alist + collect (format "%s" name))) + '("n/a")))))))) ;; @@ -86,24 +102,56 @@ ready to be pasted in a bug report on github." branch and commit." (interactive) (require 'vc-git) - (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s" - doom-version - emacs-version - (or (vc-git--symbolic-ref doom-core-dir) - "n/a") - (or (vc-git-working-revision doom-core-dir) - "n/a"))) + (let ((default-directory doom-core-dir)) + (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s\nBuild date: %s" + doom-version + emacs-version + (or (vc-git--symbolic-ref doom-core-dir) + "n/a") + (or (vc-git-working-revision doom-core-dir) + "n/a") + (or (string-trim (shell-command-to-string "git log -1 --format=%ci")) + "n/a")))) ;;;###autoload -(defun doom/info () +(defun doom/info (&optional raw) "Collects some debug information about your Emacs session, formats it into markdown and copies it to your clipboard, ready to be pasted into bug reports!" - (interactive) - (message "Generating Doom info...") - (if noninteractive - (print! (doom-info)) - (kill-new (doom-info)) - (message "Done! Copied to clipboard."))) + (interactive "P") + (let ((buffer (get-buffer-create "*doom-info*")) + (info (doom-info))) + (with-current-buffer buffer + (unless (or noninteractive + (eq major-mode 'markdown-mode) + (not (fboundp 'markdown-mode))) + (markdown-mode)) + (erase-buffer) + (if raw + (progn + (save-excursion + (pp info (current-buffer))) + (when (re-search-forward "(modules " nil t) + (goto-char (match-beginning 0)) + (cl-destructuring-bind (beg . end) + (bounds-of-thing-at-point 'sexp) + (let ((sexp (prin1-to-string (sexp-at-point)))) + (delete-region beg end) + (insert sexp))))) + (insert "
\n\n```\n") + (dolist (group info) + (insert! "%-8s%-10s %s\n" + ((car group) + (caadr group) + (cdadr group))) + (dolist (spec (cddr group)) + (insert! (indent 8 "%-10s %s\n") + ((car spec) (cdr spec))))) + (insert "```\n
")) + (if noninteractive + (print! (buffer-string)) + (switch-to-buffer buffer) + (kill-new (buffer-string)) + (print! (green "Copied markdown to clipboard")))))) ;;;###autoload (defun doom/am-i-secure () @@ -144,11 +192,11 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" (macroexp-progn (append `((setq noninteractive nil doom-debug-mode t + load-path ',load-path package--init-file-ensured t package-user-dir ,package-user-dir package-archives ',package-archives - user-emacs-directory ,doom-emacs-dir - doom--modules-cache nil) + user-emacs-directory ,doom-emacs-dir) (with-eval-after-load 'undo-tree ;; undo-tree throws errors because `buffer-undo-tree' isn't ;; corrrectly initialized @@ -169,12 +217,12 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" (load! "config" (plist-get plist :path) t))) doom-modules) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) - (doom|run-all-startup-hooks))) + (doom-run-all-startup-hooks-h))) (`vanilla-doom ; only Doom core `((setq doom-private-dir "/tmp/does/not/exist" doom-init-modules-p t) (load-file ,user-init-file) - (doom|run-all-startup-hooks))) + (doom-run-all-startup-hooks-h))) (`vanilla ; nothing loaded `((package-initialize))))))) "\n(unwind-protect (progn\n" contents "\n)\n" @@ -357,7 +405,7 @@ If INIT-FILE is non-nil, profile that instead of USER-INIT-FILE." init-file esup-server-port esup-depth) - "--eval=(doom|run-all-startup-hooks)")))) + "--eval=(doom-run-all-startup-hooks-h)")))) (when esup-run-as-batch-p (setq process-args (append process-args '("--batch")))) (setq esup-child-process (apply #'start-process process-args))) diff --git a/core/autoload/files.el b/core/autoload/files.el index da012aced..f93cce7fc 100644 --- a/core/autoload/files.el +++ b/core/autoload/files.el @@ -1,24 +1,94 @@ ;;; core/autoload/files.el -*- lexical-binding: t; -*- -;; -;; Public library +(defun doom--resolve-path-forms (spec &optional directory) + "Converts a simple nested series of or/and forms into a series of +`file-exists-p' checks. + +For example + + (doom--resolve-path-forms + '(or A (and B C)) + \"~\") + +Returns (approximately): + + '(let* ((_directory \"~\") + (A (expand-file-name A _directory)) + (B (expand-file-name B _directory)) + (C (expand-file-name C _directory))) + (or (and (file-exists-p A) A) + (and (if (file-exists-p B) B) + (if (file-exists-p C) C)))) + +This is used by `file-exists-p!' and `project-file-exists-p!'." + (declare (pure t) (side-effect-free t)) + (let ((exists-fn (if (fboundp 'projectile-file-exists-p) + #'projectile-file-exists-p + #'file-exists-p))) + (if (and (listp spec) + (memq (car spec) '(or and))) + (cons (car spec) + (mapcar (doom-rpartial #'doom--resolve-path-forms directory) + (cdr spec))) + (let ((filevar (make-symbol "file"))) + `(let* ((file-name-handler-alist nil) + (,filevar ,spec)) + (and ,(if directory + `(let ((default-directory ,directory)) + (,exists-fn ,filevar)) + (list exists-fn filevar)) + ,filevar)))))) + +(defun doom--path (&rest segments) + (let (file-name-handler-alist) + (let ((dir (pop segments))) + (unless segments + (setq dir (expand-file-name dir))) + (while segments + (setq dir (expand-file-name (car segments) dir) + segments (cdr segments))) + dir))) + +;;;###autoload +(defun doom-glob (&rest segments) + "Construct a path from SEGMENTS and expand glob patterns. +Returns nil if the path doesn't exist." + (let* (case-fold-search + file-name-handler-alist + (dir (apply #'doom--path segments))) + (if (string-match-p "[[*?]" dir) + (file-expand-wildcards dir t) + (if (file-exists-p dir) + dir)))) + +;;;###autoload +(defun doom-path (&rest segments) + "Constructs a file path from SEGMENTS." + (if segments + (apply #'doom--path segments) + (file!))) + +;;;###autoload +(defun doom-dir (&rest segments) + "Constructs a path from SEGMENTS. +See `doom-path'." + (when-let (path (apply #'doom-path segments)) + (directory-file-name (file-name-directory path)))) ;;;###autoload (cl-defun doom-files-in - (path-or-paths &rest rest - &key - filter - map - full - (sort t) ; TODO Allow a function for custom sorting? - (follow-symlinks t) - (type 'files) - (relative-to (unless full default-directory)) - (depth 99999) - (mindepth 0) - (match "/[^._]")) - "Returns a list of files/directories in PATH-OR-PATHS (one string path or a -list of them). + (paths &rest rest + &key + filter + map + (full t) + (follow-symlinks t) + (type 'files) + (relative-to (unless full default-directory)) + (depth 99999) + (mindepth 0) + (match "/[^._][^/]+")) + "Return a list of files/directories in PATHS (one string or a list of them). FILTER is a function or symbol that takes one argument (the path). If it returns non-nil, the entry will be excluded. @@ -37,49 +107,50 @@ be relative to it. The search recurses up to DEPTH and no further. DEPTH is an integer. MATCH is a string regexp. Only entries that match it will be included." - (cond - ((listp path-or-paths) - (cl-loop for path in path-or-paths - if (file-directory-p path) - nconc (apply #'doom-files-in path (plist-put rest :relative-to relative-to)))) - ((let ((path path-or-paths) - result) - (when (file-directory-p path) - (dolist (file (directory-files path nil "." sort)) - (unless (member file '("." "..")) - (let ((fullpath (expand-file-name file path))) - (cond ((file-directory-p fullpath) - (when (and (memq type '(t dirs)) - (string-match-p match fullpath) - (not (and filter (funcall filter fullpath))) - (not (and (file-symlink-p fullpath) - (not follow-symlinks))) - (<= mindepth 0)) - (setq result - (nconc result - (list (cond (map (funcall map fullpath)) - (relative-to (file-relative-name fullpath relative-to)) - (fullpath)))))) - (unless (< depth 1) - (setq result - (nconc result (apply #'doom-files-in fullpath - (append `(:mindepth ,(1- mindepth) - :depth ,(1- depth) - :relative-to ,relative-to) - rest)))))) - ((and (memq type '(t files)) - (string-match-p match fullpath) - (not (and filter (funcall filter fullpath))) - (<= mindepth 0)) - (push (if relative-to - (file-relative-name fullpath relative-to) - fullpath) - result)))))) - result))))) + (let (file-name-handler-alist + result) + (dolist (file (mapcan (doom-rpartial #'doom-glob "*") (doom-enlist paths))) + (cond ((file-directory-p file) + (nconcq! result + (and (memq type '(t dirs)) + (string-match-p match file) + (not (and filter (funcall filter file))) + (not (and (file-symlink-p file) + (not follow-symlinks))) + (<= mindepth 0) + (list (cond (map (funcall map file)) + (relative-to (file-relative-name file relative-to)) + (file)))) + (and (>= depth 1) + (apply #'doom-files-in file + (append (list :mindepth (1- mindepth) + :depth (1- depth) + :relative-to relative-to) + rest))))) + ((and (memq type '(t files)) + (string-match-p match file) + (not (and filter (funcall filter file))) + (<= mindepth 0)) + (push (if relative-to + (file-relative-name file relative-to) + file) + result)))) + result)) + +;;;###autoload +(defmacro file-exists-p! (files &optional directory) + "Returns non-nil if the FILES in DIRECTORY all exist. + +DIRECTORY is a path; defaults to `default-directory'. + +Returns the last file found to meet the rules set by FILES, which can be a +single file or nested compound statement of `and' and `or' statements." + `(let ((p ,(doom--resolve-path-forms files directory))) + (and p (expand-file-name p ,directory)))) ;; -;; Helpers +;;; Helpers (defun doom--forget-file (old-path &optional new-path) "Ensure `recentf', `projectile' and `save-place' forget OLD-PATH." @@ -131,7 +202,7 @@ MATCH is a string regexp. Only entries that match it will be included." ;; -;; Commands +;;; Commands ;;;###autoload (defun doom/delete-this-file (&optional path force-p) diff --git a/core/autoload/fonts.el b/core/autoload/fonts.el index c7facde9b..6a17919e2 100644 --- a/core/autoload/fonts.el +++ b/core/autoload/fonts.el @@ -50,7 +50,7 @@ FRAME parameter defaults to current frame." (let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum)) increment))) (unless (> new-size 0) - (error "Font is to small at %d" new-size)) + (error "Font is too small at %d" new-size)) (aset font xlfd-regexp-pixelsize-subnum (number-to-string new-size))) ;; Set point size & width to "*", so frame width will adjust to new font size (aset font xlfd-regexp-pointsize-subnum "*") @@ -65,6 +65,15 @@ FRAME parameter defaults to current frame." ;; ;;; Commands +;;;###autoload +(defun doom/reload-font () + "Reload your fonts, if they're set. +See `doom-init-fonts-h'." + (interactive) + (when doom-font + (set-frame-font doom-font t)) + (mapc #'doom-init-fonts-h (frame-list))) + ;;;###autoload (defun doom/increase-font-size (count) "Enlargens the font size across the current frame." diff --git a/core/autoload/format.el b/core/autoload/format.el new file mode 100644 index 000000000..7de5af711 --- /dev/null +++ b/core/autoload/format.el @@ -0,0 +1,234 @@ +;;; core/autoload/format.el -*- lexical-binding: t; -*- + +(defvar doom-format-ansi-alist + '(;; fx + (bold 1 :weight bold) + (dark 2) + (italic 3 :slant italic) + (underscore 4 :underline t) + (blink 5) + (rapid 6) + (contrary 7) + (concealed 8) + (strike 9 :strike-through t) + ;; fg + (black 30 term-color-black) + (red 31 term-color-red) + (green 32 term-color-green) + (yellow 33 term-color-yellow) + (blue 34 term-color-blue) + (magenta 35 term-color-magenta) + (cyan 36 term-color-cyan) + (white 37 term-color-white) + ;; bg + (on-black 40 term-color-black) + (on-red 41 term-color-red) + (on-green 42 term-color-green) + (on-yellow 43 term-color-yellow) + (on-blue 44 term-color-blue) + (on-magenta 45 term-color-magenta) + (on-cyan 46 term-color-cyan) + (on-white 47 term-color-white)) + "An alist of fg/bg/fx names mapped to ansi codes and term-color-* variables. + +This serves as the cipher for converting (COLOR ...) function calls in `print!' +and `format!' into colored output, where COLOR is any car of this list.") + +(defvar doom-format-class-alist + `((color . doom--format-color) + (class . doom--format-class) + (indent . doom--format-indent) + (autofill . doom--format-autofill) + + (success . (lambda (str &rest args) + (apply #'doom--format-color 'green (format "✓ %s" str) args))) + (warn . (lambda (str &rest args) + (apply #'doom--format-color 'yellow (format "! %s" str) args))) + (error . (lambda (str &rest args) + (apply #'doom--format-color 'red (format "x %s" str) args))) + (info . (lambda (str &rest args) + (concat "- " (if args (apply #'format str args) str)))) + (start . (lambda (str &rest args) + (concat "> " (if args (apply #'format str args) str)))) + (debug . (lambda (str &rest args) + (if doom-debug-mode + (if args + (apply #'format str args) + (format "%s" str)) + ""))) + (path . abbreviate-file-name) + (symbol . symbol-name) + (relpath . (lambda (str &optional dir) + (if (or (not str) + (not (stringp str)) + (string-empty-p str)) + str + (let ((dir (or dir (file-truename default-directory))) + (str (file-truename str))) + (if (file-in-directory-p str dir) + (file-relative-name str dir) + (abbreviate-file-name str)))))) + (filename . file-name-nondirectory) + (dirname . (lambda (path) + (unless (file-directory-p path) + (setq path (file-name-directory path))) + (directory-file-name path)))) + "An alist of text classes that map to transformation functions. + +Any of these classes can be called like functions from within `format!' and +`print!' calls, which will transform their input.") + +(defvar doom-format-indent 0 + "Level to rigidly indent text returned by `format!' and `print!'.") + +(defvar doom-format-backend + (if noninteractive 'ansi 'text-properties) + "Determines whether to print colors with ANSI codes or with text properties. + +Accepts 'ansi and 'text-properties. nil means don't render colors.") + + +;; +;;; Library + +(defun doom--format (output) + (if (string-empty-p (string-trim output)) + "" + (concat (make-string doom-format-indent 32) + (replace-regexp-in-string + "\n" (concat "\n" (make-string doom-format-indent 32)) + output t t)))) + +(defun doom--format-print (output) + (unless (string-empty-p output) + (if (not noninteractive) + (message "%s" output) + (princ output) + (terpri)) ; newline + t)) + +(defun doom--format-indent (width text &optional prefix) + "Indent TEXT by WIDTH spaces. If ARGS, format TEXT with them." + (with-temp-buffer + (setq text (format "%s" text)) + (insert text) + (indent-rigidly (point-min) (point-max) width) + (when (stringp prefix) + (when (> width 2) + (goto-char (point-min)) + (beginning-of-line-text) + (delete-char (- (length prefix))) + (insert prefix))) + (buffer-string))) + +(defun doom--format-autofill (&rest msgs) + "Ensure MSG is split into lines no longer than `fill-column'." + (with-temp-buffer + (let ((fill-column 76)) + (dolist (line msgs) + (when line + (insert (format "%s" line)))) + (fill-region (point-min) (point-max)) + (buffer-string)))) + +(defun doom--format-color (style format &rest args) + "Apply STYLE to formatted MESSAGE with ARGS. + +STYLE is a symbol that correlates to `doom-format-ansi-alist'. + +In a noninteractive session, this wraps the result in ansi color codes. +Otherwise, it maps colors to a term-color-* face." + (let* ((code (cadr (assq style doom-format-ansi-alist))) + (format (format "%s" format)) + (message (if args (apply #'format format args) format))) + (unless code + (error "%S is an invalid color" style)) + (pcase doom-format-backend + (`ansi + (format "\e[%dm%s\e[%dm" code message 0)) + (`text-properties + (require 'term) ; piggyback on term's color faces + (propertize + message + 'face + (append (get-text-property 0 'face format) + (cond ((>= code 40) + `(:background ,(caddr (assq style doom-format-ansi-alist)))) + ((>= code 30) + `(:foreground ,(face-foreground (caddr (assq style doom-format-ansi-alist))))) + ((cddr (assq style doom-format-ansi-alist))))))) + (_ message)))) + +(defun doom--format-class (class format &rest args) + "Apply CLASS to formatted format with ARGS. + +CLASS is derived from `doom-format-class-alist', and can contain any arbitrary, +transformative logic." + (let (fn) + (cond ((setq fn (cdr (assq class doom-format-class-alist))) + (if (functionp fn) + (apply fn format args) + (error "%s does not have a function" class))) + (args (apply #'format format args)) + (format)))) + +(defun doom--format-apply (forms &optional sub) + "Replace color-name functions with calls to `doom--format-color'." + (cond ((null forms) nil) + ((listp forms) + (append (cond ((not (symbolp (car forms))) + (list (doom--format-apply (car forms)))) + (sub + (list (car forms))) + ((assq (car forms) doom-format-ansi-alist) + `(doom--format-color ',(car forms))) + ((assq (car forms) doom-format-class-alist) + `(doom--format-class ',(car forms))) + ((list (car forms)))) + (doom--format-apply (cdr forms) t) + nil)) + (forms))) + +;;;###autoload +(defmacro format! (message &rest args) + "An alternative to `format' that understands (color ...) and converts them +into faces or ANSI codes depending on the type of sesssion we're in." + `(doom--format (format ,@(doom--format-apply `(,message ,@args))))) + +;;;###autoload +(defmacro print-group! (&rest body) + "Indents any `print!' or `format!' output within BODY." + `(let ((doom-format-indent (+ 2 doom-format-indent))) + ,@body)) + +;;;###autoload +(defmacro print! (message &rest args) + "Uses `message' in interactive sessions and `princ' otherwise (prints to +standard out). + +Can be colored using (color ...) blocks: + + (print! \"Hello %s\" (bold (blue \"How are you?\"))) + (print! \"Hello %s\" (red \"World\")) + (print! (green \"Great %s!\") \"success\") + +Uses faces in interactive sessions and ANSI codes otherwise." + `(doom--format-print (format! ,message ,@args))) + +;;;###autoload +(defmacro insert! (message &rest args) + "Like `insert'; the last argument must be format arguments for MESSAGE. + +\(fn MESSAGE... ARGS)" + `(insert (format! (concat ,message ,@(butlast args)) + ,@(car (last args))))) + +;;;###autoload +(defmacro error! (message &rest args) + "Like `error', but with the power of `format!'." + `(error (format! ,message ,@args))) + +;;;###autoload +(defmacro user-error! (message &rest args) + "Like `user-error', but with the power of `format!'." + `(user-error (format! ,message ,@args))) diff --git a/core/autoload/help.el b/core/autoload/help.el index 9a204ebb3..72d738483 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -121,6 +121,7 @@ selection of all minor-modes, active or not." ;; ;;; Documentation commands +(defvar org-agenda-files) (defun doom--org-headings (files &optional depth include-files) "TODO" (require 'org) @@ -155,6 +156,7 @@ selection of all minor-modes, active or not." (mapc #'kill-buffer org-agenda-new-buffers) (setq org-agenda-new-buffers nil)))) +(defvar ivy-sort-functions-alist) ;;;###autoload (defun doom-completing-read-org-headings (prompt files &optional depth include-files initial-input) "TODO" @@ -197,8 +199,7 @@ selection of all minor-modes, active or not." "Find in News: " (nreverse (doom-files-in (expand-file-name "news" doom-docs-dir) :match "/[0-9]" - :relative-to doom-docs-dir - :sort t)) + :relative-to doom-docs-dir)) nil t initial-input)) ;;;###autoload @@ -365,10 +366,14 @@ current file is in, or d) the module associated with the current major mode (see (recenter) (message "Couldn't find the config block")))))))) +(defvar doom--help-packages-list nil) (defun doom--help-packages-list (&optional refresh) (or (unless refresh - (doom-cache-get 'help-packages)) - (doom-cache-set 'help-packages (doom-package-list 'all)))) + doom--help-packages-list) + (setq doom--help-packages-list + (append (cl-loop for package in doom-core-packages + collect (list package :modules '((:core internal)))) + (doom-package-list 'all))))) (defun doom--help-package-configs (package) ;; TODO Add git checks, in case ~/.emacs.d isn't a git repo @@ -376,7 +381,7 @@ current file is in, or d) the module associated with the current major mode (see (split-string (shell-command-to-string (format "git grep --no-break --no-heading --line-number '%s %s\\($\\| \\)'" - "\\(^;;;###package\\|(after!\\|(def-package!\\)" + "\\(^;;;###package\\|(after!\\|(use-package!\\)" package)) "\n" t))) @@ -392,59 +397,66 @@ If prefix arg is present, refresh the cache." (let* ((guess (or (function-called-at-point) (symbol-at-point)))) (require 'finder-inf nil t) + (require 'package) (unless package--initialized (package-initialize t)) - (let* ((doom--packages (doom--help-packages-list)) - (packages (cl-delete-duplicates - (append (mapcar 'car package-alist) - (mapcar 'car package--builtins) - (mapcar 'car doom--packages) - nil)))) + (let ((packages (cl-delete-duplicates + (append (mapcar 'car package-alist) + (mapcar 'car package--builtins) + (mapcar 'car (doom--help-packages-list)) + nil)))) (unless (memq guess packages) (setq guess nil)) - (list (intern (completing-read (if guess - (format "Select package to search for (default %s): " - guess) - "Describe package: ") - packages nil t nil nil - (if guess (symbol-name guess)))))))) + (list + (intern + (completing-read (if guess + (format "Select package to search for (default %s): " + guess) + "Describe package: ") + packages nil t nil nil + (if guess (symbol-name guess)))))))) (if (or (package-desc-p package) (and (symbolp package) (or (assq package package-alist) - (assq package package-archive-contents) (assq package package--builtins)))) (describe-package package) (help-setup-xref (list #'doom/help-packages package) (called-interactively-p 'interactive)) - (with-help-window (help-buffer) - (with-current-buffer standard-output - (prin1 package) - (princ " is a site package.\n\n")))) + (with-help-window (help-buffer))) (save-excursion (with-current-buffer (help-buffer) (let ((doom-packages (doom--help-packages-list)) (inhibit-read-only t) (indent (make-string 13 ? ))) - (goto-char (point-min)) + (goto-char (point-max)) (if (re-search-forward "^ *Status: " nil t) (progn (end-of-line) (insert "\n")) (re-search-forward "\n\n" nil t)) + (package--print-help-section "Package") + (insert (symbol-name package) "\n") + (package--print-help-section "Source") - (insert (or (pcase (ignore-errors (doom-package-backend package)) - (`elpa (concat "[M]ELPA " (doom--package-url package))) - (`quelpa (format "QUELPA %s" (prin1-to-string (doom-package-prop package :recipe)))) - (`emacs "Built-in") - (_ (symbol-file package))) + (insert (or (pcase (doom-package-backend package) + (`straight + (format! "Straight\n%s" + (indent + 13 (string-trim + (pp-to-string + (doom-package-build-recipe package)))))) + (`elpa + (format "[M]ELPA %s" (doom--package-url package))) + (`builtin "Built-in") + (_ (abbreviate-file-name (symbol-file package)))) "unknown") "\n") (when (assq package doom-packages) (package--print-help-section "Modules") (insert "Declared by the following Doom modules:\n") - (dolist (m (doom-package-prop package :modules)) + (dolist (m (doom-package-get package :modules)) (insert indent) (doom--help-package-insert-button (format "%s %s" (car m) (or (cdr m) "")) @@ -469,7 +481,9 @@ If prefix arg is present, refresh the cache." (find-file (expand-file-name file doom-emacs-dir)) (goto-char (point-min)) (forward-line (1- line)) - (recenter))))))))) + (recenter))))) + + (insert "\n\n"))))) (defvar doom--package-cache nil) (defun doom--package-list () diff --git a/core/autoload/line-numbers.el b/core/autoload/line-numbers.el index a926c0a2b..df144180a 100644 --- a/core/autoload/line-numbers.el +++ b/core/autoload/line-numbers.el @@ -1,9 +1,9 @@ ;;; core/autoload/line-numbers.el -*- lexical-binding: t; -*- ;;;###if (version< emacs-version "26.1") -;; This was lifted out of the display-line-numbers library in Emacs 26.1 and -;; modified to use nlinum for Emacs 25.x users. It should be removed should -;; Emacs 25 support be removed. +;; DEPRECATED This was lifted out of the display-line-numbers library in Emacs +;; 26.1 and modified to use nlinum for Emacs 25.x users. It should be removed +;; should Emacs 25 support be removed. ;;;###autoload (defvar display-line-numbers t @@ -52,7 +52,7 @@ to display all line numbers in the buffer." :type 'boolean) ;;;###autoload -(defun line-number-display-width () +(defun line-number-display-width (&optional _) "Return the width used for displaying line numbers in the selected window." (length (save-excursion (goto-char (point-max)) diff --git a/core/autoload/message.el b/core/autoload/message.el deleted file mode 100644 index b2b0d6aa0..000000000 --- a/core/autoload/message.el +++ /dev/null @@ -1,132 +0,0 @@ -;;; core/autoload/message.el -*- lexical-binding: t; -*- - -(defvar doom-ansi-alist - '(;; fx - (bold 1 :weight bold) - (dark 2) - (italic 3 :slant italic) - (underscore 4 :underline t) - (blink 5) - (rapid 6) - (contrary 7) - (concealed 8) - (strike 9 :strike-through t) - ;; fg - (black 30 term-color-black) - (red 31 term-color-red) - (green 32 term-color-green) - (yellow 33 term-color-yellow) - (blue 34 term-color-blue) - (magenta 35 term-color-magenta) - (cyan 36 term-color-cyan) - (white 37 term-color-white) - ;; bg - (on-black 40 term-color-black) - (on-red 41 term-color-red) - (on-green 42 term-color-green) - (on-yellow 43 term-color-yellow) - (on-blue 44 term-color-blue) - (on-magenta 45 term-color-magenta) - (on-cyan 46 term-color-cyan) - (on-white 47 term-color-white)) - "TODO") - -(defvar doom-message-backend - (if noninteractive 'ansi 'text-properties) - "Determines whether to print colors with ANSI codes or with text properties. - -Accepts 'ansi and 'text-properties. nil means don't render colors.") - -;;;###autoload -(defun doom-message-indent (width text &rest args) - "Indent TEXT by WIDTH spaces. If ARGS, format TEXT with them." - (with-temp-buffer - (insert (apply #'format text args)) - (let ((fill-column 80)) - (fill-region (point-min) (point-max)) - (indent-rigidly (point-min) (point-max) width)) - (when (> width 2) - (goto-char (point-min)) - (beginning-of-line-text) - (delete-char -2) - (insert "> ")) - (buffer-string))) - -;;;###autoload -(defun doom-message-autofill (&rest msgs) - "Ensure MSG is split into lines no longer than `fill-column'." - (with-temp-buffer - (let ((fill-column 70)) - (dolist (line msgs) - (when line - (insert line))) - (fill-region (point-min) (point-max)) - (buffer-string)))) - -;;;###autoload -(defun doom-color-apply (style text &rest args) - "Apply CODE to formatted MESSAGE with ARGS. CODE is derived from any of -`doom-message-fg', `doom-message-bg' or `doom-message-fx'. - -In a noninteractive session, this wraps the result in ansi color codes. -Otherwise, it maps colors to a term-color-* face." - (let ((code (car (cdr (assq style doom-ansi-alist)))) - (message (if args (apply #'format text args) text))) - (pcase doom-message-backend - (`ansi - (format "\e[%dm%s\e[%dm" - (car (cdr (assq style doom-ansi-alist))) - message 0)) - (`text-properties - (require 'term) ; piggyback on term's color faces - (propertize - message - 'face - (append (get-text-property 0 'face text) - (cond ((>= code 40) - `(:background ,(caddr (assq style doom-ansi-alist)))) - ((>= code 30) - `(:foreground ,(face-foreground (caddr (assq style doom-ansi-alist))))) - ((cddr (assq style doom-ansi-alist))))))) - (_ message)))) - -(defun doom--short-color-replace (forms) - "Replace color-name functions with calls to `doom-color-apply'." - (cond ((null forms) nil) - ((listp forms) - (append (cond ((not (symbolp (car forms))) - (list (doom--short-color-replace (car forms)))) - ((assq (car forms) doom-ansi-alist) - `(doom-color-apply ',(car forms))) - ((eq (car forms) 'color) - (pop forms) - `(doom-color-apply ,(car forms))) - ((memq (car forms) '(indent autofill)) - (let ((fn (pop forms))) - `(,(intern (format "doom-message-%s" fn)) - ,(car forms)))) - ((list (car forms)))) - (doom--short-color-replace (cdr forms)) - nil)) - (forms))) - -;;;###autoload -(defmacro format! (message &rest args) - "An alternative to `format' that understands (color ...) and converts them -into faces or ANSI codes depending on the type of sesssion we're in." - `(format ,@(doom--short-color-replace `(,message ,@args)))) - -;;;###autoload -(defmacro print! (message &rest args) - "Uses `message' in interactive sessions and `princ' otherwise (prints to -standard out). - -Can be colored using (color ...) blocks: - - (print! \"Hello %s\" (bold (blue \"How are you?\"))) - (print! \"Hello %s\" (red \"World\")) - (print! (green \"Great %s!\" \"success\")) - -Uses faces in interactive sessions and ANSI codes otherwise." - `(progn (princ (format! ,message ,@args)) - (terpri))) diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 723d21c0e..b2ef5f1d1 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -1,128 +1,64 @@ ;;; core/autoload/packages.el -*- lexical-binding: t; -*- -(require 'core-packages) -(load! "cache") ; in case autoloads haven't been generated yet - - -(defun doom--packages-choose (prompt) - (let ((table (cl-loop for pkg in package-alist - unless (doom-package-built-in-p (cdr pkg)) - collect (cons (package-desc-full-name (cdr pkg)) - (cdr pkg))))) - (cdr (assoc (completing-read prompt - (mapcar #'car table) - nil t) - table)))) - -(defun doom--refresh-pkg-cache () - "Clear the cache for `doom-refresh-packages-maybe'." - (setq doom--refreshed-p nil) - (doom-cache-set 'last-pkg-refresh nil)) - -;;;###autoload -(defun doom-refresh-packages-maybe (&optional force-p) - "Refresh ELPA packages, if it hasn't been refreshed recently." - (when force-p - (doom--refresh-pkg-cache)) - (unless (or (doom-cache-get 'last-pkg-refresh) - doom--refreshed-p) - (condition-case e - (progn - (message "Refreshing package archives") - (package-refresh-contents) - (doom-cache-set 'last-pkg-refresh t 1200)) - ((debug error) - (doom--refresh-pkg-cache) - (signal 'doom-error e))))) - - ;; ;;; Package metadata ;;;###autoload -(defun doom-package-plist (package) +(defun doom-package-get (package &optional prop nil-value) "Returns PACKAGE's `package!' recipe from `doom-packages'." - (cdr (assq package doom-packages))) + (let ((plist (cdr (assq package doom-packages)))) + (if prop + (if (plist-member plist prop) + (plist-get plist prop) + nil-value) + plist))) ;;;###autoload -(defun doom-package-desc (package) - "Returns PACKAGE's desc struct from `package-alist'." - (cadr (assq (or (car (doom-package-prop package :recipe)) - package) - package-alist))) +(defun doom-package-recipe (package &optional prop nil-value) + "Returns the `straight' recipe PACKAGE was registered with." + (let ((plist (gethash (symbol-name package) straight--recipe-cache))) + (if prop + (if (plist-member plist prop) + (plist-get plist prop) + nil-value) + plist))) ;;;###autoload -(defun doom-package-true-name (package) - "Return PACKAGE's true name. - -It is possible for quelpa packages to be given a psuedonym (the first argument -of `package!'). Its real name is the car of package's :recipe. e.g. - - (package! X :recipe (Y :fetcher github :repo \"abc/def\")) - -X's real name is Y." - (let ((sym (car (doom-package-prop package :recipe)))) - (or (and (symbolp sym) - (not (keywordp sym)) - sym) - package))) +(defun doom-package-build-recipe (package &optional prop nil-value) + "Returns the `straight' recipe PACKAGE was installed with." + (let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache)))) + (if prop + (if (plist-member plist prop) + (plist-get plist prop) + nil-value) + plist))) ;;;###autoload -(defun doom-package-psuedo-name (package) +(defun doom-package-build-time (package) "TODO" - (or (cl-loop for (package . plist) in doom-packages - for recipe-name = (car (plist-get plist :recipe)) - if (eq recipe-name package) - return recipe-name) - package)) + (car (gethash (symbol-name package) straight--build-cache))) ;;;###autoload -(defun doom-package-backend (package &optional noerror) - "Return backend that PACKAGE was installed with. +(defun doom-package-dependencies (package &optional recursive noerror) + "Return a list of dependencies for a package." + (let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache)))) + (if recursive + (nconc deps (mapcan (lambda (dep) (doom-package-dependencies dep t t)) + deps)) + deps))) -Can either be elpa, quelpa or emacs (built-in). Throws an error if NOERROR is -nil and the package isn't installed. - -See `doom-package-recipe-backend' to get the backend PACKAGE is registered with -\(as opposed to what it is was installed with)." - (cl-check-type package symbol) - (let ((package-truename (doom-package-true-name package))) - (cond ((assq package-truename quelpa-cache) 'quelpa) - ((assq package-truename package-alist) 'elpa) - ((doom-package-built-in-p package) 'emacs) - ((not noerror) (error "%s package is not installed" package))))) - -;;;###autoload -(defun doom-package-recipe-backend (package &optional noerror) - "Return backend that PACKAGE is registered with. - -See `doom-package-backend' to get backend for currently installed package." - (cl-check-type package symbol) - (cond ((not (doom-package-registered-p package)) - (unless noerror - (error "%s package is not registered" package))) - ((let ((builtin (eval (doom-package-prop package :built-in) t))) - (or (and (eq builtin 'prefer) - (locate-library (symbol-name package) nil doom-site-load-path)) - (eq builtin 't))) - 'emacs) - ((doom-package-prop package :recipe) - 'quelpa) - ('elpa))) - -;;;###autoload -(defun doom-package-prop (package prop &optional nil-value) - "Return PROPerty in PACKAGE's plist. - -Otherwise returns NIL-VALUE if package isn't registered or PROP doesn't -exist/isn't specified." - (cl-check-type package symbol) - (cl-check-type prop keyword) - (if-let (plist (doom-package-plist package)) - (if (plist-member plist prop) - (plist-get plist prop) - nil-value) - nil-value)) +(defun doom-package-depending-on (package &optional noerror) + "Return a list of packages that depend on the package named NAME." + (cl-check-type name symbol) + ;; can't get dependencies for built-in packages + (unless (or (doom-package-build-recipe name) + noerror) + (error "Couldn't find %s, is it installed?" name)) + (cl-loop for pkg in (hash-table-keys straight--build-cache) + for deps = (doom-package-dependencies pkg) + if (memq package deps) + collect pkg + and append (doom-package-depending-on pkg t))) ;; @@ -131,212 +67,94 @@ exist/isn't specified." ;;;###autoload (defun doom-package-built-in-p (package) "Return non-nil if PACKAGE (a symbol) is built-in." - (unless (doom-package-installed-p package) - (or (package-built-in-p (doom-package-true-name package)) - (locate-library (symbol-name package) nil doom-site-load-path)))) + (eq (doom-package-build-recipe package :type) + 'built-in)) ;;;###autoload (defun doom-package-installed-p (package) "Return non-nil if PACKAGE (a symbol) is installed." - (when-let (desc (doom-package-desc package)) - (and (package-installed-p desc) - (file-directory-p (package-desc-dir desc))))) + (file-directory-p (straight--build-dir (symbol-name package)))) ;;;###autoload (defun doom-package-registered-p (package) "Return non-nil if PACKAGE (a symbol) has been registered with `package!'. Excludes packages that have a non-nil :built-in property." - (let ((package (or (cl-loop for (pkg . plist) in doom-packages - for newname = (car (plist-get plist :recipe)) - if (and (symbolp newname) - (eq newname package)) - return pkg) - package))) - (when-let (plist (doom-package-plist package)) - (not (eval (plist-get plist :ignore)))))) + (when-let (plist (doom-package-get package)) + (not (eval (plist-get plist :ignore) t)))) ;;;###autoload (defun doom-package-private-p (package) "Return non-nil if PACKAGE was installed by the user's private config." - (doom-package-prop package :private)) + (assq :private (doom-package-get package :modules))) ;;;###autoload (defun doom-package-protected-p (package) "Return non-nil if PACKAGE is protected. A protected package cannot be deleted and will be auto-installed if missing." - (memq (doom-package-true-name package) doom-core-packages)) + (memq package doom-core-packages)) ;;;###autoload (defun doom-package-core-p (package) "Return non-nil if PACKAGE is a core Doom package." (or (doom-package-protected-p package) - (assq :core (doom-package-prop package :modules)))) + (assq :core (doom-package-get package :modules)))) ;;;###autoload -(defun doom-package-different-backend-p (package) - "Return t if a PACKAGE (a symbol) has a new backend than what it was installed -with. Returns nil otherwise, or if package isn't installed." - (cl-check-type package symbol) - (and (doom-package-installed-p package) - (not (doom-get-depending-on package)) ; not a dependency - (not (eq (doom-package-backend package 'noerror) - (doom-package-recipe-backend package 'noerror))))) +(defun doom-package-backend (package) + "Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is +installed." + (cond ((gethash (symbol-name package) straight--build-cache) + 'straight) + ((or (doom-package-built-in-p package) + (assq package package--builtins)) + 'builtin) + ((assq package package-alist) + 'elpa) + ('other))) ;;;###autoload (defun doom-package-different-recipe-p (name) "Return t if a package named NAME (a symbol) has a different recipe than it was installed with." (cl-check-type name symbol) - (when (doom-package-installed-p name) - (let ((package-truename (doom-package-true-name name))) - (when-let* ((quelpa-recipe (assq package-truename quelpa-cache)) - (doom-recipe (assq package-truename doom-packages))) - (not (equal (cdr quelpa-recipe) - (cdr (plist-get (cdr doom-recipe) :recipe)))))))) - -(defvar quelpa-upgrade-p) -;;;###autoload -(defun doom-package-outdated-p (name) - "Determine whether NAME (a symbol) is outdated or not. - -If outdated, returns a list, whose car is NAME, and cdr the current version list -and latest version list of the package." - (cl-check-type name symbol) - (when-let (desc (doom-package-desc name)) - (let* ((old-version (package-desc-version desc)) - (new-version - (pcase (doom-package-backend name) - (`quelpa - (let ((recipe (doom-package-prop name :recipe)) - (dir (expand-file-name (symbol-name name) quelpa-build-dir)) - (inhibit-message (not doom-debug-mode)) - (quelpa-upgrade-p t)) - (if-let (ver (quelpa-checkout recipe dir)) - (version-to-list ver) - old-version))) - (`elpa - (let ((desc (cadr (assq name package-archive-contents)))) - (when (package-desc-p desc) - (package-desc-version desc))))))) - (unless (and (listp old-version) (listp new-version)) - (error "Couldn't get version for %s" name)) - (when (version-list-< old-version new-version) - (list name old-version new-version))))) + ;; TODO + ;; (when (doom-package-installed-p name) + ;; (when-let* ((doom-recipe (assq name doom-packages)) + ;; (install-recipe (doom-package-recipe))) + ;; (not (equal (cdr quelpa-recipe) + ;; (cdr (plist-get (cdr doom-recipe) :recipe)))))) + ) ;; ;;; Package list getters -;;;###autoload -(cl-defun doom-find-packages (&key (installed 'any) - (private 'any) - (disabled 'any) - (pinned 'any) - (ignored 'any) - (core 'any) - _changed - backend - deps) - "Retrieves a list of primary packages (i.e. non-dependencies). Each element is -a cons cell, whose car is the package symbol and whose cdr is the quelpa recipe -(if any). - -You can build a filtering criteria using one or more of the following -properties: - - :backend 'quelpa|'elpa|'emacs|'any - Include packages installed through 'quelpa, 'elpa or 'emacs. 'any is the - wildcard. - :installed BOOL|'any - t = only include installed packages - nil = exclude installed packages - :private BOOL|'any - t = only include user-installed packages - nil = exclude user-installed packages - :core BOOL|'any - t = only include Doom core packages - nil = exclude Doom core packages - :disabled BOOL|'any - t = only include disabled packages - nil = exclude disabled packages - :ignored BOOL|'any - t = only include ignored packages - nil = exclude ignored packages - :pinned BOOL|ARCHIVE - Only return packages that are pinned (t), not pinned (nil) or pinned to a - specific archive (stringp) - :deps BOOL - Includes the package's dependencies (t) or not (nil). - -Warning: this function is expensive, as it re-evaluates your all packages.el -files." - (delete-dups - (cl-loop for (sym . plist) in doom-packages - if (and (or (not backend) - (eq (doom-package-backend sym 'noerror) backend)) - (or (eq ignored 'any) - (let* ((form (plist-get plist :ignore)) - (value (eval form))) - (if ignored value (not value)))) - (or (eq disabled 'any) - (if disabled - (plist-get plist :disable) - (not (plist-get plist :disable)))) - (or (eq installed 'any) - (if installed - (doom-package-installed-p sym) - (not (doom-package-installed-p sym)))) - (or (eq private 'any) - (let ((modules (plist-get plist :modules))) - (if private - (assq :private modules) - (not (assq :private modules))))) - (or (eq core 'any) - (let ((modules (plist-get plist :modules))) - (if core - (assq :core modules) - (not (assq :core modules))))) - (or (eq pinned 'any) - (cond ((eq pinned 't) - (plist-get plist :pin)) - ((null pinned) - (not (plist-get plist :pin))) - ((equal (plist-get plist :pin) pinned))))) - collect (cons sym plist) - and if (and deps (not (doom-package-built-in-p sym))) - nconc - (cl-loop for pkg in (doom-get-dependencies-for sym 'recursive 'noerror) - if (or (eq installed 'any) - (if installed - (doom-package-installed-p pkg) - (not (doom-package-installed-p pkg)))) - collect (cons pkg (cdr (assq pkg doom-packages))))))) - -(defun doom--read-module-packages-file (file &optional raw noerror) +(defun doom--read-module-packages-file (file &optional eval noerror) (with-temp-buffer ; prevent buffer-local settings from propagating (condition-case e - (if (not raw) + (if (not eval) (load file noerror t t) (when (file-readable-p file) (insert-file-contents file) + (delay-mode-hooks (emacs-lisp-mode)) (while (re-search-forward "(package! " nil t) (save-excursion (goto-char (match-beginning 0)) - (cl-destructuring-bind (name . plist) (cdr (sexp-at-point)) - (push (cons name - (plist-put plist :modules - (cond ((file-in-directory-p file doom-private-dir) - '((:private))) - ((file-in-directory-p file doom-core-dir) - '((:core))) - ((doom-module-from-path file))))) - doom-packages)))))) + (unless (let ((ppss (syntax-ppss))) + (or (nth 3 ppss) + (nth 4 ppss))) + (cl-destructuring-bind (name . plist) + (cdr (sexp-at-point)) + (push (cons + name (plist-put + plist :modules + (list (doom-module-from-path file)))) + doom-packages))))))) ((debug error) (signal 'doom-package-error - (list (or (doom-module-from-path file) - '(:private . packages)) + (list (doom-module-from-path file) e)))))) ;;;###autoload @@ -348,279 +166,38 @@ This excludes core packages listed in `doom-core-packages'. If ALL-P, gather packages unconditionally across all modules, including disabled ones." (let ((noninteractive t) - (doom--stage 'packages) (doom-modules (doom-modules)) doom-packages - doom-disabled-packages - package-pinned-packages) - (doom--read-module-packages-file (expand-file-name "packages.el" doom-core-dir) all-p) - (let ((private-packages (expand-file-name "packages.el" doom-private-dir))) + doom-disabled-packages) + (doom--read-module-packages-file + (doom-path doom-core-dir "packages.el") all-p t) + (let ((private-packages (doom-path doom-private-dir "packages.el"))) (unless all-p ;; We load the private packages file twice to ensure disabled packages ;; are seen ASAP, and a second time to ensure privately overridden ;; packages are properly overwritten. - (doom--read-module-packages-file private-packages nil t)) + (doom--read-module-packages-file private-packages t t)) (if all-p (mapc #'doom--read-module-packages-file (doom-files-in doom-modules-dir :depth 2 - :full t - :match "/packages\\.el$" - :sort nil)) + :match "/packages\\.el$")) (cl-loop for key being the hash-keys of doom-modules for path = (doom-module-path (car key) (cdr key) "packages.el") for doom--current-module = key do (doom--read-module-packages-file path nil t))) (doom--read-module-packages-file private-packages all-p t)) - (append (cl-loop for package in doom-core-packages - collect (list package :modules '((:core internal)))) - (nreverse doom-packages)))) - -;;;###autoload -(defun doom-get-package-alist () - "Returns a list of all desired packages, their dependencies and their desc -objects, in the order of their `package! blocks.'" - (cl-remove-duplicates - (cl-loop for name in (mapcar #'car doom-packages) - if (assq name package-alist) - nconc (cl-loop for dep in (package--get-deps name) - if (assq dep package-alist) - collect (cons dep (cadr it))) - and collect (cons name (cadr it))) - :key #'car - :from-end t)) - -;;;###autoload -(defun doom-get-depending-on (name &optional noerror) - "Return a list of packages that depend on the package named NAME." - (cl-check-type name symbol) - (setq name (or (car (doom-package-prop name :recipe)) name)) - (unless (doom-package-built-in-p name) - (if-let (desc (cadr (assq name package-alist))) - (mapcar #'package-desc-name (package--used-elsewhere-p desc nil t)) - (unless noerror - (error "Couldn't find %s, is it installed?" name))))) - -;;;###autoload -(defun doom-get-dependencies-for (name &optional recursive noerror) - "Return a list of dependencies for a package." - (cl-check-type name symbol) - ;; can't get dependencies for built-in packages - (unless (doom-package-built-in-p name) - (if-let (desc (doom-package-desc name)) - (let* ((deps (mapcar #'car (package-desc-reqs desc))) - (deps (cl-remove-if #'doom-package-built-in-p deps))) - (if recursive - (nconc deps (mapcan (lambda (dep) (doom-get-dependencies-for dep t t)) - deps)) - deps)) - (unless noerror - (error "Couldn't find %s, is it installed?" name))))) - -;;;###autoload -(defun doom-get-outdated-packages (&optional include-frozen-p) - "Return a list of packages that are out of date. Each element is a list, -containing (PACKAGE-SYMBOL OLD-VERSION-LIST NEW-VERSION-LIST). - -If INCLUDE-FROZEN-P is non-nil, check frozen packages as well. - -Used by `doom-packages-update'." - (doom-refresh-packages-maybe doom-debug-mode) - (cl-loop for package in (mapcar #'car package-alist) - when (and (or (not (eval (doom-package-prop package :freeze))) - include-frozen-p) - (not (eval (doom-package-prop package :ignore))) - (not (doom-package-different-backend-p package)) - (doom-package-outdated-p package)) - collect it)) - -;;;###autoload -(defun doom-get-orphaned-packages () - "Return a list of symbols representing packages that are no longer needed or -depended on. - -Used by `doom-packages-autoremove'." - (let ((package-selected-packages - (mapcar #'car (doom-find-packages :ignored nil :disabled nil)))) - (append (cl-remove-if #'doom-package-registered-p (package--removable-packages)) - (cl-loop for pkg in package-selected-packages - if (and (doom-package-different-backend-p pkg) - (not (doom-package-built-in-p pkg))) - collect pkg)))) - -;;;###autoload -(defun doom-get-missing-packages () - "Return a list of requested packages that aren't installed or built-in, but -are enabled (with a `package!' directive). Each element is a list whose CAR is -the package symbol, and whose CDR is a plist taken from that package's -`package!' declaration. - -Used by `doom-packages-install'." - (cl-loop for (name . plist) - in (doom-find-packages :ignored nil - :disabled nil - :deps t) - if (and (equal (plist-get plist :pin) - (ignore-errors - (package-desc-archive - (cadr (assq name package-alist))))) - (or (not (doom-package-installed-p name)) - (doom-package-different-backend-p name) - (doom-package-different-recipe-p name))) - collect (cons name plist))) + (nreverse doom-packages))) ;; -;; Main functions - -(defun doom--delete-package-files (name-or-desc) - (let ((pkg-build-dir - (if (package-desc-p name-or-desc) - (package-desc-dir name-or-desc) - (expand-file-name (symbol-name name-or-desc) quelpa-build-dir)))) - (when (file-directory-p pkg-build-dir) - (delete-directory pkg-build-dir t)))) - -;;;###autoload -(defun doom-install-package (name &optional plist) - "Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an -example; the package name can be omitted)." - (cl-check-type name symbol) - (when (and (doom-package-installed-p name) - (not (doom-package-built-in-p name))) - (if (or (doom-package-different-backend-p name) - (doom-package-different-recipe-p name)) - (doom-delete-package name t) - (user-error "%s is already installed" name))) - (let* ((inhibit-message (not doom-debug-mode)) - (plist (or plist (doom-package-plist name)))) - (if-let (recipe (plist-get plist :recipe)) - (condition-case e - (let (quelpa-upgrade-p) - (quelpa recipe)) - ((debug error) - (doom--delete-package-files name) - (signal (car e) (cdr e)))) - (package-install name)) - (if (not (doom-package-installed-p name)) - (doom--delete-package-files name) - (add-to-list 'package-selected-packages name nil 'eq) - (setf (alist-get name doom-packages) plist) - name))) - -;;;###autoload -(defun doom-update-package (name &optional force-p) - "Updates package NAME (a symbol) if it is out of date, using quelpa or -package.el as appropriate." - (cl-check-type name symbol) - (unless (doom-package-installed-p name) - (error "%s isn't installed" name)) - (when (doom-package-different-backend-p name) - (user-error "%s's backend has changed and must be uninstalled first" name)) - (when (or force-p (doom-package-outdated-p name)) - (let ((inhibit-message (not doom-debug-mode)) - (desc (doom-package-desc name))) - (pcase (doom-package-backend name) - (`quelpa - (let ((name (doom-package-true-name name))) - (condition-case e - (let ((quelpa-upgrade-p t)) - (quelpa (assq name quelpa-cache))) - ((debug error) - (doom--delete-package-files name) - (signal (car e) (cdr e)))))) - (`elpa - (let* ((archive (cadr (assq name package-archive-contents))) - (packages - (if (package-desc-p archive) - (package-compute-transaction (list archive) (package-desc-reqs archive)) - (package-compute-transaction () (list (list archive)))))) - (package-download-transaction packages)))) - (unless (doom-package-outdated-p name) - (doom--delete-package-files desc) - t)))) - -;;;###autoload -(defun doom-delete-package (name &optional force-p) - "Uninstalls package NAME if it exists, and clears it from `quelpa-cache'." - (cl-check-type name symbol) - (unless (doom-package-installed-p name) - (user-error "%s isn't installed" name)) - (let ((inhibit-message (not doom-debug-mode)) - (name (doom-package-true-name name))) - (when-let (spec (assq name quelpa-cache)) - (delq! spec quelpa-cache) - (quelpa-save-cache)) - (package-delete (doom-package-desc name) force-p) - (doom--delete-package-files name) - (not (doom-package-installed-p name)))) - - -;; -;; Interactive commands +;;; Main functions ;;;###autoload (defun doom/reload-packages () "Reload `doom-packages', `package' and `quelpa'." (interactive) + ;; HACK straight.el must be loaded for this to work (message "Reloading packages") (doom-initialize-packages t) (message "Reloading packages...DONE")) - -;;;###autoload -(defun doom/update-package (pkg) - "Prompts the user with a list of outdated packages and updates the selected -package. Use this interactively. Use `doom-update-package' for direct -calls." - (declare (interactive-only t)) - (interactive - (let* ((packages (doom-get-outdated-packages)) - (selection (if packages - (completing-read "Update package: " - (mapcar #'car packages) - nil t) - (user-error "All packages are up to date"))) - (name (car (assoc (intern selection) package-alist)))) - (unless name - (user-error "'%s' is already up-to-date" selection)) - (list (assq name packages)))) - (cl-destructuring-bind (package old-version new-version) pkg - (if-let (desc (doom-package-outdated-p package)) - (let ((old-v-str (package-version-join old-version)) - (new-v-str (package-version-join new-version))) - (if (y-or-n-p (format "%s will be updated from %s to %s. Update?" - package old-v-str new-v-str)) - (message "%s %s (%s => %s)" - (if (doom-update-package package t) "Updated" "Failed to update") - package old-v-str new-v-str) - (message "Aborted"))) - (message "%s is up-to-date" package)))) - - -;; -;; Advice - -;;;###autoload -(defun doom*package-delete (desc &rest _) - "Update `quelpa-cache' upon a successful `package-delete'." - (let ((name (package-desc-name desc))) - (unless (doom-package-installed-p name) - (when-let (spec (assq name quelpa-cache)) - (setq quelpa-cache (delq spec quelpa-cache)) - (quelpa-save-cache) - (doom--delete-package-files name))))) - - -;; -;; Make package.el cooperate with Doom - -;; Updates QUELPA after deleting a package -;;;###autoload -(advice-add #'package-delete :after #'doom*package-delete) - -;; Replace with Doom variants -;;;###autoload -(advice-add #'package-autoremove :override #'doom//autoremove) - -;;;###autoload -(advice-add #'package-install-selected-packages :override #'doom//install) diff --git a/core/autoload/plist.el b/core/autoload/plist.el new file mode 100644 index 000000000..1d06895cc --- /dev/null +++ b/core/autoload/plist.el @@ -0,0 +1,89 @@ +;;; core/autoload/plist.el -*- lexical-binding: t; -*- + +;; +;;; Macros + +;;;###autoload +(cl-defmacro doplist! ((arglist plist &optional retval) &rest body) + "Loop over a PLIST's (property value) pairs then return RETVAL. + +Evaluate BODY with either ARGLIST bound to (cons PROP VAL) or, if ARGLIST is a +list, the pair is destructured into (CAR . CDR)." + (declare (indent defun)) + (let ((plist-var (make-symbol "plist"))) + `(let ((,plist-var ,plist)) + (while ,plist-var + (let ,(if (listp arglist) + `((,(pop arglist) (pop ,plist-var)) + (,(pop arglist) (pop ,plist-var))) + `((,arglist (cons (pop ,plist-var) + (pop ,plist-var))))) + ,@body)) + ,retval))) + +;;;###autoload +(defmacro plist-put! (plist prop value) + "Set PROP to VALUE in PLIST in-place." + `(setq ,plist (plist-put ,plist ,prop ,value))) + +;;;###autoload +(defmacro plist-delete! (plist prop) + "Delete PROP from PLIST in-place." + `(setq ,plist (doom-plist-delete ,plist ,prop))) + +;;;###autoload +(defmacro with-plist! (plist props &rest body) + "With props bound from PLIST to PROPS, evaluate BODY. + +PROPS is a list of symbols. Each one is converted to a keyword and then its +value is looked up in the PLIST and bound to the symbol for the duration of +BODY." + (declare (indent 2)) + (let ((plist-sym (make-symbol "plist"))) + `(let* ((,plist-sym ,plist) + ,@(cl-loop for prop in props + collect + `(,prop + (plist-get + ,plist-sym + ,(doom-keyword-intern (symbol-name prop)))))) + ,@body))) + + +;; +;;; Library + +;;;###autoload +(defun doom-plist-get (plist prop &optional nil-value) + "Return PROP in PLIST, if it exists. Otherwise NIL-VALUE." + (if-let (val (plist-member plist prop)) + (cadr val) + nil-value)) + +;;;###autoload +(defun doom-plist-merge (from-plist to-plist) + "Destructively merge FROM-PLIST onto TO-PLIST" + (let ((plist (copy-sequence from-plist))) + (while plist + (plist-put! to-plist (pop plist) (pop plist))) + to-plist)) + +;;;###autoload +(defun doom-plist-delete-nil (plist) + "Delete `nil' properties from a copy of PLIST." + (let (p) + (while plist + (if (car plist) + (plist-put! p (car plist) (nth 1 plist))) + (setq plist (cddr plist))) + p)) + +;;;###autoload +(defun doom-plist-delete (plist prop) + "Delete PROP from a copy of PLIST." + (let (p) + (while plist + (if (not (eq prop (car plist))) + (plist-put! p (car plist) (nth 1 plist))) + (setq plist (cddr plist))) + p)) diff --git a/core/autoload/projects.el b/core/autoload/projects.el index 9fed5c423..364941ab5 100644 --- a/core/autoload/projects.el +++ b/core/autoload/projects.el @@ -2,8 +2,7 @@ (defvar projectile-project-root nil) -;;;###autoload -(autoload 'projectile-relevant-known-projects "projectile") +;;;###autoload (autoload 'projectile-relevant-known-projects "projectile") ;;;###autodef (cl-defun set-project-type! (name &key predicate compile run test configure dir) @@ -23,16 +22,6 @@ ;; ;;; Macros -;;;###autoload -(defmacro without-project-cache! (&rest body) - "Run BODY with projectile's project-root cache disabled. This is necessary if -you want to interactive with a project other than the one you're in." - `(let ((projectile-project-root-cache (make-hash-table :test 'equal)) - projectile-project-name - projectile-project-root - projectile-require-project-root) - ,@body)) - ;;;###autoload (defmacro project-file-exists-p! (files) "Checks if the project has the specified FILES. @@ -90,8 +79,8 @@ Returns nil if not in a project." "Return the name of the current project. Returns '-' if not in a valid project." - (if-let* ((project-root (or (doom-project-root dir) - (if dir (expand-file-name dir))))) + (if-let (project-root (or (doom-project-root dir) + (if dir (expand-file-name dir)))) (funcall projectile-project-name-function project-root) "-")) @@ -121,14 +110,16 @@ If DIR is not a project, it will be indexed (but not cached)." (setq projectile-enable-caching nil)) (call-interactively ;; Intentionally avoid `helm-projectile-find-file', because it runs - ;; asynchronously, and thus doesn't see the lexical `default-directory' - (if (featurep! :completion ivy) + ;; asynchronously, and thus doesn't see the lexical + ;; `default-directory' + (if (doom-module-p :completion 'ivy) #'counsel-projectile-find-file #'projectile-find-file))) - ((fboundp 'project-find-file-in) ; emacs 26.1+ only - (project-find-file-in nil (list default-directory) nil)) ((fboundp 'counsel-file-jump) ; ivy only (call-interactively #'counsel-file-jump)) + ((and (fboundp 'project-find-file-in) ; emacs 26.1+ only + (project-current)) + (project-find-file-in nil (list default-directory) nil)) ((fboundp 'helm-find-files) (call-interactively #'helm-find-files)) ((call-interactively #'find-file))))) @@ -138,8 +129,8 @@ If DIR is not a project, it will be indexed (but not cached)." "Traverse a file structure starting linearly from DIR." (let ((default-directory (file-truename (expand-file-name dir)))) (call-interactively - (cond ((featurep! :completion ivy) + (cond ((doom-module-p :completion 'ivy) #'counsel-find-file) - ((featurep! :completion helm) + ((doom-module-p :completion 'helm) #'helm-find-files) (#'find-file))))) diff --git a/core/autoload/scratch.el b/core/autoload/scratch.el index 39812b2fe..a93456ebc 100644 --- a/core/autoload/scratch.el +++ b/core/autoload/scratch.el @@ -19,18 +19,22 @@ following: (defvar doom-scratch-buffers nil "A list of active scratch buffers.") -(defvar-local doom-scratch-current-project nil +(defvar doom-scratch-current-project nil "The name of the project associated with the current scratch buffer.") (defvar doom-scratch-buffer-hook () "The hooks to run after a scratch buffer is created.") + (defun doom--load-persistent-scratch-buffer (name) - (let ((scratch-file (expand-file-name (or name doom-scratch-default-file) - doom-scratch-dir))) + (setq-local doom-scratch-current-project + (or name + doom-scratch-default-file)) + (let ((scratch-file + (expand-file-name doom-scratch-current-project + doom-scratch-dir))) (make-directory doom-scratch-dir t) - (if (not (file-readable-p scratch-file)) - nil + (when (file-readable-p scratch-file) (erase-buffer) (insert-file-contents scratch-file) (set-auto-mode) @@ -39,48 +43,54 @@ following: ;;;###autoload (defun doom-scratch-buffer (&optional mode directory project-name) "Return a scratchpad buffer in major MODE." - (let* ((buffer-name (if project-name - (format "*doom:scratch (%s)*" project-name) - "*doom:scratch*")) - (buffer (get-buffer buffer-name))) - (with-current-buffer (get-buffer-create buffer-name) - (unless buffer - (setq buffer (current-buffer) - default-directory directory - doom-scratch-current-project project-name) - (setq doom-scratch-buffers (cl-delete-if-not #'buffer-live-p doom-scratch-buffers)) - (cl-pushnew buffer doom-scratch-buffers) - (doom--load-persistent-scratch-buffer project-name) - (when (and (eq major-mode 'fundamental-mode) - (functionp mode)) - (funcall mode)) - (add-hook 'kill-buffer-hook #'doom|persist-scratch-buffer nil 'local) - (run-hooks 'doom-scratch-buffer-created-hook)) - buffer))) + (with-current-buffer + (get-buffer-create (if project-name + (format "*doom:scratch (%s)*" project-name) + "*doom:scratch*")) + (setq default-directory directory) + (unless doom-scratch-current-project + (doom--load-persistent-scratch-buffer project-name) + (when (and (eq major-mode 'fundamental-mode) + (functionp mode)) + (funcall mode))) + (cl-pushnew (current-buffer) doom-scratch-buffers) + (add-hook 'kill-buffer-hook #'doom-persist-scratch-buffer-h nil 'local) + (add-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h) + (run-hooks 'doom-scratch-buffer-created-hook) + (current-buffer))) ;; ;;; Persistent scratch buffer ;;;###autoload -(defun doom|persist-scratch-buffer () +(defun doom-persist-scratch-buffer-h () "Save the current buffer to `doom-scratch-dir'." (write-region (point-min) (point-max) - (expand-file-name (or doom-scratch-current-project doom-scratch-default-file) + (expand-file-name (or doom-scratch-current-project + doom-scratch-default-file) doom-scratch-dir))) ;;;###autoload -(defun doom|persist-scratch-buffers () +(defun doom-persist-scratch-buffers-h () "Save all scratch buffers to `doom-scratch-dir'." - (setq doom-scratch-buffers (cl-delete-if-not #'buffer-live-p doom-scratch-buffers)) + (setq doom-scratch-buffers + (cl-delete-if-not #'buffer-live-p doom-scratch-buffers)) (dolist (buffer doom-scratch-buffers) (with-current-buffer buffer - (doom|persist-scratch-buffer)))) + (doom-persist-scratch-buffer-h)))) + +;;;###autoload +(defun doom-persist-scratch-buffers-after-switch-h () + "Kill scratch buffers when they are no longer visible, saving them to disk." + (unless (cl-some #'get-buffer-window doom-scratch-buffers) + (mapc #'kill-buffer doom-scratch-buffers) + (remove-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h))) ;;;###autoload (unless noninteractive - (add-hook 'kill-emacs-hook #'doom|persist-scratch-buffers)) + (add-hook 'kill-emacs-hook #'doom-persist-scratch-buffers-h)) ;; @@ -115,17 +125,19 @@ If PROJECT-P is non-nil, open a persistent scratch buffer associated with the ;;;###autoload (defun doom/switch-to-scratch-buffer (&optional project-p) - "Like `doom/open-scratch-buffer', but switches to it in the current window." - (interactive) - (doom/open-scratch-buffer t)) + "Like `doom/open-scratch-buffer', but switches to it in the current window. + +If passed the prefix arg, open project scratch buffer." + (interactive "P") + (doom/open-scratch-buffer t project-p)) ;;;###autoload -(defun doom/open-project-scratch-buffer (&optional arg) +(defun doom/open-project-scratch-buffer (&optional current-window) "Opens the (persistent) project scratch buffer in a popup. -If passed the prefix ARG, switch to it in the current window." +If passed the prefix arg, switch to it in the current window." (interactive "P") - (doom/open-scratch-buffer arg 'project)) + (doom/open-scratch-buffer current-window 'project)) ;;;###autoload (defun doom/switch-to-project-scratch-buffer () diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el index 3474fc3e1..45dd18ae0 100644 --- a/core/autoload/sessions.el +++ b/core/autoload/sessions.el @@ -1,5 +1,11 @@ ;;; core/autoload/sessions.el -*- lexical-binding: t; -*- +(defvar desktop-base-file-name) +(defvar desktop-dirname) +(defvar desktop-restore-eager) +(defvar desktop-file-modtime) + + ;; ;;; Helpers @@ -59,9 +65,6 @@ "TODO" (add-hook 'window-setup-hook #'doom-load-session 'append)) -;;;###autoload -(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)) - ;; ;;; Commands diff --git a/core/autoload/text.el b/core/autoload/text.el index 0f11d95f5..cbb4af2f7 100644 --- a/core/autoload/text.el +++ b/core/autoload/text.el @@ -25,6 +25,41 @@ lines, above and below, with only whitespace in between." (or (not balanced) (= (- pt nbeg) (- nend pt)))))))))))) +;;;###autoload +(defun doom-point-in-comment-p (&optional pos) + "Return non-nil if POS is in a comment. + +POS defaults to the current position." + ;; REVIEW Should we cache `syntax-ppss'? + (let* ((pos (or pos (point))) + (ppss (syntax-ppss pos))) + (or (nth 4 ppss) + (nth 8 ppss) + (and (< pos (point-max)) + (memq (char-syntax (char-after pos)) '(?< ?>)) + (not (eq (char-after pos) ?\n))) + (when-let (s (car (syntax-after pos))) + (or (and (/= 0 (logand (lsh 1 16) s)) + (nth 4 (doom-syntax-ppss (+ pos 2)))) + (and (/= 0 (logand (lsh 1 17) s)) + (nth 4 (doom-syntax-ppss (+ pos 1)))) + (and (/= 0 (logand (lsh 1 18) s)) + (nth 4 (doom-syntax-ppss (- pos 1)))) + (and (/= 0 (logand (lsh 1 19) s)) + (nth 4 (doom-syntax-ppss (- pos 2))))))))) + +;;;###autoload +(defun doom-point-in-string-p (&optional pos) + "Return non-nil if POS is in a string." + ;; REVIEW Should we cache `syntax-ppss'? + (nth 3 (syntax-ppss pos))) + +;;;###autoload +(defun doom-point-in-string-or-comment-p (&optional pos) + "Return non-nil if POS is in a string or comment." + (or (doom-point-in-string-p pos) + (doom-point-in-comment-p pos))) + ;; ;; Commands @@ -186,9 +221,13 @@ Respects `require-final-newline'." (setq indent-tabs-mode (not indent-tabs-mode)) (message "Indent style changed to %s" (if indent-tabs-mode "tabs" "spaces"))) +(defvar editorconfig-lisp-use-default-indent) ;;;###autoload (defun doom/set-indent-width (width) - "Change the indentation width of the current buffer." + "Change the indentation size to WIDTH of the current buffer. + +The effectiveness of this command is significantly improved if you have +editorconfig or dtrt-indent installed." (interactive (list (if (integerp current-prefix-arg) current-prefix-arg @@ -211,25 +250,25 @@ Respects `require-final-newline'." ;; Hooks ;;;###autoload -(defun doom|enable-delete-trailing-whitespace () +(defun doom-enable-delete-trailing-whitespace-h () "Enables the automatic deletion of trailing whitespaces upon file save. i.e. enables `ws-butler-mode' in the current buffer." (ws-butler-mode +1)) ;;;###autoload -(defun doom|disable-delete-trailing-whitespace () +(defun doom-disable-delete-trailing-whitespace-h () "Disables the automatic deletion of trailing whitespaces upon file save. i.e. disables `ws-butler-mode' in the current buffer." (ws-butler-mode -1)) ;;;###autoload -(defun doom|enable-show-trailing-whitespace () +(defun doom-enable-show-trailing-whitespace-h () "Enable `show-trailing-whitespace' in the current buffer." (setq-local show-trailing-whitespace t)) ;;;###autoload -(defun doom|disable-show-trailing-whitespace () +(defun doom-disable-show-trailing-whitespace-h () "Disable `show-trailing-whitespace' in the current buffer." (setq-local show-trailing-whitespace nil)) diff --git a/core/autoload/themes.el b/core/autoload/themes.el new file mode 100644 index 000000000..ef9d9a4b5 --- /dev/null +++ b/core/autoload/themes.el @@ -0,0 +1,50 @@ +;;; core/autoload/themes.el -*- lexical-binding: t; -*- + +(defun doom--custom-theme-set-face (spec) + (cond ((listp (car spec)) + (cl-loop for face in (car spec) + collect + (doom--custom-theme-set-face `(,face ,(cdr spec))))) + ((keywordp (cadr spec)) + `((,(car spec) ((t ,(cdr spec)))))) + (`((,(car spec) ,(cdr spec)))))) + +;;;###autoload +(defun custom-theme-set-faces! (theme &rest specs) + "Apply a list of face SPECS as user customizations for THEME. + +THEME can be a single symbol or list thereof. If nil, apply these settings to +all themes. It will apply to all themes once they are loaded." + (let* ((themes (doom-enlist (or theme 'user))) + (fn (gensym (format "doom--customize-%s-h-" (mapconcat #'symbol-name themes "-"))))) + (fset fn + (lambda () + (dolist (theme themes) + (when (or (eq theme 'user) + (custom-theme-enabled-p theme)) + (apply #'custom-theme-set-faces 'user + (mapcan #'doom--custom-theme-set-face + specs)))))) + (funcall fn) + (add-hook 'doom-load-theme-hook fn))) + +;;;###autoload +(defun custom-set-faces! (&rest specs) + "Apply a list of face SPECS as user customizations. + +This is a drop-in replacement for `custom-set-face' that allows for a simplified +face format." + (apply #'custom-theme-set-faces! 'user specs)) + +(defvar doom--prefer-theme-elc) +;;;###autoload +(defun doom/reload-theme () + "Reload the current color theme." + (interactive) + (let ((theme (or (car-safe custom-enabled-themes) doom-theme))) + (when theme + (mapc #'disable-theme custom-enabled-themes)) + (when (and doom-theme (not (memq doom-theme custom-enabled-themes))) + (let (doom--prefer-theme-elc) + (load-theme doom-theme t))) + (doom-init-fonts-h))) diff --git a/core/autoload/ui.el b/core/autoload/ui.el index 92b9df093..d1cb2dd97 100644 --- a/core/autoload/ui.el +++ b/core/autoload/ui.el @@ -27,12 +27,12 @@ are open." ;; Advice ;;;###autoload -(defun doom*recenter (&rest _) +(defun doom-recenter-a (&rest _) "Generic advisor for recentering window (typically :after other functions)." (recenter)) ;;;###autoload -(defun doom*shut-up (orig-fn &rest args) +(defun doom-shut-up-a (orig-fn &rest args) "Generic advisor for silencing noisy functions." (quiet! (apply orig-fn args))) @@ -41,14 +41,14 @@ are open." ;; Hooks ;;;###autoload -(defun doom|apply-ansi-color-to-compilation-buffer () +(defun doom-apply-ansi-color-to-compilation-buffer-h () "Applies ansi codes to the compilation buffers. Meant for `compilation-filter-hook'." (with-silent-modifications (ansi-color-apply-on-region compilation-filter-start (point)))) ;;;###autoload -(defun doom|disable-show-paren-mode () +(defun doom-disable-show-paren-mode-h () "Turn off `show-paren-mode' buffer-locally." (setq-local show-paren-mode nil)) @@ -67,6 +67,7 @@ visual-line-mode is on, this skips relative and uses visual instead. See `display-line-numbers' for what these values mean." (interactive) (defvar doom--line-number-style display-line-numbers-type) + ;; DEPRECATED (let* ((styles `(t ,(if (and EMACS26+ visual-line-mode) 'visual 'relative) nil)) (order (cons display-line-numbers-type (remq display-line-numbers-type styles))) (queue (memq doom--line-number-style order)) @@ -74,6 +75,7 @@ See `display-line-numbers' for what these values mean." (car order) (car (cdr queue))))) (setq doom--line-number-style next) + ;; DEPRECATED (if EMACS26+ (setq display-line-numbers next) (pcase next diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el index e16b74ebe..b28fed6c1 100644 --- a/core/cli/autoloads.el +++ b/core/cli/autoloads.el @@ -1,16 +1,12 @@ ;;; core/cli/autoloads.el -*- lexical-binding: t; -*- -(dispatcher! (autoloads a) - (doom-reload-autoloads nil 'force) - "Regenerates Doom's autoloads files. +(require 'autoload) -It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, -modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates and -byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file' -(created from the concatenated autoloads files of all installed packages). -It also caches `load-path', `Info-directory-list', `doom-disabled-packages', -`package-activated-list' and `auto-mode-alist'.") +(defvar doom-autoload-excluded-packages '("gh") + "Packages that have silly or destructive autoload files that try to load +everyone in the universe and their dog, causing errors that make babies cry. No +one wants that.") ;; external variables (defvar autoload-timestamps) @@ -19,57 +15,57 @@ It also caches `load-path', `Info-directory-list', `doom-disabled-packages', ;; -;;; Helpers +;;; Commands -(defvar doom-autoload-excluded-packages '(marshal gh) - "Packages that have silly or destructive autoload files that try to load -everyone in the universe and their dog, causing errors that make babies cry. No -one wants that.") +(defcli! (autoloads a) () + "Regenerates Doom's autoloads files. + +It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, +modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates and +byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file' +(created from the concatenated autoloads files of all installed packages). + +It also caches `load-path', `Info-directory-list', `doom-disabled-packages', +`package-activated-list' and `auto-mode-alist'." + (doom-reload-autoloads nil 'force)) + + +;; +;;; Helpers (defun doom-delete-autoloads-file (file) "Delete FILE (an autoloads file) and accompanying *.elc file, if any." (cl-check-type file string) (when (file-exists-p file) - (when-let (buf (find-buffer-visiting doom-autoload-file)) + (when-let (buf (find-buffer-visiting file)) (with-current-buffer buf (set-buffer-modified-p nil)) (kill-buffer buf)) (delete-file file) (ignore-errors (delete-file (byte-compile-dest-file file))) - (message "Deleted old %s" (file-name-nondirectory file)))) + t)) -(defun doom--warn-refresh-session () - (print! (bold (green "\nFinished!"))) - (message "If you have a running Emacs Session, you will need to restart it or") - (message "reload Doom for changes to take effect:\n") +(defun doom--warn-refresh-session-h () + (message "Restart or reload Doom Emacs for changes to take effect:\n") (message " M-x doom/restart-and-restore") (message " M-x doom/restart") (message " M-x doom/reload")) -(defun doom--reload-files (&rest files) - (if (not noninteractive) - (dolist (file files) - (load-file (byte-compile-dest-file file))) - (add-hook 'kill-emacs-hook #'doom--warn-refresh-session))) - (defun doom--byte-compile-file (file) - (let ((short-name (file-name-nondirectory file)) + (let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)) + (byte-compile-dynamic t) (byte-compile-dynamic-docstrings t)) - (condition-case e + (condition-case-unless-debug e (when (byte-compile-file file) - ;; Give autoloads file a chance to report error - (load (if doom-debug-mode - file - (byte-compile-dest-file file)) - nil t) - (unless noninteractive - (message "Finished compiling %s" short-name))) + (prog1 (load file 'noerror 'nomessage) + (when noninteractive + (add-hook 'doom-cli-post-success-execute-hook #'doom--warn-refresh-session-h)))) ((debug error) (let ((backup-file (concat file ".bk"))) - (message "Copied backup to %s" backup-file) + (print! (warn "Copied backup to %s") (relpath backup-file)) (copy-file file backup-file 'overwrite)) (doom-delete-autoloads-file file) - (signal 'doom-autoload-error (list short-name e)))))) + (signal 'doom-autoload-error (list file e)))))) (defun doom-reload-autoloads (&optional file force-p) "Reloads FILE (an autoload file), if it needs reloading. @@ -82,71 +78,128 @@ even if it doesn't need reloading!" (signal 'wrong-type-argument (list 'stringp file))) (if (stringp file) (cond ((file-equal-p file doom-autoload-file) - (doom-reload-doom-autoloads force-p)) + (doom-reload-core-autoloads force-p)) ((file-equal-p file doom-package-autoload-file) (doom-reload-package-autoloads force-p)) ((error "Invalid autoloads file: %s" file))) - (doom-reload-doom-autoloads force-p) + (doom-reload-core-autoloads force-p) (doom-reload-package-autoloads force-p))) ;; ;;; Doom autoloads -(defun doom--file-cookie-p (file) - "Returns the return value of the ;;;###if predicate form in FILE." - (with-temp-buffer - (insert-file-contents-literally file nil 0 256) - (if (and (re-search-forward "^;;;###if " nil t) - (<= (line-number-at-pos) 3)) - (let ((load-file-name file)) - (eval (sexp-at-point))) - t))) - (defun doom--generate-header (func) (goto-char (point-min)) - (insert ";; -*- lexical-binding:t -*-\n" + (insert ";; -*- lexical-binding:t; -*-\n" ";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n")) (defun doom--generate-autoloads (targets) - (require 'autoload) - (dolist (file targets) - (let* ((file (file-truename file)) - (generated-autoload-file doom-autoload-file) - (generated-autoload-load-name (file-name-sans-extension file)) - (noninteractive (not doom-debug-mode)) - autoload-timestamps) - (print! - (cond ((not (doom--file-cookie-p file)) - "⚠ Ignoring %s") - ((autoload-generate-file-autoloads file (current-buffer)) - (yellow "✕ Nothing in %s")) - ((green "✓ Scanned %s"))) - (if (file-in-directory-p file default-directory) - (file-relative-name file) - (abbreviate-file-name file)))))) + (let ((n 0)) + (dolist (file targets) + (insert + (with-temp-buffer + (cond ((not (doom-file-cookie-p file)) + (print! (debug "Ignoring %s") (relpath file))) -(defun doom--expand-autoloads () + ((let ((generated-autoload-load-name (file-name-sans-extension file))) + (autoload-generate-file-autoloads file (current-buffer))) + (print! (debug "Nothing in %s") (relpath file))) + + ((cl-incf n) + (print! (debug "Scanning %s...") (relpath file)))) + (buffer-string)))) + (print! (class (if (> n 0) 'success 'info) + "Scanned %d file(s)") + n))) + +(defun doom--expand-autoload-paths (&optional allow-internal-paths) (let ((load-path ;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files ;; will be unable to declare autoloads for the built-in autoload.el ;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why ;; they'd want to though, so it's an acceptable compromise. - (append (list doom-private-dir doom-emacs-dir) + (append (list doom-private-dir) doom-modules-dirs - load-path)) - cache) - (while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) + (straight--directory-files (straight--build-dir) nil t) + load-path))) + (defvar doom--autoloads-path-cache nil) + (while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) (let ((path (match-string 1))) (replace-match - (or (cdr (assoc path cache)) - (when-let* ((libpath (locate-library path)) - (libpath (file-name-sans-extension libpath))) - (push (cons path (abbreviate-file-name libpath)) cache) + (or (cdr (assoc path doom--autoloads-path-cache)) + (when-let* ((libpath (or (and allow-internal-paths + (locate-library path nil (cons doom-emacs-dir doom-modules-dirs))) + (locate-library path))) + (libpath (file-name-sans-extension libpath)) + (libpath (abbreviate-file-name libpath))) + (push (cons path libpath) doom--autoloads-path-cache) libpath) path) t t nil 1))))) +(defun doom--generate-autodefs-1 (path &optional member-p) + (let (forms) + (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t) + (let* ((sexp (sexp-at-point)) + (alt-sexp (match-string 1)) + (type (car sexp)) + (name (doom-unquote (cadr sexp))) + (origin (doom-module-from-path path))) + (cond + ((and (not member-p) + alt-sexp) + (push (read alt-sexp) forms)) + + ((memq type '(defun defmacro cl-defun cl-defmacro)) + (cl-destructuring-bind (_ _name arglist &rest body) sexp + (appendq! + forms + (list (if member-p + (make-autoload sexp path) + (let ((docstring + (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" + origin + (if (stringp (car body)) + (pop body) + "No documentation.")))) + (condition-case-unless-debug e + (if alt-sexp + (read alt-sexp) + (append + (list (pcase type + (`defun 'defmacro) + (`cl-defun `cl-defmacro) + (_ type)) + name arglist docstring) + (cl-loop for arg in arglist + if (and (symbolp arg) + (not (keywordp arg)) + (not (memq arg cl--lambda-list-keywords))) + collect arg into syms + else if (listp arg) + collect (car arg) into syms + finally return (if syms `((ignore ,@syms)))))) + ('error + (print! "- Ignoring autodef %s (%s)" name e) + nil)))) + `(put ',name 'doom-module ',origin))))) + + ((eq type 'defalias) + (cl-destructuring-bind (_type name target &optional docstring) sexp + (let ((name (doom-unquote name)) + (target (doom-unquote target))) + (unless member-p + (setq target #'ignore + docstring + (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" + origin docstring))) + (appendq! forms `((put ',name 'doom-module ',origin) + (defalias ',name #',target ,docstring)))))) + + (member-p (push sexp forms))))) + forms)) + (defun doom--generate-autodefs (targets enabled-targets) (goto-char (point-max)) (search-backward ";;;***" nil t) @@ -155,83 +208,17 @@ even if it doesn't need reloading!" (insert (with-temp-buffer (insert-file-contents path) - (let ((member-p (or (member path enabled-targets) - (file-in-directory-p path doom-core-dir))) - forms) - (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t) - (let* ((sexp (sexp-at-point)) - (alt-sexp (match-string 1)) - (type (car sexp)) - (name (doom-unquote (cadr sexp))) - (origin (cond ((doom-module-from-path path)) - ((file-in-directory-p path doom-private-dir) - `(:private . ,(intern (file-name-base path)))) - ((file-in-directory-p path doom-emacs-dir) - `(:core . ,(intern (file-name-base path)))))) - (doom-file-form - `(put ',name 'doom-file ,(abbreviate-file-name path)))) - (cond ((and (not member-p) alt-sexp) - (push (read alt-sexp) forms)) - - ((memq type '(defun defmacro cl-defun cl-defmacro)) - (cl-destructuring-bind (_ name arglist &rest body) sexp - (let ((docstring (if (stringp (car body)) - (pop body) - "No documentation."))) - (push (if member-p - (make-autoload sexp (abbreviate-file-name (file-name-sans-extension path))) - (push doom-file-form forms) - (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" - origin docstring)) - (condition-case-unless-debug e - (if alt-sexp - (read alt-sexp) - (append (list (pcase type - (`defun 'defmacro) - (`cl-defun `cl-defmacro) - (_ type)) - name arglist docstring) - (cl-loop for arg in arglist - if (and (symbolp arg) - (not (keywordp arg)) - (not (memq arg cl--lambda-list-keywords))) - collect arg into syms - else if (listp arg) - collect (car arg) into syms - finally return (if syms `((ignore ,@syms)))))) - ('error - (message "Ignoring autodef %s (%s)" - name e) - nil))) - forms) - (push `(put ',name 'doom-module ',origin) forms)))) - - ((eq type 'defalias) - (cl-destructuring-bind (_type name target &optional docstring) sexp - (let ((name (doom-unquote name)) - (target (doom-unquote target))) - (unless member-p - (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" - origin docstring)) - (setq target #'ignore)) - (push doom-file-form forms) - (push `(put ',name 'doom-module ',origin) forms) - (push `(defalias ',name #',target ,docstring) - forms)))) - - (member-p - (push sexp forms))))) - (if forms - (concat (mapconcat #'prin1-to-string (nreverse forms) "\n") - "\n") - "")))))) + (if-let (forms (doom--generate-autodefs-1 path (member path enabled-targets))) + (concat (mapconcat #'prin1-to-string (nreverse forms) "\n") + "\n") + ""))))) (defun doom--cleanup-autoloads () (goto-char (point-min)) (when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t) (replace-match "" t t))) -(defun doom-reload-doom-autoloads (&optional force-p) +(defun doom-reload-core-autoloads (&optional force-p) "Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil). It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, @@ -241,62 +228,77 @@ modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates Run this whenever your `doom!' block, or a module autoload file, is modified." (let* ((default-directory doom-emacs-dir) (doom-modules (doom-modules)) - (abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir)) - (targets - (file-expand-wildcards - (expand-file-name "autoload/*.el" doom-core-dir))) - (enabled-targets (copy-sequence targets)) - case-fold-search) - (dolist (path (doom-module-load-path t)) - (let* ((auto-dir (expand-file-name "autoload" path)) - (auto-file (expand-file-name "autoload.el" path)) - (module (doom-module-from-path auto-file)) - (module-p (or (doom-module-p (car module) (cdr module)) - (file-equal-p path doom-private-dir)))) - (when (file-exists-p auto-file) - (push auto-file targets) - (if module-p (push auto-file enabled-targets))) - (dolist (file (doom-files-in auto-dir :match "\\.el$" :full t :sort nil)) - (push file targets) - (if module-p (push file enabled-targets))))) - (if (and (not force-p) - (not doom-emacs-changed-p) - (file-exists-p doom-autoload-file) - (not (file-newer-than-file-p (expand-file-name "init.el" doom-private-dir) - doom-autoload-file)) - (not (cl-loop for file in targets - if (file-newer-than-file-p file doom-autoload-file) - return t))) - (progn (print! (green "Doom core autoloads is up-to-date")) - (doom-initialize-autoloads doom-autoload-file) - nil) - (doom-delete-autoloads-file doom-autoload-file) - (message "Generating new autoloads.el") - (make-directory (file-name-directory doom-autoload-file) t) - (with-temp-file doom-autoload-file - (doom--generate-header 'doom-reload-doom-autoloads) - (prin1 `(setq doom--modules-cache ',doom-modules) (current-buffer)) - (save-excursion - (doom--generate-autoloads (reverse enabled-targets))) - ;; Replace autoload paths (only for module autoloads) with absolute - ;; paths for faster resolution during load and simpler `load-path' - (save-excursion - (doom--expand-autoloads) - (print! (green "✓ Expanded module autoload paths"))) - ;; Generates stub definitions for functions/macros defined in disabled - ;; modules, so that you will never get a void-function when you use - ;; them. - (save-excursion - (doom--generate-autodefs (reverse targets) enabled-targets) - (print! (green "✓ Generated autodefs"))) - ;; Remove byte-compile-inhibiting file variables so we can byte-compile - ;; the file, and autoload comments. - (doom--cleanup-autoloads) - (print! (green "✓ Clean up autoloads"))) - ;; Byte compile it to give the file a chance to reveal errors. - (doom--byte-compile-file doom-autoload-file) - (doom--reload-files doom-autoload-file) - t))) + + ;; The following bindings are in `package-generate-autoloads'. + ;; Presumably for a good reason, so I just copied them + (noninteractive t) + (backup-inhibited t) + (version-control 'never) + (case-fold-search nil) ; reduce magic + (autoload-timestamps nil) + + ;; Where we'll store the files we'll scan for autoloads. This should + ;; contain *all* autoload files, even in disabled modules, so we can + ;; scan those for autodefs. We start with the core libraries. + (targets (doom-glob doom-core-dir "autoload/*.el")) + ;; A subset of `targets' in enabled modules + (active-targets (copy-sequence targets))) + + (dolist (path (doom-module-load-path 'all-p)) + (when-let* ((files (cons (doom-glob path "autoload.el") + (doom-files-in (doom-path path "autoload") + :match "\\.el$"))) + (files (delq nil files))) + (appendq! targets files) + (when (or (doom-module-from-path path 'enabled-only) + (file-equal-p path doom-private-dir)) + (appendq! active-targets files)))) + + (print! (start "Checking core autoloads file")) + (print-group! + (if (and (not force-p) + (file-exists-p doom-autoload-file) + (not (file-newer-than-file-p doom-emacs-dir doom-autoload-file)) + (not (cl-loop for dir + in (append (doom-glob doom-private-dir "init.el*") + targets) + if (file-newer-than-file-p dir doom-autoload-file) + return t))) + (ignore + (print! (success "Skipping core autoloads, they are up-to-date")) + (doom-load-autoloads-file doom-autoload-file)) + (print! (start "Regenerating core autoloads file")) + + (if (doom-delete-autoloads-file doom-autoload-file) + (print! (success "Deleted old %s") (filename doom-autoload-file)) + (make-directory (file-name-directory doom-autoload-file) t)) + + (with-temp-file doom-autoload-file + (doom--generate-header 'doom-reload-core-autoloads) + (save-excursion + (doom--generate-autoloads active-targets) + (print! (success "Generated new autoloads.el"))) + ;; Replace autoload paths (only for module autoloads) with absolute + ;; paths for faster resolution during load and simpler `load-path' + (save-excursion + (doom--expand-autoload-paths 'allow-internal-paths) + (print! (success "Expanded module autoload paths"))) + ;; Generates stub definitions for functions/macros defined in disabled + ;; modules, so that you will never get a void-function when you use + ;; them. + (save-excursion + (doom--generate-autodefs targets (reverse active-targets)) + (print! (success "Generated autodefs"))) + ;; Remove byte-compile-inhibiting file variables so we can byte-compile + ;; the file, and autoload comments. + (doom--cleanup-autoloads) + (print! (success "Clean up autoloads"))) + ;; Byte compile it to give the file a chance to reveal errors (and buy us a + ;; few marginal performance boosts) + (print! "> Byte-compiling %s..." (relpath doom-autoload-file)) + (when (doom--byte-compile-file doom-autoload-file) + (print! (success "Finished compiling %s") (relpath doom-autoload-file)))) + t))) ;; @@ -304,23 +306,24 @@ Run this whenever your `doom!' block, or a module autoload file, is modified." (defun doom--generate-package-autoloads () "Concatenates package autoload files, let-binds `load-file-name' around -them,and remove unnecessary `provide' statements or blank links. - -Skips over packages in `doom-autoload-excluded-packages'." - (dolist (spec (doom-get-package-alist)) - (if-let* ((pkg (car spec)) - (desc (cdr spec))) - (unless (memq pkg doom-autoload-excluded-packages) - (let ((file (concat (package--autoloads-file-name desc) ".el"))) - (when (file-exists-p file) - (insert "(let ((load-file-name " (prin1-to-string (abbreviate-file-name file)) "))\n") - (insert-file-contents file) - (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t) - (unless (nth 8 (syntax-ppss)) - (replace-match "" t t))) - (unless (bolp) (insert "\n")) - (insert ")\n")))) - (message "Couldn't find package desc for %s" (car spec))))) +them,and remove unnecessary `provide' statements or blank links." + (dolist (pkg (hash-table-keys straight--build-cache)) + (unless (member pkg doom-autoload-excluded-packages) + (let ((file (straight--autoloads-file pkg))) + (when (file-exists-p file) + (insert-file-contents-literally file) + (save-excursion + (while (re-search-forward "\\(?:\\_" nil t) + ;; `load-file-name' is meaningless in a concatenated + ;; mega-autoloads file, so we replace references to it and #$ with + ;; the file they came from. + (unless (doom-point-in-string-or-comment-p) + (replace-match (prin1-to-string (abbreviate-file-name file)) + t t)))) + (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t) + (unless (doom-point-in-string-p) + (replace-match "" t t))) + (unless (bolp) (insert "\n"))))))) (defun doom--generate-var-cache () "Print a `setq' form for expensive-to-initialize variables, so we can cache @@ -329,8 +332,7 @@ them in Doom's autoloads file." (prin1 `(setq load-path ',load-path auto-mode-alist ',auto-mode-alist Info-directory-list ',Info-directory-list - doom-disabled-packages ',(mapcar #'car (doom-find-packages :disabled t)) - package-activated-list ',package-activated-list) + doom-disabled-packages ',doom-disabled-packages) (current-buffer))) (defun doom--cleanup-package-autoloads () @@ -351,34 +353,63 @@ Will do nothing if none of your installed packages have been modified. If FORCE-P (universal argument) is non-nil, regenerate it anyway. This should be run whenever your `doom!' block or update your packages." - (let ((abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))) - (if (and (not force-p) - (not doom-emacs-changed-p) - (file-exists-p doom-package-autoload-file) - (not (file-newer-than-file-p doom-packages-dir doom-package-autoload-file)) - (not (ignore-errors - (cl-loop for key being the hash-keys of (doom-modules) - for path = (doom-module-path (car key) (cdr key) "packages.el") - if (file-newer-than-file-p path doom-package-autoload-file) - return t)))) - (ignore (print! (green "Doom package autoloads is up-to-date")) - (doom-initialize-autoloads doom-package-autoload-file)) - (let (case-fold-search) - (doom-delete-autoloads-file doom-package-autoload-file) - (with-temp-file doom-package-autoload-file - (doom--generate-header 'doom-reload-package-autoloads) - (save-excursion - ;; Cache important and expensive-to-initialize state here. - (doom--generate-var-cache) - (print! (green "✓ Cached package state")) - ;; Concatenate the autoloads of all installed packages. - (doom--generate-package-autoloads) - (print! (green "✓ Package autoloads included"))) - ;; Remove `load-path' and `auto-mode-alist' modifications (most of them, - ;; at least); they are cached later, so all those membership checks are - ;; unnecessary overhead. - (doom--cleanup-package-autoloads) - (print! (green "✓ Removed load-path/auto-mode-alist entries")))) - (doom--byte-compile-file doom-package-autoload-file) - (doom--reload-files doom-package-autoload-file) - t))) + (print! (start "Checking package autoloads file")) + (print-group! + (if (and (not force-p) + (file-exists-p doom-package-autoload-file) + (not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file)) + (not (cl-loop for dir in (straight--directory-files (straight--build-dir)) + if (cl-find-if + (lambda (dir) + (file-newer-than-file-p dir doom-package-autoload-file)) + (doom-glob (straight--build-dir dir) "*.el")) + return t)) + (not (cl-loop with doom-modules = (doom-modules) + for key being the hash-keys of doom-modules + for path = (doom-module-path (car key) (cdr key) "packages.el") + if (file-newer-than-file-p path doom-package-autoload-file) + return t))) + (ignore + (print! (success "Skipping package autoloads, they are up-to-date")) + (doom-load-autoloads-file doom-package-autoload-file)) + (let (;; The following bindings are in `package-generate-autoloads'. + ;; Presumably for a good reason, so I just copied them + (noninteractive t) + (backup-inhibited t) + (version-control 'never) + (case-fold-search nil) ; reduce magit + (autoload-timestamps nil)) + (print! (start "Regenerating package autoloads file")) + + (if (doom-delete-autoloads-file doom-package-autoload-file) + (print! (success "Deleted old %s") (filename doom-package-autoload-file)) + (make-directory (file-name-directory doom-autoload-file) t)) + + (with-temp-file doom-package-autoload-file + (doom--generate-header 'doom-reload-package-autoloads) + + (save-excursion + ;; Cache important and expensive-to-initialize state here. + (doom--generate-var-cache) + (print! (success "Cached package state")) + ;; Concatenate the autoloads of all installed packages. + (doom--generate-package-autoloads) + (print! (success "Package autoloads included"))) + + ;; Replace autoload paths (only for module autoloads) with absolute + ;; paths for faster resolution during load and simpler `load-path' + (save-excursion + (doom--expand-autoload-paths) + (print! (success "Expanded module autoload paths"))) + + ;; Remove `load-path' and `auto-mode-alist' modifications (most of them, + ;; at least); they are cached later, so all those membership checks are + ;; unnecessary overhead. + (doom--cleanup-package-autoloads) + (print! (success "Removed load-path/auto-mode-alist entries"))) + ;; Byte compile it to give the file a chance to reveal errors (and buy us a + ;; few marginal performance boosts) + (print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file)) + (when (doom--byte-compile-file doom-package-autoload-file) + (print! (success "Finished compiling %s") (relpath doom-package-autoload-file))))) + t)) diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el index 4d7533011..40fff8b81 100644 --- a/core/cli/byte-compile.el +++ b/core/cli/byte-compile.el @@ -1,21 +1,24 @@ ;;; core/cli/byte-compile.el -*- lexical-binding: t; -*- -(dispatcher! (compile c) (doom-byte-compile args) +(defcli! (compile c) (&rest targets) "Byte-compiles your config or selected modules. compile [TARGETS...] compile :core :private lang/python compile feature lang -Accepts :core, :private and :plugins as special arguments, indicating you want -to byte-compile Doom's core files, your private config or your ELPA plugins, -respectively.") +Accepts :core and :private as special arguments, which target Doom's core files +and your private config files, respectively. To recompile your packages, use +'doom rebuild' instead." + (doom-byte-compile targets)) -(dispatcher! (recompile rc) (doom-byte-compile args 'recompile) - "Re-byte-compiles outdated *.elc files.") +(defcli! (recompile rc) (&rest targets) + "Re-byte-compiles outdated *.elc files." + (doom-byte-compile targets 'recompile)) -(dispatcher! clean (doom-clean-byte-compiled-files) - "Delete all *.elc files.") +(defcli! clean () + "Delete all *.elc files." + (doom-clean-byte-compiled-files)) ;; @@ -25,9 +28,10 @@ respectively.") (let ((filename (file-name-nondirectory path))) (or (string-prefix-p "." filename) (string-prefix-p "test-" filename) - (not (equal (file-name-extension path) "el"))))) + (not (equal (file-name-extension path) "el")) + (member filename (list "packages.el" "doctor.el"))))) -(defun doom-byte-compile (&optional modules recompile-p) +(cl-defun doom-byte-compile (&optional modules recompile-p) "Byte compiles your emacs configuration. init.el is always byte-compiled by this. @@ -45,35 +49,36 @@ byte-compilation. If RECOMPILE-P is non-nil, only recompile out-of-date files." (let ((default-directory doom-emacs-dir) - (total-ok 0) - (total-fail 0) - (total-noop 0) - compile-plugins-p + (doom-modules (doom-modules)) + (byte-compile-verbose doom-debug-mode) + (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) + + ;; In case it is changed during compile-time + (auto-mode-alist auto-mode-alist) + (noninteractive t) + targets) - (dolist (module (delete-dups modules) (nreverse targets)) - (pcase module - (":core" (push doom-core-dir targets)) - (":private" (push doom-private-dir targets)) - (":plugins" - (cl-loop for (_name . desc) in (doom-get-package-alist) - do (package--compile desc)) - (setq compile-plugins-p t - modules (delete ":plugins" modules))) - ((pred file-directory-p) - (push module targets)) - ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) - (push (doom-module-locate-path - (doom-keyword-intern (match-string 1 module)) - (intern (match-string 2 module))) - targets)))) - (cl-block 'byte-compile - ;; If we're just here to byte-compile our plugins, we're done! - (and (not modules) - compile-plugins-p - (cl-return-from 'byte-compile t)) - (unless (or (equal modules '(":core")) - recompile-p) - (unless (or doom-auto-accept + + (let (target-dirs) + (dolist (module (delete-dups modules)) + (pcase module + (":core" + (push (doom-glob doom-emacs-dir "init.el") targets) + (push doom-core-dir target-dirs)) + (":private" + (push doom-private-dir target-dirs)) + ((pred file-directory-p) + (push module target-dirs)) + ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) + (push (doom-module-locate-path + (doom-keyword-intern (match-string 1 module)) + (intern (match-string 2 module))) + target-dirs)) + (_ (user-error "%S is not a valid target" module)))) + + (and (or (null modules) (member ":private" modules)) + (not recompile-p) + (not (or doom-auto-accept (y-or-n-p (concat "Warning: byte compiling is for advanced users. It will interfere with your\n" "efforts to debug issues. It is not recommended you do it if you frequently\n" @@ -82,109 +87,117 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." "Doom core files, as these don't change often.\n\n" "If you have issues, please make sure byte-compilation isn't the cause by using\n" "`bin/doom clean` to clear out your *.elc files.\n\n" - "Byte-compile anyway?"))) - (message "Aborting.") - (cl-return-from 'byte-compile))) - (when (and (not recompile-p) - (or (null modules) - (equal modules '(":core")))) - (doom-clean-byte-compiled-files)) - (let (doom-emacs-changed-p - noninteractive) - ;; But first we must be sure that Doom and your private config have been - ;; fully loaded. Which usually aren't so in an noninteractive session. - (unless (and (doom-initialize-autoloads doom-autoload-file) - (doom-initialize-autoloads doom-package-autoload-file)) - (doom-reload-autoloads)) - (doom-initialize) + "Byte-compile anyway?")))) + (user-error "Aborting")) + + ;; But first we must be sure that Doom and your private config have been + ;; fully loaded. Which usually aren't so in an noninteractive session. + (let (noninteractive) + (doom-initialize 'force) + (doom-initialize-core) (doom-initialize-modules 'force)) - ;; If no targets were supplied, then we use your module list. - (unless modules - (let ((doom-modules-dirs (delete (expand-file-name "modules/" doom-private-dir) - doom-modules-dirs))) - (setq targets - (append (list doom-core-dir) - (delete doom-private-dir (doom-module-load-path)))))) - ;; Assemble el files we want to compile; taking into account that - ;; MODULES may be a list of MODULE/SUBMODULE strings from the command - ;; line. - (let ((target-files (doom-files-in targets :filter #'doom--byte-compile-ignore-file-p :sort nil))) - (when (or (not modules) - (member ":core" modules)) - (push (expand-file-name "init.el" doom-emacs-dir) - target-files)) - (unless target-files - (if targets - (message "Couldn't find any valid targets") - (message "No targets to %scompile" (if recompile-p "re" ""))) - (cl-return-from 'byte-compile)) - (require 'use-package) - (condition-case e - (let ((use-package-defaults use-package-defaults) - (use-package-expand-minimally t) - (load-path load-path) - kill-emacs-hook kill-buffer-query-functions) - ;; Prevent packages from being loaded at compile time if they - ;; don't meet their own predicates. - (push (list :no-require t - (lambda (_name args) - (or (when-let (pred (or (plist-get args :if) - (plist-get args :when))) - (not (eval pred t))) - (when-let (pred (plist-get args :unless)) - (eval pred t))))) - use-package-defaults) - (dolist (target (cl-delete-duplicates (mapcar #'file-truename target-files) :test #'equal)) - (if (or (not recompile-p) - (let ((elc-file (byte-compile-dest-file target))) - (and (file-exists-p elc-file) - (file-newer-than-file-p target elc-file)))) - (let ((result (if (or (string-match-p "/\\(?:packages\\|doctor\\)\\.el$" target) - (not (doom--file-cookie-p target))) - 'no-byte-compile - (byte-compile-file target))) - (short-name (if (file-in-directory-p target doom-emacs-dir) - (file-relative-name target doom-emacs-dir) - (abbreviate-file-name target)))) - (cl-incf - (cond ((eq result 'no-byte-compile) - (print! (dark (white "⚠ Ignored %s")) short-name) - total-noop) - ((null result) - (print! (red "✕ Failed to compile %s") short-name) - total-fail) - (t - (print! (green "✓ Compiled %s") short-name) - (load target t t) - total-ok)))) - (cl-incf total-noop))) - (print! (bold (color (if (= total-fail 0) 'green 'red) - "%s %d/%d file(s) (%d ignored)")) - (if recompile-p "Recompiled" "Compiled") - total-ok (- (length target-files) total-noop) - total-noop) - (or (= total-fail 0) - (error "Failed to compile some files"))) - ((debug error) - (print! (red "\nThere were breaking errors.\n\n%s") - "Reverting changes...") - (signal 'doom-error (list 'byte-compile e)))))))) + + ;; + (unless target-dirs + (push (doom-glob doom-emacs-dir "init.el") targets) + ;; If no targets were supplied, then we use your module list. + (appendq! target-dirs + (list doom-core-dir) + (nreverse + (cl-remove-if-not + (lambda (path) (file-in-directory-p path doom-emacs-dir)) + ;; Omit `doom-private-dir', which is always first + (cdr (doom-module-load-path)))))) + + ;; Assemble el files we want to compile; taking into account that MODULES + ;; may be a list of MODULE/SUBMODULE strings from the command line. + (appendq! targets + (doom-files-in target-dirs + :match "\\.el$" + :filter #'doom--byte-compile-ignore-file-p))) + + (unless targets + (print! + (if targets + (warn "Couldn't find any valid targets") + (info "No targets to %scompile" (if recompile-p "re" "")))) + (cl-return nil)) + + (print! + (info (if recompile-p + "Recompiling stale elc files..." + "Byte-compiling your config (may take a while)..."))) + (print-group! + (require 'use-package) + (condition-case e + (let ((total-ok 0) + (total-fail 0) + (total-noop 0) + (use-package-defaults use-package-defaults) + (use-package-expand-minimally t) + kill-emacs-hook kill-buffer-query-functions) + ;; Prevent packages from being loaded at compile time if they + ;; don't meet their own predicates. + (push (list :no-require t + (lambda (_name args) + (or (when-let (pred (or (plist-get args :if) + (plist-get args :when))) + (not (eval pred t))) + (when-let (pred (plist-get args :unless)) + (eval pred t))))) + use-package-defaults) + + (unless recompile-p + (doom-clean-byte-compiled-files)) + + (dolist (target (delete-dups targets)) + (cl-incf + (if (not (or (not recompile-p) + (let ((elc-file (byte-compile-dest-file target))) + (and (file-exists-p elc-file) + (file-newer-than-file-p target elc-file))))) + total-noop + (pcase (if (doom-file-cookie-p target) + (byte-compile-file target) + 'no-byte-compile) + (`no-byte-compile + (print! (info "Ignored %s") (relpath target)) + total-noop) + (`nil + (print! (error "Failed to compile %s") (relpath target)) + total-fail) + (_ + (print! (success "Compiled %s") (relpath target)) + (load target t t) + total-ok))))) + (print! (class (if (= total-fail 0) 'success 'error) + "%s %d/%d file(s) (%d ignored)") + (if recompile-p "Recompiled" "Compiled") + total-ok (- (length targets) total-noop) + total-noop) + t) + ((debug error) + (print! (error "\nThere were breaking errors.\n\n%s") + "Reverting changes...") + (signal 'doom-error (list 'byte-compile e))))))) (defun doom-clean-byte-compiled-files () "Delete all the compiled elc files in your Emacs configuration and private module. This does not include your byte-compiled, third party packages.'" - (cl-loop with default-directory = doom-emacs-dir - for path - in (append (doom-files-in doom-emacs-dir :match "\\.elc$" :depth 0 :sort nil) - (doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil) - (doom-files-in doom-core-dir :match "\\.elc$" :sort nil) - (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil)) - for truepath = (file-truename path) - if (file-exists-p path) - do (delete-file path) - and do - (print! (green "✓ Deleted %s") - (if (file-in-directory-p truepath default-directory) - (file-relative-name truepath) - (abbreviate-file-name truepath))) - finally do (print! (bold (green "Everything is clean"))))) + (print! (start "Cleaning .elc files")) + (print-group! + (cl-loop with default-directory = doom-emacs-dir + with success = nil + for path + in (append (doom-glob doom-emacs-dir "*.elc") + (doom-files-in doom-private-dir :match "\\.elc$" :depth 1) + (doom-files-in doom-core-dir :match "\\.elc$") + (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4)) + if (file-exists-p path) + do (delete-file path) + and do (print! (success "Deleted %s") (relpath path)) + and do (setq success t) + finally do + (print! (if success + (success "All elc files deleted") + (info "No elc files to clean")))))) diff --git a/core/cli/debug.el b/core/cli/debug.el index a66a52507..278140fdc 100644 --- a/core/cli/debug.el +++ b/core/cli/debug.el @@ -1,7 +1,38 @@ ;;; core/cli/debug.el -*- lexical-binding: t; -*- -(dispatcher! info (doom/info) - "Output system info in markdown for bug reports.") +(load! "autoload/debug" doom-core-dir) -(dispatcher! (version v) (doom/version) - "Reports the version of Doom and Emacs.") + +;; +;;; Commands + +(defcli! info (&optional format) + "Output system info in markdown for bug reports. + +Will print in the following formats: + + --json + --md / --markdown + --lisp + +If no arguments are given, --raw is assumed." + (pcase format + ("--json" + (require 'json) + (with-temp-buffer + (insert (json-encode (doom-info))) + (json-pretty-print-buffer) + (print! (buffer-string)))) + ((or "--md" "--markdown") + (doom/info)) + ((or `nil "--lisp") + (doom/info 'raw)) + (_ + (user-error "I don't understand %S. Did you mean --json, --md/--markdown or --lisp?" + format))) + nil) + +(defcli! (version v) () + "Reports the version of Doom and Emacs." + (doom/version) + nil) diff --git a/core/cli/env.el b/core/cli/env.el index 185ff7e1c..9a1240bda 100644 --- a/core/cli/env.el +++ b/core/cli/env.el @@ -1,44 +1,46 @@ ;;; core/cli/env.el -*- lexical-binding: t; -*- -(dispatcher! env - (let ((env-file (abbreviate-file-name doom-env-file))) - (pcase (car args) - ((or "refresh" "re") - (doom-reload-env-file 'force)) - ((or "enable" "auto") - (setenv "DOOMENV" "1") - (print! (green "Enabling auto-reload of %S") env-file) - (doom-reload-env-file 'force) - (print! (green "Done! `doom refresh' will now refresh your envvar file."))) - ("clear" - (setenv "DOOMENV" nil) - (unless (file-exists-p env-file) - (user-error "%S does not exist to be cleared" env-file)) - (delete-file env-file) - (print! (green "Disabled envvar file by deleting %S") env-file)) - (_ - (print! "%s\n\n%s" - (bold (red "No valid subcommand provided.")) - "See `doom help env` to see available commands.")))) - "Manages your envvars file. +(defcli! env (&rest args) + "Creates or regenerates your envvars file. - env [SUBCOMMAND] + doom env [-c|--clear] -Available subcommands: +This is meant to be a faster and more comprehensive alternative to +exec-path-from-shell. See the FAQ in the documentation for an explanation why. - refresh Create or regenerate your envvar file - auto enable auto-reloading of your envvars file (on `doom refresh`) - clear deletes your envvar file (if it exists) and disables auto-reloading +The envvars file is created by scraping your (interactive) shell environment +into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env' +(or '$SHELL -c set' on windows). Doom loads this file at startup (if it exists) +to ensure Emacs mirrors your shell environment (particularly to ensure PATH and +SHELL are correctly set). -An envvars file (its location is controlled by the `doom-env-file' variable) -will contain a list of environment variables scraped from your shell environment -and loaded when Doom starts (if it exists). This is necessary when Emacs can't -be launched from your shell environment (e.g. on MacOS or certain app launchers -on Linux). +This is useful in cases where you cannot guarantee that Emacs (or the daemon) +will be launched from the correct environment (e.g. on MacOS or through certain +app launchers on Linux). -To generate a file, run `doom env refresh`. If you'd like this file to be -auto-reloaded when running `doom refresh`, run `doom env enable` instead (only -needs to be run once).") +This file is automatically regenerated when you run this command or 'doom +refresh'. However, 'doom refresh' will only regenerate this file if it exists. + +Use the -c or --clear switch to delete your envvar file." + (let ((default-directory doom-emacs-dir)) + (when (member "clear" args) ; DEPRECATED + (message "'doom env clear' is deprecated. Use 'doom env -c' or 'doom env --clear' instead") + (push "-c" args)) + + (cond ((or (member "-c" args) + (member "--clear" args)) + (unless (file-exists-p doom-env-file) + (user-error! "%S does not exist to be cleared" + (relpath doom-env-file))) + (delete-file doom-env-file) + (print! (success "Successfully deleted %S") + (relpath doom-env-file))) + + ((null args) + (doom-reload-env-file 'force)) + + ((user-error "I don't understand 'doom env %s'" + (string-join args " ")))))) ;; @@ -55,7 +57,8 @@ needs to be run once).") ;; Doom envvars "^INSECURE$" "^DEBUG$" - "^YES$") + "^YES$" + "^__") "Environment variables to not save in `doom-env-file'. Each string is a regexp, matched against variable names to omit from @@ -86,50 +89,54 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in `doom-env-ignored-vars' are removed." (when (or force-p (not (file-exists-p doom-env-file))) (with-temp-file doom-env-file - (message "%s envvars file at %S" + (print! (start "%s envvars file at %S") (if (file-exists-p doom-env-file) "Regenerating" "Generating") - (abbreviate-file-name doom-env-file)) - (let ((process-environment doom-site-process-environment)) - (insert - (concat - "# -*- mode: dotenv -*-\n" - (format "# Generated with: %s %s %s\n" - shell-file-name - doom-env-switches - doom-env-executable) - "# ---------------------------------------------------------------------------\n" - "# This file was auto-generated by `doom env refresh'. It contains a list of\n" - "# environment variables scraped from your default shell (excluding variables\n" - "# blacklisted in doom-env-ignored-vars).\n" - "#\n" - "# It is NOT safe to edit this file. Changes will be overwritten next time that\n" - "# `doom env refresh` is executed. Alternatively, create your own env file and\n" - "# load it with `(doom-load-env-vars FILE)`.\n" - "#\n" - "# To auto-regenerate this file when `doom reload` is run, use `doom env auto' or\n" - "# set DOOMENV=1 in your shell environment/config.\n" - "# ---------------------------------------------------------------------------\n\n")) - (let ((shell-command-switch doom-env-switches)) - (message "Scraping env from '%s %s %s'" - shell-file-name - shell-command-switch - doom-env-executable) + (relpath doom-env-file doom-emacs-dir)) + (let ((process-environment doom--initial-process-environment)) + (let ((shell-command-switch doom-env-switches) + (error-buffer (get-buffer-create "*env errors*"))) + (print! (info "Scraping shell environment with '%s %s %s'") + (filename shell-file-name) + shell-command-switch + (filename doom-env-executable)) (save-excursion - (insert (shell-command-to-string doom-env-executable))) - ;; Remove undesireable variables - (while (re-search-forward "\n\\([^= \n]+\\)=" nil t) - (save-excursion - (let* ((valend (or (save-match-data - (when (re-search-forward "^\\([^= ]+\\)=" nil t) - (line-beginning-position))) - (point-max))) - (var (match-string 1)) - (value (buffer-substring-no-properties (point) (1- valend)))) - (when (cl-loop for regexp in doom-env-ignored-vars - if (string-match-p regexp var) - return t) - (message "Ignoring %s" var) - (delete-region (match-beginning 0) (1- valend)))))) - (print! (green "Envvar successfully generated"))))))) + (shell-command doom-env-executable (current-buffer) error-buffer)) + (print-group! + (let ((errors (with-current-buffer error-buffer (buffer-string)))) + (unless (string-empty-p errors) + (print! (info "Error output:\n\n%s") (indent 4 errors)))) + ;; Remove undesireable variables + (insert + (concat + "# -*- mode: dotenv -*-\n" + (format "# Generated with: %s %s %s\n" + shell-file-name + doom-env-switches + doom-env-executable) + "# ---------------------------------------------------------------------------\n" + "# This file was auto-generated by `doom env'. It contains a list of environment\n" + "# variables scraped from your default shell (excluding variables blacklisted\n" + "# in doom-env-ignored-vars).\n" + "#\n" + "# It is NOT safe to edit this file. Changes will be overwritten next time that\n" + "# `doom refresh` is executed. Alternatively, create your own env file and load\n" + "# it with `(doom-load-envvars-file FILE)` in your private config.el.\n" + "# ---------------------------------------------------------------------------\n\n")) + (goto-char (point-min)) + (while (re-search-forward "\n\\([^= \n]+\\)=" nil t) + (save-excursion + (let* ((valend (or (save-match-data + (when (re-search-forward "^\\([^= ]+\\)=" nil t) + (line-beginning-position))) + (point-max))) + (var (match-string 1))) + (when (cl-loop for regexp in doom-env-ignored-vars + if (string-match-p regexp var) + return t) + (print! (info "Ignoring %s") var) + (delete-region (match-beginning 0) (1- valend))))))) + (print! (success "Successfully generated %S") + (relpath doom-env-file doom-emacs-dir)) + t))))) diff --git a/core/cli/install.el b/core/cli/install.el new file mode 100644 index 000000000..4059aa7e5 --- /dev/null +++ b/core/cli/install.el @@ -0,0 +1,104 @@ +;;; core/cli/install.el -*- lexical-binding: t; -*- + +(defcli! quickstart (&rest args) ; DEPRECATED + "This is a deprecated alias for 'doom install'. + +See 'doom help install' instead." + :hidden t + (apply #'doom-cli-install args)) + +(defcli! (install i) (&rest args) + "A wizard for installing Doom for the first time. + +This command does the following: + + 1. Creates DOOMDIR at ~/.doom.d, + 2. Copies ~/.emacs.d/init.example.el to DOOMDIR/init.el (if it doesn't exist), + 3. Creates dummy files for DOOMDIR/{config,packages}.el, + 4. Prompts you to generate an envvar file (via 'doom env refresh'), + 5. Installs any dependencies of enabled modules (specified by DOOMDIR/init.el), + 6. And prompts to install all-the-icons' fonts + +This command is idempotent and safe to reuse. + +The location of DOOMDIR can be changed with the -p option, or by setting the +DOOMDIR environment variable. e.g. + + doom -p ~/.config/doom install + DOOMDIR=~/.config/doom doom install + +install understands the following switches: + + --no-config Don't create DOOMDIR or dummy files therein + --no-install Don't auto-install packages + --no-env Don't generate an envvars file (see `doom help env`) + --no-fonts Don't install (or prompt to install) all-the-icons fonts + -y / --yes Auto-accept any confirmation prompts" + (print! (green "Installing Doom Emacs!\n")) + (let ((default-directory (doom-path "~"))) + ;; Create `doom-private-dir' + (if (member "--no-config" args) + (print! (warn "Not copying private config template, as requested")) + (print! "> Creating %s" (relpath doom-private-dir)) + (make-directory doom-private-dir 'parents) + (print! (success "Created %s") (relpath doom-private-dir)) + + ;; Create init.el, config.el & packages.el + (mapc (lambda (file) + (cl-destructuring-bind (filename . fn) file + (if (file-exists-p! filename doom-private-dir) + (print! (warn "%s already exists, skipping") filename) + (print! (info "Creating %s%s") (relpath doom-private-dir) filename) + (with-temp-file (doom-path doom-private-dir filename) + (funcall fn)) + (print! (success "Done!"))))) + '(("init.el" . + (lambda () + (insert-file-contents (doom-path doom-emacs-dir "init.example.el")))) + ("config.el" . + (lambda () + (insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n" + ";; Place your private configuration here\n" + ((relpath doom-private-dir))))) + ("packages.el" . + (lambda () + (insert! ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n" + ";;; Examples:\n" + ";; (package! some-package)\n" + ";; (package! another-package :recipe (:host github :repo \"username/repo\"))\n" + ";; (package! builtin-package :disable t)\n" + ((relpath doom-private-dir)))))))) + + ;; In case no init.el was present the first time `doom-initialize-modules' was + ;; called in core.el (e.g. on first install) + (doom-initialize-packages 'force-p) + + ;; Ask if Emacs.app should be patched + (if (member "--no-env" args) + (print! (warn "- Not generating envvars file, as requested")) + (when (or doom-auto-accept + (y-or-n-p "Generate an env file? (see `doom help env` for details)")) + (doom-reload-env-file 'force-p))) + + ;; Install Doom packages + (if (member "--no-install" args) + (print! (warn "- Not installing plugins, as requested")) + (print! "Installing plugins") + (doom-packages-install doom-auto-accept)) + + (print! "Regenerating autoloads files") + (doom-reload-autoloads nil 'force-p) + + (if (member "--no-fonts" args) + (print! (warn "- Not installing fonts, as requested")) + (when (or doom-auto-accept + (y-or-n-p "Download and install all-the-icon's fonts?")) + (require 'all-the-icons) + (let ((window-system (cond (IS-MAC 'ns) + (IS-LINUX 'x)))) + (all-the-icons-install-fonts 'yes)))) + + (print! (success "\nFinished! Doom is ready to go!\n")) + (with-temp-buffer + (doom-template-insert "QUICKSTART_INTRO") + (print! (buffer-string))))) diff --git a/core/cli/packages.el b/core/cli/packages.el index 661186a50..6137cf60b 100644 --- a/core/cli/packages.el +++ b/core/cli/packages.el @@ -1,55 +1,62 @@ ;; -*- no-byte-compile: t; -*- ;;; core/cli/packages.el -;; -;;; Helpers - -(defmacro doom--condition-case! (&rest body) - `(condition-case-unless-debug e - (progn ,@body) - ('user-error - (print! (bold (red " NOTICE: %s")) e)) - ('file-error - (print! " %s\n %s" - (bold (red "FILE ERROR: %s" (error-message-string e))) - "Trying again...") - (quiet! (doom-refresh-packages-maybe t)) - ,@body) - ('error - (print! (bold (red " %s %s\n %s")) - "FATAL ERROR: " e - "Run again with the -d flag for details")))) - -(defsubst doom--ensure-autoloads-while (fn) - (doom-reload-doom-autoloads) - (when (funcall fn doom-auto-accept) - (doom-reload-package-autoloads))) +(defmacro doom--ensure-autoloads-while (&rest body) + `(progn + (doom-reload-core-autoloads) + (when (progn ,@body) + (doom-reload-package-autoloads 'force-p)) + t)) ;; ;;; Dispatchers -(dispatcher! (install i) - (doom--ensure-autoloads-while #'doom-packages-install) - "Installs wanted packages that aren't installed. - -Package management in Doom is declarative. A `package!' declaration in an -enabled module or your private packages.el marks a package as 'wanted'.") - -(dispatcher! (update u) - (doom--ensure-autoloads-while #'doom-packages-update) +(defcli! (update u) (&rest args) "Updates packages. +This works by fetching all installed package repos and checking the distance +between HEAD and FETCH_HEAD. This can take a while. + This excludes packages whose `package!' declaration contains a non-nil :freeze -or :ignore property.") +or :ignore property." + (doom--ensure-autoloads-while + (straight-check-all) + (doom-packages-update + doom-auto-accept + (when-let (timeout (cadr (or (member "--timeout" args) + (member "-t" args)))) + (string-to-number timeout))))) -(dispatcher! (autoremove r) - (doom--ensure-autoloads-while #'doom-packages-autoremove) - "Removes packages that are no longer needed. +(defcli! (rebuild build b) (&rest args) + "Rebuilds all installed packages. -This includes packages installed with 'M-x package-install' without an -accompanying `package!' declaration in an enabled module's packages.el file or -your private one.") +This ensures that all needed files are symlinked from their package repo and +their elisp files are byte-compiled." + (doom--ensure-autoloads-while + (doom-packages-rebuild doom-auto-accept (member "-f" args)))) + +(defcli! (purge p) (&rest args) + "Deletes any unused ELPA packages, straight builds, and (optionally) repos. + +By default, this does not purge repos. + +Available options: + +--no-elpa Don't purge ELPA packages +--no-builds Don't purge unneeded (built) packages +--repos Purge unused repos" + (doom--ensure-autoloads-while + (straight-check-all) + (doom-packages-purge (not (member "--no-elpa" args)) + (not (member "--no-builds" args)) + (or (member "-r" args) + (member "--repos" args)) + doom-auto-accept))) + +;; (defcli! rollback () ; TODO rollback +;; "" +;; (user-error "Not implemented yet, sorry!")) ;; @@ -63,153 +70,410 @@ declaration) or dependency thereof that hasn't already been. Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with a list of packages that will be installed." - (print! "Looking for packages to install...") - (let ((packages (doom-get-missing-packages))) - (cond ((not packages) - (print! (green "No packages to install!")) - nil) + (print! "> Installing & building packages...") + (print-group! + (let ((n 0)) + (dolist (package (hash-table-keys straight--recipe-cache)) + (straight--with-plist (gethash package straight--recipe-cache) + (local-repo) + (let ((existed-p (file-directory-p (straight--repos-dir package)))) + (condition-case-unless-debug e + (and (straight-use-package (intern package) nil nil " ") + (not existed-p) + (file-directory-p (straight--repos-dir package)) + (cl-incf n)) + (error + (signal 'doom-package-error + (list e (straight--process-get-output)))))))) + (if (= n 0) + (ignore (print! (success "No packages need to be installed"))) + (print! (success "Installed & built %d packages") n) + t)))) - ((not (or auto-accept-p - (y-or-n-p - (format "%s packages will be installed:\n\n%s\n\nProceed?" - (length packages) - (mapconcat - (lambda (pkg) - (format "+ %s (%s)" - (car pkg) - (cond ((doom-package-different-recipe-p (car pkg)) - "new recipe") - ((doom-package-different-backend-p (car pkg)) - (format "%s -> %s" - (doom-package-backend (car pkg) 'noerror) - (doom-package-recipe-backend (car pkg) 'noerror))) - ((plist-get (cdr pkg) :recipe) - "quelpa") - ("elpa")))) - (cl-sort (cl-copy-list packages) #'string-lessp - :key #'car) - "\n"))))) - (user-error "Aborted!")) - ((let (success) - (doom-refresh-packages-maybe doom-debug-mode) - (dolist (pkg packages) - (print! "Installing %s" (car pkg)) - (doom--condition-case! - (let ((result - (or (and (doom-package-installed-p (car pkg)) - (not (doom-package-different-backend-p (car pkg))) - (not (doom-package-different-recipe-p (car pkg))) - 'already-installed) - (and (doom-install-package (car pkg) (cdr pkg)) - (setq success t) - 'success) - 'failure)) - (pin-label - (and (plist-member (cdr pkg) :pin) - (format " [pinned: %s]" (plist-get (cdr pkg) :pin))))) - (print! "%s%s" - (pcase result - (`already-installed (dark (white "⚠ ALREADY INSTALLED"))) - (`success (green "✓ DONE")) - (`failure (red "✕ FAILED"))) - (or pin-label ""))))) - (print! (bold (green "Finished!"))) - (when success - (set-file-times doom-packages-dir) - (doom-delete-autoloads-file doom-package-autoload-file)) - success))))) +(defun doom-packages-rebuild (&optional auto-accept-p all) + "(Re)build all packages." + (print! (start "(Re)building %spackages...") (if all "all " "")) + (print-group! + (let ((n 0)) + (if all + (let ((straight--packages-to-rebuild :all) + (straight--packages-not-to-rebuild (make-hash-table :test #'equal))) + (dolist (package (hash-table-keys straight--recipe-cache)) + (straight-use-package + (intern package) nil (lambda (_) (cl-incf n) nil) " "))) + (dolist (recipe (hash-table-values straight--recipe-cache)) + (straight--with-plist recipe (package local-repo no-build) + (unless (or no-build (null local-repo)) + ;; REVIEW We do these modification checks manually because + ;; Straight's checks seem to miss stale elc files. Need + ;; more tests to confirm this. + (when (or (ignore-errors + (gethash package straight--packages-to-rebuild)) + (gethash package straight--cached-package-modifications) + (not (file-directory-p (straight--build-dir package))) + (cl-loop for file + in (doom-files-in (straight--build-dir package) + :match "\\.el$" + :full t) + for elc-file = (byte-compile-dest-file file) + if (and (file-exists-p elc-file) + (file-newer-than-file-p file elc-file)) + return t)) + (let ((straight-use-package-pre-build-functions + straight-use-package-pre-build-functions)) + (add-hook 'straight-use-package-pre-build-functions + (lambda (&rest _) (cl-incf n))) + (let ((straight--packages-to-rebuild :all) + (straight--packages-not-to-rebuild (make-hash-table :test #'equal))) + (straight-use-package (intern package) nil nil " ")) + (straight--byte-compile-package recipe) + (dolist (dep (straight--get-dependencies package)) + (when-let (recipe (gethash dep straight--recipe-cache)) + (straight--byte-compile-package recipe))))))))) + (if (= n 0) + (ignore (print! (success "No packages need rebuilding"))) + (doom--finalize-straight) + (print! (success "Rebuilt %d package(s)" n)) + t)))) -(defun doom-packages-update (&optional auto-accept-p) + +(defun doom--packages-remove-outdated-f (packages) + (async-start + `(lambda () + (setq load-path ',load-path + doom-modules ',doom-modules + user-emacs-directory ',user-emacs-directory) + (condition-case e + (let (packages errors) + (load ,(concat doom-core-dir "core.el")) + (doom-initialize 'force-p) + (dolist (recipe ',group) + (when (straight--repository-is-available-p recipe) + (straight-vc-git--destructure recipe + (package local-repo nonrecursive upstream-remote upstream-repo upstream-host + branch remote) + (condition-case e + (let ((default-directory (straight--repos-dir local-repo))) + ;; HACK We normalize packages to avoid certain scenarios + ;; where `straight-fetch-package' will create an + ;; interactive popup prompting for action (which will + ;; cause this async process to block indefinitely). We + ;; can't use `straight-normalize-package' because could + ;; create popup prompts too, so we do it manually: + (shell-command-to-string "git merge --abort") + (straight--get-call "git" "reset" "--hard" (format "%s/%s" remote branch)) + (straight--get-call "git" "clean" "-ffd") + (unless nonrecursive + (shell-command-to-string "git submodule update --init --recursive")) + (when upstream-repo + (let ((desired-url (straight-vc-git--encode-url upstream-repo upstream-host)) + (actual-url (condition-case nil + (straight--get-call "git" "remote" "get-url" upstream-remote) + (error nil)))) + (unless (straight-vc-git--urls-compatible-p actual-url desired-url) + (straight--get-call "git" "remote" "remove" upstream-remote) + (straight--get-call "git" "remote" "add" upstream-remote desired-url) + (straight--get-call "git" "fetch" upstream-remote)))) + (straight-fetch-package package) + ;; REVIEW Is there no better way to get this information? + (let ((n (length + (split-string + (straight--get-call "git" "rev-list" "--left-right" "HEAD..@{u}") + "\n" t))) + (pretime + (string-to-number + (shell-command-to-string "git log -1 --format=%at HEAD"))) + (time + (string-to-number + ;; HACK `straight--get-call' has a higher failure + ;; rate when querying FETCH_HEAD; not sure why. + ;; Doing this manually, with + ;; `shell-command-to-string' works fine. + (shell-command-to-string "git log -1 --format=%at FETCH_HEAD")))) + (with-current-buffer (straight--process-get-buffer) + (with-silent-modifications + (print! (debug (autofill "%s") (indent 2 (buffer-string)))) + (erase-buffer))) + (when (> n 0) + (push (list n pretime time recipe) + packages)))) + (error + (push (list package e (string-trim (or (straight--process-get-output) ""))) + errors)))))) + (if errors + (cons 'error errors) + (cons 'ok (nreverse packages)))) + (error + (cons 'error e)))))) + + +(defun doom-packages-update (&optional auto-accept-p timeout) "Updates packages. Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with a list of packages that will be updated." - (print! "Looking for outdated packages...") - (let ((packages (cl-sort (cl-copy-list (doom-get-outdated-packages)) #'string-lessp - :key #'car))) - (cond ((not packages) - (print! (green "Everything is up-to-date")) - nil) + (print! (start "Scanning for outdated packages (this may take a while)...")) + (print-group! + (when timeout + (print! (info "Using %S as timeout value" timeout))) + ;; REVIEW Does this fail gracefully enough? Is it error tolerant? + ;; TODO Add version-lock checks; don't want to spend all this effort on + ;; packages that shouldn't be updated + (let* ((futures + (or (cl-loop for group + in (seq-partition (hash-table-values straight--repo-cache) + (/ (hash-table-count straight--repo-cache) + 16)) + for future = (doom--packages-remove-outdated-f group) + if (processp future) + collect (cons future group) + else + do (print! (warn "Failed to create thread for:\n\n%s\n\nReason: %s" + group future))) + (error! "Failed to create any threads"))) + (total (length futures)) + (timeout (or timeout 45))) + (condition-case-unless-debug e + (let (specs) + (while futures + (print! ". %.0f%%" (* (/ (- total (length futures)) + (float total)) + 100)) + (let ((time 0)) + (catch 'timeout + (while (not (async-ready (caar futures))) + (when (> time timeout) + (print! (warn "A thread has timed out. The following packages were skipped: %s" + (mapconcat (lambda (p) (plist-get p :package)) + (cdar futures) + ", "))) + (throw 'timeout (pop futures))) + (sleep-for 1) + (when (cl-evenp time) + (print! ".")) + (cl-incf time)) + (cl-destructuring-bind (status . result) + (or (async-get (car (pop futures))) + (cons nil nil)) + (cond ((null status) + (error "Thread returned an invalid result: %S" errors)) + ((eq status 'error) + (error "There were errors:\n\n%s" + (cond ((and (listp result) + (symbolp (car result))) + (prin1-to-string result)) + ((stringp result) + result) + ((mapconcat (lambda (e) + (format! " - %s: %s" (yellow (car e)) (cdr e))) + result + "\n"))))) + ((eq status 'ok) + (print! (debug "Appended %S to package list") (or result "nothing")) + (appendq! specs result)) + ((error "Thread returned a non-standard status: %s\n\n%s" + status result))))))) + (print! ". 100%%") + (terpri) + (if-let (specs (delq nil specs)) + (if (not + (or auto-accept-p + (y-or-n-p + (format! + "%s\n\nThere %s %d package%s available to update. Update them?" + (mapconcat + (lambda (spec) + (cl-destructuring-bind (n pretime time recipe) spec + (straight--with-plist recipe (package) + (format! "+ %-33s %s commit(s) behind %s -> %s" + (yellow package) (yellow n) + (format-time-string "%Y%m%d" pretime) + (format-time-string "%Y%m%d" time))))) + specs + "\n") + (if (cdr specs) "are" "is") + (length specs) + (if (cdr specs) "s" ""))))) + (ignore (print! (info "Aborted update"))) + (terpri) + (straight--make-package-modifications-available) + (let ((straight--packages-to-rebuild (make-hash-table :test #'equal)) + (straight--packages-not-to-rebuild (make-hash-table :test #'equal))) + (dolist (spec specs) + (cl-destructuring-bind (n pretime time recipe) spec + (straight--with-plist recipe (local-repo package) + (let ((default-directory (straight--repos-dir local-repo))) + (print! (start "Updating %S") package) + (straight-merge-package package) + ;; HACK `straight-rebuild-package' doesn't pick up that + ;; this package has changed, so we do it manually. Is + ;; there a better way? + (ignore-errors + (delete-directory (straight--build-dir package) 'recursive)) + (puthash package t straight--packages-to-rebuild) + (cl-incf n)) + (with-current-buffer (straight--process-get-buffer) + (with-silent-modifications + (print! (debug (autofill "%s") (indent 2 (buffer-string)))) + (erase-buffer)))))) + (doom--finalize-straight) + (doom-packages-rebuild auto-accept-p)) + t) + (print! (success "No packages to update")) + nil)) + (error + (message "Output:\n%s" (straight--process-get-output)) + (signal (car e) (error-message-string e))))))) - ((not (or auto-accept-p - (y-or-n-p - (format "%s packages will be updated:\n\n%s\n\nProceed?" - (length packages) - (let ((max-len - (or (car (sort (mapcar (lambda (it) (length (symbol-name (car it)))) packages) - #'>)) - 10))) - (mapconcat - (lambda (pkg) - (format (format "+ %%-%ds (%%s) %%-%ds -> %%s" - (+ max-len 2) 14) - (symbol-name (car pkg)) - (doom-package-backend (car pkg)) - (package-version-join (cadr pkg)) - (package-version-join (cl-caddr pkg)))) - packages - "\n")))))) - (user-error "Aborted!")) - ((let (success) - (dolist (pkg packages) - (print! "Updating %s" (car pkg)) - (doom--condition-case! - (print! - (let ((result (doom-update-package (car pkg) t))) - (when result (setq success t)) - (color (if result 'green 'red) - (if result "✓ DONE" "✕ FAILED")))))) - (print! (bold (green "Finished!"))) - (when success - (set-file-times doom-packages-dir) - (doom-delete-autoloads-file doom-package-autoload-file)) - success))))) +;;; PURGE (for the emperor) +(defun doom--prompt-p (list-fn list preamble postamble) + (or (y-or-n-p (format "%s%s\n\n%s" + (if preamble (concat preamble "\n\n") "") + (mapconcat list-fn list "\n") + (or postamble ""))) + (user-error! "Aborted"))) -(defun doom-packages-autoremove (&optional auto-accept-p) - "Auto-removes orphaned packages. +(defun doom--prompt-columns-p (row-fn list preamble postamble) + (doom--prompt-p (lambda (row) + (mapconcat row-fn row "")) + (seq-partition (cl-sort (copy-sequence list) #'string-lessp) + 3) + preamble + postamble)) + +(defun doom--packages-purge-build (build) + (let ((build-dir (straight--build-dir build))) + (print! (start "Purging build/%s..." build)) + (delete-directory build-dir 'recursive) + (if (file-directory-p build-dir) + (ignore (print! (error "Failed to purg build/%s" build))) + (print! (success "Purged build/%s" build)) + t))) + +(defun doom--packages-purge-builds (builds &optional auto-accept-p) + (if (not builds) + (progn (print! (info "No builds to purge")) + 0) + (or auto-accept-p + (doom--prompt-columns-p + (lambda (p) (format " + %-20.20s" p)) builds nil + (format! "Found %d orphaned package builds. Purge them?" + (length builds)))) + (length + (delq nil (mapcar #'doom--packages-purge-build builds))))) + +(defun doom--packages-regraft-repo (repo) + (let ((default-directory (straight--repos-dir repo))) + (if (not (file-directory-p ".git")) + (ignore (print! (warn "repos/%s is not a git repo, skipping" repo))) + (print! (debug "Regrafting repos/%s..." repo)) + (straight--call "git" "reset" "--hard") + (straight--call "git" "clean" "--ffd") + (straight--call "git" "replace" "--graft" "HEAD") + (straight--call "git" "gc") + (print! (debug "%s" (straight--process-get-output))) + (print! (success "Regrafted repos/%s" repo)) + t))) + +(defun doom--packages-regraft-repos (repos &optional auto-accept-p) + (if (not repos) + (progn (print! (info "No repos to regraft")) + 0) + (or auto-accept-p + (y-or-n-p (format! "Preparing to regraft all %d repos. Continue?" + (length repos))) + (user-error! "Aborted!")) + (if (executable-find "du") + (cl-destructuring-bind (status . size) + (doom-sh "du" "-sh" (straight--repos-dir)) + (prog1 (delq nil (mapcar #'doom--packages-regraft-repo repos)) + (cl-destructuring-bind (status . newsize) + (doom-sh "du" "-sh" (straight--repos-dir)) + (print! (success "Finshed regrafted. Size before: %s and after: %s" + (car (split-string size "\t")) + (car (split-string newsize "\t"))))))) + (delq nil (mapcar #'doom--packages-regraft-repo repos))))) + +(defun doom--packages-purge-repo (repo) + (print! (debug "Purging repos/%s..." repo)) + (let ((repo-dir (straight--repos-dir repo))) + (delete-directory repo-dir 'recursive) + (ignore-errors + (delete-file (straight--modified-file repo))) + (if (file-directory-p repo-dir) + (ignore (print! (error "Failed to purge repos/%s" repo))) + (print! (success "Purged repos/%s" repo)) + t))) + +(defun doom--packages-purge-repos (repos &optional auto-accept-p) + (if (not repos) + (progn (print! (info "No repos to purge")) + 0) + (or auto-accept-p + (doom--prompt-columns-p + (lambda (p) (format " + %-20.20s" p)) repos nil + (format! "Found %d orphaned repos. Purge them?" + (length repos)))) + (length + (delq nil (mapcar #'doom--packages-purge-repo repos))))) + +(defun doom--packages-purge-elpa (&optional auto-accept-p) + (unless (bound-and-true-p package--initialized) + (package-initialize)) + (if (not package-alist) + (progn (print! (info "No ELPA packages to purge")) + 0) + (doom--prompt-columns-p + (lambda (p) (format " + %-20.20s" p)) + (mapcar #'car package-alist) nil + (format! "Found %d orphaned ELPA packages. Purge them?" + (length package-alist))) + (mapc (doom-rpartial #'delete-directory 'recursive) + (mapcar #'package-desc-dir + (mapcar #'cadr package-alist))) + (length package-alist))) + +(defun doom-packages-purge (&optional elpa-p builds-p repos-p auto-accept-p) + "Auto-removes orphaned packages and repos. An orphaned package is a package that isn't a primary package (i.e. doesn't have a `package!' declaration) or isn't depended on by another primary package. +If BUILDS-P, include straight package builds. +If REPOS-P, include straight repos. +If ELPA-P, include packages installed with package.el (M-x package-install). + Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with a list of packages that will be removed." - (print! "Looking for orphaned packages...") - (let ((packages (doom-get-orphaned-packages))) - (cond ((not packages) - (print! (green "No unused packages to remove")) - nil) - - ((not - (or auto-accept-p - (y-or-n-p - (format "%s packages will be deleted:\n\n%s\n\nProceed?" - (length packages) - (mapconcat - (lambda (sym) - (let ((old-backend (doom-package-backend sym 'noerror)) - (new-backend (doom-package-recipe-backend sym 'noerror))) - (format "+ %s (%s)" sym - (cond ((null new-backend) - "removed") - ((eq old-backend new-backend) - (symbol-name new-backend)) - ((format "%s -> %s" old-backend new-backend)))))) - (sort (cl-copy-list packages) #'string-lessp) - "\n"))))) - (user-error "Aborted!")) - - ((let (success) - (dolist (pkg packages) - (doom--condition-case! - (let ((result (doom-delete-package pkg t))) - (if result (setq success t)) - (print! (color (if result 'green 'red) "%s %s") - (if result "✓ Removed" "✕ Failed to remove") - pkg)))) - (print! (bold (green "Finished!"))) - (when success - (set-file-times doom-packages-dir) - (doom-delete-autoloads-file doom-package-autoload-file)) - success))))) + (print! (start "Searching for orphaned packages to purge (for the emperor)...")) + (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft) + (let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort)) + (bdirs (straight--directory-files (straight--build-dir) nil nil 'sort))) + (list (cl-remove-if (doom-rpartial #'gethash straight--profile-cache) + bdirs) + (cl-remove-if (doom-rpartial #'straight--checkhash straight--repo-cache) + rdirs) + (cl-remove-if-not (doom-rpartial #'straight--checkhash straight--repo-cache) + rdirs))) + (let (success) + (print-group! + (if (not builds-p) + (print! (info "Skipping builds")) + (and (/= 0 (doom--packages-purge-builds builds-to-purge auto-accept-p)) + (setq success t) + (straight-prune-build-cache))) + (if (not elpa-p) + (print! (info "Skipping elpa packages")) + (and (/= 0 (doom--packages-purge-elpa auto-accept-p)) + (setq success t))) + (if (not repos-p) + (print! (info "Skipping repos")) + (and (/= 0 (doom--packages-purge-repos repos-to-purge auto-accept-p)) + (setq success t)) + (and (doom--packages-regraft-repos repos-to-regraft auto-accept-p) + (setq success t))) + (when success + (doom--finalize-straight) + t))))) diff --git a/core/cli/patch-macos.el b/core/cli/patch-macos.el index 91fbb5a79..4e1816398 100644 --- a/core/cli/patch-macos.el +++ b/core/cli/patch-macos.el @@ -1,9 +1,6 @@ ;;; core/cli/patch-macos.el -*- lexical-binding: t; -*- -(dispatcher! (patch-macos) - (doom-patch-macos (or (member "--undo" args) - (member "-u" args)) - (doom--find-emacsapp-path)) +(defcli! patch-macos () ; DEPRECATED "Patches Emacs.app to respect your shell environment. WARNING: This command is deprecated. Use 'doom env' instead. @@ -30,7 +27,11 @@ It can be undone with the --undo or -u options. Alternatively, you can install the exec-path-from-shell Emacs plugin, which will scrape your shell environment remotely, at startup. However, this can be slow -depending on your shell configuration and isn't always reliable.") +depending on your shell configuration and isn't always reliable." + :hidden t + (doom-patch-macos (or (member "--undo" args) + (member "-u" args)) + (doom--find-emacsapp-path))) ;; @@ -65,24 +66,7 @@ depending on your shell configuration and isn't always reliable.") (message "%s successfully unpatched" appdir)) ((file-exists-p newbin) - (user-error "%s is already patched" appdir)) + (user-error "%s is already patched. Use 'doom patch-macos --undo' to unpatch it" + appdir)) - ((or doom-auto-accept - (y-or-n-p - (concat "(WARNING: patch-macos is deprecated, use `doom env refresh` instead)\n\n" - "Doom would like to patch your Emacs.app bundle so that it respects\n" - "your shell configuration. For more information on why and how, run\n\n" - " bin/doom help patch-macos\n\n" - "Patch Emacs.app?"))) - (message "Patching '%s'" appdir) - (copy-file oldbin newbin nil nil nil 'preserve-permissions) - (unless (file-exists-p newbin) - (error "Failed to copy %s to %s" oldbin newbin)) - (with-temp-buffer - (insert "#!/usr/bin/env bash\n" - "args=\"$@\"\n" - "pwd=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\"; pwd -P)\"\n" - "exec \"$SHELL\" -l -c \"$pwd/RunEmacs $args\"") - (write-file oldbin) - (chmod oldbin (file-modes newbin))) - (message "%s successfully patched" appdir))))) + ((user-error "patch-macos has been disabled. Please use 'doom env refresh' instead"))))) diff --git a/core/cli/quickstart.el b/core/cli/quickstart.el deleted file mode 100644 index 1e061653a..000000000 --- a/core/cli/quickstart.el +++ /dev/null @@ -1,105 +0,0 @@ -;;; core/cli/quickstart.el -*- lexical-binding: t; -*- - -(dispatcher! (quickstart qs) (apply #'doom-quickstart args) - "Guides you through setting up Doom for first time use. - -This command does the following: - - 1. Creates DOOMDIR at ~/.doom.d, - 2. Copies ~/.emacs.d/init.example.el to DOOMDIR/init.el (if it doesn't exist), - 3. Creates dummy files for DOOMDIR/{config,packages}.el, - 4. Prompts you to generate an envvar file (via 'doom env refresh'), - 5. Installs any dependencies of enabled modules (specified by DOOMDIR/init.el), - 6. And prompts to install all-the-icons' fonts - -This command is idempotent and safe to reuse. - -The location of DOOMDIR can be changed with the -p option, or by setting the -DOOMDIR environment variable. e.g. - - doom -p ~/.config/doom quickstart - DOOMDIR=~/.config/doom doom quickstart - -Quickstart understands the following switches: - - --no-config Don't create DOOMDIR or dummy files therein - --no-install Don't auto-install packages - --no-env Don't generate an envvars file (see `doom help env`) - --no-fonts Don't install (or prompt to install) all-the-icons fonts") - - -;; -;; Library - -(defun doom-quickstart (&rest args) - "Quickly deploy a private module and setup Doom. - -This deploys a barebones config to `doom-private-dir', installs all missing -packages, prompts to install all-the-icons fonts, generates an env file and -regenerates the autoloads file." - ;; Create `doom-private-dir' - (let ((short-private-dir (abbreviate-file-name doom-private-dir))) - (if (member "--no-config" args) - (print! (yellow "Not copying private config template, as requested")) - (print! "Creating %s" short-private-dir) - (make-directory doom-private-dir t) - (print! (green "Done!")) - - ;; Create init.el, config.el & packages.el - (dolist (file (list (cons "init.el" - (lambda () - (insert-file-contents (expand-file-name "init.example.el" doom-emacs-dir)))) - (cons "config.el" - (lambda () - (insert (format ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n" - short-private-dir) - ";; Place your private configuration here\n"))) - (cons "packages.el" - (lambda () - (insert (format ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n" - short-private-dir) - ";;; Examples:\n" - ";; (package! some-package)\n" - ";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n" - ";; (package! builtin-package :disable t)\n"))))) - (cl-destructuring-bind (path . fn) file - (if (file-exists-p! path doom-private-dir) - (print! "%s already exists, skipping" path) - (print! "Creating %s%s" short-private-dir path) - (with-temp-file (expand-file-name path doom-private-dir) - (funcall fn)) - (print! (green "Done!"))))))) - - ;; In case no init.el was present the first time `doom-initialize-modules' was - ;; called in core.el (e.g. on first install) - (doom-initialize-packages 'force-p) - - ;; Ask if Emacs.app should be patched - (if (member "--no-env" args) - (print! (yellow "Not generating envvars file, as requested")) - (when (or doom-auto-accept - (y-or-n-p "Generate an env file? (see `doom help env` for details)")) - (doom-reload-env-file 'force-p))) - - ;; Install Doom packages - (if (member "--no-install" args) - (print! (yellow "Not installing plugins, as requested")) - (print! "Installing plugins") - (doom-packages-install doom-auto-accept)) - - (print! "Regenerating autoloads files") - (doom-reload-autoloads nil 'force-p) - - (if (member "--no-fonts" args) - (print! (yellow "Not installing fonts, as requested")) - (when (or doom-auto-accept - (y-or-n-p "Download and install all-the-icon's fonts?")) - (require 'all-the-icons) - (let ((window-system (cond (IS-MAC 'ns) - (IS-LINUX 'x)))) - (all-the-icons-install-fonts 'yes)))) - - (print! (bold (green "\nFinished! Doom is ready to go!\n"))) - (with-temp-buffer - (doom-template-insert "QUICKSTART_INTRO") - (print! (buffer-string)))) diff --git a/core/cli/test.el b/core/cli/test.el index f67953fbc..003705222 100644 --- a/core/cli/test.el +++ b/core/cli/test.el @@ -1,69 +1,66 @@ ;;; core/cli/test.el -*- lexical-binding: t; -*- -(dispatcher! test (doom-run-tests args) - "Run Doom unit tests.") - - -;; -;; Library - -(defun doom-run-tests (&optional modules) - "Run all loaded tests, specified by MODULES (a list of module cons cells) or -command line args following a double dash (each arg should be in the -'module/submodule' format). - -If neither is available, run all tests in all enabled modules." - ;; Core libraries aren't fully loaded in a noninteractive session, so we - ;; reload it with `noninteractive' set to nil to force them to. - (let* ((noninteractive t) - (doom-modules (doom-modules))) - (quiet! (doom-reload-autoloads)) - (let ((target-paths - ;; Convert targets into a list of string paths, pointing to the root - ;; directory of modules - (cond ((stringp (car modules)) ; command line - (save-match-data - (cl-loop for arg in modules - if (string= arg ":core") collect doom-core-dir - else if (string-match-p "/" arg) - nconc (mapcar (apply-partially #'expand-file-name arg) - doom-modules-dirs) - else - nconc (cl-loop for dir in doom-modules-dirs - for path = (expand-file-name arg dir) - if (file-directory-p path) - nconc (doom-files-in path :type 'dirs :depth 1 :full t :sort nil)) - finally do (setq argv nil)))) - - (modules ; cons-cells given to MODULES - (cl-loop for (module . submodule) in modules - if (doom-module-locate-path module submodule) - collect it)) - - ((append (list doom-core-dir) - (doom-module-load-path)))))) - ;; Load all the unit test files... - (require 'buttercup) - (mapc (lambda (file) (load file :noerror (not doom-debug-mode))) - (doom-files-in (mapcar (apply-partially #'expand-file-name "test/") - target-paths) - :match "\\.el$" :full t)) - ;; ... then run them - (when doom-debug-mode - (setq buttercup-stack-frame-style 'pretty)) - (let ((split-width-threshold 0) - (split-height-threshold 0) - (window-min-width 0) - (window-min-height 0)) - (buttercup-run))))) - - -;; -;; Test library - -(defmacro insert! (&rest text) - "Insert TEXT in buffer, then move cursor to last {0} marker." - `(progn - (insert ,@text) - (when (search-backward "{0}" nil t) - (replace-match "" t t)))) +(defcli! test (&rest targets) + "Run Doom unit tests." + (let (files error) + (unless targets + (setq targets + (cons doom-core-dir + (cl-remove-if-not + (lambda (path) (file-in-directory-p path doom-emacs-dir)) + ;; Omit `doom-private-dir', which is always first + (let (doom-modules) + (load! "test/init" doom-core-dir) + (cdr (doom-module-load-path))))))) + (while targets + (let ((target (pop targets))) + (cond ((equal target ":core") + (appendq! files (nreverse (doom-glob doom-core-dir "test/test-*.el")))) + ((file-directory-p target) + (setq target (expand-file-name target)) + (appendq! files (nreverse (doom-glob target "test/test-*.el")))) + ((file-exists-p target) + (push target files))))) + (require 'restart-emacs) + (with-temp-buffer + (setenv "DOOMDIR" (concat doom-core-dir "test/")) + (setenv "DOOMLOCALDIR" (concat doom-local-dir "test/")) + (print! (start "Bootstrapping test environment, if necessary...")) + (if (zerop + (call-process + (restart-emacs--get-emacs-binary) + nil t nil "--batch" + "-l" (concat doom-core-dir "core.el") + "--eval" (prin1-to-string + `(progn (doom-initialize 'force) + (doom-initialize-modules) + (require 'core-cli) + (unless (package-installed-p 'buttercup) + (package-refresh-contents) + (package-install 'buttercup)) + (doom-reload-core-autoloads 'force) + (when (doom-packages-install 'auto-accept) + (doom-reload-package-autoloads 'force)))))) + (message "%s" (buffer-string)) + (message "%s" (buffer-string)) + (error "Failed to bootstrap unit tests"))) + (dolist (file files) + (if (doom-file-cookie-p file) + (with-temp-buffer + (unless + (zerop + (call-process + (restart-emacs--get-emacs-binary) + nil t nil "--batch" + "-l" (concat doom-core-dir "core.el") + "-l" (concat doom-core-dir "test/helpers.el") + "--eval" (prin1-to-string `(doom-initialize 'force)) + "-l" "buttercup" + "-l" file + "-f" "buttercup-run")) + (setq error t)) + (message "%s" (buffer-string))) + (print! (info "Ignoring %s" (relpath file))))) + (if error + (error "A test failed") + t))) diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el index b7664ceb5..d6e2573dc 100644 --- a/core/cli/upgrade.el +++ b/core/cli/upgrade.el @@ -1,86 +1,110 @@ ;;; core/cli/upgrade.el -*- lexical-binding: t; -*- -(dispatcher! (upgrade up) (doom-upgrade) - "Checks out the latest Doom on this branch. +(defcli! (upgrade up) (&rest args) + "Updates Doom and packages. -Doing so is equivalent to: +This requires that ~/.emacs.d is a git repo, and is the equivalent of the +following shell commands: cd ~/.emacs.d - git pull + git pull --rebase bin/doom clean bin/doom refresh - bin/doom update") + bin/doom update" + (and (doom-upgrade (or (member "-f" args) + (member "--force" args))) + (doom-packages-update + doom-auto-accept + (when-let (timeout (cadr (or (member "--timeout" args) + (member "-t" args)))) + (string-to-number timeout))) + + (doom-reload-package-autoloads 'force-p))) ;; -;; Quality of Life Commands +;;; Library (defvar doom-repo-url "https://github.com/hlissner/doom-emacs" - "TODO") + "The git repo url for Doom Emacs.") (defvar doom-repo-remote "_upgrade" - "TODO") + "The name to use as our staging remote.") (defun doom--working-tree-dirty-p (dir) - (with-temp-buffer - (let ((default-directory dir)) - (if (zerop (process-file "git" nil (current-buffer) nil - "status" "--porcelain" "-uno")) - (string-match-p "[^ \t\n]" (buffer-string)) - (error "Failed to check working tree in %s" dir))))) + (cl-destructuring-bind (success . stdout) + (doom-sh "git" "status" "--porcelain" "-uno") + (if (= 0 success) + (string-match-p "[^ \t\n]" (buffer-string)) + (error "Failed to check working tree in %s" dir)))) -(defun doom-upgrade () + +(defun doom-upgrade (&optional force-p) "Upgrade Doom to the latest version non-destructively." (require 'vc-git) - (let* ((gitdir (expand-file-name ".git" doom-emacs-dir)) - (branch (vc-git--symbolic-ref doom-emacs-dir)) - (default-directory doom-emacs-dir)) - (unless (file-exists-p gitdir) - (error "Couldn't find %s. Was Doom cloned properly?" - (abbreviate-file-name gitdir))) - (unless branch - (error "Couldn't detect what branch you're using. Is Doom detached?")) - (when (doom--working-tree-dirty-p doom-emacs-dir) - (user-error "Refusing to upgrade because %S has been modified. Stash or undo your changes" - (abbreviate-file-name doom-emacs-dir))) - (with-temp-buffer - (let ((buf (current-buffer))) - (condition-case-unless-debug e - (progn - (process-file "git" nil buf nil "remote" "remove" doom-repo-remote) - (unless (zerop (process-file "git" nil buf nil "remote" "add" - doom-repo-remote doom-repo-url)) + (let ((default-directory doom-emacs-dir) + process-file-side-effects) + (print! (start "Preparing to upgrade Doom Emacs and its packages...")) + + (let* ((branch (vc-git--symbolic-ref doom-emacs-dir)) + (target-remote (format "%s/%s" doom-repo-remote branch))) + (unless branch + (error! (if (file-exists-p! ".git" doom-emacs-dir) + "Couldn't find Doom's .git directory. Was Doom cloned properly?" + "Couldn't detect what branch you're on. Is Doom detached?"))) + + ;; We assume that a dirty .emacs.d is intentional and abort + (when (doom--working-tree-dirty-p default-directory) + (if (not force-p) + (user-error! "%s\n\n%s" + (format "Refusing to upgrade because %S has been modified." (path doom-emacs-dir)) + "Either stash/undo your changes or run 'doom upgrade -f' to discard local changes.") + (print! (info "You have local modifications in Doom's source. Discarding them...")) + (doom-sh "git" "reset" "--hard" (format "origin/%s" branch)) + (doom-sh "git" "clean" "-ffd"))) + + (doom-sh "git" "remote" "remove" doom-repo-remote) + (unwind-protect + (progn + (or (zerop (car (doom-sh "git" "remote" "add" doom-repo-remote doom-repo-url))) (error "Failed to add %s to remotes" doom-repo-remote)) - (unless (zerop (process-file "git" nil buf nil "fetch" "--tags" - doom-repo-remote branch)) + (or (zerop (car (doom-sh "git" "fetch" "--tags" doom-repo-remote branch))) (error "Failed to fetch from upstream")) - (let ((current-rev (vc-git-working-revision doom-emacs-dir)) - (rev (string-trim (shell-command-to-string (format "git rev-parse %s/%s" doom-repo-remote branch))))) - (unless rev - (error "Couldn't detect Doom's version. Is %s a repo?" - (abbreviate-file-name doom-emacs-dir))) - (if (equal current-rev rev) - (message "Doom is up to date!") - (message "Updates for Doom are available!\n\n Old revision: %s\n New revision: %s\n" - current-rev rev) - (message "Comparision diff: https://github.com/hlissner/doom-emacs/compare/%s...%s\n" - (substring current-rev 0 10) (substring rev 0 10)) - ;; TODO Display newsletter diff - (unless (or doom-auto-accept (y-or-n-p "Proceed?")) - (user-error "Aborted")) - (message "Removing byte-compiled files from your config (if any)") - (doom-clean-byte-compiled-files) - (unless (zerop (process-file "git" nil buf nil "reset" "--hard" - (format "%s/%s" doom-repo-remote branch))) - (error "An error occurred while checking out the latest commit\n\n%s" - (buffer-string))) - (unless (equal (vc-git-working-revision doom-emacs-dir) rev) - (error "Failed to checkout latest commit.\n\n%s" (buffer-string)))) - (doom-refresh 'force-p) - (when (doom-packages-update doom-auto-accept) - (doom-reload-package-autoloads)) - (message "Done! Please restart Emacs for changes to take effect"))) - (user-error - (message "%s Aborting." (error-message-string e))) - (error - (message "There was an unexpected error.\n\n%s\n\nOutput:\n%s" - e (buffer-string)))))))) + + (let ((this-rev (vc-git--rev-parse "HEAD")) + (new-rev (vc-git--rev-parse target-remote))) + (cond + ((and (null this-rev) + (null new-rev)) + (error "Failed to get revisions for %s" target-remote)) + + ((equal this-rev new-rev) + (print! (success "Doom is already up-to-date!")) + t) + + ((print! (info "A new version of Doom Emacs is available!\n\n Old revision: %s (%s)\n New revision: %s (%s)\n" + (substring this-rev 0 10) + (cdr (doom-sh "git" "log" "-1" "--format=%cr" "HEAD")) + (substring new-rev 0 10) + (cdr (doom-sh "git" "log" "-1" "--format=%cr" target-remote)))) + + (when (y-or-n-p "View the comparison diff in your browser?") + (print! (info "Opened github in your browser.")) + (browse-url (format "https://github.com/hlissner/doom-emacs/compare/%s...%s" + this-rev + new-rev))) + (if (not (y-or-n-p "Proceed with upgrade?")) + (ignore (print! (error "Aborted"))) + (print! (start "Upgrading Doom Emacs...")) + (print-group! + (doom-clean-byte-compiled-files) + (unless (and (zerop (car (doom-sh "git" "reset" "--hard" target-remote))) + (equal (vc-git--rev-parse "HEAD") new-rev)) + (error "Failed to check out %s" (substring new-rev 0 10))) + (print! (success "Finished upgrading Doom Emacs"))) + (doom-delete-autoloads-file doom-autoload-file) + (doom-cli-refresh "-f") + t) + + (print! (success "Done! Restart Emacs for changes to take effect.")))))) + (ignore-errors + (doom-sh "git" "remote" "remove" doom-repo-remote)))))) diff --git a/core/core-cli.el b/core/core-cli.el index 7ca20ba45..f4410de96 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -1,54 +1,98 @@ ;;; -*- lexical-binding: t; no-byte-compile: t; -*- -;; Eagerly load these libraries because this module may be loaded in a session -;; that hasn't been fully initialized (where autoloads files haven't been -;; generated or `load-path' populated). -(load! "autoload/debug") -(load! "autoload/files") -(load! "autoload/message") -(load! "autoload/packages") +(require 'seq) -;; -;; Dispatcher API - (defvar doom-auto-accept (getenv "YES") "If non-nil, Doom will auto-accept any confirmation prompts during batch commands like `doom-packages-install', `doom-packages-update' and `doom-packages-autoremove'.") -(defconst doom--dispatch-command-alist ()) -(defconst doom--dispatch-alias-alist ()) +(defvar doom-cli-pre-execute-hook nil + "TODO") +(defvar doom-cli-post-success-execute-hook nil + "TODO") + +(defvar doom--cli-commands (make-hash-table :test 'equal)) +(defvar doom--cli-groups (make-hash-table :test 'equal)) +(defvar doom--cli-group nil) + + +;; +;;; Dispatcher API + +(defun doom-file-cookie-p (file) + (with-temp-buffer + (insert-file-contents-literally file nil 0 256) + (if (and (re-search-forward "^;;;###if " nil t) + (<= (line-number-at-pos) 3)) + (let ((load-file-name file)) + (eval (sexp-at-point) t)) + t))) + +(defun doom-sh (command &rest args) + "Execute COMMAND with ARGS in the shell and return (STATUS . OUTPUT). + +STATUS is a boolean" + (let ((output (get-buffer-create "*doom-sh-output*"))) + (unwind-protect + (cons (or (apply #'call-process command nil output nil args) + -1) + (with-current-buffer output + (string-trim (buffer-string)))) + (kill-buffer output)))) + +(defun doom--dispatch-command (command) + (when (symbolp command) + (setq command (symbol-name command))) + (cl-check-type command string) + (intern-soft + (format "doom-cli-%s" + (if (gethash command doom--cli-commands) + command + (cl-loop for key + being the hash-keys in doom--cli-commands + for aliases = (plist-get (gethash key doom--cli-commands) :aliases) + if (member command aliases) + return key))))) (defun doom--dispatch-format (desc &optional short) (with-temp-buffer (let ((fill-column 72)) - (insert desc) - (goto-char (point-min)) - (while (re-search-forward "\n\n[^ \n]" nil t) - (fill-paragraph))) + (save-excursion + (insert desc) + (while (re-search-backward "\n\n[^ \n]" nil t) + (fill-paragraph)))) (if (not short) (buffer-string) - (goto-char (point-min)) - (buffer-substring-no-properties - (line-beginning-position) - (line-end-position))))) + (buffer-substring (line-beginning-position) + (line-end-position))))) -(defun doom--dispatch-help (&optional command desc &rest args) - "Display help documentation for a dispatcher command. If COMMAND and DESC are +(defun doom--dispatch-help-1 (command) + (cl-destructuring-bind (&key aliases hidden _group) + (gethash command doom--cli-commands) + (unless hidden + (print! "%-11s\t%s\t%s" + command (if aliases (string-join aliases ",") "") + (doom--dispatch-format + (documentation (doom--dispatch-command command)) + t))))) + +(defun doom--dispatch-help (&optional fn &rest args) + "Display help documentation for a dispatcher command. If fn and DESC are omitted, show all available commands, their aliases and brief descriptions." - (if command - (princ (doom--dispatch-format desc)) - (print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description")) - (dolist (spec (cl-sort doom--dispatch-command-alist #'string-lessp - :key #'car)) - (cl-destructuring-bind (command &key desc _body) spec - (let ((aliases (cl-loop for (alias . cmd) in doom--dispatch-alias-alist - if (eq cmd command) - collect (symbol-name alias)))) - (print! " %-10s\t%s\t%s" - command (if aliases (string-join aliases ",") "") - (doom--dispatch-format desc t))))))) + (if fn + (princ (documentation fn)) + (print! (bold "%-11s\t%s\t%s" "Command:" "Alias" "Description")) + (print-group! + (dolist (group (seq-group-by (lambda (key) (plist-get (gethash key doom--cli-commands) :group)) + (hash-table-keys doom--cli-commands))) + (if (null (car group)) + (mapc #'doom--dispatch-help-1 (cdr group)) + (print! "%-30s\t%s" (bold (car group)) (gethash (car group) doom--cli-groups)) + (print-group! + (mapc #'doom--dispatch-help-1 (cdr group)))) + (terpri))))) (defun doom-dispatch (cmd args &optional show-help) "Parses ARGS and invokes a dispatcher. @@ -59,17 +103,33 @@ If SHOW-HELP is non-nil, show the documentation for said dispatcher." (when args (setq cmd (car args) args (cdr args)))) - (cl-destructuring-bind (command &key desc body) - (let ((sym (intern cmd))) - (or (assq sym doom--dispatch-command-alist) - (assq (cdr (assq sym doom--dispatch-alias-alist)) - doom--dispatch-command-alist) - (user-error "Invalid command: %s" sym))) + (let ((fn (doom--dispatch-command cmd))) + (unless (fboundp fn) + (user-error "%S is not any command *I* know!" cmd)) (if show-help - (apply #'doom--dispatch-help command desc args) - (funcall body args)))) + (doom--dispatch-help fn args) + (let ((start-time (current-time))) + (run-hooks 'doom-cli-pre-execute-hook) + (unwind-protect + (when-let (ret (apply fn args)) + (print! + "\n%s" + (success "Finished! (%.4fs)" + (float-time + (time-subtract (current-time) + start-time)))) + (run-hooks 'doom-cli-post-execute-hook) + ret) + (run-hooks 'doom-cli-post-error-execute-hook)))))) -(defmacro dispatcher! (command form &optional docstring) +(defmacro defcligroup! (name docstring &rest body) + "TODO" + (declare (indent defun) (doc-string 2)) + `(let ((doom--cli-group ,name)) + (puthash doom--cli-group ,docstring doom--cli-groups) + ,@body)) + +(defmacro defcli! (names arglist docstring &rest body) "Define a dispatcher command. COMMAND is a symbol or a list of symbols representing the aliases for this command. DESC is a string description. The first line should be short (under 60 letters), as it will be displayed for @@ -77,79 +137,30 @@ bin/doom help. BODY will be run when this dispatcher is called." (declare (indent defun) (doc-string 3)) - (cl-destructuring-bind (cmd &rest aliases) - (doom-enlist command) + (let* ((names (mapcar #'symbol-name (doom-enlist names))) + (fn (intern (format "doom-cli-%s" (car names)))) + (plist (cl-loop while (keywordp (car body)) + collect (pop body) + collect (pop body)))) (macroexp-progn - (append - (when aliases - `((dolist (alias ',aliases) - (setf (alist-get alias doom--dispatch-alias-alist) ',cmd)))) - `((setf (alist-get ',cmd doom--dispatch-command-alist) - (list :desc ,docstring - :body (lambda (args) (ignore args) ,form)))))))) + (reverse + `((let ((plist ',plist)) + (setq plist (plist-put plist :aliases ',(cdr names))) + (unless (or (plist-member plist :group) + (null doom--cli-group)) + (plist-put plist :group doom--cli-group)) + (puthash ,(car names) plist doom--cli-commands)) + (defun ,fn ,arglist + ,docstring + ,@body)))))) ;; -;; Dummy dispatch commands +;;; Dispatch commands -;; These are handled by bin/doom, except we still want 'help CMD' to print out -;; documentation for them, so... - -(dispatcher! run :noop - "Run Doom Emacs from bin/doom's parent directory. - -All arguments are passed on to Emacs (except for -p and -e). - - doom run - doom run -nw init.el - -WARNING: this command exists for convenience and testing. Doom will suffer -additional overhead by being started this way. For the best performance, it is -best to run Doom out of ~/.emacs.d and ~/.doom.d.") - -(dispatcher! (doctor doc) :noop - "Checks for issues with your environment & Doom config. - -Use the doctor to diagnose common problems or list missing dependencies in -enabled modules.") - -(dispatcher! (help h) :noop - "Look up additional information about a command.") - - -;; -;; Real dispatch commands - -(load! "cli/autoloads") -(load! "cli/byte-compile") -(load! "cli/debug") -(load! "cli/env") -(load! "cli/packages") -(load! "cli/patch-macos") -(load! "cli/quickstart") -(load! "cli/upgrade") -(load! "cli/test") - - -;; -(defun doom-refresh (&optional force-p) - "Ensure Doom is in a working state by checking autoloads and packages, and -recompiling any changed compiled files. This is the shotgun solution to most -problems with doom." - (when (getenv "DOOMENV") - (doom-reload-env-file 'force)) - (doom-reload-doom-autoloads force-p) - (unwind-protect - (progn - (ignore-errors - (doom-packages-autoremove doom-auto-accept)) - (ignore-errors - (doom-packages-install doom-auto-accept))) - (doom-reload-package-autoloads force-p) - (doom-byte-compile nil 'recompile))) - -(dispatcher! (refresh re) (doom-refresh 'force) - "Refresh Doom. +;; Load all of our subcommands +(defcli! (refresh re) (&rest args) + "Ensure Doom is properly set up. This is the equivalent of running autoremove, install, autoloads, then recompile. Run this whenever you: @@ -161,7 +172,70 @@ recompile. Run this whenever you: It will ensure that unneeded packages are removed, all needed packages are installed, autoloads files are up-to-date and no byte-compiled files have gone -stale.") +stale." + (print! (green "Initiating a refresh of Doom Emacs...\n")) + (let ((force-p (or (member "-f" args) + (member "--force" args))) + success) + (when (file-exists-p doom-env-file) + (doom-reload-env-file 'force)) + (doom-reload-core-autoloads force-p) + (unwind-protect + (progn + (and (doom-packages-install doom-auto-accept) + (setq success t)) + (and (doom-packages-rebuild doom-auto-accept) + (setq success t)) + (and (doom-packages-purge 'elpa-p 'builds-p nil doom-auto-accept) + (setq success t))) + (doom-reload-package-autoloads (or success force-p)) + (doom-byte-compile nil 'recompile)) + t)) + + +;; Load all of our subcommands +(load! "cli/install") + +(defcligroup! "Diagnostics" + "For troubleshooting and diagnostics" + (defcli! (doctor doc) () + "Checks for issues with your environment & Doom config. + +Use the doctor to diagnose common problems or list missing dependencies in +enabled modules.") + + (load! "cli/debug") + (load! "cli/test")) + +(defcligroup! "Maintenance" + "For managing your config and packages" + (load! "cli/env") + (load! "cli/upgrade") + (load! "cli/packages") + (load! "cli/autoloads") + (load! "cli/patch-macos")) + +(defcligroup! "Byte compilation" + "For byte-compiling Doom and your config" + (load! "cli/byte-compile")) + +(defcligroup! "Utilities" + "Conveniences for interacting with Doom externally" + (defcli! run () + "Run Doom Emacs from bin/doom's parent directory. + +All arguments are passed on to Emacs (except for -p and -e). + + doom run + doom run -nw init.el + +WARNING: this command exists for convenience and testing. Doom will suffer +additional overhead by being started this way. For the best performance, it is +best to run Doom out of ~/.emacs.d and ~/.doom.d.") + + ;; (load! "cli/batch") + ;; (load! "cli/org") + ) (provide 'core-cli) ;;; core-cli.el ends here diff --git a/core/core-editor.el b/core/core-editor.el index f47276edd..c131c0f7b 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -1,66 +1,89 @@ ;;; core-editor.el -*- lexical-binding: t; -*- -(defvar doom-large-file-size 2 - "Size (in MB) above which the user will be prompted to open the file literally -to avoid performance issues. Opening literally means that no major or minor -modes are active and the buffer is read-only.") - -(defvar doom-large-file-modes-list - '(fundamental-mode special-mode archive-mode tar-mode jka-compr - git-commit-mode image-mode doc-view-mode doc-view-mode-maybe - ebrowse-tree-mode pdf-view-mode tags-table-mode) - "Major modes that `doom|check-large-file' will ignore.") +(defvar doom-detect-indentation-excluded-modes '(fundamental-mode) + "A list of major modes in which indentation should be automatically +detected.") (defvar-local doom-inhibit-indent-detection nil "A buffer-local flag that indicates whether `dtrt-indent' should try to detect indentation settings or not. This should be set by editorconfig if it successfully sets indent_style/indent_size.") -(defvar doom-detect-indentation-excluded-modes '(fundamental-mode) - "A list of major modes in which indentation should be automatically -detected.") -(setq-default - large-file-warning-threshold 15000000 - vc-follow-symlinks t - ;; Save clipboard contents into kill-ring before replacing them - save-interprogram-paste-before-kill t - ;; Bookmarks - bookmark-default-file (concat doom-etc-dir "bookmarks") - bookmark-save-flag t - ;; Formatting - delete-trailing-lines nil - fill-column 80 - sentence-end-double-space nil - word-wrap t - ;; Scrolling - hscroll-margin 2 - hscroll-step 1 - scroll-conservatively 1001 - scroll-margin 0 - scroll-preserve-screen-position t - mouse-wheel-scroll-amount '(5 ((shift) . 2)) - mouse-wheel-progressive-speed nil ; don't accelerate scrolling - ;; Whitespace (see `editorconfig') - indent-tabs-mode nil - require-final-newline t - tab-always-indent t - tab-width 4 - tabify-regexp "^\t* [ \t]+" ; for :retab - ;; Wrapping - truncate-lines t - truncate-partial-width-windows 50) +;; +;;; File handling -;; Remove hscroll-margin in shells, otherwise it causes jumpiness -(setq-hook! '(eshell-mode-hook term-mode-hook) hscroll-margin 0) +;; Resolve symlinks when opening files, so that any operations are conducted +;; from the file's true directory (like `find-file'). +(setq find-file-visit-truename t) -(defun doom*optimize-literal-mode-for-large-files (buffer) - (with-current-buffer buffer - (when find-file-literally - (setq buffer-read-only t) - (buffer-disable-undo)) - buffer)) -(advice-add #'find-file-noselect-1 :filter-return #'doom*optimize-literal-mode-for-large-files) +;; Disable the warning "X and Y are the same file". It's fine to ignore this +;; warning as it will redirect you to the existing buffer anyway. +(setq find-file-suppress-same-file-warnings t) + +;; Create missing directories when we open a file that doesn't exist under a +;; directory tree that may not exist. +(add-hook! 'find-file-not-found-functions + (defun doom-create-missing-directories-h () + "Automatically create missing directories when creating new files." + (let ((parent-directory (file-name-directory buffer-file-name))) + (when (and (not (file-exists-p parent-directory)) + (y-or-n-p (format "Directory `%s' does not exist! Create it?" parent-directory))) + (make-directory parent-directory t))))) + +;; Don't autosave files or create lock/history/backup files. The +;; editor doesn't need to hold our hands so much. We'll rely on git +;; and our own good fortune instead. Fingers crossed! +(setq auto-save-default nil + create-lockfiles nil + make-backup-files nil + ;; But have a place to store them in case we do use them... + auto-save-list-file-name (concat doom-cache-dir "autosave") + backup-directory-alist `(("." . ,(concat doom-cache-dir "backup/")))) + + +;; +;;; Formatting + +;; Indentation +(setq-default tab-width 4 + tab-always-indent t + indent-tabs-mode nil + fill-column 80) + +;; Word wrapping +(setq-default word-wrap t + truncate-lines t + truncate-partial-width-windows nil) + +(setq sentence-end-double-space nil + delete-trailing-lines nil + require-final-newline t + tabify-regexp "^\t* [ \t]+") ; for :retab + + +;; +;;; Clipboard / kill-ring + + ;; Eliminate duplicates in the kill ring. That is, if you kill the + ;; same thing twice, you won't have to use M-y twice to get past it + ;; to older entries in the kill ring. +(setq kill-do-not-save-duplicates t) + +;; +(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) + +;; Save clipboard contents into kill-ring before replacing them +(setq save-interprogram-paste-before-kill t) + +;; Fix the clipboard in terminal or daemon Emacs (non-GUI) +(add-hook! 'tty-setup-hook + (defun doom-init-clipboard-in-tty-emacs-h () + (unless (getenv "SSH_CONNECTION") + (cond (IS-MAC + (if (require 'osx-clipboard nil t) (osx-clipboard-mode))) + ((executable-find "xclip") + (if (require 'xclip nil t) (xclip-mode))))))) ;; @@ -72,12 +95,12 @@ detected.") ;; ;;; Built-in plugins -(def-package! autorevert +(use-package! autorevert ;; revert buffers when their files/state have changed - :hook (focus-in . doom|auto-revert-buffers) - :hook (after-save . doom|auto-revert-buffers) - :hook (doom-switch-buffer . doom|auto-revert-buffer) - :hook (doom-switch-window . doom|auto-revert-buffer) + :hook (focus-in . doom-auto-revert-buffers-h) + :hook (after-save . doom-auto-revert-buffers-h) + :hook (doom-switch-buffer . doom-auto-revert-buffer-h) + :hook (doom-switch-window . doom-auto-revert-buffer-h) :config (setq auto-revert-verbose t ; let us know when it happens auto-revert-use-notify nil @@ -90,21 +113,27 @@ detected.") ;; grind Emacs to a halt if you do expensive IO (outside of Emacs) on the ;; files you have open (like compression). We only really need revert changes ;; when we switch to a buffer or when we focus the Emacs frame. - (defun doom|auto-revert-buffers () + (defun doom-auto-revert-buffer-h () + "Auto revert current buffer, if necessary." + (unless auto-revert-mode + (let ((revert-without-query t)) + (auto-revert-handler)))) + + (defun doom-auto-revert-buffers-h () "Auto revert's stale buffers (that are visible)." (unless auto-revert-mode (dolist (buf (doom-visible-buffers)) (with-current-buffer buf - (auto-revert-handler))))) + (doom-auto-revert-buffer-h)))))) - (defun doom|auto-revert-buffer () - "Auto revert current buffer, if necessary." - (unless auto-revert-mode - (auto-revert-handler)))) -(def-package! recentf +(after! bookmark + (setq bookmark-save-flag t)) + + +(use-package! recentf ;; Keep track of recently opened files - :defer-incrementally (easymenu tree-widget timer) + :defer-incrementally easymenu tree-widget timer :after-call after-find-file :commands recentf-open-files :config @@ -126,27 +155,27 @@ detected.") file)) (setq recentf-filename-handlers '(doom--recent-file-truename abbreviate-file-name)) - (defun doom|recentf-touch-buffer () - "Bump file in recent file list when it is switched or written to." - (when buffer-file-name - (recentf-add-file buffer-file-name)) - ;; Return nil for `write-file-functions' - nil) - (add-hook 'doom-switch-window-hook #'doom|recentf-touch-buffer) - (add-hook 'write-file-functions #'doom|recentf-touch-buffer) + (add-hook! '(doom-switch-window-hook write-file-functions) + (defun doom--recentf-touch-buffer-h () + "Bump file in recent file list when it is switched or written to." + (when buffer-file-name + (recentf-add-file buffer-file-name)) + ;; Return nil for `write-file-functions' + nil)) - (defun doom|recentf-add-dired-directory () - "Add dired directory to recentf file list." - (recentf-add-file default-directory)) - (add-hook 'dired-mode-hook #'doom|recentf-add-dired-directory) + (add-hook! 'dired-mode-hook + (defun doom--recentf-add-dired-directory-h () + "Add dired directory to recentf file list." + (recentf-add-file default-directory))) (unless noninteractive (add-hook 'kill-emacs-hook #'recentf-cleanup) (quiet! (recentf-mode +1)))) -(def-package! savehist + +(use-package! savehist ;; persist variables across sessions - :defer-incrementally (custom) + :defer-incrementally custom :after-call post-command-hook :config (setq savehist-file (concat doom-cache-dir "savehist") @@ -155,32 +184,32 @@ detected.") savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) (savehist-mode +1) - (defun doom|unpropertize-kill-ring () - "Remove text properties from `kill-ring' in the interest of shrinking the -savehist file." - (setq kill-ring (cl-loop for item in kill-ring - if (stringp item) - collect (substring-no-properties item) - else if item collect it))) - (add-hook 'kill-emacs-hook #'doom|unpropertize-kill-ring)) + (add-hook! 'kill-emacs-hook + (defun doom-unpropertize-kill-ring-h () + "Remove text properties from `kill-ring' for a smaller savehist file." + (setq kill-ring (cl-loop for item in kill-ring + if (stringp item) + collect (substring-no-properties item) + else if item collect it))))) -(def-package! saveplace + +(use-package! saveplace ;; persistent point location in buffers - :after-call (after-find-file dired-initial-position-hook) + :after-call after-find-file dired-initial-position-hook :config (setq save-place-file (concat doom-cache-dir "saveplace") save-place-forget-unreadable-files t save-place-limit 200) - (defun doom*recenter-on-load-saveplace (&rest _) + (defadvice! doom--recenter-on-load-saveplace-a (&rest _) "Recenter on cursor when loading a saved place." + :after-while #'save-place-find-file-hook (if buffer-file-name (ignore-errors (recenter)))) - (advice-add #'save-place-find-file-hook - :after-while #'doom*recenter-on-load-saveplace) (save-place-mode +1)) -(def-package! server + +(use-package! server :when (display-graphic-p) - :after-call (pre-command-hook after-find-file focus-out-hook) + :after-call pre-command-hook after-find-file focus-out-hook :init (when-let (name (getenv "EMACS_SERVER_NAME")) (setq server-name name)) @@ -192,8 +221,8 @@ savehist file." ;; ;;; Packages -(def-package! better-jumper - :after-call (pre-command-hook) +(use-package! better-jumper + :after-call pre-command-hook :init (global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward) (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) @@ -202,14 +231,14 @@ savehist file." (better-jumper-mode +1) (add-hook 'better-jumper-post-jump-hook #'recenter) - (defun doom*set-jump (orig-fn &rest args) + (defadvice! doom-set-jump-a (orig-fn &rest args) "Set a jump point and ensure ORIG-FN doesn't set any new jump points." (better-jumper-set-jump (if (markerp (car args)) (car args))) (let ((evil--jumps-jumping t) (better-jumper--jumping t)) (apply orig-fn args))) - (defun doom*set-jump-maybe (orig-fn &rest args) + (defadvice! doom-set-jump-maybe-a (orig-fn &rest args) "Set a jump point if ORIG-FN returns non-nil." (let ((origin (point-marker)) (result @@ -224,13 +253,13 @@ savehist file." origin)))) result)) - (defun doom|set-jump () + (defun doom-set-jump-h () "Run `better-jumper-set-jump' but return nil, for short-circuiting hooks." (better-jumper-set-jump) nil)) -(def-package! command-log-mode +(use-package! command-log-mode :commands global-command-log-mode :config (setq command-log-mode-auto-show t @@ -239,21 +268,20 @@ savehist file." command-log-mode-window-size 50)) -(def-package! dtrt-indent +(use-package! dtrt-indent ;; Automatic detection of indent settings :unless noninteractive :defer t :init - (defun doom|detect-indentation () - (unless (or (not after-init-time) - doom-inhibit-indent-detection - (member (substring (buffer-name) 0 1) '(" " "*")) - (memq major-mode doom-detect-indentation-excluded-modes)) - ;; Don't display messages in the echo area, but still log them - (let ((inhibit-message (not doom-debug-mode))) - (dtrt-indent-mode +1)))) (add-hook! '(change-major-mode-after-body-hook read-only-mode-hook) - #'doom|detect-indentation) + (defun doom-detect-indentation-h () + (unless (or (not after-init-time) + doom-inhibit-indent-detection + (member (substring (buffer-name) 0 1) '(" " "*")) + (memq major-mode doom-detect-indentation-excluded-modes)) + ;; Don't display messages in the echo area, but still log them + (let ((inhibit-message (not doom-debug-mode))) + (dtrt-indent-mode +1))))) :config (setq dtrt-indent-run-after-smie t) @@ -261,9 +289,10 @@ savehist file." (push '(t tab-width) dtrt-indent-hook-generic-mapping-list) (defvar dtrt-indent-run-after-smie) - (defun doom*fix-broken-smie-modes (orig-fn arg) + (defadvice! doom--fix-broken-smie-modes-a (orig-fn arg) "Some smie modes throw errors when trying to guess their indentation, like `nim-mode'. This prevents them from leaving Emacs in a broken state." + :around #'dtrt-indent-mode (let ((dtrt-indent-run-after-smie dtrt-indent-run-after-smie)) (cl-letf* ((old-smie-config-guess (symbol-function 'smie-config-guess)) ((symbol-function 'smie-config-guess) @@ -273,11 +302,10 @@ savehist file." (message "[WARNING] Indent detection: %s" (error-message-string e)) (message "")))))) ; warn silently - (funcall orig-fn arg)))) - (advice-add #'dtrt-indent-mode :around #'doom*fix-broken-smie-modes)) + (funcall orig-fn arg))))) -(def-package! helpful +(use-package! helpful ;; a better *help* buffer :commands helpful--read-symbol :init @@ -288,6 +316,12 @@ savehist file." [remap describe-key] #'helpful-key [remap describe-symbol] #'doom/describe-symbol) + (defun doom-use-helpful-a (orig-fn &rest args) + "Force ORIG-FN to use helpful instead of the old describe-* commands." + (cl-letf (((symbol-function #'describe-function) #'helpful-function) + ((symbol-function #'describe-variable) #'helpful-variable)) + (apply orig-fn args))) + (after! apropos ;; patch apropos buttons to call helpful instead of help (dolist (fun-bt '(apropos-function apropos-macro apropos-command)) @@ -306,44 +340,69 @@ savehist file." (add-hook 'imenu-after-jump-hook #'recenter) -(def-package! smartparens +(use-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-switch-buffer-hook after-find-file) - :commands (sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string) + :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 + ;; Load default smartparens rules for various languages (require 'smartparens-config) + + ;; Overlays are too distracting and not terribly helpful. show-parens does + ;; this for us already, so... (setq sp-highlight-pair-overlay nil sp-highlight-wrap-overlay nil - sp-highlight-wrap-tag-overlay nil - sp-show-pair-from-inside t - sp-cancel-autoskip-on-backward-movement nil - sp-show-pair-delay 0.1 - sp-max-pair-length 4 - sp-max-prefix-length 50 - sp-escape-quotes-after-insert nil) ; not smart enough + sp-highlight-wrap-tag-overlay nil) + ;; But if someone does want overlays enabled, evil users will be stricken with + ;; an off-by-one issue where smartparens assumes you're outside the pair when + ;; you're really at the last character in insert mode. We must correct this + ;; vile injustice. + (setq sp-show-pair-from-inside t) + ;; ...and stay highlighted until we've truly escaped the pair! + (setq sp-cancel-autoskip-on-backward-movement nil) + ;; The default is 100, because smartparen's scans are relatively expensive + ;; (especially with large pair lists for somoe modes), we halve it, as a + ;; better compromise between performance and accuracy. + (setq sp-max-prefix-length 50) + ;; This speeds up smartparens. No pair has any business being longer than 4 + ;; characters; if they must, the modes that need it set it buffer-locally. + (setq sp-max-pair-length 4) + ;; This isn't always smart enough to determine when we're in a string or not. + ;; See https://github.com/Fuco1/smartparens/issues/783. + (setq sp-escape-quotes-after-insert nil) - ;; autopairing in `eval-expression' and `evil-ex' - (defun doom|init-smartparens-in-eval-expression () - "Enable `smartparens-mode' in the minibuffer, during `eval-expression' or + ;; Silence some harmless but annoying echo-area spam + (dolist (key '(:unmatched-expression :no-matching-tag)) + (setf (cdr (assq key sp-message-alist)) nil)) + + (add-hook! 'minibuffer-setup-hook + (defun doom-init-smartparens-in-minibuffer-maybe-h () + "Enable `smartparens-mode' in the minibuffer, during `eval-expression' or `evil-ex'." - (when (memq this-command '(eval-expression evil-ex)) - (smartparens-mode))) - (add-hook 'minibuffer-setup-hook #'doom|init-smartparens-in-eval-expression) + (when (memq this-command '(eval-expression evil-ex)) + (smartparens-mode)))) + ;; You're likely writing lisp in the minibuffer, therefore, disable these + ;; quote pairs, which lisps doesn't use for strings: (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil) (sp-local-pair 'minibuffer-inactive-mode "`" nil :actions nil) - ;; smartparens breaks evil-mode's replace state + ;; Smartparens breaks evil-mode's replace state (add-hook 'evil-replace-state-entry-hook #'turn-off-smartparens-mode) (add-hook 'evil-replace-state-exit-hook #'turn-on-smartparens-mode) (smartparens-global-mode +1)) -(def-package! undo-tree +(use-package! so-long + :after-call after-find-file + :config (global-so-long-mode +1)) + + +(use-package! undo-tree ;; Branching & persistent undo - :after-call (doom-switch-buffer-hook after-find-file) + :after-call doom-switch-buffer-hook after-find-file :config (setq undo-tree-auto-save-history nil ; disable because unstable ;; undo-in-region is known to cause undo history corruption, which can @@ -354,28 +413,26 @@ savehist file." `(("." . ,(concat doom-cache-dir "undo-tree-hist/")))) (when (executable-find "zstd") - (defun doom*undo-tree-make-history-save-file-name (file) - (concat file ".zst")) - (advice-add #'undo-tree-make-history-save-file-name :filter-return - #'doom*undo-tree-make-history-save-file-name)) + (defadvice! doom--undo-tree-make-history-save-file-name-a (file) + :filter-return #'undo-tree-make-history-save-file-name + (concat file ".zst"))) - (defun doom*strip-text-properties-from-undo-history (&rest _) + (defadvice! doom--undo-tree-strip-text-properties-a (&rest _) + :before #'undo-list-transfer-to-tree (dolist (item buffer-undo-list) (and (consp item) (stringp (car item)) (setcar item (substring-no-properties (car item)))))) - (advice-add #'undo-list-transfer-to-tree :before #'doom*strip-text-properties-from-undo-history) (global-undo-tree-mode +1)) -(def-package! ws-butler +(use-package! ws-butler ;; a less intrusive `delete-trailing-whitespaces' on save - :after-call (after-find-file) + :after-call after-find-file :config - (setq ws-butler-global-exempt-modes - (append ws-butler-global-exempt-modes - '(special-mode comint-mode term-mode eshell-mode))) + (appendq! ws-butler-global-exempt-modes + '(special-mode comint-mode term-mode eshell-mode)) (ws-butler-global-mode)) (provide 'core-editor) diff --git a/core/core-keybinds.el b/core/core-keybinds.el index 60d71fa7f..507e7276e 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -6,26 +6,18 @@ ;; entirely for performance reasons). (defvar doom-leader-key "SPC" - "The leader prefix key for Evil users. - -This needs to be changed from $DOOMDIR/init.el.") + "The leader prefix key for Evil users.") (defvar doom-leader-alt-key "M-SPC" "An alternative leader prefix key, used for Insert and Emacs states, and for -non-evil users. - -This needs to be changed from $DOOMDIR/init.el.") +non-evil users.") (defvar doom-localleader-key "SPC m" - "The localleader prefix key, for major-mode specific commands. - -This needs to be changed from $DOOMDIR/init.el.") + "The localleader prefix key, for major-mode specific commands.") (defvar doom-localleader-alt-key "M-SPC m" "The localleader prefix key, for major-mode specific commands. Used for Insert -and Emacs states, and for non-evil users. - -This needs to be changed from $DOOMDIR/init.el.") +and Emacs states, and for non-evil users.") (defvar doom-leader-map (make-sparse-keymap) "An overriding keymap for keys.") @@ -73,13 +65,14 @@ If any hook returns non-nil, all hooks after it are ignored.") ;; ;;; General + leader/localleader keys -(require 'general) -;; Convenience aliases -(defalias 'define-key! #'general-def) -(defalias 'unmap! #'general-unbind) +(use-package general + :init + ;; Convenience aliases + (defalias 'define-key! #'general-def) + (defalias 'unmap! #'general-unbind)) -;; `map!' uses this instead of `define-leader-key!' because it consumes 20-30% -;; more startup time, so we reimplement it ourselves. +;; HACK `map!' uses this instead of `define-leader-key!' because it consumes +;; 20-30% more startup time, so we reimplement it ourselves. (defmacro doom--define-leader-key (&rest keys) (let (prefix forms wkforms) (while keys @@ -99,19 +92,16 @@ If any hook returns non-nil, all hooks after it are ignored.") ,bdef) forms)) (when-let (desc (cadr (memq :which-key udef))) - (push `(which-key-add-key-based-replacements - (general--concat t doom-leader-alt-key ,key) - ,desc) - wkforms) - (push `(which-key-add-key-based-replacements - (general--concat t doom-leader-key ,key) - ,desc) - wkforms)))))) + (prependq! + wkforms `((which-key-add-key-based-replacements + (general--concat t doom-leader-alt-key ,key) + ,desc) + (which-key-add-key-based-replacements + (general--concat t doom-leader-key ,key) + ,desc)))))))) (macroexp-progn - (append (nreverse forms) - (when wkforms - `((after! which-key - ,@(nreverse wkforms)))))))) + (cons `(after! which-key ,@(nreverse wkforms)) + (nreverse forms))))) (defmacro define-leader-key! (&rest args) "Define keys. @@ -157,26 +147,26 @@ localleader prefix." ;; Bind `doom-leader-key' and `doom-leader-alt-key' as late as possible to give ;; the user a chance to modify them. -(defun doom|init-leader-keys () - "Bind `doom-leader-key' and `doom-leader-alt-key'." - (let ((map general-override-mode-map)) - (if (not (featurep 'evil)) - (progn - (cond ((equal doom-leader-alt-key "C-c") - (set-keymap-parent doom-leader-map mode-specific-map)) - ((equal doom-leader-alt-key "C-x") - (set-keymap-parent doom-leader-map ctl-x-map))) - (define-key map (kbd doom-leader-alt-key) 'doom/leader)) - (evil-define-key* '(normal visual motion) map (kbd doom-leader-key) 'doom/leader) - (evil-define-key* '(emacs insert) map (kbd doom-leader-alt-key) 'doom/leader)) - (general-override-mode +1))) -(add-hook 'doom-after-init-modules-hook #'doom|init-leader-keys) +(add-hook! 'doom-after-init-modules-hook + (defun doom-init-leader-keys-h () + "Bind `doom-leader-key' and `doom-leader-alt-key'." + (let ((map general-override-mode-map)) + (if (not (featurep 'evil)) + (progn + (cond ((equal doom-leader-alt-key "C-c") + (set-keymap-parent doom-leader-map mode-specific-map)) + ((equal doom-leader-alt-key "C-x") + (set-keymap-parent doom-leader-map ctl-x-map))) + (define-key map (kbd doom-leader-alt-key) 'doom/leader)) + (evil-define-key* '(normal visual motion) map (kbd doom-leader-key) 'doom/leader) + (evil-define-key* '(emacs insert) map (kbd doom-leader-alt-key) 'doom/leader)) + (general-override-mode +1)))) ;; ;;; Packages -(def-package! which-key +(use-package! which-key :defer 1 :after-call pre-command-hook :init @@ -198,10 +188,6 @@ localleader prefix." (which-key-mode +1)) -;;;###package hydra -(setq lv-use-seperator t) - - ;; ;;; `map!' macro @@ -216,7 +202,7 @@ localleader prefix." (?g . global)) "A list of cons cells that map a letter to a evil state symbol.") -(defun doom--keyword-to-states (keyword) +(defun doom--map-keyword-to-states (keyword) "Convert a KEYWORD into a list of evil state symbols. For example, :nvi will map to (list 'normal 'visual 'insert). See @@ -232,12 +218,9 @@ For example, :nvi will map to (list 'normal 'visual 'insert). See (put :leader 'lisp-indent-function 'defun) (put :localleader 'lisp-indent-function 'defun) (put :map 'lisp-indent-function 'defun) -(put :keymap 'lisp-indent-function 'defun) (put :mode 'lisp-indent-function 'defun) (put :prefix 'lisp-indent-function 'defun) (put :prefix-map 'lisp-indent-function 'defun) -(put :unless 'lisp-indent-function 'defun) -(put :when 'lisp-indent-function 'defun) ;; specials (defvar doom--map-forms nil) @@ -271,7 +254,7 @@ For example, :nvi will map to (list 'normal 'visual 'insert). See (setq rest nil)) (:desc (setq desc (pop rest))) - ((or :map :map* :keymap) + (:map (doom--map-set :keymaps `(quote ,(doom-enlist (pop rest))))) (:mode (push (cl-loop for m in (doom-enlist (pop rest)) @@ -307,7 +290,9 @@ For example, :nvi will map to (list 'normal 'visual 'insert). See doom--map-forms))) (_ (condition-case _ - (doom--map-def (pop rest) (pop rest) (doom--keyword-to-states key) desc) + (doom--map-def (pop rest) (pop rest) + (doom--map-keyword-to-states key) + desc) (error (error "Not a valid `map!' property: %s" key))) (setq desc nil)))) @@ -414,7 +399,6 @@ Properties :localleader [...] bind to localleader; requires a keymap :mode [MODE(s)] [...] inner keybinds are applied to major MODE(s) :map [KEYMAP(s)] [...] inner keybinds are applied to KEYMAP(S) - :keymap [KEYMAP(s)] [...] same as :map :prefix [PREFIX] [...] set keybind prefix for following keys. PREFIX can be a cons cell: (PREFIX . DESCRIPTION) :prefix-map [PREFIX] [...] same as :prefix, but defines a prefix keymap diff --git a/core/core-lib.el b/core/core-lib.el index 054ba5187..ed2abf35b 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -1,6 +1,6 @@ ;;; core-lib.el -*- lexical-binding: t; -*- -(let ((load-path doom-site-load-path)) +(let ((load-path doom--initial-load-path)) (require 'subr-x) (require 'cl-lib)) @@ -12,56 +12,36 @@ ;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we alias ;; them for 25 users. (defalias 'if-let* #'if-let) - (defalias 'when-let* #'when-let))) + (defalias 'when-let* #'when-let) + + ;; `mapcan' was introduced in 26.1. `cl-mapcan' isn't a perfect replacement, + ;; but it's close enough. + (defalias 'mapcan #'cl-mapcan) + + (defun alist-get (key alist &optional default remove testfn) + "Return the value associated with KEY in ALIST. +If KEY is not found in ALIST, return DEFAULT. +Use TESTFN to lookup in the alist if non-nil. Otherwise, use `assq'. + +This is a generalized variable suitable for use with `setf'. +When using it to set a value, optional argument REMOVE non-nil +means to remove KEY from ALIST if the new value is `eql' to DEFAULT." + (ignore remove) ;;Silence byte-compiler. + (let ((x (if (not testfn) + (assq key alist) + ;; In Emacs<26, `assoc' has no testfn arg, so we have to + ;; implement it ourselves + (if testfn + (cl-loop for entry in alist + if (funcall testfn key entry) + return entry) + (assoc key alist))))) + (if x (cdr x) default))))) ;; ;;; Helpers -(defun doom--resolve-path-forms (spec &optional directory) - "Converts a simple nested series of or/and forms into a series of -`file-exists-p' checks. - -For example - - (doom--resolve-path-forms - '(or A (and B C)) - \"~\") - -Returns (approximately): - - '(let* ((_directory \"~\") - (A (expand-file-name A _directory)) - (B (expand-file-name B _directory)) - (C (expand-file-name C _directory))) - (or (and (file-exists-p A) A) - (and (if (file-exists-p B) B) - (if (file-exists-p C) C)))) - -This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." - (declare (pure t) (side-effect-free t)) - (cond ((stringp spec) - `(let ((--file-- ,(if (file-name-absolute-p spec) - spec - `(expand-file-name ,spec ,directory)))) - (and (file-exists-p --file--) - --file--))) - ((and (listp spec) - (memq (car spec) '(or and))) - `(,(car spec) - ,@(cl-loop for i in (cdr spec) - collect (doom--resolve-path-forms i directory)))) - ((or (symbolp spec) - (listp spec)) - `(let ((--file-- ,(if (and directory - (or (not (stringp directory)) - (file-name-absolute-p directory))) - `(expand-file-name ,spec ,directory) - spec))) - (and (file-exists-p --file--) - --file--))) - (spec))) - (defun doom--resolve-hook-forms (hooks) "Converts a list of modes into a list of hook symbols. @@ -76,20 +56,25 @@ list is returned as-is." collect (cadr hook) else collect (intern (format "%s-hook" (symbol-name hook))))))) -(defun doom--assert-stage-p (stage macro) - (unless (or (bound-and-true-p byte-compile-current-file) - ;; Don't complain if we're being evaluated on-the-fly. Since forms - ;; are often evaluated (by `eval-region') or expanded (by - ;; macroexpand) in a temp buffer in `emacs-lisp-mode'... - (eq major-mode 'emacs-lisp-mode)) - (cl-assert (eq stage doom--stage) - nil - "Found %s call in non-%s.el file (%s)" - macro (symbol-name stage) - (let ((path (FILE!))) - (if (file-in-directory-p path doom-emacs-dir) - (file-relative-name path doom-emacs-dir) - (abbreviate-file-name path)))))) +(defun doom--setq-hook-fns (hooks rest &optional singles) + (unless (or singles (= 0 (% (length rest) 2))) + (signal 'wrong-number-of-arguments (list #'evenp (length rest)))) + (cl-loop with vars = (let ((args rest) + vars) + (while args + (push (if singles + (list (pop args)) + (cons (pop args) (pop args))) + vars)) + (nreverse vars)) + for hook in (doom--resolve-hook-forms hooks) + for mode = (string-remove-suffix "-hook" (symbol-name hook)) + append + (cl-loop for (var . val) in vars + collect + (list var val hook + (intern (format "doom--setq-%s-for-%s-h" + var mode)))))) ;; @@ -116,7 +101,7 @@ list is returned as-is." (defun doom-keyword-name (keyword) "Returns the string name of KEYWORD (`keywordp') minus the leading colon." (declare (pure t) (side-effect-free t)) - (cl-check-type :test keyword) + (cl-check-type keyword keyword) (substring (symbol-name keyword) 1)) (defmacro doom-log (format-string &rest args) @@ -136,57 +121,239 @@ Accepts the same arguments as `message'." format-string) ,@args)))) -(defun FILE! () - "Return the emacs lisp file this macro is called from." - (cond ((bound-and-true-p byte-compile-current-file)) - (load-file-name) - (buffer-file-name) - ((stringp (car-safe current-load-list)) (car current-load-list)))) +(defalias 'doom-partial #'apply-partially) -(defun DIR! () - "Returns the directory of the emacs lisp file this macro is called from." - (let ((file (FILE!))) - (and file (file-name-directory file)))) +(defun doom-rpartial (fn &rest args) + "Return a function that is a partial application of FUN to right-hand ARGS. + +ARGS is a list of the last N arguments to pass to FUN. The result is a new +function which does the same as FUN, except that the last N arguments are fixed +at the values with which this function was called." + (lambda (&rest pre-args) + (apply fn (append pre-args args)))) ;; -;; Macros +;;; Sugars (defmacro λ! (&rest body) "Expands to (lambda () (interactive) ,@body)." (declare (doc-string 1)) `(lambda () (interactive) ,@body)) +(defalias 'lambda! 'λ!) -(defmacro λ!! (command &optional arg) +(defun λ!! (command &optional arg) "Expands to a command that interactively calls COMMAND with prefix ARG." (declare (doc-string 1)) - `(lambda () (interactive) - (let ((current-prefix-arg ,arg)) - (call-interactively ,command)))) - -(defalias 'lambda! 'λ!) + (lambda () (interactive) + (let ((current-prefix-arg arg)) + (call-interactively command)))) (defalias 'lambda!! 'λ!!) +(defun file! () + "Return the emacs lisp file this macro is called from." + (cond ((bound-and-true-p byte-compile-current-file)) + (load-file-name) + ((stringp (car-safe current-load-list)) + (car current-load-list)) + (buffer-file-name) + ((error "Cannot get this file-path")))) + +(defun dir! () + "Returns the directory of the emacs lisp file this macro is called from." + (when-let (path (file!)) + (directory-file-name (file-name-directory path)))) + +(defmacro setq! (&rest settings) + "A stripped-down `customize-set-variable' with the syntax of `setq'." + (macroexp-progn + (cl-loop for (var val) on settings by 'cddr + collect `(funcall (or (get ',var 'custom-set) #'set) + ',var ,val)))) + (defmacro pushnew! (place &rest values) - "Like `cl-pushnew', but will prepend VALUES to PLACE. -The order VALUES is preserved." - `(dolist (--value-- (nreverse (list ,@values))) - (cl-pushnew --value-- ,place))) + "Push VALUES sequentially into PLACE, if they aren't already present. +This is a variadic `cl-pushnew'." + (let ((var (make-symbol "result"))) + `(dolist (,var (list ,@values)) + (cl-pushnew ,var ,place :test #'equal)))) + +(defmacro prependq! (sym &rest lists) + "Prepend LISTS to SYM in place." + `(setq ,sym (append ,@lists ,sym))) + +(defmacro appendq! (sym &rest lists) + "Append LISTS to SYM in place." + `(setq ,sym (append ,sym ,@lists))) + +(defmacro nconcq! (sym &rest lists) + "Append LISTS to SYM by altering them in place." + `(setq ,sym (nconc ,sym ,@lists))) (defmacro delq! (elt list &optional fetcher) - "Delete ELT from LIST in-place." + "`delq' ELT from LIST in-place. + +If FETCHER is a function, ELT is used as the key in LIST (an alist)." `(setq ,list (delq ,(if fetcher `(funcall ,fetcher ,elt ,list) elt) ,list))) -(defmacro cond! (&rest clauses) - "An anaphoric `cond', which stores the conditional value in `it'." - `(let (it) - (cond ,@(cl-loop for (cond . body) in clauses - collect `((setq it ,cond) - ,@body))))) +(defmacro delete! (elt list) + "Delete ELT from LIST in-place." + `(setq ,list (delete ,elt ,list))) + +(defmacro add-transient-hook! (hook-or-function &rest forms) + "Attaches a self-removing function to HOOK-OR-FUNCTION. + +FORMS are evaluated once, when that function/hook is first invoked, then never +again. + +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 (intern (format "doom--transient-%s-h" (sxhash hook-or-function))))) + `(let ((sym ,hook-or-function)) + (defun ,fn (&rest _) + ,@forms + (let ((sym ,hook-or-function)) + (cond ((functionp sym) (advice-remove sym #',fn)) + ((symbolp sym) (remove-hook sym #',fn)))) + (unintern ',fn nil)) + (cond ((functionp sym) + (advice-add ,hook-or-function ,(if append :after :before) #',fn)) + ((symbolp sym) + (put ',fn 'permanent-local-hook t) + (add-hook sym #',fn ,append)))))) + +(defmacro add-hook! (hooks &rest rest) + "A convenience macro for adding N functions to M hooks. + +If N and M = 1, there's no benefit to using this macro over `add-hook'. + +This macro accepts, in order: + + 1. Optional properties :local and/or :append, which will make the hook + buffer-local or append to the list of hooks (respectively), + 2. The hook(s) to be added to: either an unquoted mode, an unquoted list of + modes, a quoted hook variable or a quoted list of hook variables. If + unquoted, '-hook' will be appended to each symbol. + 3. The function(s) to be added: this can be one function, a list thereof, a + list of `defun's, or body forms (implicitly wrapped in a closure). + +\(fn [:append :local] HOOKS FUNCTIONS)" + (declare (indent (lambda (indent-point state) + (goto-char indent-point) + (when (looking-at-p "\\s-*(") + (lisp-indent-defform state indent-point)))) + (debug t)) + (let* ((hook-forms (doom--resolve-hook-forms hooks)) + (func-forms ()) + (defn-forms ()) + append-p + local-p + remove-p + forms) + (while (keywordp (car rest)) + (pcase (pop rest) + (:append (setq append-p t)) + (:local (setq local-p t)) + (:remove (setq remove-p t)))) + (let ((first (car-safe (car rest)))) + (cond ((null first) + (setq func-forms rest)) + + ((eq first 'defun) + (setq func-forms (mapcar #'cadr rest) + defn-forms rest)) + + ((memq first '(quote function)) + (setq func-forms + (if (cdr rest) + (mapcar #'doom-unquote rest) + (doom-enlist (doom-unquote (car rest)))))) + + ((setq func-forms (list `(lambda () ,@rest))))) + (dolist (hook hook-forms) + (dolist (func func-forms) + (push (if remove-p + `(remove-hook ',hook #',func ,local-p) + `(add-hook ',hook #',func ,append-p ,local-p)) + forms))) + (macroexp-progn + (append defn-forms + (if append-p + (nreverse forms) + forms)))))) + +(defmacro remove-hook! (hooks &rest rest) + "A convenience macro for removing N functions from M hooks. + +Takes the same arguments as `add-hook!'. + +If N and M = 1, there's no benefit to using this macro over `remove-hook'. + +\(fn [:append :local] HOOKS FUNCTIONS)" + (declare (indent defun) (debug t)) + `(add-hook! ,hooks :remove ,@rest)) + +(defmacro setq-hook! (hooks &rest var-vals) + "Sets buffer-local variables on HOOKS. + + (setq-hook! 'markdown-mode-hook + line-spacing 2 + fill-column 80) + +\(fn HOOKS &rest [SYM VAL]...)" + (declare (indent 1)) + (macroexp-progn + (cl-loop for (var val hook fn) in (doom--setq-hook-fns hooks var-vals) + collect `(defun ,fn (&rest _) + ,(format "%s = %s" var (pp-to-string val)) + (setq-local ,var ,val)) + collect `(remove-hook ',hook #',fn) ; ensure set order + collect `(add-hook ',hook #',fn)))) + +(defmacro unsetq-hook! (hooks &rest vars) + "Unbind setq hooks on HOOKS for VARS. + +\(fn HOOKS &rest [SYM VAL]...)" + (declare (indent 1)) + (macroexp-progn + (cl-loop for (_var _val hook fn) in (doom--setq-hook-fns hooks vars 'singles) + collect `(remove-hook ',hook #',fn)))) + +(defmacro load! (filename &optional path noerror) + "Load a file relative to the current executing file (`load-file-name'). + +FILENAME is either a file path string or a form that should evaluate to such a +string at run time. PATH is where to look for the file (a string representing a +directory path). If omitted, the lookup is relative to either `load-file-name', +`byte-compile-current-file' or `buffer-file-name' (checked in that order). + +If NOERROR is non-nil, don't throw an error if the file doesn't exist." + (unless path + (setq path (or (dir!) + (error "Could not detect path to look for '%s' in" + filename)))) + (let ((file (if path `(expand-file-name ,filename ,path) filename))) + `(condition-case e + (load ,file ,noerror ,(not doom-debug-mode)) + ((debug doom-error) (signal (car e) (cdr e))) + ((debug error) + (let* ((source (file-name-sans-extension ,file)) + (err (cond ((file-in-directory-p source doom-core-dir) + (cons 'doom-error doom-core-dir)) + ((file-in-directory-p source doom-private-dir) + (cons 'doom-private-error doom-private-dir)) + ((cons 'doom-module-error doom-emacs-dir))))) + (signal (car err) + (list (file-relative-name + (concat source ".el") + (cdr err)) + e))))))) (defmacro defer-until! (condition &rest body) "Run BODY when CONDITION is true (checks on `after-load-functions'). Meant to @@ -194,16 +361,16 @@ serve as a predicated alternative to `after!'." (declare (indent defun) (debug t)) `(if ,condition (progn ,@body) - ,(let ((fun (make-symbol "doom|delay-form-"))) + ,(let ((fn (intern (format "doom--delay-form-%s-h" (sxhash (cons condition body)))))) `(progn - (fset ',fun (lambda (&rest args) - (when ,(or condition t) - (remove-hook 'after-load-functions #',fun) - (unintern ',fun nil) - (ignore args) - ,@body))) - (put ',fun 'permanent-local-hook t) - (add-hook 'after-load-functions #',fun))))) + (fset ',fn (lambda (&rest args) + (when ,(or condition t) + (remove-hook 'after-load-functions #',fn) + (unintern ',fn nil) + (ignore args) + ,@body))) + (put ',fn 'permanent-local-hook t) + (add-hook 'after-load-functions #',fn))))) (defmacro defer-feature! (feature &optional mode) "Pretend FEATURE hasn't been loaded yet, until FEATURE-hook is triggered. @@ -213,7 +380,7 @@ startup, which will prematurely trigger `after!' (and `with-eval-after-load') blocks. To get around this we make Emacs believe FEATURE hasn't been loaded yet, then wait until FEATURE-hook (or MODE-hook, if MODE is provided) is triggered to reverse this and trigger `after!' blocks at a more reasonable time." - (let ((advice-fn (intern (format "doom|defer-feature-%s" feature))) + (let ((advice-fn (intern (format "doom--defer-feature-%s-a" feature))) (mode (or mode feature))) `(progn (setq features (delq ',feature features)) @@ -250,291 +417,32 @@ writes to `standard-output'." (save-silently t)) (prog1 ,@forms (message "")))))) -(defmacro add-transient-hook! (hook-or-function &rest forms) - "Attaches a self-removing function to HOOK-OR-FUNCTION. -FORMS are evaluated once, when that function/hook is first invoked, then never -again. +;; +;;; Definers -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)) - (intern (format "doom|transient-hook-%s" (pop forms))) - (make-symbol "doom|transient-hook")))) - `(let ((sym ,hook-or-function)) - (fset ',fn - (lambda (&rest _) - ,@forms - (let ((sym ,hook-or-function)) - (cond ((functionp sym) (advice-remove sym #',fn)) - ((symbolp sym) (remove-hook sym #',fn)))) - (unintern ',fn nil))) - (cond ((functionp sym) - (advice-add ,hook-or-function ,(if append :after :before) #',fn)) - ((symbolp sym) - (put ',fn 'permanent-local-hook t) - (add-hook sym #',fn ,append)))))) +(defmacro defadvice! (symbol arglist &optional docstring &rest body) + "Define an advice called SYMBOL and add it to PLACES. -(defmacro add-hook! (&rest args) - "A convenience macro for adding N functions to M hooks. +ARGLIST is as in `defun'. WHERE is a keyword as passed to `advice-add', and +PLACE is the function to which to add the advice, like in `advice-add'. +DOCSTRING and BODY are as in `defun'. -If N and M = 1, there's no benefit to using this macro over `add-hook'. - -This macro accepts, in order: - - 1. Optional properties :local and/or :append, which will make the hook - buffer-local or append to the list of hooks (respectively), - 2. The hook(s) to be added to: either an unquoted mode, an unquoted list of - modes, a quoted hook variable or a quoted list of hook variables. If - unquoted, '-hook' will be appended to each symbol. - 3. The function(s) to be added: this can be one function, a list thereof, or - body forms (implicitly wrapped in a closure). - -Examples: - (add-hook! 'some-mode-hook 'enable-something) (same as `add-hook') - (add-hook! some-mode '(enable-something and-another)) - (add-hook! '(one-mode-hook second-mode-hook) 'enable-something) - (add-hook! (one-mode second-mode) 'enable-something) - (add-hook! :append (one-mode second-mode) 'enable-something) - (add-hook! :local (one-mode second-mode) 'enable-something) - (add-hook! (one-mode second-mode) (setq v 5) (setq a 2)) - (add-hook! :append :local (one-mode second-mode) (setq v 5) (setq a 2)) - -\(fn [:append :local] HOOKS FUNCTIONS)" - (declare (indent defun) (debug t)) - (let ((hook-fn 'add-hook) - append-p local-p) - (while (keywordp (car args)) - (pcase (pop args) - (:append (setq append-p t)) - (:local (setq local-p t)) - (:remove (setq hook-fn 'remove-hook)))) - (let ((hooks (doom--resolve-hook-forms (pop args))) - (funcs - (let ((val (car args))) - (if (memq (car-safe val) '(quote function)) - (if (cdr-safe (cadr val)) - (cadr val) - (list (cadr val))) - (list args)))) - forms) - (dolist (fn funcs) - (setq fn (if (symbolp fn) - `(function ,fn) - `(lambda (&rest _) ,@args))) - (dolist (hook hooks) - (push (if (eq hook-fn 'remove-hook) - `(remove-hook ',hook ,fn ,local-p) - `(add-hook ',hook ,fn ,append-p ,local-p)) - forms))) - `(progn ,@(if append-p (nreverse forms) forms))))) - -(defmacro remove-hook! (&rest args) - "A convenience macro for removing N functions from M hooks. - -Takes the same arguments as `add-hook!'. - -If N and M = 1, there's no benefit to using this macro over `remove-hook'. - -\(fn [:append :local] HOOKS FUNCTIONS)" - (declare (indent defun) (debug t)) - `(add-hook! :remove ,@args)) - -(defmacro setq-hook! (hooks &rest rest) - "Sets buffer-local variables on HOOKS. - - (setq-hook! 'markdown-mode-hook - line-spacing 2 - fill-column 80) - -\(fn HOOKS &rest SYM VAL...)" - (declare (indent 1)) - (unless (= 0 (% (length rest) 2)) - (signal 'wrong-number-of-arguments (list #'evenp (length rest)))) - (let ((vars (let ((args rest) - vars) - (while args - (push (symbol-name (car args)) vars) - (setq args (cddr args))) - (string-join (sort vars #'string-lessp) "-")))) - (macroexp-progn - (cl-loop for hook in (doom--resolve-hook-forms hooks) - for mode = (string-remove-suffix "-hook" (symbol-name hook)) - for fn = (intern (format "doom|setq-%s-for-%s" vars mode)) - collect `(fset ',fn - (lambda (&rest _) - ,@(let (forms) - (while rest - (let ((var (pop rest)) - (val (pop rest))) - (push `(setq-local ,var ,val) forms))) - (nreverse forms)))) - collect `(add-hook ',hook #',fn 'append))))) - -(defun advice-add! (symbols where functions) - "Variadic version of `advice-add'. - -SYMBOLS and FUNCTIONS can be lists of functions." - (let ((functions (if (functionp functions) - (list functions) - functions))) - (dolist (s (doom-enlist symbols)) - (dolist (f (doom-enlist functions)) - (advice-add s where f))))) - -(defun advice-remove! (symbols where-or-fns &optional functions) - "Variadic version of `advice-remove'. - -WHERE-OR-FNS is ignored if FUNCTIONS is provided. This lets you substitute -advice-add with advice-remove and evaluate them without having to modify every -statement." - (unless functions - (setq functions where-or-fns - where-or-fns nil)) - (let ((functions (if (functionp functions) - (list functions) - functions))) - (dolist (s (doom-enlist symbols)) - (dolist (f (doom-enlist functions)) - (advice-remove s f))))) - -(cl-defmacro associate! (mode &key modes match files when) - "Enables a minor mode if certain conditions are met. - -The available conditions are: - - :modes SYMBOL_LIST - A list of major/minor modes in which this minor mode may apply. - :match REGEXP - A regexp to be tested against the current file path. - :files SPEC - Accepts what `project-file-exists-p!' accepts. Checks if certain files or - directories exist relative to the project root. - :when FORM - Whenever FORM returns non-nil." - (declare (indent 1)) - (unless noninteractive - (cond ((or files modes when) - (when (and files - (not (or (listp files) - (stringp files)))) - (user-error "associate! :files expects a string or list of strings")) - (let ((hook-name (intern (format "doom--init-mode-%s" mode)))) - `(progn - (fset ',hook-name - (lambda () - (and (fboundp ',mode) - (not (bound-and-true-p ,mode)) - (and buffer-file-name (not (file-remote-p buffer-file-name))) - ,(or (not match) - `(if buffer-file-name (string-match-p ,match buffer-file-name))) - ,(or (not files) - (doom--resolve-path-forms - (if (stringp (car files)) (cons 'and files) files) - '(doom-project-root))) - ,(or when t) - (,mode 1)))) - ,@(if (and modes (listp modes)) - (cl-loop for hook in (doom--resolve-hook-forms modes) - collect `(add-hook ',hook #',hook-name)) - `((add-hook 'after-change-major-mode-hook #',hook-name)))))) - (match - `(add-to-list 'doom-auto-minor-mode-alist '(,match . ,mode))) - ((user-error "Invalid `associate!' rules for mode [%s] (:modes %s :match %s :files %s :when %s)" - mode modes match files when))))) - -(defmacro file-exists-p! (spec &optional directory) - "Returns non-nil if the files in SPEC all exist. - -Returns the last file found to meet the rules set by SPEC. SPEC can be a single -file or a list of forms/files. It understands nested (and ...) and (or ...), as -well. - -DIRECTORY is where to look for the files in SPEC if they aren't absolute. - -For example: - (file-exists-p! (or doom-core-dir \"~/.config\" \"some-file\") \"~\")" - (if directory - `(let ((--directory-- ,directory)) - ,(doom--resolve-path-forms spec '--directory--)) - (doom--resolve-path-forms spec))) - -(defmacro load! (filename &optional path noerror) - "Load a file relative to the current executing file (`load-file-name'). - -FILENAME is either a file path string or a form that should evaluate to such a -string at run time. PATH is where to look for the file (a string representing a -directory path). If omitted, the lookup is relative to either `load-file-name', -`byte-compile-current-file' or `buffer-file-name' (checked in that order). - -If NOERROR is non-nil, don't throw an error if the file doesn't exist." - (unless path - (setq path (or (DIR!) - (error "Could not detect path to look for '%s' in" - filename)))) - (let ((file (if path `(expand-file-name ,filename ,path) filename))) - `(condition-case e - (load ,file ,noerror ,(not doom-debug-mode)) - ((debug doom-error) (signal (car e) (cdr e))) - ((debug error) - (let* ((source (file-name-sans-extension ,file)) - (err (cond ((file-in-directory-p source doom-core-dir) - (cons 'doom-error doom-core-dir)) - ((file-in-directory-p source doom-private-dir) - (cons 'doom-private-error doom-private-dir)) - ((cons 'doom-module-error doom-emacs-dir))))) - (signal (car err) - (list (file-relative-name - (concat source ".el") - (cdr err)) - e))))))) - -(defmacro custom-theme-set-faces! (theme &rest specs) - "Apply a list of face specs as user customizations for THEME. - -THEME can be a single symbol or list thereof. If nil, apply these settings to -all themes. It will apply to all themes once they are loaded. - - (custom-theme-set-faces! '(doom-one doom-one-light) - `(mode-line :foreground ,(doom-color 'blue)) - `(mode-line-buffer-id :foreground ,(doom-color 'fg) :background \"#000000\") - '(mode-line-success-highlight :background \"#00FF00\") - '(org-tag :background \"#4499FF\") - '(org-ellipsis :inherit org-tag) - '(which-key-docstring-face :inherit font-lock-comment-face))" - `(let* ((themes (doom-enlist (or ,theme 'user))) - (fn (gensym (format "doom|customize-%s-" (mapconcat #'symbol-name themes "-"))))) - (fset fn - (lambda () - (dolist (theme themes) - (when (or (eq theme 'user) - (custom-theme-enabled-p theme)) - (apply #'custom-theme-set-faces 'user - (cl-loop for (face . spec) in (list ,@specs) - if (keywordp (car spec)) - collect `(,face ((t ,spec))) - else collect `(,face ,spec))))))) - (funcall fn) - (add-hook 'doom-load-theme-hook fn))) - -(defmacro custom-set-faces! (&rest specs) - "Apply a list of face specs as user customizations. - -SPECS is a list of face specs. - -This is a drop-in replacement for `custom-set-face' that allows for a simplified -face format, e.g. - - (custom-set-faces! - `(mode-line :foreground ,(doom-color 'blue)) - `(mode-line-buffer-id :foreground ,(doom-color 'fg) :background \"#000000\") - '(mode-line-success-highlight :background \"#00FF00\") - '(org-tag :background \"#4499FF\") - '(org-ellipsis :inherit org-tag) - '(which-key-docstring-face :inherit font-lock-comment-face))" - `(custom-theme-set-faces! 'user ,@specs)) +\(fn SYMBOL ARGLIST &optional DOCSTRING &rest [WHERE PLACES...] BODY\)" + (declare (doc-string 3) (indent defun)) + (unless (stringp docstring) + (push docstring body) + (setq docstring nil)) + (let (where-alist) + (while (keywordp (car body)) + (push `(cons ,(pop body) (doom-enlist ,(pop body))) + where-alist)) + `(progn + (defun ,symbol ,arglist ,docstring ,@body) + ,(when where-alist + `(dolist (targets (list ,@(nreverse where-alist))) + (dolist (target (cdr targets)) + (advice-add target (car targets) #',symbol))))))) (provide 'core-lib) ;;; core-lib.el ends here diff --git a/core/core-modules.el b/core/core-modules.el index 64a847f1e..1ca47f678 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -11,9 +11,6 @@ doom-modules-dir) "A list of module root directories. Order determines priority.") -(defvar doom-inhibit-module-warnings (not noninteractive) - "If non-nil, don't emit deprecated or missing module warnings at startup.") - (defconst doom-obsolete-modules '((:feature (version-control (:emacs vc) (:ui vc-gutter)) (spellcheck (:tools flyspell)) @@ -34,7 +31,8 @@ (term (:term term))) (:ui (doom-modeline (:ui modeline)) (fci (:ui fill-column)) - (evil-goggles (:ui ophints))) + (evil-goggles (:ui ophints)) + (tabbar (:ui tabs))) (:app (email (:email mu4e)) (notmuch (:email notmuch)))) "A tree alist that maps deprecated modules to their replacement(s). @@ -50,14 +48,10 @@ syntax-checker modules obsolete. e.g. If :feature version-control is found in your `doom!' block, a warning is emitted before replacing it with :emacs vc and :ui vc-gutter.") -(defvar doom--current-module nil) -(defvar doom--current-flags nil) -(defvar doom--modules-cache ()) +(defvar doom-inhibit-module-warnings (not noninteractive) + "If non-nil, don't emit deprecated or missing module warnings at startup.") - -;; ;;; Custom hooks - (defvar doom-before-init-modules-hook nil "A list of hooks to run before Doom's modules' config.el files are loaded, but after their init.el files are loaded.") @@ -68,8 +62,8 @@ before the user's private module.") (defvaralias 'doom-after-init-modules-hook 'after-init-hook) -(define-obsolete-variable-alias 'doom-post-init-hook 'doom-init-modules-hook "2.1.0") -(define-obsolete-variable-alias 'doom-init-hook 'doom-before-init-modules-hook "2.1.0") +(defvar doom--current-module nil) +(defvar doom--current-flags nil) ;; @@ -79,27 +73,26 @@ before the user's private module.") "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy session of Dooming. Will noop if used more than once, unless FORCE-P is non-nil." - (when (and (or force-p - (not doom-init-modules-p)) - (not (setq doom-modules nil)) - (load! "init" doom-private-dir t)) - (setq doom-init-modules-p t) - (unless (hash-table-p doom-modules) - (setq doom-modules (make-hash-table :test 'equal))) - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "init" (plist-get plist :path) t))) - doom-modules) - (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) - (unless noninteractive - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "config" (plist-get plist :path) t))) - doom-modules) - (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) - (load! "config" doom-private-dir t)))) + (when (or force-p (not doom-init-modules-p)) + (setq doom-init-modules-p t + doom-modules nil) + (when (load! "init" doom-private-dir t) + (when doom-modules + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "init" (plist-get plist :path) t))) + doom-modules)) + (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) + (unless noninteractive + (when doom-modules + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "config" (plist-get plist :path) t))) + doom-modules)) + (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) + (load! "config" doom-private-dir t))))) ;; @@ -108,12 +101,11 @@ non-nil." (defun doom-module-p (category module &optional flag) "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." (declare (pure t) (side-effect-free t)) - (when (hash-table-p doom-modules) - (let ((plist (gethash (cons category module) doom-modules))) - (and plist - (or (null flag) - (memq flag (plist-get plist :flags))) - t)))) + (let ((plist (gethash (cons category module) doom-modules))) + (and plist + (or (null flag) + (memq flag (plist-get plist :flags))) + t))) (defun doom-module-get (category module &optional property) "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." @@ -128,7 +120,7 @@ non-nil." of PROPERTY and VALUEs. \(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" - (if-let* ((old-plist (doom-module-get category module))) + (if-let (old-plist (doom-module-get category module)) (progn (when plist (when (cl-oddp (length plist)) @@ -159,9 +151,10 @@ MODULE (symbol). If the category isn't enabled this will always return nil. For finding disabled modules use `doom-module-locate-path'." - (let ((path (doom-module-get category module :path)) - file-name-handler-alist) - (if file (expand-file-name file path) + (let ((path (doom-module-get category module :path))) + (if file + (let (file-name-handler-alist) + (expand-file-name file path)) path))) (defun doom-module-locate-path (category &optional module file) @@ -183,37 +176,53 @@ This doesn't require modules to be enabled. For enabled modules us if (file-exists-p path) return (expand-file-name path))) -(defun doom-module-from-path (&optional path) - "Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)." - (or doom--current-module - (let* (file-name-handler-alist - (path (or path (FILE!)))) - (save-match-data - (setq path (file-truename path)) - (when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path) - (when-let* ((category (match-string 1 path)) - (module (match-string 2 path))) - (cons (doom-keyword-intern category) - (intern module)))))))) +(defun doom-module-from-path (&optional path enabled-only) + "Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path). +If ENABLED-ONLY, return nil if the containing module isn't enabled." + (if (null path) + (if doom--current-module + (if enabled-only + (and (doom-module-p (car doom--current-module) + (cdr doom--current-module)) + doom--current-module) + doom--current-module) + (doom-module-from-path (file!))) + (let* ((file-name-handler-alist nil) + (path (file-truename (or path (file!))))) + (save-match-data + (cond ((string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path) + (when-let* ((category (doom-keyword-intern (match-string 1 path))) + (module (intern (match-string 2 path)))) + (and (or (null enabled-only) + (doom-module-p category module)) + (cons category module)))) + ((file-in-directory-p path doom-core-dir) + (cons :core (intern (file-name-base path)))) + ((file-in-directory-p path doom-private-dir) + (cons :private (intern (file-name-base path))))))))) -(defun doom-module-load-path (&optional all-p) - "Return an unsorted list of absolute file paths to activated modules. +(defun doom-module-load-path (&optional module-dirs) + "Return a list of file paths to activated modules. -If ALL-P is non-nil, return paths of possible modules, activated or otherwise." +The list is in no particular order and its file paths are absolute. If +MODULE-DIRS is non-nil, include all modules (even disabled ones) available in +those directories. The first returned path is always `doom-private-dir'." (declare (pure t) (side-effect-free t)) - (append (if all-p - (doom-files-in doom-modules-dirs + (append (list doom-private-dir) + (if module-dirs + (doom-files-in (if (listp module-dirs) + module-dirs + doom-modules-dirs) :type 'dirs :mindepth 1 - :depth 1 - :full t - :sort nil) + :depth 1) (cl-loop for plist being the hash-values of (doom-modules) collect (plist-get plist :path))) - (list doom-private-dir))) + nil)) (defun doom-modules (&optional refresh-p) - "Minimally initialize `doom-modules' (a hash table) and return it." + "Minimally initialize `doom-modules' (a hash table) and return it. +This value is cached. If REFRESH-P, then don't use the cached value." (or (unless refresh-p doom-modules) (let ((noninteractive t) doom-modules @@ -236,31 +245,39 @@ If ALL-P is non-nil, return paths of possible modules, activated or otherwise." use-package-minimum-reported-time (if doom-debug-mode 0 0.1) use-package-expand-minimally (not noninteractive))) -;; Adds two new keywords to `use-package' (and consequently, `def-package!') to +;; Adds four new keywords to `use-package' (and consequently, `use-package!') to ;; expand its lazy-loading capabilities. They are: ;; -;; :after-call SYMBOL|LIST -;; :defer-incrementally SYMBOL|LIST|t +;; Check out `use-package!'s documentation for more about these two. +;; :after-call SYMBOL|LIST +;; :defer-incrementally SYMBOL|LIST|t +;; +;; Provided by `auto-minor-mode' package: +;; :minor +;; :magic-minor ;; -;; Check out `def-package!'s documentation for more about these two. (defvar doom--deferred-packages-alist '(t)) (with-eval-after-load 'use-package-core ;; Macros are already fontified, no need for this (font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords) - ;; Disable :ensure and :pin, because they don't work with Doom because we do - ;; our own package management. - (with-eval-after-load 'use-package-ensure - (dolist (keyword '(:ensure :pin)) - (delq! keyword use-package-keywords) - (delq! keyword use-package-defaults 'assq))) - - ;; Insert new deferring keywords + ;; Register all new keywords (dolist (keyword '(:defer-incrementally :after-call)) - (add-to-list 'use-package-deferring-keywords keyword nil #'eq) + (push keyword use-package-deferring-keywords) (setq use-package-keywords (use-package-list-insert keyword use-package-keywords :after))) + (dolist (keyword '(:minor :magic-minor)) + (setq use-package-keywords + (use-package-list-insert keyword use-package-keywords :commands))) + + (defalias 'use-package-normalize/:minor #'use-package-normalize-mode) + (defun use-package-handler/:minor (name _ arg rest state) + (use-package-handle-mode name 'auto-minor-mode-alist arg rest state)) + + (defalias 'use-package-normalize/:magic-minor #'use-package-normalize-mode) + (defun use-package-handler/:magic-minor (name _ arg rest state) + (use-package-handle-mode name 'auto-minor-mode-magic-alist arg rest state)) (defalias 'use-package-normalize/:defer-incrementally #'use-package-normalize-symlist) (defun use-package-handler/:defer-incrementally (name _keyword targets rest state) @@ -275,13 +292,18 @@ If ALL-P is non-nil, return paths of possible modules, activated or otherwise." (defun use-package-handler/:after-call (name _keyword hooks rest state) (if (plist-get state :demand) (use-package-process-keywords name rest state) - (let ((fn (make-symbol (format "doom|transient-hook--load-%s" name)))) + (let ((fn (make-symbol (format "doom--after-call-%s-h" name)))) (use-package-concat `((fset ',fn (lambda (&rest _) (doom-log "Loading deferred package %s from %s" ',name ',fn) (condition-case e - (require ',name) + ;; If `default-directory' is a directory that doesn't + ;; exist or is unreadable, Emacs throws up file-missing + ;; errors, so we set it to a directory we know exists and + ;; is readable. + (let ((default-directory doom-emacs-dir)) + (require ',name)) ((debug error) (message "Failed to load deferred package %s: %s" ',name e))) (when-let (deferral-list (assq ',name doom--deferred-packages-alist)) @@ -306,6 +328,10 @@ If ALL-P is non-nil, return paths of possible modules, activated or otherwise." ;; ;;; Module config macros +(put :if 'lisp-indent-function 2) +(put :when 'lisp-indent-function 'defun) +(put :unless 'lisp-indent-function 'defun) + (defmacro doom! (&rest modules) "Bootstraps DOOM Emacs and its modules. @@ -335,51 +361,62 @@ The overall load order of Doom is as follows: 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)." - (if doom--modules-cache - (progn - (setq doom-modules doom--modules-cache) - (doom-log "Using `doom-modules' cache")) - (unless doom-modules - (setq doom-modules - (make-hash-table :test 'equal - :size (if modules (length modules) 150) - :rehash-threshold 1.0))) - (let ((inhibit-message doom-inhibit-module-warnings) - category m) - (while modules - (setq m (pop modules)) - (cond ((keywordp m) (setq category m)) - ((not category) (error "No module category specified for %s" m)) - ((catch 'doom-modules - (let* ((module (if (listp m) (car m) m)) - (flags (if (listp m) (cdr m)))) - (when-let* ((obsolete (assq category doom-obsolete-modules)) - (new (assq module obsolete))) - (let ((newkeys (cdr new))) - (if (null newkeys) - (message "WARNING %s module was removed" key) - (if (cdr newkeys) - (message "WARNING %s module was removed and split into the %s modules" - (list category module) (mapconcat #'prin1-to-string newkeys ", ")) - (message "WARNING %s module was moved to %s" - (list category module) (car newkeys))) - (push category modules) - (dolist (key newkeys) - (push (if flags - (nconc (cdr key) flags) - (cdr key)) - modules) - (push (car key) modules)) - (throw 'doom-modules t)))) - (if-let (path (doom-module-locate-path category module)) - (doom-module-set category module :flags flags :path path) - (message "WARNING Couldn't find the %s %s module" category module))))))))) - (when noninteractive - (setq doom-inhibit-module-warnings t)) - `(setq doom-modules ',doom-modules)) + (unless (keywordp (car modules)) + (setq modules (eval modules t))) + (let ((doom-modules + (make-hash-table :test 'equal + :size (if modules (length modules) 150) + :rehash-threshold 1.0)) + (inhibit-message doom-inhibit-module-warnings) + category m) + (while modules + (setq m (pop modules)) + (cond ((keywordp m) (setq category m)) + ((not category) (error "No module category specified for %s" m)) + ((and (listp m) + (keywordp (car m))) + (pcase (car m) + (:cond + (cl-loop for (cond . mods) in (cdr m) + if (eval cond t) + return (prependq! modules mods))) + (:if (if (eval (cadr m) t) + (push (caddr m) modules) + (prependq! modules (cdddr m)))) + (fn (if (or (eval (cadr m) t) + (eq fn :unless)) + (prependq! modules (cddr m)))))) + ((catch 'doom-modules + (let* ((module (if (listp m) (car m) m)) + (flags (if (listp m) (cdr m)))) + (when-let* ((obsolete (assq category doom-obsolete-modules)) + (new (assq module obsolete))) + (let ((newkeys (cdr new))) + (if (null newkeys) + (message "WARNING %s module was removed" key) + (if (cdr newkeys) + (message "WARNING %s module was removed and split into the %s modules" + (list category module) (mapconcat #'prin1-to-string newkeys ", ")) + (message "WARNING %s module was moved to %s" + (list category module) (car newkeys))) + (push category modules) + (dolist (key newkeys) + (push (if flags + (nconc (cdr key) flags) + (cdr key)) + modules) + (push (car key) modules)) + (throw 'doom-modules t)))) + (if-let (path (doom-module-locate-path category module)) + (doom-module-set category module :flags flags :path path) + (message "WARNING Couldn't find the %s %s module" category module))))))) + (when noninteractive + (setq doom-inhibit-module-warnings t)) + `(setq doom-modules ',doom-modules))) (defvar doom-disabled-packages) -(defmacro def-package! (name &rest plist) +(define-obsolete-function-alias 'def-package! 'use-package!) ; DEPRECATED +(defmacro use-package! (name &rest plist) "Declares and configures a package. This is a thin wrapper around `use-package', and is ignored if the NAME package @@ -393,7 +430,7 @@ two extra properties: The first time any of these functions or hooks are executed, the package is loaded. e.g. - (def-package! projectile + (use-package! projectile :after-call (pre-command-hook after-find-file dired-before-readin-hook) ...) @@ -407,10 +444,11 @@ two extra properties: NAME is implicitly added if this property is present and non-nil. No need to specify it. A value of `t' implies NAME, e.g. - (def-package! abc + (use-package! abc ;; This is equivalent to :defer-incrementally (abc) :defer-incrementally t ...)" + (declare (indent 1)) (unless (or (memq name doom-disabled-packages) ;; At compile-time, use-package will forcibly load packages to ;; prevent compile-time errors. However, if a Doom user has @@ -420,8 +458,9 @@ two extra properties: (not (locate-library (symbol-name name))))) `(use-package ,name ,@plist))) -(defmacro def-package-hook! (package when &rest body) - "Reconfigures a package's `def-package!' block. +(define-obsolete-function-alias 'def-package-hook! 'use-package-hook!) ; DEPRECATED +(defmacro use-package-hook! (package when &rest body) + "Reconfigures a package's `use-package!' block. Only use this macro in a module's init.el file. @@ -432,19 +471,18 @@ WHEN should be one of the following: :pre-init :post-init :pre-config :post-config WARNING: If :pre-init or :pre-config hooks return nil, the original -`def-package!''s :init/:config block (respectively) is overwritten, so remember +`use-package!''s :init/:config block (respectively) is overwritten, so remember to have them return non-nil (or exploit that to overwrite Doom's config)." (declare (indent defun)) - (doom--assert-stage-p 'init #'package!) (unless (memq when '(:pre-init :post-init :pre-config :post-config)) - (error "'%s' isn't a valid hook for def-package-hook!" when)) + (error "'%s' isn't a valid hook for use-package-hook!" when)) `(progn (setq use-package-inject-hooks t) - (add-hook! - ',(intern (format "use-package--%s--%s-hook" - package - (substring (symbol-name when) 1))) - ,@body))) + (add-hook ',(intern (format "use-package--%s--%s-hook" + package + (substring (symbol-name when) 1))) + (lambda () ,@body) + 'append))) (defmacro require! (category module &rest flags) "Loads the CATEGORY MODULE module with FLAGS. @@ -470,8 +508,7 @@ module." (let ((doom--current-module ',(cons category module)) (doom--current-flags ',flags)) (load! "init" module-path :noerror) - (let ((doom--stage 'config)) - (load! "config" module-path :noerror))) + (load! "config" module-path :noerror)) ('error (lwarn 'doom-modules :error "%s in '%s %s' -> %s" @@ -497,63 +534,62 @@ CATEGORY and MODULE can be omitted When this macro is used from inside a module (and (cond (flag (memq flag (doom-module-get category module :flags))) (module (doom-module-p category module)) (doom--current-flags (memq category doom--current-flags)) - ((let ((module-pair - (or doom--current-module - (doom-module-from-path (FILE!))))) - (unless module-pair - (error "featurep! call couldn't auto-detect what module its in (from %s)" (FILE!))) - (memq category (doom-module-get (car module-pair) (cdr module-pair) :flags))))) + ((let ((module (doom-module-from-path))) + (unless module + (error "featurep! couldn't figure out what module it was called from (in %s)" + (file!))) + (memq category (doom-module-get (car module) (cdr module) :flags))))) t)) -(defmacro after! (targets &rest body) - "Evaluate BODY after TARGETS (packages) have loaded. +(defmacro after! (package &rest body) + "Evaluate BODY after PACKAGE have loaded. -This is a wrapper around `with-eval-after-load' that: - -1. Suppresses warnings for disabled packages at compile-time -2. No-ops for TARGETS that are disabled by the user (via `package!') -3. Supports compound TARGETS statements (see below) - -TARGETS is a list of packages in one of these formats: +PACKAGE is a symbol or list of them. These are package names, not modes, +functions or variables. It can be: - An unquoted package symbol (the name of a package) (after! helm BODY...) - An unquoted list of package symbols (i.e. BODY is evaluated once both magit and git-gutter have loaded) (after! (magit git-gutter) BODY...) -- An unquoted, nested list of compound package lists, using :or/:any and/or - :and/:all +- An unquoted, nested list of compound package lists, using any combination of + :or/:any and :and/:all (after! (:or package-a package-b ...) BODY...) (after! (:and package-a package-b ...) BODY...) (after! (:and package-a (:or package-b package-c) ...) BODY...) + Without :or/:any/:and/:all, :and/:all are implied. -Note that: -- :or and :any are equivalent -- :and and :all are equivalent -- If these are omitted, :and is implied." +This is a wrapper around `eval-after-load' that: + +1. Suppresses warnings for disabled packages at compile-time +2. No-ops for package that are disabled by the user (via `package!') +3. Supports compound package statements (see below) +4. Prevents eager expansion pulling in autoloaded macros all at once" (declare (indent defun) (debug t)) - (unless (and (symbolp targets) - (memq targets (bound-and-true-p doom-disabled-packages))) - (list (if (or (not (bound-and-true-p byte-compile-current-file)) - (dolist (next (doom-enlist targets)) - (unless (keywordp next) - (if (symbolp next) - (require next nil :no-error) - (load next :no-message :no-error))))) - #'progn - #'with-no-warnings) - (if (symbolp targets) - `(with-eval-after-load ',targets ,@body) - (pcase (car-safe targets) - ((or :or :any) - (macroexp-progn - (cl-loop for next in (cdr targets) - collect `(after! ,next ,@body)))) - ((or :and :all) - (dolist (next (cdr targets)) - (setq body `((after! ,next ,@body)))) - (car body)) - (_ `(after! (:and ,@targets) ,@body))))))) + (if (symbolp package) + (unless (memq package (bound-and-true-p doom-disabled-packages)) + (list (if (or (not (bound-and-true-p byte-compile-current-file)) + (require package nil 'noerror)) + #'progn + #'with-no-warnings) + (let ((body (macroexp-progn body))) + `(if (featurep ',package) + ,body + ;; We intentionally avoid `with-eval-after-load' to prevent + ;; eager macro expansion from pulling (or failing to pull) in + ;; autoloaded macros/packages. + (eval-after-load ',package ',body))))) + (let ((p (car package))) + (cond ((not (keywordp p)) + `(after! (:and ,@package) ,@body)) + ((memq p '(:or :any)) + (macroexp-progn + (cl-loop for next in (cdr package) + collect `(after! ,next ,@body)))) + ((memq p '(:and :all)) + (dolist (next (cdr package)) + (setq body `((after! ,next ,@body)))) + (car body)))))) (provide 'core-modules) ;;; core-modules.el ends here diff --git a/core/core-os.el b/core/core-os.el deleted file mode 100644 index 871b9acae..000000000 --- a/core/core-os.el +++ /dev/null @@ -1,55 +0,0 @@ -;;; core-os.el -*- lexical-binding: t; -*- - -;; clipboard -(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) - -;; fewer opts to process for systems that don't need them -(unless IS-MAC (setq command-line-ns-option-alist nil)) -(unless IS-LINUX (setq command-line-x-option-alist nil)) - -;; Fix the clipboard in terminal or daemon Emacs (non-GUI) -(defun doom|init-clipboard-in-tty-emacs () - (if IS-MAC - (if (require 'osx-clipboard nil t) (osx-clipboard-mode)) - (if (require 'xclip nil t) (xclip-mode)))) -(add-hook 'tty-setup-hook #'doom|init-clipboard-in-tty-emacs) - -;; Enable mouse in terminal Emacs -(add-hook 'tty-setup-hook #'xterm-mouse-mode) - -;; stop copying each visual state move to the clipboard: -;; https://bitbucket.org/lyro/evil/issue/336/osx-visual-state-copies-the-region-on -;; grokked from: http://stackoverflow.com/questions/15873346/elisp-rename-macro -(advice-add #'evil-visual-update-x-selection :override #'ignore) - -(cond (IS-MAC - (setq mac-command-modifier 'super - mac-option-modifier 'meta - ;; sane trackpad/mouse scroll settings - mac-redisplay-dont-reset-vscroll t - mac-mouse-wheel-smooth-scroll nil - ;; Curse Lion and its sudden but inevitable fullscreen mode! - ;; NOTE Meaningless to railwaycat's emacs-mac build - ns-use-native-fullscreen nil - ;; Visit files opened outside of Emacs in existing frame, rather - ;; than a new one - ns-pop-up-frames nil) - - ;; Syncs ns frame parameters with theme (and fixes mismatching text color - ;; in the frame title) - (when (and (or (daemonp) - (display-graphic-p)) - (require 'ns-auto-titlebar nil t)) - (add-hook 'doom-load-theme-hook #'ns-auto-titlebar-mode))) - - (IS-LINUX - (setq x-gtk-use-system-tooltips nil ; native tooltips are ugly! - x-underline-at-descent-line t)) ; draw underline lower - - (IS-WINDOWS - (setq w32-get-true-file-attributes nil) ; fix file io slowdowns - (when (display-graphic-p) - (setenv "GIT_ASKPASS" "git-gui--askpass")))) - -(provide 'core-os) -;;; core-os.el ends here diff --git a/core/core-packages.el b/core/core-packages.el index dff6e7f4e..39dd4aa7d 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -1,154 +1,205 @@ ;;; core/core-packages.el -*- lexical-binding: t; -*- -;; Emacs package management is opinionated, and so am I. I've bound together -;; `use-package', `quelpa' and package.el to create my own, rolling-release, -;; lazily-loaded package management system for Emacs. +;; Emacs package management is opinionated, and so is Doom. Doom uses `straight' +;; to create a declarative, lazy-loaded and optionally rolling-release package +;; management system. We use `straight' over `package' because the latter is +;; tempermental. ELPA sources suffer downtime occasionally, and often fail at +;; building some packages when GNU Tar is unavailable (e.g. MacOS users start +;; with BSD tar). There are also known gnutls errors in the current stable +;; release of Emacs (26.x) which bork TLS handshakes with ELPA repos (mainly +;; gnu.elpa.org). See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434. ;; -;; The three key commands are: +;; What's worse, you can only get the latest version of packages through ELPA. +;; In an ecosystem that is constantly changing, this is more frustrating than +;; convenient. Straight (and Doom) can do rolling release, but it is optional +;; (and will eventually be opt-in). ;; -;; + `bin/doom install`: Installs packages that are wanted, but not installed. -;; + `bin/doom update`: Updates packages that are out-of-date. -;; + `bin/doom autoremove`: Uninstalls packages that are no longer needed. +;; ANyhow, interacting with this package management system is done through the +;; bin/doom script included with Doom Emacs. You'll find more about it by +;; running 'doom help' (I highly recommend you add it to your PATH), but here +;; are the highlights: ;; -;; This system reads packages.el files located in each activated module (and one -;; in `doom-core-dir'). These contain `package!' blocks that tell DOOM what +;; + `bin/doom install`: a wizard that guides you through setting up Doom and +;; your private config for the first time. +;; + `bin/doom refresh`: your go-to command for making sure Doom is in optimal +;; condition. It ensures all unneeded packages are removed, all needed ones +;; are installed, and all metadata associated with them is generated. +;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest +;; versions. There's also 'bin/doom update' for updating only your packages. +;; +;; How this works is: the system reads packages.el files located in each +;; activated module, your private directory (`doom-private-dir'), and one in +;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what ;; plugins to install and where from. ;; -;; Why all the trouble? Because: -;; 1. *Scriptability:* I live in the command line. I want a shell-scriptable -;; interface for updating and installing Emacs packages. -;; 2. *Reach:* I want packages from sources other than ELPA (like github or -;; gitlab). Some plugins are out-of-date through official channels, have -;; changed hands, have a superior fork, or simply aren't available in ELPA -;; repos. -;; 3. *Performance:* The package management system isn't loaded until you use -;; the package management API. Not having to initialize package.el or quelpa -;; (and check that your packages are installed) every time you start up (or -;; load a package) speeds things up a great deal. -;; 4. *Separation of concerns:* It's more organized and reduces cognitive load -;; to separate configuring of packages and installing/updating them. -;; -;; You should be able to use package.el commands without any conflicts. -;; -;; See core/autoload/packages.el for more functions. +;; All that said, you can still use package.el's commands, but 'bin/doom +;; refresh' will purge ELPA packages. + +(defvar doom-init-packages-p nil + "If non-nil, Doom's package management system has been initialized.") (defvar doom-packages () "A list of enabled packages. Each element is a sublist, whose CAR is the package's name as a symbol, and whose CDR is the plist supplied to its `package!' declaration. Set by `doom-initialize-packages'.") -(defvar doom-core-packages - '(persistent-soft use-package quelpa async) +(defvar doom-core-packages '(straight use-package async) "A list of packages that must be installed (and will be auto-installed if missing) and shouldn't be deleted.") -(defvar doom-disabled-packages () - "A list of packages that should be ignored by `def-package!' and `after!'.") +(defvar doom-core-package-sources + '((org-elpa :local-repo nil) + (melpa + :type git :host github + :repo "melpa/melpa" + :no-build t) + (gnu-elpa-mirror + :type git :host github + :repo "emacs-straight/gnu-elpa-mirror" + :no-build t) + (emacsmirror-mirror + :type git :host github + :repo "emacs-straight/emacsmirror-mirror" + :no-build t)) + "A list of recipes for straight's recipe repos.") -;;; package.el +(defvar doom-disabled-packages () + "A list of packages that should be ignored by `use-package!' and `after!'.") + + +;; +;;; Package managers + +;; Ensure that, if we do need package.el, it is configured correctly. You really +;; shouldn't be using it, but it may be convenient for quick package testing. (setq package--init-file-ensured t - package-user-dir (expand-file-name "elpa" doom-packages-dir) - package-gnupghome-dir (expand-file-name "gpg" doom-packages-dir) package-enable-at-startup nil + package-user-dir doom-elpa-dir + package-gnupghome-dir (expand-file-name "gpg" doom-elpa-dir) ;; I omit Marmalade because its packages are manually submitted rather ;; than pulled, so packages are often out of date with upstream. package-archives - `(("gnu" . "https://elpa.gnu.org/packages/") - ("melpa" . "https://melpa.org/packages/") - ("org" . "https://orgmode.org/elpa/"))) + (let ((proto (if gnutls-verify-error "https" "http"))) + `(("gnu" . ,(concat proto "://elpa.gnu.org/packages/")) + ("melpa" . ,(concat proto "://melpa.org/packages/")) + ("org" . ,(concat proto "://orgmode.org/elpa/"))))) ;; Don't save `package-selected-packages' to `custom-file' -(advice-add #'package--save-selected-packages :override - (lambda (&optional value) (if value (setq package-selected-packages value)))) +(defadvice! doom--package-inhibit-custom-file-a (&optional value) + :override #'package--save-selected-packages + (if value (setq package-selected-packages value))) -(when (or (not gnutls-verify-error) - (not (ignore-errors (gnutls-available-p)))) - (dolist (archive package-archives) - (setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil)))) +;;; straight +(setq straight-base-dir doom-local-dir + straight-repository-branch "develop" + straight-cache-autoloads nil ; we already do this, and better. + ;; Doom doesn't encourage you to modify packages in place. Disabling this + ;; makes 'doom refresh' instant (once everything set up), which is much + ;; nicer UX than the several seconds modification checks. + straight-check-for-modifications nil + ;; We handle package.el ourselves (and a little more comprehensively) + straight-enable-package-integration nil + ;; Before switching to straight, `doom-local-dir' would average out at + ;; around 100mb with half Doom's modules at ~230 packages. Afterwards, at + ;; around 1gb. With shallow cloning, that is reduced to ~400mb. This + ;; imposes an issue with packages that require their git history for + ;; certain things to work (like magit and org), but we can deal with that + ;; when we cross that bridge. + straight-vc-git-default-clone-depth 1 + ;; Straight's own emacsmirror mirror is a little smaller and faster. + straight-recipes-emacsmirror-use-mirror t + ;; Prefix declarations are unneeded bulk added to our autoloads file. Best + ;; we just don't have to deal with them at all. + autoload-compute-prefixes nil) -;;; quelpa -(setq quelpa-dir (expand-file-name "quelpa" doom-packages-dir) - quelpa-verbose doom-debug-mode +;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch! +(defadvice! doom--straight-use-local-dir-a (orig-fn &rest args) + :around #'straight--emacs-dir + (let ((user-emacs-directory doom-local-dir)) + (apply orig-fn args))) - ;; Don't track MELPA, we'll use package.el for that - quelpa-checkout-melpa-p nil - quelpa-update-melpa-p nil - quelpa-melpa-recipe-stores nil - quelpa-self-upgrade-p nil) +(defun doom--finalize-straight () + (mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist))) + (setq straight--transaction-alist nil)) ;; ;;; Bootstrapper (defun doom-initialize-packages (&optional force-p) - "Ensures that Doom's package management system, package.el and quelpa are -initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are -populated, if they aren't already. + "Ensures that Doom's package system and straight.el are initialized. If FORCE-P is non-nil, do it anyway. -If FORCE-P is 'internal, only (re)populate `doom-packages'. -Use this before any of package.el, quelpa or Doom's package management's API to -ensure all the necessary package metadata is initialized and available for -them." - (let ((load-prefer-newer t)) ; reduce stale code issues - ;; package.el and quelpa handle themselves if their state changes during the - ;; current session, but if you change a packages.el file in a module, - ;; there's no non-trivial way to detect that, so to reload only - ;; `doom-packages' pass 'internal as FORCE-P or use `doom/reload-packages'. - (unless (eq force-p 'internal) - ;; `package-alist' - (when (or force-p (not (bound-and-true-p package-alist))) - (doom-ensure-packages-initialized 'force) - (setq load-path (cl-delete-if-not #'file-directory-p load-path))) - ;; `quelpa-cache' - (when (or force-p (not (bound-and-true-p quelpa-cache))) - ;; ensure un-byte-compiled version of quelpa is loaded - (unless (featurep 'quelpa) - (load (locate-library "quelpa.el") nil t t)) - (setq quelpa-initialized-p nil) - (or (quelpa-setup-p) - (error "Could not initialize quelpa")))) - ;; `doom-packages' - (when (or force-p (not doom-packages)) - (setq doom-packages (doom-package-list))))) - - -;; -;;; Package API - -(defun doom-ensure-packages-initialized (&optional force-p) - "Make sure package.el is initialized." +This ensure `doom-packages' is populated, if isn't aren't already. Use this +before any of straight's or Doom's package management's API to ensure all the +necessary package metadata is initialized and available for them." + (unless doom-init-packages-p + (setq force-p t)) (when (or force-p (not (bound-and-true-p package--initialized))) + (doom-log "Initializing package.el") (require 'package) - (setq package-activated-list nil - package--initialized nil) - (let (byte-compile-warnings) - (condition-case _ - (package-initialize) - ('error (package-refresh-contents) - (setq doom--refreshed-p t) - (package-initialize)))))) + (package-initialize)) + (when (or force-p (not doom-packages)) + (doom-log "Initializing straight") + (setq doom-init-packages-p t) + (unless (fboundp 'straight--reset-caches) + (doom-ensure-straight) + (require 'straight)) + (straight--reset-caches) + (mapc #'straight-use-recipes doom-core-package-sources) + (straight-register-package + `(straight :type git :host github + :repo ,(format "%s/straight.el" straight-repository-user) + :files ("straight*.el") + :branch ,straight-repository-branch + :no-byte-compile t)) + (mapc #'straight-use-package doom-core-packages) + (when noninteractive + (add-hook 'kill-emacs-hook #'doom--finalize-straight))) + (doom-log "Initializing doom-packages") + (setq doom-disabled-packages nil + doom-packages (doom-package-list)) + (cl-loop for (pkg . plist) in doom-packages + for ignored = (eval (plist-get plist :ignore) t) + for disabled = (eval (plist-get plist :disable) t) + if disabled + do (cl-pushnew pkg doom-disabled-packages) + else if (not ignored) + do (with-demoted-errors "Package error: %s" + (straight-register-package + (if-let (recipe (plist-get plist :recipe)) + (let ((plist (straight-recipes-retrieve pkg))) + `(,pkg ,@(doom-plist-merge recipe (cdr plist)))) + pkg))))) -(defun doom-ensure-core-packages () - "Make sure `doom-core-packages' are installed." - (when-let (core-packages (cl-remove-if #'package-installed-p doom-core-packages)) - (message "Installing core packages") - (unless doom--refreshed-p - (package-refresh-contents)) - (dolist (package core-packages) - (let ((inhibit-message t)) - (package-install package)) - (if (package-installed-p package) - (message "✓ Installed %s" package) - (error "✕ Couldn't install %s" package))) - (message "Installing core packages...done"))) +(defun doom-ensure-straight () + "Ensure `straight' is installed and was compiled with this version of Emacs." + (defvar bootstrap-version) + (let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of + ;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d. + (user-emacs-directory straight-base-dir) + (bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el")) + (bootstrap-version 5)) + (make-directory (doom-path straight-base-dir "straight/build") 'parents) + (unless (featurep 'straight) + (unless (or (require 'straight nil t) + (file-readable-p bootstrap-file)) + (with-current-buffer + (url-retrieve-synchronously + (format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el" + straight-repository-branch) + 'silent 'inhibit-cookies) + (goto-char (point-max)) + (eval-print-last-sexp))) + (load bootstrap-file nil t)))) ;; -;; Module package macros +;;; Module package macros -(cl-defmacro package! (name &rest plist &key built-in recipe pin disable ignore _freeze) +(cl-defmacro package! + (name &rest plist &key built-in _recipe disable ignore _freeze) "Declares a package and how to install it (if applicable). This macro is declarative and does not load nor install packages. It is used to @@ -162,9 +213,6 @@ Accepts the following properties: :recipe RECIPE Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example); for packages to be installed from external sources. - :pin ARCHIVE-NAME - Instructs ELPA to only look for this package in ARCHIVE-NAME. e.g. \"org\". - Ignored if RECIPE is present. :disable BOOL Do not install or update this package AND disable all of its `def-package!' blocks. @@ -179,63 +227,55 @@ Accepts the following properties: Returns t if package is successfully registered, and nil if it was disabled elsewhere." (declare (indent defun)) - (doom--assert-stage-p 'packages #'package!) (let ((old-plist (cdr (assq name doom-packages)))) - (when recipe - (when (cl-evenp (length recipe)) - (setq plist (plist-put plist :recipe (cons name recipe)))) - (setq pin nil - plist (plist-put plist :pin nil))) + ;; Add current module to :modules (let ((module-list (plist-get old-plist :modules)) - (module (or doom--current-module - (let ((file (FILE!))) - (cond ((file-in-directory-p file doom-private-dir) - (list :private)) - ((file-in-directory-p file doom-core-dir) - (list :core)) - ((doom-module-from-path file))))))) + (module (doom-module-from-path))) (unless (member module module-list) - (setq module-list (append module-list (list module) nil) - plist (plist-put plist :modules module-list)))) - (when built-in - (doom-log "Ignoring built-in package %S" name) - (when (equal built-in '(quote prefer)) - (setq built-in `(locate-library ,(symbol-name name) nil doom-site-load-path)))) - (setq plist (plist-put plist :ignore (or built-in ignore))) - (while plist - (unless (null (cadr plist)) - (setq old-plist (plist-put old-plist (car plist) (cadr plist)))) - (pop plist) - (pop plist)) + (plist-put! plist :modules + (append module-list + (list module) + nil)))) + + ;; Handle :built-in + (unless ignore + (when built-in + (doom-log "Ignoring built-in package %S" name) + (when (equal built-in '(quote prefer)) + (setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path)))) + (plist-put! plist :ignore built-in)) + + ;; DEPRECATED Translate :fetcher to :host + (with-plist! plist (recipe) + (with-plist! recipe (fetcher) + (when fetcher + (message "%s\n%s" + (format "WARNING: The :fetcher property was used for the %S package." + name) + "This property is deprecated. Replace it with :host.") + (plist-put! recipe :host fetcher) + (plist-delete! recipe :fetcher)) + (plist-put! plist :recipe recipe))) + + (doplist! ((prop val) plist) + (unless (null val) + (plist-put! old-plist prop val))) (setq plist old-plist) + + ;; TODO Add `straight-use-package-pre-build-function' support (macroexp-progn - (append (when pin - (doom-log "Pinning package '%s' to '%s'" name pin) - `((setf (alist-get ',name package-pinned-packages) ,pin))) - `((setf (alist-get ',name doom-packages) ',plist)) + (append `((setf (alist-get ',name doom-packages) ',plist)) (when disable - (doom-log "Disabling package '%s'" name) - `((add-to-list 'doom-disabled-packages ',name nil 'eq) + `((doom-log "Disabling package %S" ',name) + (add-to-list 'doom-disabled-packages ',name nil 'eq) nil)))))) -(defmacro packages! (&rest packages) - "A convenience macro for `package!' for declaring multiple packages at once. - -Only use this macro in a module's packages.el file." - (doom--assert-stage-p 'packages #'packages!) - (macroexp-progn - (cl-loop for desc in packages - collect (macroexpand `(package! ,@(doom-enlist desc)))))) - (defmacro disable-packages! (&rest packages) - "A convenience macro like `package!', but allows you to disable multiple -packages at once. - -Only use this macro in a module's packages.el file." - (doom--assert-stage-p 'packages #'disable-packages!) + "A convenience macro for disabling packages in bulk. +Only use this macro in a module's (or your private) packages.el file." (macroexp-progn - (cl-loop for pkg in packages - collect (macroexpand `(package! ,pkg :disable t))))) + (cl-loop for p in packages + collect `(package! ,p :disable t)))) (provide 'core-packages) ;;; core-packages.el ends here diff --git a/core/core-projects.el b/core/core-projects.el index 338ba28d8..c4bf12519 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -11,7 +11,8 @@ Emacs.") "If non-nil, non-projects are purged from the cache on `kill-emacs-hook'.") (defvar doom-projectile-fd-binary - (cl-find-if #'executable-find '("fd" "fdfind")) + (or (cl-find-if #'executable-find '("fd" "fdfind")) + "fd") "name of `fd-find' executable binary") (defvar doom-projectile-cache-timer-file (concat doom-cache-dir "projectile.timers") @@ -21,8 +22,8 @@ Emacs.") ;; ;;; Packages -(def-package! projectile - :after-call (after-find-file dired-before-readin-hook minibuffer-setup-hook) +(use-package! projectile + :after-call after-find-file dired-before-readin-hook minibuffer-setup-hook :commands (projectile-project-root projectile-project-name projectile-project-p @@ -44,59 +45,42 @@ Emacs.") (global-set-key [remap find-tag] #'projectile-find-tag) :config - (defun doom*projectile-cache-timers () - "Persist `projectile-projects-cache-time' across sessions, so that -`projectile-files-cache-expire' checks won't reset when restarting Emacs." - (projectile-serialize projectile-projects-cache-time doom-projectile-cache-timer-file)) - (advice-add #'projectile-serialize-cache :before #'doom*projectile-cache-timers) - ;; Restore it - (setq projectile-projects-cache-time (projectile-unserialize doom-projectile-cache-timer-file)) - - (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) (projectile-mode +1) ;; a more generic project root file (push ".project" projectile-project-root-files-bottom-up) (push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories) - (defun doom*projectile-default-generic-command (orig-fn &rest args) - "If projectile can't tell what kind of project you're in, it issues an error -when using many of projectile's command, e.g. `projectile-compile-command', -`projectile-run-project', `projectile-test-project', and -`projectile-configure-project', for instance. - -This suppresses the error so these commands will still run, but prompt you for -the command instead." - (ignore-errors (apply orig-fn args))) - (advice-add #'projectile-default-generic-command :around #'doom*projectile-default-generic-command) + ;; Treat current directory in dired as a "file in a project" and track it + (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) ;; Accidentally indexing big directories like $HOME or / will massively bloat ;; projectile's cache (into the hundreds of MBs). This purges those entries ;; when exiting Emacs to prevent slowdowns/freezing when cache files are ;; loaded or written to. - (defun doom|cleanup-project-cache () - "Purge projectile cache entries that: + (add-hook! 'kill-emacs-hook + (defun doom-cleanup-project-cache-h () + "Purge projectile cache entries that: a) have too many files (see `doom-projectile-cache-limit'), b) represent blacklisted directories that are too big, change too often or are private. (see `doom-projectile-cache-blacklist'), c) are not valid projectile projects." - (when (bound-and-true-p projectile-projects-cache) - (cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist) - for proot in (hash-table-keys projectile-projects-cache) - if (or (not (stringp proot)) - (>= (length (gethash proot projectile-projects-cache)) - doom-projectile-cache-limit) - (member (substring proot 0 -1) blacklist) - (and doom-projectile-cache-purge-non-projects - (not (doom-project-p proot)))) - do (doom-log "Removed %S from projectile cache" proot) - and do (remhash proot projectile-projects-cache) - and do (remhash proot projectile-projects-cache-time) - and do (remhash proot projectile-project-type-cache)) - (projectile-serialize-cache))) - (unless noninteractive - (add-hook 'kill-emacs-hook #'doom|cleanup-project-cache)) + (when (and (bound-and-true-p projectile-projects-cache) + (not noninteractive)) + (cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist) + for proot in (hash-table-keys projectile-projects-cache) + if (or (not (stringp proot)) + (>= (length (gethash proot projectile-projects-cache)) + doom-projectile-cache-limit) + (member (substring proot 0 -1) blacklist) + (and doom-projectile-cache-purge-non-projects + (not (doom-project-p proot)))) + do (doom-log "Removed %S from projectile cache" proot) + and do (remhash proot projectile-projects-cache) + and do (remhash proot projectile-projects-cache-time) + and do (remhash proot projectile-project-type-cache)) + (projectile-serialize-cache)))) ;; It breaks projectile's project root resolution if HOME is a project (e.g. ;; it's a git repo). In that case, we disable bottom-up root searching to @@ -111,21 +95,11 @@ c) are not valid projectile projects." projectile-project-root-files) projectile-project-root-files-bottom-up nil))) - ;; Projectile root-searching functions can cause an infinite loop on TRAMP - ;; connections, so disable them. - ;; TODO Is this still necessary? - (defun doom*projectile-locate-dominating-file (orig-fn file name) - "Don't traverse the file system if on a remote connection." - (when (and (stringp file) - (not (file-remote-p file nil t))) - (funcall orig-fn file name))) - (advice-add #'projectile-locate-dominating-file :around #'doom*projectile-locate-dominating-file) - (cond ;; If fd exists, use it for git and generic projects. fd is a rust program ;; that is significantly faster than git ls-files or find, and it respects ;; .gitignore. This is recommended in the projectile docs. - (doom-projectile-fd-binary + ((executable-find doom-projectile-fd-binary) (setq projectile-git-command (concat doom-projectile-fd-binary " . --color=never --type f -0 -H -E .git") @@ -143,10 +117,42 @@ c) are not valid projectile projects." projectile-indexing-method 'alien) ;; fix breakage on windows in git projects (unless (executable-find "tr") - (setq projectile-git-submodule-command nil))))) + (setq projectile-git-submodule-command nil)))) + + (defadvice! doom--projectile-cache-timers-a () + "Persist `projectile-projects-cache-time' across sessions, so that +`projectile-files-cache-expire' checks won't reset when restarting Emacs." + :before #'projectile-serialize-cache + (projectile-serialize projectile-projects-cache-time doom-projectile-cache-timer-file)) + ;; Restore it + (when (file-readable-p doom-projectile-cache-timer-file) + (setq projectile-projects-cache-time + (projectile-unserialize doom-projectile-cache-timer-file))) + + (defadvice! doom--projectile-default-generic-command-a (orig-fn &rest args) + "If projectile can't tell what kind of project you're in, it issues an error +when using many of projectile's command, e.g. `projectile-compile-command', +`projectile-run-project', `projectile-test-project', and +`projectile-configure-project', for instance. + +This suppresses the error so these commands will still run, but prompt you for +the command instead." + :around #'projectile-default-generic-command + (ignore-errors (apply orig-fn args))) + + ;; Projectile root-searching functions can cause an infinite loop on TRAMP + ;; connections, so disable them. + ;; TODO Is this still necessary? + (defadvice! doom--projectile-locate-dominating-file-a (orig-fn file name) + "Don't traverse the file system if on a remote connection." + :around #'projectile-locate-dominating-file + (when (and (stringp file) + (not (file-remote-p file nil t))) + (funcall orig-fn file name)))) + ;; -;; Project-based minor modes +;;; Project-based minor modes (defvar doom-project-hook nil "Hook run when a project is enabled. The name of the project's mode and its @@ -197,30 +203,54 @@ should be activated. If they are *all* true, NAME is activated. Relevant: `doom-project-hook'." (declare (indent 1)) (let ((init-var (intern (format "%s-init" name)))) - `(progn - ,(if on-load `(defvar ,init-var nil)) - (define-minor-mode ,name - "A project minor mode generated by `def-project-mode!'." - :init-value nil - :lighter "" - :keymap (make-sparse-keymap) - (if (not ,name) - ,on-exit - (run-hook-with-args 'doom-project-hook ',name ,name) - ,(when on-load - `(unless ,init-var - ,on-load - (setq ,init-var t))) - ,on-enter)) - ,@(cl-loop for hook in add-hooks - collect `(add-hook ',(intern (format "%s-hook" name)) - #',hook)) - ,(when (or modes match files when) - `(associate! ,name - :modes ,modes - :match ,match - :files ,files - :when ,when))))) + (macroexp-progn + (append + (when on-load + `((defvar ,init-var nil))) + `((define-minor-mode ,name + "A project minor mode generated by `def-project-mode!'." + :init-value nil + :lighter "" + :keymap (make-sparse-keymap) + (if (not ,name) + ,on-exit + (run-hook-with-args 'doom-project-hook ',name ,name) + ,(when on-load + `(unless ,init-var + ,on-load + (setq ,init-var t))) + ,on-enter)) + (dolist (hook ,add-hooks) + (add-hook ',(intern (format "%s-hook" name)) hook))) + (cond ((or files modes when) + (cl-check-type files (or null list string)) + (let ((fn + `(lambda () + (and (not (bound-and-true-p ,name)) + (and buffer-file-name (not (file-remote-p buffer-file-name nil t))) + ,(or (null match) + `(if buffer-file-name (string-match-p ,match buffer-file-name))) + ,(or (null files) + ;; Wrap this in `eval' to prevent eager expansion + ;; of `project-file-exists-p!' from pulling in + ;; autoloaded files prematurely. + `(eval + '(project-file-exists-p! + ,(if (stringp (car files)) (cons 'and files) files)))) + ,(or when t) + (,name 1))))) + (if modes + `((dolist (mode ,modes) + (let ((hook-name + (intern (format "doom--enable-%s%s-h" ',name + (if (eq mode t) "" (format "-in-%s" mode)))))) + (fset hook-name #',fn) + (if (eq mode t) + (add-to-list 'auto-minor-mode-magic-alist (cons hook-name #',name)) + (add-hook (intern (format "%s-hook" mode)) hook-name))))) + `((add-hook 'change-major-mode-after-body-hook #',fn))))) + (match + `((add-to-list 'auto-minor-mode-alist (cons ,match #',name))))))))) (provide 'core-projects) ;;; core-projects.el ends here diff --git a/core/core-ui.el b/core/core-ui.el index 424e65992..6dd2f1fed 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -51,10 +51,6 @@ examples. It is recommended you don't set specify a font-size, as to inherit `doom-font's size.") -(defvar doom--prefer-theme-elc nil - "If non-nil, `load-theme' will prefer the compiled theme (unlike its default -behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") - ;; ;;; Custom hooks @@ -85,8 +81,8 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (defvar doom--last-window nil) (defvar doom--last-frame nil) -(defun doom|run-switch-window-hooks () - (let ((gc-cons-threshold doom-gc-cons-upper-limit)) +(defun doom-run-switch-window-hooks-h () + (let ((gc-cons-threshold most-positive-fixnum)) (unless (or doom-inhibit-switch-window-hooks (eq doom--last-window (selected-window)) (minibufferp)) @@ -94,7 +90,7 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (run-hooks 'doom-switch-window-hook) (setq doom--last-window (selected-window)))))) -(defun doom|run-switch-frame-hooks (&rest _) +(defun doom-run-switch-frame-hooks-h (&rest _) (unless (or doom-inhibit-switch-frame-hooks (eq doom--last-frame (selected-frame)) (frame-parameter nil 'parent-frame)) @@ -102,8 +98,8 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (run-hooks 'doom-switch-frame-hook) (setq doom--last-frame (selected-frame))))) -(defun doom*run-switch-buffer-hooks (orig-fn buffer-or-name &rest args) - (let ((gc-cons-threshold doom-gc-cons-upper-limit)) +(defun doom-run-switch-buffer-hooks-a (orig-fn buffer-or-name &rest args) + (let ((gc-cons-threshold most-positive-fixnum)) (if (or doom-inhibit-switch-buffer-hooks (eq (current-buffer) (get-buffer buffer-or-name)) (and (eq orig-fn #'switch-to-buffer) (car args))) @@ -116,8 +112,8 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (run-hooks 'doom-switch-buffer-hook)) buffer))))) -(defun doom*run-switch-to-next-prev-buffer-hooks (orig-fn &rest args) - (let ((gc-cons-threshold doom-gc-cons-upper-limit)) +(defun doom-run-switch-to-next-prev-buffer-hooks-a (orig-fn &rest args) + (let ((gc-cons-threshold most-positive-fixnum)) (if doom-inhibit-switch-buffer-hooks (apply orig-fn args) (let ((doom-inhibit-switch-buffer-hooks t)) @@ -126,17 +122,11 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (run-hooks 'doom-switch-buffer-hook)) buffer))))) -(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." - (unless no-enable - (setq doom-theme theme) - (run-hooks 'doom-load-theme-hook))) - -(defun doom|protect-fallback-buffer () +(defun doom-protect-fallback-buffer-h () "Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'." (not (eq (current-buffer) (doom-fallback-buffer)))) -(defun doom|highlight-non-default-indentation () +(defun doom-highlight-non-default-indentation-h () "Highlight whitespace that doesn't match your `indent-tabs-mode' setting. e.g. If you indent with spaces by default, tabs will be highlighted. If you @@ -151,99 +141,236 @@ read-only or not file-visiting." (set (make-local-variable 'whitespace-style) (let ((style (if indent-tabs-mode '(indentation) '(tabs tab-mark)))) (if whitespace-mode - (cl-union style whitespace-style) - `(face ,@style)))) - (add-to-list 'whitespace-style 'face) + (cl-union style whitespace-active-style) + style))) + (cl-pushnew 'face whitespace-style) (whitespace-mode +1))) ;; -;;; General configuration +;;; General UX -(setq-default - ansi-color-for-comint-mode t - bidi-display-reordering nil ; disable bidirectional text for tiny performance boost - blink-matching-paren nil ; don't blink--too distracting - compilation-always-kill t ; kill compilation process before starting another - compilation-ask-about-save nil ; save all buffers on `compile' - compilation-scroll-output 'first-error - confirm-nonexistent-file-or-buffer t - confirm-kill-emacs #'doom-quit-p ; custom confirmation when killing Emacs - cursor-in-non-selected-windows nil ; hide cursors in other windows - custom-theme-directory (expand-file-name "themes/" doom-private-dir) - display-line-numbers-width 3 - echo-keystrokes 0.02 - enable-recursive-minibuffers nil - frame-inhibit-implied-resize t - frame-title-format '("%b – Doom Emacs") ; simple name in frame title - ;; remove continuation arrow on right fringe - fringe-indicator-alist - (delq (assq 'continuation fringe-indicator-alist) - fringe-indicator-alist) - highlight-nonselected-windows nil - image-animate-loop t - indicate-buffer-boundaries nil - indicate-empty-lines nil - max-mini-window-height 0.3 - mode-line-default-help-echo nil ; disable mode-line mouseovers - mouse-yank-at-point t ; middle-click paste at point, not at click - show-help-function nil ; hide :help-echo text - use-dialog-box nil ; always avoid GUI - uniquify-buffer-name-style 'forward - visible-cursor nil - x-stretch-cursor nil - ;; Favor vertical splits - split-width-threshold 160 - split-height-threshold nil - ;; `pos-tip' defaults - pos-tip-internal-border-width 6 - pos-tip-border-width 1 - ;; no beeping or blinking please - ring-bell-function #'ignore - visible-bell nil - ;; don't resize emacs in steps, it looks weird - window-resize-pixelwise t - frame-resize-pixelwise t) -;; y/n instead of yes/no -(fset #'yes-or-no-p #'y-or-n-p) -;; Truly silence startup message -(fset #'display-startup-echo-area-message #'ignore) -;; relegate tooltips to echo area only -(if (bound-and-true-p tooltip-mode) (tooltip-mode -1)) -;; enabled by default; no thanks, too distracting +;; Simpler confirmation prompt when killing Emacs +(setq confirm-kill-emacs #'doom-quit-p) + +(setq uniquify-buffer-name-style 'forward + ;; no beeping or blinking please + ring-bell-function #'ignore + visible-bell nil) + +;; middle-click paste at point, not at click +(setq mouse-yank-at-point t) + +;; Enable mouse in terminal Emacs +(add-hook 'tty-setup-hook #'xterm-mouse-mode) + + +;; +;;; Scrolling + +(setq hscroll-margin 2 + hscroll-step 1 + scroll-conservatively 10 + scroll-margin 0 + scroll-preserve-screen-position t + ;; mouse + mouse-wheel-scroll-amount '(5 ((shift) . 2)) + mouse-wheel-progressive-speed nil) ; don't accelerate scrolling + +;; Remove hscroll-margin in shells, otherwise it causes jumpiness +(setq-hook! '(eshell-mode-hook term-mode-hook) hscroll-margin 0) + +(when IS-MAC + ;; sane trackpad/mouse scroll settings + (setq mac-redisplay-dont-reset-vscroll t + mac-mouse-wheel-smooth-scroll nil)) + + +;; +;;; Cursor + +;; Don't blink the cursor, it's too distracting. (blink-cursor-mode -1) -;; Handle ansi codes in compilation buffer -(add-hook 'compilation-filter-hook #'doom|apply-ansi-color-to-compilation-buffer) -;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers. -(add-to-list 'default-frame-alist '(buffer-predicate . doom-buffer-frame-predicate)) -;; Prevent the glimpse of un-styled Emacs by setting these early. -(add-to-list 'default-frame-alist '(tool-bar-lines . 0)) -(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) -(add-to-list 'default-frame-alist '(vertical-scroll-bars)) -;; prompts the user for confirmation when deleting a non-empty frame -(global-set-key [remap delete-frame] #'doom/delete-frame) -;; don't resize minibuffer for large text -(setq resize-mini-windows nil) -;; Except when it's asking for input -(setq-hook! 'minibuffer-setup-hook resize-mini-windows 'grow-only) -;; Use `show-trailing-whitespace' instead of `whitespace-mode' because it's -;; faster (implemented in C). But try to only enable it in editing buffers. -(setq-default show-trailing-whitespace nil) -(setq-hook! '(prog-mode-hook text-mode-hook conf-mode-hook) show-trailing-whitespace t) +;; Don't blink the paren matching the one at point, it's too distracting. +(setq blink-matching-paren nil) + +(setq visible-cursor nil) + +;; Don't stretch the cursor to fit wide characters, it is disorienting, +;; especially for tabs. +(setq x-stretch-cursor nil) + + +;; +;;; Buffers + +;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers. +(push '(buffer-predicate . doom-buffer-frame-predicate) default-frame-alist) + +(setq confirm-nonexistent-file-or-buffer t) + +(defadvice! doom--switch-to-fallback-buffer-maybe-a (orig-fn) + "Switch to `doom-fallback-buffer' if on last real buffer. + +Advice for `kill-current-buffer'. If in a dedicated window, delete it. If there +are no real buffers left OR if all remaining buffers are visible in other +windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original +`kill-current-buffer'." + :around #'kill-current-buffer + (let ((buf (current-buffer))) + (cond ((window-dedicated-p) + (delete-window)) + ((eq buf (doom-fallback-buffer)) + (message "Can't kill the fallback buffer.")) + ((doom-real-buffer-p buf) + (if (and buffer-file-name + (buffer-modified-p buf) + (not (y-or-n-p + (format "Buffer %s is modified; kill anyway?" buf)))) + (message "Aborted") + (set-buffer-modified-p nil) + (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 (switch-to-prev-buffer nil t) (list buf 'nil))) + (switch-to-buffer (doom-fallback-buffer))) + (unless (delq (selected-window) (get-buffer-window-list buf nil t)) + (kill-buffer buf))) + (run-hooks 'buffer-list-update-hook))) + ((funcall orig-fn))))) + + +;; +;;; Fringes + +;; Reduce the clutter in the fringes; we'd like to reserve that space for more +;; useful information, like git-gutter and flycheck. +(setq indicate-buffer-boundaries nil + indicate-empty-lines nil) + +;; remove continuation arrow on right fringe +(delq! 'continuation fringe-indicator-alist 'assq) + + +;; +;;; Windows/frames + +;; A simple frame title +(setq frame-title-format '("%b – Doom Emacs") + icon-title-format frame-title-format) + +;; Don't resize emacs in steps, it looks weird. +(setq window-resize-pixelwise t + frame-resize-pixelwise t) + +(unless EMACS27+ ; We already do this in early-init.el + ;; Disable tool and scrollbars; Doom encourages keyboard-centric workflows, so + ;; these are just clutter (the scrollbar also impacts Emacs' performance). + (push '(menu-bar-lines . 0) default-frame-alist) + (push '(tool-bar-lines . 0) default-frame-alist) + (push '(vertical-scroll-bars) default-frame-alist)) + +;; Sets `ns-appearance' and `ns-transparent-titlebar' on GUI frames (and fixes +;; mismatching text color in the frame title) +(when IS-MAC + ;; Curse Lion and its sudden but inevitable fullscreen mode! + ;; NOTE Meaningless to railwaycat's emacs-mac build + (setq ns-use-native-fullscreen nil + ;; Visit files opened outside of Emacs in existing frame, rather than a + ;; new one + ns-pop-up-frames nil) + + ;; Sets ns-transparent-titlebar and ns-appearance frame parameters as is + ;; appropriate for the loaded theme. + (and (or (daemonp) + (display-graphic-p)) + (require 'ns-auto-titlebar nil t) + (ns-auto-titlebar-mode +1)) + + (add-hook! 'after-make-frame-functions + (defun doom-init-menu-bar-in-gui-frames-h (frame) + "On MacOS, the menu bar isn't part of the frame. Disabling it makes MacOS +treat Emacs as a non-application window." + (when (display-graphic-p frame) + (set-frame-parameter frame 'menu-bar-lines 1))))) ;; The native border "consumes" a pixel of the fringe on righter-most splits, ;; `window-divider' does not. Available since Emacs 25.1. -(setq-default window-divider-default-places t - window-divider-default-bottom-width 1 - window-divider-default-right-width 1) +(setq window-divider-default-places t + window-divider-default-bottom-width 1 + window-divider-default-right-width 1) (add-hook 'doom-init-ui-hook #'window-divider-mode) +;; Prompt the user for confirmation when deleting a non-empty frame +(global-set-key [remap delete-frame] #'doom/delete-frame) + +;; always avoid GUI +(setq use-dialog-box nil) +;; Don't display floating tooltips; display their contents in the echo-area. +(if (bound-and-true-p tooltip-mode) (tooltip-mode -1)) +;; native linux tooltips are ugly +(when IS-LINUX + (setq x-gtk-use-system-tooltips nil)) + + ;; Favor vertical splits over horizontal ones +(setq split-width-threshold 160 + split-height-threshold nil) + + +;; +;;; Minibuffer + +;; Allow for minibuffer-ception. Sometimes we need another minibuffer command +;; _while_ we're in the minibuffer. +(setq enable-recursive-minibuffers t) + +;; Show current key-sequence in minibuffer, like vim does. Any feedback after +;; typing is better UX than no feedback at all. +(setq echo-keystrokes 0.02) + +;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This +;; doesn't look too great with direnv, however... +(setq resize-mini-windows 'grow-only + ;; But don't let the minibuffer grow beyond this size + max-mini-window-height 0.15) + +;; Disable help mouse-overs for mode-line segments (i.e. :help-echo text). +;; They're generally unhelpful and only add confusing visual clutter. +(setq mode-line-default-help-echo nil + show-help-function nil) + +;; y/n is easier to type than yes/no +(fset #'yes-or-no-p #'y-or-n-p) + +;; Try really hard to keep the cursor from getting stuck in the read-only prompt +;; portion of the minibuffer. +(setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) +(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) + ;; ;;; Built-in packages -(def-package! ediff +;;;###package ansi-color +(setq ansi-color-for-comint-mode t) + + +(use-package! compile + :defer t + :config + (setq compilation-always-kill t ; kill compilation process before starting another + compilation-ask-about-save nil ; save all buffers on `compile' + compilation-scroll-output 'first-error) + ;; Handle ansi codes in compilation buffer + (add-hook 'compilation-filter-hook #'doom-apply-ansi-color-to-compilation-buffer-h)) + + +(use-package! ediff :defer t :init (setq ediff-diff-options "-w" ; turn off whitespace checking @@ -252,23 +379,21 @@ read-only or not file-visiting." :config (defvar doom--ediff-saved-wconf nil) ;; Restore window config after quitting ediff - (defun doom|ediff-save-wconf () - (setq doom--ediff-saved-wconf (current-window-configuration))) - (add-hook 'ediff-before-setup-hook #'doom|ediff-save-wconf) - - (defun doom|ediff-restore-wconf () - (when (window-configuration-p doom--ediff-saved-wconf) - (set-window-configuration doom--ediff-saved-wconf))) - (add-hook 'ediff-quit-hook #'doom|ediff-restore-wconf 'append) - (add-hook 'ediff-suspend-hook #'doom|ediff-restore-wconf 'append)) + (add-hook! 'ediff-before-setup-hook + (defun doom-ediff-save-wconf-h () + (setq doom--ediff-saved-wconf (current-window-configuration)))) + (add-hook! '(ediff-quit-hook ediff-suspend-hook) :append + (defun doom-ediff-restore-wconf-h () + (when (window-configuration-p doom--ediff-saved-wconf) + (set-window-configuration doom--ediff-saved-wconf))))) -(def-package! hl-line +(use-package! hl-line ;; Highlights the current line :hook ((prog-mode text-mode conf-mode) . hl-line-mode) :config - ;; I don't need hl-line showing in other windows. This also offers a small - ;; speed boost when buffer is displayed in multiple windows. + ;; Not having to render the hl-line overlay in multiple buffers offers a tiny + ;; performance boost. I also don't need to see it in other buffers. (setq hl-line-sticky-flag nil global-hl-line-sticky-flag nil) @@ -276,29 +401,27 @@ read-only or not file-visiting." ;; selection region harder to see while in evil visual mode. (after! evil (defvar doom-buffer-hl-line-mode nil) - - (defun doom|disable-hl-line () - (when hl-line-mode - (setq-local doom-buffer-hl-line-mode t) - (hl-line-mode -1))) - (add-hook 'evil-visual-state-entry-hook #'doom|disable-hl-line) - - (defun doom|enable-hl-line-maybe () - (when doom-buffer-hl-line-mode - (hl-line-mode +1))) - (add-hook 'evil-visual-state-exit-hook #'doom|enable-hl-line-maybe))) + (add-hook! 'evil-visual-state-entry-hook + (defun doom-disable-hl-line-h () + (when hl-line-mode + (setq-local doom-buffer-hl-line-mode t) + (hl-line-mode -1)))) + (add-hook! 'evil-visual-state-exit-hook + (defun doom-enable-hl-line-maybe-h () + (when doom-buffer-hl-line-mode + (hl-line-mode +1)))))) -(def-package! winner +(use-package! winner ;; undo/redo changes to Emacs' window layout - :after-call (after-find-file doom-switch-window-hook) + :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 +(use-package! paren ;; highlight matching delimiters - :after-call (after-find-file doom-switch-buffer-hook) + :after-call after-find-file doom-switch-buffer-hook :config (setq show-paren-delay 0.1 show-paren-highlight-openparen t @@ -316,56 +439,63 @@ read-only or not file-visiting." (newline-mark ?\n [?¬ ?\n]) (space-mark ?\ [?·] [?.]))) -;; Disable these because whitespace should be customized programmatically -;; (through `whitespace-style'), and not through these commands. -(put 'whitespace-toggle-options 'disabled t) -(put 'global-whitespace-toggle-options 'disabled t) - ;; ;;; Third party packages -(def-package! all-the-icons - :commands (all-the-icons-octicon all-the-icons-faicon all-the-icons-fileicon - all-the-icons-wicon all-the-icons-material all-the-icons-alltheicon) +(use-package! all-the-icons + :commands (all-the-icons-octicon + all-the-icons-faicon + all-the-icons-fileicon + all-the-icons-wicon + all-the-icons-material + all-the-icons-alltheicon) :init - (defun doom*disable-all-the-icons-in-tty (orig-fn &rest args) - (if (display-graphic-p) + (defadvice! doom--disable-all-the-icons-in-tty-a (orig-fn &rest args) + "Return a blank string in tty Emacs, which doesn't support multiple fonts." + :around '(all-the-icons-octicon all-the-icons-material + all-the-icons-faicon all-the-icons-fileicon + all-the-icons-wicon all-the-icons-alltheicon) + (if (display-multi-font-p) (apply orig-fn args) - "")) - :config - ;; all-the-icons doesn't work in the terminal, so we "disable" it. - (dolist (fn '(all-the-icons-octicon all-the-icons-material - all-the-icons-faicon all-the-icons-fileicon - all-the-icons-wicon all-the-icons-alltheicon)) - (advice-add fn :around #'doom*disable-all-the-icons-in-tty))) + ""))) ;;;###package hide-mode-line-mode -(add-hook 'completion-list-mode-hook #'hide-mode-line-mode) -(add-hook 'Man-mode-hook #'hide-mode-line-mode) +(add-hook! '(completion-list-mode-hook Man-mode-hook) + #'hide-mode-line-mode) ;; Better fontification of number literals in code -(def-package! highlight-numbers +(use-package! highlight-numbers :hook ((prog-mode conf-mode) . highlight-numbers-mode) :config (setq highlight-numbers-generic-regexp "\\_<[[:digit:]]+\\(?:\\.[0-9]*\\)?\\_>")) +;;;###package image +(setq image-animate-loop t) + ;;;###package rainbow-delimiters ;; Helps us distinguish stacked delimiter pairs, especially in parentheses-drunk ;; languages like Lisp. (setq rainbow-delimiters-max-face-count 3) +;;;###package pos-tip +(setq pos-tip-internal-border-width 6 + pos-tip-border-width 1) + ;; ;;; Line numbers +(setq-default display-line-numbers-width 3) + ;; line numbers in most modes -(add-hook! (prog-mode text-mode conf-mode) #'display-line-numbers-mode) +(add-hook! '(prog-mode-hook text-mode-hook conf-mode-hook) + #'display-line-numbers-mode) -(defun doom|enable-line-numbers () (display-line-numbers-mode +1)) -(defun doom|disable-line-numbers () (display-line-numbers-mode -1)) +(defun doom-enable-line-numbers-h () (display-line-numbers-mode +1)) +(defun doom-disable-line-numbers-h () (display-line-numbers-mode -1)) -;; `nlinum' is used for Emacs 25 users, as Emacs 26+ has native line numbers. -(def-package! nlinum +;; DEPRECATED `nlinum' is used for Emacs 25 users; 26+ has native line numbers. +(use-package! nlinum ;; Line number column. A faster (or equivalent, in the worst case) line number ;; plugin than `linum-mode'. :unless EMACS26+ @@ -407,14 +537,14 @@ character that looks like a space that `whitespace-mode' won't affect.") str)) (setq nlinum-format-function #'doom-nlinum-format-fn) - (defun doom|init-nlinum-width () - "Calculate line number column width beforehand (optimization)." - (setq nlinum--width - (length (save-excursion (goto-char (point-max)) - (format-mode-line "%l"))))) - (add-hook 'nlinum-mode-hook #'doom|init-nlinum-width)) + (add-hook! 'nlinum-mode-hook + (defun doom-init-nlinum-width-h () + "Calculate line number column width beforehand (optimization)." + (setq nlinum--width + (length (save-excursion (goto-char (point-max)) + (format-mode-line "%l"))))))) -(def-package! nlinum-hl +(use-package! nlinum-hl ;; Fixes disappearing line numbers in nlinum and other quirks :unless EMACS26+ :after nlinum @@ -430,7 +560,7 @@ character that looks like a space that `whitespace-mode' won't affect.") ;; forces them to resize. (add-hook 'after-setting-font-hook #'nlinum-hl-flush-all-windows)) -(def-package! nlinum-relative +(use-package! nlinum-relative :unless EMACS26+ :defer t :config @@ -441,7 +571,14 @@ character that looks like a space that `whitespace-mode' won't affect.") ;; ;;; Theme & font -(defun doom|init-fonts () +;; Underline looks a bit better when drawn lower +(setq x-underline-at-descent-line t) + +(defvar doom--prefer-theme-elc nil + "If non-nil, `load-theme' will prefer the compiled theme (unlike its default +behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.") + +(defun doom-init-fonts-h () "Loads fonts. Fonts are specified by `doom-font', `doom-variable-pitch-font', @@ -458,9 +595,11 @@ Fonts are specified by `doom-font', `doom-variable-pitch-font', ((display-graphic-p) (setq doom-font (face-attribute 'default :font)))) (when doom-serif-font - (set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font)) + (set-face-attribute 'fixed-pitch-serif t :font doom-serif-font)) (when doom-variable-pitch-font - (set-face-attribute 'variable-pitch nil :font doom-variable-pitch-font))) + (set-face-attribute 'variable-pitch t :font doom-variable-pitch-font)) + (when (and doom-unicode-font (fboundp 'set-fontset-font)) + (set-fontset-font t 'unicode doom-unicode-font nil 'prepend))) ((debug error) (if (string-prefix-p "Font not available: " (error-message-string e)) (lwarn 'doom-ui :warning @@ -468,56 +607,61 @@ Fonts are specified by `doom-font', `doom-variable-pitch-font', (font-get (caddr e) :family)) (signal 'doom-error e))))) -(defun doom|init-emoji-fonts (frame) - "Set up unicode fonts (if `doom-unicode-font' is set). - -By default, this uses Apple Color Emoji on MacOS and Symbola on Linux." - (when doom-unicode-font - (set-fontset-font t 'unicode doom-unicode-font frame 'prepend))) - -(defun doom|init-theme (&optional frame) +(defun doom-init-theme-h (&optional frame) "Load the theme specified by `doom-theme' in FRAME." (when (and doom-theme (not (memq doom-theme custom-enabled-themes))) (with-selected-frame (or frame (selected-frame)) (let ((doom--prefer-theme-elc t)) (load-theme doom-theme t))))) +(defadvice! doom--run-load-theme-hooks-a (theme &optional _no-confirm no-enable) + "Set up `doom-load-theme-hook' to run after `load-theme' is called." + :after-while #'load-theme + (unless no-enable + (setq doom-theme theme) + (run-hooks 'doom-load-theme-hook))) + +(defadvice! doom--prefer-compiled-theme-a (orig-fn &rest args) + "Make `load-theme' prioritize the byte-compiled theme for a moderate boost in +startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." + :around #'load-theme + (if (or (null after-init-time) + doom--prefer-theme-elc) + (cl-letf* ((old-locate-file (symbol-function 'locate-file)) + ((symbol-function 'locate-file) + (lambda (filename path &optional _suffixes predicate) + (funcall old-locate-file filename path '("c" "") predicate)))) + (apply orig-fn args)) + (apply orig-fn args))) + ;; ;;; Bootstrap -(defun doom|init-ui () +(defun doom-init-ui-h () "Initialize Doom's user interface by applying all its advice and hooks." (run-hook-wrapped 'doom-init-ui-hook #'doom-try-run-hook) - (add-to-list 'kill-buffer-query-functions #'doom|protect-fallback-buffer nil 'eq) - (add-hook 'after-change-major-mode-hook #'doom|highlight-non-default-indentation) + (add-to-list 'kill-buffer-query-functions #'doom-protect-fallback-buffer-h nil 'eq) + (add-hook 'after-change-major-mode-hook #'doom-highlight-non-default-indentation-h 'append) ;; 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-next-buffer switch-to-prev-buffer) - :around #'doom*run-switch-to-next-prev-buffer-hooks) - (advice-add! '(switch-to-buffer display-buffer) - :around #'doom*run-switch-buffer-hooks)) + (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) + (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) + (dolist (fn '(switch-to-next-buffer switch-to-prev-buffer)) + (advice-add fn :around #'doom-run-switch-to-next-prev-buffer-hooks-a)) + (dolist (fn '(switch-to-buffer display-buffer)) + (advice-add fn :around #'doom-run-switch-buffer-hooks-a))) ;; Apply `doom-theme' -(add-hook (if (daemonp) - 'after-make-frame-functions - 'doom-init-ui-hook) - #'doom|init-theme) +(add-hook 'doom-init-ui-hook #'doom-init-theme-h) ;; Apply `doom-font' et co -(add-hook 'doom-after-init-modules-hook #'doom|init-fonts) -;; Ensure unicode fonts are set on each frame -(add-hook 'after-make-frame-functions #'doom|init-emoji-fonts) -;; Setup `doom-load-theme-hook' and ensure `doom-theme' is always set to the -;; currently loaded theme -(advice-add #'load-theme :after #'doom*run-load-theme-hooks) +(add-hook 'doom-after-init-modules-hook #'doom-init-fonts-h) -(add-hook 'window-setup-hook #'doom|init-ui) +(add-hook 'window-setup-hook #'doom-init-ui-h) ;; @@ -527,39 +671,13 @@ By default, this uses Apple Color Emoji on MacOS and Symbola on Linux." (unless (fboundp 'define-fringe-bitmap) (defun define-fringe-bitmap (&rest _))) -(defun doom*prefer-compiled-theme (orig-fn &rest args) - "Make `load-theme' prioritize the byte-compiled theme for a moderate boost in -startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." - (if (or (null after-init-time) - doom--prefer-theme-elc) - (cl-letf* ((old-locate-file (symbol-function 'locate-file)) - ((symbol-function 'locate-file) - (lambda (filename path &optional _suffixes predicate) - (funcall old-locate-file filename path '("c" "") predicate)))) - (apply orig-fn args)) - (apply orig-fn args))) -(advice-add #'load-theme :around #'doom*prefer-compiled-theme) - (after! whitespace - (defun doom*disable-whitespace-mode-in-childframes (orig-fn) + (defun doom-disable-whitespace-mode-in-childframes-a (orig-fn) "`whitespace-mode' inundates child frames with whitspace markers, so disable it to fix all that visual noise." (unless (frame-parameter nil 'parent-frame) (funcall orig-fn))) - (add-function :around whitespace-enable-predicate #'doom*disable-whitespace-mode-in-childframes) - - (defun doom|disable-whitespace-mode-in-childframes (frame) - "`whitespace-mode' inundates child frames with whitspace markers, so disable -it to fix all that visual noise." - (when (frame-parameter frame 'parent-frame) - (with-selected-frame frame - (setq-local whitespace-style nil) - frame))) - (add-hook 'after-make-frame-functions #'doom|disable-whitespace-mode-in-childframes)) - -;; Don't allow cursor to enter the prompt -(setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) -(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) + (add-function :around whitespace-enable-predicate #'doom-disable-whitespace-mode-in-childframes-a)) ;; Don't display messages in the minibuffer when using the minibuffer (defmacro doom-silence-motion-key (command key) @@ -572,8 +690,5 @@ it to fix all that visual noise." (doom-silence-motion-key backward-delete-char "") (doom-silence-motion-key delete-char "") -;; Switch to `doom-fallback-buffer' if on last real buffer -(advice-add #'kill-current-buffer :around #'doom*switch-to-fallback-buffer-maybe) - (provide 'core-ui) ;;; core-ui.el ends here diff --git a/core/core.el b/core/core.el index 27f93f45e..5f57fb1dd 100644 --- a/core/core.el +++ b/core/core.el @@ -1,9 +1,10 @@ ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- -(eval-when-compile - (and (version< emacs-version "25.3") - (error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher" - emacs-version))) +(defvar doom-init-p nil + "Non-nil if Doom has been initialized.") + +(defvar doom-init-time nil + "The time it took, in seconds, for Doom Emacs to initialize.") (defvar doom-debug-mode (or (getenv "DEBUG") init-file-debug) "If non-nil, Doom will log more. @@ -11,10 +12,11 @@ Use `doom/toggle-debug-mode' to toggle it. The --debug-init flag and setting the DEBUG envvar will enable this at startup.") +(defvar doom-gc-cons-threshold 16777216 ; 16mb + "The default value to use for `gc-cons-threshold'. If you experience freezing, +decrease this. If you experience stuttering, increase this.") -;; ;;; Constants - (defconst doom-version "2.0.9" "Current version of Doom Emacs.") @@ -26,8 +28,7 @@ DEBUG envvar will enable this at startup.") (defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos))) (defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix))) - -;; +;;; Directories/files (defvar doom-emacs-dir (eval-when-compile (file-truename user-emacs-directory)) "The path to the currently loaded .emacs.d directory. Must end with a slash.") @@ -38,7 +39,9 @@ DEBUG envvar will enable this at startup.") (defvar doom-modules-dir (concat doom-emacs-dir "modules/") "The root directory for Doom's modules. Must end with a slash.") -(defvar doom-local-dir (concat doom-emacs-dir ".local/") +(defvar doom-local-dir + (or (getenv "DOOMLOCALDIR") + (concat doom-emacs-dir ".local/")) "Root directory for local storage. Use this as a storage location for this system's installation of Doom Emacs. @@ -56,7 +59,7 @@ dependencies or long-term shared data. Must end with a slash.") Use this for files that change often, like cache files. Must end with a slash.") -(defvar doom-packages-dir (concat doom-local-dir "packages/") +(defvar doom-elpa-dir (concat doom-local-dir "elpa/") "Where package.el and quelpa plugins (and their caches) are stored. Must end with a slash.") @@ -78,13 +81,13 @@ Defaults to ~/.config/doom, ~/.doom.d or the value of the DOOMDIR envvar; whichever is found first. Must end in a slash.") (defvar doom-autoload-file (concat doom-local-dir "autoloads.el") - "Where `doom-reload-doom-autoloads' stores its core autoloads. + "Where `doom-reload-core-autoloads' stores its core autoloads. This file is responsible for informing Emacs where to find all of Doom's autoloaded core functions (in core/autoload/*.el).") (defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el") - "Where `doom-reload-package-autoloads' stores its package.el autoloads. + "Where `doom-reload-package-autoloads' stores its package autoloads. This file is compiled from the autoloads files of all installed packages combined.") @@ -97,45 +100,12 @@ which is loaded at startup (if it exists). This is helpful if Emacs can't \(easily) be launched from the correct shell session (particularly for MacOS users).") +(defvar doom--initial-load-path (cons doom-core-dir load-path)) +(defvar doom--initial-process-environment process-environment) +(defvar doom--initial-exec-path exec-path) +(defvar doom--initial-file-name-handler-alist file-name-handler-alist) -;; -;;; Doom core variables - -(defvar doom-init-p nil - "Non-nil if Doom has been initialized.") - -(defvar doom-init-time nil - "The time it took, in seconds, for Doom Emacs to initialize.") - -(defvar doom-emacs-changed-p nil - "If non-nil, the running version of Emacs is different from the first time -Doom was setup, which may cause problems.") - -(defvar doom-site-load-path (cons doom-core-dir load-path) - "The initial value of `load-path', before it was altered by -`doom-initialize'.") - -(defvar doom-site-process-environment process-environment - "The initial value of `process-environment', before it was altered by -`doom-initialize'.") - -(defvar doom-site-exec-path exec-path - "The initial value of `exec-path', before it was altered by -`doom-initialize'.") - -(defvar doom-site-shell-file-name shell-file-name - "The initial value of `shell-file-name', before it was altered by -`doom-initialize'.") - -(defvar doom--last-emacs-file (concat doom-local-dir "emacs-version.el")) -(defvar doom--last-emacs-version nil) -(defvar doom--refreshed-p nil) -(defvar doom--stage 'init) - - -;; ;;; Custom error types - (define-error 'doom-error "Error in Doom Emacs core") (define-error 'doom-hook-error "Error in a Doom startup hook" 'doom-error) (define-error 'doom-autoload-error "Error in an autoloads file" 'doom-error) @@ -144,111 +114,168 @@ Doom was setup, which may cause problems.") (define-error 'doom-package-error "Error with packages" 'doom-error) -;; -;;; Custom hooks - -(defvar doom-reload-hook nil - "A list of hooks to run when `doom/reload' is called.") - - ;; ;;; Emacs core configuration +;; Ensure `doom-core-dir' is in `load-path' +(push doom-core-dir load-path) + +;; Reduce debug output, well, unless we've asked for it. +(setq debug-on-error doom-debug-mode + jka-compr-verbose doom-debug-mode) + ;; UTF-8 as the default coding system (when (fboundp 'set-charset-priority) - (set-charset-priority 'unicode)) ; pretty -(prefer-coding-system 'utf-8) ; pretty -(setq locale-coding-system 'utf-8) ; please + (set-charset-priority 'unicode)) ; pretty +(prefer-coding-system 'utf-8) ; pretty +(setq locale-coding-system 'utf-8) ; please +;; Except for the clipboard on Windows, where its contents could be in an +;; encoding that's wider than utf-8, so we let Emacs/the OS decide what encoding +;; to use. (unless IS-WINDOWS - (setq selection-coding-system 'utf-8)) ; with sugar on top + (setq selection-coding-system 'utf-8)) ; with sugar on top -(setq-default - ad-redefinition-action 'accept ; silence redefined function warnings - apropos-do-all t ; make `apropos' more useful - auto-mode-case-fold nil - autoload-compute-prefixes nil - debug-on-error doom-debug-mode - jka-compr-verbose doom-debug-mode ; silence compression messages - ffap-machine-p-known 'reject ; don't ping things that look like domain names - find-file-visit-truename t ; resolve symlinks when opening files - idle-update-delay 1 ; update ui slightly less often - ;; be quiet at startup; don't load or display anything unnecessary - inhibit-startup-message t - inhibit-startup-echo-area-message user-login-name - inhibit-default-init t - initial-major-mode 'fundamental-mode - initial-scratch-message nil - ;; History & backup settings (save nothing, that's what git is for) - auto-save-default nil - create-lockfiles nil - history-length 500 - make-backup-files nil ; don't create backup~ files - ;; byte compilation - byte-compile-verbose doom-debug-mode - byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local) - ;; security - gnutls-verify-error (not (getenv "INSECURE")) ; you shouldn't use this - tls-checktrust gnutls-verify-error - tls-program (list "gnutls-cli --x509cafile %t -p %p %h" - ;; compatibility fallbacks - "gnutls-cli -p %p %h" - "openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof") - ;; Don't store authinfo in plain text! - auth-sources (list (expand-file-name "authinfo.gpg" doom-etc-dir) - "~/.authinfo.gpg") - ;; Don't litter `doom-emacs-dir' - abbrev-file-name (concat doom-local-dir "abbrev.el") - async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") - auto-save-list-file-name (concat doom-cache-dir "autosave") - backup-directory-alist (list (cons "." (concat doom-cache-dir "backup/"))) - custom-file (concat doom-private-dir "init.el") - desktop-dirname (concat doom-etc-dir "desktop") - desktop-base-file-name "autosave" - desktop-base-lock-name "autosave-lock" - pcache-directory (concat doom-cache-dir "pcache/") - request-storage-directory (concat doom-cache-dir "request") - server-auth-dir (concat doom-cache-dir "server/") - shared-game-score-directory (concat doom-etc-dir "shared-game-score/") - tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/") - tramp-backup-directory-alist backup-directory-alist - tramp-persistency-file-name (concat doom-cache-dir "tramp-persistency.el") - url-cache-directory (concat doom-cache-dir "url/") - url-configuration-directory (concat doom-etc-dir "url/") - gamegrid-user-score-file-directory (concat doom-etc-dir "games/")) +;; Disable warnings from legacy advice system. They aren't useful, and we can't +;; often do anything about them besides changing packages upstream +(setq ad-redefinition-action 'accept) -(defun doom*symbol-file (orig-fn symbol &optional type) - "If a `doom-file' symbol property exists on SYMBOL, use that instead of the -original value of `symbol-file'." - (or (if (symbolp symbol) (get symbol 'doom-file)) - (funcall orig-fn symbol type))) -(advice-add #'symbol-file :around #'doom*symbol-file) +;; Make apropos omnipotent. It's more useful this way. +(setq apropos-do-all t) + +;; Don't make a second case-insensitive pass over `auto-mode-alist'. If it has +;; to, it's our (the user's) failure. One case for all! +(setq auto-mode-case-fold nil) + +;; Enable all disabled commands. +(setq disabled-command-function nil) + +;; Display the bare minimum at startup. We don't need all that noise. The +;; dashboard/empty scratch buffer is good enough. +(setq inhibit-startup-message t + inhibit-startup-echo-area-message user-login-name + inhibit-default-init t + initial-major-mode 'fundamental-mode + initial-scratch-message nil) +(fset #'display-startup-echo-area-message #'ignore) + +;; Emacs "updates" its ui more often than it needs to, so we slow it down +;; slightly, from 0.5s: +(setq idle-update-delay 1) + +;; Emacs is a huge security vulnerability, what with all the dependencies it +;; pulls in from all corners of the globe. Let's at least try to be more +;; discerning. +(setq gnutls-verify-error (not (getenv "INSECURE")) + tls-checktrust gnutls-verify-error + tls-program '("gnutls-cli --x509cafile %t -p %p %h" + ;; compatibility fallbacks + "gnutls-cli -p %p %h" + "openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof")) + +;; Emacs stores authinfo in HOME and in plaintext. Let's not do that, mkay? This +;; file usually stores usernames, passwords, and other such treasures for the +;; aspiring malicious third party. +(setq auth-sources (list (expand-file-name "authinfo.gpg" doom-etc-dir) + "~/.authinfo.gpg")) + +;; Emacs on Windows frequently confuses HOME (C:\Users\) and APPDATA, +;; causing `abbreviate-home-dir' to produce incorrect paths. +(when IS-WINDOWS + (setq abbreviated-home-dir "\\`'")) + +;; Don't litter `doom-emacs-dir' +(setq abbrev-file-name (concat doom-local-dir "abbrev.el") + async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") + bookmark-default-file (concat doom-etc-dir "bookmarks") + custom-file (concat doom-private-dir "init.el") + custom-theme-directory (concat doom-private-dir "themes/") + desktop-dirname (concat doom-etc-dir "desktop") + desktop-base-file-name "autosave" + desktop-base-lock-name "autosave-lock" + pcache-directory (concat doom-cache-dir "pcache/") + request-storage-directory (concat doom-cache-dir "request") + server-auth-dir (concat doom-cache-dir "server/") + shared-game-score-directory (concat doom-etc-dir "shared-game-score/") + tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/") + tramp-backup-directory-alist backup-directory-alist + tramp-persistency-file-name (concat doom-cache-dir "tramp-persistency.el") + url-cache-directory (concat doom-cache-dir "url/") + url-configuration-directory (concat doom-etc-dir "url/") + gamegrid-user-score-file-directory (concat doom-etc-dir "games/")) ;; -;;; Minor mode version of `auto-mode-alist' +;;; Optimizations -(defvar doom-auto-minor-mode-alist '() - "Alist mapping filename patterns to corresponding minor mode functions, like -`auto-mode-alist'. All elements of this alist are checked, meaning you can -enable multiple minor modes for the same regexp.") +;; Disable bidirectional text rendering for a modest performance boost. Of +;; course, this renders Emacs unable to detect/display right-to-left languages +;; (sorry!), but for us left-to-right language speakers/writers, it's a boon. +(setq-default bidi-display-reordering 'left-to-right) -(defun doom|enable-minor-mode-maybe () - "Check file name against `doom-auto-minor-mode-alist'." - (when (and buffer-file-name doom-auto-minor-mode-alist) - (let ((name buffer-file-name) - (remote-id (file-remote-p buffer-file-name)) - (alist doom-auto-minor-mode-alist)) - ;; Remove backup-suffixes from file name. - (setq name (file-name-sans-versions name)) - ;; Remove remote file name identification. - (when (and (stringp remote-id) - (string-match (regexp-quote remote-id) name)) - (setq name (substring name (match-end 0)))) - (while (and alist (caar alist) (cdar alist)) - (if (string-match-p (caar alist) name) - (funcall (cdar alist) 1)) - (setq alist (cdr alist)))))) -(add-hook 'find-file-hook #'doom|enable-minor-mode-maybe) +;; Reduce rendering/line scan work for Emacs by not rendering cursors or regions +;; in non-focused windows. +(setq-default cursor-in-non-selected-windows nil) +(setq highlight-nonselected-windows nil) + +;; More performant rapid scrolling over unfontified regions. May cause brief +;; spells of inaccurate fontification immediately after scrolling. +(setq fast-but-imprecise-scrolling t) + +;; Resizing the Emacs frame can be a terribly expensive part of changing the +;; font. By inhibiting this, we easily halve startup times with fonts that are +;; larger than the system default. +(setq frame-inhibit-implied-resize t) + +;; Don't ping things that look like domain names. +(setq ffap-machine-p-known 'reject) + +;; Performance on Windows is considerably worse than elsewhere. We'll need +;; everything we can get. +(when IS-WINDOWS + ;; Reduce the workload when doing file IO + (setq w32-get-true-file-attributes nil) + + ;; Font compacting can be terribly expensive, especially for rendering icon + ;; fonts on Windows. Whether it has a noteable affect on Linux and Mac hasn't + ;; been determined. + (setq inhibit-compacting-font-caches t)) + +;; Remove command line options that aren't relevant to our current OS; that +;; means less to process at startup. +(unless IS-MAC (setq command-line-ns-option-alist nil)) +(unless IS-LINUX (setq command-line-x-option-alist nil)) + +;; This is consulted on every `require', `load' and various path/io functions. +;; You get a minor speed up by nooping this. +(setq file-name-handler-alist nil) + +(defun doom-restore-file-name-handler-alist-h () + (setq file-name-handler-alist doom--initial-file-name-handler-alist)) + +(add-hook 'emacs-startup-hook #'doom-restore-file-name-handler-alist-h) + +;; To speed up minibuffer commands (like helm and ivy), we defer garbage +;; collection while the minibuffer is active. +(defun doom-defer-garbage-collection-h () + "TODO" + (setq gc-cons-threshold most-positive-fixnum)) + +(defun doom-restore-garbage-collection-h () + "TODO" + ;; Defer it so that commands launched immediately after will enjoy the + ;; benefits. + (run-at-time + 1 nil (lambda () (setq gc-cons-threshold doom-gc-cons-threshold)))) + +(add-hook 'minibuffer-setup-hook #'doom-defer-garbage-collection-h) +(add-hook 'minibuffer-exit-hook #'doom-restore-garbage-collection-h) + +;; Not restoring these to their defaults will cause stuttering/freezes. +(add-hook 'emacs-startup-hook #'doom-restore-garbage-collection-h) + +;; When Emacs loses focus seems like a great time to do some garbage collection +;; all sneaky breeky like, so we can return a fresh(er) Emacs. +(add-hook 'focus-out-hook #'garbage-collect) ;; @@ -257,27 +284,21 @@ enable multiple minor modes for the same regexp.") ;; File+dir local variables are initialized after the major mode and its hooks ;; have run. If you want hook functions to be aware of these customizations, add ;; them to MODE-local-vars-hook instead. -(defun doom|run-local-var-hooks () +(defun doom-run-local-var-hooks-h () "Run MODE-local-vars-hook after local variables are initialized." (run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode)) #'doom-try-run-hook)) -(add-hook 'hack-local-variables-hook #'doom|run-local-var-hooks) +(add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) ;; If `enable-local-variables' is disabled, then `hack-local-variables-hook' is ;; never triggered. -(defun doom|run-local-var-hooks-if-necessary () - "Run `doom|run-local-var-hooks' if `enable-local-variables' is disabled." +(defun doom-run-local-var-hooks-if-necessary-h () + "Run `doom-run-local-var-hooks-h' if `enable-local-variables' is disabled." (unless enable-local-variables - (doom|run-local-var-hooks))) -(add-hook 'after-change-major-mode-hook #'doom|run-local-var-hooks-if-necessary 'append) - -(defun doom|create-non-existent-directories () - "Automatically create missing directories when creating new files." - (let ((parent-directory (file-name-directory buffer-file-name))) - (when (and (not (file-exists-p parent-directory)) - (y-or-n-p (format "Directory `%s' does not exist! Create it?" parent-directory))) - (make-directory parent-directory t)))) -(add-hook 'find-file-not-found-functions #'doom|create-non-existent-directories) + (doom-run-local-var-hooks-h))) +(add-hook 'after-change-major-mode-hook + #'doom-run-local-var-hooks-if-necessary-h + 'append) ;; @@ -298,7 +319,7 @@ broken up into: This is already done by the lang/org module, however. If you want to disable incremental loading altogether, either remove -`doom|load-packages-incrementally' from `emacs-startup-hook' or set +`doom-load-packages-incrementally-h' from `emacs-startup-hook' or set `doom-incremental-first-idle-timer' to nil.") (defvar doom-incremental-first-idle-timer 2 @@ -317,13 +338,19 @@ intervals." (if (not now) (nconc doom-incremental-packages packages) (when packages - (let ((gc-cons-threshold doom-gc-cons-upper-limit) - (reqs (cl-delete-if #'featurep packages)) - file-name-handler-alist) - (when-let (req (if reqs (ignore-errors (pop reqs)))) + (let ((gc-cons-threshold most-positive-fixnum) + (file-name-handler-alist nil) + (reqs (cl-delete-if #'featurep packages))) + (when-let (req (if reqs (pop reqs))) (doom-log "Incrementally loading %s" req) (condition-case e - (or (while-no-input (require req nil t) t) + (or (while-no-input + ;; If `default-directory' is a directory that doesn't exist + ;; or is unreadable, Emacs throws up file-missing errors, so + ;; we set it to a directory we know exists and is readable. + (let ((default-directory doom-emacs-dir)) + (require req nil t)) + t) (push req reqs)) ((error debug) (message "Failed to load '%s' package incrementally, because: %s" @@ -334,7 +361,7 @@ intervals." reqs t) (doom-log "Finished incremental loading"))))))) -(defun doom|load-packages-incrementally () +(defun doom-load-packages-incrementally-h () "Begin incrementally loading packages in `doom-incremental-packages'. If this is a daemon session, load them all immediately instead." @@ -345,7 +372,7 @@ If this is a daemon session, load them all immediately instead." nil #'doom-load-packages-incrementally (cdr doom-incremental-packages) t)))) -(add-hook 'window-setup-hook #'doom|load-packages-incrementally) +(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) ;; @@ -364,73 +391,34 @@ Meant to be used with `run-hook-wrapped'." ;; return nil so `run-hook-wrapped' won't short circuit nil) -(defun doom-ensure-same-emacs-version-p () - "Check if the running version of Emacs has changed and set -`doom-emacs-changed-p' if it has." - (if (load doom--last-emacs-file 'noerror 'nomessage 'nosuffix) - (setq doom-emacs-changed-p - (not (equal emacs-version doom--last-emacs-version))) - (with-temp-file doom--last-emacs-file - (princ `(setq doom--last-emacs-version ,(prin1-to-string emacs-version)) - (current-buffer)))) - (cond ((not doom-emacs-changed-p)) - ((y-or-n-p - (format - (concat "Your version of Emacs has changed from %s to %s, which may cause incompatibility\n" - "issues. If you run into errors, run `bin/doom compile :plugins` or reinstall your\n" - "plugins to resolve them.\n\n" - "Continue?") - doom--last-emacs-version - emacs-version)) - (delete-file doom--last-emacs-file)) - (noninteractive (error "Aborting")) - ((kill-emacs)))) - -(defun doom-ensure-core-directories-exist () - "Make sure all Doom's essential local directories (in and including -`doom-local-dir') exist." - (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir)) - (unless (file-directory-p dir) - (make-directory dir t)))) - -(defun doom|display-benchmark (&optional return-p) +(defun doom-display-benchmark-h (&optional return-p) "Display a benchmark, showing number of packages and modules, and how quickly they were loaded at startup. If RETURN-P, return the message as a string instead of displaying it." (funcall (if return-p #'format #'message) - "Doom loaded %s packages across %d modules in %.03fs" - (length package-activated-list) + "Doom loaded %d packages across %d modules in %.03fs" + (- (length load-path) (length doom--initial-load-path)) (if doom-modules (hash-table-count doom-modules) 0) (or doom-init-time (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) -(defun doom|run-all-startup-hooks () - "Run all startup Emacs hooks. Meant to be executed after starting Emacs with --q or -Q, for example: - - emacs -Q -l init.el -f doom|run-all-startup-hooks" - (run-hook-wrapped 'after-init-hook #'doom-try-run-hook) - (setq after-init-time (current-time)) - (dolist (hook (list 'delayed-warnings-hook - 'emacs-startup-hook 'term-setup-hook - 'window-setup-hook)) - (run-hook-wrapped hook #'doom-try-run-hook))) - -(defun doom-initialize-autoloads (file) +(defun doom-load-autoloads-file (file) "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) + (let (command-switch-alist) + (load (substring file 0 -3) 'noerror 'nomessage)) ((debug error) (if noninteractive (message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) (signal 'doom-autoload-error (list (file-name-nondirectory file) e)))))) -(defun doom-load-env-vars (file) +(defun doom-load-envvars-file (file &optional noerror) "Read and set envvars in FILE." (if (not (file-readable-p file)) - (doom-log "Couldn't read %S envvar file" file) + (unless noerror + (signal 'file-error (list "Couldn't read envvar file" file))) (with-temp-buffer (insert-file-contents file) (search-forward "\n\n" nil t) @@ -443,11 +431,12 @@ in interactive sessions, nil otherwise (but logs a warning)." (line-beginning-position)) (point-max)))))) (setenv var value))))) - (setq exec-path (append (split-string (getenv "PATH") - (if IS-WINDOWS ";" ":")) - (list exec-directory)) - shell-file-name (or (getenv "SHELL") - shell-file-name)) + (setq-default + exec-path (append (split-string (getenv "PATH") + (if IS-WINDOWS ";" ":")) + (list exec-directory)) + shell-file-name (or (getenv "SHELL") + shell-file-name)) t)) (defun doom-initialize (&optional force-p) @@ -479,66 +468,79 @@ The overall load order of Doom is as follows: 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)." - (add-to-list 'load-path doom-core-dir) - (require 'core-lib) - (when (or force-p (not doom-init-p)) - (setq doom-init-p t) ; Prevent infinite recursion + (setq doom-init-p t) - ;; Reset as much state as possible - (setq exec-path doom-site-exec-path - load-path doom-site-load-path - process-environment doom-site-process-environment - shell-file-name doom-site-shell-file-name) + ;; Reset as much state as possible, so `doom-initialize' can be treated like + ;; a reset function. Particularly useful for reloading the config. + (setq exec-path doom--initial-exec-path + load-path doom--initial-load-path + process-environment doom--initial-process-environment) - ;; `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-exist) - (doom-ensure-same-emacs-version-p) + (require 'core-lib) + (require 'core-modules) - (require 'core-packages) - (doom-ensure-packages-initialized force-p) - (doom-ensure-core-packages) + ;; Load shell environment, optionally generated from 'doom env' + (when (and (or (display-graphic-p) + (daemonp)) + (file-exists-p doom-env-file)) + (doom-load-envvars-file doom-env-file)) - (unless (or force-p noninteractive) - (user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them"))) + (let (;; `doom-autoload-file' tells Emacs where to load all its functions + ;; from. This includes everything in core/autoload/*.el and autoload + ;; files in enabled modules. + (core-autoloads-p (doom-load-autoloads-file doom-autoload-file)) + ;; Loads `doom-package-autoload-file', which loads a concatenated + ;; package autoloads file which caches `load-path', `auto-mode-alist', + ;; `Info-directory-list', and `doom-disabled-packages'. A big + ;; reduction in startup time. + (pkg-autoloads-p + (unless noninteractive + (doom-load-autoloads-file doom-package-autoload-file)))) - ;; 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. - (let (command-switch-alist) - (unless (or force-p - (doom-initialize-autoloads doom-package-autoload-file) + (if (and core-autoloads-p (not force-p)) + ;; In case we want to use package.el or straight via M-x + (progn + (with-eval-after-load 'package + (require 'core-packages)) + (with-eval-after-load 'straight + (require 'core-packages) + (doom-initialize-packages))) + + ;; Eagerly load these libraries because this module may be loaded in a session + ;; that hasn't been fully initialized (where autoloads files haven't been + ;; generated or `load-path' populated). + (mapc (doom-rpartial #'load 'noerror 'nomessage) + (file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) + + ;; Create all our core directories to quell file errors + (dolist (dir (list doom-local-dir + doom-etc-dir + doom-cache-dir + doom-elpa-dir)) + (unless (file-directory-p dir) + (make-directory dir 'parents))) + + ;; Ensure the package management system (and straight) are ready for + ;; action (and all core packages/repos are installed) + (require 'core-packages) + (doom-initialize-packages force-p)) + + (unless (or (and core-autoloads-p pkg-autoloads-p) + force-p noninteractive) - (user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them"))) + (unless core-autoloads-p + (message "Your Doom core autoloads file is missing")) + (unless pkg-autoloads-p + (message "Your package autoloads file is missing")) + (user-error "Run `bin/doom refresh' to generate them"))))) - ;; Load shell environment - (unless noninteractive - (doom-load-env-vars doom-env-file))) - - (require 'core-modules) - (require 'core-os) - (if noninteractive - (require 'core-cli) - (add-hook 'window-setup-hook #'doom|display-benchmark) - (require 'core-keybinds) - (require 'core-ui) - (require 'core-projects) - (require 'core-editor))) - - -;; -;;; Bootstrap Doom - -(doom-initialize noninteractive) -(unless noninteractive - (doom-initialize-modules)) -(with-eval-after-load 'package - (require 'core-packages) - (doom-initialize-packages)) +(defun doom-initialize-core () + "Load Doom's core files for an interactive session." + (require 'core-keybinds) + (require 'core-ui) + (require 'core-projects) + (require 'core-editor)) (provide 'core) ;;; core.el ends here diff --git a/core/doctor.el b/core/doctor.el index 3a611a1cd..1a5f5fc0e 100644 --- a/core/doctor.el +++ b/core/doctor.el @@ -18,7 +18,7 @@ (/ size 1024)) (explain! "Consider deleting it from your system (manually)")))) -(unless (executable-find doom-projectile-fd-binary) +(unless (ignore-errors (executable-find doom-projectile-fd-binary)) (warn! "Couldn't find the `fd' binary; project file searches will be slightly slower") (unless (executable-find "rg") (warn! "Couldn't find the `rg' binary either; project file searches will be even slower"))) diff --git a/core/packages.el b/core/packages.el index 4e1f28a17..3cef86642 100644 --- a/core/packages.el +++ b/core/packages.el @@ -3,12 +3,7 @@ ;; core.el (package! dotenv-mode) - -;; core-os.el -(if (not IS-MAC) - (package! xclip) - (package! osx-clipboard) - (package! ns-auto-titlebar)) +(package! auto-minor-mode) ;; core-ui.el (package! all-the-icons) @@ -26,10 +21,16 @@ (package! command-log-mode) (package! dtrt-indent) (package! helpful) +(package! ns-auto-titlebar :ignore (not IS-MAC)) (package! pcre2el) (package! smartparens) +(package! so-long + :built-in 'prefer + :recipe (:repo "https://git.savannah.gnu.org/git/so-long.git")) +(package! osx-clipboard :ignore (not IS-MAC)) (package! undo-tree) (package! ws-butler) +(package! xclip :ignore IS-LINUX) ;; core-projects.el (package! projectile) @@ -37,13 +38,6 @@ ;; core-keybinds.el (package! general) (package! which-key) -(package! hydra) - -;; core-packages.el -(package! gnu-elpa-keyring-update) ;; autoload/debug.el (package! esup) - -;; cli/test.el -(package! buttercup) diff --git a/core/test/helpers.el b/core/test/helpers.el new file mode 100644 index 000000000..89b1401db --- /dev/null +++ b/core/test/helpers.el @@ -0,0 +1,9 @@ +;; -*- no-byte-compile: t; -*- +;;; core/test/helpers.el + +(defmacro insert!! (&rest text) + "Insert TEXT in buffer, then move cursor to last {0} marker." + `(progn + (insert ,@text) + (when (search-backward "{0}" nil t) + (replace-match "" t t)))) diff --git a/init.test.el b/core/test/init.el similarity index 59% rename from init.test.el rename to core/test/init.el index b59d71568..ff1c81b0c 100644 --- a/init.test.el +++ b/core/test/init.el @@ -1,4 +1,6 @@ -;;; init.test.el -- for automated unit tests -*- lexical-binding: t; -*- +;;; core/test/init.el -*- lexical-binding: t; no-byte-compile: t; -*- + +;; An init.el for our unit test suites. Do not use this! (doom! :completion company diff --git a/core/test/test-core.el b/core/test/test-core.el index 1a3fc80fc..07361463a 100644 --- a/core/test/test-core.el +++ b/core/test/test-core.el @@ -62,13 +62,15 @@ (setq a (switch-to-buffer (get-buffer-create "a")) b (get-buffer-create "b")) (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)) + (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) + (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) + (dolist (fn '(switch-to-buffer display-buffer)) + (advice-add fn :around #'doom-run-switch-buffer-hooks-a))) (after-each - (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) + (remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) + (remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) + (dolist (fn '(switch-to-buffer display-buffer)) + (advice-remove fn #'doom-run-switch-buffer-hooks-a)) (kill-buffer a) (kill-buffer b)) diff --git a/docs/api.org b/docs/api.org new file mode 100644 index 000000000..7a55d6d26 --- /dev/null +++ b/docs/api.org @@ -0,0 +1,150 @@ +#+TITLE: API Demos + +This appendix serves as a reference on how to use Doom Emacs' standard library. +It is integrated into Helpful, in Doom. + +* add-hook! +#+BEGIN_SRC elisp :eval no +;; With only one hook and one function, this is identical to `add-hook'. In that +;; case, use that instead. +(add-hook! 'some-mode-hook #'enable-something) + +;; Adding many-to-many functions to hooks +(add-hook! some-mode #'enable-something #'and-another) +(add-hook! some-mode #'(enable-something and-another)) +(add-hook! '(one-mode-hook second-mode-hook) #'enable-something) +(add-hook! (one-mode second-mode) #'enable-something) + +;; Appending and local hooks +(add-hook! (one-mode second-mode) :append #'enable-something) +(add-hook! (one-mode second-mode) :local #'enable-something) + +;; With arbitrary forms +(add-hook! (one-mode second-mode) (setq v 5) (setq a 2)) +(add-hook! (one-mode second-mode) :append :local (setq v 5) (setq a 2)) + +;; Inline named hook functions +(add-hook! '(one-mode-hook second-mode-hook) + (defun do-something () + ...) + (defun do-another-thing () + ...)) +#+END_SRC + +* custom-theme-set-faces! +#+BEGIN_SRC elisp :eval no +(custom-theme-set-faces! 'doom-one-theme + '(outline-1 :weight normal) + '(outline-2 :weight normal) + '(outline-3 :weight normal) + '(outline-4 :weight normal) + '(outline-5 :weight normal) + '(outline-6 :weight normal) + '(default :background "red" :weight bold) + '(region :background "red" :weight bold)) + +(custom-theme-set-faces! '(doom-one-theme doom-one-light-theme) + '((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6) + :weight normal) + '((default region) + :background "red" :weight bold)) + +(let ((red-bg-faces '(default region))) + (custom-theme-set-faces! '(doom-one-theme doom-one-light-theme) + `(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i))) + :weight normal) + `(,red-bg-faces + :background "red" :weight bold))) +#+END_SRC + +* custom-set-faces! +#+BEGIN_SRC elisp :eval no +(custom-set-faces! + '(outline-1 :weight normal) + '(outline-2 :weight normal) + '(outline-3 :weight normal) + '(outline-4 :weight normal) + '(outline-5 :weight normal) + '(outline-6 :weight normal) + '(default :background "red" :weight bold) + '(region :background "red" :weight bold)) + +(custom-set-faces! + '((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6) + :weight normal) + '((default region) + :background "red" :weight bold)) + +(let ((red-bg-faces '(default region))) + (custom-set-faces! + `(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i))) + :weight normal) + `(,red-bg-faces + :background "red" :weight bold))) +#+END_SRC + +* doom! +#+BEGIN_SRC elisp :eval no +(doom! :completion + company + ivy + ;;helm + + :tools + (:if IS-MAC macos) + docker + lsp + + :lang + (cc +lsp) + (:cond ((string= system-name "work-pc") + python + rust + web) + ((string= system-name "writing-pc") + (org +dragndrop) + ruby)) + (:if IS-LINUX + (web +lsp) + web) + + :config + literate + (default +bindings +smartparens)) +#+END_SRC + +* file-exists-p! +#+BEGIN_SRC elisp +(file-exists-p! "init.el" doom-emacs-dir) +#+END_SRC + +#+RESULTS: +: /home/hlissner/.emacs.d/init.el + +#+BEGIN_SRC elisp +(file-exists-p! (and (or "doesnotexist" "init.el") + "LICENSE") + doom-emacs-dir) +#+END_SRC + +#+RESULTS: +: /home/hlissner/.emacs.d/LICENSE + +* remove-hook! +#+BEGIN_SRC elisp :eval no +;; With only one hook and one function, this is identical to `remove-hook'. In +;; that case, use that instead. +(remove-hook! 'some-mode-hook #'enable-something) + +;; Removing N functions from M hooks +(remove-hook! some-mode #'enable-something #'and-another) +(remove-hook! some-mode #'(enable-something and-another)) +(remove-hook! '(one-mode-hook second-mode-hook) #'enable-something) +(remove-hook! (one-mode second-mode) #'enable-something) + +;; Removing buffer-local hooks +(remove-hook! (one-mode second-mode) :local #'enable-something) + +;; Removing arbitrary forms (must be exactly the same as the definition) +(remove-hook! (one-mode second-mode) (setq v 5) (setq a 2)) +#+END_SRC diff --git a/early-init.el b/early-init.el index 8365ba3a8..a4b3682b8 100644 --- a/early-init.el +++ b/early-init.el @@ -4,17 +4,19 @@ ;; before package and UI initialization happens. ;; Defer garbage collection further back in the startup process -(setq gc-cons-threshold 268435456) +(setq gc-cons-threshold most-positive-fixnum) -;; Package initialize occurs automatically, before `user-init-file' is -;; loaded, but after `early-init-file'. Doom handles package -;; initialization, so we must prevent Emacs from doing it early! +;; In Emacs 27+, package initialization occurs before `user-init-file' is +;; loaded, but after `early-init-file'. Doom handles package initialization, so +;; we must prevent Emacs from doing it early! (setq package-enable-at-startup nil) -;; Prevent the glimpse of un-styled Emacs by setting these early. -(add-to-list 'default-frame-alist '(tool-bar-lines . 0)) -(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) -(add-to-list 'default-frame-alist '(vertical-scroll-bars)) +;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early. +(push '(menu-bar-lines . 0) default-frame-alist) +(push '(tool-bar-lines . 0) default-frame-alist) +(push '(vertical-scroll-bars) default-frame-alist) -;; One less file to load at startup -(setq site-run-file nil) +;; Resizing the Emacs frame can be a terribly expensive part of changing the +;; font. By inhibiting this, we easily halve startup times with fonts that are +;; larger than the system default. +(setq frame-inhibit-implied-resize t) diff --git a/init.el b/init.el index be1e83269..179629068 100644 --- a/init.el +++ b/init.el @@ -27,61 +27,33 @@ ;; ;;; License: MIT -(defvar doom-gc-cons-threshold 16777216 ; 16mb - "The default value to use for `gc-cons-threshold'. If you experience freezing, -decrease this. If you experience stuttering, increase this.") - -(defvar doom-gc-cons-upper-limit 536870912 ; 512mb - "The temporary value for `gc-cons-threshold' to defer it.") - - -(defvar doom--file-name-handler-alist file-name-handler-alist) - -(defun doom|restore-startup-optimizations () - "Resets garbage collection settings to reasonable defaults (a large -`gc-cons-threshold' can cause random freezes otherwise) and resets -`file-name-handler-alist'." - (setq file-name-handler-alist doom--file-name-handler-alist) - ;; Do this on idle timer to defer a possible GC pause that could result; also - ;; allows deferred packages to take advantage of these optimizations. - (run-with-idle-timer - 3 nil - (lambda () - (setq-default gc-cons-threshold doom-gc-cons-threshold) - ;; To speed up minibuffer commands (like helm and ivy), we defer garbage - ;; collection while the minibuffer is active. - (defun doom|defer-garbage-collection () - (setq gc-cons-threshold doom-gc-cons-upper-limit)) - (defun doom|restore-garbage-collection () - ;; Defer it so that commands launched from the minibuffer can enjoy the - ;; benefits. - (run-at-time 1 nil (lambda () (setq gc-cons-threshold doom-gc-cons-threshold)))) - (add-hook 'minibuffer-setup-hook #'doom|defer-garbage-collection) - (add-hook 'minibuffer-exit-hook #'doom|restore-garbage-collection) - ;; GC all sneaky breeky like - (add-hook 'focus-out-hook #'garbage-collect)))) - - -(if (ignore-errors (or after-init-time noninteractive)) - (setq gc-cons-threshold doom-gc-cons-threshold) - ;; A big contributor to startup times is garbage collection. We up the gc - ;; threshold to temporarily prevent it from running, then reset it later in - ;; `doom|restore-startup-optimizations'. - (setq gc-cons-threshold doom-gc-cons-upper-limit) - ;; This is consulted on every `require', `load' and various path/io functions. - ;; You get a minor speed up by nooping this. - (setq file-name-handler-alist nil) - ;; Not restoring these to their defaults will cause stuttering/freezes. - (add-hook 'after-init-hook #'doom|restore-startup-optimizations)) - +(when (version< emacs-version "25.3") + (error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher" + emacs-version)) ;; Ensure Doom is running out of this file's directory (setq user-emacs-directory (file-name-directory load-file-name)) + +;; A big contributor to startup times is garbage collection. We up the gc +;; threshold to temporarily prevent it from running, then reset it later with +;; `doom-restore-garbage-collection-h'. Not resetting it will cause +;; stuttering/freezes. +(setq gc-cons-threshold most-positive-fixnum) + ;; In noninteractive sessions, prioritize non-byte-compiled source files to -;; prevent stale, byte-compiled code from running. However, if you're getting -;; recursive load errors, it may help to set this to nil. +;; prevent the use of stale byte-code. Otherwise, it saves us a little IO time +;; to skip the mtime checks on every *.elc file we load. (setq load-prefer-newer noninteractive) - -;; Let 'er rip! +;; Load the heart of Doom Emacs (require 'core (concat user-emacs-directory "core/core")) + +;; And let 'er rip! +(add-hook 'window-setup-hook #'doom-display-benchmark-h) +(when (cdr command-line-args) + (add-to-list 'command-switch-alist + (cons "--restore" #'doom-restore-session-handler))) + +(doom-initialize) +(doom-initialize-core) +(doom-initialize-modules) diff --git a/init.example.el b/init.example.el index 9800e26f5..fec33a81c 100644 --- a/init.example.el +++ b/init.example.el @@ -1,9 +1,9 @@ ;;; init.el -*- lexical-binding: t; -*- -;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom -;; quickstart' will do this for you). The `doom!' block below controls what -;; modules are enabled and in what order they will be loaded. Remember to run -;; 'doom refresh' after modifying it. +;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install' +;; will do this for you). The `doom!' block below controls what modules are +;; enabled and in what order they will be loaded. Remember to run 'doom refresh' +;; after modifying it. ;; ;; More information about these modules (and what flags they support) can be ;; found in modules/README.org. @@ -24,7 +24,8 @@ doom-dashboard ; a nifty splash screen for Emacs doom-quit ; DOOM quit-message prompts when you quit Emacs ;;fill-column ; a `fill-column' indicator - hl-todo ; highlight TODO/FIXME/NOTE tags + hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW + ;;hydra ;;indent-guides ; highlighted indent columns modeline ; snazzy, Atom-inspired modeline, plus API nav-flash ; blink the current line after jumping @@ -34,8 +35,8 @@ +all ; catch all popups that start with an asterix +defaults) ; default popup rules ;;pretty-code ; replace bits of code with pretty symbols - ;;tabbar ; FIXME an (incomplete) tab bar for Emacs - treemacs ; a project drawer, like neotree but cooler + ;;tabs ; an tab bar for Emacs + ;;treemacs ; a project drawer, like neotree but cooler ;;unicode ; extended unicode support for various languages vc-gutter ; vcs diff in the fringe vi-tilde-fringe ; fringe tildes to mark beyond EOB @@ -53,12 +54,10 @@ ;;parinfer ; turn lisp into python, sort of rotate-text ; cycle region at point between text candidates snippets ; my elves. They type so I don't have to + ;;word-wrap ; soft wrapping with language-aware indent :emacs - (dired ; making dired pretty [functional] - ;;+ranger ; bringing the goodness of ranger to dired - ;;+icons ; colorful icons for dired-mode - ) + dired ; making dired pretty [functional] electric ; smarter, keyword-based electric-indent vc ; version-control and Emacs, sitting in a tree @@ -159,17 +158,13 @@ ;; should be loaded late. :app ;;calendar - ;;irc ; how neckbeards socialize + ;;irc ; how neckbeards socialize ;;(rss +org) ; emacs as an RSS reader ;;twitter ; twitter client https://twitter.com/vnought - ;;(write ; emacs as a word processor (latex + org + markdown) + ;;(write ; emacs for writers (fiction, notes, papers, etc.) ;; +wordnut ; wordnet (wn) search ;; +langtool) ; a proofreader (grammar/style check) for Emacs - :collab - ;;floobits ; peer programming for a price - ;;impatient-mode ; show off code over HTTP - :config ;; For literate config users. This will tangle+compile a config.org ;; literate config in your `doom-private-dir' whenever it changes. diff --git a/modules/README.org b/modules/README.org index 26bebab6a..aab14fca3 100644 --- a/modules/README.org +++ b/modules/README.org @@ -10,7 +10,6 @@ - [[#lang][:lang]] - [[#email][:email]] - [[#app][:app]] -- [[#collab][:collab]] - [[#config][:config]] * :completion @@ -38,7 +37,7 @@ Aesthetic modules that affect the Emacs interface or user experience. + [[file:ui/ophints/README.org][ophints]]: + [[file:ui/popup/README.org][popup]] =+all +defaults=: Makes temporary/disposable windows less intrusive + pretty-code: -+ [[file:ui/tabbar/README.org][tabbar]]: ++ [[file:ui/tabs/README.org][tabs]]: + treemacs: + [[file:ui/unicode/README.org][unicode]]: + vc-gutter: @@ -58,6 +57,7 @@ Modules that affect and augment your ability to manipulate or insert text. + [[file:editor/parinfer/README.org][parinfer]]: + rotate-text: + [[file:editor/snippets/README.org][snippets]]: Snippet expansion for lazy typists ++ [[file:editor/word-wrap/README.org][word-wrap]]: soft wrapping with language-aware indent * :emacs Modules that reconfigure or augment packages or features built into Emacs. @@ -80,6 +80,7 @@ Small modules that give Emacs access to external tools & services. + ansible: + debugger: A (nigh-)universal debugger in Emacs + [[file:tools/docker/README.org][docker]]: ++ [[file:tools/direnv/README.org][direnv]]: + [[file:tools/editorconfig/README.org][editorconfig]]: + [[file:tools/ein/README.org][ein]]: + [[file:tools/eval/README.org][eval]]: REPL & code evaluation support for a variety of languages @@ -141,7 +142,7 @@ Modules that bring support for a language or group of languages to Emacs. + qt: + racket: + [[file:lang/rest/README.org][rest]]: -+ ruby =+lsp=: ++ ruby =+lsp +rvm +rbenv=: + [[file:lang/rust/README.org][rust]] =+lsp=: + scala: + [[file:lang/sh/README.org][sh]] =+fish +lsp=: @@ -166,12 +167,6 @@ Doom-specific porcelains. + twitter: + [[file:app/write/README.org][write]] =+wordnut +langtool=: -* :collab -Modules that enable collaborative programming over the internet. - -+ floobits: -+ impatient-mode: - * :config Modules that configure Emacs one way or another, or focus on making it easier for you to customize it yourself. diff --git a/modules/app/calendar/autoload.el b/modules/app/calendar/autoload.el index 471147e2d..f642c184b 100644 --- a/modules/app/calendar/autoload.el +++ b/modules/app/calendar/autoload.el @@ -46,7 +46,7 @@ ))) ;;;###autoload -(defun +calendar*cfw:render-button (title command &optional state) +(defun +calendar-cfw:render-button-a (title command &optional state) "render-button TITLE COMMAND diff --git a/modules/app/calendar/config.el b/modules/app/calendar/config.el index f85f59225..66a26291e 100644 --- a/modules/app/calendar/config.el +++ b/modules/app/calendar/config.el @@ -7,8 +7,8 @@ ;; ;; Packages -(def-package! calfw - :commands (cfw:open-calendar-buffer) +(use-package! calfw + :commands cfw:open-calendar-buffer :config ;; better frame for calendar (setq cfw:face-item-separator-color nil @@ -24,20 +24,20 @@ (define-key cfw:calendar-mode-map "q" #'+calendar/quit) - (add-hook 'cfw:calendar-mode-hook #'doom|mark-buffer-as-real) + (add-hook 'cfw:calendar-mode-hook #'doom-mark-buffer-as-real-h) (add-hook 'cfw:calendar-mode-hook 'hide-mode-line-mode) - (advice-add #'cfw:render-button :override #'+calendar*cfw:render-button)) + (advice-add #'cfw:render-button :override #'+calendar-cfw:render-button-a)) -(def-package! calfw-org +(use-package! calfw-org :commands (cfw:open-org-calendar cfw:org-create-source cfw:open-org-calendar-withkevin my-open-calendar)) -(def-package! org-gcal +(use-package! org-gcal :commands (org-gcal-sync org-gcal-fetch org-gcal-post-at-point @@ -48,4 +48,4 @@ (message "org-gcal::%s - %s" title mes))) -;; (def-package! alert) +;; (use-package! alert) diff --git a/modules/app/irc/autoload/irc.el b/modules/app/irc/autoload/irc.el index 51fb63adb..8ff58ee24 100644 --- a/modules/app/irc/autoload/irc.el +++ b/modules/app/irc/autoload/irc.el @@ -97,3 +97,27 @@ argument) is non-nil only show channels in current server." (interactive) (when (derived-mode-p 'circe-mode) (tracking-next-buffer))) + + +;; +;;; Hooks/fns + +;;;###autoload +(defun +circe-buffer-p (buf) + "Return non-nil if BUF is a `circe-mode' buffer." + (with-current-buffer buf + (and (derived-mode-p 'circe-mode) + (eq (safe-persp-name (get-current-persp)) + +irc--workspace-name)))) + +;;;###autoload +(defun +irc--add-circe-buffer-to-persp-h () + (when (bound-and-true-p persp-mode) + (let ((persp (get-current-persp)) + (buf (current-buffer))) + ;; Add a new circe buffer to irc workspace when we're in another workspace + (unless (eq (safe-persp-name persp) +irc--workspace-name) + ;; Add new circe buffers to the persp containing circe buffers + (persp-add-buffer buf (persp-get-by-name +irc--workspace-name)) + ;; Remove new buffer from accidental workspace + (persp-remove-buffer buf persp))))) diff --git a/modules/app/irc/config.el b/modules/app/irc/config.el index ae8780299..eda3563ab 100644 --- a/modules/app/irc/config.el +++ b/modules/app/irc/config.el @@ -46,8 +46,8 @@ playback.") ;; ;; Packages -(def-package! circe - :commands (circe circe-server-buffers) +(use-package! circe + :commands circe circe-server-buffers :init (setq circe-network-defaults nil) :config (setq circe-default-quit-message nil @@ -90,48 +90,32 @@ playback.") circe-format-server-lurker-activity (+irc--pad "Lurk" "{nick} joined {joindelta} ago")) - (add-hook 'circe-channel-mode-hook #'turn-on-visual-line-mode) - - (defun +irc*circe-disconnect-hook (&rest _) - (run-hooks '+irc-disconnect-hook)) - (advice-add 'circe--irc-conn-disconnected :after #'+irc*circe-disconnect-hook) - - (defun +irc*circe-truncate-nicks () - "Truncate long nicknames in chat output non-destructively." - (when-let (beg (text-property-any (point-min) (point-max) 'lui-format-argument 'nick)) - (goto-char beg) - (let ((end (next-single-property-change beg 'lui-format-argument)) - (nick (plist-get (plist-get (text-properties-at beg) 'lui-keywords) - :nick))) - (when (> (length nick) +irc-left-padding) - (compose-region (+ beg +irc-left-padding -1) end - +irc-truncate-nick-char))))) - (add-hook 'lui-pre-output-hook #'+irc*circe-truncate-nicks) - - (defun +circe-buffer-p (buf) - "Return non-nil if BUF is a `circe-mode' buffer." - (with-current-buffer buf - (and (derived-mode-p 'circe-mode) - (eq (safe-persp-name (get-current-persp)) - +irc--workspace-name)))) (add-hook 'doom-real-buffer-functions #'+circe-buffer-p) + (add-hook 'circe-channel-mode-hook #'turn-on-visual-line-mode) + (add-hook 'circe-mode-hook #'+irc--add-circe-buffer-to-persp-h) - (defun +irc|circe-message-option-bot (nick &rest ignored) - "Fontify known bots and mark them to not be tracked." - (when (member nick +irc-bot-list) - '((text-properties . (face circe-fool-face lui-do-not-track t))))) - (add-hook 'circe-message-option-functions #'+irc|circe-message-option-bot) + (defadvice! +irc--circe-run-disconnect-hook-a (&rest _) + "Runs `+irc-disconnect-hook' after circe disconnects." + :after #'circe--irc-conn-disconnected + (run-hooks '+irc-disconnect-hook)) - (defun +irc|add-circe-buffer-to-persp () - (let ((persp (get-current-persp)) - (buf (current-buffer))) - ;; Add a new circe buffer to irc workspace when we're in another workspace - (unless (eq (safe-persp-name persp) +irc--workspace-name) - ;; Add new circe buffers to the persp containing circe buffers - (persp-add-buffer buf (persp-get-by-name +irc--workspace-name)) - ;; Remove new buffer from accidental workspace - (persp-remove-buffer buf persp)))) - (add-hook 'circe-mode-hook #'+irc|add-circe-buffer-to-persp) + (add-hook! 'lui-pre-output-hook + (defun +irc-circe-truncate-nicks-h () + "Truncate long nicknames in chat output non-destructively." + (when-let (beg (text-property-any (point-min) (point-max) 'lui-format-argument 'nick)) + (goto-char beg) + (let ((end (next-single-property-change beg 'lui-format-argument)) + (nick (plist-get (plist-get (text-properties-at beg) 'lui-keywords) + :nick))) + (when (> (length nick) +irc-left-padding) + (compose-region (+ beg +irc-left-padding -1) end + +irc-truncate-nick-char)))))) + + (add-hook! 'circe-message-option-functions + (defun +irc-circe-message-option-bot-h (nick &rest ignored) + "Fontify known bots and mark them to not be tracked." + (when (member nick +irc-bot-list) + '((text-properties . (face circe-fool-face lui-do-not-track t)))))) ;; Let `+irc/quit' and `circe' handle buffer cleanup (define-key circe-mode-map [remap kill-buffer] #'bury-buffer) @@ -152,14 +136,14 @@ playback.") "n" #'circe-command-NAMES))) -(def-package! circe-color-nicks +(use-package! circe-color-nicks :hook (circe-channel-mode . enable-circe-color-nicks) :config (setq circe-color-nicks-min-constrast-ratio 4.5 circe-color-nicks-everywhere t)) -(def-package! circe-new-day-notifier +(use-package! circe-new-day-notifier :after circe :config (enable-circe-new-day-notifier) @@ -167,7 +151,7 @@ playback.") (+irc--pad "Day" "Date changed [{day}]"))) -(def-package! circe-notifications +(use-package! circe-notifications :commands enable-circe-notifications :init (if +irc-defer-notifications @@ -181,10 +165,11 @@ playback.") circe-notifications-emacs-focused nil circe-notifications-alert-style (cond (IS-MAC 'osx-notifier) - (IS-LINUX 'libnotify)))) + (IS-LINUX 'libnotify) + (circe-notifications-alert-style)))) -(def-package! lui +(use-package! lui :commands lui-mode :config (define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line) @@ -194,20 +179,21 @@ playback.") (setq lui-flyspell-p t)) (after! evil - (defun +irc|evil-insert () + (defun +irc-evil-insert-h () "Ensure entering insert mode will put us at the prompt, unless editing after prompt marker." (when (> (marker-position lui-input-marker) (point)) (goto-char (point-max)))) (add-hook! 'lui-mode-hook - (add-hook 'evil-insert-state-entry-hook #'+irc|evil-insert nil t)) + (add-hook 'evil-insert-state-entry-hook #'+irc-evil-insert-h + nil 'local)) (mapc (lambda (cmd) (push cmd +irc-scroll-to-bottom-on-commands)) '(evil-paste-after evil-paste-before evil-open-above evil-open-below))) - (defun +irc|preinput-scroll-to-bottom () + (defun +irc-preinput-scroll-to-bottom-h () "Go to the end of the buffer in all windows showing it. Courtesy of esh-mode.el" (when (memq this-command +irc-scroll-to-bottom-on-commands) @@ -224,28 +210,26 @@ Courtesy of esh-mode.el" nil t))))) (add-hook! 'lui-mode-hook - (add-hook 'pre-command-hook #'+irc|preinput-scroll-to-bottom nil t)) + (add-hook 'pre-command-hook #'+irc-preinput-scroll-to-bottom-h nil t)) ;; enable a horizontal line marking the last read message - (add-hook! 'lui-mode-hook #'enable-lui-track-bar) + (add-hook 'lui-mode-hook #'enable-lui-track-bar) - (defun +irc|init-lui-margins () - (setq lui-time-stamp-position 'right-margin - lui-time-stamp-format +irc-time-stamp-format - right-margin-width (length (format-time-string lui-time-stamp-format)))) - - (defun +irc|init-lui-wrapping () - (setq fringes-outside-margins t - word-wrap t - wrap-prefix (make-string (+ +irc-left-padding 3) ? ))) - - (add-hook! 'lui-mode-hook #'(+irc|init-lui-margins +irc|init-lui-wrapping))) + (add-hook! 'lui-mode-hook + (defun +irc-init-lui-margins-h () + (setq lui-time-stamp-position 'right-margin + lui-time-stamp-format +irc-time-stamp-format + right-margin-width (length (format-time-string lui-time-stamp-format)))) + (defun +irc-init-lui-wrapping-a () + (setq fringes-outside-margins t + word-wrap t + wrap-prefix (make-string (+ +irc-left-padding 3) ? ))))) -(def-package! lui-logging +(use-package! lui-logging :after lui :config (enable-lui-logging)) -(def-package! lui-autopaste +(use-package! lui-autopaste :hook (circe-channel-mode . enable-lui-autopaste)) diff --git a/modules/app/rss/autoload.el b/modules/app/rss/autoload.el index 1fa6b498a..603cbed11 100644 --- a/modules/app/rss/autoload.el +++ b/modules/app/rss/autoload.el @@ -46,7 +46,7 @@ ;; Hooks ;;;###autoload -(defun +rss|elfeed-wrap () +(defun +rss-elfeed-wrap-h () "Enhances an elfeed entry's readability by wrapping it to a width of `fill-column'." (let ((inhibit-read-only t) @@ -57,7 +57,7 @@ (set-buffer-modified-p nil))) ;;;###autoload -(defun +rss|cleanup () +(defun +rss-cleanup-h () "Clean up after an elfeed session. Kills all elfeed and elfeed-org files." (interactive) ;; `delete-file-projectile-remove-from-cache' slows down `elfeed-db-compact' @@ -75,7 +75,7 @@ (kill-buffer buf))) (dolist (b search-buffers) (with-current-buffer b - (remove-hook 'kill-buffer-hook #'+rss|cleanup :local) + (remove-hook 'kill-buffer-hook #'+rss-cleanup-h :local) (kill-buffer b))) (mapc #'kill-buffer show-buffers))) @@ -99,7 +99,7 @@ collect url))) ;;;###autoload -(defun +rss-put-sliced-image (spec alt &optional flags) +(defun +rss-put-sliced-image-fn (spec alt &optional flags) "TODO" (cl-letf (((symbol-function #'insert-image) (lambda (image &optional alt _area _slice) @@ -108,7 +108,7 @@ (shr-put-image spec alt flags))) ;;;###autoload -(defun +rss-render-image-tag-without-underline (dom &optional url) +(defun +rss-render-image-tag-without-underline-fn (dom &optional url) "TODO" (let ((start (point))) (shr-tag-img dom url) diff --git a/modules/app/rss/config.el b/modules/app/rss/config.el index 06a6e176d..7e1af21a5 100644 --- a/modules/app/rss/config.el +++ b/modules/app/rss/config.el @@ -19,7 +19,7 @@ easier to scroll through.") ;; ;; Packages -(def-package! elfeed +(use-package! elfeed :commands elfeed :config (setq elfeed-search-filter "@2-week-ago " @@ -36,21 +36,21 @@ easier to scroll through.") (make-directory elfeed-db-directory t) ;; Ensure elfeed buffers are treated as real - (defun +rss-buffer-p (buf) - (string-match-p "^\\*elfeed" (buffer-name buf))) - (add-to-list 'doom-real-buffer-functions #'+rss-buffer-p nil #'eq) + (add-hook! 'doom-real-buffer-functions + (defun +rss-buffer-p (buf) + (string-match-p "^\\*elfeed" (buffer-name buf)))) ;; Enhance readability of a post - (add-hook 'elfeed-show-mode-hook #'+rss|elfeed-wrap) + (add-hook 'elfeed-show-mode-hook #'+rss-elfeed-wrap-h) (add-hook! 'elfeed-search-mode-hook - (add-hook 'kill-buffer-hook #'+rss|cleanup nil t)) + (add-hook 'kill-buffer-hook #'+rss-cleanup-h nil 'local)) ;; Large images are annoying to scroll through, because scrolling follows the ;; cursor, so we force shr to insert images in slices. (when +rss-enable-sliced-images (setq-hook! 'elfeed-show-mode-hook - shr-put-image-function #'+rss-put-sliced-image - shr-external-rendering-functions '((img . +rss-render-image-tag-without-underline)))) + shr-put-image-function #'+rss-put-sliced-image-fn + shr-external-rendering-functions '((img . +rss-render-image-tag-without-underline-fn)))) ;; Keybindings (after! elfeed-show @@ -64,11 +64,11 @@ easier to scroll through.") (kbd "M-RET") #'elfeed-search-browse-url))) -(def-package! elfeed-org +(use-package! elfeed-org :when (featurep! +org) :after elfeed :config - (setq rmh-elfeed-org-files - (let ((default-directory org-directory)) + (let ((default-directory org-directory)) + (setq rmh-elfeed-org-files (mapcar #'expand-file-name +rss-elfeed-files))) (elfeed-org)) diff --git a/modules/app/twitter/autoload.el b/modules/app/twitter/autoload.el index cf1d2eb94..1116633be 100644 --- a/modules/app/twitter/autoload.el +++ b/modules/app/twitter/autoload.el @@ -4,7 +4,7 @@ "The name to use for the twitter workspace.") ;;;###autoload -(defun +twitter-display-buffer (buf) +(defun +twitter-display-buffer-fn (buf) "A replacement display-buffer command for `twittering-pop-to-buffer-function' that works with the feature/popup module." (let ((win (selected-window))) diff --git a/modules/app/twitter/config.el b/modules/app/twitter/config.el index c01d1ec25..db8849a50 100644 --- a/modules/app/twitter/config.el +++ b/modules/app/twitter/config.el @@ -1,9 +1,10 @@ ;;; app/twitter/config.el -*- lexical-binding: t; -*- -(def-package! twittering-mode +(use-package! twittering-mode :commands twit :config - (setq twittering-private-info-file (expand-file-name "twittering-mode.gpg" doom-etc-dir) + (setq twittering-private-info-file + (expand-file-name "twittering-mode.gpg" doom-etc-dir) twittering-use-master-password t twittering-request-confirmation-on-posting t ;; twittering-icon-mode t @@ -35,24 +36,16 @@ (add-hook 'doom-real-buffer-functions #'+twitter-buffer-p) (when (featurep! :ui popup) - (setq twittering-pop-to-buffer-function #'+twitter-display-buffer)) + (setq twittering-pop-to-buffer-function #'+twitter-display-buffer-fn)) (after! solaire-mode (add-hook 'twittering-mode-hook #'solaire-mode)) ;; Custom header-line for twitter buffers - (defun +twitter|switch-mode-and-header-line () - (setq header-line-format mode-line-format - mode-line-format nil)) - (add-hook 'twittering-mode-hook #'+twitter|switch-mode-and-header-line) - - (cond ((featurep! :ui doom-modeline +new) - (setq-hook! 'twittering-mode-hook mode-line-format-right nil)) - ((featurep! :ui doom-modeline) - (def-modeline! 'twitter - '(bar matches " %b " selection-info) - '()) - (add-hook! 'twittering-mode-hook (doom-set-modeline 'twitter)))) + (add-hook! 'twittering-mode-hook + (defun +twitter-switch-mode-and-header-line-h () + (setq header-line-format mode-line-format + mode-line-format nil))) ;; `epa--decode-coding-string' isn't defined in later versions of Emacs 27 (unless (fboundp 'epa--decode-coding-string) diff --git a/modules/app/write/config.el b/modules/app/write/config.el index e82a0137c..e42620df0 100644 --- a/modules/app/write/config.el +++ b/modules/app/write/config.el @@ -9,7 +9,7 @@ ;; ;; Packages -(def-package! langtool +(use-package! langtool :when (featurep! +langtool) :commands (langtool-check langtool-check-done @@ -23,7 +23,7 @@ (locate-file "libexec/languagetool-commandline.jar" (doom-files-in "/usr/local/Cellar/languagetool" :type 'dirs - :depth 1))) + :depth 2))) (IS-LINUX "/usr/share/java/languagetool/languagetool-commandline.jar"))))) diff --git a/modules/collab/floobits/packages.el b/modules/collab/floobits/packages.el deleted file mode 100644 index 98eb4d29b..000000000 --- a/modules/collab/floobits/packages.el +++ /dev/null @@ -1,4 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; collab/foobits/packages.el - -(package! floobits) diff --git a/modules/collab/impatient-mode/autoload.el b/modules/collab/impatient-mode/autoload.el deleted file mode 100644 index 41047d359..000000000 --- a/modules/collab/impatient-mode/autoload.el +++ /dev/null @@ -1,24 +0,0 @@ -;;; collab/impatient-mode/autoload.el -*- lexical-binding: t; -*- - -;;;###autoload -(defun +impatient-mode/toggle () - "Toggle `impatient-mode' in the current buffer." - (interactive) - (unless (process-status "httpd") - (httpd-start)) - (impatient-mode) - (if impatient-mode - (add-hook 'kill-buffer-hook '+impatient-mode--cleanup-impatient-mode) - (+impatient-mode--cleanup-impatient-mode))) - -(defun +impatient-mode--cleanup-impatient-mode () - (unless (cl-loop for buf in (doom-buffer-list) - if (buffer-local-value 'impatient-mode buf) - return t) - (httpd-stop) - (cl-loop for buf in (doom-buffer-list) - if (buffer-local-value 'impatient-mode buf) - do - (with-current-buffer buf - (impatient-mode -1))) - (remove-hook 'kill-buffer-hook '+impatient-mode--cleanup-impatient-mode))) diff --git a/modules/collab/impatient-mode/packages.el b/modules/collab/impatient-mode/packages.el deleted file mode 100644 index 2469c675e..000000000 --- a/modules/collab/impatient-mode/packages.el +++ /dev/null @@ -1,5 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; collab/impatient-mode/packages.el - -(package! htmlize) -(package! impatient-mode) diff --git a/modules/completion/company/autoload.el b/modules/completion/company/autoload.el index 05e606c4b..a37627d00 100644 --- a/modules/completion/company/autoload.el +++ b/modules/completion/company/autoload.el @@ -65,15 +65,15 @@ Examples: ;;; Hooks ;;;###autoload -(defun +company|init-backends () +(defun +company-init-backends-h () "Set `company-backends' for the current buffer." (if (not company-mode) - (remove-hook 'change-major-mode-after-body-hook #'+company|init-backends 'local) + (remove-hook 'change-major-mode-after-body-hook #'+company-init-backends-h 'local) (unless (eq major-mode 'fundamental-mode) (setq-local company-backends (+company--backends))) - (add-hook 'change-major-mode-after-body-hook #'+company|init-backends nil 'local))) + (add-hook 'change-major-mode-after-body-hook #'+company-init-backends-h nil 'local))) -(put '+company|init-backends 'permanent-local-hook t) +(put '+company-init-backends-h 'permanent-local-hook t) ;; @@ -129,12 +129,13 @@ C-x C-l." (`candidates (all-completions arg - (split-string - (replace-regexp-in-string - "^[\t\s]+" "" - (concat (buffer-substring-no-properties (point-min) (line-beginning-position)) - (buffer-substring-no-properties (line-end-position) (point-max)))) - "\\(\r\n\\|[\n\r]\\)" t))))) + (delete-dups + (split-string + (replace-regexp-in-string + "^[\t\s]+" "" + (concat (buffer-substring-no-properties (point-min) (line-beginning-position)) + (buffer-substring-no-properties (line-end-position) (point-max)))) + "\\(\r\n\\|[\n\r]\\)" t)))))) ;;;###autoload (defun +company/dict-or-keywords () diff --git a/modules/completion/company/config.el b/modules/completion/company/config.el index f281086c8..9c92a53c0 100644 --- a/modules/completion/company/config.el +++ b/modules/completion/company/config.el @@ -1,8 +1,8 @@ ;;; completion/company/config.el -*- lexical-binding: t; -*- -(def-package! company - :commands (company-complete-common company-manual-begin company-grab-line) - :after-call (evil-insert-state-entry-hook evil-emacs-state-entry-hook) +(use-package! company + :commands company-complete-common company-manual-begin company-grab-line + :after-call evil-insert-state-entry-hook evil-emacs-state-entry-hook :init (setq company-minimum-prefix-length 2 company-tooltip-limit 14 @@ -27,16 +27,16 @@ ;; Allow users to switch between backends on the fly. E.g. C-x C-s followed ;; by C-x C-n, will switch from `company-yasnippet' to ;; `company-dabbrev-code'. - (defun +company*abort-previous (&rest _) (company-abort)) - (advice-add #'company-begin-backend :before #'+company*abort-previous)) + (defadvice! +company--abort-previous-a (&rest _) + :before #'company-begin-backend + (company-abort))) - (add-hook 'company-mode-hook #'+company|init-backends) + (add-hook 'company-mode-hook #'+company-init-backends-h) (global-company-mode +1)) -(def-package! company-tng +(use-package! company-tng :when (featurep! +tng) - :defer 2 :after-call post-self-insert-hook :config (add-to-list 'company-frontends 'company-tng-frontend) @@ -51,7 +51,7 @@ ;; ;; Packages -(def-package! company-prescient +(use-package! company-prescient :hook (company-mode . company-prescient-mode) :config ;; NOTE prescient config duplicated with `ivy' @@ -59,7 +59,7 @@ (prescient-persist-mode +1)) -(def-package! company-box +(use-package! company-box :when (and EMACS26+ (featurep! +childframe)) :hook (company-mode . company-box-mode) :config @@ -68,7 +68,10 @@ company-box-max-candidates 50 company-box-icons-alist 'company-box-icons-all-the-icons company-box-icons-functions - '(+company-box-icons--yasnippet company-box-icons--lsp +company-box-icons--elisp company-box-icons--acphp) + '(+company-box-icons--yasnippet-fn + company-box-icons--lsp + +company-box-icons--elisp-fn + company-box-icons--acphp) company-box-icons-all-the-icons `((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple)) (Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green)) @@ -103,11 +106,11 @@ (ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange)) (ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink)))) - (defun +company-box-icons--yasnippet (candidate) + (defun +company-box-icons--yasnippet-fn (candidate) (when (get-text-property 0 'yas-annotation candidate) 'Yasnippet)) - (defun +company-box-icons--elisp (candidate) + (defun +company-box-icons--elisp-fn (candidate) (when (derived-mode-p 'emacs-lisp-mode) (let ((sym (intern candidate))) (cond ((fboundp sym) 'ElispFunction) @@ -116,14 +119,13 @@ ((facep sym) 'ElispFace)))))) -(def-package! company-dict +(use-package! company-dict :defer t :config (setq company-dict-dir (expand-file-name "dicts" doom-private-dir)) - (defun +company|enable-project-dicts (mode &rest _) - "Enable per-project dictionaries." - (if (symbol-value mode) - (add-to-list 'company-dict-minor-mode-list mode nil #'eq) - (setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list)))) - (add-hook 'doom-project-hook #'+company|enable-project-dicts)) - + (add-hook! 'doom-project-hook + (defun +company-enable-project-dicts-h (mode &rest _) + "Enable per-project dictionaries." + (if (symbol-value mode) + (add-to-list 'company-dict-minor-mode-list mode nil #'eq) + (setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list)))))) diff --git a/modules/completion/helm/autoload/posframe.el b/modules/completion/helm/autoload/posframe.el index 308679c74..25ac3fd09 100644 --- a/modules/completion/helm/autoload/posframe.el +++ b/modules/completion/helm/autoload/posframe.el @@ -1,9 +1,7 @@ ;;; completion/helm/autoload/posframe.el -*- lexical-binding: t; -*- -(add-hook 'helm-cleanup-hook #'+helm|posframe-cleanup) - ;;;###autoload -(defun +helm-poshandler-frame-center-near-bottom (info) +(defun +helm-poshandler-frame-center-near-bottom-fn (info) "Display the child frame in the center of the frame, slightly closer to the bottom, which is easier on the eyes on big displays." (let ((parent-frame (plist-get info :parent-frame)) @@ -14,7 +12,7 @@ bottom, which is easier on the eyes on big displays." (defvar +helm--posframe-buffer nil) ;;;###autoload -(defun +helm-posframe-display (buffer &optional _resume) +(defun +helm-posframe-display-fn (buffer &optional _resume) "TODO" (setq helm--buffer-in-new-frame-p t) (let ((solaire-p (bound-and-true-p solaire-mode)) @@ -52,15 +50,10 @@ bottom, which is easier on the eyes on big displays." (text-scale-set +helm-posframe-text-scale))))) ;;;###autoload -(defun +helm|posframe-cleanup () +(defun +helm-posframe-cleanup-h () "TODO" - ;; Ensure focus is properly returned to the underlying window, by forcing a - ;; chance in buffer/window focus. This gives the modeline a chance to refresh. - (switch-to-buffer +helm--posframe-buffer t) - ;; - (posframe-delete +helm--posframe-buffer)) + ;; Ensure focus is properly returned to the underlying window. This gives the + ;; modeline a chance to refresh. + (switch-to-buffer +helm--posframe-buffer t)) - -;;;###autoload -(defun +helm*fix-get-font-height (orig-fn position) - (ignore-errors (funcall orig-fn position))) +(add-hook 'helm-cleanup-hook #'+helm-posframe-cleanup-h) diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index deb5c68ba..eae06e513 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -11,8 +11,7 @@ silently ignored. This falls back to git-grep (then grep) if none of these available.") ;; Posframe (requires +childframe) -(defvar +helm-posframe-handler - #'+helm-poshandler-frame-center-near-bottom +(defvar +helm-posframe-handler #'+helm-poshandler-frame-center-near-bottom-fn "The function that determines the location of the childframe. It should return a cons cell representing the X and Y coordinates. See `posframe-poshandler-frame-center' as a reference.") @@ -33,7 +32,7 @@ be negative.") ;; ;;; Packages -(def-package! helm-mode +(use-package! helm-mode :defer t :after-call pre-command-hook :init @@ -58,7 +57,7 @@ be negative.") (add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil))) -(def-package! helm +(use-package! helm :after helm-mode :preface (setq helm-candidate-number-limit 50 @@ -83,9 +82,7 @@ be negative.") :init (when (and EMACS26+ (featurep! +childframe)) - (setq helm-display-function #'+helm-posframe-display) - ;; Fix "Specified window is not displaying the current buffer" error - (advice-add #'posframe--get-font-height :around #'+helm*fix-get-font-height)) + (setq helm-display-function #'+helm-posframe-display-fn)) (let ((fuzzy (featurep! +fuzzy))) (setq helm-M-x-fuzzy-match fuzzy @@ -110,27 +107,28 @@ be negative.") :config (set-popup-rule! "^\\*helm" :vslot -100 :size 0.22 :ttl nil) + ;; HACK Doom doesn't support these commands, which invite the user to install + ;; the package via ELPA. Force them to use +helm/* instead, because they work + ;; out of the box. + (advice-add #'helm-projectile-rg :override #'+helm/rg) + (advice-add #'helm-projectile-ag :override #'+helm/ag) + (advice-add #'helm-projectile-grep :override #'+helm/grep) + ;; Hide the modeline - (defun +helm|hide-mode-line (&rest _) + (defun +helm--hide-mode-line (&rest _) (with-current-buffer (helm-buffer-get) (unless helm-mode-line-string (hide-mode-line-mode +1)))) - (add-hook 'helm-after-initialize-hook #'+helm|hide-mode-line) - (advice-add #'helm-display-mode-line :override #'+helm|hide-mode-line) + (add-hook 'helm-after-initialize-hook #'+helm--hide-mode-line) + (advice-add #'helm-display-mode-line :override #'+helm--hide-mode-line) (advice-add #'helm-ag-show-status-default-mode-line :override #'ignore) - ;; TODO Find a better way - (defun +helm*use-helpful (orig-fn arg) - (cl-letf (((symbol-function #'describe-function) - (symbol-function #'helpful-callable)) - ((symbol-function #'describe-variable) - (symbol-function #'helpful-variable))) - (funcall orig-fn arg))) - (advice-add #'helm-describe-variable :around #'+helm*use-helpful) - (advice-add #'helm-describe-function :around #'+helm*use-helpful)) + ;; Use helpful instead of describe-* to display documentation + (dolist (fn '(helm-describe-variable helm-describe-function)) + (advice-add fn :around #'doom-use-helpful-a))) -(def-package! helm-flx +(use-package! helm-flx :when (featurep! +fuzzy) :hook (helm-mode . helm-flx-mode) :config (helm-flx-mode +1)) @@ -142,9 +140,9 @@ be negative.") (define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort) (set-popup-rule! "^\\*helm-ag-edit" :size 0.35 :ttl 0 :quit nil) ;; Recenter after jumping to match - (advice-add #'helm-ag--find-file-action :after-while #'doom*recenter) + (advice-add #'helm-ag--find-file-action :after-while #'doom-recenter-a) ;; And record position before jumping - (advice-add #'helm-ag--find-file-action :around #'doom*set-jump-maybe)) + (advice-add #'helm-ag--find-file-action :around #'doom-set-jump-maybe-a)) ;;;###package helm-bookmark @@ -164,7 +162,7 @@ be negative.") ;;;###package helm-projectile -(def-package! helm-projectile +(use-package! helm-projectile :commands (helm-projectile-find-file helm-projectile-recentf helm-projectile-switch-project diff --git a/modules/completion/helm/packages.el b/modules/completion/helm/packages.el index 1e8d9d178..2021ecf93 100644 --- a/modules/completion/helm/packages.el +++ b/modules/completion/helm/packages.el @@ -5,7 +5,7 @@ (package! helm-ag) (package! helm-c-yasnippet) (package! helm-company) -(package! helm-describe-modes :recipe (:fetcher github :repo "emacs-helm/helm-describe-modes")) +(package! helm-describe-modes :recipe (:host github :repo "emacs-helm/helm-describe-modes")) (package! helm-projectile) (package! swiper-helm) (when (featurep! +fuzzy) diff --git a/modules/completion/ido/config.el b/modules/completion/ido/config.el index de96fc24b..39e53d998 100644 --- a/modules/completion/ido/config.el +++ b/modules/completion/ido/config.el @@ -1,6 +1,6 @@ ;;; completion/ido/config.el -*- lexical-binding: t; -*- -(defun +ido|init () +(defun +ido-init-h () (setq ido-ignore-buffers '("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer" "^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-" @@ -28,8 +28,9 @@ (insert "~/") (call-interactively #'self-insert-command)))) - (defun +ido*sort-mtime () + (defadvice! +ido--sort-mtime-a () "Sort ido filelist by mtime instead of alphabetically." + :override #'ido-sort-mtime (setq ido-temp-list (sort ido-temp-list (lambda (a b) @@ -40,8 +41,8 @@ (cl-loop for x in ido-temp-list if (char-equal (string-to-char x) ?.) collect x))) - (advice-add #'ido-sort-mtime :override #'+ido*sort-mtime) - (add-hook! (ido-make-file-list ido-make-dir-list) #'+ido*sort-mtime) + (add-hook! '(ido-make-file-list-hook ido-make-dir-list-hook) + #'ido-sort-mtime) ;; (ido-mode 1) @@ -52,7 +53,7 @@ (crm-custom-mode +1) ;; - (remove-hook 'ido-setup-hook #'+ido|init)) + (remove-hook 'ido-setup-hook #'+ido-init-h)) ;; -(add-hook 'ido-setup-hook #'+ido|init) +(add-hook 'ido-setup-hook #'+ido-init-h) diff --git a/modules/completion/ivy/autoload/hydras.el b/modules/completion/ivy/autoload/hydras.el index 5f0f45708..ee71f5118 100644 --- a/modules/completion/ivy/autoload/hydras.el +++ b/modules/completion/ivy/autoload/hydras.el @@ -1,32 +1,33 @@ ;;; completion/ivy/autoload/hydras.el -*- lexical-binding: t; -*- +;;;###if (featurep! :ui hydra) -;;;###autoload -(after! ivy-hydra - (with-no-warnings - (defhydra+ hydra-ivy (:hint nil :color pink) - " +(eval-when-compile (require 'ivy-hydra)) + +;;;###autoload (autoload 'hydra-ivy/body "completion/ivy/autoload/hydras" nil nil) +(defhydra+ hydra-ivy (:hint nil :color pink) + " Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name) ----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+--------------------------------- _g_ ^ ^ _k_ ^ ^ _u_ | _f_orward _o_ccur | _i_nsert | _c_alling: %-7s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search ^↨^ _h_ ^+^ _l_ ^↕^ | _RET_ done ^^ | _q_uit | _m_atcher: %-7s(ivy--matcher-desc) _t_runcate: %-11`truncate-lines _G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow " - ;; arrows - ("l" ivy-alt-done) - ("h" ivy-backward-delete-char) - ("g" ivy-beginning-of-buffer) - ("G" ivy-end-of-buffer) - ("d" ivy-scroll-up-command) - ("u" ivy-scroll-down-command) - ("e" ivy-scroll-down-command) - ;; actions - ("q" keyboard-escape-quit :exit t) - ("" keyboard-escape-quit :exit t) - ("TAB" ivy-alt-done :exit nil) - ("RET" ivy-done :exit t) - ("C-SPC" ivy-call-and-recenter :exit nil) - ("f" ivy-call) - ("c" ivy-toggle-calling) - ("m" ivy-toggle-fuzzy) - ("t" (setq truncate-lines (not truncate-lines))) - ("o" ivy-occur :exit t)))) + ;; arrows + ("l" ivy-alt-done) + ("h" ivy-backward-delete-char) + ("g" ivy-beginning-of-buffer) + ("G" ivy-end-of-buffer) + ("d" ivy-scroll-up-command) + ("u" ivy-scroll-down-command) + ("e" ivy-scroll-down-command) + ;; actions + ("q" keyboard-escape-quit :exit t) + ("" keyboard-escape-quit :exit t) + ("TAB" ivy-alt-done :exit nil) + ("RET" ivy-done :exit t) + ("C-SPC" ivy-call-and-recenter :exit nil) + ("f" ivy-call) + ("c" ivy-toggle-calling) + ("m" ivy-toggle-fuzzy) + ("t" (setq truncate-lines (not truncate-lines))) + ("o" ivy-occur :exit t)) diff --git a/modules/completion/ivy/autoload/posframe.el b/modules/completion/ivy/autoload/posframe.el index 0b31f9a6b..eb080af5e 100644 --- a/modules/completion/ivy/autoload/posframe.el +++ b/modules/completion/ivy/autoload/posframe.el @@ -2,12 +2,12 @@ ;;;###if (featurep! +childframe) ;;;###autoload -(defun +ivy-display-at-frame-center-near-bottom (str) +(defun +ivy-display-at-frame-center-near-bottom-fn (str) "TODO" - (ivy-posframe--display str #'+ivy-poshandler-frame-center-near-bottom)) + (ivy-posframe--display str #'+ivy-poshandler-frame-center-near-bottom-fn)) ;;;###autoload -(defun +ivy-poshandler-frame-center-near-bottom (info) +(defun +ivy-poshandler-frame-center-near-bottom-fn (info) "TODO" (let ((parent-frame (plist-get info :parent-frame)) (pos (posframe-poshandler-frame-center info))) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index 10688cbd6..fa7b46653 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -43,7 +43,7 @@ immediately runs it on the current candidate (ending the ivy session)." ;; ;;; Packages -(def-package! ivy +(use-package! ivy :defer 1 :after-call pre-command-hook :init @@ -75,31 +75,57 @@ immediately runs it on the current candidate (ending the ivy session)." ;; enable ability to select prompt (alternative to `ivy-immediate-done') ivy-use-selectable-prompt t) + ;; REVIEW Move this somewhere else and perhaps generalize this so both + ;; ivy/helm users can enjoy it. + (defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args) + "Change `counsel-file-jump' to use fd or ripgrep, if they are available." + :override #'counsel--find-return-list + (cl-destructuring-bind (find-program . args) + (cond ((executable-find "fd") + (cons "fd" (list "-t" "f" "-E" ".git"))) + ((executable-find "rg") + (cons "rg" (list "--files" "--hidden" "--no-messages"))) + ((cons find-program args))) + (unless (listp args) + (user-error "`counsel-file-jump-args' is a list now, please customize accordingly.")) + (counsel--call + (cons find-program args) + (lambda () + (goto-char (point-min)) + (let ((offset (if (member find-program '("fd" "rg")) 0 2)) + files) + (while (< (point) (point-max)) + (push (buffer-substring + (+ offset (line-beginning-position)) (line-end-position)) files) + (forward-line 1)) + (nreverse files)))))) + ;; Ensure a jump point is registered before jumping to new locations with ivy (defvar +ivy--origin nil) - - (defun +ivy|record-position-maybe () + (defun +ivy--record-position-maybe-fn () (with-ivy-window (setq +ivy--origin (point-marker)))) - (setq ivy-hooks-alist '((t . +ivy|record-position-maybe))) + (setq ivy-hooks-alist '((t . +ivy--record-position-maybe-fn))) - (defun +ivy|set-jump-point-maybe () - (when (and (markerp +ivy--origin) - (not (equal (with-ivy-window (point-marker)) +ivy--origin))) - (with-current-buffer (marker-buffer +ivy--origin) - (better-jumper-set-jump +ivy--origin))) - (setq +ivy--origin nil)) - (add-hook 'minibuffer-exit-hook #'+ivy|set-jump-point-maybe) + (add-hook! 'minibuffer-exit-hook + (defun +ivy--set-jump-point-maybe-h () + (with-demoted-errors "Ivy error: %s" + (when (and (markerp +ivy--origin) + (not (equal (with-ivy-window (point-marker)) + +ivy--origin))) + (with-current-buffer (marker-buffer +ivy--origin) + (better-jumper-set-jump +ivy--origin))) + (setq +ivy--origin nil)))) (after! yasnippet (add-to-list 'yas-prompt-functions #'+ivy-yas-prompt nil #'eq)) - (defun +ivy*inhibit-ivy-in-evil-ex (orig-fn &rest args) + (defadvice! +ivy--inhibit-in-evil-ex-a (orig-fn &rest args) "`ivy-completion-in-region' struggles with completing certain evil-ex-specific constructs, so we disable it solely in evil-ex." + :around #'evil-ex (let ((completion-in-region-function #'completion--in-region)) (apply orig-fn args))) - (advice-add #'evil-ex :around #'+ivy*inhibit-ivy-in-evil-ex) (define-key! ivy-mode-map [remap switch-to-buffer] #'+ivy/switch-buffer @@ -110,7 +136,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (ivy-mode +1) - (def-package! ivy-hydra + (use-package! ivy-hydra :commands (ivy-dispatching-done-hydra ivy--matcher-desc ivy-hydra/body) :init (define-key! ivy-minibuffer-map @@ -121,7 +147,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (define-key ivy-minibuffer-map (kbd "M-o") #'hydra-ivy/body))) -(def-package! ivy-rich +(use-package! ivy-rich :after ivy :config (when (featurep! +icons) @@ -159,7 +185,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (ivy-rich-mode +1)) -(def-package! all-the-icons-ivy +(use-package! all-the-icons-ivy :when (featurep! +icons) :after ivy :config @@ -175,7 +201,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (all-the-icons-ivy-setup)))) -(def-package! counsel +(use-package! counsel :commands counsel-describe-face :init (map! [remap apropos] #'counsel-apropos @@ -247,7 +273,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." '(("O" +ivy-git-grep-other-window-action "open in other window")))) -(def-package! counsel-projectile +(use-package! counsel-projectile :defer t :init (map! [remap projectile-find-file] #'+ivy/projectile-find-file @@ -261,12 +287,12 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (ivy-set-display-transformer #'counsel-projectile-find-file nil)) -(def-package! wgrep +(use-package! wgrep :commands wgrep-change-to-wgrep-mode :config (setq wgrep-auto-save-buffer t)) -(def-package! ivy-posframe +(use-package! ivy-posframe :when (and EMACS26+ (featurep! +childframe)) :hook (ivy-mode . ivy-posframe-mode) :config @@ -277,14 +303,16 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (min-height . ,ivy-height))) ;; default to posframe display function - (setf (alist-get t ivy-posframe-display-functions-alist) #'+ivy-display-at-frame-center-near-bottom) + (setf (alist-get t ivy-posframe-display-functions-alist) + #'+ivy-display-at-frame-center-near-bottom-fn) ;; posframe doesn't work well with async sources (dolist (fn '(swiper counsel-ag counsel-grep counsel-git-grep)) - (setf (alist-get fn ivy-posframe-display-functions-alist) #'ivy-display-function-fallback))) + (setf (alist-get fn ivy-posframe-display-functions-alist) + #'ivy-display-function-fallback))) -(def-package! flx +(use-package! flx :when (and (featurep! +fuzzy) (not (featurep! +prescient))) :defer t ; is loaded by ivy @@ -294,7 +322,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." ivy-flx-limit 10000)) -(def-package! ivy-prescient +(use-package! ivy-prescient :hook (ivy-mode . ivy-prescient-mode) :when (featurep! +prescient) :init @@ -323,5 +351,5 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (prescient-persist-mode +1)) -;; Used by `counsel-M-x' -(setq amx-save-file (concat doom-cache-dir "amx-items")) +;;;###package amx +(setq amx-save-file (concat doom-cache-dir "amx-items")) ; used by `counsel-M-x' diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index 153e03d85..4f3e34db1 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -150,6 +150,7 @@ :desc "Initialize repo" "r" #'magit-init :desc "Clone repo" "R" #'+magit/clone :desc "Commit" "c" #'magit-commit-create + :desc "Fixup" "f" #'magit-commit-fixup :desc "Issue" "i" #'forge-create-issue :desc "Pull request" "p" #'forge-create-pullreq))) diff --git a/modules/config/default/+emacs.el b/modules/config/default/+emacs.el index 40c41b697..56981eec0 100644 --- a/modules/config/default/+emacs.el +++ b/modules/config/default/+emacs.el @@ -9,15 +9,14 @@ (setq shift-select-mode t) (delete-selection-mode +1) -(def-package! expand-region +(use-package! expand-region :commands (er/contract-region er/mark-symbol er/mark-word) :config - (defun doom*quit-expand-region () + (defadvice! doom--quit-expand-region-a () "Properly abort an expand-region region." + :before '(evil-escape doom/escape) (when (memq last-command '(er/expand-region er/contract-region)) - (er/contract-region 0))) - (advice-add #'evil-escape :before #'doom*quit-expand-region) - (advice-add #'doom/escape :before #'doom*quit-expand-region)) + (er/contract-region 0)))) ;; diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index c0ab2b1e0..1d364381d 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -4,10 +4,15 @@ ;; Don't let evil-collection interfere with certain keys (setq evil-collection-key-blacklist - (list "C-j" "C-k" "gd" "gf" "K" "[" "]" "gz" "" + (list "gd" "gf" "K" "[" "]" "gz" "" doom-leader-key doom-localleader-key doom-leader-alt-key doom-localleader-alt-key)) +(defadvice! +default-evil-collection-disable-blacklist-a (orig-fn) + :around #'evil-collection-vterm-toggle-send-escape ; allow binding to ESC + (let (evil-collection-key-blacklist) + (apply orig-fn))) + ;; ;;; Global keybindings @@ -53,6 +58,9 @@ ;; misc :n "C-S-f" #'toggle-frame-fullscreen + :n "C-+" #'doom/reset-font-size + :n "C-=" #'doom/increase-font-size + :n "C--" #'doom/decrease-font-size ;; ported from vim :m "]m" #'+evil/next-beginning-of-method @@ -140,7 +148,6 @@ "C-r" #'winner-redo "o" #'doom/window-enlargen ;; Delete window - "c" #'+workspace/close-window-or-workspace "C-C" #'ace-delete-window) ;; Plugins @@ -162,8 +169,8 @@ :bind ((evil-snipe-scope 'buffer) (evil-snipe-enable-highlight) (evil-snipe-enable-incremental-highlight))) - "SPC" (λ!! #'avy-goto-char-timer t) - "/" #'avy-goto-char-timer) + "SPC" (λ!! #'evil-avy-goto-char-timer t) + "/" #'evil-avy-goto-char-timer) ;; text object plugins :textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr @@ -520,6 +527,7 @@ :desc "Switch workspace buffer" "," #'persp-switch-to-buffer :desc "Switch buffer" "<" #'switch-to-buffer) + :desc "Switch to last buffer" "`" #'evil-switch-to-windows-last-buffer :desc "Resume last search" "'" (cond ((featurep! :completion ivy) #'ivy-resume) ((featurep! :completion helm) #'helm-resume)) @@ -580,7 +588,9 @@ :desc "Switch buffer" "B" #'switch-to-buffer) (:unless (featurep! :ui workspaces) :desc "Switch buffer" "b" #'switch-to-buffer) + :desc "Kill buffer" "d" #'kill-current-buffer :desc "Kill buffer" "k" #'kill-current-buffer + :desc "Switch to last buffer" "l" #'evil-switch-to-windows-last-buffer :desc "Next buffer" "n" #'next-buffer :desc "New empty buffer" "N" #'evil-buffer-new :desc "Kill other buffers" "o" #'doom/kill-other-buffers @@ -674,6 +684,7 @@ :desc "Initialize repo" "r" #'magit-init :desc "Clone repo" "R" #'+magit/clone :desc "Commit" "c" #'magit-commit-create + :desc "Fixup" "f" #'magit-commit-fixup :desc "Branch" "b" #'magit-branch-and-checkout :desc "Issue" "i" #'forge-create-issue :desc "Pull request" "p" #'forge-create-pullreq))) @@ -715,6 +726,9 @@ (:when (featurep! :ui treemacs) :desc "Project sidebar" "p" #'+treemacs/toggle :desc "Find file in project sidebar" "P" #'+treemacs/find-file) + (:when (featurep! :term shell) + :desc "Toggle shell popup" "t" #'+shell/toggle + :desc "Open shell here" "T" #'+shell/here) (:when (featurep! :term term) :desc "Toggle terminal popup" "t" #'+term/toggle :desc "Open terminal here" "T" #'+term/here) @@ -724,16 +738,6 @@ (:when (featurep! :term eshell) :desc "Toggle eshell popup" "e" #'+eshell/toggle :desc "Open eshell here" "E" #'+eshell/here) - (:when (featurep! :collab floobits) - (:prefix ("f" . "floobits") - "c" #'floobits-clear-highlights - "f" #'floobits-follow-user - "j" #'floobits-join-workspace - "l" #'floobits-leave-workspace - "R" #'floobits-share-dir-private - "s" #'floobits-summon - "t" #'floobits-follow-mode-toggle - "U" #'floobits-share-dir-public)) (:when (featurep! :tools macos) :desc "Reveal in Finder" "o" #'+macos/reveal-in-finder :desc "Reveal project in Finder" "O" #'+macos/reveal-project-in-finder @@ -795,11 +799,11 @@ (:prefix-map ("s" . "snippets") :desc "View snippet for mode" "/" #'+snippets/find-for-current-mode :desc "View snippet (global)" "?" #'+snippets/find - :desc "Edit snippet" "c" #'+snippet/edit + :desc "Edit snippet" "c" #'+snippets/edit :desc "View private snippet" "f" #'+snippets/find-private :desc "Insert snippet" "i" #'yas-insert-snippet - :desc "New snippet" "n" #'+snippet/new - :desc "New snippet alias" "N" #'+snippet/new-alias + :desc "New snippet" "n" #'+snippets/new + :desc "New snippet alias" "N" #'+snippets/new-alias :desc "Reload snippets" "r" #'yas-reload-all :desc "Create temporary snippet" "s" #'aya-create :desc "Expand temporary snippet" "e" #'aya-expand)) @@ -810,10 +814,10 @@ :desc "Flycheck" "f" #'flycheck-mode :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen :desc "Evil goggles" "g" #'evil-goggles-mode - :desc "Impatient mode" "h" #'+impatient-mode/toggle :desc "Indent guides" "i" #'highlight-indent-guides-mode :desc "Indent style" "I" #'doom/toggle-indent-style :desc "Line numbers" "l" #'doom/toggle-line-numbers + :desc "Word-wrap mode" "w" #'+word-wrap-mode :desc "org-tree-slide mode" "p" #'+org-present/start :desc "Flyspell" "s" #'flyspell-mode)) @@ -831,7 +835,7 @@ whose CDR is for repeating backward. They should both be kbd-able strings.") (defmacro set-repeater! (command next-func prev-func) "Makes ; and , the universal repeat-keys in evil-mode. To change these keys see `+default-repeat-keys'." - (let ((fn-sym (intern (format "+default*repeat-%s" (doom-unquote command))))) + (let ((fn-sym (intern (format "+default/repeat-%s" (doom-unquote command))))) `(progn (defun ,fn-sym (&rest _) (evil-define-key* 'motion 'local diff --git a/modules/config/default/autoload/default.el b/modules/config/default/autoload/default.el index ebf2bc087..89fa2f4dd 100644 --- a/modules/config/default/autoload/default.el +++ b/modules/config/default/autoload/default.el @@ -119,6 +119,7 @@ languages)." (if (and (sp-point-in-comment) comment-line-break-function) (funcall comment-line-break-function) + (delete-horizontal-space t) (newline nil t) (indent-according-to-mode))) @@ -267,7 +268,7 @@ If prefix ARG is set, prompt for a known project to search from." "Conduct a text search in the current project for symbol at point. If prefix ARG is set, prompt for a known project to search from." (interactive - (list current-prefix-arg (thing-at-point 'symbol t))) + (list current-prefix-arg (or (thing-at-point 'symbol t) ""))) (let ((default-directory (if arg (if-let* ((projects (projectile-relevant-known-projects))) @@ -304,6 +305,5 @@ ARG is set, prompt for a known project to search from." (defun +default/org-notes-headlines () "Jump to an Org headline in `org-agenda-files'." (interactive) - (completing-read - "Jump to org headline: " - (doom-completing-read-org-headings org-agenda-files 3 t))) + (doom-completing-read-org-headings + "Jump to org headline: " org-agenda-files 3 t)) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index a358c86dc..e3671d1d9 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -44,13 +44,18 @@ ;; or specific :post-handlers with: ;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC"))) (after! smartparens + ;; Smartparens is broken in `cc-mode' as of Emacs 27. See + ;; . + (unless EMACS27+ + (pushnew! sp--special-self-insert-commands 'c-electric-paren 'c-electric-brace)) + ;; Smartparens' navigation feature is neat, but does not justify how ;; expensive it is. It's also less useful for evil users. This may need to ;; be reactivated for non-evil users though. Needs more testing! - (defun doom|disable-smartparens-navigate-skip-match () - (setq sp-navigate-skip-match nil - sp-navigate-consider-sgml-tags nil)) - (add-hook 'after-change-major-mode-hook #'doom|disable-smartparens-navigate-skip-match) + (add-hook! 'after-change-major-mode-hook + (defun doom-disable-smartparens-navigate-skip-match-h () + (setq sp-navigate-skip-match nil + sp-navigate-consider-sgml-tags nil))) ;; Autopair quotes more conservatively; if I'm next to a word/before another ;; quote, I likely don't want to open a new pair. @@ -101,10 +106,27 @@ ;; intelligently. The result isn't very intelligent (causes redundant ;; characters), so just do it ourselves. (define-key! c++-mode-map "<" nil ">" nil) + + (defun +default-cc-sp-point-is-template-p (id action context) + "Return t if point is in the right place for C++ angle-brackets." + (and (sp-in-code-p id action context) + (cond ((eq action 'insert) + (sp-point-after-word-p id action context)) + ((eq action 'autoskip) + (/= (char-before) 32))))) + + (defun +default-cc-sp-point-after-include-p (id action context) + "Return t if point is in an #include." + (and (sp-in-code-p id action context) + (save-excursion + (goto-char (line-beginning-position)) + (looking-at-p "[ ]*#include[^<]+")))) + ;; ...and leave it to smartparens (sp-local-pair '(c++-mode objc-mode) "<" ">" - :when '(+cc-sp-point-is-template-p +cc-sp-point-after-include-p) + :when '(+default-cc-sp-point-is-template-p + +default-cc-sp-point-after-include-p) :post-handlers '(("| " "SPC"))) (sp-local-pair '(c-mode c++-mode objc-mode java-mode) @@ -138,6 +160,27 @@ :actions '(insert) :post-handlers '(("| " "SPC") ("|\n[i]*)[d-2]" "RET"))))) + (after! smartparens-markdown + (sp-with-modes '(markdown-mode gfm-mode) + (sp-local-pair "```" "```" :post-handlers '(:add ("||\n[i]" "RET"))) + + ;; The original rules for smartparens had an odd quirk: inserting two + ;; asterixex would replace nearby quotes with asterixes. These two rules + ;; set out to fix this. + (sp-local-pair "**" nil :actions :rem) + (sp-local-pair "*" "*" + :actions '(insert skip) + :unless '(:rem sp-point-at-bol-p) + ;; * then SPC will delete the second asterix and assume + ;; you wanted a bullet point. * followed by another * + ;; will produce an extra, assuming you wanted **|**. + :post-handlers '(("[d1]" "SPC") ("|*" "*")))) + + ;; This keybind allows * to skip over **. + (map! :map markdown-mode-map + :ig "*" (λ! (if (looking-at-p "\\*\\* *$") + (forward-char 2) + (call-interactively 'self-insert-command))))) ;; Highjacks backspace to: ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) diff --git a/modules/config/literate/autoload.el b/modules/config/literate/autoload.el index 93880736a..c7226db0b 100644 --- a/modules/config/literate/autoload.el +++ b/modules/config/literate/autoload.el @@ -4,7 +4,7 @@ (defalias '+literate/reload #'doom/reload) ;;;###autoload -(defun +literate|recompile-maybe () +(defun +literate-recompile-maybe-h () "Recompile config.org if we're editing an org file in our DOOMDIR. We assume any org file in `doom-private-dir' is connected to your literate diff --git a/modules/config/literate/init.el b/modules/config/literate/init.el index 4eb3b8d18..050ea65a6 100644 --- a/modules/config/literate/init.el +++ b/modules/config/literate/init.el @@ -1,11 +1,11 @@ ;;; config/literate/init.el -*- lexical-binding: t; -*- (defvar +literate-config-file - (expand-file-name "config.org" doom-private-dir) + (concat doom-private-dir "config.org") "The file path of your literate config file.") (defvar +literate-config-cache-file - (expand-file-name "literate-last-compile" doom-cache-dir) + (concat doom-cache-dir "literate-last-compile") "The file path that `+literate-config-file' will be tangled to, then byte-compiled from.") @@ -13,32 +13,34 @@ byte-compiled from.") ;; (defun +literate-tangle (&optional force-p) "Tangles `+literate-config-file' if it has changed." - (let ((default-directory doom-private-dir) - (org +literate-config-file)) - (when (or force-p (file-newer-than-file-p org +literate-config-cache-file)) + (let ((default-directory doom-private-dir)) + (when (or (file-newer-than-file-p +literate-config-file + +literate-config-cache-file) + force-p) (message "Compiling your literate config...") - (let* ((org (file-truename +literate-config-file)) - (dest (concat (file-name-sans-extension org) ".el"))) - (or (and (if (fboundp 'org-babel-tangle-file) - (org-babel-tangle-file org dest "emacs-lisp") - ;; We tangle in a separate, blank process because loading it - ;; here would load all of :lang org (very expensive!). - (zerop (call-process - "emacs" nil nil nil - "-q" "--batch" "-l" "ob-tangle" "--eval" - (format "(org-babel-tangle-file %S %S \"emacs-lisp\")" - org dest)))) - ;; Write the cache file to serve as our mtime cache - (with-temp-file +literate-config-cache-file - (message "Done!"))) - (warn "There was a problem tangling your literate config!")))))) + (dest (concat (file-name-sans-extension org) ".el")) + (output (get-buffer-create "*org-tangle*"))) + (unwind-protect + ;; We tangle in a separate, blank process because loading it here + ;; would load all of :lang org (very expensive!). + (or (and (zerop (call-process + "emacs" nil output nil + "-q" "--batch" + "-l" "ob-tangle" + "--eval" (format "(org-babel-tangle-file %S %S)" + org dest))) + (with-current-buffer output + (message "%s" (buffer-string)) + t) + ;; Write the cache file to serve as our mtime cache + (with-temp-file +literate-config-cache-file + (message "Done!"))) + (warn "There was a problem tangling your literate config!")) + (kill-buffer output)))))) ;; Let 'er rip! -(when noninteractive - (require 'ob-tangle nil t)) - (+literate-tangle (or doom-reloading-p noninteractive)) ;; No need to load the resulting file. Doom will do this for us after all ;; modules have finished loading. @@ -46,4 +48,4 @@ byte-compiled from.") ;; Recompile our literate config if we modify it (after! org - (add-hook 'after-save-hook #'+literate|recompile-maybe)) + (add-hook 'after-save-hook #'+literate-recompile-maybe-h)) diff --git a/modules/editor/evil/+commands.el b/modules/editor/evil/+commands.el index 263dce2db..de2058f01 100644 --- a/modules/editor/evil/+commands.el +++ b/modules/editor/evil/+commands.el @@ -1,100 +1,6 @@ ;;; editor/evil/+commands.el -*- lexical-binding: t; -*- -(evil-define-operator +evil:open-scratch-buffer (bang) - (interactive "") - (doom/open-scratch-buffer bang)) - -(evil-define-command +evil:pwd (bang) - "Display the current working directory. If BANG, copy it to your clipboard." - (interactive "") - (if (not bang) - (pwd) - (kill-new default-directory) - (message "Copied to clipboard"))) - -(evil-define-command +evil:make (arguments &optional bang) - "Run make with ARGUMENTS. -If BANG is non-nil, open compilation output in a comint buffer. - -If BANG, then run ARGUMENTS as a full command. This command understands vim file -modifiers (like %:p:h). See `+evil*resolve-vim-path' for details." - (interactive "") - (+evil:compile (format "make %s" - (evil-ex-replace-special-filenames - arguments)) - bang)) - -(evil-define-command +evil:compile (arguments &optional bang) - "Run `compile-command' with ARGUMENTS. -If BANG is non-nil, open compilation output in a comint buffer. - -This command understands vim file modifiers (like %:p:h). See -`+evil*resolve-vim-path' for details." - (interactive "") - (compile (evil-ex-replace-special-filenames - (format "%s %s" - (eval compile-command) - arguments)) - bang)) - -(evil-define-command +evil:reverse-lines (beg end) - "Reverse lines between BEG and END." - (interactive "") - (reverse-region beg end)) - -(evil-define-command +evil:cd (&optional path) - "Change `default-directory' with `cd'." - (interactive "") - (let ((path (or path "~"))) - (cd path) - (message "Changed directory to '%s'" (abbreviate-file-name (expand-file-name path))))) - -(evil-define-command +evil:kill-all-buffers (&optional bang) - "Kill all buffers. If BANG, kill current session too." - (interactive "") - (if (and bang (fboundp '+workspace/kill-session)) - (+workspace/kill-session) - (doom/kill-all-buffers))) - -(evil-define-command +evil:kill-matching-buffers (&optional bang pattern) - "Kill all buffers matching PATTERN regexp. If BANG, only match project -buffers." - (interactive "") - (doom/kill-matching-buffers pattern bang)) - -(evil-define-command +evil:help (&optional bang query) - "Look up help documentation for QUERY in Emacs documentation. - -If BANG, search Doom documentation." - (interactive "") - (if bang - (doom/help-search query) - (cond ((or (null query) (string-empty-p (string-trim query))) - (call-interactively - (or (command-remapping #'apropos) - #'apropos))) - ((string-match-p "^ *:[a-z]" query) - (let* ((modules - (cl-loop for path in (doom-module-load-path 'all) - for (cat . mod) = (doom-module-from-path path) - for format = (format "%s %s" cat mod) - if (doom-module-p cat mod) - collect (propertize format 'module (list cat mod)) - else if (and cat mod) - collect (propertize format - 'face 'font-lock-comment-face - 'module (list cat mod)))) - (module (completing-read "Describe module: " modules nil t query)) - (key (get-text-property 0 'module module))) - (doom/help-modules key))) - ((and (string-match-p "\\(?:SPC\\|[CMsSH]-[^ ]\\|<[^>]+>\\)" query) - (helpful-key (kbd (string-trim query))))) - ((apropos query t))))) - - ;; -;; Commands - ;;; Custom commands ;; Editing (evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me @@ -142,7 +48,7 @@ If BANG, search Doom documentation." (evil-ex-define-cmd "k[ill]o" #'doom/kill-other-buffers) (evil-ex-define-cmd "k[ill]b" #'doom/kill-buried-buffers) (evil-ex-define-cmd "l[ast]" #'doom/popup-restore) -(evil-ex-define-cmd "m[sg]" #'view-echo-area-messages) +(evil-ex-define-cmd "messages" #'view-echo-area-messages) (evil-ex-define-cmd "pop[up]" #'doom/popup-this-buffer) ;;; Project navigation diff --git a/modules/editor/evil/+everywhere.el b/modules/editor/evil/+everywhere.el index 9a19aab0b..3edab99aa 100644 --- a/modules/editor/evil/+everywhere.el +++ b/modules/editor/evil/+everywhere.el @@ -9,11 +9,10 @@ ;; 2. This ensures a predictable load order, versus lazy loading using :defer or ;; :after-call. This means users can use (after! org ...) and be sure that ;; their changes will override evil-collection's. -;; 3. Eventually, I'd like to remove evil-collection. It changes too often, -;; introduces breaking bugs too frequently, and I don't always agree with -;; their design choices. Regardless, there are useful tidbits I'd like to -;; keep. This will be a slow transition, but this file is where most of it -;; will happen. +;; 3. Ideally, we'd do away with evil-collection entirely. It changes too often, +;; introduces breaking bugs too frequently, and I don't agree with all their +;; design choices. Regardless, it does mork than it causes trouble, so it may +;; be here to stay. ;; 4. Adds `+evil-collection-disabled-list', to make it easier for users to ;; disable modules, and to reduce the effort required to maintain our copy of ;; `evil-collection-list' (now I can just copy it from time to time). @@ -43,8 +42,18 @@ variable for an explanation of the defaults (in comments). See (defvar evil-collection-setup-minibuffer nil) +;; We do this ourselves, and better. +(defvar evil-collection-want-unimpaired-p nil) + ;; This has to be defined here since evil-collection doesn't autoload its own. -;; It must be updated whenever evil-collection updates theirs. +;; It must be updated whenever evil-collection updates theirs. Here's an easy +;; way to update it: +;; +;; (with-current-buffer +;; (url-retrieve-synchronously "https://raw.githubusercontent.com/emacs-evil/evil-collection/master/evil-collection.el" t t) +;; (goto-char (point-min)) +;; (when (re-search-forward "^(defcustom evil-collection-mode-list\n[^(]+") +;; (kill-new (thing-at-point 'sexp t)))) (defvar evil-collection-mode-list `(2048-game ag @@ -152,7 +161,7 @@ variable for an explanation of the defaults (in comments). See rtags simple slime - (term term ansi-term) + (term term ansi-term multi-term) tetris tide transmission @@ -173,15 +182,15 @@ variable for an explanation of the defaults (in comments). See youtube-dl (ztree ztree-diff))) -(defun +evil-collection-init (module) +(defun +evil-collection-init (module &optional disabled-list) "Initialize evil-collection-MODULE. Unlike `evil-collection-init', this respects `+evil-collection-disabled-list', and complains if a module is loaded too early (during startup)." - (unless (memq (or (car-safe module) module) +evil-collection-disabled-list) - (let ((module-sym (or (car-safe module) module))) - (doom-log "Initialized evil-collection-%s %s" - module-sym (if doom-init-time "" "(too early!)"))) + (unless (memq (or (car-safe module) module) disabled-list) + (doom-log "Initialized evil-collection-%s %s" + (or (car-safe module) module) + (if doom-init-time "" "(too early!)")) (with-demoted-errors "evil-collection error: %s" (evil-collection-init (list module))))) @@ -192,9 +201,8 @@ and complains if a module is loaded too early (during startup)." ;; These modes belong to packages that Emacs always loads at startup, causing ;; evil-collection to load immediately. We avoid this by loading them after ;; evil-collection has first loaded... -(after! evil-collection - (let (+evil-collection-disabled-list) - (mapc #'+evil-collection-init '(comint custom help)))) +(with-eval-after-load 'evil-collection + (mapc #'+evil-collection-init '(comint custom help))) ;; ...or on first invokation of their associated major/minor modes. (add-transient-hook! 'Buffer-menu-mode @@ -214,4 +222,4 @@ and complains if a module is loaded too early (during startup)." (dolist (mode evil-collection-mode-list) (dolist (req (or (cdr-safe mode) (list mode))) (with-eval-after-load req - (+evil-collection-init mode)))) + (+evil-collection-init mode +evil-collection-disabled-list)))) diff --git a/modules/editor/evil/autoload/advice.el b/modules/editor/evil/autoload/advice.el index 4b573b10f..0287244fb 100644 --- a/modules/editor/evil/autoload/advice.el +++ b/modules/editor/evil/autoload/advice.el @@ -1,76 +1,13 @@ ;;; editor/evil/autoload/advice.el -*- lexical-binding: t; -*- -(defun +evil--insert-newline (&optional above _noextranewline) - (let ((pos (save-excursion (beginning-of-line-text) (point))) - comment-auto-fill-only-comments) - (require 'smartparens) - (evil-narrow-to-field - (if above - (if (save-excursion (nth 4 (sp--syntax-ppss pos))) - (evil-save-goal-column - (setq evil-auto-indent nil) - (goto-char pos) - (let ((ws (abs (skip-chars-backward " \t")))) - ;; FIXME oh god why - (save-excursion - (if comment-line-break-function - (funcall comment-line-break-function) - (comment-indent-new-line)) - (when (and (derived-mode-p 'c-mode 'c++-mode 'objc-mode 'java-mode 'js2-mode) - (eq (char-after) ?/)) - (insert "*")) - (insert - (make-string (max 0 (+ ws (skip-chars-backward " \t"))) - 32))) - (insert (make-string (max 1 ws) 32)))) - (evil-move-beginning-of-line) - (insert (if use-hard-newlines hard-newline "\n")) - (forward-line -1) - (back-to-indentation)) - (evil-move-end-of-line) - (cond ((sp-point-in-comment pos) - (setq evil-auto-indent nil) - (if comment-line-break-function - (funcall comment-line-break-function) - (comment-indent-new-line))) - ;; TODO Find a better way to do this - ((and (eq major-mode 'haskell-mode) - (fboundp 'haskell-indentation-newline-and-indent)) - (setq evil-auto-indent nil) - (haskell-indentation-newline-and-indent)) - (t - (insert (if use-hard-newlines hard-newline "\n")) - (back-to-indentation))))))) +;;;###autoload +(defun +evil-escape-a (&rest _) + "Call `doom/escape' if `evil-force-normal-state' is called interactively." + (when (called-interactively-p 'any) + (call-interactively #'doom/escape))) ;;;###autoload -(defun +evil*insert-newline-below-and-respect-comments (orig-fn count) - (if (or (not +evil-want-o/O-to-continue-comments) - (not (eq this-command 'evil-open-below)) - (evil-insert-state-p)) - (funcall orig-fn count) - (cl-letf (((symbol-function 'evil-insert-newline-below) - (lambda () (+evil--insert-newline)))) - (let ((evil-auto-indent evil-auto-indent)) - (funcall orig-fn count))))) - -;;;###autoload -(defun +evil*insert-newline-above-and-respect-comments (orig-fn count) - (if (or (not +evil-want-o/O-to-continue-comments) - (not (eq this-command 'evil-open-above)) - (evil-insert-state-p)) - (funcall orig-fn count) - (cl-letf (((symbol-function 'evil-insert-newline-above) - (lambda () (+evil--insert-newline 'above)))) - (let ((evil-auto-indent evil-auto-indent)) - (funcall orig-fn count))))) - -;;;###autoload -(defun +evil*static-reindent (orig-fn &rest args) - "Don't move cursor on indent." - (save-excursion (apply orig-fn args))) - -;;;###autoload -(defun +evil*resolve-vim-path (file-name) +(defun +evil-resolve-vim-path-a (file-name) "Take a path and resolve any vim-like filename modifiers in it. This adds support for most vim file modifiers, as well as: @@ -148,8 +85,77 @@ more information on modifiers." path file-name t t 1)))) (replace-regexp-in-string regexp "\\1" file-name t))) -;;;###autoload (autoload '+evil*window-split "editor/evil/autoload/advice" nil t) -(evil-define-command +evil*window-split (&optional count file) +(defun +evil--insert-newline (&optional above _noextranewline) + (let ((pos (save-excursion (beginning-of-line-text) (point))) + comment-auto-fill-only-comments) + (require 'smartparens) + (evil-narrow-to-field + (if above + (if (save-excursion (nth 4 (sp--syntax-ppss pos))) + (evil-save-goal-column + (setq evil-auto-indent nil) + (goto-char pos) + (let ((ws (abs (skip-chars-backward " \t")))) + ;; FIXME oh god why + (save-excursion + (if comment-line-break-function + (funcall comment-line-break-function) + (comment-indent-new-line)) + (when (and (derived-mode-p 'c-mode 'c++-mode 'objc-mode 'java-mode 'js2-mode) + (eq (char-after) ?/)) + (insert "*")) + (insert + (make-string (max 0 (+ ws (skip-chars-backward " \t"))) + 32))) + (insert (make-string (max 1 ws) 32)))) + (evil-move-beginning-of-line) + (insert (if use-hard-newlines hard-newline "\n")) + (forward-line -1) + (back-to-indentation)) + (evil-move-end-of-line) + (cond ((sp-point-in-comment pos) + (setq evil-auto-indent nil) + (if comment-line-break-function + (funcall comment-line-break-function) + (comment-indent-new-line))) + ;; TODO Find a better way to do this + ((and (eq major-mode 'haskell-mode) + (fboundp 'haskell-indentation-newline-and-indent)) + (setq evil-auto-indent nil) + (haskell-indentation-newline-and-indent)) + (t + (insert (if use-hard-newlines hard-newline "\n")) + (back-to-indentation))))))) + +;;;###autoload +(defun +evil--insert-newline-below-and-respect-comments-a (orig-fn count) + (if (or (not +evil-want-o/O-to-continue-comments) + (not (eq this-command 'evil-open-below)) + (evil-insert-state-p)) + (funcall orig-fn count) + (cl-letf (((symbol-function 'evil-insert-newline-below) + (lambda () (+evil--insert-newline)))) + (let ((evil-auto-indent evil-auto-indent)) + (funcall orig-fn count))))) + +;;;###autoload +(defun +evil--insert-newline-above-and-respect-comments-a (orig-fn count) + (if (or (not +evil-want-o/O-to-continue-comments) + (not (eq this-command 'evil-open-above)) + (evil-insert-state-p)) + (funcall orig-fn count) + (cl-letf (((symbol-function 'evil-insert-newline-above) + (lambda () (+evil--insert-newline 'above)))) + (let ((evil-auto-indent evil-auto-indent)) + (funcall orig-fn count))))) + +;;;###autoload +(defun +evil--static-reindent-a (orig-fn &rest args) + "Don't move cursor on indent." + (save-excursion (apply orig-fn args))) + +;;;###autoload (autoload '+evil-window-split-a "editor/evil/autoload/advice" nil t) +(evil-define-command +evil-window-split-a (&optional count file) "Same as `evil-window-split', but focuses (and recenters) the new split." :repeat nil (interactive "P") @@ -164,8 +170,8 @@ more information on modifiers." (balance-windows (window-parent))) (if file (evil-edit file))) -;;;###autoload (autoload '+evil*window-vsplit "editor/evil/autoload/advice" nil t) -(evil-define-command +evil*window-vsplit (&optional count file) +;;;###autoload (autoload '+evil-window-vsplit-a "editor/evil/autoload/advice" nil t) +(evil-define-command +evil-window-vsplit-a (&optional count file) "Same as `evil-window-vsplit', but focuses (and recenters) the new split." :repeat nil (interactive "P") @@ -181,18 +187,12 @@ more information on modifiers." (if file (evil-edit file))) ;;;###autoload -(defun +evil*escape (&rest _) - "Call `doom/escape' if `evil-force-normal-state' is called interactively." - (when (called-interactively-p 'any) - (call-interactively #'doom/escape))) - -;;;###autoload -(defun +evil*make-numbered-markers-global (orig-fn char) +(defun +evil--make-numbered-markers-global-a (orig-fn char) (or (and (>= char ?2) (<= char ?9)) (funcall orig-fn char))) ;;;###autoload -(defun +evil*fix-dabbrev-in-minibuffer () +(defun +evil--fix-dabbrev-in-minibuffer-h () "Make `try-expand-dabbrev' from `hippie-expand' work in minibuffer. See `he-dabbrev-beg', so we need to redefine syntax for '/'." (set-syntax-table (let* ((table (make-syntax-table))) diff --git a/modules/editor/evil/autoload/evil.el b/modules/editor/evil/autoload/evil.el index e5d0690be..4cb4bd7c3 100644 --- a/modules/editor/evil/autoload/evil.el +++ b/modules/editor/evil/autoload/evil.el @@ -1,5 +1,4 @@ ;; editor/evil/autoload/evil.el -*- lexical-binding: t; -*- -;;;###if (featurep! :editor evil) ;;;###autodef (defun set-evil-initial-state! (modes state) @@ -12,72 +11,6 @@ (evil-set-initial-state modes state)))) -;; -;;; Custom arg handlers - -(defvar +evil--flag nil) - -(defun +evil--ex-match-init (name &optional face update-hook) - (with-current-buffer evil-ex-current-buffer - (cond - ((eq +evil--flag 'start) - (evil-ex-make-hl name - :face (or face 'evil-ex-substitute-matches) - :update-hook (or update-hook #'evil-ex-pattern-update-ex-info)) - (setq +evil--flag 'update)) - - ((eq +evil--flag 'stop) - (evil-ex-delete-hl name))))) - -(defun +evil--ex-buffer-match (arg &optional hl-name flags beg end) - (when (and (eq +evil--flag 'update) - evil-ex-substitute-highlight-all - (not (zerop (length arg)))) - (condition-case lossage - (let ((pattern (evil-ex-make-substitute-pattern - arg - (or flags (list)))) - (range (or (evil-copy-range evil-ex-range) - (evil-range (or beg (line-beginning-position)) - (or end (line-end-position)) - 'line - :expanded t)))) - (evil-expand-range range) - (evil-ex-hl-set-region hl-name - (max (evil-range-beginning range) (window-start)) - (min (evil-range-end range) (window-end))) - (evil-ex-hl-change hl-name pattern)) - (end-of-file - (evil-ex-pattern-update-ex-info nil "incomplete replacement")) - (user-error - (evil-ex-pattern-update-ex-info nil (format "?%s" lossage)))))) - -;;;###autoload -(defun +evil-ex-buffer-match (flag &optional arg) - (let ((hl-name 'evil-ex-buffer-match) - (+evil--flag flag)) - (with-selected-window (minibuffer-selected-window) - (+evil--ex-match-init hl-name) - (+evil--ex-buffer-match arg hl-name (list (if evil-ex-substitute-global ?g)))))) - -;;;###autoload -(defun +evil-ex-global-match (flag &optional arg) - (let ((hl-name 'evil-ex-global-match) - (+evil--flag flag)) - (with-selected-window (minibuffer-selected-window) - (+evil--ex-match-init hl-name) - (+evil--ex-buffer-match arg hl-name nil (point-min) (point-max))))) - -;;;###autoload -(defun +evil-ex-global-delim-match (flag &optional arg) - (let ((hl-name 'evil-ex-global-delim-match) - (+evil--flag flag)) - (with-selected-window (minibuffer-selected-window) - (+evil--ex-match-init hl-name) - (let ((result (car-safe (evil-delimited-arguments arg 2)))) - (+evil--ex-buffer-match result hl-name nil (point-min) (point-max)))))) - - ;; ;;; Interactive commands @@ -167,28 +100,6 @@ integration." t)) prefix))))) -;;;###autoload (autoload '+evil:align "editor/evil/autoload/evil" nil t) -(evil-define-operator +evil:align (beg end pattern &optional bang) - "Ex interface to `align-regexp'. PATTERN is a vim-style regexp. If BANG, -repeat the alignment for all matches (otherwise just the first match on each -line)." - (interactive "") - (align-regexp - beg end - (concat "\\(\\s-*\\)" (evil-transform-vim-style-regexp pattern)) - 1 1 bang)) - -;;;###autoload (autoload '+evil:align-right "editor/evil/autoload/evil" nil t) -(evil-define-operator +evil:align-right (beg end pattern &optional bang) - "Like `+evil:align', except alignments are right-justified. PATTERN is a -vim-style regexp. If BANG, repeat the alignment for all matches (otherwise just -the first match on each line)." - (interactive "") - (align-regexp - beg end - (concat "\\(" (evil-transform-vim-style-regexp pattern) "\\)") - -1 1 bang)) - ;;;###autoload (autoload '+evil:apply-macro "editor/evil/autoload/evil" nil t) (evil-define-operator +evil:apply-macro (beg end) "Apply macro to each line." diff --git a/modules/editor/evil/autoload/ex.el b/modules/editor/evil/autoload/ex.el new file mode 100644 index 000000000..ab7c4919a --- /dev/null +++ b/modules/editor/evil/autoload/ex.el @@ -0,0 +1,183 @@ +;;; editor/evil/autoload/ex.el -*- lexical-binding: t; -*- + +(defvar +evil--flag nil) + +(defun +evil--ex-match-init (name &optional face update-hook) + (with-current-buffer evil-ex-current-buffer + (cond + ((eq +evil--flag 'start) + (evil-ex-make-hl name + :face (or face 'evil-ex-lazy-highlight) + :update-hook (or update-hook #'evil-ex-pattern-update-ex-info)) + (setq +evil--flag 'update)) + + ((eq +evil--flag 'stop) + (evil-ex-delete-hl name))))) + +(defun +evil--ex-buffer-match (arg &optional hl-name flags beg end) + (when (and (eq +evil--flag 'update) + evil-ex-substitute-highlight-all + (not (zerop (length arg)))) + (condition-case lossage + (let* ((pattern (evil-ex-make-substitute-pattern + arg + (or flags (list)))) + (range (or (evil-copy-range evil-ex-range) + (evil-range (or beg (line-beginning-position)) + (or end (line-end-position)) + 'line + :expanded t)))) + (evil-expand-range range) + (evil-ex-hl-set-region hl-name + (max (evil-range-beginning range) (window-start)) + (min (evil-range-end range) (window-end))) + (evil-ex-hl-change hl-name pattern)) + (end-of-file + (evil-ex-pattern-update-ex-info nil "incomplete replacement")) + (user-error + (evil-ex-pattern-update-ex-info nil (format "?%s" lossage)))))) + +;;;###autoload +(defun +evil-ex-regexp-match (flag &optional arg invert) + (let ((hl-name 'evil-ex-buffer-match) + (+evil--flag flag)) + (with-selected-window (minibuffer-selected-window) + (+evil--ex-match-init hl-name) + (cl-destructuring-bind (&optional arg flags) + (evil-delimited-arguments arg 2) + (let ((evil-ex-substitute-global + (if invert + (not evil-ex-substitute-global) + evil-ex-substitute-global))) + (+evil--ex-buffer-match + arg hl-name (string-to-list flags))))))) + + +;; +;;; Ex Commands + +;;;###autoload (autoload '+evil:align "editor/evil/autoload/ex" nil t) +(evil-define-operator +evil:align (beg end pattern &optional flags) + "Ex interface to `align-regexp'. PATTERN is a vim-style regexp. If BANG, +repeat the alignment for all matches (otherwise just the first match on each +line)." + (interactive "") + (align-regexp + beg end + (concat "\\(\\s-*\\)" (evil-transform-vim-style-regexp pattern)) + 1 1 (memq ?g flags))) + +;;;###autoload (autoload '+evil:align-right "editor/evil/autoload/ex" nil t) +(evil-define-operator +evil:align-right (beg end pattern &optional flags) + "Like `+evil:align', except alignments are right-justified. PATTERN is a +vim-style regexp. If BANG, repeat the alignment for all matches (otherwise just +the first match on each line)." + (interactive "") + (align-regexp + beg end + (concat "\\(" (evil-transform-vim-style-regexp pattern) "\\)") + -1 1 (memq ?g flags))) + +;; ;;;###autoload (autoload '+evil:sort "editor/evil/autoload/ex" nil nil) +;; (evil-define-command +evil:sort (beg end &optional pattern flags reverse) +;; (interactive "")) + +;;;###autoload (autoload '+evil:open-scratch-buffer "editor/evil/autoload/ex" nil t) +(evil-define-operator +evil:open-scratch-buffer (bang) + (interactive "") + (doom/open-scratch-buffer bang)) + +;;;###autoload (autoload '+evil:pwd "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:pwd (bang) + "Display the current working directory. If BANG, copy it to your clipboard." + (interactive "") + (if (not bang) + (pwd) + (kill-new default-directory) + (message "Copied to clipboard"))) + +;;;###autoload (autoload '+evil:make "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:make (arguments &optional bang) + "Run make with ARGUMENTS. +If BANG is non-nil, open compilation output in a comint buffer. + +If BANG, then run ARGUMENTS as a full command. This command understands vim file +modifiers (like %:p:h). See `+evil-resolve-vim-path-a' for details." + (interactive "") + (+evil:compile (format "make %s" + (evil-ex-replace-special-filenames + arguments)) + bang)) + +;;;###autoload (autoload '+evil:compile "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:compile (arguments &optional bang) + "Run `compile-command' with ARGUMENTS. +If BANG is non-nil, open compilation output in a comint buffer. + +This command understands vim file modifiers (like %:p:h). See +`+evil-resolve-vim-path-a' for details." + (interactive "") + (compile (evil-ex-replace-special-filenames + (format "%s %s" + (eval compile-command) + arguments)) + bang)) + +;;;###autoload (autoload '+evil:reverse-lines "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:reverse-lines (beg end) + "Reverse lines between BEG and END." + (interactive "") + (reverse-region beg end)) + +;;;###autoload (autoload '+evil:cd "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:cd (&optional path) + "Change `default-directory' with `cd'." + (interactive "") + (let ((path (or path "~"))) + (cd path) + (message "Changed directory to '%s'" (abbreviate-file-name (expand-file-name path))))) + +;;;###autoload (autoload '+evil:kill-all-buffers "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:kill-all-buffers (&optional bang) + "Kill all buffers. If BANG, kill current session too." + (interactive "") + (if (and bang (fboundp '+workspace/kill-session)) + (+workspace/kill-session) + (doom/kill-all-buffers))) + +;;;###autoload (autoload '+evil:kill-matching-buffers "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:kill-matching-buffers (&optional bang pattern) + "Kill all buffers matching PATTERN regexp. If BANG, only match project +buffers." + (interactive "") + (doom/kill-matching-buffers pattern bang)) + +;;;###autoload (autoload '+evil:help "editor/evil/autoload/ex" nil t) +(evil-define-command +evil:help (&optional bang query) + "Look up help documentation for QUERY in Emacs documentation. + +If BANG, search Doom documentation." + (interactive "") + (if bang + (doom/help-search query) + (cond ((or (null query) (string-empty-p (string-trim query))) + (call-interactively + (or (command-remapping #'apropos) + #'apropos))) + ((string-match-p "^ *:[a-z]" query) + (let* ((modules + (cl-loop for path in (doom-module-load-path 'all) + for (cat . mod) = (doom-module-from-path path) + for format = (format "%s %s" cat mod) + if (doom-module-p cat mod) + collect (propertize format 'module (list cat mod)) + else if (and cat mod) + collect (propertize format + 'face 'font-lock-comment-face + 'module (list cat mod)))) + (module (completing-read "Describe module: " modules nil t query)) + (key (get-text-property 0 'module module))) + (doom/help-modules key))) + ((and (string-match-p "\\(?:SPC\\|[CMsSH]-[^ ]\\|<[^>]+>\\)" query) + (helpful-key (kbd (string-trim query))))) + ((apropos query t))))) diff --git a/modules/editor/evil/autoload/unimpaired.el b/modules/editor/evil/autoload/unimpaired.el index e2fcf4b52..5a3fe6ce0 100644 --- a/modules/editor/evil/autoload/unimpaired.el +++ b/modules/editor/evil/autoload/unimpaired.el @@ -44,8 +44,8 @@ (user-error "Must be called from a file-visiting buffer")) (let* ((directory (file-name-directory buffer-file-name)) (filename (file-name-nondirectory buffer-file-name)) - (files (doom-files-in directory :depth 0 :sort t :match "/[^._][^/]*$")) - (index (cl-position filename files :test #'string=))) + (files (doom-glob (file-name-directory buffer-file-name) "[!.]*")) + (index (cl-position filename files :test #'file-equal-p))) (when (null index) (user-error "Couldn't find this file in current directory")) (let ((index (+ index n))) diff --git a/modules/editor/evil/config.el b/modules/editor/evil/config.el index 6af39c522..720e0514e 100644 --- a/modules/editor/evil/config.el +++ b/modules/editor/evil/config.el @@ -18,8 +18,9 @@ directives. By default, this only recognizes C directives.") (defvar evil-want-C-u-scroll t) (defvar evil-want-C-w-scroll t) (defvar evil-want-Y-yank-to-eol t) +(defvar evil-want-abbrev-expand-on-insert-exit nil) -(def-package! evil +(use-package! evil :hook (doom-init-modules . evil-mode) :demand t :preface @@ -36,9 +37,9 @@ directives. By default, this only recognizes C directives.") ;; more vim-like behavior evil-symbol-word-search t ;; cursor appearance - evil-default-cursor '+evil-default-cursor + evil-default-cursor '+evil-default-cursor-fn evil-normal-state-cursor 'box - evil-emacs-state-cursor '(box +evil-emacs-cursor) + evil-emacs-state-cursor '(box +evil-emacs-cursor-fn) evil-insert-state-cursor 'bar evil-visual-state-cursor 'hollow ;; must be set before evil/evil-collection is loaded @@ -49,15 +50,21 @@ directives. By default, this only recognizes C directives.") (put 'evil-define-key* 'lisp-indent-function 'defun) + ;; stop copying each visual state move to the clipboard: + ;; https://bitbucket.org/lyro/evil/issue/336/osx-visual-state-copies-the-region-on + ;; grokked from: + ;; http://stackoverflow.com/questions/15873346/elisp-rename-macro + (advice-add #'evil-visual-update-x-selection :override #'ignore) + ;; Start help-with-tutorial in emacs state (advice-add #'help-with-tutorial :after (lambda (&rest _) (evil-emacs-state +1))) ;; Done in a hook to ensure the popup rules load as late as possible - (defun +evil|init-popup-rules () - (set-popup-rules! - '(("^\\*evil-registers" :size 0.3) - ("^\\*Command Line" :size 8)))) - (add-hook 'doom-init-modules-hook #'+evil|init-popup-rules) + (add-hook! 'doom-init-modules-hook + (defun +evil--init-popup-rules-h () + (set-popup-rules! + '(("^\\*evil-registers" :size 0.3) + ("^\\*Command Line" :size 8))))) ;; Change the cursor color in emacs state. We do it this roundabout way ;; instead of changing `evil-default-cursor' (or `evil-emacs-state-cursor') so @@ -65,19 +72,17 @@ directives. By default, this only recognizes C directives.") (defvar +evil--default-cursor-color "#ffffff") (defvar +evil--emacs-cursor-color "#ff9999") - (defun +evil|update-cursor-color () - (setq +evil--default-cursor-color (face-background 'cursor) - +evil--emacs-cursor-color (face-foreground 'warning))) - (add-hook 'doom-load-theme-hook #'+evil|update-cursor-color) + (add-hook! 'doom-load-theme-hook + (defun +evil-update-cursor-color-h () + (setq +evil--default-cursor-color (face-background 'cursor) + +evil--emacs-cursor-color (face-foreground 'warning)))) - (defun +evil-default-cursor () + (defun +evil-default-cursor-fn () (evil-set-cursor-color +evil--default-cursor-color)) - (defun +evil-emacs-cursor () + (defun +evil-emacs-cursor-fn () (evil-set-cursor-color +evil--emacs-cursor-color)) - (defun +evil|update-shift-width () - (setq evil-shift-width tab-width)) - (add-hook 'after-change-major-mode-hook #'+evil|update-shift-width) + (setq-hook! 'after-change-major-mode-hook evil-shift-width tab-width) ;; --- keybind fixes ---------------------- @@ -86,92 +91,101 @@ directives. By default, this only recognizes C directives.") ;; `evil-delete' in wgrep buffers. (define-key wgrep-mode-map [remap evil-delete] #'+evil-delete)) - (defun +evil|disable-highlights () - "Disable ex search buffer highlights." - (when (evil-ex-hl-active-p 'evil-ex-search) - (evil-ex-nohighlight) - t)) - (add-hook 'doom-escape-hook #'+evil|disable-highlights) + (add-hook! 'doom-escape-hook + (defun +evil-disable-ex-highlights-h () + "Disable ex search buffer highlights." + (when (evil-ex-hl-active-p 'evil-ex-search) + (evil-ex-nohighlight) + t))) ;; --- evil hacks ------------------------- - (defun +evil|display-vimlike-save-message () - "Shorter, vim-esque save messages." - (message "\"%s\" %dL, %dC written" - (if buffer-file-name - (file-relative-name (file-truename buffer-file-name) (doom-project-root)) - (buffer-name)) - (count-lines (point-min) (point-max)) - (buffer-size))) (unless noninteractive (setq save-silently t) - (add-hook 'after-save-hook #'+evil|display-vimlike-save-message)) + (add-hook! 'after-save-hook + (defun +evil-display-vimlike-save-message-h () + "Shorter, vim-esque save messages." + (message "\"%s\" %dL, %dC written" + (if buffer-file-name + (file-relative-name (file-truename buffer-file-name) (doom-project-root)) + (buffer-name)) + (count-lines (point-min) (point-max)) + (buffer-size))))) ;; Make ESC (from normal mode) the universal escaper. See `doom-escape-hook'. - (advice-add #'evil-force-normal-state :after #'+evil*escape) + (advice-add #'evil-force-normal-state :after #'+evil-escape-a) ;; Don't move cursor when indenting - (advice-add #'evil-indent :around #'+evil*static-reindent) + (advice-add #'evil-indent :around #'+evil--static-reindent-a) ;; monkey patch `evil-ex-replace-special-filenames' to improve support for ;; file modifiers like %:p:h. This adds support for most of vim's modifiers, ;; and one custom one: %:P (expand to the project root). - (advice-add #'evil-ex-replace-special-filenames :override #'+evil*resolve-vim-path) + (advice-add #'evil-ex-replace-special-filenames :override #'+evil-resolve-vim-path-a) ;; make `try-expand-dabbrev' (from `hippie-expand') work in minibuffer - (add-hook 'minibuffer-inactive-mode-hook #'+evil*fix-dabbrev-in-minibuffer) + (add-hook 'minibuffer-inactive-mode-hook #'+evil--fix-dabbrev-in-minibuffer-h) ;; Focus and recenter new splits - (advice-add #'evil-window-split :override #'+evil*window-split) - (advice-add #'evil-window-vsplit :override #'+evil*window-vsplit) + (advice-add #'evil-window-split :override #'+evil-window-split-a) + (advice-add #'evil-window-vsplit :override #'+evil-window-vsplit-a) ;; In evil, registers 2-9 are buffer-local. In vim, they're global, so... - (advice-add #'evil-global-marker-p :around #'+evil*make-numbered-markers-global) + (advice-add #'evil-global-marker-p :around #'+evil--make-numbered-markers-global-a) ;; Make o/O continue comments (see `+evil-want-o/O-to-continue-comments') - (advice-add #'evil-open-above :around #'+evil*insert-newline-above-and-respect-comments) - (advice-add #'evil-open-below :around #'+evil*insert-newline-below-and-respect-comments) + (advice-add #'evil-open-above :around #'+evil--insert-newline-above-and-respect-comments-a) + (advice-add #'evil-open-below :around #'+evil--insert-newline-below-and-respect-comments-a) ;; Recenter screen after most searches - (advice-add! '(evil-visualstar/begin-search-forward - evil-visualstar/begin-search-backward - evil-ex-search-word-backward - evil-ex-search-word-backward - evil-ex-search-forward - evil-ex-search-backward) - :after #'doom*recenter) + (dolist (fn '(evil-visualstar/begin-search-forward + evil-visualstar/begin-search-backward + evil-ex-search-word-backward + evil-ex-search-word-backward + evil-ex-search-forward + evil-ex-search-backward)) + (advice-add fn :after #'doom-recenter-a)) ;; --- custom interactive codes ----------- ;; These arg types will highlight matches in the current buffer - (evil-ex-define-argument-type buffer-match :runner +evil-ex-buffer-match) - (evil-ex-define-argument-type global-match :runner +evil-ex-global-match) + (evil-ex-define-argument-type regexp-match + :runner (lambda (flag &optional arg) (+evil-ex-regexp-match flag arg 'inverted))) + (evil-ex-define-argument-type regexp-global-match + :runner +evil-ex-regexp-match) + + (defun +evil--regexp-match-args (arg) + (when (evil-ex-p) + (cl-destructuring-bind (&optional arg flags) + (evil-delimited-arguments arg 2) + (list arg (string-to-list flags))))) + ;; Other commands can make use of this (evil-define-interactive-code "" - :ex-arg buffer-match (list (if (evil-ex-p) evil-ex-argument))) - (evil-define-interactive-code "" - :ex-arg global-match (list (if (evil-ex-p) evil-ex-argument))) + :ex-arg regexp-match + (+evil--regexp-match-args evil-ex-argument)) - ;; By default :g[lobal] doesn't highlight matches in the current buffer. I've - ;; got to write my own argument type and interactive code to get it to do so. - (evil-ex-define-argument-type global-delim-match :runner +evil-ex-global-delim-match) - (dolist (sym '(evil-ex-global evil-ex-global-inverted)) - (evil-set-command-property sym :ex-arg 'global-delim-match)) + (evil-define-interactive-code "" + :ex-arg regexp-global-match + (+evil--regexp-match-args evil-ex-argument)) ;; Forward declare these so that ex completion works, even if the autoloaded ;; functions aren't loaded yet. - (evil-set-command-properties - '+evil:align :move-point t :ex-arg 'buffer-match :ex-bang t :keep-visual t :suppress-operator t) + (evil-add-command-properties '+evil:align :ex-arg 'regexp-match) + (evil-add-command-properties '+evil:align-right :ex-arg 'regexp-match) + (evil-add-command-properties '+multiple-cursors:evil-mc :ex-arg 'regexp-global-match) ;; `evil-collection' (when (and (featurep! +everywhere) (not doom-reloading-p)) (load! "+everywhere")) - ;; Custom evil ex commands - (load! "+commands")) + ;; Lazy load evil ex commands + (delq! 'evil-ex features) + (add-transient-hook! 'evil-ex (provide 'evil-ex)) + (after! evil-ex (load! "+commands"))) ;; ;; Packages -(def-package! evil-commentary +(use-package! evil-commentary :commands (evil-commentary evil-commentary-yank evil-commentary-yank-line @@ -179,8 +193,8 @@ directives. By default, this only recognizes C directives.") :config (evil-commentary-mode 1)) -(def-package! evil-easymotion - :commands (evilem-create evilem-default-keybindings) +(use-package! evil-easymotion + :commands evilem-create evilem-default-keybindings :config ;; Use evil-search backend, instead of isearch (evilem-make-motion evilem-motion-search-next #'evil-ex-search-next @@ -194,27 +208,27 @@ directives. By default, this only recognizes C directives.") :bind ((evil-ex-search-highlight-all nil)))) -(def-package! evil-embrace - :commands (embrace-add-pair embrace-add-pair-regexp) +(use-package! evil-embrace + :commands embrace-add-pair embrace-add-pair-regexp :hook (LaTeX-mode . embrace-LaTeX-mode-hook) :hook (org-mode . embrace-org-mode-hook) :hook ((ruby-mode enh-ruby-mode) . embrace-ruby-mode-hook) :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) :hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode) - . +evil|embrace-lisp-mode-hook) - :hook ((org-mode LaTeX-mode) . +evil|embrace-latex-mode-hook) + . +evil-embrace-lisp-mode-hook-h) + :hook ((org-mode LaTeX-mode) . +evil-embrace-latex-mode-hook-h) :hook ((c++-mode rust-mode rustic-mode csharp-mode java-mode swift-mode typescript-mode) - . +evil|embrace-angle-bracket-modes-hook) + . +evil-embrace-angle-bracket-modes-hook-h) :init (after! evil-surround (evil-embrace-enable-evil-surround-integration)) :config (setq evil-embrace-show-help-p nil) - (defun +evil|embrace-latex-mode-hook () + (defun +evil-embrace-latex-mode-hook-h () (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex)) - (defun +evil|embrace-lisp-mode-hook () + (defun +evil-embrace-lisp-mode-hook-h () (push (cons ?f (make-embrace-pair-struct :key ?f :read-function #'+evil--embrace-elisp-fn @@ -222,7 +236,7 @@ directives. By default, this only recognizes C directives.") :right-regexp ")")) embrace--pairs-list)) - (defun +evil|embrace-angle-bracket-modes-hook () + (defun +evil-embrace-angle-bracket-modes-hook-h () (set (make-local-variable 'evil-embrace-evil-surround-keys) (delq ?< evil-embrace-evil-surround-keys)) (push (cons ?< (make-embrace-pair-struct @@ -241,9 +255,9 @@ directives. By default, this only recognizes C directives.") :right-regexp "\\[]})]"))) -(def-package! evil-escape - :commands (evil-escape) - :after-call (evil-normal-state-exit-hook) +(use-package! evil-escape + :commands evil-escape + :after-call evil-normal-state-exit-hook :init (setq evil-escape-excluded-states '(normal visual multiedit emacs motion) evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode) @@ -257,19 +271,21 @@ directives. By default, this only recognizes C directives.") (evil-escape-mode +1)) -(def-package! evil-exchange +(use-package! evil-exchange :commands evil-exchange :config - (defun +evil|escape-exchange () - (when evil-exchange--overlays - (evil-exchange-cancel) - t)) - (add-hook 'doom-escape-hook #'+evil|escape-exchange)) + (add-hook! 'doom-escape-hook + (defun +evil--escape-exchange-h () + (when evil-exchange--overlays + (evil-exchange-cancel) + t)))) -(def-package! evil-snipe - :commands (evil-snipe-mode evil-snipe-override-mode - evil-snipe-local-mode evil-snipe-override-local-mode) +(use-package! evil-snipe + :commands (evil-snipe-mode + evil-snipe-override-mode + evil-snipe-local-mode + evil-snipe-override-local-mode) :after-call pre-command-hook :init (setq evil-snipe-smart-case t @@ -282,7 +298,7 @@ directives. By default, this only recognizes C directives.") (evil-snipe-override-mode +1)) -(def-package! evil-surround +(use-package! evil-surround :commands (global-evil-surround-mode evil-surround-edit evil-Surround-edit @@ -290,8 +306,17 @@ directives. By default, this only recognizes C directives.") :config (global-evil-surround-mode 1)) +(use-package! evil-traces + :after evil-ex + :config + (pushnew! evil-traces-argument-type-alist + '(+evil:align . evil-traces-global) + '(+evil:align-right . evil-traces-global)) + (evil-traces-mode)) + + ;; Allows you to use the selection for * and # -(def-package! evil-visualstar +(use-package! evil-visualstar :commands (evil-visualstar/begin-search evil-visualstar/begin-search-forward evil-visualstar/begin-search-backward) @@ -302,7 +327,7 @@ directives. By default, this only recognizes C directives.") ;; -;; Text object plugins +;;; Text object plugins -(def-package! exato - :commands (evil-outer-xml-attr evil-inner-xml-attr)) +(use-package! exato + :commands evil-outer-xml-attr evil-inner-xml-attr) diff --git a/modules/editor/evil/packages.el b/modules/editor/evil/packages.el index 4c345d5e8..aed8f38bc 100644 --- a/modules/editor/evil/packages.el +++ b/modules/editor/evil/packages.el @@ -9,14 +9,14 @@ (package! evil-escape) (package! evil-exchange) (package! evil-indent-plus) -(package! evil-numbers :recipe (:fetcher github :repo "janpath/evil-numbers")) -(package! evil-textobj-anyblock) +(package! evil-numbers :recipe (:host github :repo "janpath/evil-numbers")) (package! evil-snipe) (package! evil-surround) +(package! evil-textobj-anyblock) +(package! evil-traces) (package! evil-visualstar) (package! exato) - ;; (when (featurep! +everywhere) ;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this diff --git a/modules/editor/evil/test/test-evil.el b/modules/editor/evil/test/test-evil.el index 40aedb594..240a74b13 100644 --- a/modules/editor/evil/test/test-evil.el +++ b/modules/editor/evil/test/test-evil.el @@ -10,10 +10,10 @@ (after-all (unload-feature 'evil t)) (before-each - (fset 'resv #'+evil*resolve-vim-path) + (fset 'resv #'+evil-resolve-vim-path-a) (spy-on 'doom-project-root :and-call-fake (lambda () project-root))) - ;; `evil-ex-replace-special-filenames' / `+evil*resolve-vim-path' + ;; `evil-ex-replace-special-filenames' / `+evil-resolve-vim-path-a' (describe "file modifiers" (it "supports basic vim file modifiers" (let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el") diff --git a/modules/editor/file-templates/config.el b/modules/editor/file-templates/config.el index e9557c639..754afd78c 100644 --- a/modules/editor/file-templates/config.el +++ b/modules/editor/file-templates/config.el @@ -114,11 +114,12 @@ information.") (or (file-in-directory-p file doom-private-dir) (file-in-directory-p file doom-emacs-dir))) -(defun +file-templates|check () +(defun +file-templates-check-h () "Check if the current buffer is a candidate for file template expansion. It must be non-read-only, empty, and there must be a rule in `+file-templates-alist' that applies to it." - (when (and (not buffer-read-only) + (when (and (not (file-exists-p (or (buffer-file-name) ""))) + (not buffer-read-only) (bobp) (eobp) (not (string-match-p "^ *\\*" (buffer-name)))) (when-let (rule (cl-find-if #'+file-template-p +file-templates-alist)) @@ -143,4 +144,4 @@ must be non-read-only, empty, and there must be a rule in (yas-reload-all))) ;; -(add-hook 'find-file-hook #'+file-templates|check) +(add-hook 'find-file-hook #'+file-templates-check-h) diff --git a/modules/editor/fold/autoload/hideshow.el b/modules/editor/fold/autoload/hideshow.el index d28dba48d..2a5071cba 100644 --- a/modules/editor/fold/autoload/hideshow.el +++ b/modules/editor/fold/autoload/hideshow.el @@ -6,12 +6,12 @@ :group 'doom-themes) ;;;###autoload -(defun +fold-hideshow-haml-forward-sexp (arg) +(defun +fold-hideshow-haml-forward-sexp-fn (arg) (haml-forward-sexp arg) (move-beginning-of-line 1)) ;;;###autoload -(defun +fold-hideshow-forward-block-by-indent (_arg) +(defun +fold-hideshow-forward-block-by-indent-fn (_arg) (let ((start (current-indentation))) (forward-line) (unless (= start (current-indentation)) @@ -20,7 +20,7 @@ (end-of-line))))) ;;;###autoload -(defun +fold-hideshow-set-up-overlay (ov) +(defun +fold-hideshow-set-up-overlay-fn (ov) (when (eq 'code (overlay-get ov 'hs)) (when (featurep 'vimish-fold) (overlay-put diff --git a/modules/editor/fold/config.el b/modules/editor/fold/config.el index 8a1fed409..05ab1000f 100644 --- a/modules/editor/fold/config.el +++ b/modules/editor/fold/config.el @@ -17,21 +17,22 @@ ;; ;; Packages -(def-package! hideshow ; built-in - :commands (hs-toggle-hiding hs-hide-block hs-hide-level hs-show-all hs-hide-all) +(use-package! hideshow ; built-in + :commands (hs-toggle-hiding + hs-hide-block + hs-hide-level + hs-show-all + hs-hide-all) :config (setq hs-hide-comments-when-hiding-all nil ;; Nicer code-folding overlays (with fringe indicators) - hs-set-up-overlay #'+fold-hideshow-set-up-overlay) + hs-set-up-overlay #'+fold-hideshow-set-up-overlay-fn) - (defun +fold-hideshow*ensure-mode (&rest _) + (defadvice! +fold--hideshow-ensure-mode-a (&rest _) "Ensure `hs-minor-mode' is enabled." + :before '(hs-toggle-hiding hs-hide-block hs-hide-level hs-show-all hs-hide-all) (unless (bound-and-true-p hs-minor-mode) (hs-minor-mode +1))) - (advice-add! '(hs-toggle-hiding - hs-hide-block hs-hide-level - hs-show-all hs-hide-all) - :before #'+fold-hideshow*ensure-mode) ;; extra folding support for more languages (unless (assq 't hs-special-modes-alist) @@ -41,8 +42,8 @@ (yaml-mode "\\s-*\\_<\\(?:[^:]+\\)\\_>" "" "#" - +fold-hideshow-forward-block-by-indent nil) - (haml-mode "[#.%]" "\n" "/" +fold-hideshow-haml-forward-sexp nil) + +fold-hideshow-forward-block-by-indent-fn nil) + (haml-mode "[#.%]" "\n" "/" +fold-hideshow-haml-forward-sexp-fn nil) (ruby-mode "class\\|d\\(?:ef\\|o\\)\\|module\\|[[{]" "end\\|[]}]" "#\\|=begin" @@ -61,7 +62,7 @@ '((t)))))) -(def-package! evil-vimish-fold +(use-package! evil-vimish-fold :when (featurep! :editor evil) :commands (evil-vimish-fold/next-fold evil-vimish-fold/previous-fold evil-vimish-fold/delete evil-vimish-fold/delete-all diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index b203505e2..8b6a19441 100644 --- a/modules/editor/format/autoload/format.el +++ b/modules/editor/format/autoload/format.el @@ -98,7 +98,7 @@ Stolen shamelessly from go-mode" (if fmt (cons (intern fmt) t)))) ;;;###autoload -(defun +format*probe (orig-fn) +(defun +format-probe-a (orig-fn) "Use `+format-with' instead, if it is set." (if +format-with (list +format-with t) @@ -119,7 +119,7 @@ formatted text, ERRORS are any errors in string format, and FIRST-DIFF is the position of the first change in the buffer. See `+format/buffer' for the interactive version of this function, and -`+format|buffer' to use as a `before-save-hook' hook." +`+format-buffer-h' to use as a `before-save-hook' hook." (if (not formatter) 'no-formatter (let ((f-function (gethash formatter format-all--format-table)) @@ -228,12 +228,12 @@ is selected)." ;; Hooks ;;;###autoload -(defun +format|enable-on-save () +(defun +format-enable-on-save-h () "Enables formatting on save." - (add-hook 'before-save-hook #'+format|buffer nil t)) + (add-hook 'before-save-hook #'+format-buffer-h nil t)) ;;;###autoload -(defalias '+format|buffer #'+format/buffer +(defalias '+format-buffer-h #'+format/buffer "Format the source code in the current buffer with minimal feedback. Meant for `before-save-hook'.") diff --git a/modules/editor/format/config.el b/modules/editor/format/config.el index 20460750d..92dae226a 100644 --- a/modules/editor/format/config.el +++ b/modules/editor/format/config.el @@ -25,7 +25,7 @@ Indentation is always preserved when formatting regions.") ;; ;;; Bootstrap -(defun +format|enable-on-save-maybe () +(defun +format-enable-on-save-maybe-h () "Enable formatting on save in certain major modes. This is controlled by `+format-on-save-enabled-modes'." @@ -39,7 +39,7 @@ This is controlled by `+format-on-save-enabled-modes'." (format-all-mode +1))) (when (featurep! +onsave) - (add-hook 'after-change-major-mode-hook #'+format|enable-on-save-maybe)) + (add-hook 'after-change-major-mode-hook #'+format-enable-on-save-maybe-h)) ;; @@ -47,7 +47,7 @@ This is controlled by `+format-on-save-enabled-modes'." ;; Allow a specific formatter to be used by setting `+format-with', either ;; buffer-locally or let-bound. -(advice-add #'format-all--probe :around #'+format*probe) +(advice-add #'format-all--probe :around #'+format-probe-a) ;; Doom uses a modded `format-all-buffer', which ;; 1. Enables partial reformatting (while preserving leading indentation), diff --git a/modules/editor/lispy/config.el b/modules/editor/lispy/config.el index 21e1d64dd..3d78c72f5 100644 --- a/modules/editor/lispy/config.el +++ b/modules/editor/lispy/config.el @@ -1,6 +1,6 @@ ;;; editor/lispy/config.el -*- lexical-binding: t; -*- -(def-package! lispy +(use-package! lispy :hook ((common-lisp-mode . lispy-mode) (emacs-lisp-mode . lispy-mode) (scheme-mode . lispy-mode) @@ -12,7 +12,7 @@ (setq lispy-close-quotes-at-end-p t) (add-hook 'lispy-mode-hook #'turn-off-smartparens-mode)) -(def-package! lispyville +(use-package! lispyville :when (featurep! :editor evil) :hook (lispy-mode . lispyville-mode) :config diff --git a/modules/editor/multiple-cursors/config.el b/modules/editor/multiple-cursors/config.el index 8e061ba02..9ceb9e394 100644 --- a/modules/editor/multiple-cursors/config.el +++ b/modules/editor/multiple-cursors/config.el @@ -1,18 +1,27 @@ ;;; editor/multiple-cursors/config.el -*- lexical-binding: t; -*- -(def-package! evil-mc +(use-package! evil-mc :when (featurep! :editor evil) - :commands (evil-mc-make-cursor-here evil-mc-make-all-cursors - evil-mc-undo-all-cursors evil-mc-pause-cursors - evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor + :commands (evil-mc-make-cursor-here + evil-mc-make-all-cursors + evil-mc-undo-all-cursors + evil-mc-pause-cursors + evil-mc-resume-cursors + evil-mc-make-and-goto-first-cursor evil-mc-make-and-goto-last-cursor evil-mc-make-cursor-move-next-line - evil-mc-make-cursor-move-prev-line evil-mc-make-cursor-at-pos - evil-mc-has-cursors-p evil-mc-make-and-goto-next-cursor - evil-mc-skip-and-goto-next-cursor evil-mc-make-and-goto-prev-cursor - evil-mc-skip-and-goto-prev-cursor evil-mc-make-and-goto-next-match - evil-mc-skip-and-goto-next-match evil-mc-skip-and-goto-next-match - evil-mc-make-and-goto-prev-match evil-mc-skip-and-goto-prev-match) + evil-mc-make-cursor-move-prev-line + evil-mc-make-cursor-at-pos + evil-mc-has-cursors-p + evil-mc-make-and-goto-next-cursor + evil-mc-skip-and-goto-next-cursor + evil-mc-make-and-goto-prev-cursor + evil-mc-skip-and-goto-prev-cursor + evil-mc-make-and-goto-next-match + evil-mc-skip-and-goto-next-match + evil-mc-skip-and-goto-next-match + evil-mc-make-and-goto-prev-match + evil-mc-skip-and-goto-prev-match) :init (defvar evil-mc-key-map (make-sparse-keymap)) :config @@ -39,13 +48,13 @@ ;; disable evil-escape in evil-mc; causes unwanted text on invocation (add-to-list 'evil-mc-incompatible-minor-modes 'evil-escape-mode nil #'eq) - (defun +multiple-cursors|escape-multiple-cursors () - "Clear evil-mc cursors and restore state." - (when (evil-mc-has-cursors-p) - (evil-mc-undo-all-cursors) - (evil-mc-resume-cursors) - t)) - (add-hook 'doom-escape-hook #'+multiple-cursors|escape-multiple-cursors) + (add-hook! 'doom-escape-hook + (defun +multiple-cursors-escape-multiple-cursors-h () + "Clear evil-mc cursors and restore state." + (when (evil-mc-has-cursors-p) + (evil-mc-undo-all-cursors) + (evil-mc-resume-cursors) + t))) ;; Forward declare these so that ex completion and evil-mc support is ;; recognized before the autoloaded functions are loaded. @@ -71,45 +80,45 @@ (defvar +mc--compat-evil-prev-state nil) (defvar +mc--compat-mark-was-active nil) - (defun +multiple-cursors|compat-switch-to-emacs-state () - (when (and (bound-and-true-p evil-mode) - (not (memq evil-state '(insert emacs)))) - (setq +mc--compat-evil-prev-state evil-state) - (when (region-active-p) - (setq +mc--compat-mark-was-active t)) - (let ((mark-before (mark)) - (point-before (point))) - (evil-emacs-state 1) - (when (or +mc--compat-mark-was-active (region-active-p)) - (goto-char point-before) - (set-mark mark-before))))) - (add-hook 'multiple-cursors-mode-enabled-hook #'+multiple-cursors|compat-switch-to-emacs-state) + (add-hook! 'multiple-cursors-mode-enabled-hook + (defun +multiple-cursors-compat-switch-to-emacs-state-h () + (when (and (bound-and-true-p evil-mode) + (not (memq evil-state '(insert emacs)))) + (setq +mc--compat-evil-prev-state evil-state) + (when (region-active-p) + (setq +mc--compat-mark-was-active t)) + (let ((mark-before (mark)) + (point-before (point))) + (evil-emacs-state 1) + (when (or +mc--compat-mark-was-active (region-active-p)) + (goto-char point-before) + (set-mark mark-before)))))) - (defun +multiple-cursors|compat-back-to-previous-state () - (when +mc--compat-evil-prev-state - (unwind-protect - (case +mc--compat-evil-prev-state - ((normal visual) (evil-force-normal-state)) - (t (message "Don't know how to handle previous state: %S" - +mc--compat-evil-prev-state))) - (setq +mc--compat-evil-prev-state nil) - (setq +mc--compat-mark-was-active nil)))) - (add-hook 'multiple-cursors-mode-disabled-hook #'+multiple-cursors|compat-back-to-previous-state) + (add-hook! 'multiple-cursors-mode-disabled-hook + (defun +multiple-cursors-compat-back-to-previous-state-h () + (when +mc--compat-evil-prev-state + (unwind-protect + (case +mc--compat-evil-prev-state + ((normal visual) (evil-force-normal-state)) + (t (message "Don't know how to handle previous state: %S" + +mc--compat-evil-prev-state))) + (setq +mc--compat-evil-prev-state nil) + (setq +mc--compat-mark-was-active nil))))) - ;; When running edit-lines, point will return (position + 1) as a - ;; result of how evil deals with regions - (defun +multiple-cursors*adjust-mark-for-evil (&rest _) + ;; When running edit-lines, point will return (position + 1) as a result of + ;; how evil deals with regions + (defadvice! +multiple--cursors-adjust-mark-for-evil-a (&rest _) + :before #'mc/edit-lines (when (and (bound-and-true-p evil-mode) (not (memq evil-state '(insert emacs)))) (if (> (point) (mark)) (goto-char (1- (point))) (push-mark (1- (mark)))))) - (advice-add #'mc/edit-lines :before #'+multiple-cursors*adjust-mark-for-evil) - (defun +multiple-cursors|evil-compat-rect-switch-state () - (if rectangular-region-mode - (+multiple-cursors|compat-switch-to-emacs-state) - (setq +mc--compat-evil-prev-state nil))) - (add-hook 'rectangular-region-mode-hook '+multiple-cursors|evil-compat-rect-switch-state) + (add-hook! 'rectangular-region-mode-hook + (defun +multiple-cursors-evil-compat-rect-switch-state-h () + (if rectangular-region-mode + (+multiple-cursors-compat-switch-to-emacs-state-h) + (setq +mc--compat-evil-prev-state nil)))) (defvar mc--default-cmds-to-run-once nil))) diff --git a/modules/editor/objed/config.el b/modules/editor/objed/config.el index bd58d4997..adfabed32 100644 --- a/modules/editor/objed/config.el +++ b/modules/editor/objed/config.el @@ -1,9 +1,8 @@ ;;; editor/objed/config.el -*- lexical-binding: t; -*- -(def-package! objed +(use-package! objed :after-call pre-command-hook :config - ;; Prevent undo actions from exiting edit state (add-to-list 'objed-keeper-commands 'undo-tree-undo) (add-to-list 'objed-keeper-commands 'undo-tree-redo) @@ -11,21 +10,20 @@ (defvar +objed--extra-face-remaps nil) - (defun +objed*add-face-remaps (&rest _) + (defadvice! +objed--add-face-remaps-a (&rest _) "Add extra face remaps when objed activates." + :after 'objed--init (when (memq 'objed-hl (assq 'hl-line face-remapping-alist)) (push (face-remap-add-relative 'solaire-hl-line-face 'objed-hl) +objed--extra-face-remaps))) - (defun +objed*remove-face-remaps (&rest _) + (defadvice! +objed--remove-face-remaps-a (&rest _) "Remove extra face remaps when objed de-activates." + :after 'objed--reset (unless (memq 'objed-hl (assq 'hl-line face-remapping-alist)) (dolist (remap +objed--extra-face-remaps) (face-remap-remove-relative remap)) (setq +objed--extra-face-remaps nil))) - (advice-add 'objed--init :after #'+objed*add-face-remaps) - (advice-add 'objed--reset :after #'+objed*remove-face-remaps) - (unless (featurep! +manual) (objed-mode +1))) diff --git a/modules/editor/parinfer/config.el b/modules/editor/parinfer/config.el index 15f59cac5..93e481da2 100644 --- a/modules/editor/parinfer/config.el +++ b/modules/editor/parinfer/config.el @@ -1,6 +1,6 @@ ;;; editor/parinfer/config.el -*- lexical-binding: t; -*- -(def-package! parinfer +(use-package! parinfer :hook ((emacs-lisp-mode clojure-mode scheme-mode lisp-mode) . parinfer-mode) :init (setq parinfer-extensions diff --git a/modules/editor/rotate-text/autoload.el b/modules/editor/rotate-text/autoload.el index f696ead51..30ede3ab1 100644 --- a/modules/editor/rotate-text/autoload.el +++ b/modules/editor/rotate-text/autoload.el @@ -10,7 +10,7 @@ `rotate-text' will cycle through." (declare (indent defun)) (dolist (mode (doom-enlist modes)) - (let ((fn-name (intern (format "+rotate-text|init-%s" mode)))) + (let ((fn-name (intern (format "+rotate-text-init-%s-h" mode)))) (fset fn-name (lambda () (setq-local rotate-text-local-symbols symbols) diff --git a/modules/editor/rotate-text/packages.el b/modules/editor/rotate-text/packages.el index 955e531ed..5d6486109 100644 --- a/modules/editor/rotate-text/packages.el +++ b/modules/editor/rotate-text/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/rotate-text/packages.el -(package! rotate-text :recipe (:fetcher github :repo "debug-ito/rotate-text.el")) +(package! rotate-text :recipe (:host github :repo "debug-ito/rotate-text.el")) diff --git a/modules/editor/snippets/autoload/snippets.el b/modules/editor/snippets/autoload/snippets.el index 1aaa9df14..685ccce18 100644 --- a/modules/editor/snippets/autoload/snippets.el +++ b/modules/editor/snippets/autoload/snippets.el @@ -19,7 +19,7 @@ you will be prompted to select one. If there are conflicting keys across the two camps, the built-in ones are ignored. This makes it easy to override built-in snippets with private ones." (when (eq this-command 'yas-expand) - (let* ((gc-cons-threshold doom-gc-cons-upper-limit) + (let* ((gc-cons-threshold most-positive-fixnum) (choices (cl-remove-duplicates choices :test #'+snippets--remove-p))) (if (cdr choices) (cl-loop for fn in (cdr (memq '+snippets-prompt-private yas-prompt-functions)) @@ -256,7 +256,7 @@ shadow the default snippet)." +snippets-dir)))))))) ;;;###autoload -(defun +snippets|show-hints-in-header-line () +(defun +snippets-show-hints-in-header-line-h () (setq header-line-format (substitute-command-keys (concat "\\[yas-load-snippet-buffer-and-close] to finish, " @@ -268,7 +268,7 @@ shadow the default snippet)." ;;; Hooks ;;;###autoload -(defun +snippets|enable-project-modes (mode &rest _) +(defun +snippets-enable-project-modes-h (mode &rest _) "Automatically enable snippet libraries for project minor modes defined with `def-project-mode!'." (if (symbol-value mode) @@ -276,7 +276,7 @@ shadow the default snippet)." (yas-deactivate-extra-mode mode))) ;;;###autoload -(defun +snippets|read-only-maybe () +(defun +snippets-read-only-maybe-h () "Enable `read-only-mode' if snippet is built-in." (when (file-in-directory-p default-directory doom-local-dir) (read-only-mode 1) @@ -287,7 +287,7 @@ shadow the default snippet)." ;;; Advice ;;;###autoload -(defun +snippets*expand-on-region (orig-fn &optional no-condition) +(defun +snippets-expand-on-region-a (orig-fn &optional no-condition) "Fix off-by-one issue with expanding snippets on an evil visual region, and switches to insert mode. diff --git a/modules/editor/snippets/config.el b/modules/editor/snippets/config.el index 5052608d3..ff195c85d 100644 --- a/modules/editor/snippets/config.el +++ b/modules/editor/snippets/config.el @@ -7,22 +7,31 @@ ;; ;; Packages -(def-package! yasnippet - :commands (yas-minor-mode-on yas-expand yas-expand-snippet yas-lookup-snippet - yas-insert-snippet yas-new-snippet yas-visit-snippet-file) +(use-package! yasnippet + :commands (yas-minor-mode-on + yas-expand + yas-expand-snippet + yas-lookup-snippet + yas-insert-snippet + yas-new-snippet + yas-visit-snippet-file) :init ;; Ensure `yas-reload-all' is called as late as possible. Other modules could ;; have additional configuration for yasnippet. For example, file-templates. (add-transient-hook! 'yas-minor-mode-hook (yas-reload-all)) - (add-hook! (text-mode prog-mode conf-mode snippet-mode) - #'yas-minor-mode-on) + (add-hook! '(text-mode-hook + prog-mode-hook + conf-mode-hook + snippet-mode-hook) + #'yas-minor-mode-on) :config (setq yas-verbosity (if doom-debug-mode 3 0) yas-also-auto-indent-first-line t ;; Remove default ~/.emacs.d/snippets - yas-snippet-dirs (delete yas--default-user-snippets-dir yas-snippet-dirs)) + yas-snippet-dirs (delete yas--default-user-snippets-dir + yas-snippet-dirs)) ;; default snippets library, if available (require 'doom-snippets nil t) @@ -31,14 +40,14 @@ (add-to-list 'yas-snippet-dirs '+snippets-dir nil #'eq) ;; Remove GUI dropdown prompt (prefer ivy/helm) - (setq yas-prompt-functions (delq 'yas-dropdown-prompt yas-prompt-functions)) + (delq! 'yas-dropdown-prompt yas-prompt-functions) ;; Prioritize private snippets in `+snippets-dir' over built-in ones if there ;; are multiple choices. (add-to-list 'yas-prompt-functions #'+snippets-prompt-private 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) + (add-hook 'doom-project-hook #'+snippets-enable-project-modes-h) ;; Exit snippets on ESC from normal mode (add-hook 'doom-escape-hook #'yas-abort-snippet) @@ -48,16 +57,16 @@ (advice-add #'yas-expand :before #'sp-remove-active-pair-overlay)) ;; Enable `read-only-mode' for built-in snippets (in `doom-local-dir') - (add-hook 'snippet-mode-hook #'+snippets|read-only-maybe) + (add-hook 'snippet-mode-hook #'+snippets-read-only-maybe-h) ;; (Evil only) fix off-by-one issue with line-wise visual selections in ;; `yas-insert-snippet', and switches to insert mode afterwards. - (advice-add #'yas-insert-snippet :around #'+snippets*expand-on-region) + (advice-add #'yas-insert-snippet :around #'+snippets-expand-on-region-a) (define-key! snippet-mode-map "C-c C-k" #'+snippet--abort "C-c C-e" #'+snippet--edit) - (add-hook 'snippet-mode-hook #'+snippets|show-hints-in-header-line) + (add-hook 'snippet-mode-hook #'+snippets-show-hints-in-header-line-h) ;; Replace commands with superior alternatives (define-key! yas-minor-mode-map @@ -65,5 +74,15 @@ [remap yas-visit-snippet-file] #'+snippets/edit)) -;;;###package auto-yasnippet -(setq aya-persist-snippets-dir (concat doom-etc-dir "auto-snippets/")) +(use-package! auto-yasnippet + :defer t + :init (setq aya-persist-snippets-dir (concat doom-etc-dir "auto-snippets/")) + :config + (defadvice! +snippets--inhibit-yas-global-mode-a (orig-fn &rest args) + "auto-yasnippet enables `yas-global-mode'. This is obnoxious for folks like +us who use yas-minor-mode and enable yasnippet more selectively. This advice +swaps `yas-global-mode' with `yas-minor-mode'." + :around '(aya-expand aya-open-line) + (cl-letf (((symbol-function #'yas-global-mode) #'yas-minor-mode) + (yas-global-mode yas-minor-mode)) + (apply orig-fn args)))) diff --git a/modules/editor/snippets/packages.el b/modules/editor/snippets/packages.el index 64123c6d3..f0274ca26 100644 --- a/modules/editor/snippets/packages.el +++ b/modules/editor/snippets/packages.el @@ -5,6 +5,6 @@ (package! auto-yasnippet) (package! doom-snippets - :recipe (:fetcher github + :recipe (:host github :repo "hlissner/doom-snippets" :files ("*.el" "snippets"))) diff --git a/modules/editor/word-wrap/README.org b/modules/editor/word-wrap/README.org new file mode 100644 index 000000000..2b6f37187 --- /dev/null +++ b/modules/editor/word-wrap/README.org @@ -0,0 +1,73 @@ +#+TITLE: editor/word-wrap +#+DATE: August 26, 2019 +#+SINCE: v2.1 + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#configuration][Configuration]] + +* Description +This module adds a minor-mode ~+word-wrap-mode~, which intelligently wraps long +lines in the buffer without modifying the buffer content. + +Wrapped lines will be indented to match the preceding line. Lines which are not +inside a comment will have extra indentation as determined by +~+word-wrap-extra-indent~. The default is to increase the indent by twice the +major-mode indent. + +The ~+word-wrap-extra-indent~ variable supports the following values: +- ~double~: indent by twice the major-mode indentation +- ~single~: indent by the major-mode indentation +- a positive integer: indent by this fixed amount +- a negative integer: dedent by this fixed amount +- ~nil~: no extra indent + +This module also includes a global minor-mode ~+global-word-wrap-mode~ to +automatically enable wrapping in most buffers. Wrapping will not be enabled in +buffers whose major mode is marked "special", or are listed in +~+word-wrap-disabled-modes~. + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://elpa.gnu.org/packages/adaptive-wrap.html][adaptive-wrap]] + +* Configuration +Word wrapping is not enabled by default. + +Wrapping can be toggled in the current buffer with ~M-x +word-wrap-mode~. The +default doom bindings bind this to ~SPC t w~ for ~evil~ users. + +To enable wrapping in a specific mode, add it to the appropriate hook in your +~config.el~: + +#+BEGIN_SRC emacs-lisp +;; enable word-wrap in C/C++/ObjC/Java +(add-hook 'c-mode-common-hook #'+word-wrap-mode) +#+END_SRC + +To customize the extra indent for a specific mode: + +#+BEGIN_SRC emacs-lisp +;; enable word-wrap with fixed extra 2 spaces in org-mode +(add-hook! 'org-mode-hook + (setq-local +word-wrap-extra-indent 2) + (+word-wrap-mode +1)) +#+END_SRC + +To turn on word wrapping (almost) everywhere: + +#+BEGIN_SRC emacs-lisp +;; enable word-wrap (almost) everywhere +(+global-word-wrap-mode +1) +#+END_SRC + +To disable global word-wrapping in a specific mode: + +#+BEGIN_SRC emacs-lisp +;; disable global word-wrap in emacs-lisp-mode +(add-to-list '+word-wrap-disabled-modes 'emacs-lisp-mode) +#+END_SRC diff --git a/modules/editor/word-wrap/autoload.el b/modules/editor/word-wrap/autoload.el new file mode 100644 index 000000000..f024dd6a0 --- /dev/null +++ b/modules/editor/word-wrap/autoload.el @@ -0,0 +1,76 @@ +;;; editor/word-wrap/autoload.el -*- lexical-binding: t; -*- + +(defvar +word-wrap--prev-adaptive-wrap-mode nil) +(defvar +word-wrap--prev-visual-line-mode nil) +(defvar +word-wrap--major-mode-indent-var nil) + +(defun +word-wrap--adjust-extra-indent-a (orig-fn beg end) + "Contextually adjust extra word-wrap indentation." + (let ((adaptive-wrap-extra-indent (+word-wrap--calc-extra-indent beg))) + (funcall orig-fn beg end))) + +(defun +word-wrap--calc-extra-indent (p) + "Calculate extra word-wrap indentation at point." + (if (not (sp-point-in-comment p)) + (pcase +word-wrap-extra-indent + ('double + (* 2 (symbol-value +word-wrap--major-mode-indent-var))) + ('single + (symbol-value +word-wrap--major-mode-indent-var)) + ((and (pred integerp) fixed) + fixed) + (_ 0)) + 0)) + +;;;###autoload +(define-minor-mode +word-wrap-mode + "Wrap long lines in the buffer with language-aware indentation. + +This mode configures `adaptive-wrap' and `visual-line-mode' to wrap long lines +without modifying the buffer content. This is useful when dealing with legacy +code which contains gratuitously long lines, or running emacs on your +wrist-phone. + +Wrapped lines will be indented to match the preceding line. Lines which are not +inside a comment will have additional indentation according to the configuration +of `+word-wrap-extra-indent'." + :init-value nil + (if +word-wrap-mode + (progn + (require 'adaptive-wrap) + (require 'dtrt-indent) ; for dtrt-indent--search-hook-mapping + (require 'smartparens) ; for sp-point-in-string-or-comment + + (setq-local +word-wrap--prev-adaptive-wrap-mode adaptive-wrap-prefix-mode) + (setq-local +word-wrap--prev-visual-line-mode visual-line-mode) + (setq-local +word-wrap--major-mode-indent-var + (caddr (dtrt-indent--search-hook-mapping major-mode))) + + (advice-add #'adaptive-wrap-fill-context-prefix :around #'+word-wrap--adjust-extra-indent-a) + + (unless +word-wrap--prev-adaptive-wrap-mode + (adaptive-wrap-prefix-mode +1)) + (unless +word-wrap--prev-visual-line-mode + (visual-line-mode +1))) + + ;; disable +word-wrap-mode + (advice-remove #'adaptive-wrap-fill-context-prefix #'+word-wrap--adjust-extra-indent-a) + + (unless +word-wrap--prev-adaptive-wrap-mode + (adaptive-wrap-prefix-mode -1)) + (unless +word-wrap--prev-visual-line-mode + (visual-line-mode -1)))) + +(defun +word-wrap--enable-global-mode () + "Enable `+word-wrap-mode' for `+word-wrap-global-mode'. + +Wrapping will be automatically enabled in all modes except special modes, or +modes explicitly listed in `+word-wrap-disabled-modes'." + (unless (or (eq (get major-mode 'mode-class) 'special) + (memq major-mode +word-wrap-disabled-modes)) + (+word-wrap-mode +1))) + +;;;###autoload +(define-globalized-minor-mode +global-word-wrap-mode + +word-wrap-mode + +word-wrap--enable-global-mode) diff --git a/modules/editor/word-wrap/config.el b/modules/editor/word-wrap/config.el new file mode 100644 index 000000000..f45faa88b --- /dev/null +++ b/modules/editor/word-wrap/config.el @@ -0,0 +1,15 @@ +;;; editor/word-wrap/config.el -*- lexical-binding: t; -*- + +(defvar +word-wrap-extra-indent 'double + "The amount of extra indentation for wrapped non-comment lines. + +When 'double, indent by twice the major-mode indentation. +When 'single, indent by the major-mode indentation. +When a positive integer, indent by this fixed amount. +When a negative integer, dedent by this fixed amount. +Otherwise no extra indentation will be used.") + +(defvar +word-wrap-disabled-modes + '(fundamental-mode so-long-mode) + "Major-modes where `+global-word-wrap-mode' should not enable + `+word-wrap-mode'.") diff --git a/modules/editor/word-wrap/packages.el b/modules/editor/word-wrap/packages.el new file mode 100644 index 000000000..85d7b2c55 --- /dev/null +++ b/modules/editor/word-wrap/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; editor/word-wrap/packages.el + +(package! adaptive-wrap) diff --git a/modules/emacs/dired/README.org b/modules/emacs/dired/README.org new file mode 100644 index 000000000..c1118555a --- /dev/null +++ b/modules/emacs/dired/README.org @@ -0,0 +1,46 @@ +#+TITLE: emacs/dired +#+DATE: May 27, 2018 +#+SINCE: v2.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#keybindings][Keybindings]] + +* Description + +This module provides configuration for dired. + +** Module Flags + ++ =+ranger= Enables dired to be more like [[https://github.com/ranger/ranger][ranger]]. ++ =+icons= Enables the display of fancy icons depending on file types in dired + buffers. + +** Plugins ++ [[https:https://github.com/syohex/emacs-dired-k][dired-k]] ++ [[https://github.com/purcell/diredfl][diredfl]] ++ [[https://github.com/stsquad/dired-rsync][dired-rsync]] ++ =+ranger= + + [[https://github.com/ralesi/ranger.el][ranger]] ++ =+icons= + + [[https://github.com/jtbm37/all-the-icons-dired][all-the-icons-dired]] + +* Prerequisites + +This module has no prerequisites *except on BSDs* where =GNU ls= (~gls~) is +needed for it to work properly (Doom doctor will notify you if it is missing). + +* Keybindings + +| Keybind | Description | +|-----------+------------------------------| +| =SPC f d= | Find directory with dired | +| =q= | Exit dired buffer | +| =C-c C-r= | Run =dired-rsync= | +| =C-c C-e= | Rename entries with =wdired= | + +Other keybindings can be found on the official [[https://www.gnu.org/software/emacs/refcards/pdf/dired-ref.pdf][Dired reference card]]. diff --git a/modules/emacs/dired/config.el b/modules/emacs/dired/config.el index 5691f9301..a51f8b9ba 100755 --- a/modules/emacs/dired/config.el +++ b/modules/emacs/dired/config.el @@ -1,11 +1,15 @@ ;;; tools/dired/config.el -*- lexical-binding: t; -*- -(def-package! dired +(use-package! dired :commands dired-jump :init (setq ;; Always copy/delete recursively dired-recursive-copies 'always dired-recursive-deletes 'top + ;; Instantly revert Dired buffers on re-visiting them, with no message. + ;; (A message is shown if insta-revert is either disabled or determined + ;; dynamically by setting this variable to a function.) + dired-auto-revert-buffer t ;; Auto refresh dired, but be quiet about it dired-hide-details-hide-symlink-targets nil ;; files @@ -33,15 +37,15 @@ "C-c C-e" #'wdired-change-to-wdired-mode)) -(def-package! dired-rsync +(use-package! dired-rsync :general (dired-mode-map "C-c C-r" #'dired-rsync)) -(def-package! diredfl +(use-package! diredfl :hook (dired-mode . diredfl-mode)) -(def-package! diff-hl +(use-package! diff-hl :hook (dired-mode . diff-hl-dired-mode) :init (when (featurep! :tools magit) @@ -51,7 +55,7 @@ (diff-hl-margin-mode)) -(def-package! ranger +(use-package! ranger :when (featurep! +ranger) :after dired :init @@ -64,24 +68,24 @@ (set-popup-rule! "^\\*ranger" :ignore t) - (defun +dired*cleanup-header-line () + (defadvice! +dired--cleanup-header-line-a () "Ranger fails to clean up `header-line-format' when it is closed, so..." + :before #'ranger-revert (dolist (buffer (buffer-list)) (when (buffer-live-p buffer) (with-current-buffer buffer (when (equal header-line-format '(:eval (ranger-header-line))) (setq header-line-format nil)))))) - (advice-add #'ranger-revert :before #'+dired*cleanup-header-line) - (defun +dired*cleanup-mouse1-bind () + (defadvice! +dired--cleanup-mouse1-bind-a () "Ranger binds an anonymous function to mouse-1 after previewing a buffer that prevents the user from escaping the window with the mouse. This command is never cleaned up if the buffer already existed before ranger was initialized, so we have to clean it up ourselves." + :after #'ranger-setup-preview (when (window-live-p ranger-preview-window) (with-current-buffer (window-buffer ranger-preview-window) (local-unset-key [mouse-1])))) - (advice-add #'ranger-setup-preview :after #'+dired*cleanup-mouse1-bind) (setq ranger-cleanup-on-disable t ranger-excluded-extensions '("mkv" "iso" "mp4") @@ -91,13 +95,16 @@ we have to clean it up ourselves." ranger-hide-cursor nil)) -(def-package! all-the-icons-dired +(use-package! all-the-icons-dired :when (featurep! +icons) :hook (dired-mode . all-the-icons-dired-mode)) -(def-package! dired-x +(use-package! dired-x :unless (featurep! +ranger) :hook (dired-mode . dired-omit-mode) :config - (setq dired-omit-verbose nil)) + (setq dired-omit-verbose nil) + ;; Disable the prompt about whether I want to kill the Dired buffer for a + ;; deleted directory. Of course I do! + (setq dired-clean-confirm-killing-deleted-buffers nil)) diff --git a/modules/emacs/electric/config.el b/modules/emacs/electric/config.el index 2075d3c3a..82188a6d2 100644 --- a/modules/emacs/electric/config.el +++ b/modules/emacs/electric/config.el @@ -10,10 +10,9 @@ current line.") (after! electric (setq-default electric-indent-chars '(?\n ?\^?)) - (defun +electric-indent|char (_c) - (when (and (eolp) +electric-indent-words) - (save-excursion - (backward-word) - (looking-at-p (concat "\\<" (regexp-opt +electric-indent-words)))))) - (add-to-list 'electric-indent-functions #'+electric-indent|char nil #'eq)) - + (add-hook! 'electric-indent-functions + (defun +electric-indent-char-fn (_c) + (when (and (eolp) +electric-indent-words) + (save-excursion + (backward-word) + (looking-at-p (concat "\\<" (regexp-opt +electric-indent-words)))))))) diff --git a/modules/emacs/vc/autoload/hydra.el b/modules/emacs/vc/autoload/hydra.el new file mode 100644 index 000000000..7b69d4602 --- /dev/null +++ b/modules/emacs/vc/autoload/hydra.el @@ -0,0 +1,41 @@ +;;; emacs/vc/autoload/hydra.el -*- lexical-binding: t; -*- +;;;###if (featurep! :ui hydra) + +;;;###autoload (autoload '+vc/smerge-hydra/body "emacs/vc/autoload/hydra" nil t) +(defhydra +vc/smerge-hydra (:hint nil + :pre (if (not smerge-mode) (smerge-mode 1)) + ;; Disable `smerge-mode' when quitting hydra if + ;; no merge conflicts remain. + :post (smerge-auto-leave)) + " + [smerge] + Movement Keep Diff Other + ╭─────────────────────────────────────────────────────────╯ + ^_g_^ [_b_] base [_<_] upper/base [_C_] Combine + ^_C-k_^ [_u_] upper [_=_] upper/lower [_r_] resolve + ^_k_ ↑^ [_l_] lower [_>_] base/lower [_R_] remove + ^_j_ ↓^ [_a_] all [_H_] hightlight + ^_C-j_^ [_RET_] current [_E_] ediff ╭────────── + ^_G_^ │ [_q_] quit +" + ("g" (progn (goto-char (point-min)) (smerge-next))) + ("G" (progn (goto-char (point-max)) (smerge-prev))) + ("C-j" smerge-next) + ("C-k" smerge-prev) + ("j" next-line) + ("k" previous-line) + ("b" smerge-keep-base) + ("u" smerge-keep-upper) + ("l" smerge-keep-lower) + ("a" smerge-keep-all) + ("RET" smerge-keep-current) + ("\C-m" smerge-keep-current) + ("<" smerge-diff-base-upper) + ("=" smerge-diff-upper-lower) + (">" smerge-diff-base-lower) + ("H" smerge-refine) + ("E" smerge-ediff) + ("C" smerge-combine-with-next) + ("r" smerge-resolve) + ("R" smerge-kill-current) + ("q" nil :color blue)) diff --git a/modules/emacs/vc/autoload/vc.el b/modules/emacs/vc/autoload/vc.el index 9edaff2ed..c4dedf394 100644 --- a/modules/emacs/vc/autoload/vc.el +++ b/modules/emacs/vc/autoload/vc.el @@ -26,7 +26,7 @@ repository root." (git-link (git-link--select-remote) beg end))))) ;;;###autoload -(defun +vc*update-header-line (revision) +(defun +vc-update-header-line-a (revision) "Show revision details in the header-line, instead of the minibuffer. Sometimes I forget `git-timemachine' is enabled in a buffer. Putting revision @@ -40,42 +40,3 @@ info in the `header-line-format' is a good indication." (propertize author 'face 'git-timemachine-minibuffer-author-face) (propertize sha-or-subject 'face 'git-timemachine-minibuffer-detail-face) date-full date-relative)))) - -;;;###autoload (autoload '+vc-smerge-hydra/body "emacs/vc/autoload/vc" nil t) -(defhydra +vc-smerge-hydra (:hint nil - :pre (if (not smerge-mode) (smerge-mode 1)) - ;; Disable `smerge-mode' when quitting hydra if - ;; no merge conflicts remain. - :post (smerge-auto-leave)) - " - [smerge] - Movement Keep Diff Other - ╭─────────────────────────────────────────────────────────╯ - ^_g_^ [_b_] base [_<_] upper/base [_C_] Combine - ^_C-k_^ [_u_] upper [_=_] upper/lower [_r_] resolve - ^_k_ ↑^ [_l_] lower [_>_] base/lower [_R_] remove - ^_j_ ↓^ [_a_] all [_H_] hightlight - ^_C-j_^ [_RET_] current [_E_] ediff ╭────────── - ^_G_^ │ [_q_] quit -" - ("g" (progn (goto-char (point-min)) (smerge-next))) - ("G" (progn (goto-char (point-max)) (smerge-prev))) - ("C-j" smerge-next) - ("C-k" smerge-prev) - ("j" next-line) - ("k" previous-line) - ("b" smerge-keep-base) - ("u" smerge-keep-upper) - ("l" smerge-keep-lower) - ("a" smerge-keep-all) - ("RET" smerge-keep-current) - ("\C-m" smerge-keep-current) - ("<" smerge-diff-base-upper) - ("=" smerge-diff-upper-lower) - (">" smerge-diff-base-lower) - ("H" smerge-refine) - ("E" smerge-ediff) - ("C" smerge-combine-with-next) - ("r" smerge-resolve) - ("R" smerge-kill-current) - ("q" nil :color blue)) diff --git a/modules/emacs/vc/config.el b/modules/emacs/vc/config.el index 4d5750d23..d3b360a9f 100644 --- a/modules/emacs/vc/config.el +++ b/modules/emacs/vc/config.el @@ -1,59 +1,13 @@ ;;; emacs/vc/config.el -*- lexical-binding: t; -*- -(setq vc-make-backup-files nil) +(when IS-WINDOWS + (setenv "GIT_ASKPASS" "git-gui--askpass")) -(after! git-timemachine - ;; HACK Waiting for https://gitlab.com/pidu/git-timemachine/issues/77 - (defun +vc*git-timemachine-show-commit () - (interactive) - (let ((rev (car git-timemachine-revision))) - (if (fboundp 'magit-revision-mode) - (with-temp-buffer - (save-excursion - (magit-setup-buffer #'magit-revision-mode nil - (magit-buffer-revision rev) - (magit-buffer-range (format "%s^..%s" rev rev)) - (magit-buffer-diff-args nil) - (magit-buffer-diff-files nil)))) - (message "You need to install magit to show commit")))) - (advice-add #'git-timemachine-show-commit :override #'+vc*git-timemachine-show-commit) +;;;###package vc +(setq vc-make-backup-files nil + vc-follow-symlinks t) - ;; Sometimes I forget `git-timemachine' is enabled in a buffer, so instead of - ;; showing revision details in the minibuffer, show them in - ;; `header-line-format', which has better visibility. - (setq git-timemachine-show-minibuffer-details t) - (advice-add #'git-timemachine--show-minibuffer-details :override #'+vc*update-header-line) - - (after! evil - ;; rehash evil keybindings so they are recognized - (add-hook 'git-timemachine-mode-hook #'evil-normalize-keymaps)) - - (when (featurep! :tools magit) - (add-transient-hook! #'git-timemachine-blame (require 'magit-blame)))) - - -;;;###package git-commit -(def-package! git-commit - :after-call after-find-file - :config - (global-git-commit-mode +1) - (set-yas-minor-mode! 'git-commit-mode) - - (defun +vc|enforce-git-commit-conventions () - "See https://chris.beams.io/posts/git-commit/" - (setq fill-column 72 - git-commit-summary-max-length 50 - git-commit-style-convention-checks '(overlong-summary-line non-empty-second-line))) - (add-hook 'git-commit-mode-hook #'+vc|enforce-git-commit-conventions) - - (defun +vc|start-in-insert-state-maybe () - "Start git-commit-mode in insert state if in a blank commit message, -otherwise in default state." - (when (and (bound-and-true-p evil-mode) - (bobp) (eolp)) - (evil-insert-state))) - (add-hook 'git-commit-setup-hook #'+vc|start-in-insert-state-maybe)) (after! vc-annotate (set-popup-rules! @@ -63,6 +17,47 @@ otherwise in default state." '(vc-annotate-mode vc-git-log-view-mode) 'normal)) + +(after! git-timemachine + ;; Sometimes I forget `git-timemachine' is enabled in a buffer, so instead of + ;; showing revision details in the minibuffer, show them in + ;; `header-line-format', which has better visibility. + (setq git-timemachine-show-minibuffer-details t) + (advice-add #'git-timemachine--show-minibuffer-details + :override #'+vc-update-header-line-a) + + (after! evil + ;; rehash evil keybindings so they are recognized + (add-hook 'git-timemachine-mode-hook #'evil-normalize-keymaps)) + + (when (featurep! :tools magit) + (add-transient-hook! #'git-timemachine-blame (require 'magit-blame))) + + (map! :map git-timemachine-mode-map + :n "gtc" #'git-timemachine-show-commit)) + + +(use-package! git-commit + :after-call after-find-file + :config + (global-git-commit-mode +1) + (set-yas-minor-mode! 'git-commit-mode) + + ;; Enforce git commit conventions. + ;; See https://chris.beams.io/posts/git-commit/ + (setq git-commit-summary-max-length 50 + git-commit-style-convention-checks '(overlong-summary-line non-empty-second-line)) + (setq-hook! 'git-commit-mode-hook fill-column 72) + + (add-hook! 'git-commit-setup-hook + (defun +vc-start-in-insert-state-maybe () + "Start git-commit-mode in insert state if in a blank commit message, +otherwise in default state." + (when (and (bound-and-true-p evil-mode) + (bobp) (eolp)) + (evil-insert-state))))) + + (after! smerge-mode (unless EMACS26+ (with-no-warnings diff --git a/modules/email/mu4e/README.org b/modules/email/mu4e/README.org index 46b4818ec..2132fba1d 100644 --- a/modules/email/mu4e/README.org +++ b/modules/email/mu4e/README.org @@ -54,6 +54,17 @@ sudo pacman --noconfirm --needed -S mu sudo pacman -S isync # mbsync sudo pacman -S offlineimap #+END_SRC +** NixOS +#+BEGIN_SRC nix +environment.systemPackages = with pkgs; [ + mu + # And one of the following + isync + offlineimap +]; +#+END_SRC + +[[https://github.com/Emiller88/dotfiles/blob/master/modules/shell/mail.nix][An example of setting up mbsync with home-manager]] * TODO Features diff --git a/modules/email/mu4e/autoload/email.el b/modules/email/mu4e/autoload/email.el index e2636355d..1fb11cf76 100644 --- a/modules/email/mu4e/autoload/email.el +++ b/modules/email/mu4e/autoload/email.el @@ -45,7 +45,7 @@ default/fallback account." (defvar +mu4e-workspace-name "*mu4e*" "TODO") -(add-hook 'mu4e-main-mode-hook #'+mu4e|init) +(add-hook 'mu4e-main-mode-hook #'+mu4e-init-h) ;;;###autoload (defun =mu4e () @@ -69,10 +69,10 @@ default/fallback account." ;; ;; Hooks -(defun +mu4e|init () - (add-hook 'kill-buffer-hook #'+mu4e|kill-mu4e nil t)) +(defun +mu4e-init-h () + (add-hook 'kill-buffer-hook #'+mu4e-kill-mu4e-h nil t)) -(defun +mu4e|kill-mu4e () +(defun +mu4e-kill-mu4e-h () ;; (prolusion-mail-hide) (when (+workspace-exists-p +mu4e-workspace-name) (+workspace/delete +mu4e-workspace-name))) diff --git a/modules/email/mu4e/config.el b/modules/email/mu4e/config.el index 54ab82eab..bfff0d228 100644 --- a/modules/email/mu4e/config.el +++ b/modules/email/mu4e/config.el @@ -10,8 +10,8 @@ (add-to-list 'auto-mode-alist '("\\.\\(?:offlineimap\\|mbsync\\)rc\\'" . conf-mode)) -(def-package! mu4e - :commands (mu4e mu4e-compose-new) +(use-package! mu4e + :commands mu4e mu4e-compose-new :init (provide 'html2text) ; disable obsolete package (setq mu4e-maildir "~/.mail" @@ -88,9 +88,8 @@ (let ((maildir (mu4e-message-field msg :maildir))) (format "%s" (substring maildir 1 (string-match-p "/" maildir 1))))))) - ;; Refresh the current view after marks are executed - (defun +mu4e*refresh (&rest _) (mu4e-headers-rerun-search)) - (advice-add #'mu4e-mark-execute-all :after #'+mu4e*refresh) + (defadvice! +mu4e--refresh-current-view-a (&rest _) + :after #'mu4e-mark-execute-all (mu4e-headers-rerun-search)) (when (featurep! :tools flyspell) (add-hook 'mu4e-compose-mode-hook #'flyspell-mode)) @@ -110,14 +109,14 @@ 'normal)) -(def-package! mu4e-maildirs-extension +(use-package! mu4e-maildirs-extension :after mu4e :config (mu4e-maildirs-extension) (setq mu4e-maildirs-extension-title nil)) -(def-package! org-mu4e +(use-package! org-mu4e :hook (mu4e-compose-mode . org-mu4e-compose-org-mode) :config (setq org-mu4e-link-query-in-headers-mode nil @@ -152,7 +151,7 @@ (defun +mu4e--mark-seen (docid _msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N")) - (delq (assq 'delete mu4e-marks) mu4e-marks) + (delq! 'delete mu4e-marks #'assq) (setf (alist-get 'trash mu4e-marks) (list :char '("d" . "▼") :prompt "dtrash" @@ -169,10 +168,10 @@ ;; Without it, refiling (archiving), trashing, and flagging (starring) email ;; won't properly result in the corresponding gmail action, since the marks ;; are ineffectual otherwise. - (defun +mu4e|gmail-fix-flags (mark msg) - (pcase mark - (`trash (mu4e-action-retag-message msg "-\\Inbox,+\\Trash,-\\Draft")) - (`refile (mu4e-action-retag-message msg "-\\Inbox")) - (`flag (mu4e-action-retag-message msg "+\\Starred")) - (`unflag (mu4e-action-retag-message msg "-\\Starred")))) - (add-hook 'mu4e-mark-execute-pre-hook #'+mu4e|gmail-fix-flags))) + (add-hook! 'mu4e-mark-execute-pre-hook + (defun +mu4e-gmail-fix-flags-h (mark msg) + (pcase mark + (`trash (mu4e-action-retag-message msg "-\\Inbox,+\\Trash,-\\Draft")) + (`refile (mu4e-action-retag-message msg "-\\Inbox")) + (`flag (mu4e-action-retag-message msg "+\\Starred")) + (`unflag (mu4e-action-retag-message msg "-\\Starred"))))))) diff --git a/modules/email/notmuch/autoload.el b/modules/email/notmuch/autoload.el index 3262e28fc..ab7ae5610 100644 --- a/modules/email/notmuch/autoload.el +++ b/modules/email/notmuch/autoload.el @@ -89,7 +89,7 @@ ;; Advice ;;;###autoload -(defun +notmuch*dont-confirm-on-kill-process (orig-fn &rest args) +(defun +notmuch-dont-confirm-on-kill-process-a (orig-fn &rest args) "Don't prompt for confirmation when killing notmuch sentinel." (let (confirm-kill-processes) (apply orig-fn args))) diff --git a/modules/email/notmuch/config.el b/modules/email/notmuch/config.el index ab7268a38..67656bea8 100644 --- a/modules/email/notmuch/config.el +++ b/modules/email/notmuch/config.el @@ -42,26 +42,28 @@ ;; (setq-hook! 'notmuch-show-mode-hook line-spacing 0) - (add-to-list 'doom-real-buffer-functions #'notmuch-interesting-buffer nil #'eq) + (add-hook 'doom-real-buffer-functions #'notmuch-interesting-buffer) - (advice-add #'notmuch-start-notmuch-sentinel :around #'+notmuch*dont-confirm-on-kill-process) + (advice-add #'notmuch-start-notmuch-sentinel :around #'+notmuch-dont-confirm-on-kill-process-a) ;; modeline doesn't have much use in these modes - (add-hook! (notmuch-show-mode notmuch-tree-mode notmuch-search-mode) - #'hide-mode-line-mode)) + (add-hook! '(notmuch-show-mode-hook + notmuch-tree-mode-hook + notmuch-search-mode-hook) + #'hide-mode-line-mode)) -(def-package! org-mime +(use-package! org-mime :after (org notmuch) :config (setq org-mime-library 'mml)) -(def-package! counsel-notmuch +(use-package! counsel-notmuch :when (featurep! :completion ivy) :commands counsel-notmuch :after notmuch) -(def-package! helm-notmuch +(use-package! helm-notmuch :when (featurep! :completion helm) :commands helm-notmuch :after notmuch) diff --git a/modules/email/wanderlust/config.el b/modules/email/wanderlust/config.el index 0eff763a1..75b4dcc85 100644 --- a/modules/email/wanderlust/config.el +++ b/modules/email/wanderlust/config.el @@ -1,6 +1,6 @@ ;;; app/wanderlust/config.el -*- lexical-binding: t; -*- -(def-package! wl +(use-package! wl :defer t :config (setq mail-user-agent 'wl-user-agent @@ -74,6 +74,6 @@ ;; Neither wl-folder-mode or wl-summary-mode are correctly defined as major ;; modes, so `evil-set-initial-state' won't work here. (add-hook! '(wl-folder-mode-hook wl-summary-mode-hook) - #'evil-emacs-state)) + #'evil-emacs-state)) (add-hook 'mime-edit-mode-hook #'auto-fill-mode)) diff --git a/modules/input/chinese/config.el b/modules/input/chinese/config.el index f019f1a6d..666cfe395 100644 --- a/modules/input/chinese/config.el +++ b/modules/input/chinese/config.el @@ -1,28 +1,28 @@ ;;; input/chinese/config.el -*- lexical-binding: t; -*- -(def-package! pyim - :after-call (after-find-file pre-command-hook) +(use-package! pyim + :after-call after-find-file pre-command-hook :config (setq pyim-dcache-directory (concat doom-cache-dir "pyim/") pyim-page-tooltip t default-input-method "pyim")) -(def-package! pangu-spacing +(use-package! pangu-spacing :hook (text-mode . pangu-spacing-mode) :config ;; Always insert `real' space in org-mode. (setq-hook! 'org-mode-hook pangu-spacing-real-insert-separtor t)) -(def-package! fcitx +(use-package! fcitx :after evil :config (when (executable-find "fcitx-remote") (fcitx-evil-turn-on))) -(def-package! ace-pinyin +(use-package! ace-pinyin :after avy :init (setq ace-pinyin-use-avy t) :config (ace-pinyin-global-mode t)) @@ -31,15 +31,20 @@ ;; ;;; Hacks -(defun +chinese*org-html-paragraph (paragraph contents info) +(defadvice! +chinese--org-html-paragraph-a (args) "Join consecutive Chinese lines into a single long line without unwanted space when exporting org-mode to html." - (let* ((fix-regexp "[[:multibyte:]]") - (origin-contents contents) - (fixed-contents - (replace-regexp-in-string - (concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)") - "\\1\\2" - origin-contents))) - (list paragraph fixed-contents info))) -(advice-add #'org-html-paragraph :filter-args #'+chinese*org-html-paragraph) + :filter-args #'org-html-paragraph + (cl-destructuring-bind (paragraph contents info) args + (let* ((fix-regexp "[[:multibyte:]]") + (origin-contents + (replace-regexp-in-string + "<[Bb][Rr] */>" + "" + contents)) + (fixed-contents + (replace-regexp-in-string + (concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)") + "\\1\\2" + origin-contents))) + (list paragraph fixed-contents info)))) diff --git a/modules/input/japanese/config.el b/modules/input/japanese/config.el index 4040234f7..985db25b2 100644 --- a/modules/input/japanese/config.el +++ b/modules/input/japanese/config.el @@ -1,7 +1,7 @@ ;;; input/japanese/config.el -*- lexical-binding: t; -*- -(def-package! migemo - :after-call (after-find-file pre-command-hook) +(use-package! migemo + :after-call after-find-file pre-command-hook :init (setq search-default-regexp-mode nil migemo-options '("-q" "--emacs" "-i" "\a") @@ -14,7 +14,7 @@ (when (executable-find migemo-command) (migemo-init) - (def-package! avy-migemo + (use-package! avy-migemo :after avy :config (avy-migemo-mode 1)) @@ -34,22 +34,27 @@ pangu-spacing-real-insert-separtor t)) -(def-package! ddskk +(use-package! ddskk :general ("C-x j" #'skk-mode)) ;; ;;; Hacks -(defun +japanese*org-html-paragraph (paragraph contents info) +(defadvice! +japanese--org-html-paragraph-a (args) "Join consecutive Japanese lines into a single long line without unwanted space when exporting org-mode to html." - (let* ((fix-regexp "[[:multibyte:]]") - (origin-contents contents) - (fixed-contents - (replace-regexp-in-string - (concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)") - "\\1\\2" - origin-contents))) - (list paragraph fixed-contents info))) -(advice-add #'org-html-paragraph :filter-args #'+japanese*org-html-paragraph) + :filter-args #'org-html-paragraph + (cl-destructuring-bind (paragraph contents info) args + (let* ((fix-regexp "[[:multibyte:]]") + (origin-contents + (replace-regexp-in-string + "<[Bb][Rr] */>" + "" + contents)) + (fixed-contents + (replace-regexp-in-string + (concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)") + "\\1\\2" + origin-contents))) + (list paragraph fixed-contents info)))) diff --git a/modules/lang/agda/config.el b/modules/lang/agda/config.el index bfd443c90..e2cb6ad4a 100644 --- a/modules/lang/agda/config.el +++ b/modules/lang/agda/config.el @@ -1,41 +1,32 @@ ;;; lang/agda/config.el -*- lexical-binding: t; -*- -(defvar +agda-dir - (when (executable-find "agda-mode") - (file-name-directory (shell-command-to-string "agda-mode locate")))) - -(def-package! agda2 - :when +agda-dir - :load-path +agda-dir) - -(def-package! agda2-mode - :defer t - :config - (map! :map agda2-mode-map - :localleader - "?" #'agda2-show-goals - "." #'agda2-goal-and-context-and-inferred - "," #'agda2-goal-and-context - "=" #'agda2-show-constraints - "SPC" #'agda2-give - "a" #'agda2-auto - "b" #'agda2-previous-goal - "c" #'agda2-make-case - "d" #'agda2-infer-type-maybe-toplevel - "e" #'agda2-show-context - "f" #'agda2-next-goal - "gG" #'agda2-go-back - "h" #'agda2-helper-function-type - "l" #'agda2-load - "n" #'agda2-compute-normalised-maybe-toplevel - "p" #'agda2-module-contents-maybe-toplevel - "r" #'agda2-refine - "s" #'agda2-solveAll - "t" #'agda2-goal-type - "w" #'agda2-why-in-scope-maybe-toplevel - (:prefix "x" - "c" #'agda2-compile - "d" #'agda2-remove-annotations - "h" #'agda2-display-implicit-arguments - "q" #'agda2-quit - "r" #'agda2-restart))) +(map! + :after agda2-mode + :map agda2-mode-map + :localleader + "?" #'agda2-show-goals + "." #'agda2-goal-and-context-and-inferred + "," #'agda2-goal-and-context + "=" #'agda2-show-constraints + "SPC" #'agda2-give + "a" #'agda2-auto-maybe-all + "b" #'agda2-previous-goal + "c" #'agda2-make-case + "d" #'agda2-infer-type-maybe-toplevel + "e" #'agda2-show-context + "f" #'agda2-next-goal + "gG" #'agda2-go-back + "h" #'agda2-helper-function-type + "l" #'agda2-load + "n" #'agda2-compute-normalised-maybe-toplevel + "p" #'agda2-module-contents-maybe-toplevel + "r" #'agda2-refine + "s" #'agda2-solveAll + "t" #'agda2-goal-type + "w" #'agda2-why-in-scope-maybe-toplevel + (:prefix "x" + "c" #'agda2-compile + "d" #'agda2-remove-annotations + "h" #'agda2-display-implicit-arguments + "q" #'agda2-quit + "r" #'agda2-restart)) diff --git a/modules/lang/agda/doctor.el b/modules/lang/agda/doctor.el deleted file mode 100644 index e38ccbc03..000000000 --- a/modules/lang/agda/doctor.el +++ /dev/null @@ -1,5 +0,0 @@ -;; -*- lexical-binding: t; no-byte-compile: t; -*- -;;; lang/agda/doctor.el - -(unless (executable-find "agda-mode") - (warn! "Couldn't find agda-mode. Agda support won't work")) diff --git a/modules/lang/agda/packages.el b/modules/lang/agda/packages.el new file mode 100644 index 000000000..35bead2a1 --- /dev/null +++ b/modules/lang/agda/packages.el @@ -0,0 +1,15 @@ +;; -*- no-byte-compile: t; -*- +;;; lang/agda/packages.el + + +(package! agda-input + :recipe + (:host github :repo "agda/agda" + :files ("src/data/emacs-mode/agda-input.el"))) + +(package! agda2-mode + :recipe + (:host github :repo "agda/agda" + :files + ("src/data/emacs-mode/*.el" + (:exclude "agda-input.el")))) diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index 4daf46a24..251840b85 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -7,23 +7,6 @@ ;; ;; Library -;;;###autoload -(defun +cc-sp-point-is-template-p (id action context) - "Return t if point is in the right place for C++ angle-brackets." - (and (sp-in-code-p id action context) - (cond ((eq action 'insert) - (sp-point-after-word-p id action context)) - ((eq action 'autoskip) - (/= (char-before) 32))))) - -;;;###autoload -(defun +cc-sp-point-after-include-p (id action context) - "Return t if point is in an #include." - (and (sp-in-code-p id action context) - (save-excursion - (goto-char (line-beginning-position)) - (looking-at-p "[ ]*#include[^<]+")))) - ;;;###autoload (defun +cc-c++-lineup-inclass (langelem) "Indent inclass lines one level further than access modifier keywords." @@ -120,7 +103,7 @@ simpler." (rtags-call-rc :silent t "-J" (or (doom-project-root) default-directory)))) ;; then irony (when (and (featurep 'irony) irony-mode) - (+cc|irony-init-compile-options))) + (+cc-init-irony-compile-options-h))) ;;;###autoload (defun +cc/imenu () @@ -139,7 +122,7 @@ simpler." ;; Hooks ;;;###autoload -(defun +cc|fontify-constants () +(defun +cc-fontify-constants-h () "Better fontification for preprocessor constants" (when (memq major-mode '(c-mode c++-mode)) (font-lock-add-keywords @@ -149,7 +132,7 @@ simpler." (defvar +cc--project-includes-alist nil) ;;;###autoload -(defun +cc|init-irony-compile-options () +(defun +cc-init-irony-compile-options-h () "Initialize compiler options for irony-mode. It searches for the nearest compilation database and initailizes it, otherwise falling back on `+cc-default-compiler-options' and `+cc-default-include-paths'. @@ -186,7 +169,7 @@ compilation dbs." ;; collect (format "-I%s" path))]))))))) ;;;###autoload -(defun +cc|init-ffap-integration () +(defun +cc-init-ffap-integration-h () "Takes the local project include paths and registers them with ffap. This way, `find-file-at-point' (and `+lookup/file') will know where to find most header files." diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index d6ea77286..5652c8df1 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -32,7 +32,7 @@ This is ignored by ccls.") ;; ;; Packages -(def-package! cc-mode +(use-package! cc-mode :commands (c-mode c++-mode objc-mode java-mode) :mode ("\\.mm\\'" . objc-mode) :init @@ -50,8 +50,10 @@ This is ignored by ccls.") ;; Ensure find-file-at-point works in C modes, must be added before irony ;; and/or lsp hooks are run. - (add-hook! (c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) - #'+cc|init-ffap-integration) + (add-hook! '(c-mode-local-vars-hook + c++-mode-local-vars-hook + objc-mode-local-vars-hook) + #'+cc-init-ffap-integration-h) :config (set-electric! '(c-mode c++-mode objc-mode java-mode) :chars '(?\n ?\} ?\{)) @@ -80,7 +82,7 @@ This is ignored by ccls.") ;;; Better fontification (also see `modern-cpp-font-lock') (add-hook 'c-mode-common-hook #'rainbow-delimiters-mode) - (add-hook! (c-mode c++-mode) #'+cc|fontify-constants) + (add-hook! '(c-mode-hook c++-mode-hook) #'+cc-fontify-constants-h) ;; Custom style, based off of linux (c-add-style @@ -115,37 +117,38 @@ This is ignored by ccls.") (label . 0))))) -(def-package! modern-cpp-font-lock +(use-package! modern-cpp-font-lock :hook (c++-mode . modern-c++-font-lock-mode)) -(def-package! irony +(use-package! irony :unless (featurep! +lsp) :commands (irony-install-server irony-mode) :preface (setq irony-server-install-prefix (concat doom-etc-dir "irony-server/")) :init - (defun +cc|init-irony-mode () - (if (file-directory-p irony-server-install-prefix) - (irony-mode +1) - (message "Irony server isn't installed"))) - (add-hook! (c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) - #'+cc|init-irony-mode) + (add-hook! '(c-mode-local-vars-hook + c++-mode-local-vars-hook + objc-mode-local-vars-hook) + (defun +cc-init-irony-mode-h () + (if (file-directory-p irony-server-install-prefix) + (irony-mode +1) + (message "Irony server isn't installed")))) :config (setq irony-cdb-search-directory-list '("." "build" "build-conda")) ;; Initialize compilation database, if present. Otherwise, fall back on ;; `+cc-default-compiler-options'. - (add-hook 'irony-mode-hook #'+cc|init-irony-compile-options) + (add-hook 'irony-mode-hook #'+cc-init-irony-compile-options-h) - (def-package! irony-eldoc + (use-package! irony-eldoc :hook (irony-mode . irony-eldoc)) - (def-package! flycheck-irony + (use-package! flycheck-irony :when (featurep! :tools flycheck) :config (flycheck-irony-setup)) - (def-package! company-irony + (use-package! company-irony :when (featurep! :completion company) :init (set-company-backend! 'irony-mode @@ -157,17 +160,17 @@ This is ignored by ccls.") ;; ;; Major modes -(def-package! company-cmake ; for `cmake-mode' +(use-package! company-cmake ; for `cmake-mode' :when (featurep! :completion company) :after cmake-mode :config (set-company-backend! 'cmake-mode 'company-cmake)) -(def-package! demangle-mode +(use-package! demangle-mode :hook llvm-mode) -(def-package! company-glsl ; for `glsl-mode' +(use-package! company-glsl ; for `glsl-mode' :when (featurep! :completion company) :after glsl-mode :config (set-company-backend! 'glsl-mode 'company-glsl)) @@ -176,19 +179,20 @@ This is ignored by ccls.") ;; ;; Rtags Support -(def-package! rtags +(use-package! rtags :unless (featurep! +lsp) :commands rtags-executable-find :preface (setq rtags-install-path (concat doom-etc-dir "rtags/")) :init - (defun +cc|init-rtags () - "Start an rtags server in c-mode and c++-mode buffers." - (when (and (require 'rtags nil t) - (rtags-executable-find rtags-rdm-binary-name)) - (rtags-start-process-unless-running))) - (add-hook! (c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) - #'+cc|init-rtags) + (add-hook! '(c-mode-local-vars-hook + c++-mode-local-vars-hook + objc-mode-local-vars-hook) + (defun +cc-init-rtags-h () + "Start an rtags server in c-mode and c++-mode buffers." + (when (and (require 'rtags nil t) + (rtags-executable-find rtags-rdm-binary-name)) + (rtags-start-process-unless-running)))) :config (setq rtags-autostart-diagnostics t rtags-use-bookmarks nil @@ -199,9 +203,11 @@ This is ignored by ccls.") ('default)) ;; These executables are named rtags-* on debian rtags-rc-binary-name - (cl-find-if #'executable-find (list rtags-rc-binary-name "rtags-rc")) + (or (cl-find-if #'executable-find (list rtags-rc-binary-name "rtags-rc")) + rtags-rc-binary-name) rtags-rdm-binary-name - (cl-find-if #'executable-find (list rtags-rdm-binary-name "rtags-rdm")) + (or (cl-find-if #'executable-find (list rtags-rdm-binary-name "rtags-rdm")) + rtags-rdm-binary-name) ;; If not using ivy or helm to view results, use a pop-up window rather ;; than displaying it in the current window... rtags-results-buffer-other-window t @@ -224,17 +230,22 @@ This is ignored by ccls.") ;; ;; LSP -(def-package! ccls +(when (featurep! +lsp) + (add-hook! '(c-mode-local-vars-hook + c++-mode-local-vars-hook + objc-mode-local-vars-hook) + (defun +cc-init-lsp-h () + (setq-local company-transformers nil) + (setq-local company-lsp-async t) + (setq-local company-lsp-cache-candidates nil) + (lsp!)))) + + +(use-package! ccls :when (featurep! +lsp) - :hook ((c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) . +cc|init-ccls) + :after lsp :init (after! projectile (add-to-list 'projectile-globally-ignored-directories ".ccls-cache") (add-to-list 'projectile-project-root-files-bottom-up ".ccls-root") - (add-to-list 'projectile-project-root-files-top-down-recurring "compile_commands.json")) - :config - (defun +cc|init-ccls () - (setq-local company-transformers nil) - (setq-local company-lsp-async t) - (setq-local company-lsp-cache-candidates nil) - (lsp!))) + (add-to-list 'projectile-project-root-files-top-down-recurring "compile_commands.json"))) diff --git a/modules/lang/cc/packages.el b/modules/lang/cc/packages.el index 5a73da2f9..ae5401fc5 100644 --- a/modules/lang/cc/packages.el +++ b/modules/lang/cc/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/cc/packages.el -(package! cmake-mode) +(package! cmake-mode :recipe (:host github :repo "emacsmirror/cmake-mode" :files (:defaults "*"))) (package! cuda-mode) (package! demangle-mode) (package! disaster) @@ -10,7 +10,7 @@ (when (package! glsl-mode) (when (featurep! :completion company) - (package! company-glsl :recipe (:fetcher github :repo "Kaali/company-glsl")))) + (package! company-glsl :recipe (:host github :repo "Kaali/company-glsl")))) (if (featurep! +lsp) (package! ccls) diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index d6cf9e32c..e115f4c15 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -4,7 +4,7 @@ (add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) -(def-package! cider +(use-package! cider ;; NOTE: if you don't have an org directory set (the dir doesn't exist), ;; cider jack in won't work. :commands (cider-jack-in cider-jack-in-clojurescript) @@ -27,7 +27,6 @@ cider-font-lock-dynamically '(macro core function var) cider-overlays-use-font-lock t cider-prompt-for-symbol nil - cider-repl-display-help-banner nil cider-repl-history-display-duplicates nil cider-repl-history-display-style 'one-line cider-repl-history-file (concat doom-cache-dir "cider-repl-history") @@ -43,10 +42,29 @@ cider-repl-wrap-history nil cider-stacktrace-default-filters '(tooling dup)) + ;; Error messages emitted from CIDER is silently funneled into *nrepl-server* + ;; rather than the *cider-repl* buffer. How silly. We might want to see that + ;; stuff and who's going to check *nrepl-server* on every startup? I've got a + ;; better idea: we copy these errors into the *cider-repl* buffer. + (add-hook! 'cider-connected-hook + (defun +clojure--cider-dump-nrepl-server-log-h () + "Copy contents of *nrepl-server* to beginning of *cider-repl*." + (save-excursion + (goto-char (point-min)) + (insert + (with-current-buffer nrepl-server-buffer + (buffer-string)))))) + + ;; The CIDER welcome message obscures error messages that the above code is + ;; supposed to be make visible. + (setq cider-repl-display-help-banner nil) + (map! (:localleader - (:map clojure-mode-map - "'" #'cider-jack-in - "\"" #'cider-jack-in-clojurescript + (:map (clojure-mode-map clojurescript-mode-map) + "'" #'cider-jack-in-clj + "\"" #'cider-jack-in-cljs + "c" #'cider-connect-clj + "C" #'cider-connect-cljs (:prefix ("e" . "eval") "d" #'cider-eval-defun-at-point @@ -67,6 +85,7 @@ "g" #'cider-grimoire-web "j" #'cider-javadoc) (:prefix ("i" . "inspect") + "e" #'cider-enlighten-mode "i" #'cider-inspect "r" #'cider-inspect-last-result) (:prefix ("m" . "macro") @@ -82,11 +101,22 @@ "R" #'cider-restart "b" #'cider-switch-to-repl-buffer "B" #'+clojure/cider-switch-to-repl-buffer-and-switch-ns - "c" #'cider-find-and-clear-repl-output))) + "c" #'cider-find-and-clear-repl-output + "l" #'cider-load-buffer + "L" #'cider-load-buffer-and-switch-to-repl-buffer) + (:prefix ("t" . "test") + "a" #'cider-test-rerun-test + "l" #'cider-test-run-loaded-tests + "n" #'cider-test-run-ns-tests + "p" #'cider-test-run-project-tests + "r" #'cider-test-rerun-failed-tests + "s" #'cider-test-run-ns-tests-with-filters + "t" #'cider-test-run-test))) (:when (featurep! :editor evil +everywhere) :map cider-repl-mode-map :i [S-return] #'cider-repl-newline-and-indent + :i [M-return] #'cider-repl-return (:localleader ("n" #'cider-repl-set-ns "q" #'cider-quit @@ -102,7 +132,7 @@ :i "U" #'cider-repl-history-undo-other-window))) -(def-package! clj-refactor +(use-package! clj-refactor :hook (clojure-mode . clj-refactor-mode) :init (set-lookup-handlers! 'clj-refactor-mode @@ -113,6 +143,6 @@ :desc "refactor" "R" #'hydra-cljr-help-menu/body)) -(def-package! flycheck-joker +(use-package! flycheck-joker :when (featurep! :tools flycheck) :after flycheck) diff --git a/modules/lang/common-lisp/autoload/common-lisp.el b/modules/lang/common-lisp/autoload/common-lisp.el index 7fd720f11..cd13df8bb 100644 --- a/modules/lang/common-lisp/autoload/common-lisp.el +++ b/modules/lang/common-lisp/autoload/common-lisp.el @@ -1,7 +1,7 @@ ;;; lang/common-lisp/autoload/common-lisp.el -*- lexical-binding: t; -*- ;;;###autoload -(defun +common-lisp*sly-last-sexp (command &rest args) +(defun +common-lisp--sly-last-sexp-a (command &rest args) "In normal-state or motion-state, last sexp ends at point." (if (and (not evil-move-beyond-eol) (or (evil-normal-state-p) (evil-motion-state-p))) diff --git a/modules/lang/common-lisp/config.el b/modules/lang/common-lisp/config.el index 5e658d362..a41e135f1 100644 --- a/modules/lang/common-lisp/config.el +++ b/modules/lang/common-lisp/config.el @@ -39,7 +39,7 @@ (sp-local-pair "'" "'" :actions nil) (sp-local-pair "`" "`" :actions nil)) - (defun +common-lisp|cleanup-sly-maybe () + (defun +common-lisp--cleanup-sly-maybe-h () "Kill processes and leftover buffers when killing the last sly buffer." (unless (cl-loop for buf in (delq (current-buffer) (buffer-list)) if (and (buffer-local-value 'sly-mode buf) @@ -53,27 +53,17 @@ if (buffer-local-value 'sly-mode buf) collect buf))))) - (defun +common-lisp|init-sly () - "Attempt to auto-start sly when opening a lisp buffer." - (cond ((or (doom-temp-buffer-p (current-buffer)) - (sly-connected-p))) - ((executable-find inferior-lisp-program) - (let ((sly-auto-start 'always)) - (sly-auto-start) - (add-hook 'kill-buffer-hook #'+common-lisp|cleanup-sly-maybe nil t))) - ((message "WARNING: Couldn't find `inferior-lisp-program' (%s)" - inferior-lisp-program)))) - (add-hook 'sly-mode-hook #'+common-lisp|init-sly) - - (defun +common-lisp*refresh-sly-version (version conn) - "Update `sly-protocol-version', which will likely be incorrect or nil due to -an issue where `load-file-name' is incorrect. Because Doom's packages are -installed through an external script (bin/doom), `load-file-name' is set to -bin/doom while packages at compile-time (not a runtime though)." - (unless sly-protocol-version - (setq sly-protocol-version (sly-version nil (locate-library "sly.el")))) - (advice-remove #'sly-check-version #'+common-lisp*refresh-sly-version)) - (advice-add #'sly-check-version :before #'+common-lisp*refresh-sly-version) + (add-hook! 'sly-mode-hook + (defun +common-lisp-init-sly-h () + "Attempt to auto-start sly when opening a lisp buffer." + (cond ((or (doom-temp-buffer-p (current-buffer)) + (sly-connected-p))) + ((executable-find inferior-lisp-program) + (let ((sly-auto-start 'always)) + (sly-auto-start) + (add-hook 'kill-buffer-hook #'+common-lisp--cleanup-sly-maybe-h nil t))) + ((message "WARNING: Couldn't find `inferior-lisp-program' (%s)" + inferior-lisp-program))))) (map! :localleader :map lisp-mode-map @@ -138,14 +128,20 @@ bin/doom while packages at compile-time (not a runtime though)." (when (featurep! :editor evil +everywhere) (add-hook 'sly-mode-hook #'evil-normalize-keymaps) (add-hook 'sly-popup-buffer-mode-hook #'evil-normalize-keymaps) + (unless evil-move-beyond-eol - (advice-add #'sly-eval-last-expression :around #'+common-lisp*sly-last-sexp) - (advice-add #'sly-pprint-eval-last-expression :around #'+common-lisp*sly-last-sexp) - (advice-add #'sly-eval-print-last-expression :around #'+common-lisp*sly-last-sexp) - (advice-add #'sly-eval-last-expression-in-repl :around #'+common-lisp*sly-last-sexp)) + (dolist (fn '(sly-eval-last-expression + sly-pprint-eval-last-expression + sly-eval-print-last-expression + sly-eval-last-expression-in-repl)) + (advice-add fn :around #'+common-lisp--sly-last-sexp-a))) (set-evil-initial-state! - '(sly-db-mode sly-inspector-mode sly-popup-buffer-mode sly-xref-mode) + '(sly-db-mode + sly-inspector-mode + sly-popup-buffer-mode + sly-xref-mode) 'normal) + (evil-define-key 'insert sly-mrepl-mode-map [S-return] #'newline-and-indent [backspace] #'sp-backward-delete-char @@ -239,7 +235,8 @@ bin/doom while packages at compile-time (not a runtime though)." "q" 'quit-window "r" 'sly-xref-retract))) -(def-package! sly-repl-ansi-color + +(use-package! sly-repl-ansi-color :defer t :init (add-to-list 'sly-contribs 'sly-repl-ansi-color nil #'eq)) diff --git a/modules/lang/crystal/config.el b/modules/lang/crystal/config.el index dc38bc408..ece217290 100644 --- a/modules/lang/crystal/config.el +++ b/modules/lang/crystal/config.el @@ -12,10 +12,10 @@ (add-to-list 'dtrt-indent-hook-mapping-list '(crystal-mode ruby crystal-indent-level)))) -(def-package! flycheck-crystal +(use-package! flycheck-crystal :when (featurep! :tools flycheck) :after crystal-mode) -(def-package! inf-crystal +(use-package! inf-crystal :commands crystal-switch-to-inf) diff --git a/modules/lang/csharp/config.el b/modules/lang/csharp/config.el index cb8e40aae..960b4b943 100644 --- a/modules/lang/csharp/config.el +++ b/modules/lang/csharp/config.el @@ -12,19 +12,19 @@ :post-handlers '(("| " "SPC")))) -(def-package! omnisharp +(use-package! omnisharp :hook (csharp-mode . omnisharp-mode) :commands omnisharp-install-server :preface (setq omnisharp-auto-complete-want-documentation nil omnisharp-cache-directory (concat doom-cache-dir "omnisharp")) :config - (defun +csharp|cleanup-omnisharp-server () + (defun +csharp-cleanup-omnisharp-server-h () "Clean up the omnisharp server once you kill the last csharp-mode buffer." (unless (doom-buffers-in-mode 'csharp-mode (buffer-list)) (omnisharp-stop-server))) - (add-hook! csharp-mode - (add-hook 'kill-buffer-hook #'+csharp|cleanup-omnisharp-server nil t)) + (add-hook! 'csharp-mode-hook + (add-hook 'kill-buffer-hook #'+csharp-cleanup-omnisharp-server-h nil t)) (set-company-backend! 'csharp-mode 'company-omnisharp) (set-lookup-handlers! 'csharp-mode @@ -36,7 +36,6 @@ :map omnisharp-mode-map "b" #'omnisharp-recompile (:prefix "r" - "i" #'omnisharp-fix-code-issue-at-point "u" #'omnisharp-fix-usings "r" #'omnisharp-rename "a" #'omnisharp-show-last-auto-complete-result @@ -52,9 +51,9 @@ "ti" #'omnisharp-current-type-information "td" #'omnisharp-current-type-documentation) (:prefix "t" - "r" (λ! (omnisharp-unit-test "fixture")) - "s" (λ! (omnisharp-unit-test "single")) - "a" (λ! (omnisharp-unit-test "all"))))) + "s" #'omnisharp-unit-test-at-point + "l" #'omnisharp-unit-test-last + "b" #'omnisharp-unit-test-buffer))) (when (featurep! +unity) @@ -62,5 +61,5 @@ (add-to-list 'auto-mode-alist '("\\.shader$" . shader-mode)) (def-project-mode! +csharp-unity-mode - :modes (csharp-mode shader-mode) + :modes '(csharp-mode shader-mode) :files (and "Assets" "Library/MonoManager.asset" "Library/ScriptMapper"))) diff --git a/modules/lang/data/config.el b/modules/lang/data/config.el index c18fb15a9..2692fac8c 100644 --- a/modules/lang/data/config.el +++ b/modules/lang/data/config.el @@ -3,14 +3,20 @@ ;; Built in plugins (add-to-list 'auto-mode-alist '("/sxhkdrc\\'" . conf-mode)) (add-to-list 'auto-mode-alist '("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode)) -(add-to-list 'auto-mode-alist '("\\.plist\\'" . nxml-mode)) -(after! nxml-mode +(use-package! nxml-mode + :mode "\\.p\\(?:list\\|om\\)\\'" ; plist, pom + :mode "\\.xs\\(?:d\\|lt\\)\\'" ; xslt, xsd + :mode "\\.rss\\'" + :magic "<\\?xml" + :config + (setq nxml-slash-auto-complete-flag t + nxml-auto-insert-xml-declaration-flag t) (set-company-backend! 'nxml-mode '(company-nxml company-yasnippet))) ;; -;; Third-party plugins +;;; Third-party plugins ;; `csv-mode' (map! :after csv-mode @@ -23,18 +29,21 @@ "k" #'csv-kill-fields "t" #'csv-transpose) -(def-package! graphql-mode +(use-package! graphql-mode :mode "\\.gql\\'") -(def-package! json-mode +(use-package! json-mode :mode "\\.js\\(?:on\\|[hl]int\\(?:rc\\)?\\)\\'" :config (set-electric! 'json-mode :chars '(?\n ?: ?{ ?}))) +(use-package! jsonnet-mode + :defer t + :config + (set-electric! 'jsonnet-mode :chars '(?\n ?: ?{ ?}))) ;; ;; Frameworks (def-project-mode! +data-vagrant-mode :files ("Vagrantfile")) - diff --git a/modules/lang/data/packages.el b/modules/lang/data/packages.el index dfe07cfe7..c70f4f8b4 100644 --- a/modules/lang/data/packages.el +++ b/modules/lang/data/packages.el @@ -3,8 +3,9 @@ (package! graphql-mode) (package! json-mode) +(package! jsonnet-mode) (package! toml-mode) (package! yaml-mode) (package! csv-mode) (package! dhall-mode) -(package! protobuf-mode) +(package! protobuf-mode :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*"))) diff --git a/modules/lang/elixir/README.org b/modules/lang/elixir/README.org new file mode 100644 index 000000000..9a6b0aaa0 --- /dev/null +++ b/modules/lang/elixir/README.org @@ -0,0 +1,56 @@ +#+TITLE: lang/elixir +#+DATE: June 24, 2019 +#+SINCE: v2.0.9 + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#install-elixir][Install Elixir]] + - [[#with-asdf][With ~asdf~]] + - [[#arch-linux][Arch Linux]] + - [[#gentoo-linux][Gentoo Linux]] +- [[#features][Features]] + +* Description +This module provides support for [[https://elixir-lang.org/][Elixir programming language]] via [[https://github.com/tonini/alchemist.el][alchemist.el]] +or [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]]. + +** Module flags +| ~+lsp~ | add support for LSP | + +** Plugins ++ [[https://github.com/rust-lang/rust-mode][elixir-mode]] ++ [[https://github.com/tonini/alchemist.el][alchemist.el]] ++ [[https://github.com/aaronjensen/flycheck-credo][flycheck-credo]] + +* Prerequisites +You should have Elixir installed, for example, via your distribution's package +manager or a version management tool such as [[https://github.com/asdf-vm/asdf-elixir][asdf]]. + +If you want to add support for LSP ([[modules/tools/lsp][:tools lsp]]), be sure to install [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]] +and enable ~:tools lsp~ in your ~init.el~. + +To support linting with [[https://github.com/rrrene/credo][credo]], add ~:tools flycheck~ to your ~init.el~ +** Install Elixir +*** With ~asdf~ +#+BEGIN_SRC sh +asdf plugin-add elixir +asdf install elixir 1.9.1 +#+END_SRC +*** Arch Linux +#+BEGIN_SRC sh :dir /sudo:: +sudo pacman -S elixir +#+END_SRC +*** Gentoo Linux +#+BEGIN_SRC sh :dir /sudo:: +sudo emerge -v dev-lang/elixir +#+END_SRC +* Features +- Code completion (~:completion company~) +- Documentation lookup (~:tools lookup~) +- Mix integration +- Phoenix support +- ~iex~ integration (~:tools eval~) +- Syntax checking (~:tools flycheck~, using [[https://github.com/aaronjensen/flycheck-credo][flycheck-credo]]~) diff --git a/modules/lang/elixir/config.el b/modules/lang/elixir/config.el index 71c4973e0..b6750c24c 100644 --- a/modules/lang/elixir/config.el +++ b/modules/lang/elixir/config.el @@ -1,6 +1,6 @@ ;;; lang/elixir/config.el -*- lexical-binding: t; -*- -(def-package! elixir-mode +(use-package! elixir-mode :defer t :init ;; Disable default smartparens config. There are too many pairs; we only want @@ -21,16 +21,18 @@ :return "return" :yield "use") ;; ...and only complete the basics - (after! smartparens - (sp-with-modes 'elixir-mode - (sp-local-pair "do" "end" - :when '(("RET" "")) - :unless '(sp-in-comment-p sp-in-string-p) - :post-handlers '("||\n[i]")) - (sp-local-pair "do " " end" :unless '(sp-in-comment-p sp-in-string-p)) - (sp-local-pair "fn " " end" :unless '(sp-in-comment-p sp-in-string-p)))) + (sp-with-modes 'elixir-mode + (sp-local-pair "do" "end" + :when '(("RET" "")) + :unless '(sp-in-comment-p sp-in-string-p) + :post-handlers '("||\n[i]")) + (sp-local-pair "do " " end" :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "fn " " end" :unless '(sp-in-comment-p sp-in-string-p))) - (def-package! alchemist-company + (when (featurep! +lsp) + (add-hook 'elixir-mode-local-vars-hook #'lsp!)) + + (use-package! alchemist-company :when (featurep! :completion company) :commands alchemist-company :init @@ -42,12 +44,12 @@ (remove-hook 'alchemist-mode-hook fn) (remove-hook 'alchemist-iex-mode-hook fn))) - (def-package! flycheck-credo + (use-package! flycheck-credo :when (featurep! :tools flycheck) :config (flycheck-credo-setup))) -(def-package! alchemist +(use-package! alchemist :hook (elixir-mode . alchemist-mode) :config (set-lookup-handlers! 'elixir-mode diff --git a/modules/lang/elm/config.el b/modules/lang/elm/config.el index b10dde8c1..c96baafb4 100644 --- a/modules/lang/elm/config.el +++ b/modules/lang/elm/config.el @@ -19,7 +19,7 @@ :and "&&" :or "||")) -(def-package! flycheck-elm +(use-package! flycheck-elm :when (featurep! :tools flycheck) :after elm-mode :config (add-to-list 'flycheck-checkers 'elm nil #'eq)) diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 545876d36..9432513ae 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -71,6 +71,13 @@ library/userland functions" (throw 'matcher t))))))) nil)) +;; `+emacs-lisp-highlight-vars-and-faces' is a potentially expensive function +;; and should be byte-compiled, no matter what, to ensure it runs as fast as +;; possible: +(unless (byte-code-function-p (symbol-function '+emacs-lisp-highlight-vars-and-faces)) + (with-no-warnings + (byte-compile #'+emacs-lisp-highlight-vars-and-faces))) + ;;;###autoload (defun +emacs-lisp-lookup-documentation (thing) "Lookup THING with `helpful-variable' if it's a variable, `helpful-callable' @@ -79,13 +86,6 @@ if it's callable, `apropos' otherwise." (doom/describe-symbol thing) (call-interactively #'doom/describe-symbol))) -;; `+emacs-lisp-highlight-vars-and-faces' is a potentially expensive function -;; and should be byte-compiled, no matter what, to ensure it runs as fast as -;; possible: -(when (not (byte-code-function-p (symbol-function '+emacs-lisp-highlight-vars-and-faces))) - (with-no-warnings - (byte-compile #'+emacs-lisp-highlight-vars-and-faces))) - ;; ;;; Commands @@ -101,12 +101,35 @@ if it's callable, `apropos' otherwise." (bury-buffer buf) buf))))) +;;;###autoload +(defun +emacs-lisp/buttercup-run-file () + "Run all buttercup tests in the focused buffer." + (interactive) + (let ((load-path (append (list (doom-path (dir!) "..") + (or (doom-project-root) + default-directory)) + load-path))) + (save-selected-window + (eval-buffer) + (buttercup-run)) + (message "File executed successfully"))) + +;;;###autoload +(defun +emacs-lisp/buttercup-run-project () + "Run all buttercup tests in the project." + (interactive) + (let* ((default-directory (doom-project-root)) + (load-path (append (list (doom-path "test") + default-directory) + load-path))) + (buttercup-run-discover))) + ;; ;;; Hooks ;;;###autoload -(defun +emacs-lisp|extend-imenu () +(defun +emacs-lisp-extend-imenu-h () "Improve imenu support in `emacs-lisp-mode', including recognition for Doom's API." (setq imenu-generic-expression `(("Section" "^[ \t]*;;;;*[ \t]+\\([^\n]+\\)" 1) @@ -125,7 +148,7 @@ if it's callable, `apropos' otherwise." ("Types" "^\\s-*(\\(cl-def\\(?:struct\\|type\\)\\|def\\(?:class\\|face\\|group\\|ine-\\(?:condition\\|error\\|widget\\)\\|package\\|struct\\|t\\(?:\\(?:hem\\|yp\\)e\\)\\)\\)\\s-+'?\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" 2)))) ;;;###autoload -(defun +emacs-lisp|reduce-flycheck-errors-in-emacs-config () +(defun +emacs-lisp-reduce-flycheck-errors-in-emacs-config-h () "Remove `emacs-lisp-checkdoc' checker and reduce `emacs-lisp' checker verbosity when editing a file in `doom-private-dir' or `doom-emacs-dir'." (when (and (bound-and-true-p flycheck-mode) diff --git a/modules/lang/emacs-lisp/config.el b/modules/lang/emacs-lisp/config.el index beb2fbc30..d673197b9 100644 --- a/modules/lang/emacs-lisp/config.el +++ b/modules/lang/emacs-lisp/config.el @@ -16,7 +16,7 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") ;; ;;; Config -(def-package! elisp-mode +(use-package! elisp-mode :mode ("\\.Cask\\'" . emacs-lisp-mode) :config (set-repl-handler! 'emacs-lisp-mode #'+emacs-lisp/open-repl) @@ -32,10 +32,12 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") ("when" "unless") ("advice-add" "advice-remove") ("add-hook" "remove-hook") - ("add-hook!" "remove-hook!"))) + ("add-hook!" "remove-hook!") + ("it" "xit") + ("describe" "xdescribe"))) (setq-hook! 'emacs-lisp-mode-hook - tab-width 2 + tab-width (or lisp-indent-offset 2) ;; shorter name in modeline mode-name "Elisp" ;; Don't treat autoloads or sexp openers as outline headers, we have @@ -45,20 +47,23 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") ;; variable-width indentation is superior in elisp (add-to-list 'doom-detect-indentation-excluded-modes 'emacs-lisp-mode nil #'eq) + ;; Use helpful instead of describe-* from `company' + (advice-add #'elisp--company-doc-buffer :around #'doom-use-helpful-a) + (add-hook! 'emacs-lisp-mode-hook - #'(outline-minor-mode - ;; fontificiation - rainbow-delimiters-mode - highlight-quoted-mode - ;; initialization - +emacs-lisp|extend-imenu)) + #'outline-minor-mode + ;; fontificiation + #'rainbow-delimiters-mode + #'highlight-quoted-mode + ;; initialization + #'+emacs-lisp-extend-imenu-h) ;; Flycheck's two emacs-lisp checkers produce a *lot* of false positives in ;; emacs configs, so we disable `emacs-lisp-checkdoc' and reduce the ;; `emacs-lisp' checker's verbosity. - (add-hook 'flycheck-mode-hook #'+emacs-lisp|reduce-flycheck-errors-in-emacs-config) + (add-hook 'flycheck-mode-hook #'+emacs-lisp-reduce-flycheck-errors-in-emacs-config-h) - ;; Special fontification for elisp + ;; Special syntax highlighting for elisp... (font-lock-add-keywords 'emacs-lisp-mode (append `(;; custom Doom cookies @@ -68,7 +73,7 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") `((+emacs-lisp-highlight-vars-and-faces . +emacs-lisp--face))))) ;; Recenter window after following definition - (advice-add #'elisp-def :after #'doom*recenter) + (advice-add #'elisp-def :after #'doom-recenter-a) (map! :localleader :map emacs-lisp-mode-map @@ -78,28 +83,10 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") ;; ;;; Packages -(when (featurep! :editor evil) - (after! macrostep - (evil-define-key* 'normal macrostep-keymap - [return] #'macrostep-expand - "e" #'macrostep-expand - "u" #'macrostep-collapse - "c" #'macrostep-collapse - - [tab] #'macrostep-next-macro - "\C-n" #'macrostep-next-macro - "J" #'macrostep-next-macro - - [backtab] #'macrostep-prev-macro - "K" #'macrostep-prev-macro - "\C-p" #'macrostep-prev-macro - - "q" #'macrostep-collapse-all - "C" #'macrostep-collapse-all) - - ;; `evil-normalize-keymaps' seems to be required for macrostep or it won't - ;; apply for the very first invocation - (add-hook 'macrostep-mode-hook #'evil-normalize-keymaps))) +(map! :when (featurep! :editor evil) + :after macrostep + :map macrostep-keymap + :n [return] #'macrostep-expand) ;;;###package overseer @@ -107,7 +94,7 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") (remove-hook 'emacs-lisp-mode-hook 'overseer-enable-mode) -(def-package! flycheck-cask +(use-package! flycheck-cask :when (featurep! :tools flycheck) :defer t :init @@ -115,25 +102,45 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") (add-hook 'flycheck-mode-hook #'flycheck-cask-setup nil t))) -(def-package! elisp-demos +(use-package! elisp-demos :defer t :init (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1) - (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update)) + (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update) + :config + (defadvice! +emacs-lisp--add-doom-elisp-demos-a (orig-fn symbol) + "Add Doom's own demos to help buffers." + :around #'elisp-demos--search + (or (funcall orig-fn symbol) + (when-let* ((elisp-demos--elisp-demos.org (doom-glob doom-docs-dir "api.org"))) + (funcall orig-fn symbol))))) + + +(use-package! buttercup + :defer t + :minor ("/test[/-].+\\.el$" . buttercup-minor-mode) + :preface + ;; buttercup.el doesn't define a keymap for `buttercup-minor-mode', as we have + ;; to fool its internal `define-minor-mode' call into thinking one exists, so + ;; it will associate it with the mode. + (defvar buttercup-minor-mode-map (make-sparse-keymap)) + :config + (set-popup-rule! "^\\*Buttercup\\*$" :size 0.45 :select nil :ttl 0) + (set-yas-minor-mode! 'buttercup-minor-mode) + (when (featurep 'evil) + (add-hook 'buttercup-minor-mode-hook #'evil-normalize-keymaps)) + (map! :map buttercup-minor-mode-map + :localleader + :prefix "t" + "t" #'+emacs-lisp/buttercup-run-file + "a" #'+emacs-lisp/buttercup-run-project + "s" #'buttercup-run-at-point)) ;; ;;; Project modes (def-project-mode! +emacs-lisp-ert-mode - :modes (emacs-lisp-mode) + :modes '(emacs-lisp-mode) :match "/test[/-].+\\.el$" - :add-hooks (overseer-enable-mode)) - -(associate! buttercup-minor-mode - :modes (emacs-lisp-mode) - :match "/test[/-].+\\.el$") - -(after! buttercup - (set-yas-minor-mode! 'buttercup-minor-mode)) - + :add-hooks '(overseer-enable-mode)) diff --git a/modules/lang/emacs-lisp/packages.el b/modules/lang/emacs-lisp/packages.el index 52ca5e6c5..5f7405ac1 100644 --- a/modules/lang/emacs-lisp/packages.el +++ b/modules/lang/emacs-lisp/packages.el @@ -11,3 +11,5 @@ (when (featurep! :tools flycheck) (package! flycheck-cask)) + +(package! buttercup) diff --git a/modules/lang/erlang/config.el b/modules/lang/erlang/config.el index ed91e5229..7e748fbba 100644 --- a/modules/lang/erlang/config.el +++ b/modules/lang/erlang/config.el @@ -1,18 +1,18 @@ ;;; lang/erlang/config.el -*- lexical-binding: t; -*- -(def-package! erlang +(use-package! erlang :mode ("\\.erlang$" . erlang-mode) :mode ("/rebar\\.config\\(?:\\.script\\)?$" . erlang-mode) :mode ("/\\(?:app\\|sys\\)\\.config$" . erlang-mode)) -(def-package! flycheck-rebar3 +(use-package! flycheck-rebar3 :when (featurep! :tools flycheck) :after flycheck :config (flycheck-rebar3-setup)) -(def-package! ivy-erlang-complete +(use-package! ivy-erlang-complete :when (featurep! :completion ivy) :hook (erlang-mode . ivy-erlang-complete-init) :config @@ -20,6 +20,6 @@ (add-hook 'after-save-hook #'ivy-erlang-complete-reparse nil t))) -(def-package! company-erlang +(use-package! company-erlang :when (featurep! :completion company) :hook (erlang-mode . company-erlang-init)) diff --git a/modules/lang/ess/config.el b/modules/lang/ess/config.el index 741a778b4..afbde141b 100644 --- a/modules/lang/ess/config.el +++ b/modules/lang/ess/config.el @@ -1,6 +1,6 @@ ;;; lang/ess/config.el -*- lexical-binding: t; -*- -(def-package! ess +(use-package! ess :commands (stata SAS) :init (setq ess-smart-S-assign-key nil) diff --git a/modules/lang/go/config.el b/modules/lang/go/config.el index b29047ffa..cb999a169 100644 --- a/modules/lang/go/config.el +++ b/modules/lang/go/config.el @@ -53,11 +53,11 @@ "n" #'+go/test-nested))) -(def-package! gorepl-mode +(use-package! gorepl-mode :commands gorepl-run-load-current-file) -(def-package! company-go +(use-package! company-go :when (and (featurep! :completion company) (not (featurep! +lsp))) :after go-mode diff --git a/modules/lang/haskell/+dante.el b/modules/lang/haskell/+dante.el index 2774b8a62..801303b9b 100644 --- a/modules/lang/haskell/+dante.el +++ b/modules/lang/haskell/+dante.el @@ -1,7 +1,7 @@ ;;; lang/haskell/+dante.el -*- lexical-binding: t; -*- ;;;###if (featurep! +dante) -(def-package! dante +(use-package! dante :hook (haskell-mode-local-vars . dante-mode) :init (setq dante-load-flags '(;; defaults: @@ -19,15 +19,16 @@ (set-company-backend! 'dante-mode #'dante-company) - (defun +haskell*restore-modified-state (orig-fn &rest args) - "Dante quietly saves the current buffer (without triggering save hooks) before + (defadvice! +haskell--restore-modified-state-a (orig-fn &rest args) + "Marks the buffer as falsely modified. +Dante quietly saves the current buffer (without triggering save hooks) before invoking flycheck, unexpectedly leaving the buffer in an unmodified state. This is annoying if we depend on save hooks to do work on the buffer (like -reformatting), so we restore a (false) modified state." +reformatting)." + :around #'dante-async-load-current-buffer (let ((modified-p (buffer-modified-p))) (apply orig-fn args) (if modified-p (set-buffer-modified-p t)))) - (advice-add #'dante-async-load-current-buffer :around #'+haskell*restore-modified-state) (when (featurep 'evil) (add-hook 'dante-mode-hook #'evil-normalize-keymaps)) diff --git a/modules/lang/haskell/+intero.el b/modules/lang/haskell/+intero.el index 1ba04599d..309a92aac 100644 --- a/modules/lang/haskell/+intero.el +++ b/modules/lang/haskell/+intero.el @@ -1,17 +1,17 @@ ;;; lang/haskell/+intero.el -*- lexical-binding: t; -*- ;;;###if (featurep! +intero) -(def-package! intero +(use-package! intero :commands intero-mode :init - (defun +haskell|init-intero () - "Initializes `intero-mode' in haskell-mode, unless stack isn't installed. + (add-hook! 'haskell-mode-local-vars-hook + (defun +haskell-init-intero-h () + "Initializes `intero-mode' in haskell-mode, unless stack isn't installed. This is necessary because `intero-mode' doesn't do its own error checks." - (when (derived-mode-p 'haskell-mode) - (if (executable-find "stack") - (intero-mode +1) - (message "Couldn't find stack. Refusing to enable intero-mode.")))) - (add-hook 'haskell-mode-local-vars-hook #'+haskell|init-intero) + (when (derived-mode-p 'haskell-mode) + (if (executable-find "stack") + (intero-mode +1) + (message "Couldn't find stack. Refusing to enable intero-mode."))))) :config (setq haskell-compile-cabal-build-command "stack build --fast") (set-lookup-handlers! 'intero-mode :definition #'intero-goto-definition) diff --git a/modules/lang/haskell/+lsp.el b/modules/lang/haskell/+lsp.el index 576e26e67..ae6339d55 100644 --- a/modules/lang/haskell/+lsp.el +++ b/modules/lang/haskell/+lsp.el @@ -1,6 +1,6 @@ ;;; lang/haskell/+lsp.el -*- lexical-binding: t; -*- -(def-package! lsp-haskell +(use-package! lsp-haskell :after haskell-mode :init (add-hook 'haskell-mode-hook #'lsp!) :config diff --git a/modules/lang/haskell/config.el b/modules/lang/haskell/config.el index 6f8b016bd..c427f5b64 100644 --- a/modules/lang/haskell/config.el +++ b/modules/lang/haskell/config.el @@ -13,13 +13,19 @@ haskell-process-auto-import-loaded-modules t haskell-process-show-overlays (not (featurep! :tools flycheck))) ; redundant with flycheck - (set-lookup-handlers! 'haskell-mode :definition #'haskell-mode-jump-to-def-or-tag) - (set-file-template! 'haskell-mode :trigger #'haskell-auto-insert-module-template :project t) - (set-repl-handler! '(haskell-mode haskell-cabal-mode literate-haskell-mode) #'+haskell/open-repl) + (set-lookup-handlers! 'haskell-mode + :definition #'haskell-mode-jump-to-def-or-tag) + (set-file-template! 'haskell-mode + :trigger #'haskell-auto-insert-module-template + :project t) + (set-repl-handler! '(haskell-mode + haskell-cabal-mode + literate-haskell-mode) + #'+haskell/open-repl) (add-hook! 'haskell-mode-hook - #'(haskell-collapse-mode ; support folding haskell code blocks - interactive-haskell-mode)) + #'haskell-collapse-mode ; support folding haskell code blocks + #'interactive-haskell-mode) (add-to-list 'completion-ignored-extensions ".hi") diff --git a/modules/lang/hy/config.el b/modules/lang/hy/config.el index 92163ac89..aac5bcdc1 100644 --- a/modules/lang/hy/config.el +++ b/modules/lang/hy/config.el @@ -1,6 +1,6 @@ ;;; lang/hy/config.el -*- lexical-binding: t; -*- -(def-package! hy-mode +(use-package! hy-mode :mode "\\.hy\\'" :interpreter "hy" :config diff --git a/modules/lang/idris/config.el b/modules/lang/idris/config.el index cd56fb071..249fd2d08 100644 --- a/modules/lang/idris/config.el +++ b/modules/lang/idris/config.el @@ -1,7 +1,7 @@ ;;; lang/idris/config.el -*- lexical-binding: t; -*- (after! idris-mode - (add-hook! 'idris-mode-hook 'turn-on-idris-simple-indent) + (add-hook 'idris-mode-hook #'turn-on-idris-simple-indent) (set-repl-handler! 'idris-mode 'idris-pop-to-repl) (set-lookup-handlers! 'idris-mode :documentation #'idris-docs-at-point diff --git a/modules/lang/java/+eclim.el b/modules/lang/java/+eclim.el index df30de7bb..9b5d18bbd 100644 --- a/modules/lang/java/+eclim.el +++ b/modules/lang/java/+eclim.el @@ -3,7 +3,7 @@ ;; NOTE This submodule is incomplete -(def-package! eclim +(use-package! eclim :hook (java-mode . eclim-mode) :config (set-lookup-handlers! 'java-mode @@ -43,7 +43,7 @@ "u" #'eclim-project-update))) -(def-package! company-emacs-eclim +(use-package! company-emacs-eclim :when (featurep! :completion company) :after java-mode :config diff --git a/modules/lang/java/+lsp.el b/modules/lang/java/+lsp.el index c6bb00f94..dc917a6c7 100644 --- a/modules/lang/java/+lsp.el +++ b/modules/lang/java/+lsp.el @@ -1,7 +1,7 @@ ;;; lang/java/+lsp.el -*- lexical-binding: t; -*- ;;;###if (featurep! +lsp) -(def-package! lsp-java +(use-package! lsp-java :after-call java-mode :init (add-hook 'java-mode-local-vars-hook #'lsp!) :config diff --git a/modules/lang/java/+meghanada.el b/modules/lang/java/+meghanada.el index e422deb9b..120b49a8b 100644 --- a/modules/lang/java/+meghanada.el +++ b/modules/lang/java/+meghanada.el @@ -1,7 +1,7 @@ ;;; lang/java/+meghanada.el -*- lexical-binding: t; -*- ;;;###if (featurep! +meghanada) -(def-package! meghanada +(use-package! meghanada :hook (java-mode . meghanada-mode) :init (setq meghanada-server-install-dir (concat doom-etc-dir "meghanada-server/") diff --git a/modules/lang/java/autoload.el b/modules/lang/java/autoload.el index f4a1eeacf..86e06f26c 100644 --- a/modules/lang/java/autoload.el +++ b/modules/lang/java/autoload.el @@ -23,7 +23,7 @@ (buffer-substring-no-properties beg end)))) ;;;###autoload -(defun +java|android-mode-maybe () +(defun +java-android-mode-maybe-h () "Enable `android-mode' if this looks like an android project. It determines this by the existence of AndroidManifest.xml or diff --git a/modules/lang/java/config.el b/modules/lang/java/config.el index f0fc35743..ed9ee04d0 100644 --- a/modules/lang/java/config.el +++ b/modules/lang/java/config.el @@ -30,15 +30,16 @@ If the depth is 2, the first two directories are removed: net.lissner.game.") ;; ;; Common packages -(def-package! android-mode +(use-package! android-mode :commands android-mode :init - (add-hook! (java-mode groovy-mode nxml-mode) #'+java|android-mode-maybe) + (add-hook! '(java-mode-hook groovy-mode-hook nxml-mode-hook) + #'+java-android-mode-maybe-h) :config (set-yas-minor-mode! 'android-mode)) -(def-package! groovy-mode +(use-package! groovy-mode :mode "\\.g\\(?:radle\\|roovy\\)$" :config (set-eval-handler! 'groovy-mode "groovy")) diff --git a/modules/lang/javascript/autoload.el b/modules/lang/javascript/autoload.el index 2226b536c..a570be4ff 100644 --- a/modules/lang/javascript/autoload.el +++ b/modules/lang/javascript/autoload.el @@ -84,7 +84,7 @@ Run this for any buffer you want to skewer." ;; Hooks ;;;###autoload -(defun +javascript|add-node-modules-path () +(defun +javascript-add-node-modules-path-h () "Add current project's `node_modules/.bin` to `exec-path', so js tools prioritize project-local packages over global ones." (make-local-variable 'exec-path) @@ -96,7 +96,7 @@ prioritize project-local packages over global ones." exec-path :test #'string=)) ;;;###autoload -(defun +javascript|cleanup-tide-processes () +(defun +javascript-cleanup-tide-processes-h () "Clean up dangling tsserver processes if there are no more buffers with `tide-mode' active that belong to that server's project." (when tide-mode @@ -113,7 +113,7 @@ prioritize project-local packages over global ones." ;; Advice ;;;###autoload -(defun +javascript*tide-project-root () +(defun +javascript-tide-project-root-a () "Resolve to `doom-project-root' if `tide-project-root' fails." (or tide-project-root (or (locate-dominating-file default-directory "tsconfig.json") diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el index 29ecfd456..a569871b5 100644 --- a/modules/lang/javascript/config.el +++ b/modules/lang/javascript/config.el @@ -31,13 +31,14 @@ ;; ;; Major modes -(def-package! js2-mode +(use-package! js2-mode :mode "\\.m?js\\'" :interpreter "node" :commands js2-line-break :config - (setq js2-skip-preprocessor-directives t - js-chain-indent t + (setq js-chain-indent t + ;; Don't mishighlight shebang lines + js2-skip-preprocessor-directives t ;; let flycheck handle this js2-mode-show-parse-errors nil js2-mode-show-strict-warnings nil @@ -47,7 +48,8 @@ js2-strict-missing-semi-warning nil ;; maximum fontification js2-highlight-level 3 - js2-highlight-external-variables t) + js2-highlight-external-variables t + js2-idle-timer-delay 0.1) (add-hook 'js2-mode-hook #'rainbow-delimiters-mode) ;; Indent switch-case another step @@ -63,7 +65,7 @@ "S" #'+javascript/skewer-this-buffer)) -(def-package! rjsx-mode +(use-package! rjsx-mode :mode "components/.+\\.js$" :init (defun +javascript-jsx-file-p () @@ -86,10 +88,10 @@ ;; a self-closing tag, so that it can insert a matching ending tag at point. ;; However, the parser doesn't run immediately, so a fast typist can outrun ;; it, causing tags to stay unclosed, so we force it to parse. - (defun +javascript|reparse (n) + (defadvice! +javascript-reparse-a (n) ;; if n != 1, rjsx-electric-gt calls rjsx-maybe-reparse itself - (if (= n 1) (rjsx-maybe-reparse))) - (advice-add #'rjsx-electric-gt :before #'+javascript|reparse)) + :before #'rjsx-electric-gt + (if (= n 1) (rjsx-maybe-reparse)))) (after! typescript-mode @@ -126,36 +128,35 @@ ;; ;;; Tools -(defun +javascript|init-lsp-or-tide-maybe () - "Start `lsp' or `tide' in the current buffer. +(add-hook! '(js-mode-hook typescript-mode-hook web-mode-hook) + (defun +javascript-init-lsp-or-tide-maybe-h () + "Start `lsp' or `tide' in the current buffer. LSP will be used if the +lsp flag is enabled for :lang javascript AND if the current buffer represents a file in a project. If LSP fails to start (e.g. no available server or project), then we fall back to tide." - (let ((buffer-file-name (buffer-file-name (buffer-base-buffer)))) - (when (or (derived-mode-p 'js-mode 'typescript-mode) - (and (eq major-mode 'web-mode) - (string= "tsx" (file-name-extension buffer-file-name)))) - (if (not buffer-file-name) - ;; necessary because `tide-setup' and `lsp' will error if not a - ;; file-visiting buffer - (add-hook 'after-save-hook #'+javascript|init-tide-or-lsp-maybe nil 'local) - (or (and (featurep! +lsp) - (progn (lsp!) lsp-mode)) - ;; fall back to tide - (if (executable-find "node") - (and (require 'tide nil t) - (progn (tide-setup) tide-mode)) - (ignore - (doom-log "Couldn't start tide because 'node' is missing")))) - (remove-hook 'after-save-hook #'+javascript|init-tide-or-lsp-maybe 'local))))) - -(add-hook! (js-mode typescript-mode web-mode) #'+javascript|init-lsp-or-tide-maybe) + (let ((buffer-file-name (buffer-file-name (buffer-base-buffer)))) + (when (or (derived-mode-p 'js-mode 'typescript-mode) + (and (eq major-mode 'web-mode) + (string= "tsx" (file-name-extension buffer-file-name)))) + (if (not buffer-file-name) + ;; necessary because `tide-setup' and `lsp' will error if not a + ;; file-visiting buffer + (add-hook 'after-save-hook #'+javascript-init-tide-or-lsp-maybe-h nil 'local) + (or (and (featurep! +lsp) + (progn (lsp!) lsp-mode)) + ;; fall back to tide + (if (executable-find "node") + (and (require 'tide nil t) + (progn (tide-setup) tide-mode)) + (ignore + (doom-log "Couldn't start tide because 'node' is missing")))) + (remove-hook 'after-save-hook #'+javascript-init-tide-or-lsp-maybe-h 'local)))))) -(def-package! tide +(use-package! tide :defer t :config (setq tide-completion-detailed t @@ -171,10 +172,16 @@ to tide." :definition '(tide-jump-to-definition :async t) :references '(tide-references :async t)) ;; resolve to `doom-project-root' if `tide-project-root' fails - (advice-add #'tide-project-root :override #'+javascript*tide-project-root) + (advice-add #'tide-project-root :override #'+javascript-tide-project-root-a) ;; cleanup tsserver when no tide buffers are left (add-hook! 'tide-mode-hook - (add-hook 'kill-buffer-hook #'+javascript|cleanup-tide-processes nil t)) + (add-hook 'kill-buffer-hook #'+javascript-cleanup-tide-processes-h nil t)) + + ;; Eldoc is activated too soon and disables itself, thinking there is no eldoc + ;; support in the current buffer, so we must re-enable it later once eldoc + ;; support exists. It is set *after* tide-mode is enabled, so enabling it on + ;; `tide-mode-hook' is too early, so... + (advice-add #'tide-setup :after #'eldoc-mode) (define-key tide-mode-map [remap +lookup/documentation] #'tide-documentation-at-point) @@ -186,7 +193,7 @@ to tide." "roi" #'tide-organize-imports)) -(def-package! xref-js2 +(use-package! xref-js2 :when (featurep! :tools lookup) :after (:or js2-mode rjsx-mode) :config @@ -194,7 +201,7 @@ to tide." :xref-backend #'xref-js2-xref-backend)) -(def-package! js2-refactor +(use-package! js2-refactor :hook ((js2-mode rjsx-mode) . js2-refactor-mode) :config (when (featurep! :editor evil +everywhere) @@ -202,12 +209,11 @@ to tide." (js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key))))) -(def-package! eslintd-fix +(use-package! eslintd-fix :commands eslintd-fix :config - (defun +javascript|set-flycheck-executable-to-eslint () - (setq flycheck-javascript-eslint-executable eslintd-fix-executable)) - (add-hook 'eslintd-fix-mode-hook #'+javascript|set-flycheck-executable-to-eslint)) + (setq-hook! 'eslintd-fix-mode-hook + flycheck-javascript-eslint-executable eslintd-fix-executable)) ;;;###package skewer-mode @@ -232,27 +238,21 @@ to tide." ;;;###package npm-mode -(map! :after npm-mode - :localleader - :map npm-mode-keymap - :prefix "n" - "n" #'npm-mode-npm-init - "i" #'npm-mode-npm-install - "s" #'npm-mode-npm-install-save - "d" #'npm-mode-npm-install-save-dev - "u" #'npm-mode-npm-uninstall - "l" #'npm-mode-npm-list - "r" #'npm-mode-npm-run - "v" #'npm-mode-visit-project-file) +(use-package npm-mode + :hook ((js-mode typescript-mode) . npm-mode) + :config + (map! :localleader + :map npm-mode-keymap + "n" npm-mode-command-keymap)) ;; ;;; Projects (def-project-mode! +javascript-npm-mode - :modes (html-mode css-mode web-mode typescript-mode js2-mode rjsx-mode json-mode markdown-mode) + :modes '(html-mode css-mode web-mode markdown-mode js-mode typescript-mode) :when (locate-dominating-file default-directory "package.json") - :add-hooks (+javascript|add-node-modules-path npm-mode)) + :add-hooks '(+javascript-add-node-modules-path-h npm-mode)) (def-project-mode! +javascript-gulp-mode :when (locate-dominating-file default-directory "gulpfile.js")) diff --git a/modules/lang/kotlin/config.el b/modules/lang/kotlin/config.el index dc02f1035..d1b2ac1ab 100644 --- a/modules/lang/kotlin/config.el +++ b/modules/lang/kotlin/config.el @@ -10,7 +10,7 @@ :desc "gradlew build" "b" (λ! (+kotlin/run-gradlew "build")) :desc "gradlew test" "t" (λ! (+kotlin/run-gradlew "test")))) -(def-package! flycheck-kotlin +(use-package! flycheck-kotlin :when (featurep! :tools flycheck) :after kotlin-mode :config (add-hook 'kotlin-mode-hook #'flycheck-kotlin-setup)) diff --git a/modules/lang/latex/+ref.el b/modules/lang/latex/+ref.el index 321337ef7..24262985e 100644 --- a/modules/lang/latex/+ref.el +++ b/modules/lang/latex/+ref.el @@ -5,7 +5,7 @@ reftex-default-bibliography bibtex-completion-bibliography)) -(def-package! reftex +(use-package! reftex :hook (LaTeX-mode . reftex-mode) :config ;; set up completion for citations and references diff --git a/modules/lang/latex/+viewers.el b/modules/lang/latex/+viewers.el index 89251647f..012ceeafd 100644 --- a/modules/lang/latex/+viewers.el +++ b/modules/lang/latex/+viewers.el @@ -1,43 +1,45 @@ ;;; lang/latex/+viewers.el -*- lexical-binding: t; -*- -(catch 'found-viewer - (dolist (viewer +latex-viewers) - (if (pcase viewer - (`skim - (when (and IS-MAC - (file-exists-p! (or "/Applications/Skim.app" - "~/Applications/Skim.app"))) - (add-to-list 'TeX-view-program-selection '(output-pdf "Skim")))) - (`sumatrapdf - (when (and IS-WINDOWS - (executable-find "SumatraPDF")) - (add-to-list 'TeX-view-program-selection '(output-pdf "SumatraPDF")))) +;; fall back pdf previewing to latex-preview-pane +(add-to-list 'TeX-view-program-selection '(output-pdf "preview-pane") 'append) +(add-to-list 'TeX-view-program-list '("preview-pane" latex-preview-pane-mode)) - (`okular - (when (executable-find "okular") - ;; Configure Okular as viewer. Including a bug fix - ;; (https://bugs.kde.org/show_bug.cgi?id=373855) - (add-to-list 'TeX-view-program-list '("Okular" ("okular --unique file:%o" (mode-io-correlate "#src:%n%a")))) - (add-to-list 'TeX-view-program-selection '(output-pdf "Okular")))) +(dolist (viewer +latex-viewers) + (pcase viewer + (`skim + (when (and IS-MAC + (file-exists-p! (or "/Applications/Skim.app" + "~/Applications/Skim.app"))) + (add-to-list 'TeX-view-program-selection '(output-pdf "Skim")))) - (`zathura - (when (executable-find "zathura") - (add-to-list 'TeX-view-program-selection '(output-pdf "Zathura")))) + (`sumatrapdf + (when (and IS-WINDOWS + (executable-find "SumatraPDF")) + (add-to-list 'TeX-view-program-selection '(output-pdf "SumatraPDF")))) - (`pdf-tools - (when (featurep! :tools pdf) - (add-to-list 'TeX-view-program-selection '(output-pdf "PDF Tools")) - (when IS-MAC - ;; PDF Tools isn't in `TeX-view-program-list-builtin' on macs - (add-to-list 'TeX-view-program-list '("PDF Tools" TeX-pdf-tools-sync-view))) - ;; Update PDF buffers after successful LaTeX runs - (add-hook 'TeX-after-compilation-finished-function #'TeX-revert-document-buffer)))) + (`okular + (when (executable-find "okular") + ;; Configure Okular as viewer. Including a bug fix + ;; (https://bugs.kde.org/show_bug.cgi?id=373855) + (add-to-list 'TeX-view-program-list '("Okular" ("okular --unique file:%o" (mode-io-correlate "#src:%n%a")))) + (add-to-list 'TeX-view-program-selection '(output-pdf "Okular")))) - (throw 'found-viewer t))) + (`zathura + (when (executable-find "zathura") + (add-to-list 'TeX-view-program-selection '(output-pdf "Zathura")))) - ;; fall back to latex-preview-pane - (add-to-list 'TeX-view-program-list '("preview-pane" latex-preview-pane-mode)) - (add-to-list 'TeX-view-program-selection '(output-pdf "preview-pane"))) + (`evince + (when (executable-find "evince") + (add-to-list 'TeX-view-program-selection '(output-pdf "Evince")))) + + (`pdf-tools + (when (featurep! :tools pdf) + (add-to-list 'TeX-view-program-selection '(output-pdf "PDF Tools")) + (when IS-MAC + ;; PDF Tools isn't in `TeX-view-program-list-builtin' on macs + (add-to-list 'TeX-view-program-list '("PDF Tools" TeX-pdf-tools-sync-view))) + ;; Update PDF buffers after successful LaTeX runs + (add-hook 'TeX-after-compilation-finished-function #'TeX-revert-document-buffer))))) (after! latex-preview-pane diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index 82efbbecf..8fae86080 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -11,7 +11,7 @@ enabling unicode symbols in math regions. This requires the unicode-math latex package to be installed.") -(defvar +latex-viewers `(skim sumatrapdf zathura okular pdf-tools) +(defvar +latex-viewers '(skim evince sumatrapdf zathura okular pdf-tools) "A list of enabled latex viewers to use, in this order. If they don't exist, they will be ignored. Recognized viewers are skim, zathura, okular and pdf-tools. @@ -96,7 +96,7 @@ If no viewers are found, `latex-preview-pane' is used.") (add-to-list 'LaTeX-indent-environment-list `(,env +latex/LaTeX-indent-item)))) -(def-package! preview +(use-package! preview :hook (LaTeX-mode . LaTeX-preview-setup) :config (setq-default preview-scale 1.4 @@ -105,12 +105,12 @@ If no viewers are found, `latex-preview-pane' is used.") ;; Nicely indent lines that have wrapped when visual line mode is activated -(def-package! adaptive-wrap +(use-package! adaptive-wrap :hook (LaTeX-mode . adaptive-wrap-prefix-mode) :init (setq-default adaptive-wrap-extra-indent 0)) -(def-package! auctex-latexmk +(use-package! auctex-latexmk :when (featurep! +latexmk) :after latex :init @@ -123,14 +123,14 @@ If no viewers are found, `latex-preview-pane' is used.") (auctex-latexmk-setup)) -(def-package! company-auctex +(use-package! company-auctex :when (featurep! :completion company) :defer t :init (add-to-list '+latex--company-backends #'company-auctex-environments nil #'eq) (add-to-list '+latex--company-backends #'company-auctex-macros nil #'eq)) -(def-package! company-math +(use-package! company-math :when (featurep! :completion company) :defer t :init diff --git a/modules/lang/ledger/config.el b/modules/lang/ledger/config.el index 898099085..d520a4d4e 100644 --- a/modules/lang/ledger/config.el +++ b/modules/lang/ledger/config.el @@ -20,12 +20,12 @@ [tab] #'ledger-reconcile-toggle) -(def-package! flycheck-ledger +(use-package! flycheck-ledger :when (featurep! :tools flycheck) :after ledger-mode) -(def-package! evil-ledger +(use-package! evil-ledger :when (featurep! :editor evil +everywhere) :hook (ledger-mode . evil-ledger-mode) :config diff --git a/modules/lang/lua/autoload/lua.el b/modules/lang/lua/autoload/lua.el index 53f23b758..4f1236771 100644 --- a/modules/lang/lua/autoload/lua.el +++ b/modules/lang/lua/autoload/lua.el @@ -25,14 +25,23 @@ ;;;###autoload (defun +lua-love-project-root () - "Returns the directory where a main.lua exists. + "Returns the directory where a main.lua or main.moon exists. Returns nil if 'love' executable can't be found." (when (executable-find "love") - (or (and (projectile-locate-dominating-file default-directory "main.lua") - (when-let (root (projectile-locate-dominating-file default-directory "src/main.lua")) - (expand-file-name "src" root))) - (and (featurep! +moonscript) - (projectile-locate-dominating-file default-directory "main.moon") - (when-let (root (projectile-locate-dominating-file default-directory "src/main.moon")) - (expand-file-name "src" root)))))) + (if (doom-project-p) + (file-name-directory + (or (project-file-exists-p! (or "main.lua" "src/main.lua")) + (and (featurep! +moonscript) + (project-file-exists-p! (or "main.moon" "src/main.moon"))) + "")) + ;; Since Love2D games are likely to be prototypes, they may not be in a + ;; well-formed project as far as projecitle is concerned, so we search for + ;; main.lua/main.moon up the file tree as a backup. + (or (projectile-locate-dominating-file default-directory "main.lua") + (when-let (root (projectile-locate-dominating-file default-directory "src/main.lua")) + (expand-file-name "src" root)) + (and (featurep! +moonscript) + (or (projectile-locate-dominating-file default-directory "main.moon") + (when-let (root (projectile-locate-dominating-file default-directory "src/main.moon")) + (expand-file-name "src" root)))))))) diff --git a/modules/lang/lua/config.el b/modules/lang/lua/config.el index e2c849787..ec8d58591 100644 --- a/modules/lang/lua/config.el +++ b/modules/lang/lua/config.el @@ -7,11 +7,11 @@ ;; ;; Major modes -(def-package! lua-mode +(use-package! lua-mode :defer t :init ;; lua-indent-level defaults to 3 otherwise. Madness. - (setq lua-indent-level tab-width) + (setq lua-indent-level 2) :config (set-lookup-handlers! 'lua-mode :documentation 'lua-search-documentation) (set-electric! 'lua-mode :words '("else" "end")) @@ -19,22 +19,22 @@ (set-company-backend! 'lua-mode '(company-lua company-yasnippet))) -(def-package! moonscript +(use-package! moonscript :when (featurep! +moonscript) :defer t :config (setq-hook! 'moonscript-mode-hook moonscript-indent-offset tab-width) (add-hook! 'moonscript-mode-hook - #'(+lua|moonscript-fix-single-quotes - +lua|moonscript-fontify-interpolation))) + #'+lua|moonscript-fix-single-quotes + #'+lua|moonscript-fontify-interpolation)) ;; ;;; Frameworks (def-project-mode! +lua-love-mode - :modes (moonscript-mode lua-mode markdown-mode json-mode) + :modes '(moonscript-mode lua-mode markdown-mode json-mode) :when #'+lua-love-project-root :on-load (progn diff --git a/modules/lang/markdown/autoload.el b/modules/lang/markdown/autoload.el index c0445c35e..1ae173fc1 100644 --- a/modules/lang/markdown/autoload.el +++ b/modules/lang/markdown/autoload.el @@ -1,26 +1,5 @@ ;;; lang/markdown/autoload.el -*- lexical-binding: t; -*- -;; Implement strike-through formatting -(defvar +markdown--regex-del - "\\(^\\|[^\\]\\)\\(\\(~\\{2\\}\\)\\([^ \n \\]\\|[^ \n ]\\(?:.\\|\n[^\n]\\)*?[^\\ ]\\)\\(\\3\\)\\)") - -;;;###autoload -(defun +markdown/insert-del () - "Surround region in github strike-through delimiters." - (interactive) - (let ((delim "~~")) - (if (markdown-use-region-p) - ;; Active region - (cl-destructuring-bind (beg . end) - (markdown-unwrap-things-in-region - (region-beginning) (region-end) - +markdown--regex-del 2 4) - (markdown-wrap-or-insert delim delim nil beg end)) - ;; Bold markup removal, bold word at point, or empty markup insertion - (if (thing-at-point-looking-at +markdown--regex-del) - (markdown-unwrap-thing-at-point nil 2 4) - (markdown-wrap-or-insert delim delim 'word nil nil))))) - ;;;###autoload (defun +markdown-flyspell-word-p () "Return t if point is on a word that should be spell checked. @@ -95,3 +74,36 @@ available. Returns its exit code." shell-file-name nil output-buffer nil shell-command-switch exe))) + +;; +;;; Commands + +;;;###autoload +(defun +markdown/insert-del () + "Surround region in github strike-through delimiters." + (interactive) + (let ((regexp "\\(^\\|[^\\]\\)\\(\\(~\\{2\\}\\)\\([^ \n \\]\\|[^ \n ]\\(?:.\\|\n[^\n]\\)*?[^\\ ]\\)\\(\\3\\)\\)") + (delim "~~")) + (if (markdown-use-region-p) + ;; Active region + (cl-destructuring-bind (beg . end) + (markdown-unwrap-things-in-region + (region-beginning) (region-end) + regexp 2 4) + (markdown-wrap-or-insert delim delim nil beg end)) + ;; Bold markup removal, bold word at point, or empty markup insertion + (if (thing-at-point-looking-at regexp) + (markdown-unwrap-thing-at-point nil 2 4) + (markdown-wrap-or-insert delim delim 'word nil nil))))) + + +;; +;;; Advice + +;;;###autoload +(defun +markdown-disable-front-matter-fontification-a (&rest _) + "Prevent fontification of YAML metadata blocks in `markdown-mode'. +This prevents a mis-feature wherein if the first line of a Markdown document has +a colon in it, then it's distractingly and usually wrongly fontified as a +metadata block. See https://github.com/jrblevin/markdown-mode/issues/328." + (ignore (goto-char (point-max)))) diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index 97593f2d3..2f41a7a9f 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -15,7 +15,7 @@ capture, the end position, and the output buffer.") ;; ;;; Packages -(def-package! markdown-mode +(use-package! markdown-mode :mode ("/README\\(?:\\.\\(?:markdown\\|md\\)\\)?\\'" . gfm-mode) :init (setq markdown-enable-wiki-links t @@ -40,10 +40,17 @@ capture, the end position, and the output buffer.") (add-hook 'markdown-mode-hook #'auto-fill-mode) - (sp-with-modes '(markdown-mode gfm-mode) - (sp-local-pair "```" "```" :post-handlers '(:add ("||\n[i]" "RET")))) + ;; Prevent mis-fontification of YAML metadata blocks in `markdown-mode' which + ;; occurs when the first line contains a colon in it. See + ;; https://github.com/jrblevin/markdown-mode/issues/328. + (advice-add :markdown-match-generic-metadata + :override #'+markdown-disable-front-matter-fontification-a) (map! :map markdown-mode-map + :n [tab] #'markdown-cycle + :n "TAB" #'markdown-cycle + :n [backtab] #'markdown-shifttab + :n "" #'markdown-shifttab :i "M-*" #'markdown-insert-list-item :i "M-b" #'markdown-insert-bold :i "M-i" #'markdown-insert-italic diff --git a/modules/lang/nim/config.el b/modules/lang/nim/config.el index 2f6680694..02b81aba3 100644 --- a/modules/lang/nim/config.el +++ b/modules/lang/nim/config.el @@ -12,31 +12,31 @@ nimsuggest isn't installed." (when IS-WINDOWS ;; TODO File PR/report upstream (https://github.com/nim-lang/nim-mode) - (defun doom*nimsuggest--get-dirty-dir () + (defadvice! +nim--suggest-get-dirty-dir-a () "The original `nimsuggest--get-dirty-dir' incorrectly extracts the frame number from the string representation of `selected-frame', which can contain characters that are illegal on Windows, causing invalid argument errors when `nimsuggest--make-tempdir' tries to use it." + :override #'nimsuggest--get-dirty-dir (let* ((frame-str (format "%s" (selected-frame))) (frame-num-str (if (string-match " \\(0x[0-9a-z]+\\)>$" frame-str) (match-string 1 frame-str)))) (file-name-as-directory (concat nimsuggest-dirty-directory frame-num-str)))) - (advice-add #'nimsuggest--get-dirty-dir :override #'doom*nimsuggest--get-dirty-dir) - + ;; TODO File PR/report upstream (https://github.com/nim-lang/nim-mode) - (defun doom*nimsuggest--get-temp-file-name (path) + (defadvice! +nim--suggest-get-temp-file-name-a (path) "Removes invalid characters from the temp file path, including the unicode character that colon is replaced with, which is known to cause issues on windows." - (replace-regexp-in-string "[꞉* |<>\"?*]" "" path)) - (advice-add #'nimsuggest--get-temp-file-name :filter-return #'doom*nimsuggest--get-temp-file-name)) + :filter-return #'nimsuggest--get-temp-file-name + (replace-regexp-in-string "[꞉* |<>\"?*]" "" path))) (map! :localleader :map nim-mode-map "b" #'nim-compile)) -(def-package! flycheck-nim +(use-package! flycheck-nim :when (featurep! :tools flycheck) :after nim-mode) diff --git a/modules/lang/nix/config.el b/modules/lang/nix/config.el index 805edff45..40ffe56d5 100644 --- a/modules/lang/nix/config.el +++ b/modules/lang/nix/config.el @@ -1,6 +1,6 @@ ;;; lang/nix/config.el -*- lexical-binding: t; -*- -(def-package! nix-mode +(use-package! nix-mode :mode "\\.nix\\'" :config (set-company-backend! 'nix-mode 'company-nixos-options) @@ -16,11 +16,11 @@ (:when (featurep! :completion helm) "o" #'helm-nixos-options))) -(def-package! nix-drv-mode +(use-package! nix-drv-mode :mode "\\.drv\\'") -(def-package! nix-update +(use-package! nix-update :commands nix-update-fetch) -(def-package! nix-repl +(use-package! nix-repl :commands nix-repl-show) diff --git a/modules/lang/ocaml/config.el b/modules/lang/ocaml/config.el index 9df9b6c2e..3394fa4f6 100644 --- a/modules/lang/ocaml/config.el +++ b/modules/lang/ocaml/config.el @@ -1,8 +1,8 @@ ;;; lang/ocaml/config.el -*- lexical-binding: t; -*- (when (featurep! +lsp) - (add-hook! (tuareg-mode-local-vars reason-mode-local-vars) - #'lsp!)) + (add-hook! '(tuareg-mode-local-vars-hook reason-mode-local-vars-hook) + #'lsp!)) (after! tuareg @@ -26,7 +26,7 @@ comment-line-break-function #'+ocaml/comment-indent-new-line) - (def-package! utop + (use-package! utop :when (featurep! :tools eval) :hook (tuareg-mode . +ocaml|init-utop) :init @@ -37,7 +37,7 @@ (utop-minor-mode))))) -(def-package! merlin +(use-package! merlin :unless (featurep! +lsp) :hook (tuareg-mode . +ocaml|init-merlin) :init @@ -60,7 +60,7 @@ "t" #'merlin-type-enclosing "a" #'tuareg-find-alternate-file) - (def-package! flycheck-ocaml + (use-package! flycheck-ocaml :when (featurep! :tools flycheck) :hook (merlin-mode . +ocaml|init-flycheck) :config @@ -72,22 +72,22 @@ ;; Enable Flycheck checker (flycheck-ocaml-setup)))) - (def-package! merlin-eldoc + (use-package! merlin-eldoc :hook (merlin-mode . merlin-eldoc-setup)) - (def-package! merlin-iedit + (use-package! merlin-iedit :when (featurep! :editor multiple-cursors) :defer t :init (map! :map tuareg-mode-map :v "R" #'merlin-iedit-occurrences)) - (def-package! merlin-imenu + (use-package! merlin-imenu :when (featurep! :emacs imenu) :hook (merlin-mode . merlin-use-merlin-imenu))) -(def-package! ocp-indent +(use-package! ocp-indent ;; must be careful to always defer this, it has autoloads that adds hooks ;; which we do not want if the executable can't be found :hook (tuareg-mode . +ocaml|init-ocp-indent) @@ -98,7 +98,7 @@ (ocp-setup-indent)))) -(def-package! ocamlformat +(use-package! ocamlformat :when (featurep! :editor format) :commands ocamlformat :hook (tuareg-mode . +ocaml|init-ocamlformat) diff --git a/modules/lang/ocaml/packages.el b/modules/lang/ocaml/packages.el index 9a5a84a40..28c8f99bc 100644 --- a/modules/lang/ocaml/packages.el +++ b/modules/lang/ocaml/packages.el @@ -16,18 +16,8 @@ ;; by default quelpa generated a version 0pre0.20180929.192844, which got ;; parsed into (0 -1 0 ...), which when compared with version nil (0) in ;; package-installed-p always yielded false - (package! ocamlformat :recipe (:fetcher github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el")))) + (package! ocamlformat :recipe + (:host github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el")))) -(package! dune :recipe (:fetcher github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el"))) - - -;; (defvar +ocaml-elisp-dir -;; (when (executable-find "opam") -;; (let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share" "--safe"))))) -;; (when (and opam-share (file-directory-p opam-share)) -;; (expand-file-name "emacs/site-lisp" opam-share))))) -;; -;; (defmacro localpackage! (name) -;; `(package! ,name :recipe (:fetcher file :path ,+ocaml-elisp-dir))) -;; -;; (localpackage! opam-site-lisp) +(package! dune :recipe + (:host github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el"))) diff --git a/modules/lang/org/README.org b/modules/lang/org/README.org index ad82e82f9..b1c059e63 100644 --- a/modules/lang/org/README.org +++ b/modules/lang/org/README.org @@ -113,22 +113,22 @@ https://www.mfoot.com/blog/2015/11/22/literate-emacs-configuration-with-org-mode #+BEGIN_SRC emacs-lisp (after! org - (remove-hook 'org-tab-first-hook #'+org|cycle-only-current-subtree t)) + (remove-hook 'org-tab-first-hook #'+org-cycle-only-current-subtree-h t)) #+END_SRC + (Evil users) Nearby tables are formatted when exiting insert or replace mode - (see ~+org|enable-auto-reformat-tables~). + (see ~+org-enable-auto-reformat-tables-h~). + Statistics cookies are updated when saving the buffer of exiting insert mode - (see ~+org|enable-auto-update-cookies~). -+ Org-protocol has been lazy loaded (see ~+org|init-protocol-lazy-loader~); + (see ~+org-enable-auto-update-cookies-h~). ++ Org-protocol has been lazy loaded (see ~+org-init-protocol-lazy-loader-h~); loaded when the server recieves a request for an org-protocol:// url. + Babel and babel plugins are now lazy loaded (see - ~+org|init-babel-lazy-loader~); loaded when a src block is executed. No need + ~+org-init-babel-lazy-loader-h~); loaded when a src block is executed. No need to use ~org-babel-do-load-languages~ in your config, just install your babel packages to extend language support (and ensure its ~org-babel-execute:*~ function is autoloaded). + If a variable is used as a file path in ~org-capture-template~, it will be resolved relative to ~org-directory~, instead of ~default-directory~ (see - ~+org*capture-expand-variable-file~). + ~+org-capture-expand-variable-file-a~). * Prerequisites Org has a few soft dependencies that you will need to make use of Org's more @@ -176,7 +176,7 @@ emacsclient --eval '(+org-capture/open-frame INTIAL-INPUT KEY)' #+END_SRC ** Built-in custom link types -This module defines a number of custom link types in ~+org|init-custom-links~. +This module defines a number of custom link types in ~+org-init-custom-links-h~. They are (with examples): + ~doom-docs:news/2.1.0~ (=~/.emacs.d/docs/%s=) diff --git a/modules/lang/org/autoload/contrib-dragndrop.el b/modules/lang/org/autoload/contrib-dragndrop.el index 100786f94..7940be5d0 100644 --- a/modules/lang/org/autoload/contrib-dragndrop.el +++ b/modules/lang/org/autoload/contrib-dragndrop.el @@ -10,30 +10,3 @@ (rassq-delete-all '+org-attach-download-dnd (copy-alist dnd-protocol-alist)))) (dnd-handle-one-url nil action uri)))) - -;;;###autoload -(defun +org-dragndrop*insert-link (_link filename) - "Produces and inserts a link to FILENAME into the document. - -If FILENAME is an image, produce an attach:%s path, otherwise use file:%s (with -an file icon produced by `+org-attach--icon')." - (if (looking-back "^[ \t]+" (line-beginning-position)) - (delete-region (match-beginning 0) (match-end 0)) - (newline)) - (cond ((image-type-from-file-name filename) - (insert - (concat (if (= org-download-image-html-width 0) "" - (format "#+attr_html: :width %dpx\n" org-download-image-html-width)) - (if (= org-download-image-latex-width 0) "" - (format "#+attr_latex: :width %dcm\n" org-download-image-latex-width)) - (cond ((file-in-directory-p filename org-attach-directory) - (format "[[attach:%s]]" (file-relative-name filename org-attach-directory))) - ((file-in-directory-p filename org-directory) - (format org-download-link-format (file-relative-name filename org-directory))) - ((format org-download-link-format filename))))) - (org-display-inline-images)) - ((insert - (format "%s [[./%s][%s]] " - (+org-attach--icon filename) - (file-relative-name filename (file-name-directory buffer-file-name)) - (file-name-nondirectory (directory-file-name filename))))))) diff --git a/modules/lang/org/autoload/contrib-ipython.el b/modules/lang/org/autoload/contrib-ipython.el index 10296d2e4..4e8e82ef3 100644 --- a/modules/lang/org/autoload/contrib-ipython.el +++ b/modules/lang/org/autoload/contrib-ipython.el @@ -2,7 +2,7 @@ ;;;###if (featurep! +ipython) ;;;###autoload -(defun +org*ob-ipython-initiate-session (&optional session params) +(defun +org-ob-ipython-initiate-session-a (&optional session params) "Create a session named SESSION according to PARAMS." (if (string= session "none") (error @@ -54,7 +54,7 @@ Make sure your src block has a :session param.") (format "*%s*" proc)))) ;;;###autoload -(defun +org*ob-ipython--create-repl (name &optional params) +(defun +org-ob-ipython-create-repl-a (name &optional params) "Create repl based on NAME and PARAMS. If PARAMS specifies remote kernel, copy the kernel config from remote server and create a repl connecting to remote session." @@ -88,7 +88,7 @@ create a repl connecting to remote session." (format "*%s*" process-name)))))) ;;;###autoload -(defun +org*babel-execute:ipython (body params) +(defun +org-babel-execute:ipython-a (body params) "Execute a BODY of IPython code with PARAMS in org-babel. This function is called by `org-babel-execute-src-block'." (message default-directory) @@ -100,7 +100,7 @@ This function is called by `org-babel-execute-src-block'." ;; * org-src-edit ;;;###autoload -(defun +org*babel-edit-prep:ipython (info) +(defun +org-babel-edit-prep:ipython-a (info) (let* ((params (nth 2 info)) (session (cdr (assoc :session params)))) (org-babel-ipython-initiate-session session params)) @@ -144,7 +144,7 @@ The optional arg SCALE is scale factor, and defaults to 2." (substring filename pos)))) ;;;###autoload -(defun +org*ob-ipython--write-base64-string (oldfunc &rest args) +(defun +org-ob-ipython-write-base64-string-a (oldfunc &rest args) (let ((file (car args)) (b64-string (cdr args))) (let ((file2x (+org--ob-ipython-mac-2x-image-file-name file))) diff --git a/modules/lang/org/autoload/contrib-present.el b/modules/lang/org/autoload/contrib-present.el index 41434a817..cd50de4f5 100644 --- a/modules/lang/org/autoload/contrib-present.el +++ b/modules/lang/org/autoload/contrib-present.el @@ -16,7 +16,7 @@ ;;; Hooks ;;;###autoload -(defun +org-present|add-overlays () +(defun +org-present-add-overlays-h () (add-to-invisibility-spec '(+org-present)) (save-excursion ;; hide org-mode options starting with #+ @@ -36,14 +36,14 @@ (remove-from-invisibility-spec '(+org-present))) ;;;###autoload -(defun +org-present|detect-slide () +(defun +org-present-detect-slide-h () (outline-show-all) (if (member "title" (org-get-tags-at)) (text-scale-set 10) (text-scale-set +org-present-text-scale))) ;;;###autoload -(defun +org-present|init-org-tree-window () +(defun +org-present-init-org-tree-window-h () "Set up the org window for presentation." (doom/window-maximize-buffer) (let ((cwm-use-vertical-padding t) diff --git a/modules/lang/org/autoload/org-capture.el b/modules/lang/org/autoload/org-capture.el index 0fbd4516a..a5041362c 100644 --- a/modules/lang/org/autoload/org-capture.el +++ b/modules/lang/org/autoload/org-capture.el @@ -15,7 +15,7 @@ "TODO") ;;;###autoload -(defun +org-capture|cleanup-frame () +(defun +org-capture-cleanup-frame-h () "Closes the org-capture frame once done adding an entry." (when (+org-capture-frame-p) (delete-frame nil t))) @@ -40,6 +40,7 @@ you're done. This can be called from an external shell script." (frame (if (+org-capture-frame-p) (selected-frame) (make-frame +org-capture-frame-parameters)))) + (select-frame-set-input-focus frame) ; fix MacOS not focusing new frames (with-selected-frame frame (require 'org-capture) (condition-case ex @@ -59,7 +60,7 @@ you're done. This can be called from an external shell script." if (buffer-local-value 'org-capture-mode buf) return buf))) (with-current-buffer buf - (add-hook 'kill-buffer-hook #'+org-capture|cleanup-frame nil t)) + (add-hook 'kill-buffer-hook #'+org-capture-cleanup-frame-h nil t)) (delete-frame frame)))))) ('error (message "org-capture: %s" (error-message-string ex)) diff --git a/modules/lang/org/autoload/org-link.el b/modules/lang/org/autoload/org-link.el index 62e37f7c0..7f87a9a84 100644 --- a/modules/lang/org/autoload/org-link.el +++ b/modules/lang/org/autoload/org-link.el @@ -1,4 +1,4 @@ -;;; org/org/autoload/org-link.el -*- lexical-binding: t; -*- +;;; lang/org/autoload/org-link.el -*- lexical-binding: t; -*- ;;;###autoload (defun +org-link-read-file (key dir) diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el index 0a569243a..a06a19259 100644 --- a/modules/lang/org/autoload/org.el +++ b/modules/lang/org/autoload/org.el @@ -1,4 +1,21 @@ -;;; org/org/autoload/org.el -*- lexical-binding: t; -*- +;;; lang/org/autoload/org.el -*- lexical-binding: t; -*- + +;; HACK A necessary hack because org requires a compilation step after being +;; cloned, and during that compilation a org-version.el is generated with these +;; two functions, which return the output of a 'git describe ...' call in the +;; repo's root. Of course, this command won't work in a sparse clone, and more +;; than that, initiating these compilation step is a hassle, so... +;;;###autoload (defun +org--release-a () "9.3") +;;;###autoload (fset 'org-release #'+org--release-a) +;;;###autoload (fset 'org-git-version #'ignore) + +;; Org itself may override the above if it's loaded too early by packages that +;; depend on it, so we have to advise it once again: +;;;###autoload (advice-add #'org-release :override #'+org--release-a) +;;;###autoload (advice-add #'org-git-version :override #'ignore) + +;; +;;; Helpers (defun +org--get-property (name &optional bound) (save-excursion @@ -298,7 +315,7 @@ wrong places)." (org-toggle-checkbox '(4))) ;;;###autoload -(defalias #'+org/toggle-fold #'+org|cycle-only-current-subtree) +(defalias #'+org/toggle-fold #'+org-cycle-only-current-subtree-h) ;;;###autoload (defun +org/open-fold () @@ -348,7 +365,7 @@ another level of headings on each invocation." ;;; Hooks ;;;###autoload -(defun +org|delete-backward-char-and-realign-table-maybe () +(defun +org-delete-backward-char-and-realign-table-maybe-h () "TODO" (when (eq major-mode 'org-mode) (org-check-before-invisible-edit 'delete-backward) @@ -371,7 +388,7 @@ another level of headings on each invocation." t)))) ;;;###autoload -(defun +org|indent-maybe () +(defun +org-indent-maybe-h () "Indent the current item (header or item), if possible. Made for `org-tab-first-hook' in evil-mode." (interactive) @@ -395,7 +412,7 @@ Made for `org-tab-first-hook' in evil-mode." t))) ;;;###autoload -(defun +org|realign-table-maybe () +(defun +org-realign-table-maybe-h () "Auto-align table under cursor and re-calculate formulas." (when (and (org-at-table-p) org-table-may-need-update) (let ((pt (point)) @@ -405,14 +422,14 @@ Made for `org-tab-first-hook' in evil-mode." (goto-char pt)))) ;;;###autoload -(defun +org|update-cookies () +(defun +org-update-cookies-h () "Update counts in headlines (aka \"cookies\")." (when (and buffer-file-name (file-exists-p buffer-file-name)) (let (org-hierarchical-todo-statistics) (org-update-parent-todo-statistics)))) ;;;###autoload -(defun +org|yas-expand-maybe () +(defun +org-yas-expand-maybe-h () "Tries to expand a yasnippet snippet, if one is available. Made for `org-tab-first-hook'." (when (bound-and-true-p yas-minor-mode) @@ -430,7 +447,7 @@ Made for `org-tab-first-hook' in evil-mode." t)))) ;;;###autoload -(defun +org|cycle-only-current-subtree (&optional arg) +(defun +org-cycle-only-current-subtree-h (&optional arg) "Toggle the local fold at the point (as opposed to cycling through all levels with `org-cycle')." (interactive "P") @@ -449,14 +466,14 @@ with `org-cycle')." t))))) ;;;###autoload -(defun +org|remove-occur-highlights () +(defun +org-remove-occur-highlights-h () "Remove org occur highlights on ESC in normal mode." (when org-occur-highlights (org-remove-occur-highlights) t)) ;;;###autoload -(defun +org|unfold-to-2nd-level-or-point () +(defun +org-unfold-to-2nd-level-or-point-h () "My version of the 'overview' #+STARTUP option: expand first-level headings. Expands the first level, but no further. If point was left somewhere deeper, unfold to point on startup." @@ -470,39 +487,39 @@ unfold to point on startup." (org-show-subtree)))))) ;;;###autoload -(defun +org|enable-auto-reformat-tables () +(defun +org-enable-auto-reformat-tables-h () "Realign tables & update formulas when exiting insert mode (`evil-mode')." (when (featurep 'evil) - (add-hook 'evil-insert-state-exit-hook #'+org|realign-table-maybe nil t) - (add-hook 'evil-replace-state-exit-hook #'+org|realign-table-maybe nil t) - (advice-add #'evil-replace :after #'+org*realign-table-maybe))) + (add-hook 'evil-insert-state-exit-hook #'+org-realign-table-maybe-h nil t) + (add-hook 'evil-replace-state-exit-hook #'+org-realign-table-maybe-h nil t) + (advice-add #'evil-replace :after #'+org-realign-table-maybe-a))) ;;;###autoload -(defun +org|enable-auto-update-cookies () +(defun +org-enable-auto-update-cookies-h () "Update statistics cookies when saving or exiting insert mode (`evil-mode')." (when (featurep 'evil) - (add-hook 'evil-insert-state-exit-hook #'+org|update-cookies nil t)) - (add-hook 'before-save-hook #'+org|update-cookies nil t)) + (add-hook 'evil-insert-state-exit-hook #'+org-update-cookies-h nil t)) + (add-hook 'before-save-hook #'+org-update-cookies-h nil t)) ;; ;;; Advice ;;;###autoload -(defun +org*fix-newline-and-indent-in-src-blocks () +(defun +org-fix-newline-and-indent-in-src-blocks-a () "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 (call-interactively #'indent-for-tab-command)))) ;;;###autoload -(defun +org*realign-table-maybe (&rest _) +(defun +org-realign-table-maybe-a (&rest _) "Auto-align table under cursor and re-calculate formulas." (when (eq major-mode 'org-mode) - (+org|realign-table-maybe))) + (+org-realign-table-maybe-h))) ;;;###autoload -(defun +org*evil-org-open-below (orig-fn count) +(defun +org-evil-org-open-below-a (orig-fn count) "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)." (cl-letf (((symbol-function 'end-of-visible-line) @@ -513,7 +530,7 @@ an effect when `evil-org-special-o/O' has `item' in it (not the default)." (funcall orig-fn count))) ;;;###autoload -(defun +org*display-link-in-eldoc (orig-fn &rest args) +(defun +org-display-link-in-eldoc-a (orig-fn &rest args) "Display the link at point in eldoc." (or (when-let (link (org-element-property :raw-link (org-element-context))) (format "Link: %s" link)) diff --git a/modules/lang/org/autoload/tables.el b/modules/lang/org/autoload/tables.el index aa1c375b3..2ec632e05 100644 --- a/modules/lang/org/autoload/tables.el +++ b/modules/lang/org/autoload/tables.el @@ -1,4 +1,4 @@ -;;; org/org/autoload/tables.el -*- lexical-binding: t; -*- +;;; lang/org/autoload/tables.el -*- lexical-binding: t; -*- ;; ;;; Row/Column traversal diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 17ca68d4c..8fed7fce5 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -3,11 +3,12 @@ ;; ;;; `org-load' hooks -(defun +org|init-agenda () +(defun +org-init-agenda-h () (unless org-agenda-files (setq org-agenda-files (list org-directory))) (setq-default - org-agenda-dim-blocked-tasks nil + ;; Hide blocked tasks in the agenda view. + org-agenda-dim-blocked-tasks 'invisible org-agenda-inhibit-startup t org-agenda-skip-unavailable-files t ;; Move the agenda to show the previous 3 days and the next 7 days for a bit @@ -18,12 +19,13 @@ org-agenda-start-day "-3d")) -(defun +org|init-appearance () +(defun +org-init-appearance-h () "Configures the UI for `org-mode'." (setq-default org-adapt-indentation nil org-cycle-include-plain-lists t org-eldoc-breadcrumb-separator " → " + org-enforce-todo-dependencies t org-entities-user '(("flat" "\\flat" nil "" "" "266D" "♭") ("sharp" "\\sharp" nil "" "" "266F" "♯")) @@ -67,14 +69,14 @@ ;; Scale up LaTeX previews a bit (default is too small) org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) - (advice-add #'org-eldoc-documentation-function :around #'+org*display-link-in-eldoc) + (advice-add #'org-eldoc-documentation-function :around #'+org-display-link-in-eldoc-a) ;; Don't do automatic indent detection in org files (add-to-list 'doom-detect-indentation-excluded-modes 'org-mode nil #'eq) ;; Previews are usually rendered with light backgrounds, so ensure their ;; background (and foreground) match the current theme. - (defun +org|update-latex-preview-background-color () + (defun +org-update-latex-preview-background-color-h () (setq-default org-format-latex-options (plist-put org-format-latex-options @@ -82,7 +84,7 @@ (face-attribute (or (cadr (assq 'default face-remapping-alist)) 'default) :background nil t)))) - (add-hook 'doom-load-theme-hook #'+org|update-latex-preview-background-color) + (add-hook 'doom-load-theme-hook #'+org-update-latex-preview-background-color-h) (set-pretty-symbols! 'org-mode :name "#+NAME:" @@ -90,7 +92,7 @@ :src_block_end "#+END_SRC")) -(defun +org|init-babel () +(defun +org-init-babel-h () (setq org-src-fontify-natively t ; make code pretty org-src-preserve-indentation t ; use native major-mode indentation org-src-tab-acts-natively t @@ -101,7 +103,7 @@ (define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit) ;; Use major-mode native TAB indentation in SRC blocks - (advice-add #'org-return-indent :after #'+org*fix-newline-and-indent-in-src-blocks) + (advice-add #'org-return-indent :after #'+org-fix-newline-and-indent-in-src-blocks-a) ;; `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: @@ -115,7 +117,7 @@ (setq org-babel-js-function-wrapper "console.log(require('util').inspect(function(){\n%s\n}()));")) -(defun +org|init-babel-lazy-loader () +(defun +org-init-babel-lazy-loader-h () "Load babel libraries lazily when babel blocks are executed." (defvar +org-babel-mode-alist '((cpp . C) @@ -135,15 +137,16 @@ when executed.") take one argument (the language specified in the src block, as a string). Stops at the first function to return non-nil.") - (defun +org*src-lazy-load-library (lang) + (defadvice! +org--src-lazy-load-library-a (lang) "Lazy load a babel package to ensure syntax highlighting." + :before #'org-src--get-lang-mode (or (cdr (assoc lang org-src-lang-modes)) (fboundp (intern-soft (format "%s-mode" lang))) (require (intern-soft (format "ob-%s" lang)) nil t))) - (advice-add #'org-src--get-lang-mode :before #'+org*src-lazy-load-library) - (defun +org*babel-lazy-load-library (info) + (defadvice! +org--babel-lazy-load-library-a (info) "Load babel libraries lazily when babel blocks are executed." + :after-while #'org-babel-confirm-evaluate (let* ((lang (nth 0 info)) (lang (if (symbolp lang) lang (intern lang))) (lang (or (cdr (assq lang +org-babel-mode-alist)) @@ -156,11 +159,10 @@ at the first function to return non-nil.") ;; child process), so we only need to make sure it's loaded. (require 'ob-async nil t)) (add-to-list 'org-babel-load-languages (cons lang t))) - t)) - (advice-add #'org-babel-confirm-evaluate :after-while #'+org*babel-lazy-load-library)) + t))) -(defun +org|init-capture-defaults () +(defun +org-init-capture-defaults-h () "Sets up some reasonable defaults, as well as two `org-capture' workflows that I like: @@ -216,43 +218,43 @@ Is relative to `org-directory', unless it is absolute. Is used in Doom's default (file+headline +org-capture-project-notes-file "Unreleased") "* TODO %?\n%i\n%a" :prepend t :kill-buffer t))) - (defun +org*capture-expand-variable-file (file) + (defadvice! +org--capture-expand-variable-file-a (file) "If a variable is used for a file path in `org-capture-template', it is used as is, and expanded relative to `default-directory'. This changes it to be relative to `org-directory', unless it is an absolute path." + :filter-args #'org-capture-expand-file (if (and (symbolp file) (boundp file)) (expand-file-name (symbol-value file) org-directory) file)) - (advice-add #'org-capture-expand-file :filter-args #'+org*capture-expand-variable-file) - (defun +org*prevent-save-prompts-when-refiling (&rest _) + (defadvice! +org--prevent-save-prompts-when-refiling-a (&rest _) "Fix #462: when refiling from org-capture, Emacs prompts to kill the underlying, modified buffer. This fixes that." + :after 'org-refile (when (bound-and-true-p org-capture-is-refiling) (org-save-all-org-buffers))) - (advice-add 'org-refile :after #'+org*prevent-save-prompts-when-refiling) - (defun +org|show-target-in-capture-header () - (setq header-line-format - (format "%s%s%s" - (propertize (abbreviate-file-name (buffer-file-name (buffer-base-buffer))) - 'face 'font-lock-string-face) - org-eldoc-breadcrumb-separator - header-line-format))) - (add-hook 'org-capture-mode-hook #'+org|show-target-in-capture-header) + (add-hook! 'org-capture-mode-hook + (defun +org-show-target-in-capture-header-h () + (setq header-line-format + (format "%s%s%s" + (propertize (abbreviate-file-name (buffer-file-name (buffer-base-buffer))) + 'face 'font-lock-string-face) + org-eldoc-breadcrumb-separator + header-line-format)))) (when (featurep! :editor evil) (add-hook 'org-capture-mode-hook #'evil-insert-state))) -(defun +org|init-capture-frame () - (add-hook 'org-capture-after-finalize-hook #'+org-capture|cleanup-frame) +(defun +org-init-capture-frame-h () + (add-hook 'org-capture-after-finalize-hook #'+org-capture-cleanup-frame-h) (when (featurep! :ui doom-dashboard) (add-hook '+doom-dashboard-inhibit-functions #'+org-capture-frame-p))) -(defun +org|init-centralized-attachments () +(defun +org-init-centralized-attachments-h () "I believe Org's native attachment system is over-complicated and litters files with metadata I don't want. So I wrote my own, which: @@ -270,11 +272,16 @@ Some commands of interest: + `+org-attach/file' + `+org-attach/url' + `+org-attach/sync'" - (setq org-attach-directory (expand-file-name org-attach-directory org-directory)) + (setq org-attach-directory (doom-path org-directory org-attach-directory)) + + ;; A shorter link to attachments + (add-to-list 'org-link-abbrev-alist + (cons "attach" + (abbreviate-file-name org-attach-directory))) (org-link-set-parameters "attach" - :follow (lambda (link) (find-file (expand-file-name link org-attach-directory))) + :follow (lambda (link) (find-file (doom-path org-attach-directory link))) :complete (lambda (&optional _arg) (+org--relpath (+org-link-read-file "attach" org-attach-directory) org-attach-directory)) @@ -291,7 +298,7 @@ Some commands of interest: (lambda (file) (file-in-directory-p file org-attach-directory))))) -(defun +org|init-centralized-exports () +(defun +org-init-centralized-exports-h () "TODO" (defvar +org-enable-centralized-exports t "If non-nil, files exported from files in `org-directory' will be stored in @@ -307,9 +314,10 @@ path too.") ;; place, and I want to be able to refer back to old exports if needed. (setq +org-export-directory (expand-file-name +org-export-directory org-directory)) - (defun +org*export-output-file-name (args) + (defadvice! +org--export-output-file-name-a (args) "Return a centralized export location unless one is provided or the current file isn't in `org-directory'." + :filter-args #'org-export-output-file-name (when (and +org-enable-centralized-exports (not (nth 2 args)) buffer-file-name @@ -319,11 +327,10 @@ file isn't in `org-directory'." (unless (file-directory-p dir) (make-directory dir t)) (setq args (list extension subtreep dir))))) - args) - (advice-add #'org-export-output-file-name :filter-args #'+org*export-output-file-name)) + args)) -(defun +org|init-custom-links () +(defun +org-init-custom-links-h () (defun +org--relpath (path root) (if (and buffer-file-name (file-in-directory-p buffer-file-name root)) (file-relative-name path) @@ -339,7 +346,6 @@ file isn't in `org-directory'." 'org-link 'error)))) - ;; Highlight broken file links (org-link-set-parameters "file" @@ -349,17 +355,16 @@ file isn't in `org-directory'." 'org-link 'error))) - ;; Add custom link types - (setq org-link-abbrev-alist - '(("github" . "https://github.com/%s") - ("youtube" . "https://youtube.com/watch?v=%s") - ("google" . "https://google.com/search?q=") - ("gimages" . "https://google.com/images?q=%s") - ("gmap" . "https://maps.google.com/maps?q=%s") - ("duckduckgo" . "https://duckduckgo.com/?q=%s") - ("wolfram" . "https://wolframalpha.com/input/?i=%s") - ("doom-repo" . "https://github.com/hlissner/doom-emacs/%s"))) + (pushnew! org-link-abbrev-alist + '("github" . "https://github.com/%s") + '("youtube" . "https://youtube.com/watch?v=%s") + '("google" . "https://google.com/search?q=") + '("gimages" . "https://google.com/images?q=%s") + '("gmap" . "https://maps.google.com/maps?q=%s") + '("duckduckgo" . "https://duckduckgo.com/?q=%s") + '("wolfram" . "https://wolframalpha.com/input/?i=%s") + '("doom-repo" . "https://github.com/hlissner/doom-emacs/%s")) (+org-def-link "org" org-directory) (+org-def-link "doom" doom-emacs-dir) @@ -372,14 +377,16 @@ file isn't in `org-directory'." (org-link-set-parameters "img" :image-data-fun #'+org-inline-data-image) ;; Add support for youtube links + previews - (def-package! org-yt)) + (use-package! org-yt)) -(defun +org|init-export () +(defun +org-init-export-h () + (setq org-export-with-smart-quotes t) + (when (featurep! :lang markdown) (add-to-list 'org-export-backends 'md)) - (def-package! ox-pandoc + (use-package! ox-pandoc :when (and (featurep! +pandoc) (executable-find "pandoc")) :after ox @@ -392,7 +399,7 @@ file isn't in `org-directory'." (variable . "revealjs-url=https://cdn.jsdelivr.net/npm/reveal.js@3/"))))) -(defun +org|init-habit () +(defun +org-init-habit-h () "TODO" (defvar +org-habit-graph-padding 2 "The padding added to the end of the consistency graph") @@ -403,41 +410,47 @@ file isn't in `org-directory'." (defvar +org-habit-graph-window-ratio 0.3 "The ratio of the consistency graphs relative to the window width") - (defun +org-habit|resize-graph() - "Right align and resize the consistency graphs based on + (add-hook! 'org-agenda-mode-hook + (defun +org-habit-resize-graph-h () + "Right align and resize the consistency graphs based on `+org-habit-graph-window-ratio'" - (require 'org-habit) - (let* ((total-days (float (+ org-habit-preceding-days org-habit-following-days))) - (preceding-days-ratio (/ org-habit-preceding-days total-days)) - (graph-width (floor (* (window-width) +org-habit-graph-window-ratio))) - (preceding-days (floor (* graph-width preceding-days-ratio))) - (following-days (- graph-width preceding-days)) - (graph-column (- (window-width) (+ preceding-days following-days))) - (graph-column-adjusted (if (> graph-column +org-habit-min-width) - (- graph-column +org-habit-graph-padding) - nil))) - (setq-local org-habit-preceding-days preceding-days) - (setq-local org-habit-following-days following-days) - (setq-local org-habit-graph-column graph-column-adjusted))) - (add-hook 'org-agenda-mode-hook #'+org-habit|resize-graph)) + (require 'org-habit) + (let* ((total-days (float (+ org-habit-preceding-days org-habit-following-days))) + (preceding-days-ratio (/ org-habit-preceding-days total-days)) + (graph-width (floor (* (window-width) +org-habit-graph-window-ratio))) + (preceding-days (floor (* graph-width preceding-days-ratio))) + (following-days (- graph-width preceding-days)) + (graph-column (- (window-width) (+ preceding-days following-days))) + (graph-column-adjusted (if (> graph-column +org-habit-min-width) + (- graph-column +org-habit-graph-padding) + nil))) + (setq-local org-habit-preceding-days preceding-days) + (setq-local org-habit-following-days following-days) + (setq-local org-habit-graph-column graph-column-adjusted))))) -(defun +org|init-hacks () +(defun +org-init-hacks-h () "Getting org to behave." ;; Don't open separate windows (setf (alist-get 'file org-link-frame-setup) #'find-file) ;; Open directory links in Emacs (add-to-list 'org-file-apps '(directory . emacs)) - (defun +org|delayed-recenter () - "`recenter', but after a tiny delay. Necessary to prevent certain race -conditions where a window's buffer hasn't changed at the time this hook is run." - (run-at-time 0.1 nil #'recenter)) - (add-hook 'org-follow-link-hook #'+org|delayed-recenter) + ;; When you create a sparse tree and `org-indent-mode' is enabled, the + ;; highlighting destroys the invisibility added by `org-indent-mode'. + ;; Therefore, don't highlight when creating a sparse tree. + (setq org-highlight-sparse-tree-matches nil) - (defun +org*strip-properties-from-outline (orig-fn path &optional width prefix separator) + (add-hook! 'org-follow-link-hook + (defun +org-delayed-recenter-h () + "`recenter', but after a tiny delay. Necessary to prevent certain race +conditions where a window's buffer hasn't changed at the time this hook is run." + (run-at-time 0.1 nil #'recenter))) + + (defadvice! +org--strip-properties-from-outline-a (orig-fn path &optional width prefix separator) "Remove link syntax and fix variable height text (e.g. org headings) in the eldoc string." + :around #'org-format-outline-path (let ((result (funcall orig-fn path width prefix separator)) (separator (or separator "/"))) (string-join @@ -449,30 +462,28 @@ eldoc string." (org-add-props (replace-regexp-in-string org-any-link-re "\\4" part) nil 'face `(:foreground ,(face-foreground face nil t) :weight bold))) separator))) - (advice-add #'org-format-outline-path :around #'+org*strip-properties-from-outline) - (defun +org|exclude-agenda-buffers-from-workspace () - "Prevent from temporarily-opened agenda buffers from being associated with -the current workspace." - (when (and org-agenda-new-buffers (bound-and-true-p persp-mode)) - (let (persp-autokill-buffer-on-remove) - (persp-remove-buffer org-agenda-new-buffers - (get-current-persp) - nil)))) - (add-hook 'org-agenda-finalize-hook #'+org|exclude-agenda-buffers-from-workspace) + (add-hook! 'org-agenda-finalize-hook + (defun +org-exclude-agenda-buffers-from-workspace-h () + "Prevent temporarily-opened agenda buffers from being associated with the +current workspace." + (when (and org-agenda-new-buffers (bound-and-true-p persp-mode)) + (let (persp-autokill-buffer-on-remove) + (persp-remove-buffer org-agenda-new-buffers + (get-current-persp) + nil))))) - (defun +org*exclude-agenda-buffers-from-recentf (orig-fn file) + (defadvice! +org--exclude-agenda-buffers-from-recentf-a (orig-fn file) "Prevent temporarily opened agenda buffers from polluting recentf." + :around #'org-get-agenda-file-buffer (let ((recentf-exclude (list (lambda (_file) t)))) - (funcall orig-fn file))) - (advice-add #'org-get-agenda-file-buffer - :around #'+org*exclude-agenda-buffers-from-recentf)) + (funcall orig-fn file)))) -(defun +org|init-keybinds () +(defun +org-init-keybinds-h () "Sets up org-mode and evil keybindings. Tries to fix the idiosyncrasies between the two." - (add-hook 'doom-escape-hook #'+org|remove-occur-highlights) + (add-hook 'doom-escape-hook #'+org-remove-occur-highlights-h) ;; 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. @@ -482,8 +493,12 @@ between the two." ;; insert new headings after current subtree rather than inside it org-insert-heading-respect-content t) - (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) + (add-hook! 'org-tab-first-hook + #'+org-indent-maybe-h + #'+org-yas-expand-maybe-h) + + (add-hook 'doom-delete-backward-functions + #'+org-delete-backward-char-and-realign-table-maybe-h) (map! :map org-mode-map ;; textmate-esque newline insertion @@ -504,6 +519,7 @@ between the two." (:when (featurep! :completion helm) "." #'helm-org-in-buffer-headings "/" #'helm-org-agenda-files-headings) + "a" #'org-attach "d" #'org-deadline "f" #'org-footnote-new "h" #'org-toggle-heading @@ -511,6 +527,7 @@ between the two." "I" #'org-toggle-inline-images "l" #'org-insert-link "L" #'org-store-link + "p" #'org-set-property "q" #'org-set-tags-command "r" #'org-refile "s" #'org-schedule @@ -571,8 +588,8 @@ between the two." "e" #'org-table-edit-formulas "=" #'org-table-eval-formulas))) - ;; Fixes #1483: this messy hack fixes `org-agenda' or `evil-org-agenda' - ;; overriding SPC, breaking the localleader. TODO Improve me! + ;; HACK Fixes #1483: this messy hack fixes `org-agenda' or `evil-org-agenda' + ;; overriding SPC, breaking the localleader (define-minor-mode org-agenda-localleader-mode "TODO" :keymap (make-sparse-keymap)) (add-hook 'org-agenda-mode-hook #'org-agenda-localleader-mode) @@ -586,10 +603,10 @@ between the two." "t" #'org-agenda-todo)) -(defun +org|init-keybinds-for-evil (&rest args) +(defun +org-init-keybinds-for-evil-h (&rest args) "TODO" (when (featurep! :editor evil +everywhere) - (def-package! evil-org + (use-package! evil-org :hook (org-mode . evil-org-mode) :init (defvar evil-org-key-theme '(navigation insert textobjects)) @@ -600,17 +617,17 @@ between the two." ;; change `evil-org-key-theme' instead (advice-add #'evil-org-set-key-theme :override #'ignore)) - (def-package! evil-org-agenda + (use-package! evil-org-agenda :after org-agenda :config (evil-org-agenda-set-keys)) ;; Only fold the current tree, rather than recursively - (add-hook 'org-tab-first-hook #'+org|cycle-only-current-subtree t) + (add-hook 'org-tab-first-hook #'+org-cycle-only-current-subtree-h t) ;; 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) + (advice-add #'evil-org-open-below :around #'+org-evil-org-open-below-a) (map! :map outline-mode-map ;; Undo keybinds from `evil-collection-outline' @@ -635,7 +652,7 @@ between the two." (org-at-table-p) '+org/table-previous-row) :i "C-j" (general-predicate-dispatch 'org-down-element (org-at-table-p) 'org-table-next-row) - ;; expanding tables (prepend/append columns/rows) + ;; moving/(de|pro)moting subtress & expanding tables (prepend/append columns/rows) :ni "C-S-l" (general-predicate-dispatch 'org-shiftmetaright (org-at-table-p) 'org-table-insert-column) :ni "C-S-h" (general-predicate-dispatch 'org-shiftmetaleft @@ -644,7 +661,7 @@ between the two." (org-at-table-p) 'org-table-insert-row) :ni "C-S-j" (general-predicate-dispatch 'org-shiftmetadown (org-at-table-p) '+org/table-insert-row-below) - ;; shifting table rows/columns + ;; moving/(de|pro)moting single headings & shifting table rows/columns :ni "C-M-S-l" (general-predicate-dispatch 'org-metaright (org-at-table-p) 'org-table-move-column-right) :ni "C-M-S-h" (general-predicate-dispatch 'org-metaleft @@ -695,7 +712,7 @@ between the two." "C-S-j" (λ! (org-eval-in-calendar '(calendar-forward-year 1)))))) -(defun +org|init-popup-rules () +(defun +org-init-popup-rules-h () (set-popup-rules! '(("^\\*Org Links" :slot -1 :vslot -1 :size 2 :ttl 0) ("^\\*\\(?:Agenda Com\\|Calendar\\|Org \\(?:Export Dispatcher\\|Select\\)\\)" @@ -705,13 +722,14 @@ between the two." ("^CAPTURE.*\\.org$" :size 0.2 :quit nil :select t :autosave t)))) -(defun +org|init-protocol-lazy-loader () +(defun +org-init-protocol-lazy-loader-h () "Brings lazy-loaded support for org-protocol, so external programs (like browsers) can invoke specialized behavior from Emacs. Normally you'd simply require `org-protocol' and use it, but the package loads all of org for no compelling reason, so..." - (defun +org*server-visit-files (args) + (defadvice! +org--server-visit-files-a (args) "Advise `server-visit-flist' to invoke `org-protocol' lazily." + :filter-args #'server-visit-files (cl-destructuring-bind (files proc &optional nowait) args (catch 'greedy (let ((flist (reverse files))) @@ -731,20 +749,19 @@ compelling reason, so..." (setcar var fname)) ((setq files (delq var files))))))))) (list files proc nowait))) - (advice-add #'server-visit-files :filter-args #'+org*server-visit-files) ;; Disable built-in, clumsy advice (after! org-protocol (ad-disable-advice 'server-visit-files 'before 'org-protocol-detect-protocol-server))) -(defun +org|init-protocol () +(defun +org-init-protocol-h () ;; TODO org-board or better link grabbing support ;; TODO org-capture + org-protocol instead of bin/org-capture ) -(defun +org|init-smartparens () +(defun +org-init-smartparens-h () "TODO" (after! smartparens (defun +org-sp-point-in-checkbox-p (_id action _context) @@ -753,22 +770,27 @@ compelling reason, so..." (defun +org-sp-point-at-bol-p (_id action _context) (and (eq action 'insert) - (eq (char-before) ?*) - (sp--looking-back-p "^\\**" (line-beginning-position)))) + (save-excursion + (skip-chars-backward "*") + (bolp)))) + + (defun +org-sp-in-src-block-p (_id action _context) + (and (eq action 'insert) + (org-in-src-block-p))) ;; make delimiter auto-closing a little more conservative (sp-with-modes 'org-mode - (sp-local-pair "*" "*" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-at-bol-p)) - (sp-local-pair "_" "_" :unless '(:add sp-point-before-word-p sp-in-math-p)) - (sp-local-pair "/" "/" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-in-checkbox-p)) - (sp-local-pair "~" "~" :unless '(:add sp-point-before-word-p)) - (sp-local-pair "=" "=" :unless '(:add sp-point-before-word-p sp-in-math-p))))) + (sp-local-pair "*" "*" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-at-bol-p +org-sp-in-src-block-p)) + (sp-local-pair "_" "_" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-in-src-block-p)) + (sp-local-pair "/" "/" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-in-checkbox-p +org-sp-in-src-block-p)) + (sp-local-pair "~" "~" :unless '(:add sp-point-before-word-p +org-sp-in-src-block-p)) + (sp-local-pair "=" "=" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-in-src-block-p))))) ;; ;;; Bootstrap -(def-package! org +(use-package! org :defer-incrementally calendar find-func format-spec org-macs org-compat org-faces org-entities org-list org-pcomplete org-src org-footnote org-macro ob org org-agenda @@ -777,59 +799,62 @@ compelling reason, so..." ;; Change org defaults (should be set before org loads) (defvar org-directory "~/org/") (defvar org-attach-directory ".attach/") - (defvar org-clock-persist-file (concat doom-etc-dir "org-clock-save.el")) - (defvar org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/")) - (defvar org-preview-latex-image-directory (concat doom-cache-dir "org-latex/")) + + (setq org-clock-persist-file (concat doom-etc-dir "org-clock-save.el") + org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/") + org-preview-latex-image-directory (concat doom-cache-dir "org-latex/")) (defvar org-modules - '(;; org-w3m - ;; org-bbdb - org-bibtex - ;; org-docview - ;; org-gnus - ;; org-info - ;; org-irc - ;; org-mhe - ;; org-rmail + '(;; ol-w3m + ;; ol-bbdb + ol-bibtex + ;; ol-docview + ;; ol-gnus + ;; ol-info + ;; ol-irc + ;; ol-mhe + ;; ol-rmail + ;; ol-eww )) (add-hook! 'org-mode-hook - #'(org-bullets-mode ; "prettier" bullets - org-indent-mode ; margin-based indentation - toc-org-enable ; auto-table of contents - auto-fill-mode ; hard line wrapping - ;; `show-paren-mode' causes flickering with indentation margins made by - ;; `org-indent-mode', so we turn off show-paren-mode altogether - doom|disable-show-paren-mode - ;; Shows a lot of false positives, so... - doom|disable-show-trailing-whitespace + #'org-bullets-mode ; "prettier" bullets + #'org-indent-mode ; margin-based indentation + #'toc-org-enable ; auto-table of contents + #'auto-fill-mode ; hard line wrapping + ;; `show-paren-mode' causes flickering with indentation margins made by + ;; `org-indent-mode', so we turn off show-paren-mode altogether + #'doom-disable-show-paren-mode-h + ;; Shows a lot of false positives, so... + #'doom-disable-show-trailing-whitespace-h - +org|enable-auto-reformat-tables - +org|enable-auto-update-cookies - +org|unfold-to-2nd-level-or-point)) + #'+org-enable-auto-reformat-tables-h + #'+org-enable-auto-update-cookies-h + #'+org-unfold-to-2nd-level-or-point-h) (add-hook! 'org-load-hook - #'(+org|init-appearance - +org|init-agenda - +org|init-babel - +org|init-babel-lazy-loader - +org|init-capture-defaults - +org|init-capture-frame - +org|init-centralized-attachments - +org|init-centralized-exports - +org|init-custom-links - +org|init-export - +org|init-habit - +org|init-hacks - +org|init-keybinds - +org|init-keybinds-for-evil ; will noop without :editor evil - +org|init-popup-rules - +org|init-protocol - +org|init-protocol-lazy-loader - +org|init-smartparens)) + #'+org-init-appearance-h + #'+org-init-agenda-h + #'+org-init-babel-h + #'+org-init-babel-lazy-loader-h + #'+org-init-capture-defaults-h + #'+org-init-capture-frame-h + #'+org-init-centralized-attachments-h + #'+org-init-centralized-exports-h + #'+org-init-custom-links-h + #'+org-init-export-h + #'+org-init-habit-h + #'+org-init-hacks-h + #'+org-init-keybinds-h + #'+org-init-keybinds-for-evil-h ; will noop without :editor evil + #'+org-init-popup-rules-h + #'+org-init-protocol-h + #'+org-init-protocol-lazy-loader-h + #'+org-init-smartparens-h) ;; In case the user has eagerly loaded org from their configs - (when (featurep 'org) + (when (and (featurep 'org) + (not doom-reloading-p)) (message "`org' was already loaded by the time lang/org loaded, this may cause issues") (run-hooks 'org-load-hook)) @@ -839,18 +864,18 @@ compelling reason, so..." (if (featurep! +present) (load! "contrib/present")) :config - (add-hook 'org-open-at-point-functions #'doom|set-jump) + (add-hook 'org-open-at-point-functions #'doom-set-jump-h) ;;; Packages (after! toc-org (setq toc-org-hrefify-default "gh") - (defun +org*unfold-toc (&rest _) + (defadvice! +org--unfold-toc-a (&rest _) + :before #'toc-org-insert-toc (save-excursion (when (re-search-forward toc-org-toc-org-regexp (point-max) t) - (+org/open-fold)))) - (advice-add #'toc-org-insert-toc :before #'+org*unfold-toc)) + (+org/open-fold))))) - (def-package! org-pdfview + (use-package! org-pdfview :when (featurep! :tools pdf) :commands (org-pdfview-open) :init @@ -860,7 +885,7 @@ compelling reason, so..." ;; support for links to specific pages (add-to-list 'org-file-apps '("\\.pdf::\\([[:digit:]]+\\)\\'" . (lambda (_file link) (org-pdfview-open link))))) - (def-package! org-crypt ; built-in + (use-package! org-crypt ; built-in :commands org-encrypt-entries :hook (org-reveal-start . org-decrypt-entry) :init @@ -869,8 +894,17 @@ compelling reason, so..." (add-to-list 'org-tags-exclude-from-inheritance "crypt") (setq org-crypt-key user-mail-address)) - (def-package! org-clock ; built-in + (use-package! org-clock ; built-in :commands org-clock-save - :hook (org-mode . org-clock-load) - :init (setq org-clock-persist 'history) - :config (add-hook 'kill-emacs-hook #'org-clock-save))) + :init + (setq org-clock-persist t) + (defadvice! +org--clock-load-a (&rest _) + "Lazy load org-clock until its commands are used." + :before '(org-clock-in + org-clock-out + org-clock-in-last + org-clock-goto + org-clock-cancel) + (org-clock-load)) + :config + (add-hook 'kill-emacs-hook #'org-clock-save))) diff --git a/modules/lang/org/contrib/dragndrop.el b/modules/lang/org/contrib/dragndrop.el index a8f571489..9ebac3bed 100644 --- a/modules/lang/org/contrib/dragndrop.el +++ b/modules/lang/org/contrib/dragndrop.el @@ -1,13 +1,13 @@ ;;; lang/org/contrib/dragndrop.el -*- lexical-binding: t; -*- ;;;###if (featurep! +dragndrop) -(def-package! org-download +(use-package! org-download :commands (org-download-dnd org-download-dnd-base64) :init - ;; Add these manually so that org-download is lazy-loaded... - (add-to-list 'dnd-protocol-alist '("^\\(https?\\|ftp\\|file\\|nfs\\):" . +org-dragndrop-download-dnd)) - (add-to-list 'dnd-protocol-alist '("^data:" . org-download-dnd-base64)) - + ;; HACK We add these manually so that org-download is truly lazy-loaded + (nconcq! dnd-protocol-alist + '(("^\\(?:https?\\|ftp\\|file\\|nfs\\):" . +org-dragndrop-download-dnd) + ("^data:" . org-download-dnd-base64))) (advice-add #'org-download-enable :override #'ignore) :config (setq org-download-image-dir org-attach-directory @@ -22,15 +22,39 @@ ;; Handle non-image files a little differently. Images should be inserted ;; as-is, as image previews. Other files, like pdfs or zips, should be linked ;; to, with an icon indicating the type of file. - (advice-add #'org-download-insert-link :override #'+org-dragndrop*insert-link) + (defadvice! +org--dragndrop-insert-link-a (_link filename) + "Produces and inserts a link to FILENAME into the document. - (defun +org-dragndrop*download-fullname (path) +If FILENAME is an image, produce an attach:%s path, otherwise use file:%s (with +an file icon produced by `+org-attach--icon')." + :override #'org-download-insert-link + (if (looking-back "^[ \t]+" (line-beginning-position)) + (delete-region (match-beginning 0) (match-end 0)) + (newline)) + (cond ((image-type-from-file-name filename) + (insert + (concat (if (= org-download-image-html-width 0) "" + (format "#+attr_html: :width %dpx\n" org-download-image-html-width)) + (if (= org-download-image-latex-width 0) "" + (format "#+attr_latex: :width %dcm\n" org-download-image-latex-width)) + (cond ((file-in-directory-p filename org-attach-directory) + (format "[[attach:%s]]" (file-relative-name filename org-attach-directory))) + ((file-in-directory-p filename org-directory) + (format org-download-link-format (file-relative-name filename org-directory))) + ((format org-download-link-format filename))))) + (org-display-inline-images)) + ((insert + (format "%s [[./%s][%s]] " + (+org-attach--icon filename) + (file-relative-name filename (file-name-directory buffer-file-name)) + (file-name-nondirectory (directory-file-name filename))))))) + + (advice-add #'org-download--dir-2 :override #'ignore) + (defadvice! +org--dragndrop-download-fullname-a (path) "Write PATH relative to current file." + :filter-return #'org-download--fullname (let ((dir (or (if buffer-file-name (file-name-directory buffer-file-name)) default-directory))) (if (file-in-directory-p dir org-directory) (file-relative-name path dir) - path))) - (advice-add #'org-download--dir-2 :override #'ignore) - (advice-add #'org-download--fullname - :filter-return #'+org-dragndrop*download-fullname)) + path)))) diff --git a/modules/lang/org/contrib/ipython.el b/modules/lang/org/contrib/ipython.el index ec0b9d47d..361811583 100644 --- a/modules/lang/org/contrib/ipython.el +++ b/modules/lang/org/contrib/ipython.el @@ -1,17 +1,17 @@ ;;; lang/org/contrib/babel.el -*- lexical-binding: t; -*- ;;;###if (featurep! +ipython) -(def-package! ob-ipython +(use-package! ob-ipython :defer t :init (defvar +ob-ipython-local-runtime-dir nil) (setq ob-ipython-resources-dir ".ob-ipython-resrc") - (defun +org|babel-load-ipython (lang) - (and (string-prefix-p "jupyter-" (symbol-name lang)) - (require 'ob-ipython nil t))) - (add-hook '+org-babel-load-functions #'+org|babel-load-ipython) + (add-hook! '+org-babel-load-functions + (defun +org-babel-load-ipython-h (lang) + (and (string-prefix-p "jupyter-" (symbol-name lang)) + (require 'ob-ipython nil t)))) (after! org-src (add-to-list 'org-src-lang-modes '("ipython" . python))) @@ -27,14 +27,14 @@ :select nil :quit nil :ttl nil))) ;; advices for remote kernel and org-src-edit - (advice-add #'ob-ipython--create-repl :override #'+org*ob-ipython--create-repl) - (advice-add #'org-babel-edit-prep:ipython :override #'+org*babel-edit-prep:ipython) - (advice-add #'org-babel-execute:ipython :before #'+org*babel-execute:ipython) - (advice-add #'org-babel-ipython-initiate-session :override #'+org*ob-ipython-initiate-session) + (advice-add #'ob-ipython--create-repl :override #'+org-ob-ipython-create-repl-a) + (advice-add #'org-babel-edit-prep:ipython :override #'+org-babel-edit-prep:ipython-a) + (advice-add #'org-babel-execute:ipython :before #'+org-babel-execute:ipython-a) + (advice-add #'org-babel-ipython-initiate-session :override #'+org-ob-ipython-initiate-session-a) ;; retina resolution image hack (when IS-MAC - (advice-add #'ob-ipython--write-base64-string :around #'+org*ob-ipython--write-base64-string)) + (advice-add #'ob-ipython--write-base64-string :around #'+org-ob-ipython-write-base64-string-a)) ;; ipython has its own async keyword, disable ipython in ob-async. (after! ob-async diff --git a/modules/lang/org/contrib/present.el b/modules/lang/org/contrib/present.el index 51996fd75..4118a2449 100644 --- a/modules/lang/org/contrib/present.el +++ b/modules/lang/org/contrib/present.el @@ -11,7 +11,7 @@ ;; ;;; Packages -(def-package! ox-reveal +(use-package! ox-reveal :after ox :init ;; Fix #1127, where ox-reveal adds an errant entry to @@ -22,7 +22,7 @@ org-reveal-mathjax t)) -(def-package! org-tree-slide +(use-package! org-tree-slide :commands org-tree-slide-mode :config (org-tree-slide-simple-profile) @@ -36,10 +36,10 @@ :n [left] #'org-tree-slide-move-previous-tree) (add-hook! 'org-tree-slide-mode-after-narrow-hook - #'(+org-present|detect-slide - +org-present|add-overlays - org-display-inline-images)) + #'+org-present-detect-slide-h + #'+org-present-add-overlays-h + #'org-display-inline-images) - (add-hook 'org-tree-slide-mode-hook #'+org-present|init-org-tree-window) + (add-hook 'org-tree-slide-mode-hook #'+org-present-init-org-tree-window-h) (advice-add #'org-tree-slide--display-tree-with-narrow :around #'+org-present*narrow-to-subtree)) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 2697d0896..b085c68b7 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -3,13 +3,12 @@ ;; Prevent built-in Org from playing into the byte-compilation of ;; `org-plus-contrib'. -(when-let (orglib (locate-library "org" nil doom-site-load-path)) +(when-let (orglib (locate-library "org" nil doom--initial-load-path)) (setq load-path (delete (substring (file-name-directory orglib) 0 -1) load-path))) -(package! org-plus-contrib) ; install cutting-edge version of org-mode -(package! org :ignore t) ; ignore org on ELPA, if possible -(package! org-bullets :recipe (:fetcher github :repo "Kaligule/org-bullets")) +(package! org-plus-contrib) ; install cutting-edge version of org-mode +(package! org-bullets :recipe (:host github :repo "Kaligule/org-bullets")) (package! toc-org) (when (featurep! :editor evil) (package! evil-org)) @@ -17,7 +16,9 @@ (package! org-pdfview)) (package! htmlize) (package! ox-clip) -(package! org-yt :recipe (:fetcher github :repo "TobiasZawada/org-yt")) +(package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt")) +(when (featurep! :tools magit) + (package! orgit)) ;;; Babel (package! ob-async) @@ -28,7 +29,7 @@ (when (featurep! :lang nim) (package! ob-nim)) (when (featurep! :lang racket) - (package! ob-racket :recipe (:fetcher github :repo "DEADB17/ob-racket"))) + (package! ob-racket :recipe (:host github :repo "DEADB17/ob-racket"))) (when (featurep! :lang rest) (package! ob-restclient)) (when (featurep! :lang rust) @@ -37,18 +38,14 @@ ;;; Modules (when (featurep! +dragndrop) (package! org-download)) - (when (featurep! +gnuplot) (package! gnuplot) (package! gnuplot-mode)) - (when (featurep! +ipython) (package! ob-ipython)) - (when (featurep! +pandoc) (package! ox-pandoc)) - (when (featurep! +present) - (package! centered-window :recipe (:fetcher github :repo "anler/centered-window-mode")) + (package! centered-window :recipe (:host github :repo "anler/centered-window-mode")) (package! org-tree-slide) (package! ox-reveal)) diff --git a/modules/lang/perl/config.el b/modules/lang/perl/config.el index e0768b554..1e433ee1e 100644 --- a/modules/lang/perl/config.el +++ b/modules/lang/perl/config.el @@ -2,9 +2,9 @@ ;; There's also `perl-mode' for perl < 6, which is already set up. -(def-package! perl6-detect) +(use-package! perl6-detect) -(def-package! flycheck-perl6 +(use-package! flycheck-perl6 :when (featurep! :tools flycheck) :after perl6-mode) diff --git a/modules/lang/php/config.el b/modules/lang/php/config.el index 55a01fe46..ced83437d 100644 --- a/modules/lang/php/config.el +++ b/modules/lang/php/config.el @@ -1,6 +1,6 @@ ;;; lang/php/config.el -*- lexical-binding: t; -*- -(def-package! php-mode +(use-package! php-mode :mode "\\.inc\\'" :config ;; Disable HTML compatibility in php-mode. `web-mode' has superior support for @@ -33,7 +33,7 @@ "s" #'phpunit-current-test)) -(def-package! phpactor +(use-package! phpactor :unless (featurep! +lsp) :after php-mode :config @@ -50,7 +50,7 @@ "ic" #'phpactor-import-class)) -(def-package! php-refactor-mode +(use-package! php-refactor-mode :hook php-mode :config (map! :localleader @@ -62,7 +62,7 @@ "rv" #'php-refactor--rename-local-variable)) -(def-package! php-extras +(use-package! php-extras :after php-mode :preface ;; We'll set up company support ourselves @@ -87,7 +87,7 @@ (message "PHP eldoc updated!"))))) -(def-package! hack-mode +(use-package! hack-mode :when (featurep! +hack) :mode "\\.hh$") @@ -96,10 +96,9 @@ ;; Projects (def-project-mode! +php-laravel-mode - :modes (php-mode yaml-mode web-mode nxml-mode js2-mode scss-mode) + :modes '(php-mode yaml-mode web-mode nxml-mode js2-mode scss-mode) :files (and "artisan" "server.php")) (def-project-mode! +php-composer-mode - :modes (web-mode php-mode) + :modes '(web-mode php-mode) :files ("composer.json")) - diff --git a/modules/lang/php/packages.el b/modules/lang/php/packages.el index 39f874bb5..f2fa57243 100644 --- a/modules/lang/php/packages.el +++ b/modules/lang/php/packages.el @@ -2,16 +2,18 @@ ;;; lang/php/packages.el (package! php-boris) -(package! php-extras :recipe (:fetcher github :repo "arnested/php-extras")) +(package! php-extras :recipe (:host github :repo "arnested/php-extras")) (package! php-mode) (package! php-refactor-mode) (package! phpunit) (when (featurep! +hack) - (package! hack-mode :recipe (:fetcher github :repo "hhvm/hack-mode"))) + (package! hack-mode :recipe (:host github :repo "hhvm/hack-mode"))) (unless (featurep! +lsp) - (package! phpactor)) + (package! phpactor) + (when (featurep! :completion company) + (package! company-phpactor))) (when (featurep! :editor format) (package! php-cs-fixer)) diff --git a/modules/lang/plantuml/autoload.el b/modules/lang/plantuml/autoload.el index 171febc8e..a66680d46 100644 --- a/modules/lang/plantuml/autoload.el +++ b/modules/lang/plantuml/autoload.el @@ -6,5 +6,5 @@ (interactive) (if (file-exists-p plantuml-jar-path) (user-error "plantuml.jar already installed") - (url-copy-file "https://kent.dl.sourceforge.net/project/plantuml/plantuml.jar" + (url-copy-file "https://datapacket.dl.sourceforge.net/project/plantuml/plantuml.jar" plantuml-jar-path))) diff --git a/modules/lang/plantuml/config.el b/modules/lang/plantuml/config.el index f32ba72b1..593ecfead 100644 --- a/modules/lang/plantuml/config.el +++ b/modules/lang/plantuml/config.el @@ -1,6 +1,6 @@ ;;; lang/plantuml/config.el -*- lexical-binding: t; -*- -(def-package! plantuml-mode +(use-package! plantuml-mode :defer t :init (setq plantuml-jar-path (concat doom-etc-dir "plantuml.jar") @@ -9,7 +9,7 @@ (set-popup-rule! "^\\*PLANTUML" :size 0.4 :select nil :ttl 0)) -(def-package! flycheck-plantuml +(use-package! flycheck-plantuml :when (featurep! :tools flycheck) :after plantuml-mode :config (flycheck-plantuml-setup)) diff --git a/modules/lang/purescript/config.el b/modules/lang/purescript/config.el index 2cf6fb038..02c83c4b0 100644 --- a/modules/lang/purescript/config.el +++ b/modules/lang/purescript/config.el @@ -2,20 +2,20 @@ (after! purescript-mode (add-hook! 'purescript-mode-hook - #'(purescript-indentation-mode - rainbow-delimiters-mode)) + #'purescript-indentation-mode + #'rainbow-delimiters-mode) (set-lookup-handlers! 'purescript-mode :definition #'psc-ide-goto-definition :documentation #'purescript-pursuit)) -;; (def-package! flycheck-purescript +;; (use-package! flycheck-purescript ;; :after purescript-mode ;; :config ;; (add-hook 'flycheck-mode-hook #'flycheck-purescript-setup)) -(def-package! psc-ide +(use-package! psc-ide :hook (purescript-mode . psc-ide-mode) :config (remove-hook 'company-backends 'company-psc-ide-backend) diff --git a/modules/lang/python/autoload/python.el b/modules/lang/python/autoload/python.el index 763bf0886..b799441a0 100644 --- a/modules/lang/python/autoload/python.el +++ b/modules/lang/python/autoload/python.el @@ -53,3 +53,10 @@ ((when-let (bin (projectile-locate-dominating-file default-directory "bin/python")) (setq-local doom-modeline-python-executable (expand-file-name "bin/python" bin)))) ((executable-find exe)))))) + +;;;###autoload +(defun +python/optimize-imports () + "organize imports" + (interactive) + (pyimport-remove-unused) + (pyimpsort-buffer)) diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 19a04e93b..2abe9f80e 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -12,13 +12,12 @@ called.") ;; ;; Packages -(def-package! python +(use-package! python :defer t :init (setq python-environment-directory doom-cache-dir python-indent-guess-indent-offset-verbose nil) :config - (set-electric! 'python-mode :chars '(?:)) (set-repl-handler! 'python-mode #'+python/open-repl) (set-docsets! 'python-mode "Python 3" "NumPy" "SciPy") @@ -40,9 +39,37 @@ called.") :for "for" :return "return" :yield "yield") + ;; Stop the spam! + (setq python-indent-guess-indent-offset-verbose nil) + (when (featurep! +lsp) (add-hook 'python-mode-local-vars-hook #'lsp!)) + ;; Default to Python 3. Prefer the versioned Python binaries since some + ;; systems stupidly make the unversioned one point at Python 2. + (when (and (executable-find "python3") + (string= python-shell-interpreter "python")) + (setq python-shell-interpreter "python3")) + + (add-hook! 'python-mode-hook + (defun +python-use-correct-flycheck-executables-h () + "Use the correct Python executables for Flycheck." + (let ((executable python-shell-interpreter)) + (save-excursion + (goto-char (point-min)) + (save-match-data + (when (or (looking-at "#!/usr/bin/env \\(python[^ \n]+\\)") + (looking-at "#!\\([^ \n]+/python[^ \n]+\\)")) + (setq executable (substring-no-properties (match-string 1)))))) + ;; Try to compile using the appropriate version of Python for + ;; the file. + (setq-local flycheck-python-pycompile-executable executable) + ;; We might be running inside a virtualenv, in which case the + ;; modules won't be available. But calling the executables + ;; directly will work. + (setq-local flycheck-python-pylint-executable "pylint") + (setq-local flycheck-python-flake8-executable "flake8")))) + (define-key python-mode-map (kbd "DEL") nil) ; interferes with smartparens (sp-local-pair 'python-mode "'" nil :unless '(sp-point-before-word-p @@ -50,14 +77,14 @@ called.") sp-point-before-same-p)) ;; Affects pyenv and conda - (advice-add #'pythonic-activate :after-while #'+modeline|update-env-in-all-windows) - (advice-add #'pythonic-deactivate :after #'+modeline|clear-env-in-all-windows) + (advice-add #'pythonic-activate :after-while #'+modeline-update-env-in-all-windows-h) + (advice-add #'pythonic-deactivate :after #'+modeline-clear-env-in-all-windows-h) (setq-hook! 'python-mode-hook tab-width python-indent-offset)) -(def-package! anaconda-mode - :hook (python-mode-local-vars . +python|init-anaconda-mode-maybe) +(use-package! anaconda-mode + :after python :init (setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/") anaconda-mode-eldoc-as-single-line t) @@ -70,18 +97,20 @@ called.") :documentation #'anaconda-mode-show-doc) (set-popup-rule! "^\\*anaconda-mode" :select nil) - (defun +python|init-anaconda-mode-maybe () - (unless (bound-and-true-p lsp-mode) - (anaconda-mode +1))) + (add-hook! 'python-mode-local-vars-hook + (defun +python-init-anaconda-mode-maybe-h () + "Enable `anaconda-mode' if `lsp-mode' isn't." + (unless (bound-and-true-p lsp-mode) + (anaconda-mode +1)))) - (defun +python|auto-kill-anaconda-processes () + (defun +python-auto-kill-anaconda-processes-h () "Kill anaconda processes if this buffer is the last python buffer." (when (and (eq major-mode 'python-mode) (not (delq (current-buffer) (doom-buffers-in-mode 'python-mode (buffer-list))))) (anaconda-mode-stop))) (add-hook! 'python-mode-hook - (add-hook 'kill-buffer-hook #'+python|auto-kill-anaconda-processes nil t)) + (add-hook 'kill-buffer-hook #'+python-auto-kill-anaconda-processes-h nil t)) (when (featurep 'evil) (add-hook 'anaconda-mode-hook #'evil-normalize-keymaps)) @@ -95,21 +124,23 @@ called.") "u" #'anaconda-mode-find-references)) -(def-package! pyimport +(use-package! pyimport :after python :config (map! :map python-mode-map :localleader - (:prefix ("i" . "insert") - :desc "Missing imports" "m" #'pyimport-insert-missing) - (:prefix ("r" . "remove") - :desc "Unused imports" "r" #'pyimport-remove-unused))) + (:prefix ("i" . "imports") + :desc "Insert missing imports" "i" #'pyimport-insert-missing + :desc "Remove unused imports" "r" #'pyimport-remove-unused + :desc "Sort imports" "s" #'pyimpsort-buffer + :desc "Optimize imports" "o" #'+python/optimize-imports + ))) -(def-package! nose +(use-package! nose :commands nose-mode :preface (defvar nose-mode-map (make-sparse-keymap)) - :init (associate! nose-mode :match "/test_.+\\.py$" :modes (python-mode)) + :minor ("/test_.+\\.py$" . nose-mode) :config (set-popup-rule! "^\\*nosetests" :size 0.4 :select nil) (set-yas-minor-mode! 'nose-mode) @@ -128,7 +159,7 @@ called.") "V" #'nosetests-pdb-module)) -(def-package! python-pytest +(use-package! python-pytest :defer t :init (map! :after python @@ -146,7 +177,7 @@ called.") ;; ;; Environment management -(def-package! pipenv +(use-package! pipenv :commands pipenv-project-p :hook (python-mode . pipenv-mode) :init (setq pipenv-with-projectile nil) @@ -161,12 +192,12 @@ called.") (:description . "Run Python script")))) -(def-package! pyvenv +(use-package! pyvenv :after python :init (when (featurep! :ui modeline) - (add-hook 'pyvenv-post-activate-hooks #'+modeline|update-env-in-all-windows) - (add-hook 'pyvenv-pre-deactivate-hooks #'+modeline|clear-env-in-all-windows)) + (add-hook 'pyvenv-post-activate-hooks #'+modeline-update-env-in-all-windows-h) + (add-hook 'pyvenv-pre-deactivate-hooks #'+modeline-clear-env-in-all-windows-h)) :config (add-hook 'hack-local-variables-hook #'pyvenv-track-virtualenv) (add-to-list 'global-mode-string @@ -174,7 +205,7 @@ called.") 'append)) -(def-package! pyenv-mode +(use-package! pyenv-mode :when (featurep! +pyenv) :after python :config @@ -183,7 +214,7 @@ called.") (add-to-list 'exec-path (expand-file-name "shims" (or (getenv "PYENV_ROOT") "~/.pyenv"))))) -(def-package! conda +(use-package! conda :when (featurep! +conda) :after python :config @@ -205,9 +236,11 @@ called.") "~/.anaconda" "~/.miniconda" "~/.miniconda3" + "~/miniconda3" "/usr/bin/anaconda3" "/usr/local/anaconda3" - "/usr/local/miniconda3") + "/usr/local/miniconda3" + "/usr/local/Caskroom/miniconda/base") if (file-directory-p dir) return (setq conda-anaconda-home dir conda-env-home-directory dir)) @@ -216,7 +249,21 @@ called.") ;; integration with term/eshell (conda-env-initialize-interactive-shells) (after! eshell (conda-env-initialize-eshell)) - + (add-to-list 'global-mode-string '(conda-env-current-name (" conda:" conda-env-current-name " ")) 'append)) + + +(use-package! lsp-python-ms + :when (featurep! +lsp) + :after lsp-clients + :init + ;; HACK lsp-python-ms shouldn't install itself if it isn't present. This + ;; circumvents LSP falling back to pyls when lsp-python-ms is absent. + ;; Installing the server should be a deliberate act; either 'M-x + ;; lsp-python-ms-setup' or setting `lsp-python-ms-executable' to an existing + ;; install will do. + (defadvice! +python--dont-auto-install-server-a () + :override #'lsp-python-ms--command-string + lsp-python-ms-executable)) diff --git a/modules/lang/python/packages.el b/modules/lang/python/packages.el index e43655be6..e04502601 100644 --- a/modules/lang/python/packages.el +++ b/modules/lang/python/packages.el @@ -4,6 +4,10 @@ ;; Major modes (package! pip-requirements) +;; LSP +(when (featurep! +lsp) + (package! lsp-python-ms)) + ;; Programming environment (package! anaconda-mode) (when (featurep! :completion company) @@ -23,3 +27,4 @@ ;; Import managements (package! pyimport) +(package! pyimpsort) diff --git a/modules/lang/racket/config.el b/modules/lang/racket/config.el index 7359baebb..12d9994ad 100644 --- a/modules/lang/racket/config.el +++ b/modules/lang/racket/config.el @@ -1,6 +1,6 @@ ;;; lang/racket/config.el -*- lexical-binding: t; -*- -(def-package! racket-mode +(use-package! racket-mode :hook (racket-repl-mode . racket-unicode-input-method-enable) :config (set-popup-rule! "^\\*Racket REPL" :size 10 :select t) @@ -16,9 +16,13 @@ (set-rotate-patterns! 'racket-mode :symbols '(("#true" "#false"))) - (setq racket-smart-open-bracket-enable t) + (add-hook! 'racket-mode-hook + #'rainbow-delimiters-mode + #'highlight-quoted-mode) + (set-lookup-handlers! 'racket-mode :definition #'racket-visit-definition) - (add-hook! racket-mode #'(rainbow-delimiters-mode highlight-quoted-mode)) + (map! :map (racket-mode-map racket-repl-mode-map) + :i "[" #'racket-smart-open-bracket) (map! :localleader :map racket-mode-map diff --git a/modules/lang/rest/config.el b/modules/lang/rest/config.el index 1dd3d0aa4..dfe9d053f 100644 --- a/modules/lang/rest/config.el +++ b/modules/lang/rest/config.el @@ -1,6 +1,6 @@ ;;; lang/rest/config.el -*- lexical-binding: t; -*- -(def-package! restclient +(use-package! restclient :mode ("\\.http\\'" . restclient-mode) :config (set-popup-rule! "^\\*HTTP Response" :size 0.4 :quit 'other) @@ -30,7 +30,7 @@ "c" #'restclient-copy-curl-command)) -(def-package! company-restclient +(use-package! company-restclient :when (featurep! :completion company) :after restclient :config (set-company-backend! 'restclient-mode 'company-restclient)) diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index 00705cd9f..1d3c6a38e 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -3,7 +3,7 @@ ;; ;; Packages -(def-package! enh-ruby-mode +(use-package! enh-ruby-mode :mode ("\\.\\(?:pry\\|irb\\)rc\\'" . +ruby|init) :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|jbuilder\\|thor\\)\\'" . +ruby|init) :mode ("/\\(?:Berks\\|Cap\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby|init) @@ -30,7 +30,7 @@ (setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6)) -(def-package! robe +(use-package! robe :defer t :init (defun +ruby|init-robe-mode-maybe () @@ -60,11 +60,11 @@ ;; NOTE Must be loaded before `robe-mode' -(def-package! yard-mode +(use-package! yard-mode :hook (ruby-mode enh-ruby-mode)) -(def-package! rubocop +(use-package! rubocop :hook (enh-ruby-mode . rubocop-mode) :config (map! :localleader @@ -78,7 +78,7 @@ ;; ;; Package & Ruby version management -(def-package! rake +(use-package! rake :defer t :init (setq rake-cache-file (concat doom-cache-dir "rake.cache")) @@ -91,7 +91,7 @@ "R" #'rake-regenerate-cache "f" #'rake-find-task)) -(def-package! bundler +(use-package! bundler :defer t :init (map! :after enh-ruby-mode @@ -113,7 +113,7 @@ ;; ;; Testing frameworks -(def-package! rspec-mode +(use-package! rspec-mode :mode ("/\\.rspec\\'" . text-mode) :init (when (featurep! :editor evil) @@ -142,7 +142,7 @@ "s" #'rspec-dired-verify-single)) -(def-package! minitest +(use-package! minitest :defer t :config (when (featurep! :editor evil) diff --git a/modules/lang/rust/config.el b/modules/lang/rust/config.el index b031fe00f..60da2e9fa 100644 --- a/modules/lang/rust/config.el +++ b/modules/lang/rust/config.el @@ -1,6 +1,6 @@ ;;; lang/rust/config.el -*- lexical-binding: t; -*- -(def-package! rust-mode +(use-package! rust-mode :defer t :config (setq rust-indent-method-chain t) @@ -10,11 +10,11 @@ ;; happen once. ;; ;; rust-mode is still required for `racer'. - (defun +rust|init () - "Switch to `rustic-mode', if it's available." - (when (require 'rustic nil t) - (rustic-mode))) - (add-hook 'rust-mode-hook #'+rust|init) + (add-hook! 'rust-mode-hook + (defun +rust-init-h () + "Switch to `rustic-mode', if it's available." + (when (require 'rustic nil t) + (rustic-mode)))) (set-docsets! '(rust-mode rustic-mode) "Rust") (when (featurep! +lsp) @@ -28,11 +28,10 @@ (when (featurep! :tools editorconfig) (after! editorconfig (pushnew! editorconfig-indentation-alist - '(rust-mode rust-indent-offset) '(rustic-mode rustic-indent-offset))))) -(def-package! racer +(use-package! racer :unless (featurep! +lsp) :hook ((rust-mode rustic-mode) . racer-mode) :config @@ -41,7 +40,7 @@ :documentation '+rust-racer-lookup-documentation)) -(def-package! rustic +(use-package! rustic :when EMACS26+ :after rust-mode :preface @@ -54,17 +53,17 @@ ;; `rustic-setup-rls' uses `package-installed-p' unnecessarily, which breaks ;; because Doom lazy loads package.el. - (defun +rust*disable-package-installed-p-call (orig-fn &rest args) + (defadvice! +rust--disable-package-call-a (orig-fn &rest args) + :around #'rustic-setup-rls (cl-letf (((symbol-function 'package-installed-p) (symbol-function 'ignore))) - (apply orig-fn args))) - (advice-add #'rustic-setup-rls :around #'+rust*disable-package-installed-p-call)) + (apply orig-fn args)))) ;; ;;; Tools -(def-package! cargo +(use-package! cargo :after rust-mode :config (defvar +rust-keymap diff --git a/modules/lang/rust/packages.el b/modules/lang/rust/packages.el index c93336763..250637459 100644 --- a/modules/lang/rust/packages.el +++ b/modules/lang/rust/packages.el @@ -4,8 +4,6 @@ (when EMACS26+ (package! rustic)) (package! rust-mode) -(when (featurep! :tools flycheck) - (package! flycheck-rust)) (unless (featurep! +lsp) (package! racer)) diff --git a/modules/lang/scala/config.el b/modules/lang/scala/config.el index 5140a12b6..702dc054f 100644 --- a/modules/lang/scala/config.el +++ b/modules/lang/scala/config.el @@ -14,7 +14,7 @@ (add-hook 'scala-mode-local-vars-hook #'lsp!))) -(def-package! ensime +(use-package! ensime :unless (featurep! +lsp) :defer t :config @@ -31,6 +31,6 @@ (require 'imenu)) -(def-package! sbt-mode +(use-package! sbt-mode :after scala-mode :config (set-repl-handler! 'scala-mode #'+scala/open-repl)) diff --git a/modules/lang/scala/packages.el b/modules/lang/scala/packages.el index cc85e38ea..93c1f879e 100644 --- a/modules/lang/scala/packages.el +++ b/modules/lang/scala/packages.el @@ -5,4 +5,7 @@ (package! scala-mode) (unless (featurep! +lsp) - (package! ensime)) + ;; Fix #1697: Ensime doesn't have a master branch and its MELPA recipe doesn't + ;; specify a branch. Straight can't handle packages with non-standard primary + ;; branches (at the time of writing), so we must specify it manually: + (package! ensime :recipe (:branch "2.0"))) diff --git a/modules/lang/sh/config.el b/modules/lang/sh/config.el index ed0f1c5da..34af5d4d0 100644 --- a/modules/lang/sh/config.el +++ b/modules/lang/sh/config.el @@ -10,10 +10,11 @@ ;; ;; Packages -(def-package! sh-script ; built-in +(use-package! sh-script ; built-in :mode ("\\.zunit\\'" . sh-mode) :mode ("/bspwmrc\\'" . sh-mode) - :mode ("/bin/[^/]+\\'" . sh-mode) + :init + (add-to-list 'auto-mode-alist '("/bin/[^/]+\\'" . sh-mode) 'append) :config (set-electric! 'sh-mode :words '("else" "elif" "fi" "done" "then" "do" "esac" ";;")) (set-repl-handler! 'sh-mode #'+sh/open-repl) @@ -29,21 +30,21 @@ (nil "^\\s-*\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*()" 1))) ;; `sh-set-shell' is chatty about setting up indentation rules - (advice-add #'sh-set-shell :around #'doom*shut-up) + (advice-add #'sh-set-shell :around #'doom-shut-up-a) ;; 1. Fontifies variables in double quotes ;; 2. Fontify command substitution in double quotes ;; 3. Fontify built-in/common commands (see `+sh-builtin-keywords') - (defun +sh|init-extra-fontification () - (font-lock-add-keywords - nil `((+sh--match-variables-in-quotes - (1 'font-lock-constant-face prepend) - (2 'font-lock-variable-name-face prepend)) - (+sh--match-command-subst-in-quotes - (1 'sh-quoted-exec prepend)) - (,(regexp-opt +sh-builtin-keywords 'words) - (0 'font-lock-type-face append))))) - (add-hook 'sh-mode-hook #'+sh|init-extra-fontification) + (add-hook! 'sh-mode-hook + (defun +sh-init-extra-fontification-h () + (font-lock-add-keywords + nil `((+sh--match-variables-in-quotes + (1 'font-lock-constant-face prepend) + (2 'font-lock-variable-name-face prepend)) + (+sh--match-command-subst-in-quotes + (1 'sh-quoted-exec prepend)) + (,(regexp-opt +sh-builtin-keywords 'words) + (0 'font-lock-type-face append)))))) ;; 4. Fontify delimiters by depth (add-hook 'sh-mode-hook #'rainbow-delimiters-mode) @@ -51,17 +52,17 @@ (sp-local-pair 'sh-mode "`" "`" :unless '(sp-point-before-word-p sp-point-before-same-p)) ;; sh-mode has file extensions checks for other shells, but not zsh, so... - (defun +sh|detect-zsh () - (when (or (and buffer-file-name - (string-match-p "\\.zsh\\'" buffer-file-name)) - (save-excursion - (goto-char (point-min)) - (looking-at-p "^#!.+/zsh[$ ]"))) - (sh-set-shell "zsh"))) - (add-hook 'sh-mode-hook #'+sh|detect-zsh)) + (add-hook! 'sh-mode-hook + (defun +sh-detect-zsh-h () + (when (or (and buffer-file-name + (string-match-p "\\.zsh\\'" buffer-file-name)) + (save-excursion + (goto-char (point-min)) + (looking-at-p "^#!.+/zsh[$ ]"))) + (sh-set-shell "zsh"))))) -(def-package! company-shell +(use-package! company-shell :when (featurep! :completion company) :after sh-script :config @@ -69,7 +70,7 @@ (setq company-shell-delete-duplicates t)) -(def-package! fish-mode +(use-package! fish-mode :when (featurep! +fish) :defer t :config (set-formatter! 'fish-mode #'fish_indent)) diff --git a/modules/lang/solidity/config.el b/modules/lang/solidity/config.el index 6a4cf3efb..f9502a67f 100644 --- a/modules/lang/solidity/config.el +++ b/modules/lang/solidity/config.el @@ -7,7 +7,7 @@ (setq solidity-comment-style 'slash) -(def-package! solidity-flycheck ; included with solidity-mode +(use-package! solidity-flycheck ; included with solidity-mode :when (featurep! :tools flycheck) :after solidity-mode :config @@ -18,7 +18,7 @@ (add-to-list 'flycheck-checkers 'solium-checker nil #'eq))) -(def-package! company-solidity +(use-package! company-solidity :when (featurep! :completion company) :after solidity-mode :config diff --git a/modules/lang/swift/config.el b/modules/lang/swift/config.el index 0129d78ac..f0a822830 100644 --- a/modules/lang/swift/config.el +++ b/modules/lang/swift/config.el @@ -4,14 +4,14 @@ (set-repl-handler! 'swift-mode #'run-swift)) -(def-package! flycheck-swift +(use-package! flycheck-swift :when (and (featurep! :tools flycheck) (not (featurep! +lsp))) :after swift-mode :config (flycheck-swift-setup)) -(def-package! company-sourcekit +(use-package! company-sourcekit :when (and (featurep! :completion company) (not (featurep! +lsp))) :after swift-mode @@ -19,7 +19,7 @@ (set-company-backend! 'swift-mode '(company-sourcekit company-yasnippet))) -(def-package! lsp-sourcekit +(use-package! lsp-sourcekit :when (featurep! +lsp) :after swift-mode :init (add-hook 'swift-mode-hook #'lsp!)) diff --git a/modules/lang/terra/config.el b/modules/lang/terra/config.el index 27515edb6..7cf7db023 100644 --- a/modules/lang/terra/config.el +++ b/modules/lang/terra/config.el @@ -3,7 +3,7 @@ ;; ;; Major modes -(def-package! terra-mode +(use-package! terra-mode :defer t :config (set-lookup-handlers! 'terra-mode :documentation 'terra-search-documentation) diff --git a/modules/lang/terra/packages.el b/modules/lang/terra/packages.el index 90115f396..4a1a1485b 100644 --- a/modules/lang/terra/packages.el +++ b/modules/lang/terra/packages.el @@ -1,7 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/lua/packages.el -(package! terra-mode :recipe (:fetcher github :repo "StanfordLegion/terra-mode")) +(package! terra-mode + :recipe (:host github :repo "StanfordLegion/terra-mode")) (when (featurep! :completion company) (package! company-lua)) diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el index 69ebdf666..dc68ec39c 100644 --- a/modules/lang/web/+css.el +++ b/modules/lang/web/+css.el @@ -24,7 +24,8 @@ ;; ;;; Major modes -(add-hook! (css-mode sass-mode stylus-mode) #'rainbow-mode) +(add-hook! '(css-mode-hook sass-mode-hook stylus-mode-hook) + #'rainbow-mode) ;; built-in, and contains both css-mode & scss-mode (after! css-mode @@ -47,10 +48,11 @@ ;;; Tools (when (featurep! +lsp) - (add-hook! (css-mode sass-mode less-css-mode) #'lsp!)) + (add-hook! '(css-mode-hook sass-mode-hook less-css-mode-hook) + #'lsp!)) -(def-package! counsel-css +(use-package! counsel-css :when (featurep! :completion ivy) :commands counsel-css :hook (css-mode . counsel-css-imenu-setup) @@ -59,7 +61,7 @@ :localleader ";" #'counsel-css)) -(def-package! helm-css-scss +(use-package! helm-css-scss :when (featurep! :completion helm) :defer t :init diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el index ae87120a1..7bac671ab 100644 --- a/modules/lang/web/+html.el +++ b/modules/lang/web/+html.el @@ -1,6 +1,6 @@ ;;; lang/web/+html.el -*- lexical-binding: t; -*- -(def-package! web-mode +(use-package! web-mode :mode "\\.p?html?$" :mode "\\.\\(?:tpl\\|blade\\)\\(\\.php\\)?$" :mode "\\.erb$" @@ -64,7 +64,7 @@ :desc "Rehighlight buffer" "h" #'web-mode-buffer-highlight :desc "Indent buffer" "i" #'web-mode-buffer-indent - (:prefix "a" + (:prefix ("a" . "attribute") "b" #'web-mode-attribute-beginning "e" #'web-mode-attribute-end "i" #'web-mode-attribute-insert @@ -74,7 +74,7 @@ "p" #'web-mode-attribute-previous "p" #'web-mode-attribute-transpose) - (:prefix "b" + (:prefix ("b" . "block") "b" #'web-mode-block-beginning "c" #'web-mode-block-close "e" #'web-mode-block-end @@ -83,7 +83,7 @@ "p" #'web-mode-block-previous "s" #'web-mode-block-select) - (:prefix "d" + (:prefix ("d" . "dom") "a" #'web-mode-dom-apostrophes-replace "d" #'web-mode-dom-errors-show "e" #'web-mode-dom-entities-encode @@ -92,7 +92,7 @@ "t" #'web-mode-dom-traverse "x" #'web-mode-dom-xpath) - (:prefix "e" + (:prefix ("e" . "element") "/" #'web-mode-element-close "a" #'web-mode-element-content-select "b" #'web-mode-element-beginning @@ -112,7 +112,7 @@ "v" #'web-mode-element-vanish "w" #'web-mode-element-wrap) - (:prefix "t" + (:prefix ("t" . "tag") "a" #'web-mode-tag-attributes-sort "b" #'web-mode-tag-beginning "e" #'web-mode-tag-end @@ -142,4 +142,4 @@ (when (featurep! +lsp) - (add-hook! (html-mode web-mode) #'lsp!)) + (add-hook! '(html-mode-hook web-mode-hook) #'lsp!)) diff --git a/modules/lang/web/autoload/html.el b/modules/lang/web/autoload/html.el index cf205b79d..dbba73e60 100644 --- a/modules/lang/web/autoload/html.el +++ b/modules/lang/web/autoload/html.el @@ -61,13 +61,14 @@ character.") "HTML encode/decode TEXT. Based on Xah's replace HTML named entities function @ http://ergoemacs.org/emacs/elisp_replace_html_entities_command.html" (interactive "") - (seq-doseq (rep +web-entities-list) - (let ((from (elt rep (if decode-p 0 1))) - (to (elt rep (if decode-p 1 0))) - case-fold-search) - (when (and (not (equal from " ")) - (string-match-p (regexp-quote from) text)) - (setq text (replace-regexp-in-string (regexp-quote from) to text t t))))) + (mapc (lambda (rep) + (let ((from (elt rep (if decode-p 0 1))) + (to (elt rep (if decode-p 1 0))) + case-fold-search) + (when (and (not (equal from " ")) + (string-match-p (regexp-quote from) text)) + (setq text (replace-regexp-in-string (regexp-quote from) to text t t))))) + +web-entities-list) text) (defun +web--entities-region (beg end &optional decode-p) @@ -75,14 +76,15 @@ character.") function @ http://ergoemacs.org/emacs/elisp_replace_html_entities_command.html" (save-restriction (narrow-to-region beg end) - (seq-doseq (rep +web-entities-list) - (let ((from (elt rep (if decode-p 0 1))) - (to (elt rep (if decode-p 1 0))) - case-fold-search) - (unless (equal from " ") - (goto-char (point-min)) - (while (search-forward from nil t) - (replace-match to 'FIXEDCASE 'LITERAL))))))) + (mapc (lambda (rep) + (let ((from (elt rep (if decode-p 0 1))) + (to (elt rep (if decode-p 1 0))) + case-fold-search) + (unless (equal from " ") + (goto-char (point-min)) + (while (search-forward from nil t) + (replace-match to 'FIXEDCASE 'LITERAL))))) + +web-entities-list))) ;;;###autoload (defun +web-encode-entities (text) diff --git a/modules/lang/web/config.el b/modules/lang/web/config.el index 7a9b96f05..50a293cc5 100644 --- a/modules/lang/web/config.el +++ b/modules/lang/web/config.el @@ -4,7 +4,7 @@ (load! "+css") -(def-package! emmet-mode +(use-package! emmet-mode :preface (defvar emmet-mode-keymap (make-sparse-keymap)) :hook (css-mode web-mode html-mode haml-mode nxml-mode rjsx-mode reason-mode) :config @@ -22,7 +22,7 @@ ;; Framework-based minor-modes (def-project-mode! +web-jekyll-mode - :modes (web-mode js-mode coffee-mode css-mode haml-mode pug-mode) + :modes '(web-mode js-mode coffee-mode css-mode haml-mode pug-mode) :files (and (or "_config.yml" "_config.toml") (or "_layouts/" "_posts/")) :on-enter @@ -30,18 +30,18 @@ (web-mode-set-engine "django"))) (def-project-mode! +web-wordpress-mode - :modes (php-mode web-mode css-mode haml-mode pug-mode) + :modes '(php-mode web-mode css-mode haml-mode pug-mode) :files (or "wp-config.php" "wp-config-sample.php")) (when (featurep! :lang javascript) (def-project-mode! +web-angularjs-mode - :modes (+javascript-npm-mode) + :modes '(+javascript-npm-mode) :when (+javascript-npm-dep-p 'angular)) (def-project-mode! +web-react-mode - :modes (+javascript-npm-mode) + :modes '(+javascript-npm-mode) :when (+javascript-npm-dep-p 'react)) (def-project-mode! +web-phaser-mode - :modes (+javascript-npm-mode) + :modes '(+javascript-npm-mode) :when (+javascript-npm-dep-p '(or phaser phaser-ce)))) diff --git a/modules/term/eshell/autoload/eshell.el b/modules/term/eshell/autoload/eshell.el index 92bc991d2..cadcd20dd 100644 --- a/modules/term/eshell/autoload/eshell.el +++ b/modules/term/eshell/autoload/eshell.el @@ -10,7 +10,7 @@ ;; -;; Helpers +;;; Helpers (defun +eshell--add-buffer (buf) (ring-remove+insert+extend +eshell-buffers buf 'grow)) @@ -75,7 +75,7 @@ ;; -;; Commands +;;; Commands ;;;###autoload (defun +eshell/toggle (arg &optional command) @@ -106,7 +106,7 @@ (evil-change-to-initial-state)) (goto-char (point-max))) (with-current-buffer (pop-to-buffer eshell-buffer) - (doom|mark-buffer-as-real) + (doom-mark-buffer-as-real-h) (if (eq major-mode 'eshell-mode) (run-hooks 'eshell-mode-hook) (eshell-mode)) @@ -117,8 +117,6 @@ (defun +eshell/here (&optional command) "Open eshell in the current buffer." (interactive "P") - (when (eq major-mode 'eshell-mode) - (user-error "Already in an eshell buffer")) (let ((buf (+eshell--unused-buffer))) (with-current-buffer (switch-to-buffer buf) (if (eq major-mode 'eshell-mode) @@ -146,7 +144,7 @@ Once the eshell process is killed, the previous frame layout is restored." ;; -;; Keybinds +;;; Keybinds ;;;###autoload (defun +eshell/search-history () @@ -266,10 +264,10 @@ delete." ;; -;; Hooks +;;; Hooks ;;;###autoload -(defun +eshell|init () +(defun +eshell-init-h () "Initialize and track this eshell buffer in `+eshell-buffers'." (let ((current-buffer (current-buffer))) (dolist (buf (+eshell-buffers)) @@ -280,7 +278,7 @@ delete." (setq +eshell--last-buffer current-buffer))) ;;;###autoload -(defun +eshell|cleanup () +(defun +eshell-cleanup-h () "Close window (or workspace) on quit." (let ((buf (current-buffer))) (when (+eshell--remove-buffer buf) @@ -307,13 +305,13 @@ delete." return (select-window win)))))))))) ;;;###autoload -(defun +eshell|switch-workspace (type) +(defun +eshell-switch-workspace-fn (type) (when (eq type 'frame) (setq +eshell-buffers (or (persp-parameter 'eshell-buffers) (make-ring 25))))) ;;;###autoload -(defun +eshell|save-workspace (_workspace target) +(defun +eshell-save-workspace-fn (_workspace target) (when (framep target) (set-persp-parameter 'eshell-buffers +eshell-buffers))) diff --git a/modules/term/eshell/autoload/evil.el b/modules/term/eshell/autoload/evil.el index 37942de6d..a3326e322 100644 --- a/modules/term/eshell/autoload/evil.el +++ b/modules/term/eshell/autoload/evil.el @@ -6,7 +6,7 @@ "TODO" (interactive "") (let ((buffer (+eshell-last-buffer)) - (command (+evil*resolve-vim-path command))) + (command (+evil-resolve-vim-path-a command))) (cond (buffer (select-window (get-buffer-window buffer)) (+eshell-run-command command buffer)) @@ -14,7 +14,7 @@ ((+eshell/open-popup nil command))))) ;;;###autoload -(defun +eshell*goto-prompt-on-insert () +(defun +eshell-goto-prompt-on-insert-a () "Move cursor to the prompt when switching to insert mode (if point isn't already there). diff --git a/modules/term/eshell/autoload/prompts.el b/modules/term/eshell/autoload/prompts.el index 7f6502df5..3a9638d09 100644 --- a/modules/term/eshell/autoload/prompts.el +++ b/modules/term/eshell/autoload/prompts.el @@ -20,7 +20,7 @@ ""))) ;;;###autoload -(defun +eshell-default-prompt () +(defun +eshell-default-prompt-fn () "Generate the prompt string for eshell. Use for `eshell-prompt-function'." (concat (if (bobp) "" "\n") (let ((pwd (eshell/pwd))) diff --git a/modules/term/eshell/config.el b/modules/term/eshell/config.el index f7b12d8ef..d2b9b5c76 100644 --- a/modules/term/eshell/config.el +++ b/modules/term/eshell/config.el @@ -67,36 +67,36 @@ You should use `set-eshell-alias!' to change this.") eshell-input-filter (lambda (input) (not (string-match-p "\\`\\s-+" input))) ;; em-prompt eshell-prompt-regexp "^.* λ " - eshell-prompt-function #'+eshell-default-prompt + eshell-prompt-function #'+eshell-default-prompt-fn ;; em-glob eshell-glob-case-insensitive t eshell-error-if-no-glob t) ;; Consider eshell buffers real - (add-hook 'eshell-mode-hook #'doom|mark-buffer-as-real) + (add-hook 'eshell-mode-hook #'doom-mark-buffer-as-real-h) ;; Keep track of open eshell buffers - (add-hook 'eshell-mode-hook #'+eshell|init) - (add-hook 'eshell-exit-hook #'+eshell|cleanup) + (add-hook 'eshell-mode-hook #'+eshell-init-h) + (add-hook 'eshell-exit-hook #'+eshell-cleanup-h) ;; Enable autopairing in eshell (add-hook 'eshell-mode-hook #'smartparens-mode) ;; Persp-mode/workspaces integration (when (featurep! :ui workspaces) - (add-hook 'persp-activated-functions #'+eshell|switch-workspace) - (add-hook 'persp-before-switch-functions #'+eshell|save-workspace)) + (add-hook 'persp-activated-functions #'+eshell-switch-workspace-fn) + (add-hook 'persp-before-switch-functions #'+eshell-save-workspace-fn)) ;; UI enhancements - (defun +eshell|remove-fringes () - (set-window-fringes nil 0 0) - (set-window-margins nil 1 nil)) - (add-hook 'eshell-mode-hook #'+eshell|remove-fringes) + (add-hook! 'eshell-mode-hook + (defun +eshell-remove-fringes-h () + (set-window-fringes nil 0 0) + (set-window-margins nil 1 nil))) - (defun +eshell|enable-text-wrapping () - (visual-line-mode +1) - (set-display-table-slot standard-display-table 0 ?\ )) - (add-hook 'eshell-mode-hook #'+eshell|enable-text-wrapping) + (add-hook! 'eshell-mode-hook + (defun +eshell-enable-text-wrapping-h () + (visual-line-mode +1) + (set-display-table-slot standard-display-table 0 ?\ ))) (add-hook 'eshell-mode-hook #'hide-mode-line-mode) @@ -109,60 +109,58 @@ You should use `set-eshell-alias!' to change this.") (after! em-term (pushnew! eshell-visual-commands "tmux" "htop" "vim" "nvim" "ncmpcpp")) - (defun +eshell|init-aliases () - (setq +eshell--default-aliases eshell-command-aliases-list - eshell-command-aliases-list - (append eshell-command-aliases-list - +eshell-aliases))) - (add-hook 'eshell-alias-load-hook #'+eshell|init-aliases) + (add-hook! 'eshell-alias-load-hook + (defun +eshell-init-aliases-h () + (setq +eshell--default-aliases eshell-command-aliases-list + eshell-command-aliases-list + (append eshell-command-aliases-list + +eshell-aliases)))) (when (featurep! :editor evil +everywhere) - (advice-add #'evil-collection-eshell-next-prompt-on-insert :override #'+eshell*goto-prompt-on-insert)) + (advice-add #'evil-collection-eshell-next-prompt-on-insert + :override #'+eshell-goto-prompt-on-insert-a)) - (defun +eshell|init-keymap () - "Setup eshell keybindings. This must be done in a hook because eshell-mode -redefines its keys every time `eshell-mode' is enabled." - (map! :map eshell-mode-map - :n "RET" #'+eshell/goto-end-of-prompt - :n [return] #'+eshell/goto-end-of-prompt - :n "c" #'+eshell/evil-change - :n "C" #'+eshell/evil-change-line - :n "d" #'+eshell/evil-delete - :n "D" #'+eshell/evil-delete-line - :i "TAB" #'+eshell/pcomplete - :i [tab] #'+eshell/pcomplete - :i "C-d" #'+eshell/quit-or-delete-char - :i "C-p" #'eshell-previous-matching-input-from-input - :i "C-k" #'eshell-previous-matching-input-from-input - :i "C-j" #'eshell-next-matching-input-from-input - :i "C-n" #'eshell-next-matching-input-from-input - "C-s" #'+eshell/search-history - ;; Tmux-esque prefix keybinds - "C-c s" #'+eshell/split-below - "C-c v" #'+eshell/split-right - "C-c x" #'+eshell/kill-and-close - "C-c h" #'evil-window-left - "C-c j" #'evil-window-down - "C-c k" #'evil-window-up - "C-c l" #'evil-window-right - [remap split-window-below] #'+eshell/split-below - [remap split-window-right] #'+eshell/split-right - [remap doom/backward-to-bol-or-indent] #'eshell-bol - [remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input - [remap evil-window-split] #'+eshell/split-below - [remap evil-window-vsplit] #'+eshell/split-right)) - (add-hook 'eshell-first-time-mode-hook #'+eshell|init-keymap)) + (add-hook! 'eshell-first-time-mode-hook + (defun +eshell-init-keymap-h () + ;; Keys must be bound in a hook because eshell resets its keymap every + ;; time `eshell-mode' is enabled. Why? It is not for us mere mortals to + ;; grasp such wisdom. + (map! :map eshell-mode-map + :n "RET" #'+eshell/goto-end-of-prompt + :n [return] #'+eshell/goto-end-of-prompt + :n "c" #'+eshell/evil-change + :n "C" #'+eshell/evil-change-line + :n "d" #'+eshell/evil-delete + :n "D" #'+eshell/evil-delete-line + :i "TAB" #'+eshell/pcomplete + :i [tab] #'+eshell/pcomplete + :i "C-d" #'+eshell/quit-or-delete-char + "C-s" #'+eshell/search-history + ;; Tmux-esque prefix keybinds + "C-c s" #'+eshell/split-below + "C-c v" #'+eshell/split-right + "C-c x" #'+eshell/kill-and-close + "C-c h" #'windmove-left + "C-c j" #'windmove-down + "C-c k" #'windmove-up + "C-c l" #'windmove-right + [remap split-window-below] #'+eshell/split-below + [remap split-window-right] #'+eshell/split-right + [remap doom/backward-to-bol-or-indent] #'eshell-bol + [remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input + [remap evil-window-split] #'+eshell/split-below + [remap evil-window-vsplit] #'+eshell/split-right)))) -(def-package! eshell-up +(use-package! eshell-up :commands eshell-up eshell-up-peek) -(def-package! shrink-path +(use-package! shrink-path :commands shrink-path-file) -(def-package! eshell-z +(use-package! eshell-z :after eshell :config ;; Use zsh's db if it exists, otherwise, store it in `doom-cache-dir' diff --git a/modules/term/shell/autoload.el b/modules/term/shell/autoload.el index 180331a60..6549e8b08 100644 --- a/modules/term/shell/autoload.el +++ b/modules/term/shell/autoload.el @@ -49,12 +49,12 @@ prompt." If popup is visible but unselected, selected it. If popup is focused, kill it." (interactive) - (let* ((buffer-name - (get-buffer-create - (format "*doom:shell-popup:%s*" - (if (bound-and-true-p persp-mode) - (safe-persp-name (get-current-persp)) - "main"))))) + (let ((buffer + (get-buffer-create + (format "*doom:shell-popup:%s*" + (if (bound-and-true-p persp-mode) + (safe-persp-name (get-current-persp)) + "main"))))) (if-let (win (get-buffer-window buffer)) (if (eq (selected-window) win) (let (confirm-kill-processes) diff --git a/modules/term/shell/config.el b/modules/term/shell/config.el index c716701c4..0845ac471 100644 --- a/modules/term/shell/config.el +++ b/modules/term/shell/config.el @@ -1,5 +1,5 @@ ;;; term/shell/config.el -*- lexical-binding: t; -*- ;;;###package shell -(add-hook 'shell-mode-hook #'doom|mark-buffer-as-real) +(add-hook 'shell-mode-hook #'doom-mark-buffer-as-real-h) (add-hook 'shell-mode-hook #'hide-mode-line-mode) diff --git a/modules/term/term/autoload.el b/modules/term/term/autoload.el index 459f29210..b74728076 100644 --- a/modules/term/term/autoload.el +++ b/modules/term/term/autoload.el @@ -36,7 +36,7 @@ If prefix ARG, recreate the term buffer." (goto-char (point-max))) (setenv "PROOT" (or (doom-project-root) default-directory)) (with-current-buffer buffer - (doom|mark-buffer-as-real) + (doom-mark-buffer-as-real-h) (multi-term-internal)) (unless (window-live-p window) (when-let (window diff --git a/modules/term/term/config.el b/modules/term/term/config.el index 12adbd62a..69bb89583 100644 --- a/modules/term/term/config.el +++ b/modules/term/term/config.el @@ -5,5 +5,5 @@ multi-term-switch-after-close 'PREVIOUS) ;;;###package term -(add-hook 'term-mode-hook #'doom|mark-buffer-as-real) +(add-hook 'term-mode-hook #'doom-mark-buffer-as-real-h) (add-hook 'term-mode-hook #'hide-mode-line-mode) diff --git a/modules/term/vterm/autoload.el b/modules/term/vterm/autoload.el index 5305493b4..85f8b22fc 100644 --- a/modules/term/vterm/autoload.el +++ b/modules/term/vterm/autoload.el @@ -33,8 +33,9 @@ If prefix ARG is non-nil, recreate vterm buffer in the current project's root." (setenv "PROOT" (or (doom-project-root) default-directory)) (let ((buffer (get-buffer-create buffer-name))) (with-current-buffer buffer - (doom|mark-buffer-as-real) - (vterm-mode)) + (doom-mark-buffer-as-real-h) + (unless (eq major-mode 'vterm-mode) + (vterm-mode))) (pop-to-buffer buffer))))) ;;;###autoload @@ -45,8 +46,6 @@ If prefix ARG is non-nil, cd into `default-directory' instead of project root." (interactive "P") (unless (fboundp 'module-load) (user-error "Your build of Emacs lacks dynamic modules support and cannot load vterm")) - (when (eq major-mode 'vterm-mode) - (user-error "Already in a vterm buffer")) (require 'vterm) ;; This hack forces vterm to redraw, fixing strange artefacting in the tty. (save-window-excursion diff --git a/modules/term/vterm/config.el b/modules/term/vterm/config.el index 3d3f8dfa8..c5d155b6b 100644 --- a/modules/term/vterm/config.el +++ b/modules/term/vterm/config.el @@ -1,15 +1,17 @@ ;;; term/vterm/config.el -*- lexical-binding: t; -*- -(def-package! vterm +(use-package! vterm :when (fboundp 'module-load) :defer t :preface (setq vterm-install t) :config (set-popup-rule! "^vterm" :size 0.25 :vslot -4 :select t :quit nil :ttl 0) - (add-hook 'vterm-mode-hook #'doom|mark-buffer-as-real) + (add-hook 'vterm-mode-hook #'doom-mark-buffer-as-real-h) ;; Automatically kill buffer when vterm exits. - (add-to-list 'vterm-exit-functions (lambda (buffer) (if buffer (kill-buffer buffer)))) + (add-hook! 'vterm-exit-functions + (defun +vterm-kill-buffer-on-quit-fn (buffer) + (if buffer (kill-buffer buffer)))) ;; Modeline serves no purpose in vterm (add-hook 'vterm-mode-hook #'hide-mode-line-mode) ;; Don't prompt about processes when killing vterm diff --git a/modules/tools/ansible/config.el b/modules/tools/ansible/config.el index 97ab196c7..d3f9d2740 100644 --- a/modules/tools/ansible/config.el +++ b/modules/tools/ansible/config.el @@ -1,6 +1,6 @@ ;;; tools/ansible/config.el -*- lexical-binding: t; -*- -(def-package! ansible +(use-package! ansible :commands ansible-auto-decrypt-encrypt :init (put 'ansible-vault-password-file 'safe-local-variable #'stringp) @@ -16,10 +16,10 @@ (after! ansible-doc (set-evil-initial-state! '(ansible-doc-module-mode) 'emacs)) -(def-package! jinja2-mode +(use-package! jinja2-mode :mode "\\.j2$") (def-project-mode! +ansible-yaml-mode - :modes (yaml-mode) - :add-hooks (ansible ansible-auto-decrypt-encrypt ansible-doc-mode) + :modes '(yaml-mode) + :add-hooks '(ansible ansible-auto-decrypt-encrypt ansible-doc-mode) :files ("roles/")) diff --git a/modules/tools/ansible/packages.el b/modules/tools/ansible/packages.el index 0601eb054..1d18e47ce 100644 --- a/modules/tools/ansible/packages.el +++ b/modules/tools/ansible/packages.el @@ -2,7 +2,7 @@ ;;; tools/ansible/packages.el (package! yaml-mode) -(package! ansible) +(package! ansible :recipe (:nonrecursive t)) (package! ansible-doc) (package! jinja2-mode) diff --git a/modules/tools/debugger/config.el b/modules/tools/debugger/config.el index 4b78f8c1e..d406a01c5 100644 --- a/modules/tools/debugger/config.el +++ b/modules/tools/debugger/config.el @@ -20,7 +20,7 @@ ;; ;;; Packages -(def-package! dap-mode +(use-package! dap-mode :when (featurep! :tools lsp) :hook (dap-mode . dap-ui-mode) :after lsp-mode @@ -34,7 +34,8 @@ ((:lang . java) lsp-java dap-java) ((:lang . php) php-mode dap-php) ((:lang . python) python dap-python) - ((:lang . ruby) enh-ruby-mode dap-ruby))) + ((:lang . ruby) enh-ruby-mode dap-ruby) + ((:lang . rust) rust-mode dap-lldb))) (when (doom-module-p (caar module) (cdar module) '+lsp) (with-eval-after-load (nth 1 module) (mapc #'require (cddr module))))) @@ -48,10 +49,10 @@ (require 'dap-edge))))) -(def-package! realgud +(use-package! realgud :defer t :init - (def-package! realgud-trepan-ni + (use-package! realgud-trepan-ni :defer t :init (add-to-list '+debugger--realgud-alist '(realgud:trepan-ni :modes (javascript-mode js2-mode js3-mode) @@ -69,20 +70,21 @@ (set-popup-rule! "^\\*\\(?:trepanjs:\\(?:g\\|zsh\\|bash\\)db\\|pdb \\)" :size 20 :select nil :quit nil) - (defun +debugger*cleanup-when-realgud-terminates (&optional buf) + (defadvice! +debugger--cleanup-after-realgud-a (&optional buf) "Kill command buffer when debugging session ends (which closes its popup)." + :after #'realgud:terminate (when (stringp buf) (setq buf (get-buffer buf))) (when-let (cmdbuf (realgud-get-cmdbuf buf)) (let (kill-buffer-hook) (kill-buffer buf)))) - (advice-add #'realgud:terminate :after #'+debugger*cleanup-when-realgud-terminates) ;; Monkey-patch `realgud:run-process' to run in a popup. ;; TODO Find a more elegant solution ;; FIXME Causes realgud:cmd-* to focus popup on every invocation - (defun +debugger*realgud-run-process - (debugger-name script-filename cmd-args minibuffer-history-var &optional no-reset) + (defadvice! +debugger--realgud-open-in-other-window-a + (debugger-name script-filename cmd-args minibuffer-history-var &optional no-reset) + :override #'realgud:run-process (let* ((cmd-buf (apply #'realgud-exec-shell debugger-name script-filename (car cmd-args) no-reset (cdr cmd-args))) (process (get-buffer-process cmd-buf))) @@ -106,6 +108,4 @@ (t (if cmd-buf (switch-to-buffer cmd-buf)) (message "Error running command: %s" (mapconcat #'identity cmd-args " ")))) - cmd-buf)) - (advice-add #'realgud:run-process :override #'+debugger*realgud-run-process)) - + cmd-buf))) diff --git a/modules/tools/direnv/README.org b/modules/tools/direnv/README.org new file mode 100644 index 000000000..40ab0f851 --- /dev/null +++ b/modules/tools/direnv/README.org @@ -0,0 +1,93 @@ +#+TITLE: tools/direnv +#+DATE: July 10, 2019 +#+SINCE: v2.1.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] + - [[#macos][MacOS]] + - [[#arch-linux][Arch Linux]] + - [[#nixos][NixOS]] +- [[#troubleshooting][Troubleshooting]] + - [[#direnv--nix-is-slow][direnv + nix is slow]] + +* Description +This module integrates direnv into Emacs. + +#+begin_quote +direnv is an environment switcher for the shell. It knows how to hook into bash, +zsh, tcsh, fish shell and elvish to load or unload environment variables +depending on the current directory. This allows project-specific environment +variables without cluttering the ~/.profile file. + +Before each prompt, direnv checks for the existence of a ".envrc" file in the +current and parent directories. If the file exists (and is authorized), it is +loaded into a bash sub-shell and all exported variables are then captured by +direnv and then made available to the current shell. +#+end_quote + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/wbolster/emacs-direnv][direnv]] + +** Hacks ++ Normally, the direnv environment is updated on ~post-command-hook~. We've + changed it to update on ~doom-switch-buffer-hook~, ~doom-switch-window-hook~ + and ~doom-switch-frame-hook~ instead. ++ Special direnv keywords/commands are highlighted in direnv-envrc-mode. ++ A fix has been applied to ensure flycheck searches for executables from within + the direnv environment, if any. + +* Prerequisites +This module requires the ~direnv~ utility. + +** MacOS +#+BEGIN_SRC bash +brew install direnv +#+END_SRC + +** Arch Linux +#+BEGIN_SRC bash +sudo pacman -S direnv +#+END_SRC + +** NixOS +#+BEGIN_SRC nix +environment.systemPackages = [ pkgs.direnv ]; +#+END_SRC + +Or ~nix-env -i direnv~ + +* Troubleshooting +** direnv + nix is slow +Consider augmenting direnv with [[https://github.com/target/lorri][lorri]], which will cache nix builds and speed up +direnv tremendously. + +At the time of writing, the lorri package in nixpkgs simply emits an error +message, telling you to install it manually. You can get around this with: + +#+BEGIN_SRC nix +nixpkgs.overlays = [ + (self: super: { + lorri = + let src = (super.fetchFromGitHub { + owner = "target"; + repo = "lorri"; + rev = "e943fa403234f1a5e403b6fdc112e79abc1e29ba"; + sha256 = "1ar7clza117qdzldld9qzg4q0wr3g20zxrnd1s51cg6gxwlpg7fa"; + }); + in super.callPackage src { inherit src; }; + }) +]; + +environment.systemPackages = [ pkgs.lorri ]; +#+END_SRC + +Otherwise, follow [[https://github.com/target/lorri#installing-lorri][the instructions in lorri's README]] on how to install it +manually. diff --git a/modules/tools/direnv/config.el b/modules/tools/direnv/config.el index 3745c80e0..5f226d5b8 100644 --- a/modules/tools/direnv/config.el +++ b/modules/tools/direnv/config.el @@ -7,32 +7,41 @@ "use" "rvm" "use_nix" "use_guix") "TODO") -(def-package! direnv - :after-call (after-find-file dired-initial-position-hook) +(use-package! direnv + :after-call after-find-file dired-initial-position-hook :config - (defun +direnv|init () - "Instead of checking for direnv on `post-command-hook', check on + (add-hook! 'direnv-mode-hook + (defun +direnv-init-h () + "Instead of checking for direnv on `post-command-hook', check on buffer/window/frame switch, which is less expensive." - (direnv--disable) - (when direnv-mode - (add-hook 'doom-switch-buffer-hook #'direnv--maybe-update-environment) - (add-hook 'doom-switch-window-hook #'direnv--maybe-update-environment) - (add-hook 'doom-switch-frame-hook #'direnv--maybe-update-environment) - (add-hook 'focus-in-hook #'direnv--maybe-update-environment))) - (add-hook 'direnv-mode-hook #'+direnv|init) + (direnv--disable) + (when direnv-mode + (add-hook! '(doom-switch-buffer-hook + doom-switch-window-hook + doom-switch-frame-hook) + #'direnv--maybe-update-environment)))) - (defun +direnv|envrc-fontify-keywords () - (font-lock-add-keywords - nil `((,(regexp-opt +direnv--keywords 'symbols) - (0 font-lock-keyword-face))))) - (add-hook 'direnv-envrc-mode-hook #'+direnv|envrc-fontify-keywords) + ;; Fontify special .envrc keywords; it's a good indication of whether or not + ;; we've typed them correctly. + (add-hook! 'direnv-envrc-mode-hook + (defun +direnv-envrc-fontify-keywords-h () + (font-lock-add-keywords + nil `((,(regexp-opt +direnv--keywords 'symbols) + (0 font-lock-keyword-face)))))) - (defun +direnv*update (&rest _) + (defadvice! +direnv--update-a (&rest _) "Update direnv. Useful to advise functions that may run environment-sensitive logic like `flycheck-default-executable-find'. This fixes flycheck issues with direnv and on nix." - (direnv-update-environment default-directory)) - (advice-add #'flycheck-default-executable-find :before #'+direnv*update) + :before #'flycheck-default-executable-find + (direnv--maybe-update-environment)) - (when (executable-find "direnv") - (direnv-mode +1))) + (defadvice! +direnv--fail-gracefully-a (orig-fn) + "Don't try to update direnv if the executable isn't present." + :around #'direnv--maybe-update-environment + (if (executable-find "direnv") + (when (file-readable-p (or buffer-file-name default-directory)) + (funcall orig-fn)) + (doom-log "Couldn't find direnv executable"))) + + (direnv-mode +1)) diff --git a/modules/tools/docker/README.org b/modules/tools/docker/README.org index 98f541756..b3153322c 100644 --- a/modules/tools/docker/README.org +++ b/modules/tools/docker/README.org @@ -1,6 +1,6 @@ #+TITLE: tools/docker #+DATE: April 30, 2019 -#+SINCE: {replace with next tagged release version} +#+SINCE: v2.0.9 #+STARTUP: inlineimages * Table of Contents :TOC_3:noexport: @@ -8,25 +8,25 @@ - [[#module-flags][Module Flags]] - [[#plugins][Plugins]] - [[#prerequisites][Prerequisites]] -- [[#todo-features][TODO: Features]] +- [[#features][Features]] - [[#docker-control][Docker control]] - [[#supported-commands][Supported commands]] - [[#tramp][TRAMP]] - [[#configuration][Configuration]] - [[#popups][Popups]] - - [[#dockerfile-mode][dockerfile-mode]] + - [[#other-useful-variables][Other useful variables]] + - [[#completion-in-dockerfiles][Completion in Dockerfiles]] - [[#troubleshooting][Troubleshooting]] - [[#tramp-hangs-on-alpine-container][Tramp hangs on Alpine container]] * Description +This module allows you to manipulate Docker images, containers & more from +Emacs. -This package allows you to manipulate docker images, containers & more from Emacs. +Provides a major =dockerfile-mode= to edit =Dockerfiles=. Additional +convenience functions allow images to be built easily. -Provides a major mode `dockerfile-mode' for use with the standard -`Dockerfile' file format. Additional convenience functions allow -images to be built easily. - -docker-tramp.el offers a TRAMP method for Docker containers. +=docker-tramp.el= offers a [[https://www.gnu.org/software/tramp/][TRAMP]] method for Docker containers. ** Module Flags This module provides no flags. @@ -37,14 +37,14 @@ This module provides no flags. + [[https://github.com/spotify/dockerfile-mode][dockerfile-mode]] * Prerequisites -This modules assumes `docker`, `docker-compose` and `docker-machine` binaries are -installed and accessible from your PATH. +This module assumes =docker=, =docker-compose= and =docker-machine= binaries +are installed and accessible from your PATH. -* TODO: Features +* Features ** Docker control - -Use =M-x docker=, select a resource then then mark or unmark items using the following keybindings (for more -marking possibilities, check out https://github.com/politza/tablist): +Use =M-x docker=, select a resource, and then mark or unmark items using the +following keybindings (for more marking possibilities, check out +https://github.com/politza/tablist): | Binding | Description | |-----------+----------------------| @@ -61,41 +61,40 @@ marking possibilities, check out https://github.com/politza/tablist): | =C-c C-e= | Export to csv | *** Supported commands -- docker container: attach, cp, diff, inspect, kill, logs, pause, rename, restart, rm, start, stop, unpause -- docker image: inspect, pull, push, rm, run, tag -- docker network: rm -- docker volume: rm -- docker-machine: create, env, restart, rm, start, stop -- docker-compose: build, config, create, down, exec, logs, pull, push, remove, - restart, run, start, stop, up +- =docker container=: =attach=, =cp=, =diff=, =inspect=, =kill=, =logs=, + =pause=, =rename=, =restart=, =rm=, =start=, =stop=, =unpause= +- =docker image=: =inspect=, =pull=, =push=, =rm=, =run=, =tag= +- =docker network=: =rm= +- =docker volume=: =rm= +- =docker-machine=: =create=, =env=, =restart=, =rm=, =start=, =stop= +- =docker-compose=: =build=, =config=, =create=, =down=, =exec=, =logs=, + =pull=, =push=, =remove=, =restart=, =run=, =start=, =stop=, =up= - You can also enter `dired` or open a file inside a container or volume. + You can also enter =dired= or open a file inside a container or volume. ** TRAMP +Offers the [[https://www.gnu.org/software/tramp/][TRAMP]] method =docker= to access running containers. -Offers the TRAMP method `docker` to access running containers - - C-x C-f /docker:user@container:/path/to/file - - where - user is the user that you want to use (optional) - container is the id or name of the container +#+BEGIN_EXAMPLE +C-x C-f /docker:user@container:/path/to/file +#+END_EXAMPLE +| =user= | the user that you want to use (optional) | +| =container= | the id or name of the container | * Configuration ** Popups - -Thanks to [magit-popup](https://github.com/magit/magit-popup), all the popups default arguments can be customized. For -example, here is how to customize the arguments for `docker-image-run-popup`: +Thanks to [[https://github.com/magit/magit-popup][magit-popup]], all the popups default arguments can be customized. For +example, here is how to customize the arguments for =docker-image-run-popup=: #+BEGIN_SRC emacs-lisp (setq docker-image-run-arguments '("-i" "-t" "--rm")) #+END_SRC -or inside a use-package declaration: +or inside a =use-package= declaration: #+BEGIN_SRC emacs-lisp -(use-package docker +(use-package! docker :ensure t :bind ("C-c d" . docker) :custom (docker-image-run-arguments '("-i" "-t" "--rm"))) @@ -103,39 +102,35 @@ or inside a use-package declaration: You can also customize these using =M-x customize-variable=. -### Others +*** Other useful variables +| Variable | Description | Default | +|-------------------------------------+---------------------------------------+------------------| +| =docker-command= | The docker binary to use | =docker= | +| =docker-container-default-sort-key= | Sort key for docker containers | =("Image")= | +| =docker-container-shell-file-name= | Shell to use when entering containers | =/bin/bash= | +| =docker-image-default-sort-key= | Sort key for docker images | =("Repository")= | +| =docker-machine-default-sort-key= | Sort key for docker machines | =("Name")= | +| =docker-network-default-sort-key= | Sort key for docker networks | =("Name")= | +| =docker-run-as-root= | Run docker as root | =nil= | +| =docker-volume-default-sort-key= | Sort key for docker volumes | =("Driver")= | -| Variable | Description | Default | -|-----------------------------------+---------------------------------------+------------------| -| docker-command | The docker binary to use | `docker` | -| docker-container-default-sort-key | Sort key for docker containers | `("Image")` | -| docker-container-shell-file-name | Shell to use when entering containers | `/bin/bash` | -| docker-image-default-sort-key | Sort key for docker images | `("Repository")` | -| docker-machine-default-sort-key | Sort key for docker machines | `("Name")` | -| docker-network-default-sort-key | Sort key for docker networks | `("Name")` | -| docker-run-as-root | Run docker as root | `nil` | -| docker-volume-default-sort-key | Sort key for docker volumes | `("Driver")` | +** Completion in Dockerfiles +By default, the keyword completion function detects the =docker-compose= +version of the current buffer and suggests the appropriate keywords. -** dockerfile-mode - -By default, the keyword completion function detects the docker-compose version of the current buffer and suggests the appropriate keywords. - -You can change the candidates offered by the backend by customizing docker-compose-keywords +You can change the candidates offered by the backend by customizing +=docker-compose-keywords=. * Troubleshooting - ** Tramp hangs on Alpine container +Busyboxes built with the =ENABLE_FEATURE_EDITING_ASK_TERMINAL= config option +also send escape sequences, which =tramp-wait-for-output= doesn't ignore +correctly. This was [[http://git.savannah.gnu.org/cgit/tramp.git/commit/?id=98a511248a9405848ed44de48a565b0b725af82c][fixed in TRAMP upstream]] and is available since TRAMP 2.3. -Busyboxes built with the `ENABLE_FEATURE_EDITING_ASK_TERMINAL' config option -send also escape sequences, which `tramp-wait-for-output' doesn't ignores -correctly. Tramp upstream fixed in [98a5112][] and is available since -Tramp>=2.3. +For older versions of TRAMP you can dump [[https://github.com/emacs-pe/docker-tramp.el/blob/master/docker-tramp-compat.el][docker-tramp-compat.el]] in your +=load-path= somewhere and add the following to your =init.el= to overwrite +=tramp-wait-for-output= with the patch applied: -For older versions of Tramp you can dump [docker-tramp-compat.el][] in your -`load-path' somewhere and add the following to your `init.el', which -overwrites `tramp-wait-for-output' with the patch applied: - - (require 'docker-tramp-compat) - -[98a5112]: http://git.savannah.gnu.org/cgit/tramp.git/commit/?id=98a511248a9405848ed44de48a565b0b725af82c -[docker-tramp-compat.el]: https://github.com/emacs-pe/docker-tramp.el/raw/master/docker-tramp-compat.el +#+BEGIN_SRC emacs-lisp +(require 'docker-tramp-compat) +#+END_SRC diff --git a/modules/tools/editorconfig/config.el b/modules/tools/editorconfig/config.el index c44095eab..7880c5efb 100644 --- a/modules/tools/editorconfig/config.el +++ b/modules/tools/editorconfig/config.el @@ -12,17 +12,18 @@ (perl-mode . "pl") (php-mode . "php")) "An alist mapping major modes to extensions. Used by -`doom*editorconfig-smart-detection' to give editorconfig filetype hints.") +`doom--editorconfig-smart-detection-a' to give editorconfig filetype hints.") ;; Handles whitespace (tabs/spaces) settings externally. This way projects can ;; specify their own formatting rules. -(def-package! editorconfig +(use-package! editorconfig :after-call (doom-switch-buffer-hook after-find-file) :config - (defun doom*editorconfig-smart-detection (orig-fn) + (defadvice! +editorconfig--smart-detection-a (orig-fn) "Retrieve the properties for the current file. If it doesn't have an extension, try to guess one." + :around #'editorconfig-call-editorconfig-exec (let ((buffer-file-name (if (and (not (bound-and-true-p org-src-mode)) (file-name-extension buffer-file-name)) @@ -32,22 +33,21 @@ extension, try to guess one." (concat "." ext) ""))))) (funcall orig-fn))) - (advice-add #'editorconfig-call-editorconfig-exec :around #'doom*editorconfig-smart-detection) - (defun +editorconfig|disable-ws-butler-maybe (props) - "Disable `ws-butler-mode' if trim_trailing_whitespace is true." - (when (and (equal (gethash 'trim_trailing_whitespace props) "true") - (bound-and-true-p ws-butler-mode)) - (ws-butler-mode -1))) - (add-hook 'editorconfig-after-apply-functions #'+editorconfig|disable-ws-butler-maybe) + (add-hook! 'editorconfig-after-apply-functions + (defun +editorconfig-disable-ws-butler-maybe-h (props) + "Disable `ws-butler-mode' if trim_trailing_whitespace is true." + (when (and (equal (gethash 'trim_trailing_whitespace props) "true") + (bound-and-true-p ws-butler-mode)) + (ws-butler-mode -1)))) - (defun +editorconfig|disable-indent-detection (props) - "Inhibit `dtrt-indent' if an explicit indent_style and indent_size is + (add-hook! 'editorconfig-after-apply-functions + (defun +editorconfig-disable-indent-detection-h (props) + "Inhibit `dtrt-indent' if an explicit indent_style and indent_size is specified by editorconfig." - (when (or (gethash 'indent_style props) - (gethash 'indent_size props)) - (setq doom-inhibit-indent-detection 'editorconfig))) - (add-hook 'editorconfig-after-apply-functions #'+editorconfig|disable-indent-detection) + (when (or (gethash 'indent_style props) + (gethash 'indent_size props)) + (setq doom-inhibit-indent-detection 'editorconfig)))) ;; Editorconfig makes indentation too rigid in Lisp modes, so tell ;; editorconfig to ignore indentation there. The dynamic indentation support diff --git a/modules/tools/ein/README.org b/modules/tools/ein/README.org index 57e54381c..5c3c86124 100644 --- a/modules/tools/ein/README.org +++ b/modules/tools/ein/README.org @@ -55,7 +55,7 @@ Change ~+ein-notebook-dir~ to tell ein where to find your Jupityr notebooks. #+END_SRC ** Using hydra -This module provides a batteries-included hydra - ~+ein-hydra~ - to make using ein +This module provides a batteries-included hydra - ~+ein/hydra~ - to make using ein easier. Things like navigating between cells, workbook management etc, are greatly simplified and are easily accessible. However, by default, it's not bound to any key. Here's an example of how to bind it: @@ -63,6 +63,6 @@ Here's an example of how to bind it: #+BEGIN_SRC emacs-lisp (map! :map ein:notebook-mode-map :localleader - "," #'+ein-hydra/body) + "," #'+ein/hydra/body) #+END_SRC diff --git a/modules/tools/ein/autoload/ein.el b/modules/tools/ein/autoload/ein.el new file mode 100644 index 000000000..0740f4652 --- /dev/null +++ b/modules/tools/ein/autoload/ein.el @@ -0,0 +1,24 @@ +;;; tools/ein/autoload.el -*- lexical-binding: t; -*- + +(defun +ein--collect-ein-buffer-links () + (let ((end (window-end)) + points) + (save-excursion + (goto-char (window-start)) + (while (re-search-forward "~?/.+\\|\s\\[" end t) + (push (+ (match-beginning 0) 1) points)) + (nreverse points)))) + +;;;###autoload +(defun +ein/ace-link-ein () + "Ace jump to links in ein notebooklist." + (interactive) + (require 'avy) + (let ((res (avy-with +ein/ace-link-ein + (avy--process + (+ein--collect-ein-buffer-links) + #'avy--overlay-pre)))) + ;(avy--style-fn avy-style))))) + (when (numberp res) + (goto-char (1+ res)) + (widget-button-press (point))))) diff --git a/modules/tools/ein/autoload.el b/modules/tools/ein/autoload/hydra.el similarity index 67% rename from modules/tools/ein/autoload.el rename to modules/tools/ein/autoload/hydra.el index 0dd10d81a..c06b65f2e 100644 --- a/modules/tools/ein/autoload.el +++ b/modules/tools/ein/autoload/hydra.el @@ -1,30 +1,8 @@ -;;; tools/ein/autoload.el -*- lexical-binding: t; -*- +;;; tools/ein/autoload/hydra.el -*- lexical-binding: t; -*- +;;;###if (featurep! :ui hydra) -(defun +ein--collect-ein-buffer-links () - (let ((end (window-end)) - points) - (save-excursion - (goto-char (window-start)) - (while (re-search-forward "~?/.+\\|\s\\[" end t) - (push (+ (match-beginning 0) 1) points)) - (nreverse points)))) - -;;;###autoload -(defun +ein/ace-link-ein () - "Ace jump to links in ein notebooklist." - (interactive) - (require 'avy) - (let ((res (avy-with +ein/ace-link-ein - (avy--process - (+ein--collect-ein-buffer-links) - #'avy--overlay-pre)))) - ;(avy--style-fn avy-style))))) - (when (numberp res) - (goto-char (1+ res)) - (widget-button-press (point))))) - -;;;###autoload (autoload '+ein-hydra/body "tools/ein/autoload" nil t) -(defhydra +ein-hydra (:hint t :color red) +;;;###autoload (autoload '+ein/hydra/body "tools/ein/autoload" nil t) +(defhydra +ein/hydra (:hint t :color red) " Operations on Cells^^^^^^ Other ----------------------------^^^^^^ ----------------------------------^^^^ diff --git a/modules/tools/eval/config.el b/modules/tools/eval/config.el index e74004554..23b28e80d 100644 --- a/modules/tools/eval/config.el +++ b/modules/tools/eval/config.el @@ -13,18 +13,18 @@ (set-popup-rule! "^\\*quickrun" :size 0.3 :ttl 0) - (defun +eval*quickrun-auto-close (&rest _) + (defadvice! +eval--quickrun-auto-close-a (&rest _) "Allows us to silently re-run quickrun from within the quickrun buffer." + :before '(quickrun quickrun-region) (when-let (win (get-buffer-window quickrun--buffer-name)) (let ((inhibit-message t)) (quickrun--kill-running-process) (message "")) (delete-window win))) - (advice-add #'quickrun :before #'+eval*quickrun-auto-close) - (advice-add #'quickrun-region :before #'+eval*quickrun-auto-close) - (defun +eval*quickrun--outputter-replace-region () + (defadvice! +eval--quickrun-fix-evil-visual-region-a () "Make `quickrun-replace-region' recognize evil visual selections." + :override #'quickrun--outputter-replace-region (let ((output (buffer-substring-no-properties (point-min) (point-max)))) (with-current-buffer quickrun--original-buffer (cl-destructuring-bind (beg . end) @@ -37,19 +37,18 @@ (delete-region beg end) (insert output)) (setq quickrun-option-outputter quickrun--original-outputter)))) - (advice-add #'quickrun--outputter-replace-region :override #'+eval*quickrun--outputter-replace-region) - (defun +eval|quickrun-shrink-window () - "Shrink the quickrun output window once code evaluation is complete." - (when-let (win (get-buffer-window quickrun--buffer-name)) - (with-selected-window (get-buffer-window quickrun--buffer-name) - (let ((ignore-window-parameters t)) - (shrink-window-if-larger-than-buffer))))) - (add-hook 'quickrun-after-run-hook #'+eval|quickrun-shrink-window) + (add-hook! 'quickrun-after-run-hook + (defun +eval-quickrun-shrink-window-h () + "Shrink the quickrun output window once code evaluation is complete." + (when-let (win (get-buffer-window quickrun--buffer-name)) + (with-selected-window (get-buffer-window quickrun--buffer-name) + (let ((ignore-window-parameters t)) + (shrink-window-if-larger-than-buffer)))))) - (defun +eval|quickrun-scroll-to-bof () - "Ensures window is scrolled to BOF on invocation." - (when-let (win (get-buffer-window quickrun--buffer-name)) - (with-selected-window win - (goto-char (point-min))))) - (add-hook 'quickrun-after-run-hook #'+eval|quickrun-scroll-to-bof)) + (add-hook! 'quickrun-after-run-hook + (defun +eval-quickrun-scroll-to-bof-h () + "Ensures window is scrolled to BOF on invocation." + (when-let (win (get-buffer-window quickrun--buffer-name)) + (with-selected-window win + (goto-char (point-min))))))) diff --git a/modules/tools/flycheck/autoload.el b/modules/tools/flycheck/autoload.el index cc8167c4e..4f3ea2929 100644 --- a/modules/tools/flycheck/autoload.el +++ b/modules/tools/flycheck/autoload.el @@ -1,7 +1,7 @@ ;;; tools/flycheck/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(defun +flycheck|init-popups () +(defun +flycheck-init-popups-h () "Activate `flycheck-posframe-mode' if available and in GUI Emacs. Activate `flycheck-popup-tip-mode' otherwise. Do nothing if `lsp-ui-mode' is active and `lsp-ui-sideline-enable' is non-nil." diff --git a/modules/tools/flycheck/config.el b/modules/tools/flycheck/config.el index 6fee6ed63..b448818a3 100644 --- a/modules/tools/flycheck/config.el +++ b/modules/tools/flycheck/config.el @@ -8,35 +8,34 @@ errors.") ;; ;;; Packages -(def-package! flycheck - :commands (flycheck-list-errors flycheck-buffer) - :after-call (doom-switch-buffer-hook after-find-file) +(use-package! flycheck + :commands flycheck-list-errors flycheck-buffer + :after-call doom-switch-buffer-hook after-find-file :config ;; new-line checks are a mote excessive; idle checks are more than enough - (setq flycheck-check-syntax-automatically - (delq 'new-line flycheck-check-syntax-automatically)) + (delq! 'new-line flycheck-check-syntax-automatically) - (defun +flycheck|buffer () - "Flycheck buffer on ESC in normal mode." - (when flycheck-mode - (ignore-errors (flycheck-buffer)) - nil)) - (add-hook 'doom-escape-hook #'+flycheck|buffer 'append) + (add-hook! 'doom-escape-hook :append + (defun +flycheck-buffer-h () + "Flycheck buffer on ESC in normal mode." + (when flycheck-mode + (ignore-errors (flycheck-buffer)) + nil))) - (defun +flycheck|adjust-syntax-check-eagerness () - "Check for errors less often when there aren't any. + (add-hook! 'flycheck-after-syntax-check-hook + (defun +flycheck-adjust-syntax-check-eagerness-h () + "Check for errors less often when there aren't any. Done to reduce the load flycheck imposes on the current buffer." - (if flycheck-current-errors - (kill-local-variable 'flycheck-idle-change-delay) - (setq-local flycheck-idle-change-delay +flycheck-lazy-idle-delay))) - (add-hook 'flycheck-after-syntax-check-hook #'+flycheck|adjust-syntax-check-eagerness) + (if flycheck-current-errors + (kill-local-variable 'flycheck-idle-change-delay) + (setq-local flycheck-idle-change-delay +flycheck-lazy-idle-delay)))) (global-flycheck-mode +1)) -(def-package! flycheck-popup-tip - :commands (flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup) - :init (add-hook 'flycheck-mode-hook #'+flycheck|init-popups) +(use-package! flycheck-popup-tip + :commands flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup + :init (add-hook 'flycheck-mode-hook #'+flycheck-init-popups-h) :config (setq flycheck-popup-tip-error-prefix "✕ ") (after! evil @@ -45,10 +44,11 @@ Done to reduce the load flycheck imposes on the current buffer." (add-hook 'flycheck-posframe-inhibit-functions #'evil-insert-state-p))) -(def-package! flycheck-posframe - :when (and EMACS26+ (featurep! +childframe)) +(use-package! flycheck-posframe + :when EMACS26+ + :when (featurep! +childframe) :defer t - :init (add-hook 'flycheck-mode-hook #'+flycheck|init-popups) + :init (add-hook 'flycheck-mode-hook #'+flycheck-init-popups-h) :config (setq flycheck-posframe-warning-prefix "⚠ " flycheck-posframe-info-prefix "··· " diff --git a/modules/tools/flyspell/autoload.el b/modules/tools/flyspell/autoload.el index 54da4e769..e33ab0df4 100644 --- a/modules/tools/flyspell/autoload.el +++ b/modules/tools/flyspell/autoload.el @@ -14,7 +14,7 @@ (add-to-list '+flyspell--predicate-alist (cons mode predicate)))) ;;;###autoload -(defun +flyspell|init-predicate () +(defun +flyspell-init-predicate-h () "TODO" (when-let (pred (assq major-mode +flyspell--predicate-alist)) (setq-local flyspell-generic-check-word-predicate (cdr pred)))) diff --git a/modules/tools/flyspell/config.el b/modules/tools/flyspell/config.el index dc7afb19a..3f1601fd1 100644 --- a/modules/tools/flyspell/config.el +++ b/modules/tools/flyspell/config.el @@ -14,6 +14,12 @@ Since spellchecking can be slow in some buffers, this can be disabled with: (after! ispell (add-to-list 'ispell-extra-args "--dont-tex-check-comments") + ;; Don't spellcheck org blocks + (pushnew! ispell-skip-region-alist + '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:") + '("#\\+BEGIN_SRC" . "#\\+END_SRC") + '("#\\+BEGIN_EXAMPLE" . "#\\+END_EXAMPLE")) + ;; Enable either aspell or hunspell. ;; If no module flags are given, enable either aspell or hunspell if their ;; binary is found. @@ -27,17 +33,16 @@ Since spellchecking can be slow in some buffers, this can be disabled with: (setq ispell-program-name "aspell" ispell-extra-args '("--sug-mode=ultra" "--run-together")) - (defun +flyspell|remove-run-together-switch-for-aspell () - (setq-local ispell-extra-args (remove "--run-together" ispell-extra-args))) - (add-hook 'text-mode-hook #'+flyspell|remove-run-together-switch-for-aspell) + (add-hook! 'text-mode-hook + (defun +flyspell-remove-run-together-switch-for-aspell-h () + (setq-local ispell-extra-args (remove "--run-together" ispell-extra-args)))) - (defun +flyspell*setup-ispell-extra-args (orig-fun &rest args) + (defun +flyspell-setup-ispell-extra-args-a (orig-fun &rest args) + :around '(ispell-word flyspell-auto-correct-word) (let ((ispell-extra-args (remove "--run-together" ispell-extra-args))) (ispell-kill-ispell t) (apply orig-fun args) - (ispell-kill-ispell t))) - (advice-add #'ispell-word :around #'+flyspell*setup-ispell-extra-args) - (advice-add #'flyspell-auto-correct-word :around #'+flyspell*setup-ispell-extra-args)) + (ispell-kill-ispell t)))) (`hunspell (setq ispell-program-name "hunspell")) @@ -52,29 +57,28 @@ Since spellchecking can be slow in some buffers, this can be disabled with: (when (featurep! +prog) (add-hook 'prog-mode-hook #'flyspell-prog-mode)) - (defun +flyspell|inhibit-duplicate-detection-maybe () - "Don't mark duplicates when style/grammar linters are present. + (add-hook! 'flyspell-mode-hook + (defun +flyspell-inhibit-duplicate-detection-maybe-h () + "Don't mark duplicates when style/grammar linters are present. e.g. proselint and langtool." - (when (or (and (bound-and-true-p flycheck-mode) - (executable-find "proselint")) - (featurep 'langtool)) - (setq-local flyspell-mark-duplications-flag nil))) - (add-hook 'flyspell-mode-hook #'+flyspell|inhibit-duplicate-detection-maybe) + (when (or (and (bound-and-true-p flycheck-mode) + (executable-find "proselint")) + (featurep 'langtool)) + (setq-local flyspell-mark-duplications-flag nil)))) - (defun +flyspell|immediately () - "Spellcheck the buffer when `flyspell-mode' is enabled." - (when (and flyspell-mode +flyspell-immediately) - (flyspell-buffer))) - (add-hook 'flyspell-mode-hook #'+flyspell|immediately) + (add-hook! 'flyspell-mode-hook + (defun +flyspell-immediately-h () + "Spellcheck the buffer when `flyspell-mode' is enabled." + (when (and flyspell-mode +flyspell-immediately) + (flyspell-buffer)))) ;; Ensure mode-local predicates declared with `set-flyspell-predicate!' are ;; used in their respective major modes. - (add-hook 'flyspell-mode-hook #'+flyspell|init-predicate)) + (add-hook 'flyspell-mode-hook #'+flyspell-init-predicate-h)) -(def-package! flyspell-correct - :commands (flyspell-correct-word-generic - flyspell-correct-previous-word-generic) +(use-package! flyspell-correct + :commands flyspell-correct-word-generic flyspell-correct-previous-word-generic :config (cond ((and (featurep! :completion helm) (require 'flyspell-correct-helm nil t))) diff --git a/modules/tools/gist/config.el b/modules/tools/gist/config.el index 4e8f84b52..9a3f608d9 100644 --- a/modules/tools/gist/config.el +++ b/modules/tools/gist/config.el @@ -9,8 +9,8 @@ (set-popup-rule! "^\\*gist-" :ignore t) - (defun +gist*list-render (orig-fn &rest args) + (defadvice! +gist--open-in-popup-a (orig-fn &rest args) + :around #'gist-list-render (funcall orig-fn (car args) t) (unless (cadr args) - (pop-to-buffer (current-buffer)))) - (advice-add #'gist-list-render :around #'+gist*list-render)) + (pop-to-buffer (current-buffer))))) diff --git a/modules/tools/lookup/autoload/docsets.el b/modules/tools/lookup/autoload/docsets.el index 7ce92b989..a3e20021f 100644 --- a/modules/tools/lookup/autoload/docsets.el +++ b/modules/tools/lookup/autoload/docsets.el @@ -51,7 +51,7 @@ Used by `+lookup/in-docsets' and `+lookup/documentation'." (add-hook hook fn 'append)))))) ;;;###autoload -(defun +lookup-dash-docsets-backend (identifier) +(defun +lookup-dash-docsets-backend-fn (identifier) "Looks up IDENTIFIER in available Dash docsets, if any are installed. This backend is meant for `+lookup-documentation-functions'. diff --git a/modules/tools/lookup/autoload/lookup.el b/modules/tools/lookup/autoload/lookup.el index 57359f1c1..f9a72e1fe 100644 --- a/modules/tools/lookup/autoload/lookup.el +++ b/modules/tools/lookup/autoload/lookup.el @@ -60,7 +60,7 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. (declare (indent defun)) (dolist (mode (doom-enlist modes)) (let ((hook (intern (format "%s-hook" mode))) - (fn (intern (format "+lookup|init-%s-handlers" mode)))) + (fn (intern (format "+lookup--init-%s-handlers-h" mode)))) (cond ((null (car plist)) (remove-hook hook fn) (unintern fn nil)) @@ -183,15 +183,15 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. 'deferred t)))) -(defun +lookup-xref-definitions-backend (identifier) +(defun +lookup-xref-definitions-backend-fn (identifier) "Non-interactive wrapper for `xref-find-definitions'" (+lookup--xref-show 'xref-backend-definitions identifier)) -(defun +lookup-xref-references-backend (identifier) +(defun +lookup-xref-references-backend-fn (identifier) "Non-interactive wrapper for `xref-find-references'" (+lookup--xref-show 'xref-backend-references identifier)) -(defun +lookup-dumb-jump-backend (_identifier) +(defun +lookup-dumb-jump-backend-fn (_identifier) "Look up the symbol at point (or selection) with `dumb-jump', which conducts a project search with ag, rg, pt, or git-grep, combined with extra heuristics to reduce false positives. @@ -200,7 +200,7 @@ This backend prefers \"just working\" over accuracy." (and (require 'dumb-jump nil t) (dumb-jump-go))) -(defun +lookup-project-search-backend (identifier) +(defun +lookup-project-search-backend-fn (identifier) "Conducts a simple project text search for IDENTIFIER. Uses and requires `+ivy-file-search' or `+helm-file-search'. Will return nil if @@ -217,7 +217,7 @@ falling back to git-grep)." (+helm-file-search nil :query query) t)))))) -(defun +lookup-evil-goto-definition-backend (_identifier) +(defun +lookup-evil-goto-definition-backend-fn (_identifier) "Uses `evil-goto-definition' to conduct a text search for IDENTIFIER in the current buffer." (and (fboundp 'evil-goto-definition) @@ -297,22 +297,24 @@ Otherwise, falls back on `find-file-at-point'." (find-file-at-point path)) ((not (+lookup--jump-to :file path)) - (let ((fullpath (expand-file-name path))) + (let ((fullpath (doom-path path))) (when (and buffer-file-name (file-equal-p fullpath buffer-file-name)) (user-error "Already here")) (let* ((insert-default-directory t) (project-root (doom-project-root)) (ffap-file-finder - (cond ((not (file-directory-p fullpath)) + (cond ((not (doom-glob fullpath)) #'find-file) ((ignore-errors (file-in-directory-p fullpath project-root)) (lambda (dir) - (let ((default-directory dir)) - (without-project-cache! - (let ((file (projectile-completing-read "Find file: " - (projectile-current-project-files) - :initial-input path))) - (find-file (expand-file-name file (doom-project-root))) - (run-hooks 'projectile-find-file-hook)))))) + (let* ((default-directory dir) + projectile-project-name + projectile-project-root + (projectile-project-root-cache (make-hash-table :test 'equal)) + (file (projectile-completing-read "Find file: " + (projectile-current-project-files) + :initial-input path))) + (find-file (expand-file-name file (doom-project-root))) + (run-hooks 'projectile-find-file-hook)))) (#'doom-project-browse)))) (find-file-at-point path)))))) diff --git a/modules/tools/lookup/autoload/online.el b/modules/tools/lookup/autoload/online.el index 79908ec19..89f49e1e4 100644 --- a/modules/tools/lookup/autoload/online.el +++ b/modules/tools/lookup/autoload/online.el @@ -15,7 +15,7 @@ provider)))) ;;;###autoload -(defun +lookup-online-backend (identifier) +(defun +lookup-online-backend-fn (identifier) "Opens the browser and searches for IDENTIFIER online. Will prompt for which search engine to use the first time (or if the universal diff --git a/modules/tools/lookup/config.el b/modules/tools/lookup/config.el index 8a44fde04..d519f2b23 100644 --- a/modules/tools/lookup/config.el +++ b/modules/tools/lookup/config.el @@ -33,10 +33,10 @@ produces an url. Used by `+lookup/online'.") "Function to use to open search urls.") (defvar +lookup-definition-functions - '(+lookup-xref-definitions-backend - +lookup-dumb-jump-backend - +lookup-project-search-backend - +lookup-evil-goto-definition-backend) + '(+lookup-xref-definitions-backend-fn + +lookup-dumb-jump-backend-fn + +lookup-project-search-backend-fn + +lookup-evil-goto-definition-backend-fn) "Functions for `+lookup/definition' to try, before resorting to `dumb-jump'. Stops at the first function to return non-nil or change the current window/point. @@ -47,8 +47,8 @@ argument: the identifier at point. See `set-lookup-handlers!' about adding to this list.") (defvar +lookup-references-functions - '(+lookup-xref-references-backend - +lookup-project-search-backend) + '(+lookup-xref-references-backend-fn + +lookup-project-search-backend-fn) "Functions for `+lookup/references' to try, before resorting to `dumb-jump'. Stops at the first function to return non-nil or change the current window/point. @@ -59,7 +59,7 @@ argument: the identifier at point. See `set-lookup-handlers!' about adding to this list.") (defvar +lookup-documentation-functions - '(+lookup-online-backend) + '(+lookup-online-backend-fn) "Functions for `+lookup/documentation' to try, before resorting to `dumb-jump'. Stops at the first function to return non-nil or change the current window/point. @@ -83,7 +83,7 @@ this list.") ;; ;;; dumb-jump -(def-package! dumb-jump +(use-package! dumb-jump :commands dumb-jump-result-follow :config (setq dumb-jump-default-project doom-emacs-dir @@ -108,21 +108,21 @@ this list.") ;; xref to be one too. (remove-hook 'xref-backend-functions #'etags--xref-backend) ;; ...however, it breaks `projectile-find-tag', unless we put it back. - (defun +lookup*projectile-find-tag (orig-fn) + (defadvice! +lookup--projectile-find-tag-a (orig-fn) + :around #'projectile-find-tag (let ((xref-backend-functions '(etags--xref-backend t))) (funcall orig-fn))) - (advice-add #'projectile-find-tag :around #'+lookup*projectile-find-tag) ;; Use `better-jumper' instead of xref's marker stack - (advice-add #'xref-push-marker-stack :around #'doom*set-jump) + (advice-add #'xref-push-marker-stack :around #'doom-set-jump-a) - (def-package! ivy-xref + (use-package! ivy-xref :when (featurep! :completion ivy) :config (setq xref-show-xrefs-function #'ivy-xref-show-xrefs) (set-popup-rule! "^\\*xref\\*$" :ignore t)) - (def-package! helm-xref + (use-package! helm-xref :when (featurep! :completion helm) :config (setq xref-show-xrefs-function #'helm-xref-show-xrefs))) @@ -130,10 +130,10 @@ this list.") ;; ;;; Dash docset integration -(def-package! dash-docs +(use-package! dash-docs :when (featurep! +docsets) :init - (add-hook '+lookup-documentation-functions #'+lookup-dash-docsets-backend) + (add-hook '+lookup-documentation-functions #'+lookup-dash-docsets-backend-fn) :config (setq dash-docs-enable-debugging doom-debug-mode dash-docs-docsets-path (concat doom-etc-dir "docsets/") @@ -143,19 +143,19 @@ this list.") ;; Before `gnutls' is loaded, `gnutls-algorithm-priority' is treated as a ;; lexical variable, which breaks `+lookup*fix-gnutls-error' (defvar gnutls-algorithm-priority) - (defun +lookup*fix-gnutls-error (orig-fn url) + (defadvice! +lookup--fix-gnutls-error-a (orig-fn url) "Fixes integer-or-marker-p errors emitted from Emacs' url library, particularly, the `url-retrieve-synchronously' call in `dash-docs-read-json-from-url'. This is part of a systemic issue with Emacs 26's networking library (fixed in Emacs 27+, apparently). See https://github.com/magit/ghub/issues/81" + :around #'dash-docs-read-json-from-url (let ((gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")) (funcall orig-fn url))) - (advice-add #'dash-docs-read-json-from-url :around #'+lookup*fix-gnutls-error) - (def-package! helm-dash + (use-package! helm-dash :when (featurep! :completion helm)) - (def-package! counsel-dash + (use-package! counsel-dash :when (featurep! :completion ivy))) diff --git a/modules/tools/lsp/README.org b/modules/tools/lsp/README.org index b7a30aac3..628a20006 100644 --- a/modules/tools/lsp/README.org +++ b/modules/tools/lsp/README.org @@ -32,6 +32,7 @@ As of this writing, this is the state of LSP support in Doom Emacs: | Module | Major modes | Default language server | |------------------+---------------------------------------------------------+---------------------------------------------------------------| | [[../../lang/cc/README.org][:lang cc]] | c-mode, c++-mode, objc-mode | ccls | +| [[../../lang/elixir/README.org][:lang elixir]] | elixir-mode | elixir-ls | | [[../../lang/go/README.org][:lang go]] | go-mode | go-langserver | | [[../../lang/haskell/README.org][:lang haskell]] | haskell-mode | haskell-ide-engine | | [[../../lang/javascript/README.org][:lang javascript]] | js2-mode, rjsx-mode, typescript-mode | typescript-language-server | diff --git a/modules/tools/lsp/autoload.el b/modules/tools/lsp/autoload.el index 27a693d4c..7b6baffd6 100644 --- a/modules/tools/lsp/autoload.el +++ b/modules/tools/lsp/autoload.el @@ -8,19 +8,23 @@ Meant to be a lighter alternative to `lsp', which is too eager about initializing lsp-ui-mode, company, yasnippet and flycheck. Instead, these have been moved out to their respective modules, or these hooks: -+ `+lsp|init-company' (on `lsp-mode-hook') -+ `+lsp|init-ui-flycheck-or-flymake' (on `lsp-ui-mode-hook')" ++ `+lsp-init-company-h' (on `lsp-mode-hook') ++ `+lsp-init-ui-flycheck-or-flymake-h' (on `lsp-ui-mode-hook')" (require 'lsp-mode) (unless lsp-mode (when lsp-auto-configure (require 'lsp-clients)) (when (and (buffer-file-name) - (setq-local lsp--buffer-workspaces - (or (lsp--try-open-in-library-workspace) - (lsp--try-project-root-workspaces (equal arg '(4)) - (and arg (not (equal arg 1))))))) + (setq-local + lsp--buffer-workspaces + (or (lsp--try-open-in-library-workspace) + (lsp--try-project-root-workspaces + (equal arg '(4)) + (and arg (not (equal arg 1))))))) (lsp-mode 1) (lsp--info "Connected to %s." - (apply #'concat (mapcar (lambda (it) (format "[%s]" (lsp--workspace-print it))) - lsp--buffer-workspaces)))))) + (apply + #'concat (mapcar + (lambda (it) (format "[%s]" (lsp--workspace-print it))) + lsp--buffer-workspaces)))))) diff --git a/modules/tools/lsp/config.el b/modules/tools/lsp/config.el index 2313f2b08..371633abb 100644 --- a/modules/tools/lsp/config.el +++ b/modules/tools/lsp/config.el @@ -1,9 +1,18 @@ ;;; tools/lsp/config.el -*- lexical-binding: t; -*- +(defvar +lsp-company-backend 'company-lsp + "What backend to prepend to `company-backends' when `lsp-mode' is active. + +This can be a single company backend or a list thereof. It can be anything +`company-backends' will accept.") + + (setq lsp-session-file (concat doom-etc-dir "lsp-session") lsp-auto-guess-root t lsp-keep-workspace-alive nil - lsp-groovy-server-install-dir (concat doom-etc-dir "groovy-langserver/")) + lsp-groovy-server-install-dir (concat doom-etc-dir "lsp-groovy/") + lsp-intelephense-storage-path (concat doom-cache-dir "lsp-intelephense/")) + (after! lsp-mode (set-lookup-handlers! 'lsp-mode :async t @@ -20,43 +29,52 @@ (add-hook! 'kill-emacs-hook (setq lsp-restart 'ignore))) -(def-package! lsp-ui +(use-package! lsp-ui :hook (lsp-mode . lsp-ui-mode) :init - (defun +lsp|init-ui-flycheck-or-flymake () - "Sets up flymake-mode or flycheck-mode, depending on `lsp-prefer-flymake'." - (unless (eq :none lsp-prefer-flymake) - (if (and (not (version< emacs-version "26.1")) - lsp-prefer-flymake) - (lsp--flymake-setup)) - (require 'lsp-ui-flycheck) - (lsp-ui-flycheck-enable t))) - (add-hook 'lsp-ui-mode-hook #'+lsp|init-ui-flycheck-or-flymake) + (add-hook! 'lsp-ui-mode-hook + (defun +lsp-init-ui-flycheck-or-flymake-h () + "Sets up flymake-mode or flycheck-mode, depending on `lsp-prefer-flymake'." + (unless (eq :none lsp-prefer-flymake) + (if (and (not (version< emacs-version "26.1")) + lsp-prefer-flymake) + (lsp--flymake-setup)) + (require 'lsp-ui-flycheck) + (lsp-ui-flycheck-enable t)))) :config (setq lsp-prefer-flymake nil lsp-ui-doc-max-height 8 lsp-ui-doc-max-width 35 lsp-ui-sideline-ignore-duplicate t - ;; lsp-ui-doc is redundant with and less invasive than + ;; lsp-ui-doc is redundant with and more invasive than ;; `+lookup/documentation' - lsp-ui-doc-enable nil) + lsp-ui-doc-enable nil + ;; Don't show symbol definitions in the sideline. They are pretty noisy, + ;; and there is a bug preventing Flycheck errors from being shown (the + ;; errors flash briefly and then disappear). + lsp-ui-sideline-show-hover nil + ;; `lsp-ui' offers its own eldoc integration, which is redundant with + ;; lsp-mode's. + lsp-eldoc-enable-hover nil) (set-lookup-handlers! 'lsp-ui-mode :async t :definition 'lsp-ui-peek-find-definitions :references 'lsp-ui-peek-find-references)) -(def-package! company-lsp +(use-package! company-lsp :when (featurep! :completion company) :defer t :init ;; Make sure that `company-capf' is disabled since it is incompatible with ;; `company-lsp' (see lsp-mode#884) - (defun +lsp|init-company () - (if (not (bound-and-true-p company-mode)) - (add-hook 'company-mode-hook #'+lsp|init-company t t) - (setq-local company-backends - (cons 'company-lsp + (add-hook! 'lsp-mode-hook + (defun +lsp-init-company-h () + (if (not (bound-and-true-p company-mode)) + (add-hook 'company-mode-hook #'+lsp-init-company-h t t) + (setq-local company-backends + (cons +lsp-company-backend (remq 'company-capf company-backends))) - (remove-hook 'company-mode-hook #'+lsp|init-company t))) - (add-hook 'lsp-mode-hook #'+lsp|init-company)) + (remove-hook 'company-mode-hook #'+lsp-init-company-h t)))) + :config + (setq company-lsp-cache-candidates 'auto)) ;; cache candidates for better performance diff --git a/modules/tools/macos/autoload.el b/modules/tools/macos/autoload.el index 05b415fa3..9ffbd5b57 100644 --- a/modules/tools/macos/autoload.el +++ b/modules/tools/macos/autoload.el @@ -19,30 +19,30 @@ (shell-command command))) ;;;###autoload -(defmacro +macos!open-with (id &optional app dir) +(defmacro +macos--open-with (id &optional app dir) `(defun ,(intern (format "+macos/%s" id)) () (interactive) (+macos-open-with ,app ,dir))) ;;;###autoload (autoload '+macos/open-in-default-program "tools/macos/autoload" nil t) -(+macos!open-with open-in-default-program) +(+macos--open-with open-in-default-program) ;;;###autoload (autoload '+macos/reveal-in-finder "tools/macos/autoload" nil t) -(+macos!open-with reveal-in-finder "Finder" default-directory) +(+macos--open-with reveal-in-finder "Finder" default-directory) ;;;###autoload (autoload '+macos/reveal-project-in-finder "tools/macos/autoload" nil t) -(+macos!open-with reveal-project-in-finder "Finder" - (or (doom-project-root) default-directory)) +(+macos--open-with reveal-project-in-finder "Finder" + (or (doom-project-root) default-directory)) ;;;###autoload (autoload '+macos/send-to-transmit "tools/macos/autoload" nil t) -(+macos!open-with send-to-transmit "Transmit") +(+macos--open-with send-to-transmit "Transmit") ;;;###autoload (autoload '+macos/send-cwd-to-transmit "tools/macos/autoload" nil t) -(+macos!open-with send-cwd-to-transmit "Transmit" default-directory) +(+macos--open-with send-cwd-to-transmit "Transmit" default-directory) ;;;###autoload (autoload '+macos/send-to-launchbar "tools/macos/autoload" nil t) -(+macos!open-with send-to-launchbar "LaunchBar") +(+macos--open-with send-to-launchbar "LaunchBar") ;;;###autoload (autoload '+macos/send-project-to-launchbar "tools/macos/autoload" nil t) -(+macos!open-with send-project-to-launchbar "LaunchBar" - (or (doom-project-root) default-directory)) +(+macos--open-with send-project-to-launchbar "LaunchBar" + (or (doom-project-root) default-directory)) diff --git a/modules/tools/magit/autoload.el b/modules/tools/magit/autoload.el index 734d9a5ea..bd167c8a5 100644 --- a/modules/tools/magit/autoload.el +++ b/modules/tools/magit/autoload.el @@ -1,38 +1,42 @@ ;;; tools/magit/autoload.el -*- lexical-binding: t; -*- -;;;###autoload -(defun +magit-display-buffer (buffer) - "Marries `magit-display-buffer-fullcolumn-most-v1' with -`magit-display-buffer-same-window-except-diff-v1', except: +;; HACK Magit complains loudly when it can't determine its own version, which is +;; the case when magit is built through straight. The warning is harmless, +;; however, so we just need it to shut up. +;;;###autoload (advice-add #'magit-version :around #'ignore) -1. Magit sub-buffers that aren't spawned from a status screen are opened as - popups. -2. The status screen isn't buried when viewing diffs or logs from the status - screen." +;;;###autoload +(defun +magit-display-buffer-fn (buffer) + "Same as `magit-display-buffer-traditional', except... + +- If opened from a commit window, it will open below it. +- Magit process windows are always opened in small windows below the current. +- Everything else will reuse the same window." (let ((buffer-mode (buffer-local-value 'major-mode buffer))) (display-buffer buffer (cond - ;; If opened from an eshell window or popup, use the same window. - ((or (derived-mode-p 'eshell-mode) - (eq (window-dedicated-p) 'side)) - '(display-buffer-same-window)) - ;; Open target buffers below the current one (we want previous - ;; magit windows to be visible; especially magit-status). + ((and (eq buffer-mode 'magit-status-mode) + (get-buffer-window buffer)) + '(display-buffer-reuse-window)) + ;; Any magit buffers opened from a commit window should open below + ;; it. Also open magit process windows below. ((or (bound-and-true-p git-commit-mode) - (derived-mode-p 'magit-mode)) + (eq buffer-mode 'magit-process-mode)) (let ((size (if (eq buffer-mode 'magit-process-mode) 0.35 0.7))) `(display-buffer-below-selected . ((window-height . ,(truncate (* (window-height) size))))))) - ;; log/stash/process buffers, unless opened from a magit-status - ;; window, should be opened in popups. - ((memq buffer-mode '(magit-process-mode - magit-log-mode - magit-stash-mode)) - '(display-buffer-below-selected)) - ;; Last resort: use current window - ('(display-buffer-same-window)))))) + + ;; Everything else should reuse the current window. + ((or (not (derived-mode-p 'magit-mode)) + (not (memq (with-current-buffer buffer major-mode) + '(magit-process-mode + magit-revision-mode + magit-diff-mode + magit-stash-mode + magit-status-mode)))) + '(display-buffer-same-window)))))) ;; @@ -65,14 +69,13 @@ control in buffers." (interactive) (quit-window) - (unless (cdr - (delq nil - (mapcar (lambda (win) - (with-selected-window win - (eq major-mode 'magit-status-mode))) - (window-list)))) + (unless (delq nil + (mapcar (lambda (win) + (with-selected-window win + (eq major-mode 'magit-status-mode))) + (window-list))) (mapc #'+magit--kill-buffer (magit-mode-get-buffers)) - (dolist (buffer (doom-buffer-list)) + (dolist (buffer (buffer-list)) (when (buffer-live-p buffer) (if (get-buffer-window buffer) (+magit--refresh-vc-in-buffer buffer) @@ -123,18 +126,3 @@ control in buffers." (url-or-repo)) dir nil)) - - -;; -;; Advice - -;;;###autoload -(defun +magit*hub-settings--format-magithub.enabled () - "Change the setting to display 'false' as its default." - (magit--format-popup-variable:choices "magithub.enabled" '("true" "false") "false")) - -;;;###autoload -(defun +magit*hub-enabled-p () - "Disables magithub by default." - (magithub-settings--value-or "magithub.enabled" nil - #'magit-get-boolean)) diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index 06f552954..28d7bd0b6 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -8,7 +8,7 @@ It is passed a user and repository name.") ;; ;; Packages -(def-package! magit +(use-package! magit :commands magit-file-delete :defer-incrementally (dash f s with-editor git-commit package eieio lv transient) :init @@ -20,7 +20,21 @@ It is passed a user and repository name.") :config (setq transient-default-level 5 magit-revision-show-gravatars '("^Author: " . "^Commit: ") - magit-diff-refine-hunk t) ; show granular diffs in selected hunk + magit-diff-refine-hunk t ; show granular diffs in selected hunk + ;; Don't autosave repo buffers. This is too magical, and saving can + ;; trigger a bunch of unwanted side-effects, like save hooks and + ;; formatters. Trust us to know what we're doing. + magit-save-repository-buffers nil) + + ;; The default location for git-credential-cache is in + ;; ~/.config/git/credential. However, if ~/.git-credential-cache/ exists, then + ;; it is used instead. Magit seems to be hardcoded to use the latter, so here + ;; we override it to have more correct behavior. + (unless (file-exists-p "~/.git-credential-cache/") + (setq magit-credential-cache-daemon-socket + (doom-glob (or (getenv "XDG_CONFIG_HOME") + "~/.config/") + "git/credential/socket"))) ;; Magit uses `magit-display-buffer-traditional' to display windows, by ;; default, which is a little primitive. `+magit-display-buffer' marries @@ -32,19 +46,21 @@ It is passed a user and repository name.") ;; 2. The status screen isn't buried when viewing diffs or logs from the ;; status screen. (setq transient-display-buffer-action '(display-buffer-below-selected) - magit-display-buffer-function #'+magit-display-buffer) + magit-display-buffer-function #'+magit-display-buffer-fn) (set-popup-rule! "^\\(?:\\*magit\\|magit:\\| \\*transient\\*\\)" :ignore t) ;; Add --tags switch - (transient-append-suffix 'magit-fetch - "-p" '("-t" "Fetch all tags" ("-t" "--tags"))) + (transient-append-suffix 'magit-fetch "-p" + '("-t" "Fetch all tags" ("-t" "--tags"))) + (transient-append-suffix 'magit-pull "-r" + '("-a" "Autostash" "--autostash")) ;; so magit buffers can be switched to (except for process buffers) - (defun +magit-buffer-p (buf) - (with-current-buffer buf - (and (derived-mode-p 'magit-mode) - (not (eq major-mode 'magit-process-mode))))) - (add-to-list 'doom-real-buffer-functions #'+magit-buffer-p nil #'eq) + (add-hook! 'doom-real-buffer-functions + (defun +magit-buffer-p (buf) + (with-current-buffer buf + (and (derived-mode-p 'magit-mode) + (not (eq major-mode 'magit-process-mode)))))) ;; properly kill leftover magit buffers on quit (define-key magit-status-mode-map [remap magit-mode-bury-buffer] #'+magit/quit) @@ -53,7 +69,7 @@ It is passed a user and repository name.") (define-key transient-map [escape] #'transient-quit-one)) -(def-package! forge +(use-package! forge ;; We defer loading even further because forge's dependencies will try to ;; compile emacsql, which is a slow and blocking operation. :after-call magit-status @@ -63,23 +79,41 @@ It is passed a user and repository name.") ;; All forge list modes are derived from `forge-topic-list-mode' (map! :map forge-topic-list-mode-map :n "q" #'kill-current-buffer) (set-popup-rule! "^\\*?[0-9]+:\\(?:new-\\|[0-9]+$\\)" :size 0.45 :modeline t :ttl 0 :quit nil) - (set-popup-rule! "^\\*\\(?:[^/]+/[^ ]+ #[0-9]+\\*$\\|Issues\\|Pull-Requests\\|forge\\)" :ignore t)) + (set-popup-rule! "^\\*\\(?:[^/]+/[^ ]+ #[0-9]+\\*$\\|Issues\\|Pull-Requests\\|forge\\)" :ignore t) + + (defadvice! +magit--forge-get-repository-lazily-a (&rest _) + "Make `forge-get-repository' return nil if the binary isn't built yet. +This prevents emacsql getting compiled, which appears to come out of the blue +and blocks Emacs for a short while." + :before-while #'forge-get-repository + (file-executable-p emacsql-sqlite-executable)) + + (defadvice! +magit--forge-build-binary-lazily-a (&rest _) + "Make `forge-dispatch' only build emacsql if necessary. +Annoyingly, the binary gets built as soon as Forge is loaded. Since we've +disabled that in `+magit--forge-get-repository-lazily-a', we must manually +ensure it is built when we actually use Forge." + :before #'forge-dispatch + (unless (file-executable-p emacsql-sqlite-executable) + (emacsql-sqlite-compile 2)))) -(def-package! magit-todos +(use-package! magit-todos :after magit :config - (setq magit-todos-keyword-suffix "\\(?:([^)]+)\\)?:?") + (setq magit-todos-keyword-suffix "\\(?:([^)]+)\\)?:?") ; make colon optional (define-key magit-todos-section-map "j" nil) - (advice-add #'magit-todos-mode :around #'doom*shut-up) + ;; Warns that jT isn't bound. Well, yeah, you don't need to tell me, that was + ;; on purpose ya goose. + (advice-add #'magit-todos-mode :around #'doom-shut-up-a) (magit-todos-mode +1)) -(def-package! magit-gitflow +(use-package! magit-gitflow :hook (magit-mode . turn-on-magit-gitflow)) -(def-package! evil-magit +(use-package! evil-magit :when (featurep! :editor evil +everywhere) :after magit :init diff --git a/modules/tools/pass/config.el b/modules/tools/pass/config.el index e76a49081..4c790bc69 100644 --- a/modules/tools/pass/config.el +++ b/modules/tools/pass/config.el @@ -14,13 +14,13 @@ (setq password-store-password-length 12) ;; Fix hard-coded password-store location; respect PASSWORD_STORE_DIR envvar -(defun +pass*read-entry (entry) +(defadvice! +pass--respect-pass-dir-envvar-a (entry) "Return a string with the file content of ENTRY." + :override #'auth-source-pass--read-entry (with-temp-buffer (insert-file-contents (expand-file-name (format "%s.gpg" entry) (password-store-dir))) (buffer-substring-no-properties (point-min) (point-max)))) -(advice-add #'auth-source-pass--read-entry :override #'+pass*read-entry) ;; `pass' diff --git a/modules/tools/pass/packages.el b/modules/tools/pass/packages.el index cb4e52e36..a65e113a8 100644 --- a/modules/tools/pass/packages.el +++ b/modules/tools/pass/packages.el @@ -7,8 +7,8 @@ ;; an older version of `auto-source-pass' is built into Emacs 26+, so we must ;; install the new version directly from the source and with a psuedonym. -(package! auth-source-pass-new - :recipe (auth-source-pass :fetcher github :repo "DamienCassou/auth-password-store")) +(package! auth-source-pass + :recipe (:host github :repo "DamienCassou/auth-password-store")) (when (featurep! :completion ivy) (package! ivy-pass)) diff --git a/modules/tools/pdf/autoload/pdf.el b/modules/tools/pdf/autoload/pdf.el new file mode 100644 index 000000000..31890d982 --- /dev/null +++ b/modules/tools/pdf/autoload/pdf.el @@ -0,0 +1,111 @@ +;;; tools/pdf/autoload/pdf.el -*- lexical-binding: t; -*- +;;;###autoload +(defun *pdf-pdf-view-use-scaling-p () + "Return t if scaling should be used." + (and (or (and (eq (framep-on-display) 'ns) (string-equal emacs-version "27.0.50")) + (memq (pdf-view-image-type) + '(imagemagick image-io))) + pdf-view-use-scaling)) +;;;###autoload +(defun *pdf-pdf-annot-show-annotation (a &optional highlight-p window) + "Make annotation A visible. + +Turn to A's page in WINDOW, and scroll it if necessary. + +If HIGHLIGHT-P is non-nil, visually distinguish annotation A from +other annotations." + + (save-selected-window + (when window (select-window window)) + (pdf-util-assert-pdf-window) + (let* ((page (pdf-annot-get a 'page)) + (size (pdf-view-image-size)) + (width (car size))) + (unless (= page (pdf-view-current-page)) + (pdf-view-goto-page page)) + (let ((edges (pdf-annot-get-display-edges a))) + (when highlight-p + (pdf-view-display-image + (pdf-view-create-image + (pdf-cache-renderpage-highlight + page width + `("white" "steel blue" 0.35 ,@edges)) + :map (pdf-view-apply-hotspot-functions + window page size) + :width width))) + (pdf-util-scroll-to-edges + (pdf-util-scale-relative-to-pixel (car edges))))))) +;;;###autoload +(defun *pdf-pdf-isearch-hl-matches (current matches &optional occur-hack-p) + "Highlighting edges CURRENT and MATCHES." + (cl-check-type current pdf-isearch-match) + (cl-check-type matches (list-of pdf-isearch-match)) + (cl-destructuring-bind (fg1 bg1 fg2 bg2) + (pdf-isearch-current-colors) + (let* ((width (car (pdf-view-image-size))) + (page (pdf-view-current-page)) + (window (selected-window)) + (buffer (current-buffer)) + (tick (cl-incf pdf-isearch--hl-matches-tick)) + (pdf-info-asynchronous + (lambda (status data) + (when (and (null status) + (eq tick pdf-isearch--hl-matches-tick) + (buffer-live-p buffer) + (window-live-p window) + (eq (window-buffer window) + buffer)) + (with-selected-window window + (when (and (derived-mode-p 'pdf-view-mode) + (or isearch-mode + occur-hack-p) + (eq page (pdf-view-current-page))) + (pdf-view-display-image + (pdf-view-create-image data + :width width)))))))) + (pdf-info-renderpage-text-regions + page width t nil + `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative + current)) + `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative + (apply 'append + (remove current matches)))))))) +;;;###autoload +(defun *pdf-pdf-util-frame-scale-factor () + "Return the frame scale factor depending on the image type used for display. +When `pdf-view-use-scaling' is non-nil and imagemagick or +image-io are used as the image type for display, return the +backing-scale-factor of the frame if available. If a +backing-scale-factor attribute isn't available, return 2 if the +frame's PPI is larger than 180. Otherwise, return 1." + (if (and pdf-view-use-scaling + (memq (pdf-view-image-type) '(imagemagick image-io)) + (fboundp 'frame-monitor-attributes)) + (or (cdr (assq 'backing-scale-factor (frame-monitor-attributes))) + (if (>= (pdf-util-frame-ppi) 180) + 2 + 1)) + (if (and (eq (framep-on-display) 'ns) (string-equal emacs-version "27.0.50")) + 2 + 1))) +;;;###autoload +(defun *pdf-pdf-view-display-region (&optional region rectangle-p) + ;; TODO: write documentation! + (unless region + (pdf-view-assert-active-region) + (setq region pdf-view-active-region)) + (let ((colors (pdf-util-face-colors + (if rectangle-p 'pdf-view-rectangle 'pdf-view-region) + (bound-and-true-p pdf-view-dark-minor-mode))) + (page (pdf-view-current-page)) + (width (car (pdf-view-image-size)))) + (pdf-view-display-image + (pdf-view-create-image + (if rectangle-p + (pdf-info-renderpage-highlight + page width nil + `(,(car colors) ,(cdr colors) 0.35 ,@region)) + (pdf-info-renderpage-text-regions + page width nil nil + `(,(car colors) ,(cdr colors) ,@region))) + :width width)))) diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index 43e5fd289..f57211695 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -1,6 +1,6 @@ ;;; tools/pdf/config.el -*- lexical-binding: t; -*- -(def-package! pdf-tools +(use-package! pdf-tools :mode ("\\.pdf\\'" . pdf-view-mode) :config (unless noninteractive @@ -8,7 +8,7 @@ (map! :map pdf-view-mode-map :gn "q" #'kill-current-buffer) - (defun +pdf|cleanup-windows () + (defun +pdf-cleanup-windows-h () "Kill left-over annotation buffers when the document is killed." (when (buffer-live-p pdf-annot-list-document-buffer) (pdf-info-close pdf-annot-list-document-buffer)) @@ -18,9 +18,17 @@ (when (and contents-buffer (buffer-live-p contents-buffer)) (kill-buffer contents-buffer)))) (add-hook! 'pdf-view-mode-hook - (add-hook 'kill-buffer-hook #'+pdf|cleanup-windows nil t)) + (add-hook 'kill-buffer-hook #'+pdf-cleanup-windows-h nil t)) - (setq-default pdf-view-display-size 'fit-page) + (setq-default pdf-view-display-size 'fit-page + pdf-view-use-scaling t + pdf-view-use-imagemagick nil) + + (advice-add 'pdf-annot-show-annotation :override #'*pdf-pdf-annot-show-annotation) + (advice-add 'pdf-isearch-hl-matches :override #'*pdf-pdf-isearch-hl-matches) + (advice-add 'pdf-util-frame-scale-factor :override #'*pdf-pdf-util-frame-scale-factor) + (advice-add 'pdf-view-display-region :override #'*pdf-pdf-view-display-region) + (advice-add 'pdf-view-use-scaling-p :override #'*pdf-pdf-view-use-scaling-p) ;; Turn off cua so copy works (add-hook! 'pdf-view-mode-hook (cua-mode 0)) ;; Handle PDF-tools related popups better diff --git a/modules/tools/prodigy/autoload.el b/modules/tools/prodigy/autoload.el index e0517419f..5f97c8db9 100644 --- a/modules/tools/prodigy/autoload.el +++ b/modules/tools/prodigy/autoload.el @@ -32,17 +32,3 @@ (file-directory-p (plist-get service :project))) collect service into services finally do (setq prodigy-service services))) - -;;;###autoload -(defun +prodigy*services (orig-fn &rest args) - "Adds a new :project property to prodigy services, which hides the service -unless invoked from the relevant project." - (let ((project-root (downcase (or (doom-project-root) default-directory))) - (services (apply orig-fn args))) - (if current-prefix-arg - services - (cl-remove-if-not (lambda (service) - (let ((project (plist-get service :project))) - (or (not project) - (file-in-directory-p project-root project)))) - services)))) diff --git a/modules/tools/prodigy/config.el b/modules/tools/prodigy/config.el index 76f8b1078..38e6dcc2e 100644 --- a/modules/tools/prodigy/config.el +++ b/modules/tools/prodigy/config.el @@ -3,11 +3,19 @@ (after! prodigy (set-evil-initial-state! 'prodigy-mode 'emacs) - ;; Make services, etc persistent between Emacs sessions - (doom-cache-persist - :prodigy '(prodigy-services prodigy-tags prodigy-filters)) - - (advice-add #'prodigy-services :around #'+prodigy*services) + (defadvice! +prodigy--add-project-property-a (orig-fn &rest args) + "Adds a new :project property to prodigy services, which hides the service +unless invoked from the relevant project." + :around #'prodigy-services + (let ((project-root (downcase (or (doom-project-root) default-directory))) + (services (apply orig-fn args))) + (if current-prefix-arg + services + (cl-remove-if-not (lambda (service) + (let ((project (plist-get service :project))) + (or (not project) + (file-in-directory-p project-root project)))) + services)))) (define-key prodigy-mode-map "d" #'+prodigy/delete)) diff --git a/modules/tools/rgb/autoload.el b/modules/tools/rgb/autoload.el index 5fdedbe29..1036cd106 100644 --- a/modules/tools/rgb/autoload.el +++ b/modules/tools/rgb/autoload.el @@ -1,7 +1,8 @@ ;;; tools/rgb/autoload.el -*- lexical-binding: t; -*- +;;;###if (featurep! :ui hydra) -;;;###autoload (autoload '+rgb-kurecolor-hydra/body "tools/rgb/autoload" nil t) -(defhydra +rgb-kurecolor-hydra (:color pink :hint nil) +;;;###autoload (autoload '+rgb/kurecolor-hydra/body "tools/rgb/autoload" nil t) +(defhydra +rgb/kurecolor-hydra (:color pink :hint nil) " Inc/Dec _w_/_W_ brightness _d_/_D_ saturation _e_/_E_ hue " ("w" kurecolor-decrease-brightness-by-step) diff --git a/modules/tools/terraform/config.el b/modules/tools/terraform/config.el index 89b0315fc..e6ca17f5e 100644 --- a/modules/tools/terraform/config.el +++ b/modules/tools/terraform/config.el @@ -8,7 +8,7 @@ :desc "terraform plan" "p" (λ! (compile "terraform plan"))) -(def-package! company-terraform +(use-package! company-terraform :when (featurep! :completion company) :after terraform-mode :config diff --git a/modules/tools/upload/config.el b/modules/tools/upload/config.el index 177ca4bf5..cb7631413 100644 --- a/modules/tools/upload/config.el +++ b/modules/tools/upload/config.el @@ -12,7 +12,7 @@ ;; Note: `ssh-deploy-root-local' is optional, and will resort to ;; `doom-project-root' if unspecified. -(def-package! ssh-deploy +(use-package! ssh-deploy :commands (ssh-deploy-upload-handler ssh-deploy-upload-handler-forced ssh-deploy-diff-handler diff --git a/modules/tools/wakatime/autoload.el b/modules/tools/wakatime/autoload.el index e07702ab8..8602b2d37 100644 --- a/modules/tools/wakatime/autoload.el +++ b/modules/tools/wakatime/autoload.el @@ -7,7 +7,7 @@ "If non-nil, obfuscate files and only show what projects you're working on.") ;;;###autoload -(add-hook 'doom-init-modules-hook #'+wakatime|delayed-autostart) +(add-hook 'doom-init-modules-hook #'+wakatime-delayed-autostart-h) ;;;###autoload (defun +wakatime/setup () @@ -32,7 +32,7 @@ changes." (message "Wakatime enabled. You're good to go!"))) ;;;###autoload -(defun +wakatime|autostart (&rest _) +(defun +wakatime-autostart-h (&rest _) "Initialize wakatime (if `wakatime-api-key' is set, otherwise no-op with a warning)." (interactive) @@ -44,22 +44,22 @@ warning)." (make-directory +wakatime-home t))) (global-wakatime-mode +1)) ;; - (remove-hook 'doom-switch-buffer-hook #'+wakatime|autostart) - (advice-remove 'after-find-file #'+wakatime|autostart)) + (remove-hook 'doom-switch-buffer-hook #'+wakatime-autostart-h) + (advice-remove 'after-find-file #'+wakatime-autostart-h)) ;;;###autoload -(defun +wakatime|delayed-autostart (&rest _) +(defun +wakatime-delayed-autostart-h (&rest _) "Lazily initialize `wakatime-mode' until the next time you switch buffers or open a file." - (add-hook 'doom-switch-buffer-hook #'+wakatime|autostart) + (add-hook 'doom-switch-buffer-hook #'+wakatime-autostart-h) ;; 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-h)) -(defun +wakatime*append-options (ret) +(defadvice! +wakatime--append-options-a (ret) "Modifies the wakatime command string so that `+wakatime-hide-filenames' and `+wakatime-home' are respected." + :filter-return #'wakatime-client-command (concat (when +wakatime-home (format "WAKATIME_HOME=%s " (shell-quote-argument +wakatime-home))) ret (if +wakatime-hide-filenames " --hide-filenames"))) -(advice-add #'wakatime-client-command :filter-return #'+wakatime*append-options) diff --git a/modules/ui/deft/config.el b/modules/ui/deft/config.el index d1d0d24dc..a84a0a6f0 100644 --- a/modules/ui/deft/config.el +++ b/modules/ui/deft/config.el @@ -1,6 +1,6 @@ ;;; ui/deft/config.el -*- lexical-binding: t; -*- -(def-package! deft +(use-package! deft :commands deft :init (setq deft-extensions '("org" "md" "tex" "txt") diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index 0e599f044..1912b18b9 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -16,7 +16,7 @@ while they run.") "The path to the image file to be used in on the dashboard. The path is relative to `+doom-dashboard-banner-dir'. If nil, always use the ASCII banner.") -(defvar +doom-dashboard-banner-dir (concat (DIR!) "banners/") +(defvar +doom-dashboard-banner-dir (concat (dir!) "/banners/") "Where to look for `+doom-dashboard-banner-file'.") (defvar +doom-dashboard-banner-padding '(4 . 4) @@ -102,29 +102,33 @@ PLIST can have the following properties: ;; ;;; Bootstrap -(defun +doom-dashboard|init () +(defun +doom-dashboard-init-h () "Initializes Doom's dashboard." (unless noninteractive ;; Ensure the dashboard becomes Emacs' go-to buffer when there's nothing ;; else to show. (setq doom-fallback-buffer-name +doom-dashboard-name initial-buffer-choice #'doom-fallback-buffer) + (unless fancy-splash-image + (setq fancy-splash-image + (expand-file-name +doom-dashboard-banner-file + +doom-dashboard-banner-dir))) (when (equal (buffer-name) "*scratch*") (set-window-buffer nil (doom-fallback-buffer)) (if (daemonp) - (add-hook 'after-make-frame-functions #'+doom-dashboard|reload-frame) + (add-hook 'after-make-frame-functions #'+doom-dashboard-reload-frame-h) (+doom-dashboard-reload))) ;; Ensure the dashboard is up-to-date whenever it is switched to or resized. - (add-hook 'window-configuration-change-hook #'+doom-dashboard|resize) - (add-hook 'window-size-change-functions #'+doom-dashboard|resize) - (add-hook 'doom-switch-buffer-hook #'+doom-dashboard|reload-maybe) - (add-hook 'delete-frame-functions #'+doom-dashboard|reload-frame) + (add-hook 'window-configuration-change-hook #'+doom-dashboard-resize-h) + (add-hook 'window-size-change-functions #'+doom-dashboard-resize-h) + (add-hook 'doom-switch-buffer-hook #'+doom-dashboard-reload-maybe-h) + (add-hook 'delete-frame-functions #'+doom-dashboard-reload-frame-h) ;; `persp-mode' integration: update `default-directory' when switching perspectives - (add-hook 'persp-created-functions #'+doom-dashboard|record-project) - (add-hook 'persp-activated-functions #'+doom-dashboard|detect-project) - (add-hook 'persp-before-switch-functions #'+doom-dashboard|record-project))) + (add-hook 'persp-created-functions #'+doom-dashboard--persp-record-project-h) + (add-hook 'persp-activated-functions #'+doom-dashboard--persp-detect-project-h) + (add-hook 'persp-before-switch-functions #'+doom-dashboard--persp-record-project-h))) -(add-hook 'doom-init-ui-hook #'+doom-dashboard|init) +(add-hook 'doom-init-ui-hook #'+doom-dashboard-init-h) ;; @@ -148,7 +152,7 @@ PLIST can have the following properties: collect (cons car nil) into alist finally do (setq fringe-indicator-alist alist)) ;; Ensure point is always on a button - (add-hook 'post-command-hook #'+doom-dashboard|reposition-point nil t)) + (add-hook 'post-command-hook #'+doom-dashboard-reposition-point-h nil t)) (define-key! +doom-dashboard-mode-map [left-margin mouse-1] #'ignore @@ -185,7 +189,7 @@ PLIST can have the following properties: ;; ;;; Hooks -(defun +doom-dashboard|reposition-point () +(defun +doom-dashboard-reposition-point-h () "Trap the point in the buttons." (when (region-active-p) (setq deactivate-mark t) @@ -198,7 +202,7 @@ PLIST can have the following properties: (progn (goto-char (point-min)) (forward-button 1)))) -(defun +doom-dashboard|reload-maybe () +(defun +doom-dashboard-reload-maybe-h () "Reload the dashboard or its state. If this isn't a dashboard buffer, move along, but record its `default-directory' @@ -214,14 +218,14 @@ If this is the dashboard buffer, reload it completely." (setq +doom-dashboard--last-cwd default-directory) (+doom-dashboard-update-pwd)))) -(defun +doom-dashboard|reload-frame (_frame) +(defun +doom-dashboard-reload-frame-h (_frame) "Reload the dashboard after a brief pause. This is necessary for new frames, whose dimensions may not be fully initialized by the time this is run." (when (timerp +doom-dashboard--reload-timer) (cancel-timer +doom-dashboard--reload-timer)) ; in case this function is run rapidly (setq +doom-dashboard--reload-timer (run-with-timer 0.1 nil #'+doom-dashboard-reload t))) -(defun +doom-dashboard|resize (&rest _) +(defun +doom-dashboard-resize-h (&rest _) "Recenter the dashboard, and reset its margins and fringes." (let (buffer-list-update-hook window-configuration-change-hook @@ -247,20 +251,20 @@ whose dimensions may not be fully initialized by the time this is run." 2)))) ?\n))))))))) -(defun +doom-dashboard|detect-project (&rest _) +(defun +doom-dashboard--persp-detect-project-h (&rest _) "Check for a `last-project-root' parameter in the perspective, and set the dashboard's `default-directory' to it if it exists. -This and `+doom-dashboard|record-project' provides `persp-mode' integration with +This and `+doom-dashboard--persp-record-project-h' provides `persp-mode' integration with the Doom dashboard. It ensures that the dashboard is always in the correct project (which may be different across perspective)." (when (bound-and-true-p persp-mode) (when-let (pwd (persp-parameter 'last-project-root)) (+doom-dashboard-update-pwd pwd)))) -(defun +doom-dashboard|record-project (&optional persp &rest _) +(defun +doom-dashboard--persp-record-project-h (&optional persp &rest _) "Record the last `doom-project-root' for the current perspective. See -`+doom-dashboard|detect-project' for more information." +`+doom-dashboard--persp-detect-project-h' for more information." (when (bound-and-true-p persp-mode) (set-persp-parameter 'last-project-root (doom-project-root) @@ -284,10 +288,10 @@ controlled by `+doom-dashboard-pwd-policy'." (doom-log "Changed dashboard's PWD to %s" pwd) (setq-local default-directory pwd)) (let ((new-pwd (+doom-dashboard--get-pwd))) - (when (and new-pwd (file-directory-p new-pwd)) - (unless (string-suffix-p "/" new-pwd) - (setq new-pwd (concat new-pwd "/"))) - (+doom-dashboard-update-pwd new-pwd))))) + (when (and new-pwd (file-accessible-directory-p new-pwd)) + (+doom-dashboard-update-pwd + (concat (directory-file-name new-pwd) + "/")))))) (defun +doom-dashboard-reload (&optional force) "Update the DOOM scratch buffer (or create it, if it doesn't exist)." @@ -305,9 +309,9 @@ controlled by `+doom-dashboard-pwd-policy'." (erase-buffer) (run-hooks '+doom-dashboard-functions) (goto-char pt) - (+doom-dashboard|reposition-point)) - (+doom-dashboard|resize) - (+doom-dashboard|detect-project) + (+doom-dashboard-reposition-point-h)) + (+doom-dashboard-resize-h) + (+doom-dashboard--persp-detect-project-h) (+doom-dashboard-update-pwd) (current-buffer))))) @@ -365,21 +369,22 @@ controlled by `+doom-dashboard-pwd-policy'." "==' _-' E M A C S \\/ `==" "\\ _-' `-_ /" " `'' ``'")) - (when (and (stringp +doom-dashboard-banner-file) - (display-graphic-p) - (file-exists-p! +doom-dashboard-banner-file +doom-dashboard-banner-dir)) - (let* ((image (create-image (expand-file-name +doom-dashboard-banner-file - +doom-dashboard-banner-dir) - 'png nil)) - (size (image-size image nil)) - (margin (+ 1 (/ (- +doom-dashboard--width (car size)) 2)))) + (when (and (display-graphic-p) + (stringp fancy-splash-image) + (file-readable-p fancy-splash-image)) + (let ((image (create-image (fancy-splash-image-file)))) (add-text-properties point (point) `(display ,image rear-nonsticky (display))) - (when (> margin 0) - (save-excursion - (goto-char point) - (insert (make-string (truncate margin) ? ))))) - (insert (make-string (or (cdr +doom-dashboard-banner-padding) 0) ?\n))))) + (save-excursion + (goto-char point) + (insert (make-string + (truncate + (max 0 (+ 1 (/ (- +doom-dashboard--width + (car (image-size image nil))) + 2)))) + ? )))) + (insert (make-string (or (cdr +doom-dashboard-banner-padding) 0) + ?\n))))) (defun doom-dashboard-widget-loaded () (insert @@ -387,7 +392,7 @@ controlled by `+doom-dashboard-pwd-policy'." (propertize (+doom-dashboard--center +doom-dashboard--width - (doom|display-benchmark 'return)) + (doom-display-benchmark-h 'return)) 'face 'font-lock-comment-face) "\n")) diff --git a/modules/ui/doom-quit/config.el b/modules/ui/doom-quit/config.el index 93e287779..8c4d86aaf 100644 --- a/modules/ui/doom-quit/config.el +++ b/modules/ui/doom-quit/config.el @@ -22,11 +22,11 @@ "A list of quit messages, picked randomly by `+doom-quit'. Taken from http://doom.wikia.com/wiki/Quit_messages and elsewhere.") -(defun +doom-quit (&rest _) +(defun +doom-quit-fn (&rest _) (doom-quit-p (format "%s Quit?" (nth (random (length +doom-quit-messages)) +doom-quit-messages)))) ;; -(setq confirm-kill-emacs #'+doom-quit) +(setq confirm-kill-emacs #'+doom-quit-fn) diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el index 7a4573c2c..e2b9c5642 100644 --- a/modules/ui/doom/config.el +++ b/modules/ui/doom/config.el @@ -22,7 +22,7 @@ ;; Packages ;; -(def-package! doom-themes +(use-package! doom-themes :defer t :init (unless doom-theme @@ -33,32 +33,27 @@ ;; more Atom-esque file icons for neotree/treemacs (when (featurep! :ui neotree) (add-hook 'doom-load-theme-hook #'doom-themes-neotree-config) - (setq doom-neotree-enable-variable-pitch t - doom-neotree-file-icons 'simple - doom-neotree-line-spacing 2)) + (setq doom-themes-neotree-enable-variable-pitch t + doom-themes-neotree-file-icons 'simple + doom-themes-neotree-line-spacing 2)) (when (featurep! :ui treemacs) - (add-hook 'doom-load-theme-hook #'doom-themes-treemacs-config) - (setq doom-treemacs-enable-variable-pitch t))) + (add-hook 'doom-load-theme-hook #'doom-themes-treemacs-config))) -(def-package! solaire-mode +(use-package! solaire-mode :defer t :init - (defun +doom|solaire-mode-swap-bg-maybe () - (when-let (rule (assq doom-theme +doom-solaire-themes)) - (require 'solaire-mode) - (when (cdr rule) - (solaire-mode-swap-bg) - (with-eval-after-load 'ansi-color - (when-let (color (face-background 'default)) - (setf (aref ansi-color-names-vector 0) color)))))) - (add-hook 'doom-load-theme-hook #'+doom|solaire-mode-swap-bg-maybe t) + (add-hook! 'doom-load-theme-hook :append + (defun +doom-solaire-mode-swap-bg-maybe-h () + (pcase-let ((`(,theme . ,swap) (assq doom-theme +doom-solaire-themes))) + (require 'solaire-mode) + (if swap (solaire-mode-swap-bg))))) :config ;; fringe can become unstyled when deleting or focusing frames (add-hook 'focus-in-hook #'solaire-mode-reset) ;; Prevent color glitches when reloading either DOOM or loading a new theme - (add-hook! :append '(doom-load-theme-hook doom-reload-hook) - #'solaire-mode-reset) + (add-hook! '(doom-load-theme-hook doom-reload-hook) :append + #'solaire-mode-reset) ;; org-capture takes an org buffer and narrows it. The result is erroneously ;; considered an unreal buffer, so solaire-mode must be restored. (add-hook 'org-capture-mode-hook #'turn-on-solaire-mode) @@ -67,7 +62,7 @@ ;; the hl-line face, hl-line's highlight bleeds into the rest of the window ;; after eob. (when EMACS26+ - (defun +doom--line-range () + (defun +doom--line-range-fn () (cons (line-beginning-position) (cond ((let ((eol (line-end-position))) (and (= eol (point-max)) @@ -77,21 +72,21 @@ (= (line-end-position 2) (point-max))) (line-end-position)) ((line-beginning-position 2))))) - (setq hl-line-range-function #'+doom--line-range)) + (setq hl-line-range-function #'+doom--line-range-fn)) ;; Because fringes can't be given a buffer-local face, they can look odd, so ;; we remove them in the minibuffer and which-key popups (they serve no ;; purpose there anyway). - (defun +doom|disable-fringes-in-minibuffer (&rest _) - (set-window-fringes (minibuffer-window) 0 0 nil)) - (add-hook 'solaire-mode-hook #'+doom|disable-fringes-in-minibuffer) + (add-hook! 'solaire-mode-hook + (defun +doom-disable-fringes-in-minibuffer-h (&rest _) + (set-window-fringes (minibuffer-window) 0 0 nil))) - (defun doom*no-fringes-in-which-key-buffer (&rest _) - (+doom|disable-fringes-in-minibuffer) + (defadvice! +doom--no-fringes-in-which-key-buffer-a (&rest _) + :after 'which-key--show-buffer-side-window + (+doom-disable-fringes-in-minibuffer-h) (set-window-fringes (get-buffer-window which-key--buffer) 0 0 nil)) - (advice-add 'which-key--show-buffer-side-window :after #'doom*no-fringes-in-which-key-buffer) (add-hook! '(minibuffer-setup-hook window-configuration-change-hook) - #'+doom|disable-fringes-in-minibuffer) + #'+doom-disable-fringes-in-minibuffer-h) (solaire-global-mode +1)) diff --git a/modules/ui/fill-column/autoload.el b/modules/ui/fill-column/autoload.el new file mode 100644 index 000000000..7e69130a0 --- /dev/null +++ b/modules/ui/fill-column/autoload.el @@ -0,0 +1,7 @@ +;;; ui/fill-column/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload (autoload 'hl-fill-column-mode "hl-fill-column" nil t) + +;;;###autoload +(add-hook! '(text-mode-hook prog-mode-hook conf-mode-hook) + #'hl-fill-column-mode) diff --git a/modules/ui/fill-column/config.el b/modules/ui/fill-column/config.el deleted file mode 100644 index 3a2629e4f..000000000 --- a/modules/ui/fill-column/config.el +++ /dev/null @@ -1,4 +0,0 @@ -;;; ui/fill-column/config.el -*- lexical-binding: t; -*- - -(def-package! hl-fill-column - :hook ((text-mode prog-mode conf-mode) . hl-fill-column-mode)) diff --git a/modules/ui/hl-todo/autoload.el b/modules/ui/hl-todo/autoload.el deleted file mode 100644 index 21f09e6b7..000000000 --- a/modules/ui/hl-todo/autoload.el +++ /dev/null @@ -1,18 +0,0 @@ -;;; ui/hl-todo/autoload.el -*- lexical-binding: t; -*- - -;;;###autoload -(defun +hl-todo|use-face-detection () - "Use a different, more primitive method of locating todo keywords. - -This is useful for major modes that don't use or have a valid syntax-table entry -for comment start/end characters." - (set (make-local-variable 'hl-todo-keywords) - '(((lambda (limit) - (let (case-fold-search) - (and (re-search-forward hl-todo-regexp limit t) - (memq 'font-lock-comment-face (doom-enlist (get-text-property (point) 'face)))))) - (1 (hl-todo-get-face) t t)))) - (when hl-todo-mode - (hl-todo-mode -1) - (hl-todo-mode +1))) - diff --git a/modules/ui/hl-todo/config.el b/modules/ui/hl-todo/config.el index a2a445f2b..547b54afe 100644 --- a/modules/ui/hl-todo/config.el +++ b/modules/ui/hl-todo/config.el @@ -1,15 +1,28 @@ ;;; ui/hl-todo/packages.el -*- lexical-binding: t; -*- -(def-package! hl-todo +(use-package! hl-todo :hook (prog-mode . hl-todo-mode) :config - (setq hl-todo-keyword-faces + (setq hl-todo-highlight-punctuation ":" + hl-todo-keyword-faces `(("TODO" . ,(face-foreground 'warning)) ("FIXME" . ,(face-foreground 'error)) - ("NOTE" . ,(face-foreground 'success)))) + ("HACK" . ,(face-foreground 'font-lock-constant-face)) + ("REVIEW" . ,(face-foreground 'font-lock-keyword-face)) + ("NOTE" . ,(face-foreground 'success)) + ("DEPRECATED" . ,(face-foreground 'font-lock-doc-face)))) ;; Use a more primitive todo-keyword detection method in major modes that ;; don't use/have a valid syntax table entry for comments. - (add-hook! - (pug-mode haml-mode) - #'+hl-todo|use-face-detection)) + (add-hook! '(pug-mode-hook haml-mode-hook) + (defun +hl-todo--use-face-detection-h () + "Use a different, more primitive method of locating todo keywords." + (set (make-local-variable 'hl-todo-keywords) + '(((lambda (limit) + (let (case-fold-search) + (and (re-search-forward hl-todo-regexp limit t) + (memq 'font-lock-comment-face (doom-enlist (get-text-property (point) 'face)))))) + (1 (hl-todo-get-face) t t)))) + (when hl-todo-mode + (hl-todo-mode -1) + (hl-todo-mode +1))))) diff --git a/core/autoload/hydras.el b/modules/ui/hydra/autoload/windows.el similarity index 75% rename from core/autoload/hydras.el rename to modules/ui/hydra/autoload/windows.el index 68f6b4360..6d703ad81 100644 --- a/core/autoload/hydras.el +++ b/modules/ui/hydra/autoload/windows.el @@ -1,7 +1,7 @@ -;;; core/autoload/hydras.el -*- lexical-binding: t; -*- +;;; ui/hydra/autoload/windows.el -*- lexical-binding: t; -*- -;;;###autoload (autoload 'doom-text-zoom-hydra/body "core/autoload/hydras" nil t) -(defhydra doom-text-zoom-hydra (:hint t :color red) +;;;###autoload (autoload '+hydra/text-zoom/body "ui/hydra/autoload/windows" nil t) +(defhydra +hydra/text-zoom (:hint t :color red) " Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset " @@ -9,8 +9,8 @@ ("k" doom/decrease-font-size "out") ("0" doom/reset-font-size "reset")) -;;;###autoload (autoload 'doom-window-nav-hydra/body "core/autoload/hydras" nil t) -(defhydra doom-window-nav-hydra (:hint nil) +;;;###autoload (autoload '+hydra/window-nav/body "ui/hydra/autoload/windows" nil t) +(defhydra +hydra/window-nav (:hint nil) " Split: _v_ert _s_:horz Delete: _c_lose _o_nly diff --git a/modules/ui/hydra/config.el b/modules/ui/hydra/config.el new file mode 100644 index 000000000..4d8bdb3fb --- /dev/null +++ b/modules/ui/hydra/config.el @@ -0,0 +1,4 @@ +;;; ui/hydra/config.el -*- lexical-binding: t; -*- + +;;;###package hydra +(setq lv-use-seperator t) diff --git a/modules/ui/hydra/packages.el b/modules/ui/hydra/packages.el new file mode 100644 index 000000000..f92e067e0 --- /dev/null +++ b/modules/ui/hydra/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; ui/hydra/packages.el + +(package! hydra) diff --git a/modules/ui/indent-guides/config.el b/modules/ui/indent-guides/config.el index 89650dcfe..214a90ff4 100644 --- a/modules/ui/indent-guides/config.el +++ b/modules/ui/indent-guides/config.el @@ -1,15 +1,13 @@ ;;; ui/indent-guides/config.el -*- lexical-binding: t; -*- -(def-package! highlight-indent-guides +(use-package! highlight-indent-guides :hook ((prog-mode text-mode conf-mode) . highlight-indent-guides-mode) :init (setq highlight-indent-guides-method 'character) :config (add-hook 'focus-in-hook #'highlight-indent-guides-auto-set-faces) - - (defun +indent-guides|disable-maybe () - (when highlight-indent-guides-mode - (highlight-indent-guides-mode -1))) ;; `highlight-indent-guides' breaks in these modes - (add-hook 'visual-line-mode-hook #'+indent-guides|disable-maybe) - (add-hook 'org-indent-mode-hook #'+indent-guides|disable-maybe)) + (add-hook! '(visual-line-mode-hook org-indent-mode-hook) + (defun +indent-guides-disable-maybe-h () + (when highlight-indent-guides-mode + (highlight-indent-guides-mode -1))))) diff --git a/modules/ui/modeline/autoload.el b/modules/ui/modeline/autoload.el index 848bbefaf..514a6d245 100644 --- a/modules/ui/modeline/autoload.el +++ b/modules/ui/modeline/autoload.el @@ -12,7 +12,7 @@ (defvar +modeline--old-bar-height nil) ;;;###autoload -(defun +modeline|resize-for-font () +(defun +modeline-resize-for-font-h () "Adjust the modeline's height when the font size is changed by `doom/increase-font-size' or `doom/decrease-font-size'. @@ -33,7 +33,7 @@ Meant for `doom-change-font-size-hook'." (unless EMACS26+ (doom-modeline-refresh-bars)))) ;;;###autoload -(defun +modeline|update-env-in-all-windows (&rest _) +(defun +modeline-update-env-in-all-windows-h (&rest _) "Update version strings in all buffers." (dolist (window (window-list)) (with-selected-window window @@ -41,7 +41,7 @@ Meant for `doom-change-font-size-hook'." (force-mode-line-update)))) ;;;###autoload -(defun +modeline|clear-env-in-all-windows (&rest _) +(defun +modeline-clear-env-in-all-windows-h (&rest _) "Blank out version strings in all buffers." (dolist (buffer (buffer-list)) (with-current-buffer buffer diff --git a/modules/ui/modeline/config.el b/modules/ui/modeline/config.el index e4e04bf70..43d35bcdf 100644 --- a/modules/ui/modeline/config.el +++ b/modules/ui/modeline/config.el @@ -1,6 +1,6 @@ ;;; ui/modeline/config.el -*- lexical-binding: t; -*- -(def-package! doom-modeline +(use-package! doom-modeline :hook (after-init . doom-modeline-mode) :init (unless after-init-time @@ -32,17 +32,17 @@ (add-hook 'doom-modeline-mode-hook #'size-indication-mode) ; filesize in modeline (add-hook 'doom-modeline-mode-hook #'column-number-mode) ; cursor column in modeline - (add-hook 'doom-change-font-size-hook #'+modeline|resize-for-font) + (add-hook 'doom-change-font-size-hook #'+modeline-resize-for-font-h) (add-hook 'doom-load-theme-hook #'doom-modeline-refresh-bars) (add-hook '+doom-dashboard-mode-hook #'doom-modeline-set-project-modeline) - (defun +modeline|hide-in-non-status-buffer () - "Show minimal modeline in magit-status buffer, no modeline elsewhere." - (if (eq major-mode 'magit-status-mode) - (doom-modeline-set-project-modeline) - (hide-mode-line-mode))) - (add-hook 'magit-mode-hook #'+modeline|hide-in-non-status-buffer) + (add-hook! 'magit-mode-hook + (defun +modeline-hide-in-non-status-buffer-h () + "Show minimal modeline in magit-status buffer, no modeline elsewhere." + (if (eq major-mode 'magit-status-mode) + (doom-modeline-set-project-modeline) + (hide-mode-line-mode)))) ;; Remove unused segments & extra padding (doom-modeline-def-modeline 'main @@ -58,18 +58,18 @@ '(misc-info mu4e github debug fancy-battery " " major-mode process)) ;; Some functions modify the buffer, causing the modeline to show a false - ;; modified state, so we try to force them to behave. - (defun +modeline*inhibit-modification-hooks (orig-fn &rest args) - (with-silent-modifications (apply orig-fn args))) - (advice-add #'ws-butler-after-save :around #'+modeline*inhibit-modification-hooks)) + ;; modified state, so force them to behave. + (defadvice! +modeline--inhibit-modification-hooks-a (orig-fn &rest args) + :around #'ws-butler-after-save + (with-silent-modifications (apply orig-fn args)))) ;; ;; Extensions -(def-package! anzu +(use-package! anzu :after-call isearch-mode) -(def-package! evil-anzu +(use-package! evil-anzu :when (featurep! :editor evil) :after-call (evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight)) diff --git a/modules/ui/nav-flash/autoload.el b/modules/ui/nav-flash/autoload.el index d73eb8036..530bf6483 100644 --- a/modules/ui/nav-flash/autoload.el +++ b/modules/ui/nav-flash/autoload.el @@ -24,19 +24,18 @@ or triggered from one of `+nav-flash-exclude-commands'." (setq +nav-flash--last-point (cons (point-marker) (selected-window))))) ;;;###autoload -(defun +nav-flash|delayed-blink-cursor (&rest _) +(defun +nav-flash-delayed-blink-cursor-h (&rest _) "Like `+nav-flash-blink-cursor', but links after a tiny pause, in case it isn't clear at run-time if the point will be in the correct window/buffer (like for `org-follow-link-hook')." - (run-at-time 0.1 nil #'+nav-flash|blink-cursor)) + (run-at-time 0.1 nil #'+nav-flash-blink-cursor-h)) ;;;###autoload -(defalias '+nav-flash|blink-cursor #'+nav-flash-blink-cursor) +(defalias '+nav-flash-blink-cursor-h #'+nav-flash-blink-cursor) ;;;###autoload -(defalias '+nav-flash|blink-cursor-maybe #'+nav-flash-blink-cursor-maybe) - +(defalias '+nav-flash-blink-cursor-maybe-h #'+nav-flash-blink-cursor-maybe) ;;;###autoload -(defalias '+nav-flash*blink-cursor #'+nav-flash-blink-cursor-maybe) +(defalias '+nav-flash-blink-cursor-a #'+nav-flash-blink-cursor-maybe) ;;;###autoload (defun +nav-flash/blink-cursor (&rest _) diff --git a/modules/ui/nav-flash/config.el b/modules/ui/nav-flash/config.el index 4b42dca51..cd3816a77 100644 --- a/modules/ui/nav-flash/config.el +++ b/modules/ui/nav-flash/config.el @@ -5,26 +5,26 @@ +org/dwim-at-point org-find-file org-find-file-at-mouse) "A list of commands that should not trigger nav-flash.") -(def-package! nav-flash +(use-package! nav-flash :defer t :init ;; NOTE In :tools lookup `recenter' is hooked to a bunch of jumping ;; commands, which will trigger nav-flash. - (add-hook! - '(imenu-after-jump-hook better-jumper-post-jump-hook - counsel-grep-post-action-hook dumb-jump-after-jump-hook) - #'+nav-flash|blink-cursor-maybe) + (add-hook! '(imenu-after-jump-hook + better-jumper-post-jump-hook + counsel-grep-post-action-hook + dumb-jump-after-jump-hook) + #'+nav-flash-blink-cursor-maybe-h) - (add-hook 'doom-switch-window-hook #'+nav-flash|blink-cursor-maybe) + (add-hook 'doom-switch-window-hook #'+nav-flash-blink-cursor-maybe-h) ;; `org' - (add-hook 'org-follow-link-hook #'+nav-flash|delayed-blink-cursor) + (add-hook 'org-follow-link-hook #'+nav-flash-delayed-blink-cursor-h) ;; `saveplace' - (advice-add #'save-place-find-file-hook :after #'+nav-flash*blink-cursor) + (advice-add #'save-place-find-file-hook :after #'+nav-flash-blink-cursor-a) ;; `evil' - (advice-add #'evil-window-top :after #'+nav-flash*blink-cursor) - (advice-add #'evil-window-middle :after #'+nav-flash*blink-cursor) - (advice-add #'evil-window-bottom :after #'+nav-flash*blink-cursor)) - + (advice-add #'evil-window-top :after #'+nav-flash-blink-cursor-a) + (advice-add #'evil-window-middle :after #'+nav-flash-blink-cursor-a) + (advice-add #'evil-window-bottom :after #'+nav-flash-blink-cursor-a)) diff --git a/modules/ui/neotree/config.el b/modules/ui/neotree/config.el index 07422fdab..f9a729427 100644 --- a/modules/ui/neotree/config.el +++ b/modules/ui/neotree/config.el @@ -1,6 +1,6 @@ ;;; ui/neotree/config.el -*- lexical-binding: t; -*- -(def-package! neotree +(use-package! neotree :commands (neotree-show neotree-hide neotree-toggle @@ -42,16 +42,14 @@ (after! winner (add-to-list 'winner-boring-buffers neo-buffer-name)) - ;; The cursor always sits at bol. `+neotree*fix-cursor' and - ;; `+neotree*indent-cursor' change that behavior, so that the cursor is always - ;; on the first non-blank character on the line, in the neo buffer. - (defun +neotree*fix-cursor (&rest _) - (with-current-buffer neo-global--buffer - (+neotree*indent-cursor))) - (add-hook 'neo-enter-hook #'+neotree*fix-cursor) - - (defun +neotree*indent-cursor (&rest _) + ;; The cursor always sits at bol. `+neotree--fix-cursor-h' and + ;; `+neotree--indent-cursor-a' change that behavior so that the cursor is + ;; always on the first non-blank character on the line, in the neo buffer. + (add-hook! 'neo-enter-hook + (defun +neotree-fix-cursor-h (&rest _) + (with-current-buffer neo-global--buffer + (+neotree*indent-cursor)))) + (defadvice! +neotree--indent-cursor-a (&rest _) + :after '(neotree-next-line neotree-previous-line) (beginning-of-line) - (skip-chars-forward " \t\r")) - (advice-add #'neotree-next-line :after #'+neotree*indent-cursor) - (advice-add #'neotree-previous-line :after #'+neotree*indent-cursor)) + (skip-chars-forward " \t\r"))) diff --git a/modules/ui/ophints/config.el b/modules/ui/ophints/config.el index c448ca2fb..4faf2f8da 100644 --- a/modules/ui/ophints/config.el +++ b/modules/ui/ophints/config.el @@ -1,6 +1,6 @@ ;;; ui/ophints/config.el -*- lexical-binding: t; -*- -(def-package! evil-goggles +(use-package! evil-goggles :when (featurep! :editor evil) :after-call pre-command-hook :init @@ -14,7 +14,7 @@ (evil-goggles-mode +1)) -(def-package! volatile-highlights +(use-package! volatile-highlights :unless (featurep! :editor evil) :after-call pre-command-hook :config diff --git a/modules/ui/popup/+hacks.el b/modules/ui/popup/+hacks.el index a12859535..a2851f43f 100644 --- a/modules/ui/popup/+hacks.el +++ b/modules/ui/popup/+hacks.el @@ -27,7 +27,7 @@ ;;; Core functions ;; Don't try to resize popup windows -(advice-add #'balance-windows :around #'+popup*save) +(advice-add #'balance-windows :around #'+popup-save-a) ;; @@ -38,11 +38,10 @@ ;;;###package company -(progn - (defun +popup*dont-select-me (orig-fn &rest args) - (let ((+popup--inhibit-select t)) - (apply orig-fn args))) - (advice-add #'company-show-doc-buffer :around #'+popup*dont-select-me)) +(defadvice! +popup--dont-select-me-a (orig-fn &rest args) + :around #'company-show-doc-buffer + (let ((+popup--inhibit-select t)) + (apply orig-fn args))) ;;;###package eshell @@ -51,21 +50,22 @@ ;; When eshell runs a visual command (see `eshell-visual-commands'), it spawns ;; a term buffer to run it in, but where it spawns it is the problem... - (defun +popup*eshell-undedicate-popup (orig-fn &rest args) + (defadvice! +popup--eshell-undedicate-popup (&rest _) "Force spawned term buffer to share with the eshell popup (if necessary)." + :before #'eshell-exec-visual (when (+popup-window-p) (set-window-dedicated-p nil nil) (add-transient-hook! #'eshell-query-kill-processes :after - (set-window-dedicated-p nil t))) - (apply orig-fn args)) - (advice-add #'eshell-exec-visual :around #'+popup*eshell-undedicate-popup)) + (set-window-dedicated-p nil t))))) ;;;###package evil (progn - (defun +popup*evil-command-window (hist cmd-key execute-fn) + ;; Make evil-mode cooperate with popups + (defadvice! +popup--evil-command-window-a (hist cmd-key execute-fn) "Monkey patch the evil command window to use `pop-to-buffer' instead of `switch-to-buffer', allowing the popup manager to handle it." + :override #'evil-command-window (when (eq major-mode 'evil-command-window-mode) (user-error "Cannot recursively open command line window")) (dolist (win (window-list)) @@ -81,9 +81,10 @@ (evil-command-window-mode) (evil-command-window-insert-commands hist))) - (defun +popup*evil-command-window-execute () + (defadvice! +popup--evil-command-window-execute-a () "Execute the command under the cursor in the appropriate buffer, rather than the command buffer." + :override #'evil-command-window-execute (interactive) (let ((result (buffer-substring (line-beginning-position) (line-end-position))) @@ -98,16 +99,12 @@ the command buffer." (funcall execute-fn result) (setq evil-command-window-current-buffer nil))) - ;; Make evil-mode cooperate with popups - (advice-add #'evil-command-window :override #'+popup*evil-command-window) - (advice-add #'evil-command-window-execute :override #'+popup*evil-command-window-execute) - ;; Don't mess with popups - (advice-add #'+evil--window-swap :around #'+popup*save) - (advice-add #'evil-window-move-very-bottom :around #'+popup*save) - (advice-add #'evil-window-move-very-top :around #'+popup*save) - (advice-add #'evil-window-move-far-left :around #'+popup*save) - (advice-add #'evil-window-move-far-right :around #'+popup*save)) + (advice-add #'+evil--window-swap :around #'+popup-save-a) + (advice-add #'evil-window-move-very-bottom :around #'+popup-save-a) + (advice-add #'evil-window-move-very-top :around #'+popup-save-a) + (advice-add #'evil-window-move-far-left :around #'+popup-save-a) + (advice-add #'evil-window-move-far-right :around #'+popup-save-a)) ;;;###package help-mode @@ -153,30 +150,30 @@ the command buffer." ;;;###package helpful -(progn - (defun +popup*helpful-open-in-origin-window (button) - "Open links in non-popup, originating window rather than helpful's window." - (let ((path (substring-no-properties (button-get button 'path))) - enable-local-variables - origin) - (save-popups! - (find-file path) - (when-let (pos (get-text-property button 'position - (marker-buffer button))) - (goto-char pos)) - (setq origin (selected-window)) - (recenter)) - (select-window origin))) - (advice-add #'helpful--navigate :override #'+popup*helpful-open-in-origin-window)) +(defadvice! +popup--helpful-open-in-origin-window-a (button) + "Open links in non-popup, originating window rather than helpful's window." + :override #'helpful--navigate + (let ((path (substring-no-properties (button-get button 'path))) + enable-local-variables + origin) + (save-popups! + (find-file path) + (when-let (pos (get-text-property button 'position + (marker-buffer button))) + (goto-char pos)) + (setq origin (selected-window)) + (recenter)) + (select-window origin))) ;;;###package helm ;;;###package helm-ag (when (featurep! :completion helm) - (setq helm-default-display-buffer-functions '(+popup-display-buffer-stacked-side-window)) + (setq helm-default-display-buffer-functions '(+popup-display-buffer-stacked-side-window-fn)) ;; Fix #897: "cannot open side window" error when TAB-completing file links - (defun +popup*hide-org-links-popup (orig-fn &rest args) + (defadvice! +popup--helm-hide-org-links-popup-a (orig-fn &rest args) + :around #'org-insert-link (cl-letf* ((old-org-completing-read (symbol-function 'org-completing-read)) ((symbol-function 'org-completing-read) (lambda (&rest args) @@ -192,23 +189,22 @@ the command buffer." (get-buffer-create "*Org Links*")) (apply old-org-completing-read args)))) (apply orig-fn args))) - (advice-add #'org-insert-link :around #'+popup*hide-org-links-popup) ;; Fix left-over popup window when closing persistent help for `helm-M-x' - (defun +popup*helm-elisp--persistent-help (candidate _fun &optional _name) + (defadvice! +popup--helm-elisp--persistent-help-a (candidate _fun &optional _name) + :before #'helm-elisp--persistent-help (let (win) (when (and (helm-attr 'help-running-p) (string= candidate (helm-attr 'help-current-symbol)) (setq win (get-buffer-window (get-buffer (help-buffer))))) (delete-window win)))) - (advice-add #'helm-elisp--persistent-help :before #'+popup*helm-elisp--persistent-help) ;; `helm-ag' - (defun +helm*pop-to-buffer (orig-fn &rest args) + (defadvice! +popup--helm-pop-to-buffer-a (orig-fn &rest args) + :around #'helm-ag--edit (pop-to-buffer (save-window-excursion (apply orig-fn args) - (current-buffer)))) - (advice-add #'helm-ag--edit :around #'+helm*pop-to-buffer)) + (current-buffer))))) ;;;###package ibuffer @@ -216,11 +212,11 @@ the command buffer." ;;;###package Info -(defun +popup*switch-to-info-window (&rest _) +(defadvice! +popup--switch-to-info-window-a (&rest _) + :after #'info-lookup-symbol (when-let (win (get-buffer-window "*info*")) (when (+popup-window-p win) (select-window win)))) -(advice-add #'info-lookup-symbol :after #'+popup*switch-to-info-window) ;;;###package multi-term @@ -235,26 +231,26 @@ the command buffer." ;;;###package org (after! org - (defvar +popup--disable-internal nil) ;; Org has a scorched-earth window management policy I'm not fond of. i.e. it ;; kills all other windows just so it can monopolize the frame. No thanks. We ;; can do better ourselves. - (defun +popup*suppress-delete-other-windows (orig-fn &rest args) + (defadvice! +popup--suppress-delete-other-windows-a (orig-fn &rest args) + :around '(org-add-log-note + org-capture-place-template + org-export--dispatch-ui + org-agenda-get-restriction-and-command + org-fast-tag-selection) (if +popup-mode (cl-letf (((symbol-function 'delete-other-windows) (symbol-function 'ignore))) (apply orig-fn args)) (apply orig-fn args))) - (advice-add #'org-add-log-note :around #'+popup*suppress-delete-other-windows) - (advice-add #'org-capture-place-template :around #'+popup*suppress-delete-other-windows) - (advice-add #'org-export--dispatch-ui :around #'+popup*suppress-delete-other-windows) - (advice-add #'org-agenda-get-restriction-and-command :around #'+popup*suppress-delete-other-windows) - (advice-add #'org-fast-tag-selection :around #'+popup*suppress-delete-other-windows) - (defun +popup*fix-tags-window (orig-fn &rest args) + (defadvice! +popup--org-fix-tags-window-a (orig-fn &rest args) "Hides the mode-line in *Org tags* buffer so you can actually see its content and displays it in a side window without deleting all other windows. Ugh, such an ugly hack." + :around #'org-fast-tag-selection (if +popup-mode (cl-letf* ((old-fit-buffer-fn (symbol-function 'org-fit-window-to-buffer)) ((symbol-function 'org-fit-window-to-buffer) @@ -268,31 +264,31 @@ Ugh, such an ugly hack." (funcall old-fit-buffer-fn window max-height min-height shrink-only)))) (apply orig-fn args)) (apply orig-fn args))) - (advice-add #'org-fast-tag-selection :around #'+popup*fix-tags-window) - (defun +popup*org-src-pop-to-buffer (orig-fn buffer context) + (defadvice! +popup--org-src-pop-to-buffer-a (orig-fn buffer context) "Hand off the src-block window to the popup system by using `display-buffer' instead of switch-to-buffer-*." + :around #'org-src-switch-to-buffer (if (and (eq org-src-window-setup 'popup-window) +popup-mode) (pop-to-buffer buffer) (funcall orig-fn buffer context))) - (advice-add #'org-src-switch-to-buffer :around #'+popup*org-src-pop-to-buffer) (setq org-src-window-setup 'popup-window) ;; Ensure todo, agenda, and other minor popups are delegated to the popup system. - (defun +popup*org-pop-to-buffer (orig-fn buf &optional norecord) + (defadvice! +popup--org-pop-to-buffer-a (orig-fn buf &optional norecord) "Use `pop-to-buffer' instead of `switch-to-buffer' to open buffer.'" + :around #'org-switch-to-buffer-other-window (if +popup-mode (pop-to-buffer buf nil norecord) (funcall orig-fn buf norecord))) - (advice-add #'org-switch-to-buffer-other-window :around #'+popup*org-pop-to-buffer) ;; `org-agenda' (setq org-agenda-window-setup 'popup-window org-agenda-restore-windows-after-quit nil) ;; Don't monopolize the frame! - (defun +popup*org-agenda-suppress-delete-other-windows (orig-fn &rest args) + (defadvice! +popup--org-agenda-suppress-delete-other-windows-a (orig-fn &rest args) + :around #'org-agenda-prepare-window (cond ((not +popup-mode) (apply orig-fn args)) ((eq org-agenda-window-setup 'popup-window) @@ -307,30 +303,28 @@ instead of switch-to-buffer-*." (symbol-function 'ignore))) (apply orig-fn args)))) ((with-popup-rules! nil - (apply orig-fn args))))) - (advice-add #'org-agenda-prepare-window :around #'+popup*org-agenda-suppress-delete-other-windows)) + (apply orig-fn args)))))) ;;;###package persp-mode -(progn - (defun +popup*persp-mode-restore-popups (&rest _) - "Restore popup windows when loading a perspective from file." - (dolist (window (window-list)) - (when (+popup-parameter 'popup window) - (+popup--init window nil)))) - (advice-add #'persp-load-state-from-file :after #'+popup*persp-mode-restore-popups)) +(defadvice! +popup--persp-mode-restore-popups-a (&rest _) + "Restore popup windows when loading a perspective from file." + :after #'persp-load-state-from-file + (dolist (window (window-list)) + (when (+popup-parameter 'popup window) + (+popup--init window nil)))) ;;;###package pdf-tools (after! pdf-tools (setq tablist-context-window-display-action - '((+popup-display-buffer-stacked-side-window) + '((+popup-display-buffer-stacked-side-window-fn) (side . left) (slot . 2) (window-height . 0.3) (inhibit-same-window . t)) pdf-annot-list-display-buffer-action - '((+popup-display-buffer-stacked-side-window) + '((+popup-display-buffer-stacked-side-window-fn) (side . left) (slot . 3) (inhibit-same-window . t))) @@ -340,18 +334,18 @@ instead of switch-to-buffer-*." ;;;###package profiler -(defun doom*profiler-report-find-entry-in-other-window (orig-fn function) +(defadvice! +popup--profiler-report-find-entry-in-other-window-a (orig-fn function) + :around #'profiler-report-find-entry (cl-letf (((symbol-function 'find-function) (symbol-function 'find-function-other-window))) (funcall orig-fn function))) -(advice-add #'profiler-report-find-entry :around #'doom*profiler-report-find-entry-in-other-window) ;;;###package wgrep (progn ;; close the popup after you're done with a wgrep buffer - (advice-add #'wgrep-abort-changes :after #'+popup*close) - (advice-add #'wgrep-finish-edit :after #'+popup*close)) + (advice-add #'wgrep-abort-changes :after #'+popup-close-a) + (advice-add #'wgrep-finish-edit :after #'+popup-close-a)) ;;;###package which-key @@ -364,23 +358,19 @@ instead of switch-to-buffer-*." (lambda (act-popup-dim) (cl-letf (((symbol-function 'display-buffer-in-side-window) (lambda (buffer alist) - (+popup-display-buffer-stacked-side-window + (+popup-display-buffer-stacked-side-window-fn buffer (append '((vslot . -9999)) alist))))) (which-key--show-buffer-side-window act-popup-dim)))))) ;;;###package windmove -(progn - ;; Users should be able to hop into popups easily, but Elisp shouldn't. - (defun doom*ignore-window-parameters (orig-fn &rest args) - "Allow *interactive* window moving commands to traverse popups." - (cl-letf (((symbol-function #'windmove-find-other-window) - (lambda (dir &optional arg window) - (window-in-direction - (pcase dir (`up 'above) (`down 'below) (_ dir)) - window (bound-and-true-p +popup-mode) arg windmove-wrap-around t)))) - (apply orig-fn args))) - (advice-add #'windmove-up :around #'doom*ignore-window-parameters) - (advice-add #'windmove-down :around #'doom*ignore-window-parameters) - (advice-add #'windmove-left :around #'doom*ignore-window-parameters) - (advice-add #'windmove-right :around #'doom*ignore-window-parameters)) +;; Users should be able to hop into popups easily, but Elisp shouldn't. +(defadvice! +popup--ignore-window-parameters-a (orig-fn &rest args) + "Allow *interactive* window moving commands to traverse popups." + :around '(windmove-up windmove-down windmove-left windmove-right) + (cl-letf (((symbol-function #'windmove-find-other-window) + (lambda (dir &optional arg window) + (window-in-direction + (pcase dir (`up 'above) (`down 'below) (_ dir)) + window (bound-and-true-p +popup-mode) arg windmove-wrap-around t)))) + (apply orig-fn args))) diff --git a/modules/ui/popup/README.org b/modules/ui/popup/README.org index 1cecf3885..3a8128983 100644 --- a/modules/ui/popup/README.org +++ b/modules/ui/popup/README.org @@ -88,7 +88,7 @@ The mode-line is hidden in popups, by default. To disable this, you can either: #+BEGIN_SRC emacs-lisp ;; in ~/.doom.d/config.el - (remove-hook '+popup-buffer-mode-hook #'+popup|set-modeline-on-enable) + (remove-hook '+popup-buffer-mode-hook #'+popup-set-modeline-on-enable-h) #+END_SRC * Appendix @@ -112,10 +112,10 @@ The mode-line is hidden in popups, by default. To disable this, you can either: + ~without-popups!~ + ~save-popups!~ + Hooks - + ~+popup|adjust-fringes~ + + ~+popup-adjust-fringes-h~ + ~+popup|set-modeline~ - + ~+popup|close-on-escape~ - + ~+popup|cleanup-rules~ + + ~+popup-close-on-escape-h~ + + ~+popup-cleanup-rules-h~ + Minor modes + ~+popup-mode~ + ~+popup-buffer-mode~ diff --git a/modules/ui/popup/autoload/popup.el b/modules/ui/popup/autoload/popup.el index 67f3b087c..cab480ebd 100644 --- a/modules/ui/popup/autoload/popup.el +++ b/modules/ui/popup/autoload/popup.el @@ -15,7 +15,7 @@ the buffer is visible, then set another timer and try again later." (when (buffer-live-p buffer) (let ((inhibit-quit t) - (kill-buffer-hook (remq '+popup|kill-buffer-hook kill-buffer-hook))) + (kill-buffer-hook (remq '+popup-kill-buffer-hook-h kill-buffer-hook))) (cond ((get-buffer-window buffer t) (with-current-buffer buffer (setq +popup--timer @@ -68,7 +68,7 @@ the buffer is visible, then set another timer and try again later." (signal 'wrong-type-argument (list 'integerp ttl))) ((= ttl 0) (+popup--kill-buffer buffer 0)) - ((add-hook 'kill-buffer-hook #'+popup|kill-buffer-hook nil t) + ((add-hook 'kill-buffer-hook #'+popup-kill-buffer-hook-h nil t) (setq +popup--timer (run-at-time ttl nil #'+popup--kill-buffer buffer ttl)))))))))) @@ -102,7 +102,20 @@ the buffer is visible, then set another timer and try again later." (setf (alist-get param alist) size)) (setf (alist-get 'window-parameters alist) parameters) - alist))) + ;; Fixes #1305: addresses an edge case where a popup with a :size, :width + ;; or :height greater than the current frame's dimensions causes + ;; hanging/freezing (a bug in Emacs' `display-buffer' API perhaps?) + (let ((width (cdr (assq 'window-width alist))) + (height (cdr (assq 'window-height alist)))) + (setf (alist-get 'window-width alist) + (if (numberp width) + (min width (frame-width)) + width)) + (setf (alist-get 'window-height alist) + (if (numberp height) + (min height (frame-height)) + height)) + alist)))) (defun +popup--split-window (window size side) "Ensure a non-dedicated/popup window is selected when splitting a window." @@ -233,14 +246,14 @@ Uses `shrink-window-if-larger-than-buffer'." ;; Hooks ;;;###autoload -(defun +popup|adjust-fringes () +(defun +popup-adjust-fringes-h () "Hides the fringe in popup windows, restoring them if `+popup-buffer-mode' is disabled." (let ((f (if (bound-and-true-p +popup-buffer-mode) 0))) (set-window-fringes nil f f fringes-outside-margins))) ;;;###autoload -(defun +popup|adjust-margins () +(defun +popup-adjust-margins-h () "Creates padding for the popup window determined by `+popup-margin-width', restoring it if `+popup-buffer-mode' is disabled." (when +popup-margin-width @@ -250,7 +263,7 @@ restoring it if `+popup-buffer-mode' is disabled." (defvar hide-mode-line-format) ;;;###autoload -(defun +popup|set-modeline-on-enable () +(defun +popup-set-modeline-on-enable-h () "Don't show modeline in popup windows without a `modeline' window-parameter. Possible values for this parameter are: @@ -271,17 +284,17 @@ Any non-nil value besides the above will be used as the raw value for (funcall modeline) modeline))) (hide-mode-line-mode +1))))))) -(put '+popup|set-modeline-on-enable 'permanent-local-hook t) +(put '+popup-set-modeline-on-enable-h 'permanent-local-hook t) ;;;###autoload -(defun +popup|unset-modeline-on-disable () +(defun +popup-unset-modeline-on-disable-h () "Restore the modeline when `+popup-buffer-mode' is deactivated." (when (and (not (bound-and-true-p +popup-buffer-mode)) (bound-and-true-p hide-mode-line-mode)) (hide-mode-line-mode -1))) ;;;###autoload -(defun +popup|close-on-escape () +(defun +popup-close-on-escape-h () "If called inside a popup, try to close that popup window (see `+popup/close'). If called outside, try to close all popup windows (see `+popup/close-all')." @@ -290,7 +303,7 @@ Any non-nil value besides the above will be used as the raw value for (+popup/close-all))) ;;;###autoload -(defun +popup|cleanup-rules () +(defun +popup-cleanup-rules-h () "Cleans up any duplicate popup rules." (interactive) (setq +popup--display-buffer-alist @@ -300,7 +313,7 @@ Any non-nil value besides the above will be used as the raw value for (setq display-buffer-alist +popup--display-buffer-alist))) ;;;###autoload -(defun +popup|kill-buffer-hook () +(defun +popup-kill-buffer-hook-h () "TODO" (when-let (window (get-buffer-window)) (when (+popup-window-p window) @@ -319,7 +332,7 @@ Any non-nil value besides the above will be used as the raw value for "Open this buffer in a popup window." (interactive) (let ((+popup-default-display-buffer-actions - '(+popup-display-buffer-stacked-side-window)) + '(+popup-display-buffer-stacked-side-window-fn)) (display-buffer-alist +popup--display-buffer-alist) (buffer (current-buffer))) (push (+popup--make "." +popup-defaults) display-buffer-alist) @@ -425,21 +438,21 @@ the message buffer in a popup window." ;; -;; Advice +;;; Advice ;;;###autoload -(defun +popup*close (&rest _) +(defun +popup-close-a (&rest _) "TODO" (+popup/close nil t)) ;;;###autoload -(defun +popup*save (orig-fn &rest args) +(defun +popup-save-a (orig-fn &rest args) "Sets aside all popups before executing the original function, usually to prevent the popup(s) from messing up the UI (or vice versa)." (save-popups! (apply orig-fn args))) ;;;###autoload -(defun +popup-display-buffer-fullframe (buffer alist) +(defun +popup-display-buffer-fullframe-fn (buffer alist) "Displays the buffer fullscreen." (let ((wconf (current-window-configuration))) (when-let (window (or (display-buffer-reuse-window buffer alist) @@ -452,7 +465,7 @@ prevent the popup(s) from messing up the UI (or vice versa)." window))) ;;;###autoload -(defun +popup-display-buffer-stacked-side-window (buffer alist) +(defun +popup-display-buffer-stacked-side-window-fn (buffer alist) "A `display-buffer' action that serves as an alternative to `display-buffer-in-side-window', but allows for stacking popups with the `vslot' alist entry. @@ -587,16 +600,17 @@ Accepts the same arguments as `display-buffer-in-side-window'. You must set ;; Emacs backwards compatibility (unless EMACS27+ - (defun +popup*set-window-dedicated (window) - "Ensure `window--dispaly-buffer' respects `display-buffer-mark-dedicated'. + (defadvice! +popup--set-window-dedicated-a (window) + "Ensure `window--display-buffer' respects `display-buffer-mark-dedicated'. This was not so until recent Emacs 27 builds, where it causes breaking errors. This advice ensures backwards compatibility for Emacs <= 26 users." + :filter-return #'window--display-buffer (when (and (windowp window) display-buffer-mark-dedicated) (set-window-dedicated-p window display-buffer-mark-dedicated)) - window) - (advice-add #'window--display-buffer :filter-return #'+popup*set-window-dedicated)) + window)) +;; DEPRECATED (unless EMACS26+ (defvar window-sides-reversed nil) diff --git a/modules/ui/popup/autoload/settings.el b/modules/ui/popup/autoload/settings.el index ad4bead4f..986e13b44 100644 --- a/modules/ui/popup/autoload/settings.el +++ b/modules/ui/popup/autoload/settings.el @@ -74,7 +74,7 @@ PLIST can be made up of any of the following properties: :side 'bottom|'top|'left|'right Which side of the frame to open the popup on. This is only respected if - `+popup-display-buffer-stacked-side-window' or `display-buffer-in-side-window' + `+popup-display-buffer-stacked-side-window-fn' or `display-buffer-in-side-window' is in :actions or `+popup-default-display-buffer-actions'. :size/:width/:height FLOAT|INT|FN @@ -93,7 +93,7 @@ PLIST can be made up of any of the following properties: :slot/:vslot INT (This only applies to popups with a :side and only if :actions is blank or - contains the `+popup-display-buffer-stacked-side-window' action) These control + contains the `+popup-display-buffer-stacked-side-window-fn' action) These control how multiple popups are laid out. INT can be any integer, positive and negative. diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index 185973ea0..2d605ebb0 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -5,7 +5,7 @@ Modifying this has no effect, unless done before ui/popup loads.") (defvar +popup-default-display-buffer-actions - '(+popup-display-buffer-stacked-side-window) + '(+popup-display-buffer-stacked-side-window-fn) "The functions to use to display the popup buffer.") (defvar +popup-default-alist @@ -55,17 +55,17 @@ adjustment.") :global t :keymap +popup-mode-map (cond (+popup-mode - (add-hook 'doom-escape-hook #'+popup|close-on-escape t) + (add-hook 'doom-escape-hook #'+popup-close-on-escape-h 'append) (setq +popup--old-display-buffer-alist display-buffer-alist display-buffer-alist +popup--display-buffer-alist window--sides-inhibit-check t) (dolist (prop +popup-window-parameters) (push (cons prop 'writable) window-persistent-parameters))) (t - (remove-hook 'doom-escape-hook #'+popup|close-on-escape) + (remove-hook 'doom-escape-hook #'+popup-close-on-escape-h) (setq display-buffer-alist +popup--old-display-buffer-alist window--sides-inhibit-check nil) - (+popup|cleanup-rules) + (+popup-cleanup-rules-h) (dolist (prop +popup-window-parameters) (delq (assq prop window-persistent-parameters) window-persistent-parameters))))) @@ -78,16 +78,17 @@ that window has been changed or closed." :init-value nil :keymap +popup-buffer-mode-map (if (not +popup-buffer-mode) - (remove-hook 'after-change-major-mode-hook #'+popup|set-modeline-on-enable t) - (add-hook 'after-change-major-mode-hook #'+popup|set-modeline-on-enable nil t) + (remove-hook 'after-change-major-mode-hook #'+popup-set-modeline-on-enable-h t) + (add-hook 'after-change-major-mode-hook #'+popup-set-modeline-on-enable-h + nil 'local) (when (timerp +popup--timer) - (remove-hook 'kill-buffer-hook #'+popup|kill-buffer-hook t) + (remove-hook 'kill-buffer-hook #'+popup-kill-buffer-hook-h t) (cancel-timer +popup--timer) (setq +popup--timer nil)))) (put '+popup-buffer-mode 'permanent-local t) (put '+popup-buffer-mode 'permanent-local-hook t) -(put '+popup|set-modeline-on-enable 'permanent-local-hook t) +(put '+popup-set-modeline-on-enable-h 'permanent-local-hook t) ;; @@ -140,7 +141,7 @@ prevent the popup(s) from messing up the UI (or vice versa)." :vslot -4 :size 0.35 :autosave t :select t :modeline t :quit nil :ttl t) ("^\\*doom:\\(?:v?term\\|eshell\\)-popup" ; editing buffers (interaction required) :vslot -5 :size 0.35 :select t :modeline t :quit nil :ttl nil) - ("^\\*Man " + ("^\\*\\(?:Wo\\)?Man " :vslot -6 :size 0.45 :select t :quit t :ttl 0) ("^\\*Calc" :vslot -7 :side bottom :size 0.4 :select t :quit nil :ttl 0) @@ -155,17 +156,18 @@ prevent the popup(s) from messing up the UI (or vice versa)." :vslot -11 :size 0.35 :select t) ("^\\*info\\*$" ; `Info-mode' :slot 2 :vslot 2 :size 0.45 :select t))) - '(("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil) + '(("^\\*Warnings" :vslot 99 :size 0.25) + ("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil) ("^\\*CPU-Profiler-Report " :side bottom :vslot 100 :slot 1 :height 0.4 :width 0.5 :quit nil) ("^\\*Memory-Profiler-Report " :side bottom :vslot 100 :slot 2 :height 0.4 :width 0.5 :quit nil))) -(add-hook 'doom-init-ui-hook #'+popup-mode :append) +(add-hook 'doom-init-ui-hook #'+popup-mode 'append) (add-hook! '+popup-buffer-mode-hook - #'(+popup|adjust-fringes - +popup|adjust-margins - +popup|set-modeline-on-enable - +popup|unset-modeline-on-disable)) + #'+popup-adjust-fringes-h + #'+popup-adjust-margins-h + #'+popup-set-modeline-on-enable-h + #'+popup-unset-modeline-on-disable-h) ;; diff --git a/modules/ui/popup/test/test-popup.el b/modules/ui/popup/test/test-popup.el index 7a0b2327c..945f5a937 100644 --- a/modules/ui/popup/test/test-popup.el +++ b/modules/ui/popup/test/test-popup.el @@ -21,7 +21,7 @@ (before-each (setq display-buffer-alist nil +popup--display-buffer-alist nil - +popup-default-display-buffer-actions '(+popup-display-buffer-stacked-side-window) + +popup-default-display-buffer-actions '(+popup-display-buffer-stacked-side-window-fn) +popup-defaults '(:side bottom :select ignore :ttl nil :slot 1 :vslot 1))) (after-each (set-window-configuration wconf)) diff --git a/modules/ui/pretty-code/+hasklig.el b/modules/ui/pretty-code/+hasklig.el new file mode 100644 index 000000000..ad4dd0580 --- /dev/null +++ b/modules/ui/pretty-code/+hasklig.el @@ -0,0 +1,58 @@ +;;; ui/pretty-code/+hasklig.el -*- lexical-binding: t; -*- + +(defvar +pretty-code-hasklig-font-name "Hasklig" + "Name of the hasklig ligature font.") + +(defvar +pretty-code-hasklig-font-ligatures + '(("&&" . #Xe100) + ("***" . #Xe101) + ("*>" . #Xe102) + ("\\\\" . #Xe103) + ("||" . #Xe104) + ("|>" . #Xe105) + ("::" . #Xe106) + ("==" . #Xe107) + ("===" . #Xe108) + ("==>" . #Xe109) + ("=>" . #Xe10a) + ("=<<" . #Xe10b) + ("!!" . #Xe10c) + (">>" . #Xe10d) + (">>=" . #Xe10e) + (">>>" . #Xe10f) + (">>-" . #Xe110) + (">-" . #Xe111) + ("->" . #Xe112) + ("-<" . #Xe113) + ("-<<" . #Xe114) + ("<*" . #Xe115) + ("<*>" . #Xe116) + ("<|" . #Xe117) + ("<|>" . #Xe118) + ("<$>" . #Xe119) + ("<>" . #Xe11a) + ("<-" . #Xe11b) + ("<<" . #Xe11c) + ("<<<" . #Xe11d) + ("<+>" . #Xe11e) + (".." . #Xe11f) + ("..." . #Xe120) + ("++" . #Xe121) + ("+++" . #Xe122) + ("/=" . #Xe123) + (":::" . #Xe124) + (">=>" . #Xe125) + ("->>" . #Xe126) + ("<=>" . #Xe127) + ("<=<" . #Xe128) + ("<->" . #Xe129))) + + +(defun +pretty-code|setup-hasklig-ligatures () + (set-fontset-font t '(#Xe100 . #Xe129) +pretty-code-hasklig-font-name) + (setq-default prettify-symbols-alist + (append prettify-symbols-alist + (mapcar #'+pretty-code--correct-symbol-bounds + +pretty-code-hasklig-font-ligatures)))) + +(add-hook 'doom-init-ui-hook #'+pretty-code|setup-hasklig-ligatures) diff --git a/modules/ui/pretty-code/+iosevka.el b/modules/ui/pretty-code/+iosevka.el index f2ae330fa..e076b34ce 100644 --- a/modules/ui/pretty-code/+iosevka.el +++ b/modules/ui/pretty-code/+iosevka.el @@ -224,7 +224,7 @@ "Defines the character mappings for ligatures for Iosevka.") (defun +pretty-code|setup-iosevka-ligatures () - (set-fontset-font t '(#Xe100 . #Xe16f) +pretty-code-iosevka-font-name) + (set-fontset-font t '(#Xe100 . #Xe1cc) +pretty-code-iosevka-font-name) (setq-default prettify-symbols-alist (append prettify-symbols-alist +pretty-code-iosevka-font-ligatures))) diff --git a/modules/ui/pretty-code/config.el b/modules/ui/pretty-code/config.el index 4583f0863..da9b5480f 100644 --- a/modules/ui/pretty-code/config.el +++ b/modules/ui/pretty-code/config.el @@ -4,6 +4,8 @@ (load! "+fira")) ((featurep! +iosevka) (load! "+iosevka")) + ((featurep! +hasklig) + (load! "+hasklig")) ((featurep! +pragmata-pro) (load! "+pragmata-pro"))) diff --git a/modules/ui/tabbar/README.org b/modules/ui/tabbar/README.org deleted file mode 100644 index 05e631886..000000000 --- a/modules/ui/tabbar/README.org +++ /dev/null @@ -1,7 +0,0 @@ -#+TITLE: :ui tabbar - -This module adds an Atom-esque tab bar to the Emacs UI. - -I don't recommend you use this module. It is here for reference, is unstable and -may be removed some day. I find ivy, helm or even ~buffer-menu~ to be better -suited for buffer management. diff --git a/modules/ui/tabbar/config.el b/modules/ui/tabbar/config.el deleted file mode 100644 index 40213b3bf..000000000 --- a/modules/ui/tabbar/config.el +++ /dev/null @@ -1,37 +0,0 @@ -;;; ui/tabbar/config.el -*- lexical-binding: t; -*- - -(def-package! centaur-tabs - :after-call (after-find-file dired-initial-position-hook) - :config - (setq centaur-tabs-height 28 - centaur-tabs-set-bar 'left - centaur-tabs-set-modified-marker t) - - (defun +tabbar|init-frames () - (dolist (frame (frame-list)) - (if (not centaur-tabs-mode) - (set-frame-parameter frame 'buffer-predicate (frame-parameter frame 'old-buffer-predicate)) - (set-frame-parameter frame 'old-buffer-predicate (frame-parameter frame 'buffer-predicate)) - (set-frame-parameter frame 'buffer-predicate #'+tabbar-buffer-predicate)))) - (add-hook 'centaur-tabs-mode-hook #'+tabbar|init-frames) - - (setq centaur-tabs-buffer-list-function #'+tabbar-window-buffer-list - centaur-tabs-buffer-groups-function #'+tabbar-buffer-groups) - - (advice-add #'centaur-tabs-buffer-close-tab :override #'+tabbar*kill-tab-maybe) - (advice-add #'bury-buffer :around #'+tabbar*bury-buffer) - (advice-add #'kill-current-buffer :before #'+tabbar*kill-current-buffer) - (add-hook 'doom-switch-buffer-hook #'+tabbar|add-buffer) - (add-hook 'doom-switch-window-hook #'+tabbar|new-window) - - (add-hook '+doom-dashboard-mode-hook #'centaur-tabs-local-mode) - - (map! (:map centaur-tabs-mode-map - [remap delete-window] #'+tabbar/close-tab-or-window - [remap +workspace/close-window-or-workspace] #'+tabbar/close-tab-or-window) - (:after persp-mode - :map persp-mode-map - [remap delete-window] #'+tabbar/close-tab-or-window - [remap +workspace/close-window-or-workspace] #'+tabbar/close-tab-or-window)) - - (centaur-tabs-mode +1)) diff --git a/modules/ui/tabs/README.org b/modules/ui/tabs/README.org new file mode 100644 index 000000000..7d4cb95f6 --- /dev/null +++ b/modules/ui/tabs/README.org @@ -0,0 +1,18 @@ +#+TITLE: ui/tabs +#+DATE: July 12, 2019 +#+SINCE: v2.1 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + +* Description +This module adds an Atom-esque tab bar to the Emacs UI. + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/ema2159/centaur-tabs][centaur-tabs]] diff --git a/modules/ui/tabbar/autoload.el b/modules/ui/tabs/autoload.el similarity index 62% rename from modules/ui/tabbar/autoload.el rename to modules/ui/tabs/autoload.el index 81935d0ee..26a131ca0 100644 --- a/modules/ui/tabbar/autoload.el +++ b/modules/ui/tabs/autoload.el @@ -1,21 +1,21 @@ -;;; ui/tabbar/autoload.el -*- lexical-binding: t; -*- +;;; ui/tabs/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(defun +tabbar-buffer-predicate (buffer) +(defun +tabs-buffer-predicate (buffer) "TODO" - (or (memq buffer (window-parameter nil 'tabbar-buffers)) + (or (memq buffer (window-parameter nil 'tab-buffers)) (eq buffer (doom-fallback-buffer)))) ;;;###autoload -(defun +tabbar-window-tab-list () - (+tabbar-window-buffer-list)) +(defun +tabs-window-tab-list () + (+tabs-window-buffer-list-fn)) ;;;###autoload -(defun +tabbar-window-buffer-list () - (cl-delete-if-not #'buffer-live-p (window-parameter nil 'tabbar-buffers))) +(defun +tabs-window-buffer-list-fn () + (cl-delete-if-not #'buffer-live-p (window-parameter nil 'tab-buffers))) ;;;###autoload -(defun +tabbar-buffer-groups () +(defun +tabs-buffer-groups-fn () (list (cond ((or (string-equal "*" (substring (buffer-name) 0 1)) (memq major-mode '(magit-process-mode @@ -38,11 +38,11 @@ ;;; Commands ;;;###autoload -(defun +tabbar/close-tab-or-window () +(defun +tabs/close-tab-or-window () "TODO" (interactive) (call-interactively - (cond ((cdr (window-parameter nil 'tabbar-buffers)) + (cond ((cdr (window-parameter nil 'tab-buffers)) #'kill-current-buffer) ((fboundp '+workspace/close-window-or-workspace) #'+workspace/close-window-or-workspace) @@ -53,21 +53,21 @@ ;;; Advice ;;;###autoload -(defun +tabbar*kill-current-buffer (&rest _) - (+tabbar|remove-buffer)) +(defun +tabs-kill-current-buffer-a (&rest _) + (+tabs|remove-buffer)) ;;;###autoload -(defun +tabbar*bury-buffer (orig-fn &rest args) +(defun +tabs-bury-buffer-a (orig-fn &rest args) (if centaur-tabs-mode (let ((b (current-buffer))) (apply orig-fn args) (unless (eq b (current-buffer)) (with-current-buffer b - (+tabbar|remove-buffer)))) + (+tabs|remove-buffer)))) (apply orig-fn args))) ;;;###autoload -(defun +tabbar*kill-tab-maybe (tab) +(defun +tabs-kill-tab-maybe-a (tab) (let ((buffer (centaur-tabs-tab-value tab))) (with-current-buffer buffer ;; `kill-current-buffer' is advised not to kill buffers visible in another @@ -80,24 +80,25 @@ ;;; Hooks ;;;###autoload -(defun +tabbar|add-buffer () +(defun +tabs-add-buffer-h () (when (and centaur-tabs-mode (doom-real-buffer-p (current-buffer))) (let* ((this-buf (current-buffer)) - (buffers (window-parameter nil 'tabbar-buffers))) + (buffers (window-parameter nil 'tab-buffers))) (cl-pushnew this-buf buffers) - (add-hook 'kill-buffer-hook #'+tabbar|remove-buffer nil t) - (set-window-parameter nil 'tabbar-buffers buffers)))) + (add-hook 'kill-buffer-hook #'+tabs|remove-buffer nil t) + (set-window-parameter nil 'tab-buffers buffers)))) ;;;###autoload -(defun +tabbar|remove-buffer () +(defun +tabs|remove-buffer () (when centaur-tabs-mode (set-window-parameter nil - 'tabbar-buffers (delete (current-buffer) (window-parameter nil 'tabbar-buffers))))) + 'tab-buffers (delete (current-buffer) + (window-parameter nil 'tab-buffers))))) ;;;###autoload -(defun +tabbar|new-window () +(defun +tabs-new-window-h () (when centaur-tabs-mode - (unless (window-parameter nil 'tabbar-buffers) - (+tabbar|add-buffer)))) + (unless (window-parameter nil 'tab-buffers) + (+tabs-add-buffer-h)))) diff --git a/modules/ui/tabs/config.el b/modules/ui/tabs/config.el new file mode 100644 index 000000000..c9e753c6e --- /dev/null +++ b/modules/ui/tabs/config.el @@ -0,0 +1,40 @@ +;;; ui/tabs/config.el -*- lexical-binding: t; -*- + +(use-package! centaur-tabs + :after-call (after-find-file dired-initial-position-hook) + :init + (setq centaur-tabs-height 28 + centaur-tabs-set-bar 'left + centaur-tabs-set-modified-marker t) + + :config + (add-hook! 'centaur-tabs-mode-hook + (defun +tabs-init-frames-h () + (dolist (frame (frame-list)) + (if (not centaur-tabs-mode) + (set-frame-parameter frame 'buffer-predicate (frame-parameter frame 'old-buffer-predicate)) + (set-frame-parameter frame 'old-buffer-predicate (frame-parameter frame 'buffer-predicate)) + (set-frame-parameter frame 'buffer-predicate #'+tabs-buffer-predicate))))) + + (add-to-list 'window-persistent-parameters '(tab-buffers . writable)) + + (setq centaur-tabs-buffer-list-function #'+tabs-window-buffer-list-fn + centaur-tabs-buffer-groups-function #'+tabs-buffer-groups-fn) + + (advice-add #'centaur-tabs-buffer-close-tab :override #'+tabs-kill-tab-maybe-a) + (advice-add #'bury-buffer :around #'+tabs-bury-buffer-a) + (advice-add #'kill-current-buffer :before #'+tabs-kill-current-buffer-a) + (add-hook 'doom-switch-buffer-hook #'+tabs-add-buffer-h) + (add-hook 'doom-switch-window-hook #'+tabs-new-window-h) + + (add-hook '+doom-dashboard-mode-hook #'centaur-tabs-local-mode) + + (map! (:map centaur-tabs-mode-map + [remap delete-window] #'+tabs/close-tab-or-window + [remap +workspace/close-window-or-workspace] #'+tabs/close-tab-or-window) + (:after persp-mode + :map persp-mode-map + [remap delete-window] #'+tabs/close-tab-or-window + [remap +workspace/close-window-or-workspace] #'+tabs/close-tab-or-window)) + + (centaur-tabs-mode +1)) diff --git a/modules/ui/tabbar/packages.el b/modules/ui/tabs/packages.el similarity index 68% rename from modules/ui/tabbar/packages.el rename to modules/ui/tabs/packages.el index 294f434f3..1bec6d791 100644 --- a/modules/ui/tabbar/packages.el +++ b/modules/ui/tabs/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- -;;; ui/tabbar/packages.el +;;; ui/tabs/packages.el (package! centaur-tabs) diff --git a/modules/ui/treemacs/config.el b/modules/ui/treemacs/config.el index f101a227e..2fd6167b1 100644 --- a/modules/ui/treemacs/config.el +++ b/modules/ui/treemacs/config.el @@ -6,6 +6,7 @@ treemacs-persist-file (concat doom-cache-dir "treemacs-persist") treemacs-last-error-persist-file (concat doom-cache-dir "treemacs-last-error-persist")) + (after! treemacs-persistence ;; This variable is defined with defconst, so we must wait to change it until ;; it has loaded. @@ -25,10 +26,10 @@ (treemacs-follow-mode -1) (after! ace-window - (setq aw-ignored-buffers (delq 'treemacs-mode aw-ignored-buffers)))) + (delq! 'treemacs-mode aw-ignored-buffers))) -(def-package! treemacs-evil +(use-package! treemacs-evil :when (featurep! :editor evil +everywhere) :after treemacs :config @@ -38,9 +39,9 @@ "TAB" #'treemacs-TAB-action)) -(def-package! treemacs-projectile +(use-package! treemacs-projectile :after treemacs) -(def-package! treemacs-magit +(use-package! treemacs-magit :when (featurep! :tools magit) :after treemacs magit) diff --git a/modules/ui/unicode/autoload.el b/modules/ui/unicode/autoload.el index 0be6bd27e..279176d97 100644 --- a/modules/ui/unicode/autoload.el +++ b/modules/ui/unicode/autoload.el @@ -1,20 +1,18 @@ ;;; ui/unicode/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(add-hook 'doom-init-ui-hook #'+unicode|init-fonts) - -;;;###autoload -(defun +unicode|init-fonts () - "Set up `unicode-fonts' to eventually run; accomodating the daemon, if +(add-hook! 'doom-init-ui-hook + (defun +unicode-init-fonts-h () + "Set up `unicode-fonts' to eventually run; accomodating the daemon, if necessary." - (setq-default bidi-display-reordering t - doom-unicode-font nil) - (if initial-window-system - (+unicode|setup-fonts (selected-frame)) - (add-hook 'after-make-frame-functions #'+unicode|setup-fonts))) + (setq-default bidi-display-reordering t + doom-unicode-font nil) + (if initial-window-system + (+unicode-setup-fonts-h (selected-frame)) + (add-hook 'after-make-frame-functions #'+unicode-setup-fonts-h)))) ;;;###autoload -(defun +unicode|setup-fonts (&optional frame) +(defun +unicode-setup-fonts-h (&optional frame) "Initialize `unicode-fonts', if in a GUI session." (when (and frame (display-graphic-p frame)) (with-selected-frame frame diff --git a/modules/ui/vc-gutter/autoload.el b/modules/ui/vc-gutter/autoload.el index bac2c9e90..741080d86 100644 --- a/modules/ui/vc-gutter/autoload.el +++ b/modules/ui/vc-gutter/autoload.el @@ -1,7 +1,8 @@ ;;; ui/vc-gutter/autoload.el -*- lexical-binding: t; -*- +;;;###if (featurep! :ui hydra) -;;;###autoload (autoload '+vc-gutter-hydra/body "ui/vc-gutter/autoload" nil t) -(defhydra +vc-gutter-hydra +;;;###autoload (autoload '+vc/gutter-hydra/body "ui/vc-gutter/autoload" nil t) +(defhydra +vc/gutter-hydra (:body-pre (git-gutter-mode 1) :hint nil) " [git gutter] diff --git a/modules/ui/vc-gutter/config.el b/modules/ui/vc-gutter/config.el index 410059889..d65a4b262 100644 --- a/modules/ui/vc-gutter/config.el +++ b/modules/ui/vc-gutter/config.el @@ -19,37 +19,46 @@ to the right fringe.") ;; ;; Packages -(def-package! git-gutter - :commands (git-gutter:revert-hunk git-gutter:stage-hunk) +(use-package! git-gutter + :commands git-gutter:revert-hunk git-gutter:stage-hunk :init - (defun +vc-gutter|init-maybe () - "Enable `git-gutter-mode' in the current buffer. + (add-hook! 'find-file-hook + (defun +vc-gutter-init-maybe-h () + "Enable `git-gutter-mode' in the current buffer. If the buffer doesn't represent an existing file, `git-gutter-mode's activation is deferred until the file is saved. Respects `git-gutter:disabled-modes'." - (when (or +vc-gutter-in-remote-files - (not (file-remote-p (or buffer-file-name default-directory)))) - (if (not buffer-file-name) - (add-hook 'after-save-hook #'+vc-gutter|init-maybe nil t) - (when (and (vc-backend buffer-file-name) - (progn - (require 'git-gutter) - (not (memq major-mode git-gutter:disabled-modes)))) - (if (and (display-graphic-p) - (require 'git-gutter-fringe nil t)) - (progn - (setq-local git-gutter:init-function #'git-gutter-fr:init) - (setq-local git-gutter:view-diff-function #'git-gutter-fr:view-diff-infos) - (setq-local git-gutter:clear-function #'git-gutter-fr:clear) - (setq-local git-gutter:window-width -1)) - (setq-local git-gutter:init-function 'nil) - (setq-local git-gutter:view-diff-function #'git-gutter:view-diff-infos) - (setq-local git-gutter:clear-function #'git-gutter:clear-diff-infos) - (setq-local git-gutter:window-width 1)) - (git-gutter-mode +1) - (remove-hook 'after-save-hook #'+vc-gutter|init-maybe t))))) - (add-hook! (text-mode prog-mode conf-mode) - #'+vc-gutter|init-maybe) + (when (or +vc-gutter-in-remote-files + (not (file-remote-p (or buffer-file-name default-directory)))) + (if (not buffer-file-name) + (add-hook 'after-save-hook #'+vc-gutter-init-maybe-h nil 'local) + (when (and (vc-backend buffer-file-name) + (progn + (require 'git-gutter) + (not (memq major-mode git-gutter:disabled-modes)))) + (if (and (display-graphic-p) + (require 'git-gutter-fringe nil t)) + (progn + (setq-local git-gutter:init-function #'git-gutter-fr:init) + (setq-local git-gutter:view-diff-function #'git-gutter-fr:view-diff-infos) + (setq-local git-gutter:clear-function #'git-gutter-fr:clear) + (setq-local git-gutter:window-width -1)) + (setq-local git-gutter:init-function 'nil) + (setq-local git-gutter:view-diff-function #'git-gutter:view-diff-infos) + (setq-local git-gutter:clear-function #'git-gutter:clear-diff-infos) + (setq-local git-gutter:window-width 1)) + (git-gutter-mode +1) + (remove-hook 'after-save-hook #'+vc-gutter-init-maybe-h 'local)))))) + + ;; Disable in Org mode, as per + ;; and + ;; . Apparently, the + ;; mode-enabling function for global minor modes gets called for new buffers + ;; while they are still in `fundamental-mode', before a major mode has been + ;; assigned. I don't know why this is the case, but adding `fundamental-mode' + ;; here fixes the issue. + (setq git-gutter:disabled-modes '(fundamental-mode image-mode pdf-view-mode)) + ;; standardize default fringe width (if (fboundp 'fringe-mode) (fringe-mode '4)) :config @@ -58,16 +67,17 @@ is deferred until the file is saved. Respects `git-gutter:disabled-modes'." ;; Update git-gutter on focus (in case I was using git externally) (add-hook 'focus-in-hook #'git-gutter:update-all-windows) - (defun +vc-gutter|update (&rest _) - "Refresh git-gutter on ESC. Return nil to prevent shadowing other + (add-hook! '(doom-escape-hook doom-switch-window-hook) :append + (defun +vc-gutter-update-h (&rest _) + "Refresh git-gutter on ESC. Return nil to prevent shadowing other `doom-escape-hook' hooks." - (when git-gutter-mode - (ignore (git-gutter)))) - (add-hook 'doom-escape-hook #'+vc-gutter|update t) - + (when (and git-gutter-mode + (not (memq this-command '(git-gutter:stage-hunk + git-gutter:revert-hunk)))) + (ignore (git-gutter))))) ;; update git-gutter when using magit commands - (advice-add #'magit-stage-file :after #'+vc-gutter|update) - (advice-add #'magit-unstage-file :after #'+vc-gutter|update)) + (advice-add #'magit-stage-file :after #'+vc-gutter-update-h) + (advice-add #'magit-unstage-file :after #'+vc-gutter-update-h)) ;; subtle diff indicators in the fringe diff --git a/modules/ui/vi-tilde-fringe/autoload.el b/modules/ui/vi-tilde-fringe/autoload.el index ab9029cfc..0d695a63f 100644 --- a/modules/ui/vi-tilde-fringe/autoload.el +++ b/modules/ui/vi-tilde-fringe/autoload.el @@ -1,4 +1,5 @@ ;;; ui/vi-tilde-fringe/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(add-hook! (prog-mode text-mode conf-mode) #'vi-tilde-fringe-mode) +(add-hook! '(prog-mode-hook text-mode-hook conf-mode-hook) + #'vi-tilde-fringe-mode) diff --git a/modules/ui/window-select/config.el b/modules/ui/window-select/config.el index cb10de02e..7ccc1cb0c 100644 --- a/modules/ui/window-select/config.el +++ b/modules/ui/window-select/config.el @@ -1,6 +1,6 @@ ;;; ui/window-select/config.el -*- lexical-binding: t; -*- -(def-package! switch-window +(use-package! switch-window :when (featurep! +switch-window) :defer t :init @@ -10,7 +10,7 @@ switch-window-qwerty-shortcuts '("a" "s" "d" "f" "g" "h" "j" "k" "l"))) -(def-package! ace-window +(use-package! ace-window :unless (featurep! +switch-window) :defer t :init @@ -21,7 +21,7 @@ aw-background t)) -(def-package! winum +(use-package! winum :when (featurep! +numbers) :after-call (doom-switch-window-hook) :config diff --git a/modules/ui/workspaces/autoload/workspaces.el b/modules/ui/workspaces/autoload/workspaces.el index 10dd85bb6..1f91854fd 100644 --- a/modules/ui/workspaces/autoload/workspaces.el +++ b/modules/ui/workspaces/autoload/workspaces.el @@ -441,7 +441,7 @@ the next." ;;; Hooks ;;;###autoload -(defun +workspaces|delete-associated-workspace (&optional frame) +(defun +workspaces-delete-associated-workspace-h (&optional frame) "Delete workspace associated with current frame. A workspace gets associated with a frame when a new frame is interactively created." @@ -453,17 +453,7 @@ created." (+workspace/delete frame-persp))))) ;;;###autoload -(defun +workspaces|cleanup-unassociated-buffers () - "Kill leftover buffers that are unassociated with any perspective." - (when persp-mode - (cl-loop for buf in (buffer-list) - unless (or (persp--buffer-in-persps buf) - (get-buffer-window buf)) - if (kill-buffer buf) - sum 1))) - -;;;###autoload -(defun +workspaces|associate-frame (frame &optional _new-frame-p) +(defun +workspaces-associate-frame-fn (frame &optional _new-frame-p) "Create a blank, new perspective and associate it with FRAME." (when persp-mode (if (not (persp-frame-list-without-daemon)) @@ -479,13 +469,13 @@ created." (defvar +workspaces--project-dir nil) ;;;###autoload -(defun +workspaces|set-project-action () +(defun +workspaces-set-project-action-fn () "A `projectile-switch-project-action' that sets the project directory for -`+workspaces|switch-to-project'." +`+workspaces-switch-to-project-h'." (setq +workspaces--project-dir default-directory)) ;;;###autoload -(defun +workspaces|switch-to-project (&optional dir) +(defun +workspaces-switch-to-project-h (&optional dir) "Creates a workspace dedicated to a new project. If one already exists, switch to it. If in the main workspace and it's empty, recycle that workspace, without renaming it. @@ -527,7 +517,7 @@ This be hooked to `projectile-after-switch-project-hook'." ;;; Advice ;;;###autoload -(defun +workspaces*autosave-real-buffers (orig-fn &rest args) +(defun +workspaces-autosave-real-buffers-a (orig-fn &rest args) "Don't autosave if no real buffers are open." (when (doom-real-buffer-list) (apply orig-fn args)) diff --git a/modules/ui/workspaces/config.el b/modules/ui/workspaces/config.el index 56431578e..9dd214f64 100644 --- a/modules/ui/workspaces/config.el +++ b/modules/ui/workspaces/config.el @@ -36,18 +36,18 @@ stored in `persp-save-dir'.") ;; ;; Packages -(def-package! persp-mode - :commands (persp-switch-to-buffer) +(use-package! persp-mode + :commands persp-switch-to-buffer :init - (defun +workspaces|init () - ;; Remove default buffer predicate so persp-mode can put in its own - (delq! 'buffer-predicate default-frame-alist 'assq) - (require 'persp-mode) - (if (daemonp) - (add-hook 'after-make-frame-functions #'persp-mode-start-and-remove-from-make-frame-hook) - (persp-mode +1))) - (unless noninteractive - (add-hook 'doom-init-modules-hook #'+workspaces|init)) + (add-hook! 'doom-init-modules-hook + (defun +workspaces-init-h () + (unless noninteractive + ;; Remove default buffer predicate so persp-mode can put in its own + (delq! 'buffer-predicate default-frame-alist 'assq) + (require 'persp-mode) + (if (daemonp) + (add-hook 'after-make-frame-functions #'persp-mode-start-and-remove-from-make-frame-hook) + (persp-mode +1))))) :config (setq persp-autokill-buffer-on-remove 'kill-weak persp-nil-hidden t @@ -59,50 +59,49 @@ stored in `persp-save-dir'.") 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) + (advice-add #'persp-asave-on-exit :around #'+workspaces-autosave-real-buffers-a) - (defun +workspaces|ensure-main-workspace (&rest _) - "Ensure the main workspace exists and the nil workspace is never active." - (when persp-mode - (let (persp-before-switch-functions) - ;; 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're in the nil perspective - (dolist (frame (frame-list)) - (when (string= (safe-persp-name (get-current-persp frame)) persp-nil-name) - (persp-frame-switch +workspaces-main frame) - ;; 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 - warnings '((window-height . shrink-window-if-larger-than-buffer)))))))))) - (add-hook 'persp-mode-hook #'+workspaces|ensure-main-workspace) - (add-hook 'persp-after-load-state-functions #'+workspaces|ensure-main-workspace) + (add-hook! '(persp-mode-hook persp-after-load-state-functions) + (defun +workspaces-ensure-main-workspace-h (&rest _) + "Ensure the main workspace exists and the nil workspace is never active." + (when persp-mode + (let (persp-before-switch-functions) + ;; 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're in the nil perspective + (dolist (frame (frame-list)) + (when (string= (safe-persp-name (get-current-persp frame)) persp-nil-name) + (persp-frame-switch +workspaces-main frame) + ;; 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 + warnings '((window-height . shrink-window-if-larger-than-buffer))))))))))) - (defun +workspaces|init-persp-mode () - (cond (persp-mode - ;; `uniquify' breaks persp-mode. It renames old buffers, which causes - ;; errors when switching between perspective (their buffers are - ;; serialized by name and persp-mode expects them to have the same - ;; name when restored). - (when uniquify-buffer-name-style - (setq +workspace--old-uniquify-style uniquify-buffer-name-style)) - (setq uniquify-buffer-name-style nil) - ;; Ensure `persp-kill-buffer-query-function' is last - (remove-hook 'kill-buffer-query-functions #'persp-kill-buffer-query-function) - (add-hook 'kill-buffer-query-functions #'persp-kill-buffer-query-function t) - ;; Restrict buffer list to workspace - (advice-add #'doom-buffer-list :override #'+workspace-buffer-list)) - (t - (when +workspace--old-uniquify-style - (setq uniquify-buffer-name-style +workspace--old-uniquify-style)) - (advice-remove #'doom-buffer-list #'+workspace-buffer-list)))) - (add-hook 'persp-mode-hook #'+workspaces|init-persp-mode) + (add-hook! 'persp-mode-hook + (defun +workspaces-init-persp-mode-h () + (cond (persp-mode + ;; `uniquify' breaks persp-mode. It renames old buffers, which causes + ;; errors when switching between perspective (their buffers are + ;; serialized by name and persp-mode expects them to have the same + ;; name when restored). + (when uniquify-buffer-name-style + (setq +workspace--old-uniquify-style uniquify-buffer-name-style)) + (setq uniquify-buffer-name-style nil) + ;; Ensure `persp-kill-buffer-query-function' is last + (remove-hook 'kill-buffer-query-functions #'persp-kill-buffer-query-function) + (add-hook 'kill-buffer-query-functions #'persp-kill-buffer-query-function t) + ;; Restrict buffer list to workspace + (advice-add #'doom-buffer-list :override #'+workspace-buffer-list)) + (t + (when +workspace--old-uniquify-style + (setq uniquify-buffer-name-style +workspace--old-uniquify-style)) + (advice-remove #'doom-buffer-list #'+workspace-buffer-list))))) ;; We don't rely on the built-in mechanism for auto-registering a buffer to ;; the current workspace; some buffers slip through the cracks. Instead, we @@ -110,43 +109,47 @@ stored in `persp-save-dir'.") (setq persp-add-buffer-on-find-file nil persp-add-buffer-on-after-change-major-mode nil) - (defun +workspaces|add-current-buffer () - "Add current buffer to focused perspective." - (when persp-mode - (persp-add-buffer (current-buffer) (get-current-persp)))) - (add-hook 'doom-switch-buffer-hook #'+workspaces|add-current-buffer) + (add-hook! 'doom-switch-buffer-hook + (defun +workspaces-add-current-buffer-h () + "Add current buffer to focused perspective." + (and persp-mode + (not (persp-buffer-filtered-out-p + (current-buffer) + persp-add-buffer-on-after-change-major-mode-filter-functions)) + (persp-add-buffer (current-buffer) (get-current-persp) nil nil)))) - (add-to-list 'persp-add-buffer-on-after-change-major-mode-filter-functions - #'doom-unreal-buffer-p) + (add-hook 'persp-add-buffer-on-after-change-major-mode-filter-functions + #'doom-unreal-buffer-p) - (defun +workspaces*evil-alternate-buffer (&optional window) + (defadvice! +workspaces--evil-alternate-buffer-a (&optional window) "Make `evil-alternate-buffer' ignore buffers outside the current workspace." - (let* ((prev-buffers (if persp-mode - (cl-remove-if-not #'persp-contain-buffer-p (window-prev-buffers) - :key #'car) - (window-prev-buffers))) + :override #'evil-alternate-buffer + (let* ((prev-buffers + (if persp-mode + (cl-remove-if-not #'persp-contain-buffer-p (window-prev-buffers) + :key #'car) + (window-prev-buffers))) (head (car prev-buffers))) (if (eq (car head) (window-buffer window)) (cadr prev-buffers) head))) - (advice-add #'evil-alternate-buffer :override #'+workspaces*evil-alternate-buffer) ;; Delete the current workspace if closing the last open window (define-key! persp-mode-map [remap delete-window] #'+workspace/close-window-or-workspace - [remap evil-delete-window] #'+workspace/close-window-or-workspace) + [remap evil-window-delete] #'+workspace/close-window-or-workspace) ;; 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) - (add-hook 'delete-frame-functions #'+workspaces|delete-associated-workspace) + persp-interactive-init-frame-behaviour-override #'+workspaces-associate-frame-fn + persp-emacsclient-init-frame-behaviour-override #'+workspaces-associate-frame-fn) + (add-hook 'delete-frame-functions #'+workspaces-delete-associated-workspace-h) ;; per-project workspaces, but reuse current workspace if empty - (setq projectile-switch-project-action #'+workspaces|set-project-action + (setq projectile-switch-project-action #'+workspaces-set-project-action-fn counsel-projectile-switch-project-action - '(1 ("o" +workspaces|switch-to-project "open project in new workspace") + '(1 ("o" +workspaces-switch-to-project-h "open project in new workspace") ("O" counsel-projectile-switch-project-action "jump to a project buffer or file") ("f" counsel-projectile-switch-project-action-find-file "jump to a project file") ("d" counsel-projectile-switch-project-action-find-dir "jump to a project directory") @@ -166,19 +169,18 @@ stored in `persp-save-dir'.") ("xt" counsel-projectile-switch-project-action-run-term "invoke term from project root") ("X" counsel-projectile-switch-project-action-org-capture "org-capture into project"))) - (add-hook 'projectile-after-switch-project-hook #'+workspaces|switch-to-project) - - ;; 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) + (add-hook 'projectile-after-switch-project-hook #'+workspaces-switch-to-project-h) ;; Fix #1017: stop session persistence from restoring a broken posframe (after! posframe - (defun +workspaces|delete-all-posframes (&rest _) - (posframe-delete-all)) - (add-hook 'persp-after-load-state-functions #'+workspaces|delete-all-posframes)) + (add-hook! 'persp-after-load-state-functions + (defun +workspaces-delete-all-posframes-h (&rest _) + (posframe-delete-all)))) + + ;; Fix #1525: Ignore dead buffers in PERSP's buffer list + (defun +workspaces-dead-buffer-p (buf) + (not (buffer-live-p buf))) + (add-hook 'persp-filter-save-buffers-functions #'+workspaces-dead-buffer-p) ;; ;; eshell @@ -204,12 +206,12 @@ stored in `persp-save-dir'.") (push (cons buf-name base-buf-name) +workspaces--indirect-buffers-to-restore) nil))) - (defun +workspaces|reload-indirect-buffers (&rest _) - (dolist (ibc +workspaces--indirect-buffers-to-restore) - (cl-destructuring-bind (buffer-name . base-buffer-name) ibc - (when (buffer-live-p (get-buffer base-buffer-name)) - (when (get-buffer buffer-name) - (setq buffer-name (generate-new-buffer-name buffer-name))) - (make-indirect-buffer bb buffer-name t)))) - (setq +workspaces--indirect-buffers-to-restore nil)) - (add-hook 'persp-after-load-state-functions #'+workspaces|reload-indirect-buffers)) + (add-hook! 'persp-after-load-state-functions + (defun +workspaces-reload-indirect-buffers-h (&rest _) + (dolist (ibc +workspaces--indirect-buffers-to-restore) + (cl-destructuring-bind (buffer-name . base-buffer-name) ibc + (when (buffer-live-p (get-buffer base-buffer-name)) + (when (get-buffer buffer-name) + (setq buffer-name (generate-new-buffer-name buffer-name))) + (make-indirect-buffer bb buffer-name t)))) + (setq +workspaces--indirect-buffers-to-restore nil))))