Merge branch 'develop' into dired

This commit is contained in:
Henrik Lissner 2019-09-02 00:51:52 -04:00 committed by GitHub
commit 3c4252ffe7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
295 changed files with 8141 additions and 6654 deletions

View file

@ -1,9 +1,18 @@
DOOM = "bin/doom" DOOM = "bin/doom"
MODULES = $(patsubst modules/%/, %, $(sort $(dir $(wildcard modules/*/ modules/*/*/)))) MODULES = $(patsubst modules/%/, %, $(sort $(dir $(wildcard modules/*/ modules/*/*/))))
all: all: deprecated
@$(DOOM) refresh @$(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 ## Shortcuts
a: autoloads a: autoloads
i: install i: install
@ -16,34 +25,33 @@ cp: compile-plugins
re: recompile re: recompile
d: doctor d: doctor
quickstart: quickstart: install
@$(DOOM) quickstart
## Package management ## Package management
install: install: deprecated
@$(DOOM) install @$(DOOM) install
update: update: deprecated
@$(DOOM) update @$(DOOM) update
autoremove: autoremove: deprecated
@$(DOOM) autoremove @$(DOOM) autoremove
autoloads: autoloads: deprecated
@$(DOOM) autoloads @$(DOOM) autoloads
upgrade: upgrade: deprecated
@$(DOOM) upgrade @$(DOOM) upgrade
## Byte compilation ## Byte compilation
compile: compile: deprecated
@$(DOOM) compile @$(DOOM) compile
compile-core: compile-core: deprecated
@$(DOOM) compile :core @$(DOOM) compile :core
compile-private: compile-private: deprecated
@$(DOOM) compile :private @$(DOOM) compile :private
compile-plugins: compile-plugins: deprecated
@$(DOOM) compile :plugins @$(DOOM) build
recompile: recompile: deprecated
@$(DOOM) recompile @$(DOOM) recompile
clean: clean: deprecated
@$(DOOM) clean @$(DOOM) clean
# compile-module # compile-module
# compile-module/submodule # compile-module/submodule

View file

@ -34,7 +34,7 @@
**Quick start** **Quick start**
```bash ```bash
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom quickstart ~/.emacs.d/bin/doom install
``` ```
**Table of Contents** **Table of Contents**
@ -109,7 +109,7 @@ Feature Highlights
support for a variety of languages. support for a variety of languages.
- A jump-to-definition/references implementation for all languages that tries to - A jump-to-definition/references implementation for all languages that tries to
"just work," resorting to mode-specific functionality, before falling back on "just work," resorting to mode-specific functionality, before falling back on
[dump-jump][url:dump-jump]. [dumb-jump][url:dumb-jump].
Troubleshooting Troubleshooting
@ -178,7 +178,7 @@ contributions:
[url:company-mode]: https://github.com/company-mode/company-mode [url:company-mode]: https://github.com/company-mode/company-mode
[url:doom-themes]: https://github.com/hlissner/emacs-doom-themes [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:editorconfig]: http://editorconfig.org/
[url:evil-mode]: https://github.com/emacs-evil/evil [url:evil-mode]: https://github.com/emacs-evil/evil
[url:helm]: https://github.com/emacs-helm/helm [url:helm]: https://github.com/emacs-helm/helm

View file

@ -10,6 +10,11 @@
":"; exec $EMACS --script "$0" -- "$@" ":"; exec $EMACS --script "$0" -- "$@"
":"; exit 0 ":"; exit 0
(setq user-emacs-directory
(or (getenv "EMACSDIR")
(expand-file-name "../" (file-name-directory (file-truename load-file-name)))))
(defun usage () (defun usage ()
(with-temp-buffer (with-temp-buffer
(insert (format! "%s %s [COMMAND] [ARGS...]\n" (insert (format! "%s %s [COMMAND] [ARGS...]\n"
@ -35,15 +40,14 @@
" -d --debug\t\tTurns on doom-debug-mode (and debug-on-error)\n" " -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" " -e --emacsd DIR\tUse the emacs config at DIR (e.g. ~/.emacs.d)\n"
" -i --insecure\t\tDisable TLS/SSL validation (not recommended)\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" " -p --private DIR\tUse the private module at DIR (e.g. ~/.doom.d)\n"
" -y --yes\t\tAuto-accept all confirmation prompts\n\n") " -y --yes\t\tAuto-accept all confirmation prompts\n\n")
(princ (buffer-string))) (princ (buffer-string)))
(doom--dispatch-help)) (doom--dispatch-help))
;; ;;
(let ((args (cdr (cdr (cdr command-line-args)))) (let ((args (cdr (cdr (cdr command-line-args)))))
(emacs-dir (or (getenv "EMACSDIR")
(expand-file-name "../" (file-name-directory (file-truename load-file-name))))))
;; Parse options ;; Parse options
(while (ignore-errors (string-prefix-p "-" (car args))) (while (ignore-errors (string-prefix-p "-" (car args)))
(pcase (pop args) (pcase (pop args)
@ -62,47 +66,57 @@
(or (file-directory-p doom-private-dir) (or (file-directory-p doom-private-dir)
(message "Warning: %s does not exist" (message "Warning: %s does not exist"
(abbreviate-file-name doom-private-dir)))) (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") ((or "-e" "--emacsd")
(setq emacs-dir (expand-file-name (concat (pop args) "/"))) (setq user-emacs-directory (expand-file-name (concat (pop args) "/")))
(message "Emacs directory changed to %s" emacs-dir)) (message "Emacs directory changed to %s" user-emacs-directory))
((or "-y" "--yes") ((or "-y" "--yes")
(setenv "YES" "1") (setenv "YES" "1")
(message "Auto-yes mode on")))) (message "Auto-yes mode on"))))
(or (file-directory-p emacs-dir) (unless (file-directory-p user-emacs-directory)
(error "%s does not exist" emacs-dir)) (error "%s does not exist" user-emacs-directory))
;; Bootstrap Doom ;; Bootstrap Doom
(load (expand-file-name "init" emacs-dir) (if (not noninteractive)
nil 'nomessage) (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) (cond ((and (not (cdr args))
(doom|run-all-startup-hooks)) (member (car args) '("help" "h")))
((and (not (cdr args)) (usage))
(member (car args) '("help" "h"))) ((not args)
(usage)) (print! (error "No command detected.\n"))
((not args) (usage))
(message "No command detected, aborting!\n\nRun %s help for documentation." ((require 'core-cli)
(file-name-nondirectory load-file-name))) (let ((default-directory user-emacs-directory))
((let ((default-directory emacs-dir)) (setq argv nil)
(setq argv nil (condition-case e
noninteractive 'doom) (doom-dispatch (car args) (cdr args))
(condition-case e (user-error
(doom-dispatch (car args) (cdr args)) (print! (error "%s\n") (error-message-string e))
(user-error (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)))
(signal (car e) (cdr e))) ((debug error)
((debug error) (message "--------------------------------------------------\n")
(message "--------------------------------------------------\n") (message "There was an unexpected error:")
(message "There was an unexpected error:") (message " %s (%s)" (get (car e) 'error-message) (car e))
(message " %s (%s)" (get (car e) 'error-message) (car e)) (dolist (item (cdr e))
(dolist (item (cdr e)) (message " %s" item))
(message " %s" item)) (unless debug-on-error
(unless debug-on-error (message
(message (concat "\nRun the command again with the -d (or --debug) option to enable debug\n"
(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"
"mode and, hopefully, generate a stack trace. If you decide to file a bug\n" "report, please include it!\n\n"
"report, please include it!\n\n" "Emacs outputs to standard error, so you'll need to redirect stderr to\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"
"stdout to pipe this to a file or clipboard!\n\n" " e.g. doom -d install 2>&1 | clipboard-program"))
" e.g. doom -d install 2>&1 | clipboard-program")) (signal 'doom-error e)))))))))
(signal 'doom-error e))))))))

View file

@ -18,7 +18,8 @@
;; specified by the EMACSDIR envvar) ;; specified by the EMACSDIR envvar)
(setq user-emacs-directory (setq user-emacs-directory
(or (getenv "EMACSDIR") (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) (unless (file-directory-p user-emacs-directory)
(error "Couldn't find a Doom config!")) (error "Couldn't find a Doom config!"))
@ -27,8 +28,9 @@
(when (getenv "DEBUG") (when (getenv "DEBUG")
(setq debug-on-error t)) (setq debug-on-error t))
(require 'subr-x)
(require 'pp) (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) (defvar doom-init-p nil)
@ -147,7 +149,7 @@
emacs-version) emacs-version)
(explain! "Byte-code compiled in one version of Emacs may not work in another 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" "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...") (section! "Checking for Emacs config conflicts...")
(when (file-exists-p "~/.emacs") (when (file-exists-p "~/.emacs")
@ -182,109 +184,7 @@
;; on windows? ;; on windows?
(when (memq system-type '(windows-nt ms-dos cygwin)) (when (memq system-type '(windows-nt ms-dos cygwin))
(warn! "Warning: Windows detected") (warn! "Warning: Windows detected")
(explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")) (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."))))
;; ;;
@ -292,24 +192,17 @@
(condition-case-unless-debug ex (condition-case-unless-debug ex
(let ((after-init-time (current-time)) (let ((after-init-time (current-time))
(doom-message-backend 'ansi) (doom-format-backend 'ansi)
noninteractive) noninteractive)
(section! "Checking DOOM Emacs...") (section! "Checking DOOM Emacs...")
(load (concat user-emacs-directory "core/core.el") nil t) (load (concat user-emacs-directory "core/core.el") nil t)
(unless (file-directory-p doom-private-dir) (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)) (let ((indent 2))
;; Make sure everything is loaded ;; Make sure Doom is initialized and loaded
(require 'core-cli) (doom-initialize 'force)
(require 'core-keybinds) (doom-initialize-core)
(require 'core-ui)
(require 'core-projects)
(require 'core-editor)
(require 'core-packages)
;; ...and initialized
(doom-initialize)
(success! "Initialized Doom Emacs %s" doom-version) (success! "Initialized Doom Emacs %s" doom-version)
(doom-initialize-modules) (doom-initialize-modules)
@ -332,26 +225,23 @@
(when doom-modules (when doom-modules
(section! "Checking your enabled modules...") (section! "Checking your enabled modules...")
(let ((indent (+ indent 2))) (let ((indent (+ indent 2)))
(advice-add #'require :around #'doom*shut-up) (advice-add #'require :around #'doom-shut-up-a)
(maphash (maphash
(lambda (key plist) (lambda (key plist)
(let ((prefix (format! (bold "(%s %s) " (car key) (cdr key))))) (let ((prefix (format! (bold "(%s %s) " (car key) (cdr key)))))
(condition-case-unless-debug ex (condition-case-unless-debug ex
(let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el")) (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el"))
(packages-file (doom-module-path (car key) (cdr key) "packages.el"))) (packages-file (doom-module-path (car key) (cdr key) "packages.el")))
(cl-loop with doom--stage = 'packages (cl-loop for name in (let (doom-packages
for name in (let (doom-packages
doom-disabled-packages) doom-disabled-packages)
(load packages-file 'noerror 'nomessage) (load packages-file 'noerror 'nomessage)
(mapcar #'car doom-packages)) (mapcar #'car doom-packages))
for name = (doom-package-true-name name) unless (or (doom-package-get name :disable)
unless (or (doom-package-prop name :disable) (eval (doom-package-get name :ignore))
(eval (doom-package-prop name :ignore)) (doom-package-built-in-p name)
(package-built-in-p name) (doom-package-installed-p name))
(package-installed-p name))
do (error! "%s is not installed" 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))) (file-missing (error! "%s" (error-message-string ex)))
(error (error! "Syntax error: %s" ex))))) (error (error! "Syntax error: %s" ex)))))
doom-modules))))) doom-modules)))))

View file

@ -250,46 +250,11 @@ regex PATTERN. Returns the number of killed buffers."
;; Hooks ;; Hooks
;;;###autoload ;;;###autoload
(defun doom|mark-buffer-as-real () (defun doom-mark-buffer-as-real-h ()
"Hook function that marks the current buffer as real." "Hook function that marks the current buffer as real."
(doom-set-buffer-real (current-buffer) t)) (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 ;; Interactive commands

View file

@ -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 ;; This little library thinly wraps around persistent-soft (which is a pcache
;; wrapper, how about that). It has three purposes: ;; 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 name under `pcache-directory' (by default a subdirectory under
`doom-cache-dir'). One file may contain multiple cache entries.") `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 "Hook to run when an Emacs session is killed. Saves all persisted variables
listed in `doom-cache-alists' to files." listed in `doom-cache-alists' to files."
(dolist (alist (butlast doom-cache-alists 1)) (dolist (alist (butlast doom-cache-alists 1))
@ -29,7 +29,7 @@ listed in `doom-cache-alists' to files."
for var in (cdr alist) for var in (cdr alist)
if (symbol-value var) if (symbol-value var)
do (doom-cache-set var it nil key)))) 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)
;; ;;

View file

@ -1,15 +1,18 @@
;;; core/autoload/cli.el -*- lexical-binding: t; -*- ;;; core/autoload/cli.el -*- lexical-binding: t; -*-
;; Externs
(defvar evil-collection-mode-list)
(require 'core-cli) (require 'core-cli)
;;;###autoload ;;;###autoload
(defun doom-cli-run (command &rest _args) (defun doom--cli-run (command &rest _args)
(when (featurep 'general) (when (featurep 'general)
(general-auto-unbind-keys)) (general-auto-unbind-keys))
(let* ((evil-collection-mode-list nil) (let* ((evil-collection-mode-list nil)
(default-directory doom-emacs-dir) (default-directory doom-emacs-dir)
(buf (get-buffer-create " *bin/doom*")) (buf (get-buffer-create " *bin/doom*"))
(doom-message-backend 'ansi) (doom-format-backend 'ansi)
(ignore-window-parameters t) (ignore-window-parameters t)
(noninteractive t) (noninteractive t)
(standard-output (standard-output
@ -39,21 +42,21 @@
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "autoloads"))) (doom--cli-run "autoloads")))
;;;###autoload ;;;###autoload
(defun doom//update (&optional yes) (defun doom//update (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "update"))) (doom--cli-run "update")))
;;;###autoload ;;;###autoload
(defun doom//upgrade (&optional yes) (defun doom//upgrade (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (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?") (when (y-or-n-p "You must restart Emacs for the upgrade to take effect. Restart?")
(doom/restart-and-restore))) (doom/restart-and-restore)))
@ -62,18 +65,18 @@
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "install"))) (doom--cli-run "install")))
;;;###autoload ;;;###autoload
(defun doom//autoremove (&optional yes) (defun doom//autoremove (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "autoremove"))) (doom--cli-run "autoremove")))
;;;###autoload ;;;###autoload
(defun doom//refresh (&optional yes) (defun doom//refresh (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "refresh"))) (doom--cli-run "refresh")))

View file

@ -1,5 +1,9 @@
;;; core/autoload/config.el -*- lexical-binding: t; -*- ;;; 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 ;;;###autoload
(defvar doom-reloading-p nil (defvar doom-reloading-p nil
"TODO") "TODO")
@ -19,7 +23,7 @@
(doom-project-find-file doom-private-dir)) (doom-project-find-file doom-private-dir))
;;;###autoload ;;;###autoload
(defun doom/reload (&optional force-p) (defun doom/reload ()
"Reloads your private config. "Reloads your private config.
This is experimental! It will try to do as `bin/doom refresh' does, but from 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. reloads your package list, and lastly, reloads your private config.el.
Runs `doom-reload-hook' afterwards." 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) (require 'core-cli)
(general-auto-unbind-keys)
(let ((doom-reloading-p t)) (let ((doom-reloading-p t))
(when (getenv "DOOMENV") (compile (format "%s/bin/doom refresh -f" doom-emacs-dir))
(doom-reload-env-file 'force)) (while compilation-in-progress
(doom-reload-autoloads force-p) (sit-for 1))
(let (doom-init-p) (doom-initialize 'force)
(doom-initialize))
(with-demoted-errors "PRIVATE CONFIG ERROR: %s" (with-demoted-errors "PRIVATE CONFIG ERROR: %s"
(let (doom-init-modules-p) (general-auto-unbind-keys)
(doom-initialize-modules))) (unwind-protect
(when (bound-and-true-p doom-packages) (doom-initialize-modules 'force)
(doom/reload-packages)) (general-auto-unbind-keys t)))
(run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook)) (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook))
(general-auto-unbind-keys t)
(message "Finished!")) (message "Finished!"))
;;;###autoload ;;;###autoload
(defun doom/reload-autoloads (&optional force-p) (defun doom/reload-autoloads ()
"Reload only `doom-autoload-file' and `doom-package-autoload-file'. "Reload only `doom-autoload-file' and `doom-package-autoload-file'.
This is much faster and safer than `doom/reload', but not as comprehensive. This 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 It is useful to only pull in changes performed by 'doom refresh' on the command
line." line."
(interactive "P") (interactive)
(doom-initialize-autoloads doom-autoload-file) (require 'core-cli)
(doom-initialize-autoloads doom-package-autoload-file)) (require 'core-packages)
(doom-initialize-packages)
(doom-reload-autoloads nil 'force))
;;;###autoload ;;;###autoload
(defun doom/reload-env () (defun doom/reload-env ()
@ -70,26 +79,4 @@ Uses the same mechanism as 'bin/doom env reload'."
(sit-for 1)) (sit-for 1))
(unless (file-readable-p doom-env-file) (unless (file-readable-p doom-env-file)
(error "Failed to generate env file")) (error "Failed to generate env file"))
(doom-load-env-vars doom-env-file)) (doom-load-envvars-file 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)))

View file

@ -1,5 +1,19 @@
;;; core/autoload/debug.el -*- lexical-binding: t; -*- ;;; 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 ;;; Helpers
@ -16,65 +30,67 @@ ready to be pasted in a bug report on github."
(require 'vc-git) (require 'vc-git)
(let ((default-directory doom-emacs-dir) (let ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))) (doom-modules (doom-modules)))
(format (cl-letf
(concat "- OS: %s (%s)\n" (((symbol-function 'sh)
"- Shell: %s\n" (lambda (format)
"- Emacs: %s (%s)\n" (string-trim
"- Doom: %s (%s)\n" (shell-command-to-string format)))))
"- Graphic display: %s (daemon: %s)\n" `((emacs
"- System features: %s\n" (version . ,emacs-version)
"- Details:\n" (features ,@system-configuration-features)
" ```elisp\n" (build . ,(format-time-string "%b %d, %Y" emacs-build-time))
" env bootstrapper: %s\n" (buildopts ,system-configuration-options))
" elc count: %s\n" (doom
" uname -a: %s\n" (version . ,doom-version)
" modules: %s\n" (build . ,(sh "git log -1 --format=\"%D %h %ci\"")))
" packages: %s\n" (system
" exec-path: %s\n" (type . ,system-type)
" ```") (config . ,system-configuration)
system-type system-configuration (shell . ,shell-file-name)
shell-file-name (uname . ,(if IS-WINDOWS
emacs-version (format-time-string "%b %d, %Y" emacs-build-time) "n/a"
doom-version (sh "uname -msrv")))
(or (string-trim (shell-command-to-string "git log -1 --format=\"%D %h %ci\"")) (path . ,(mapcar #'abbreviate-file-name exec-path)))
"n/a") (config
(display-graphic-p) (daemonp) (envfile
(bound-and-true-p system-configuration-features) . ,(cond ((file-exists-p doom-env-file) 'envvar-file)
(cond ((file-exists-p doom-env-file) 'envvar-file) ((featurep 'exec-path-from-shell) 'exec-path-from-shell)))
((featurep 'exec-path-from-shell) 'exec-path-from-shell)) (elc-files
;; details . ,(length (doom-files-in `(,@doom-modules-dirs
(length (doom-files-in `(,@doom-modules-dirs ,doom-core-dir
,doom-core-dir ,doom-private-dir)
,doom-private-dir) :type 'files :match "\\.elc$")))
:type 'files :match "\\.elc$" :sort nil)) (modules
(if IS-WINDOWS ,@(or (cl-loop with cat = nil
"n/a" for key being the hash-keys of doom-modules
(with-temp-buffer if (or (not cat) (not (eq cat (car key))))
(unless (zerop (call-process "uname" nil t nil "-msrv")) do (setq cat (car key))
(insert (format "%s" system-type))) and collect cat
(string-trim (buffer-string)))) and collect (cdr key)
(or (cl-loop with cat = nil else collect
for key being the hash-keys of doom-modules (let ((flags (doom-module-get cat (cdr key) :flags)))
if (or (not cat) (not (eq cat (car key)))) (if flags
do (setq cat (car key)) `(,(cdr key) ,@flags)
and collect cat (cdr key))))
and collect (cdr key) '("n/a")))
else collect (packages
(let ((flags (doom-module-get cat (cdr key) :flags))) ,@(or (ignore-errors
(if flags (require 'core-packages)
`(,(cdr key) ,@flags) (doom-initialize-packages)
(cdr key)))) (cl-loop for (name . plist) in doom-packages
"n/a") if (doom-package-private-p name)
(or (ignore-errors collect
(require 'use-package) (format
(cl-loop for (name . plist) in (doom-find-packages :private t) "%s" (if-let (splist (doom-plist-delete (copy-sequence plist)
if (use-package-plist-delete (copy-sequence plist) :modules) :modules))
collect (format "%s" (cons name it)) (cons name splist)
else name))))
collect (symbol-name name))) '("n/a")))
"n/a") (elpa-packages
;; abbreviate $HOME to hide username ,@(or (ignore-errors
(mapcar #'abbreviate-file-name exec-path)))) (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." branch and commit."
(interactive) (interactive)
(require 'vc-git) (require 'vc-git)
(print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s" (let ((default-directory doom-core-dir))
doom-version (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s\nBuild date: %s"
emacs-version doom-version
(or (vc-git--symbolic-ref doom-core-dir) emacs-version
"n/a") (or (vc-git--symbolic-ref doom-core-dir)
(or (vc-git-working-revision doom-core-dir) "n/a")
"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 ;;;###autoload
(defun doom/info () (defun doom/info (&optional raw)
"Collects some debug information about your Emacs session, formats it into "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!" markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(interactive) (interactive "P")
(message "Generating Doom info...") (let ((buffer (get-buffer-create "*doom-info*"))
(if noninteractive (info (doom-info)))
(print! (doom-info)) (with-current-buffer buffer
(kill-new (doom-info)) (unless (or noninteractive
(message "Done! Copied to clipboard."))) (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 "<details>\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</details>"))
(if noninteractive
(print! (buffer-string))
(switch-to-buffer buffer)
(kill-new (buffer-string))
(print! (green "Copied markdown to clipboard"))))))
;;;###autoload ;;;###autoload
(defun doom/am-i-secure () (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 (macroexp-progn
(append `((setq noninteractive nil (append `((setq noninteractive nil
doom-debug-mode t doom-debug-mode t
load-path ',load-path
package--init-file-ensured t package--init-file-ensured t
package-user-dir ,package-user-dir package-user-dir ,package-user-dir
package-archives ',package-archives package-archives ',package-archives
user-emacs-directory ,doom-emacs-dir user-emacs-directory ,doom-emacs-dir)
doom--modules-cache nil)
(with-eval-after-load 'undo-tree (with-eval-after-load 'undo-tree
;; undo-tree throws errors because `buffer-undo-tree' isn't ;; undo-tree throws errors because `buffer-undo-tree' isn't
;; corrrectly initialized ;; 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))) (load! "config" (plist-get plist :path) t)))
doom-modules) doom-modules)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (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 (`vanilla-doom ; only Doom core
`((setq doom-private-dir "/tmp/does/not/exist" `((setq doom-private-dir "/tmp/does/not/exist"
doom-init-modules-p t) doom-init-modules-p t)
(load-file ,user-init-file) (load-file ,user-init-file)
(doom|run-all-startup-hooks))) (doom-run-all-startup-hooks-h)))
(`vanilla ; nothing loaded (`vanilla ; nothing loaded
`((package-initialize))))))) `((package-initialize)))))))
"\n(unwind-protect (progn\n" contents "\n)\n" "\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 init-file
esup-server-port esup-server-port
esup-depth) esup-depth)
"--eval=(doom|run-all-startup-hooks)")))) "--eval=(doom-run-all-startup-hooks-h)"))))
(when esup-run-as-batch-p (when esup-run-as-batch-p
(setq process-args (append process-args '("--batch")))) (setq process-args (append process-args '("--batch"))))
(setq esup-child-process (apply #'start-process process-args))) (setq esup-child-process (apply #'start-process process-args)))

View file

@ -1,24 +1,94 @@
;;; core/autoload/files.el -*- lexical-binding: t; -*- ;;; core/autoload/files.el -*- lexical-binding: t; -*-
;; (defun doom--resolve-path-forms (spec &optional directory)
;; Public library "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 ;;;###autoload
(cl-defun doom-files-in (cl-defun doom-files-in
(path-or-paths &rest rest (paths &rest rest
&key &key
filter filter
map map
full (full t)
(sort t) ; TODO Allow a function for custom sorting? (follow-symlinks t)
(follow-symlinks t) (type 'files)
(type 'files) (relative-to (unless full default-directory))
(relative-to (unless full default-directory)) (depth 99999)
(depth 99999) (mindepth 0)
(mindepth 0) (match "/[^._][^/]+"))
(match "/[^._]")) "Return a list of files/directories in PATHS (one string or a list of them).
"Returns a list of files/directories in PATH-OR-PATHS (one string path or a
list of them).
FILTER is a function or symbol that takes one argument (the path). If it returns FILTER is a function or symbol that takes one argument (the path). If it returns
non-nil, the entry will be excluded. 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. 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." MATCH is a string regexp. Only entries that match it will be included."
(cond (let (file-name-handler-alist
((listp path-or-paths) result)
(cl-loop for path in path-or-paths (dolist (file (mapcan (doom-rpartial #'doom-glob "*") (doom-enlist paths)))
if (file-directory-p path) (cond ((file-directory-p file)
nconc (apply #'doom-files-in path (plist-put rest :relative-to relative-to)))) (nconcq! result
((let ((path path-or-paths) (and (memq type '(t dirs))
result) (string-match-p match file)
(when (file-directory-p path) (not (and filter (funcall filter file)))
(dolist (file (directory-files path nil "." sort)) (not (and (file-symlink-p file)
(unless (member file '("." "..")) (not follow-symlinks)))
(let ((fullpath (expand-file-name file path))) (<= mindepth 0)
(cond ((file-directory-p fullpath) (list (cond (map (funcall map file))
(when (and (memq type '(t dirs)) (relative-to (file-relative-name file relative-to))
(string-match-p match fullpath) (file))))
(not (and filter (funcall filter fullpath))) (and (>= depth 1)
(not (and (file-symlink-p fullpath) (apply #'doom-files-in file
(not follow-symlinks))) (append (list :mindepth (1- mindepth)
(<= mindepth 0)) :depth (1- depth)
(setq result :relative-to relative-to)
(nconc result rest)))))
(list (cond (map (funcall map fullpath)) ((and (memq type '(t files))
(relative-to (file-relative-name fullpath relative-to)) (string-match-p match file)
(fullpath)))))) (not (and filter (funcall filter file)))
(unless (< depth 1) (<= mindepth 0))
(setq result (push (if relative-to
(nconc result (apply #'doom-files-in fullpath (file-relative-name file relative-to)
(append `(:mindepth ,(1- mindepth) file)
:depth ,(1- depth) result))))
:relative-to ,relative-to) result))
rest))))))
((and (memq type '(t files)) ;;;###autoload
(string-match-p match fullpath) (defmacro file-exists-p! (files &optional directory)
(not (and filter (funcall filter fullpath))) "Returns non-nil if the FILES in DIRECTORY all exist.
(<= mindepth 0))
(push (if relative-to DIRECTORY is a path; defaults to `default-directory'.
(file-relative-name fullpath relative-to)
fullpath) Returns the last file found to meet the rules set by FILES, which can be a
result)))))) single file or nested compound statement of `and' and `or' statements."
result))))) `(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) (defun doom--forget-file (old-path &optional new-path)
"Ensure `recentf', `projectile' and `save-place' forget OLD-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 ;;;###autoload
(defun doom/delete-this-file (&optional path force-p) (defun doom/delete-this-file (&optional path force-p)

View file

@ -50,7 +50,7 @@ FRAME parameter defaults to current frame."
(let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum)) (let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum))
increment))) increment)))
(unless (> new-size 0) (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))) (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 ;; Set point size & width to "*", so frame width will adjust to new font size
(aset font xlfd-regexp-pointsize-subnum "*") (aset font xlfd-regexp-pointsize-subnum "*")
@ -65,6 +65,15 @@ FRAME parameter defaults to current frame."
;; ;;
;;; Commands ;;; 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 ;;;###autoload
(defun doom/increase-font-size (count) (defun doom/increase-font-size (count)
"Enlargens the font size across the current frame." "Enlargens the font size across the current frame."

234
core/autoload/format.el Normal file
View file

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

View file

@ -121,6 +121,7 @@ selection of all minor-modes, active or not."
;; ;;
;;; Documentation commands ;;; Documentation commands
(defvar org-agenda-files)
(defun doom--org-headings (files &optional depth include-files) (defun doom--org-headings (files &optional depth include-files)
"TODO" "TODO"
(require 'org) (require 'org)
@ -155,6 +156,7 @@ selection of all minor-modes, active or not."
(mapc #'kill-buffer org-agenda-new-buffers) (mapc #'kill-buffer org-agenda-new-buffers)
(setq org-agenda-new-buffers nil)))) (setq org-agenda-new-buffers nil))))
(defvar ivy-sort-functions-alist)
;;;###autoload ;;;###autoload
(defun doom-completing-read-org-headings (prompt files &optional depth include-files initial-input) (defun doom-completing-read-org-headings (prompt files &optional depth include-files initial-input)
"TODO" "TODO"
@ -197,8 +199,7 @@ selection of all minor-modes, active or not."
"Find in News: " "Find in News: "
(nreverse (doom-files-in (expand-file-name "news" doom-docs-dir) (nreverse (doom-files-in (expand-file-name "news" doom-docs-dir)
:match "/[0-9]" :match "/[0-9]"
:relative-to doom-docs-dir :relative-to doom-docs-dir))
:sort t))
nil t initial-input)) nil t initial-input))
;;;###autoload ;;;###autoload
@ -365,10 +366,14 @@ current file is in, or d) the module associated with the current major mode (see
(recenter) (recenter)
(message "Couldn't find the config block")))))))) (message "Couldn't find the config block"))))))))
(defvar doom--help-packages-list nil)
(defun doom--help-packages-list (&optional refresh) (defun doom--help-packages-list (&optional refresh)
(or (unless refresh (or (unless refresh
(doom-cache-get 'help-packages)) doom--help-packages-list)
(doom-cache-set 'help-packages (doom-package-list 'all)))) (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) (defun doom--help-package-configs (package)
;; TODO Add git checks, in case ~/.emacs.d isn't a git repo ;; 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 (split-string
(shell-command-to-string (shell-command-to-string
(format "git grep --no-break --no-heading --line-number '%s %s\\($\\| \\)'" (format "git grep --no-break --no-heading --line-number '%s %s\\($\\| \\)'"
"\\(^;;;###package\\|(after!\\|(def-package!\\)" "\\(^;;;###package\\|(after!\\|(use-package!\\)"
package)) package))
"\n" t))) "\n" t)))
@ -392,59 +397,66 @@ If prefix arg is present, refresh the cache."
(let* ((guess (or (function-called-at-point) (let* ((guess (or (function-called-at-point)
(symbol-at-point)))) (symbol-at-point))))
(require 'finder-inf nil t) (require 'finder-inf nil t)
(require 'package)
(unless package--initialized (unless package--initialized
(package-initialize t)) (package-initialize t))
(let* ((doom--packages (doom--help-packages-list)) (let ((packages (cl-delete-duplicates
(packages (cl-delete-duplicates (append (mapcar 'car package-alist)
(append (mapcar 'car package-alist) (mapcar 'car package--builtins)
(mapcar 'car package--builtins) (mapcar 'car (doom--help-packages-list))
(mapcar 'car doom--packages) nil))))
nil))))
(unless (memq guess packages) (unless (memq guess packages)
(setq guess nil)) (setq guess nil))
(list (intern (completing-read (if guess (list
(format "Select package to search for (default %s): " (intern
guess) (completing-read (if guess
"Describe package: ") (format "Select package to search for (default %s): "
packages nil t nil nil guess)
(if guess (symbol-name guess)))))))) "Describe package: ")
packages nil t nil nil
(if guess (symbol-name guess))))))))
(if (or (package-desc-p package) (if (or (package-desc-p package)
(and (symbolp package) (and (symbolp package)
(or (assq package package-alist) (or (assq package package-alist)
(assq package package-archive-contents)
(assq package package--builtins)))) (assq package package--builtins))))
(describe-package package) (describe-package package)
(help-setup-xref (list #'doom/help-packages package) (help-setup-xref (list #'doom/help-packages package)
(called-interactively-p 'interactive)) (called-interactively-p 'interactive))
(with-help-window (help-buffer) (with-help-window (help-buffer)))
(with-current-buffer standard-output
(prin1 package)
(princ " is a site package.\n\n"))))
(save-excursion (save-excursion
(with-current-buffer (help-buffer) (with-current-buffer (help-buffer)
(let ((doom-packages (doom--help-packages-list)) (let ((doom-packages (doom--help-packages-list))
(inhibit-read-only t) (inhibit-read-only t)
(indent (make-string 13 ? ))) (indent (make-string 13 ? )))
(goto-char (point-min)) (goto-char (point-max))
(if (re-search-forward "^ *Status: " nil t) (if (re-search-forward "^ *Status: " nil t)
(progn (progn
(end-of-line) (end-of-line)
(insert "\n")) (insert "\n"))
(re-search-forward "\n\n" nil t)) (re-search-forward "\n\n" nil t))
(package--print-help-section "Package")
(insert (symbol-name package) "\n")
(package--print-help-section "Source") (package--print-help-section "Source")
(insert (or (pcase (ignore-errors (doom-package-backend package)) (insert (or (pcase (doom-package-backend package)
(`elpa (concat "[M]ELPA " (doom--package-url package))) (`straight
(`quelpa (format "QUELPA %s" (prin1-to-string (doom-package-prop package :recipe)))) (format! "Straight\n%s"
(`emacs "Built-in") (indent
(_ (symbol-file package))) 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") "unknown")
"\n") "\n")
(when (assq package doom-packages) (when (assq package doom-packages)
(package--print-help-section "Modules") (package--print-help-section "Modules")
(insert "Declared by the following Doom modules:\n") (insert "Declared by the following Doom modules:\n")
(dolist (m (doom-package-prop package :modules)) (dolist (m (doom-package-get package :modules))
(insert indent) (insert indent)
(doom--help-package-insert-button (doom--help-package-insert-button
(format "%s %s" (car m) (or (cdr m) "")) (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)) (find-file (expand-file-name file doom-emacs-dir))
(goto-char (point-min)) (goto-char (point-min))
(forward-line (1- line)) (forward-line (1- line))
(recenter))))))))) (recenter)))))
(insert "\n\n")))))
(defvar doom--package-cache nil) (defvar doom--package-cache nil)
(defun doom--package-list () (defun doom--package-list ()

View file

@ -1,9 +1,9 @@
;;; core/autoload/line-numbers.el -*- lexical-binding: t; -*- ;;; core/autoload/line-numbers.el -*- lexical-binding: t; -*-
;;;###if (version< emacs-version "26.1") ;;;###if (version< emacs-version "26.1")
;; This was lifted out of the display-line-numbers library in Emacs 26.1 and ;; DEPRECATED This was lifted out of the display-line-numbers library in Emacs
;; modified to use nlinum for Emacs 25.x users. It should be removed should ;; 26.1 and modified to use nlinum for Emacs 25.x users. It should be removed
;; Emacs 25 support be removed. ;; should Emacs 25 support be removed.
;;;###autoload ;;;###autoload
(defvar display-line-numbers t (defvar display-line-numbers t
@ -52,7 +52,7 @@ to display all line numbers in the buffer."
:type 'boolean) :type 'boolean)
;;;###autoload ;;;###autoload
(defun line-number-display-width () (defun line-number-display-width (&optional _)
"Return the width used for displaying line numbers in the "Return the width used for displaying line numbers in the
selected window." selected window."
(length (save-excursion (goto-char (point-max)) (length (save-excursion (goto-char (point-max))

View file

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

View file

@ -1,128 +1,64 @@
;;; core/autoload/packages.el -*- lexical-binding: t; -*- ;;; 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 ;;; Package metadata
;;;###autoload ;;;###autoload
(defun doom-package-plist (package) (defun doom-package-get (package &optional prop nil-value)
"Returns PACKAGE's `package!' recipe from `doom-packages'." "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 ;;;###autoload
(defun doom-package-desc (package) (defun doom-package-recipe (package &optional prop nil-value)
"Returns PACKAGE's desc struct from `package-alist'." "Returns the `straight' recipe PACKAGE was registered with."
(cadr (assq (or (car (doom-package-prop package :recipe)) (let ((plist (gethash (symbol-name package) straight--recipe-cache)))
package) (if prop
package-alist))) (if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload ;;;###autoload
(defun doom-package-true-name (package) (defun doom-package-build-recipe (package &optional prop nil-value)
"Return PACKAGE's true name. "Returns the `straight' recipe PACKAGE was installed with."
(let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
It is possible for quelpa packages to be given a psuedonym (the first argument (if prop
of `package!'). Its real name is the car of package's :recipe. e.g. (if (plist-member plist prop)
(plist-get plist prop)
(package! X :recipe (Y :fetcher github :repo \"abc/def\")) nil-value)
plist)))
X's real name is Y."
(let ((sym (car (doom-package-prop package :recipe))))
(or (and (symbolp sym)
(not (keywordp sym))
sym)
package)))
;;;###autoload ;;;###autoload
(defun doom-package-psuedo-name (package) (defun doom-package-build-time (package)
"TODO" "TODO"
(or (cl-loop for (package . plist) in doom-packages (car (gethash (symbol-name package) straight--build-cache)))
for recipe-name = (car (plist-get plist :recipe))
if (eq recipe-name package)
return recipe-name)
package))
;;;###autoload ;;;###autoload
(defun doom-package-backend (package &optional noerror) (defun doom-package-dependencies (package &optional recursive noerror)
"Return backend that PACKAGE was installed with. "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 (defun doom-package-depending-on (package &optional noerror)
nil and the package isn't installed. "Return a list of packages that depend on the package named NAME."
(cl-check-type name symbol)
See `doom-package-recipe-backend' to get the backend PACKAGE is registered with ;; can't get dependencies for built-in packages
\(as opposed to what it is was installed with)." (unless (or (doom-package-build-recipe name)
(cl-check-type package symbol) noerror)
(let ((package-truename (doom-package-true-name package))) (error "Couldn't find %s, is it installed?" name))
(cond ((assq package-truename quelpa-cache) 'quelpa) (cl-loop for pkg in (hash-table-keys straight--build-cache)
((assq package-truename package-alist) 'elpa) for deps = (doom-package-dependencies pkg)
((doom-package-built-in-p package) 'emacs) if (memq package deps)
((not noerror) (error "%s package is not installed" package))))) collect pkg
and append (doom-package-depending-on pkg t)))
;;;###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))
;; ;;
@ -131,212 +67,94 @@ exist/isn't specified."
;;;###autoload ;;;###autoload
(defun doom-package-built-in-p (package) (defun doom-package-built-in-p (package)
"Return non-nil if PACKAGE (a symbol) is built-in." "Return non-nil if PACKAGE (a symbol) is built-in."
(unless (doom-package-installed-p package) (eq (doom-package-build-recipe package :type)
(or (package-built-in-p (doom-package-true-name package)) 'built-in))
(locate-library (symbol-name package) nil doom-site-load-path))))
;;;###autoload ;;;###autoload
(defun doom-package-installed-p (package) (defun doom-package-installed-p (package)
"Return non-nil if PACKAGE (a symbol) is installed." "Return non-nil if PACKAGE (a symbol) is installed."
(when-let (desc (doom-package-desc package)) (file-directory-p (straight--build-dir (symbol-name package))))
(and (package-installed-p desc)
(file-directory-p (package-desc-dir desc)))))
;;;###autoload ;;;###autoload
(defun doom-package-registered-p (package) (defun doom-package-registered-p (package)
"Return non-nil if PACKAGE (a symbol) has been registered with `package!'. "Return non-nil if PACKAGE (a symbol) has been registered with `package!'.
Excludes packages that have a non-nil :built-in property." Excludes packages that have a non-nil :built-in property."
(let ((package (or (cl-loop for (pkg . plist) in doom-packages (when-let (plist (doom-package-get package))
for newname = (car (plist-get plist :recipe)) (not (eval (plist-get plist :ignore) t))))
if (and (symbolp newname)
(eq newname package))
return pkg)
package)))
(when-let (plist (doom-package-plist package))
(not (eval (plist-get plist :ignore))))))
;;;###autoload ;;;###autoload
(defun doom-package-private-p (package) (defun doom-package-private-p (package)
"Return non-nil if PACKAGE was installed by the user's private config." "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 ;;;###autoload
(defun doom-package-protected-p (package) (defun doom-package-protected-p (package)
"Return non-nil if PACKAGE is protected. "Return non-nil if PACKAGE is protected.
A protected package cannot be deleted and will be auto-installed if missing." 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 ;;;###autoload
(defun doom-package-core-p (package) (defun doom-package-core-p (package)
"Return non-nil if PACKAGE is a core Doom package." "Return non-nil if PACKAGE is a core Doom package."
(or (doom-package-protected-p package) (or (doom-package-protected-p package)
(assq :core (doom-package-prop package :modules)))) (assq :core (doom-package-get package :modules))))
;;;###autoload ;;;###autoload
(defun doom-package-different-backend-p (package) (defun doom-package-backend (package)
"Return t if a PACKAGE (a symbol) has a new backend than what it was installed "Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is
with. Returns nil otherwise, or if package isn't installed." installed."
(cl-check-type package symbol) (cond ((gethash (symbol-name package) straight--build-cache)
(and (doom-package-installed-p package) 'straight)
(not (doom-get-depending-on package)) ; not a dependency ((or (doom-package-built-in-p package)
(not (eq (doom-package-backend package 'noerror) (assq package package--builtins))
(doom-package-recipe-backend package 'noerror))))) 'builtin)
((assq package package-alist)
'elpa)
('other)))
;;;###autoload ;;;###autoload
(defun doom-package-different-recipe-p (name) (defun doom-package-different-recipe-p (name)
"Return t if a package named NAME (a symbol) has a different recipe than it "Return t if a package named NAME (a symbol) has a different recipe than it
was installed with." was installed with."
(cl-check-type name symbol) (cl-check-type name symbol)
(when (doom-package-installed-p name) ;; TODO
(let ((package-truename (doom-package-true-name name))) ;; (when (doom-package-installed-p name)
(when-let* ((quelpa-recipe (assq package-truename quelpa-cache)) ;; (when-let* ((doom-recipe (assq name doom-packages))
(doom-recipe (assq package-truename doom-packages))) ;; (install-recipe (doom-package-recipe)))
(not (equal (cdr quelpa-recipe) ;; (not (equal (cdr quelpa-recipe)
(cdr (plist-get (cdr doom-recipe) :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)))))
;; ;;
;;; Package list getters ;;; Package list getters
;;;###autoload (defun doom--read-module-packages-file (file &optional eval noerror)
(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)
(with-temp-buffer ; prevent buffer-local settings from propagating (with-temp-buffer ; prevent buffer-local settings from propagating
(condition-case e (condition-case e
(if (not raw) (if (not eval)
(load file noerror t t) (load file noerror t t)
(when (file-readable-p file) (when (file-readable-p file)
(insert-file-contents file) (insert-file-contents file)
(delay-mode-hooks (emacs-lisp-mode))
(while (re-search-forward "(package! " nil t) (while (re-search-forward "(package! " nil t)
(save-excursion (save-excursion
(goto-char (match-beginning 0)) (goto-char (match-beginning 0))
(cl-destructuring-bind (name . plist) (cdr (sexp-at-point)) (unless (let ((ppss (syntax-ppss)))
(push (cons name (or (nth 3 ppss)
(plist-put plist :modules (nth 4 ppss)))
(cond ((file-in-directory-p file doom-private-dir) (cl-destructuring-bind (name . plist)
'((:private))) (cdr (sexp-at-point))
((file-in-directory-p file doom-core-dir) (push (cons
'((:core))) name (plist-put
((doom-module-from-path file))))) plist :modules
doom-packages)))))) (list (doom-module-from-path file))))
doom-packages)))))))
((debug error) ((debug error)
(signal 'doom-package-error (signal 'doom-package-error
(list (or (doom-module-from-path file) (list (doom-module-from-path file)
'(:private . packages))
e)))))) e))))))
;;;###autoload ;;;###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 If ALL-P, gather packages unconditionally across all modules, including disabled
ones." ones."
(let ((noninteractive t) (let ((noninteractive t)
(doom--stage 'packages)
(doom-modules (doom-modules)) (doom-modules (doom-modules))
doom-packages doom-packages
doom-disabled-packages doom-disabled-packages)
package-pinned-packages) (doom--read-module-packages-file
(doom--read-module-packages-file (expand-file-name "packages.el" doom-core-dir) all-p) (doom-path doom-core-dir "packages.el") all-p t)
(let ((private-packages (expand-file-name "packages.el" doom-private-dir))) (let ((private-packages (doom-path doom-private-dir "packages.el")))
(unless all-p (unless all-p
;; We load the private packages file twice to ensure disabled packages ;; We load the private packages file twice to ensure disabled packages
;; are seen ASAP, and a second time to ensure privately overridden ;; are seen ASAP, and a second time to ensure privately overridden
;; packages are properly overwritten. ;; 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 (if all-p
(mapc #'doom--read-module-packages-file (mapc #'doom--read-module-packages-file
(doom-files-in doom-modules-dir (doom-files-in doom-modules-dir
:depth 2 :depth 2
:full t :match "/packages\\.el$"))
:match "/packages\\.el$"
:sort nil))
(cl-loop for key being the hash-keys of doom-modules (cl-loop for key being the hash-keys of doom-modules
for path = (doom-module-path (car key) (cdr key) "packages.el") for path = (doom-module-path (car key) (cdr key) "packages.el")
for doom--current-module = key for doom--current-module = key
do (doom--read-module-packages-file path nil t))) do (doom--read-module-packages-file path nil t)))
(doom--read-module-packages-file private-packages all-p t)) (doom--read-module-packages-file private-packages all-p t))
(append (cl-loop for package in doom-core-packages (nreverse doom-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)))
;; ;;
;; Main functions ;;; 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
;;;###autoload ;;;###autoload
(defun doom/reload-packages () (defun doom/reload-packages ()
"Reload `doom-packages', `package' and `quelpa'." "Reload `doom-packages', `package' and `quelpa'."
(interactive) (interactive)
;; HACK straight.el must be loaded for this to work
(message "Reloading packages") (message "Reloading packages")
(doom-initialize-packages t) (doom-initialize-packages t)
(message "Reloading packages...DONE")) (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)

89
core/autoload/plist.el Normal file
View file

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

View file

@ -2,8 +2,7 @@
(defvar projectile-project-root nil) (defvar projectile-project-root nil)
;;;###autoload ;;;###autoload (autoload 'projectile-relevant-known-projects "projectile")
(autoload 'projectile-relevant-known-projects "projectile")
;;;###autodef ;;;###autodef
(cl-defun set-project-type! (name &key predicate compile run test configure dir) (cl-defun set-project-type! (name &key predicate compile run test configure dir)
@ -23,16 +22,6 @@
;; ;;
;;; Macros ;;; 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 ;;;###autoload
(defmacro project-file-exists-p! (files) (defmacro project-file-exists-p! (files)
"Checks if the project has the specified 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. "Return the name of the current project.
Returns '-' if not in a valid project." Returns '-' if not in a valid project."
(if-let* ((project-root (or (doom-project-root dir) (if-let (project-root (or (doom-project-root dir)
(if dir (expand-file-name dir))))) (if dir (expand-file-name dir))))
(funcall projectile-project-name-function project-root) (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)) (setq projectile-enable-caching nil))
(call-interactively (call-interactively
;; Intentionally avoid `helm-projectile-find-file', because it runs ;; Intentionally avoid `helm-projectile-find-file', because it runs
;; asynchronously, and thus doesn't see the lexical `default-directory' ;; asynchronously, and thus doesn't see the lexical
(if (featurep! :completion ivy) ;; `default-directory'
(if (doom-module-p :completion 'ivy)
#'counsel-projectile-find-file #'counsel-projectile-find-file
#'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 ((fboundp 'counsel-file-jump) ; ivy only
(call-interactively #'counsel-file-jump)) (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) ((fboundp 'helm-find-files)
(call-interactively #'helm-find-files)) (call-interactively #'helm-find-files))
((call-interactively #'find-file))))) ((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." "Traverse a file structure starting linearly from DIR."
(let ((default-directory (file-truename (expand-file-name dir)))) (let ((default-directory (file-truename (expand-file-name dir))))
(call-interactively (call-interactively
(cond ((featurep! :completion ivy) (cond ((doom-module-p :completion 'ivy)
#'counsel-find-file) #'counsel-find-file)
((featurep! :completion helm) ((doom-module-p :completion 'helm)
#'helm-find-files) #'helm-find-files)
(#'find-file))))) (#'find-file)))))

View file

@ -19,18 +19,22 @@ following:
(defvar doom-scratch-buffers nil (defvar doom-scratch-buffers nil
"A list of active scratch buffers.") "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.") "The name of the project associated with the current scratch buffer.")
(defvar doom-scratch-buffer-hook () (defvar doom-scratch-buffer-hook ()
"The hooks to run after a scratch buffer is created.") "The hooks to run after a scratch buffer is created.")
(defun doom--load-persistent-scratch-buffer (name) (defun doom--load-persistent-scratch-buffer (name)
(let ((scratch-file (expand-file-name (or name doom-scratch-default-file) (setq-local doom-scratch-current-project
doom-scratch-dir))) (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) (make-directory doom-scratch-dir t)
(if (not (file-readable-p scratch-file)) (when (file-readable-p scratch-file)
nil
(erase-buffer) (erase-buffer)
(insert-file-contents scratch-file) (insert-file-contents scratch-file)
(set-auto-mode) (set-auto-mode)
@ -39,48 +43,54 @@ following:
;;;###autoload ;;;###autoload
(defun doom-scratch-buffer (&optional mode directory project-name) (defun doom-scratch-buffer (&optional mode directory project-name)
"Return a scratchpad buffer in major MODE." "Return a scratchpad buffer in major MODE."
(let* ((buffer-name (if project-name (with-current-buffer
(format "*doom:scratch (%s)*" project-name) (get-buffer-create (if project-name
"*doom:scratch*")) (format "*doom:scratch (%s)*" project-name)
(buffer (get-buffer buffer-name))) "*doom:scratch*"))
(with-current-buffer (get-buffer-create buffer-name) (setq default-directory directory)
(unless buffer (unless doom-scratch-current-project
(setq buffer (current-buffer) (doom--load-persistent-scratch-buffer project-name)
default-directory directory (when (and (eq major-mode 'fundamental-mode)
doom-scratch-current-project project-name) (functionp mode))
(setq doom-scratch-buffers (cl-delete-if-not #'buffer-live-p doom-scratch-buffers)) (funcall mode)))
(cl-pushnew buffer doom-scratch-buffers) (cl-pushnew (current-buffer) doom-scratch-buffers)
(doom--load-persistent-scratch-buffer project-name) (add-hook 'kill-buffer-hook #'doom-persist-scratch-buffer-h nil 'local)
(when (and (eq major-mode 'fundamental-mode) (add-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h)
(functionp mode)) (run-hooks 'doom-scratch-buffer-created-hook)
(funcall mode)) (current-buffer)))
(add-hook 'kill-buffer-hook #'doom|persist-scratch-buffer nil 'local)
(run-hooks 'doom-scratch-buffer-created-hook))
buffer)))
;; ;;
;;; Persistent scratch buffer ;;; Persistent scratch buffer
;;;###autoload ;;;###autoload
(defun doom|persist-scratch-buffer () (defun doom-persist-scratch-buffer-h ()
"Save the current buffer to `doom-scratch-dir'." "Save the current buffer to `doom-scratch-dir'."
(write-region (write-region
(point-min) (point-max) (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))) doom-scratch-dir)))
;;;###autoload ;;;###autoload
(defun doom|persist-scratch-buffers () (defun doom-persist-scratch-buffers-h ()
"Save all scratch buffers to `doom-scratch-dir'." "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) (dolist (buffer doom-scratch-buffers)
(with-current-buffer buffer (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 ;;;###autoload
(unless noninteractive (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 ;;;###autoload
(defun doom/switch-to-scratch-buffer (&optional project-p) (defun doom/switch-to-scratch-buffer (&optional project-p)
"Like `doom/open-scratch-buffer', but switches to it in the current window." "Like `doom/open-scratch-buffer', but switches to it in the current window.
(interactive)
(doom/open-scratch-buffer t)) If passed the prefix arg, open project scratch buffer."
(interactive "P")
(doom/open-scratch-buffer t project-p))
;;;###autoload ;;;###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. "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") (interactive "P")
(doom/open-scratch-buffer arg 'project)) (doom/open-scratch-buffer current-window 'project))
;;;###autoload ;;;###autoload
(defun doom/switch-to-project-scratch-buffer () (defun doom/switch-to-project-scratch-buffer ()

View file

@ -1,5 +1,11 @@
;;; core/autoload/sessions.el -*- lexical-binding: t; -*- ;;; core/autoload/sessions.el -*- lexical-binding: t; -*-
(defvar desktop-base-file-name)
(defvar desktop-dirname)
(defvar desktop-restore-eager)
(defvar desktop-file-modtime)
;; ;;
;;; Helpers ;;; Helpers
@ -59,9 +65,6 @@
"TODO" "TODO"
(add-hook 'window-setup-hook #'doom-load-session 'append)) (add-hook 'window-setup-hook #'doom-load-session 'append))
;;;###autoload
(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler))
;; ;;
;;; Commands ;;; Commands

View file

@ -25,6 +25,41 @@ lines, above and below, with only whitespace in between."
(or (not balanced) (or (not balanced)
(= (- pt nbeg) (- nend pt)))))))))))) (= (- 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 ;; Commands
@ -186,9 +221,13 @@ Respects `require-final-newline'."
(setq indent-tabs-mode (not indent-tabs-mode)) (setq indent-tabs-mode (not indent-tabs-mode))
(message "Indent style changed to %s" (if indent-tabs-mode "tabs" "spaces"))) (message "Indent style changed to %s" (if indent-tabs-mode "tabs" "spaces")))
(defvar editorconfig-lisp-use-default-indent)
;;;###autoload ;;;###autoload
(defun doom/set-indent-width (width) (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 (interactive
(list (if (integerp current-prefix-arg) (list (if (integerp current-prefix-arg)
current-prefix-arg current-prefix-arg
@ -211,25 +250,25 @@ Respects `require-final-newline'."
;; Hooks ;; Hooks
;;;###autoload ;;;###autoload
(defun doom|enable-delete-trailing-whitespace () (defun doom-enable-delete-trailing-whitespace-h ()
"Enables the automatic deletion of trailing whitespaces upon file save. "Enables the automatic deletion of trailing whitespaces upon file save.
i.e. enables `ws-butler-mode' in the current buffer." i.e. enables `ws-butler-mode' in the current buffer."
(ws-butler-mode +1)) (ws-butler-mode +1))
;;;###autoload ;;;###autoload
(defun doom|disable-delete-trailing-whitespace () (defun doom-disable-delete-trailing-whitespace-h ()
"Disables the automatic deletion of trailing whitespaces upon file save. "Disables the automatic deletion of trailing whitespaces upon file save.
i.e. disables `ws-butler-mode' in the current buffer." i.e. disables `ws-butler-mode' in the current buffer."
(ws-butler-mode -1)) (ws-butler-mode -1))
;;;###autoload ;;;###autoload
(defun doom|enable-show-trailing-whitespace () (defun doom-enable-show-trailing-whitespace-h ()
"Enable `show-trailing-whitespace' in the current buffer." "Enable `show-trailing-whitespace' in the current buffer."
(setq-local show-trailing-whitespace t)) (setq-local show-trailing-whitespace t))
;;;###autoload ;;;###autoload
(defun doom|disable-show-trailing-whitespace () (defun doom-disable-show-trailing-whitespace-h ()
"Disable `show-trailing-whitespace' in the current buffer." "Disable `show-trailing-whitespace' in the current buffer."
(setq-local show-trailing-whitespace nil)) (setq-local show-trailing-whitespace nil))

50
core/autoload/themes.el Normal file
View file

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

View file

@ -27,12 +27,12 @@ are open."
;; Advice ;; Advice
;;;###autoload ;;;###autoload
(defun doom*recenter (&rest _) (defun doom-recenter-a (&rest _)
"Generic advisor for recentering window (typically :after other functions)." "Generic advisor for recentering window (typically :after other functions)."
(recenter)) (recenter))
;;;###autoload ;;;###autoload
(defun doom*shut-up (orig-fn &rest args) (defun doom-shut-up-a (orig-fn &rest args)
"Generic advisor for silencing noisy functions." "Generic advisor for silencing noisy functions."
(quiet! (apply orig-fn args))) (quiet! (apply orig-fn args)))
@ -41,14 +41,14 @@ are open."
;; Hooks ;; Hooks
;;;###autoload ;;;###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 "Applies ansi codes to the compilation buffers. Meant for
`compilation-filter-hook'." `compilation-filter-hook'."
(with-silent-modifications (with-silent-modifications
(ansi-color-apply-on-region compilation-filter-start (point)))) (ansi-color-apply-on-region compilation-filter-start (point))))
;;;###autoload ;;;###autoload
(defun doom|disable-show-paren-mode () (defun doom-disable-show-paren-mode-h ()
"Turn off `show-paren-mode' buffer-locally." "Turn off `show-paren-mode' buffer-locally."
(setq-local show-paren-mode nil)) (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." See `display-line-numbers' for what these values mean."
(interactive) (interactive)
(defvar doom--line-number-style display-line-numbers-type) (defvar doom--line-number-style display-line-numbers-type)
;; DEPRECATED
(let* ((styles `(t ,(if (and EMACS26+ visual-line-mode) 'visual 'relative) nil)) (let* ((styles `(t ,(if (and EMACS26+ visual-line-mode) 'visual 'relative) nil))
(order (cons display-line-numbers-type (remq display-line-numbers-type styles))) (order (cons display-line-numbers-type (remq display-line-numbers-type styles)))
(queue (memq doom--line-number-style order)) (queue (memq doom--line-number-style order))
@ -74,6 +75,7 @@ See `display-line-numbers' for what these values mean."
(car order) (car order)
(car (cdr queue))))) (car (cdr queue)))))
(setq doom--line-number-style next) (setq doom--line-number-style next)
;; DEPRECATED
(if EMACS26+ (if EMACS26+
(setq display-line-numbers next) (setq display-line-numbers next)
(pcase next (pcase next

View file

@ -1,16 +1,12 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*- ;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
(dispatcher! (autoloads a) (require 'autoload)
(doom-reload-autoloads nil 'force)
"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', (defvar doom-autoload-excluded-packages '("gh")
`package-activated-list' and `auto-mode-alist'.") "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 ;; external variables
(defvar autoload-timestamps) (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) (defcli! (autoloads a) ()
"Packages that have silly or destructive autoload files that try to load "Regenerates Doom's autoloads files.
everyone in the universe and their dog, causing errors that make babies cry. No
one wants that.") 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) (defun doom-delete-autoloads-file (file)
"Delete FILE (an autoloads file) and accompanying *.elc file, if any." "Delete FILE (an autoloads file) and accompanying *.elc file, if any."
(cl-check-type file string) (cl-check-type file string)
(when (file-exists-p file) (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 (with-current-buffer buf
(set-buffer-modified-p nil)) (set-buffer-modified-p nil))
(kill-buffer buf)) (kill-buffer buf))
(delete-file file) (delete-file file)
(ignore-errors (delete-file (byte-compile-dest-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 () (defun doom--warn-refresh-session-h ()
(print! (bold (green "\nFinished!"))) (message "Restart or reload Doom Emacs for changes to take effect:\n")
(message "If you have a running Emacs Session, you will need to restart it or")
(message "reload Doom for changes to take effect:\n")
(message " M-x doom/restart-and-restore") (message " M-x doom/restart-and-restore")
(message " M-x doom/restart") (message " M-x doom/restart")
(message " M-x doom/reload")) (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) (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)) (byte-compile-dynamic-docstrings t))
(condition-case e (condition-case-unless-debug e
(when (byte-compile-file file) (when (byte-compile-file file)
;; Give autoloads file a chance to report error (prog1 (load file 'noerror 'nomessage)
(load (if doom-debug-mode (when noninteractive
file (add-hook 'doom-cli-post-success-execute-hook #'doom--warn-refresh-session-h))))
(byte-compile-dest-file file))
nil t)
(unless noninteractive
(message "Finished compiling %s" short-name)))
((debug error) ((debug error)
(let ((backup-file (concat file ".bk"))) (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)) (copy-file file backup-file 'overwrite))
(doom-delete-autoloads-file file) (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) (defun doom-reload-autoloads (&optional file force-p)
"Reloads FILE (an autoload file), if it needs reloading. "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))) (signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file) (if (stringp file)
(cond ((file-equal-p file doom-autoload-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) ((file-equal-p file doom-package-autoload-file)
(doom-reload-package-autoloads force-p)) (doom-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file))) ((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-reload-package-autoloads force-p)))
;; ;;
;;; Doom autoloads ;;; 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) (defun doom--generate-header (func)
(goto-char (point-min)) (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")) ";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
(defun doom--generate-autoloads (targets) (defun doom--generate-autoloads (targets)
(require 'autoload) (let ((n 0))
(dolist (file targets) (dolist (file targets)
(let* ((file (file-truename file)) (insert
(generated-autoload-file doom-autoload-file) (with-temp-buffer
(generated-autoload-load-name (file-name-sans-extension file)) (cond ((not (doom-file-cookie-p file))
(noninteractive (not doom-debug-mode)) (print! (debug "Ignoring %s") (relpath file)))
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))))))
(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 (let ((load-path
;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files ;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files
;; will be unable to declare autoloads for the built-in autoload.el ;; will be unable to declare autoloads for the built-in autoload.el
;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why ;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why
;; they'd want to though, so it's an acceptable compromise. ;; 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 doom-modules-dirs
load-path)) (straight--directory-files (straight--build-dir) nil t)
cache) load-path)))
(while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) (defvar doom--autoloads-path-cache nil)
(while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
(let ((path (match-string 1))) (let ((path (match-string 1)))
(replace-match (replace-match
(or (cdr (assoc path cache)) (or (cdr (assoc path doom--autoloads-path-cache))
(when-let* ((libpath (locate-library path)) (when-let* ((libpath (or (and allow-internal-paths
(libpath (file-name-sans-extension libpath))) (locate-library path nil (cons doom-emacs-dir doom-modules-dirs)))
(push (cons path (abbreviate-file-name libpath)) cache) (locate-library path)))
(libpath (file-name-sans-extension libpath))
(libpath (abbreviate-file-name libpath)))
(push (cons path libpath) doom--autoloads-path-cache)
libpath) libpath)
path) path)
t t nil 1))))) 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) (defun doom--generate-autodefs (targets enabled-targets)
(goto-char (point-max)) (goto-char (point-max))
(search-backward ";;;***" nil t) (search-backward ";;;***" nil t)
@ -155,83 +208,17 @@ even if it doesn't need reloading!"
(insert (insert
(with-temp-buffer (with-temp-buffer
(insert-file-contents path) (insert-file-contents path)
(let ((member-p (or (member path enabled-targets) (if-let (forms (doom--generate-autodefs-1 path (member path enabled-targets)))
(file-in-directory-p path doom-core-dir))) (concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
forms) "\n")
(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")
""))))))
(defun doom--cleanup-autoloads () (defun doom--cleanup-autoloads ()
(goto-char (point-min)) (goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t) (when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t 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). "Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, 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." Run this whenever your `doom!' block, or a module autoload file, is modified."
(let* ((default-directory doom-emacs-dir) (let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules)) (doom-modules (doom-modules))
(abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))
(targets ;; The following bindings are in `package-generate-autoloads'.
(file-expand-wildcards ;; Presumably for a good reason, so I just copied them
(expand-file-name "autoload/*.el" doom-core-dir))) (noninteractive t)
(enabled-targets (copy-sequence targets)) (backup-inhibited t)
case-fold-search) (version-control 'never)
(dolist (path (doom-module-load-path t)) (case-fold-search nil) ; reduce magic
(let* ((auto-dir (expand-file-name "autoload" path)) (autoload-timestamps nil)
(auto-file (expand-file-name "autoload.el" path))
(module (doom-module-from-path auto-file)) ;; Where we'll store the files we'll scan for autoloads. This should
(module-p (or (doom-module-p (car module) (cdr module)) ;; contain *all* autoload files, even in disabled modules, so we can
(file-equal-p path doom-private-dir)))) ;; scan those for autodefs. We start with the core libraries.
(when (file-exists-p auto-file) (targets (doom-glob doom-core-dir "autoload/*.el"))
(push auto-file targets) ;; A subset of `targets' in enabled modules
(if module-p (push auto-file enabled-targets))) (active-targets (copy-sequence targets)))
(dolist (file (doom-files-in auto-dir :match "\\.el$" :full t :sort nil))
(push file targets) (dolist (path (doom-module-load-path 'all-p))
(if module-p (push file enabled-targets))))) (when-let* ((files (cons (doom-glob path "autoload.el")
(if (and (not force-p) (doom-files-in (doom-path path "autoload")
(not doom-emacs-changed-p) :match "\\.el$")))
(file-exists-p doom-autoload-file) (files (delq nil files)))
(not (file-newer-than-file-p (expand-file-name "init.el" doom-private-dir) (appendq! targets files)
doom-autoload-file)) (when (or (doom-module-from-path path 'enabled-only)
(not (cl-loop for file in targets (file-equal-p path doom-private-dir))
if (file-newer-than-file-p file doom-autoload-file) (appendq! active-targets files))))
return t)))
(progn (print! (green "Doom core autoloads is up-to-date")) (print! (start "Checking core autoloads file"))
(doom-initialize-autoloads doom-autoload-file) (print-group!
nil) (if (and (not force-p)
(doom-delete-autoloads-file doom-autoload-file) (file-exists-p doom-autoload-file)
(message "Generating new autoloads.el") (not (file-newer-than-file-p doom-emacs-dir doom-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t) (not (cl-loop for dir
(with-temp-file doom-autoload-file in (append (doom-glob doom-private-dir "init.el*")
(doom--generate-header 'doom-reload-doom-autoloads) targets)
(prin1 `(setq doom--modules-cache ',doom-modules) (current-buffer)) if (file-newer-than-file-p dir doom-autoload-file)
(save-excursion return t)))
(doom--generate-autoloads (reverse enabled-targets))) (ignore
;; Replace autoload paths (only for module autoloads) with absolute (print! (success "Skipping core autoloads, they are up-to-date"))
;; paths for faster resolution during load and simpler `load-path' (doom-load-autoloads-file doom-autoload-file))
(save-excursion (print! (start "Regenerating core autoloads file"))
(doom--expand-autoloads)
(print! (green "✓ Expanded module autoload paths"))) (if (doom-delete-autoloads-file doom-autoload-file)
;; Generates stub definitions for functions/macros defined in disabled (print! (success "Deleted old %s") (filename doom-autoload-file))
;; modules, so that you will never get a void-function when you use (make-directory (file-name-directory doom-autoload-file) t))
;; them.
(save-excursion (with-temp-file doom-autoload-file
(doom--generate-autodefs (reverse targets) enabled-targets) (doom--generate-header 'doom-reload-core-autoloads)
(print! (green "✓ Generated autodefs"))) (save-excursion
;; Remove byte-compile-inhibiting file variables so we can byte-compile (doom--generate-autoloads active-targets)
;; the file, and autoload comments. (print! (success "Generated new autoloads.el")))
(doom--cleanup-autoloads) ;; Replace autoload paths (only for module autoloads) with absolute
(print! (green "✓ Clean up autoloads"))) ;; paths for faster resolution during load and simpler `load-path'
;; Byte compile it to give the file a chance to reveal errors. (save-excursion
(doom--byte-compile-file doom-autoload-file) (doom--expand-autoload-paths 'allow-internal-paths)
(doom--reload-files doom-autoload-file) (print! (success "Expanded module autoload paths")))
t))) ;; 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 () (defun doom--generate-package-autoloads ()
"Concatenates package autoload files, let-binds `load-file-name' around "Concatenates package autoload files, let-binds `load-file-name' around
them,and remove unnecessary `provide' statements or blank links. them,and remove unnecessary `provide' statements or blank links."
(dolist (pkg (hash-table-keys straight--build-cache))
Skips over packages in `doom-autoload-excluded-packages'." (unless (member pkg doom-autoload-excluded-packages)
(dolist (spec (doom-get-package-alist)) (let ((file (straight--autoloads-file pkg)))
(if-let* ((pkg (car spec)) (when (file-exists-p file)
(desc (cdr spec))) (insert-file-contents-literally file)
(unless (memq pkg doom-autoload-excluded-packages) (save-excursion
(let ((file (concat (package--autoloads-file-name desc) ".el"))) (while (re-search-forward "\\(?:\\_<load-file-name\\|#\\$\\)\\_>" nil t)
(when (file-exists-p file) ;; `load-file-name' is meaningless in a concatenated
(insert "(let ((load-file-name " (prin1-to-string (abbreviate-file-name file)) "))\n") ;; mega-autoloads file, so we replace references to it and #$ with
(insert-file-contents file) ;; the file they came from.
(while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t) (unless (doom-point-in-string-or-comment-p)
(unless (nth 8 (syntax-ppss)) (replace-match (prin1-to-string (abbreviate-file-name file))
(replace-match "" t t))) t t))))
(unless (bolp) (insert "\n")) (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
(insert ")\n")))) (unless (doom-point-in-string-p)
(message "Couldn't find package desc for %s" (car spec))))) (replace-match "" t t)))
(unless (bolp) (insert "\n")))))))
(defun doom--generate-var-cache () (defun doom--generate-var-cache ()
"Print a `setq' form for expensive-to-initialize variables, so we can 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 (prin1 `(setq load-path ',load-path
auto-mode-alist ',auto-mode-alist auto-mode-alist ',auto-mode-alist
Info-directory-list ',Info-directory-list Info-directory-list ',Info-directory-list
doom-disabled-packages ',(mapcar #'car (doom-find-packages :disabled t)) doom-disabled-packages ',doom-disabled-packages)
package-activated-list ',package-activated-list)
(current-buffer))) (current-buffer)))
(defun doom--cleanup-package-autoloads () (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. FORCE-P (universal argument) is non-nil, regenerate it anyway.
This should be run whenever your `doom!' block or update your packages." This should be run whenever your `doom!' block or update your packages."
(let ((abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))) (print! (start "Checking package autoloads file"))
(if (and (not force-p) (print-group!
(not doom-emacs-changed-p) (if (and (not force-p)
(file-exists-p doom-package-autoload-file) (file-exists-p doom-package-autoload-file)
(not (file-newer-than-file-p doom-packages-dir doom-package-autoload-file)) (not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file))
(not (ignore-errors (not (cl-loop for dir in (straight--directory-files (straight--build-dir))
(cl-loop for key being the hash-keys of (doom-modules) if (cl-find-if
for path = (doom-module-path (car key) (cdr key) "packages.el") (lambda (dir)
if (file-newer-than-file-p path doom-package-autoload-file) (file-newer-than-file-p dir doom-package-autoload-file))
return t)))) (doom-glob (straight--build-dir dir) "*.el"))
(ignore (print! (green "Doom package autoloads is up-to-date")) return t))
(doom-initialize-autoloads doom-package-autoload-file)) (not (cl-loop with doom-modules = (doom-modules)
(let (case-fold-search) for key being the hash-keys of doom-modules
(doom-delete-autoloads-file doom-package-autoload-file) for path = (doom-module-path (car key) (cdr key) "packages.el")
(with-temp-file doom-package-autoload-file if (file-newer-than-file-p path doom-package-autoload-file)
(doom--generate-header 'doom-reload-package-autoloads) return t)))
(save-excursion (ignore
;; Cache important and expensive-to-initialize state here. (print! (success "Skipping package autoloads, they are up-to-date"))
(doom--generate-var-cache) (doom-load-autoloads-file doom-package-autoload-file))
(print! (green "✓ Cached package state")) (let (;; The following bindings are in `package-generate-autoloads'.
;; Concatenate the autoloads of all installed packages. ;; Presumably for a good reason, so I just copied them
(doom--generate-package-autoloads) (noninteractive t)
(print! (green "✓ Package autoloads included"))) (backup-inhibited t)
;; Remove `load-path' and `auto-mode-alist' modifications (most of them, (version-control 'never)
;; at least); they are cached later, so all those membership checks are (case-fold-search nil) ; reduce magit
;; unnecessary overhead. (autoload-timestamps nil))
(doom--cleanup-package-autoloads) (print! (start "Regenerating package autoloads file"))
(print! (green "✓ Removed load-path/auto-mode-alist entries"))))
(doom--byte-compile-file doom-package-autoload-file) (if (doom-delete-autoloads-file doom-package-autoload-file)
(doom--reload-files doom-package-autoload-file) (print! (success "Deleted old %s") (filename doom-package-autoload-file))
t))) (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))

View file

@ -1,21 +1,24 @@
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*- ;;; 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. "Byte-compiles your config or selected modules.
compile [TARGETS...] compile [TARGETS...]
compile :core :private lang/python compile :core :private lang/python
compile feature lang compile feature lang
Accepts :core, :private and :plugins as special arguments, indicating you want Accepts :core and :private as special arguments, which target Doom's core files
to byte-compile Doom's core files, your private config or your ELPA plugins, and your private config files, respectively. To recompile your packages, use
respectively.") 'doom rebuild' instead."
(doom-byte-compile targets))
(dispatcher! (recompile rc) (doom-byte-compile args 'recompile) (defcli! (recompile rc) (&rest targets)
"Re-byte-compiles outdated *.elc files.") "Re-byte-compiles outdated *.elc files."
(doom-byte-compile targets 'recompile))
(dispatcher! clean (doom-clean-byte-compiled-files) (defcli! clean ()
"Delete all *.elc files.") "Delete all *.elc files."
(doom-clean-byte-compiled-files))
;; ;;
@ -25,9 +28,10 @@ respectively.")
(let ((filename (file-name-nondirectory path))) (let ((filename (file-name-nondirectory path)))
(or (string-prefix-p "." filename) (or (string-prefix-p "." filename)
(string-prefix-p "test-" 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. "Byte compiles your emacs configuration.
init.el is always byte-compiled by this. 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." If RECOMPILE-P is non-nil, only recompile out-of-date files."
(let ((default-directory doom-emacs-dir) (let ((default-directory doom-emacs-dir)
(total-ok 0) (doom-modules (doom-modules))
(total-fail 0) (byte-compile-verbose doom-debug-mode)
(total-noop 0) (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
compile-plugins-p
;; In case it is changed during compile-time
(auto-mode-alist auto-mode-alist)
(noninteractive t)
targets) targets)
(dolist (module (delete-dups modules) (nreverse targets))
(pcase module (let (target-dirs)
(":core" (push doom-core-dir targets)) (dolist (module (delete-dups modules))
(":private" (push doom-private-dir targets)) (pcase module
(":plugins" (":core"
(cl-loop for (_name . desc) in (doom-get-package-alist) (push (doom-glob doom-emacs-dir "init.el") targets)
do (package--compile desc)) (push doom-core-dir target-dirs))
(setq compile-plugins-p t (":private"
modules (delete ":plugins" modules))) (push doom-private-dir target-dirs))
((pred file-directory-p) ((pred file-directory-p)
(push module targets)) (push module target-dirs))
((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
(push (doom-module-locate-path (push (doom-module-locate-path
(doom-keyword-intern (match-string 1 module)) (doom-keyword-intern (match-string 1 module))
(intern (match-string 2 module))) (intern (match-string 2 module)))
targets)))) target-dirs))
(cl-block 'byte-compile (_ (user-error "%S is not a valid target" module))))
;; If we're just here to byte-compile our plugins, we're done!
(and (not modules) (and (or (null modules) (member ":private" modules))
compile-plugins-p (not recompile-p)
(cl-return-from 'byte-compile t)) (not (or doom-auto-accept
(unless (or (equal modules '(":core"))
recompile-p)
(unless (or doom-auto-accept
(y-or-n-p (y-or-n-p
(concat "Warning: byte compiling is for advanced users. It will interfere with your\n" (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" "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" "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" "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" "`bin/doom clean` to clear out your *.elc files.\n\n"
"Byte-compile anyway?"))) "Byte-compile anyway?"))))
(message "Aborting.") (user-error "Aborting"))
(cl-return-from 'byte-compile)))
(when (and (not recompile-p) ;; But first we must be sure that Doom and your private config have been
(or (null modules) ;; fully loaded. Which usually aren't so in an noninteractive session.
(equal modules '(":core")))) (let (noninteractive)
(doom-clean-byte-compiled-files)) (doom-initialize 'force)
(let (doom-emacs-changed-p (doom-initialize-core)
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)
(doom-initialize-modules 'force)) (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) (unless target-dirs
doom-modules-dirs))) (push (doom-glob doom-emacs-dir "init.el") targets)
(setq targets ;; If no targets were supplied, then we use your module list.
(append (list doom-core-dir) (appendq! target-dirs
(delete doom-private-dir (doom-module-load-path)))))) (list doom-core-dir)
;; Assemble el files we want to compile; taking into account that (nreverse
;; MODULES may be a list of MODULE/SUBMODULE strings from the command (cl-remove-if-not
;; line. (lambda (path) (file-in-directory-p path doom-emacs-dir))
(let ((target-files (doom-files-in targets :filter #'doom--byte-compile-ignore-file-p :sort nil))) ;; Omit `doom-private-dir', which is always first
(when (or (not modules) (cdr (doom-module-load-path))))))
(member ":core" modules))
(push (expand-file-name "init.el" doom-emacs-dir) ;; Assemble el files we want to compile; taking into account that MODULES
target-files)) ;; may be a list of MODULE/SUBMODULE strings from the command line.
(unless target-files (appendq! targets
(if targets (doom-files-in target-dirs
(message "Couldn't find any valid targets") :match "\\.el$"
(message "No targets to %scompile" (if recompile-p "re" ""))) :filter #'doom--byte-compile-ignore-file-p)))
(cl-return-from 'byte-compile))
(require 'use-package) (unless targets
(condition-case e (print!
(let ((use-package-defaults use-package-defaults) (if targets
(use-package-expand-minimally t) (warn "Couldn't find any valid targets")
(load-path load-path) (info "No targets to %scompile" (if recompile-p "re" ""))))
kill-emacs-hook kill-buffer-query-functions) (cl-return nil))
;; Prevent packages from being loaded at compile time if they
;; don't meet their own predicates. (print!
(push (list :no-require t (info (if recompile-p
(lambda (_name args) "Recompiling stale elc files..."
(or (when-let (pred (or (plist-get args :if) "Byte-compiling your config (may take a while)...")))
(plist-get args :when))) (print-group!
(not (eval pred t))) (require 'use-package)
(when-let (pred (plist-get args :unless)) (condition-case e
(eval pred t))))) (let ((total-ok 0)
use-package-defaults) (total-fail 0)
(dolist (target (cl-delete-duplicates (mapcar #'file-truename target-files) :test #'equal)) (total-noop 0)
(if (or (not recompile-p) (use-package-defaults use-package-defaults)
(let ((elc-file (byte-compile-dest-file target))) (use-package-expand-minimally t)
(and (file-exists-p elc-file) kill-emacs-hook kill-buffer-query-functions)
(file-newer-than-file-p target elc-file)))) ;; Prevent packages from being loaded at compile time if they
(let ((result (if (or (string-match-p "/\\(?:packages\\|doctor\\)\\.el$" target) ;; don't meet their own predicates.
(not (doom--file-cookie-p target))) (push (list :no-require t
'no-byte-compile (lambda (_name args)
(byte-compile-file target))) (or (when-let (pred (or (plist-get args :if)
(short-name (if (file-in-directory-p target doom-emacs-dir) (plist-get args :when)))
(file-relative-name target doom-emacs-dir) (not (eval pred t)))
(abbreviate-file-name target)))) (when-let (pred (plist-get args :unless))
(cl-incf (eval pred t)))))
(cond ((eq result 'no-byte-compile) use-package-defaults)
(print! (dark (white "⚠ Ignored %s")) short-name)
total-noop) (unless recompile-p
((null result) (doom-clean-byte-compiled-files))
(print! (red "✕ Failed to compile %s") short-name)
total-fail) (dolist (target (delete-dups targets))
(t (cl-incf
(print! (green "✓ Compiled %s") short-name) (if (not (or (not recompile-p)
(load target t t) (let ((elc-file (byte-compile-dest-file target)))
total-ok)))) (and (file-exists-p elc-file)
(cl-incf total-noop))) (file-newer-than-file-p target elc-file)))))
(print! (bold (color (if (= total-fail 0) 'green 'red) total-noop
"%s %d/%d file(s) (%d ignored)")) (pcase (if (doom-file-cookie-p target)
(if recompile-p "Recompiled" "Compiled") (byte-compile-file target)
total-ok (- (length target-files) total-noop) 'no-byte-compile)
total-noop) (`no-byte-compile
(or (= total-fail 0) (print! (info "Ignored %s") (relpath target))
(error "Failed to compile some files"))) total-noop)
((debug error) (`nil
(print! (red "\nThere were breaking errors.\n\n%s") (print! (error "Failed to compile %s") (relpath target))
"Reverting changes...") total-fail)
(signal 'doom-error (list 'byte-compile e)))))))) (_
(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 () (defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private "Delete all the compiled elc files in your Emacs configuration and private
module. This does not include your byte-compiled, third party packages.'" module. This does not include your byte-compiled, third party packages.'"
(cl-loop with default-directory = doom-emacs-dir (print! (start "Cleaning .elc files"))
for path (print-group!
in (append (doom-files-in doom-emacs-dir :match "\\.elc$" :depth 0 :sort nil) (cl-loop with default-directory = doom-emacs-dir
(doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil) with success = nil
(doom-files-in doom-core-dir :match "\\.elc$" :sort nil) for path
(doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil)) in (append (doom-glob doom-emacs-dir "*.elc")
for truepath = (file-truename path) (doom-files-in doom-private-dir :match "\\.elc$" :depth 1)
if (file-exists-p path) (doom-files-in doom-core-dir :match "\\.elc$")
do (delete-file path) (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4))
and do if (file-exists-p path)
(print! (green "✓ Deleted %s") do (delete-file path)
(if (file-in-directory-p truepath default-directory) and do (print! (success "Deleted %s") (relpath path))
(file-relative-name truepath) and do (setq success t)
(abbreviate-file-name truepath))) finally do
finally do (print! (bold (green "Everything is clean"))))) (print! (if success
(success "All elc files deleted")
(info "No elc files to clean"))))))

View file

@ -1,7 +1,38 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*- ;;; core/cli/debug.el -*- lexical-binding: t; -*-
(dispatcher! info (doom/info) (load! "autoload/debug" doom-core-dir)
"Output system info in markdown for bug reports.")
(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)

View file

@ -1,44 +1,46 @@
;;; core/cli/env.el -*- lexical-binding: t; -*- ;;; core/cli/env.el -*- lexical-binding: t; -*-
(dispatcher! env (defcli! env (&rest args)
(let ((env-file (abbreviate-file-name doom-env-file))) "Creates or regenerates your envvars 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.
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 The envvars file is created by scraping your (interactive) shell environment
auto enable auto-reloading of your envvars file (on `doom refresh`) into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env'
clear deletes your envvar file (if it exists) and disables auto-reloading (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) This is useful in cases where you cannot guarantee that Emacs (or the daemon)
will contain a list of environment variables scraped from your shell environment will be launched from the correct environment (e.g. on MacOS or through certain
and loaded when Doom starts (if it exists). This is necessary when Emacs can't app launchers on Linux).
be launched from your shell environment (e.g. on MacOS or certain app launchers
on Linux).
To generate a file, run `doom env refresh`. If you'd like this file to be This file is automatically regenerated when you run this command or 'doom
auto-reloaded when running `doom refresh`, run `doom env enable` instead (only refresh'. However, 'doom refresh' will only regenerate this file if it exists.
needs to be run once).")
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 ;; Doom envvars
"^INSECURE$" "^INSECURE$"
"^DEBUG$" "^DEBUG$"
"^YES$") "^YES$"
"^__")
"Environment variables to not save in `doom-env-file'. "Environment variables to not save in `doom-env-file'.
Each string is a regexp, matched against variable names to omit from 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." `doom-env-ignored-vars' are removed."
(when (or force-p (not (file-exists-p doom-env-file))) (when (or force-p (not (file-exists-p doom-env-file)))
(with-temp-file 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) (if (file-exists-p doom-env-file)
"Regenerating" "Regenerating"
"Generating") "Generating")
(abbreviate-file-name doom-env-file)) (relpath doom-env-file doom-emacs-dir))
(let ((process-environment doom-site-process-environment)) (let ((process-environment doom--initial-process-environment))
(insert (let ((shell-command-switch doom-env-switches)
(concat (error-buffer (get-buffer-create "*env errors*")))
"# -*- mode: dotenv -*-\n" (print! (info "Scraping shell environment with '%s %s %s'")
(format "# Generated with: %s %s %s\n" (filename shell-file-name)
shell-file-name shell-command-switch
doom-env-switches (filename doom-env-executable))
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)
(save-excursion (save-excursion
(insert (shell-command-to-string doom-env-executable))) (shell-command doom-env-executable (current-buffer) error-buffer))
;; Remove undesireable variables (print-group!
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t) (let ((errors (with-current-buffer error-buffer (buffer-string))))
(save-excursion (unless (string-empty-p errors)
(let* ((valend (or (save-match-data (print! (info "Error output:\n\n%s") (indent 4 errors))))
(when (re-search-forward "^\\([^= ]+\\)=" nil t) ;; Remove undesireable variables
(line-beginning-position))) (insert
(point-max))) (concat
(var (match-string 1)) "# -*- mode: dotenv -*-\n"
(value (buffer-substring-no-properties (point) (1- valend)))) (format "# Generated with: %s %s %s\n"
(when (cl-loop for regexp in doom-env-ignored-vars shell-file-name
if (string-match-p regexp var) doom-env-switches
return t) doom-env-executable)
(message "Ignoring %s" var) "# ---------------------------------------------------------------------------\n"
(delete-region (match-beginning 0) (1- valend)))))) "# This file was auto-generated by `doom env'. It contains a list of environment\n"
(print! (green "Envvar successfully generated"))))))) "# 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)))))

104
core/cli/install.el Normal file
View file

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

View file

@ -1,55 +1,62 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el ;;; core/cli/packages.el
;; (defmacro doom--ensure-autoloads-while (&rest body)
;;; Helpers `(progn
(doom-reload-core-autoloads)
(defmacro doom--condition-case! (&rest body) (when (progn ,@body)
`(condition-case-unless-debug e (doom-reload-package-autoloads 'force-p))
(progn ,@body) t))
('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)))
;; ;;
;;; Dispatchers ;;; Dispatchers
(dispatcher! (install i) (defcli! (update u) (&rest args)
(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)
"Updates packages. "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 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) (defcli! (rebuild build b) (&rest args)
(doom--ensure-autoloads-while #'doom-packages-autoremove) "Rebuilds all installed packages.
"Removes packages that are no longer needed.
This includes packages installed with 'M-x package-install' without an This ensures that all needed files are symlinked from their package repo and
accompanying `package!' declaration in an enabled module's packages.el file or their elisp files are byte-compiled."
your private one.") (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
;; "<Not implemented yet>"
;; (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 Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be installed." a list of packages that will be installed."
(print! "Looking for packages to install...") (print! "> Installing & building packages...")
(let ((packages (doom-get-missing-packages))) (print-group!
(cond ((not packages) (let ((n 0))
(print! (green "No packages to install!")) (dolist (package (hash-table-keys straight--recipe-cache))
nil) (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) (defun doom-packages-rebuild (&optional auto-accept-p all)
(doom-refresh-packages-maybe doom-debug-mode) "(Re)build all packages."
(dolist (pkg packages) (print! (start "(Re)building %spackages...") (if all "all " ""))
(print! "Installing %s" (car pkg)) (print-group!
(doom--condition-case! (let ((n 0))
(let ((result (if all
(or (and (doom-package-installed-p (car pkg)) (let ((straight--packages-to-rebuild :all)
(not (doom-package-different-backend-p (car pkg))) (straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(not (doom-package-different-recipe-p (car pkg))) (dolist (package (hash-table-keys straight--recipe-cache))
'already-installed) (straight-use-package
(and (doom-install-package (car pkg) (cdr pkg)) (intern package) nil (lambda (_) (cl-incf n) nil) " ")))
(setq success t) (dolist (recipe (hash-table-values straight--recipe-cache))
'success) (straight--with-plist recipe (package local-repo no-build)
'failure)) (unless (or no-build (null local-repo))
(pin-label ;; REVIEW We do these modification checks manually because
(and (plist-member (cdr pkg) :pin) ;; Straight's checks seem to miss stale elc files. Need
(format " [pinned: %s]" (plist-get (cdr pkg) :pin))))) ;; more tests to confirm this.
(print! "%s%s" (when (or (ignore-errors
(pcase result (gethash package straight--packages-to-rebuild))
(`already-installed (dark (white "⚠ ALREADY INSTALLED"))) (gethash package straight--cached-package-modifications)
(`success (green "✓ DONE")) (not (file-directory-p (straight--build-dir package)))
(`failure (red "✕ FAILED"))) (cl-loop for file
(or pin-label ""))))) in (doom-files-in (straight--build-dir package)
(print! (bold (green "Finished!"))) :match "\\.el$"
(when success :full t)
(set-file-times doom-packages-dir) for elc-file = (byte-compile-dest-file file)
(doom-delete-autoloads-file doom-package-autoload-file)) if (and (file-exists-p elc-file)
success))))) (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. "Updates packages.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be updated." a list of packages that will be updated."
(print! "Looking for outdated packages...") (print! (start "Scanning for outdated packages (this may take a while)..."))
(let ((packages (cl-sort (cl-copy-list (doom-get-outdated-packages)) #'string-lessp (print-group!
:key #'car))) (when timeout
(cond ((not packages) (print! (info "Using %S as timeout value" timeout)))
(print! (green "Everything is up-to-date")) ;; REVIEW Does this fail gracefully enough? Is it error tolerant?
nil) ;; 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) ;;; PURGE (for the emperor)
(dolist (pkg packages) (defun doom--prompt-p (list-fn list preamble postamble)
(print! "Updating %s" (car pkg)) (or (y-or-n-p (format "%s%s\n\n%s"
(doom--condition-case! (if preamble (concat preamble "\n\n") "")
(print! (mapconcat list-fn list "\n")
(let ((result (doom-update-package (car pkg) t))) (or postamble "")))
(when result (setq success t)) (user-error! "Aborted")))
(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)))))
(defun doom-packages-autoremove (&optional auto-accept-p) (defun doom--prompt-columns-p (row-fn list preamble postamble)
"Auto-removes orphaned packages. (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 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. 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 Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be removed." a list of packages that will be removed."
(print! "Looking for orphaned packages...") (print! (start "Searching for orphaned packages to purge (for the emperor)..."))
(let ((packages (doom-get-orphaned-packages))) (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
(cond ((not packages) (let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort))
(print! (green "No unused packages to remove")) (bdirs (straight--directory-files (straight--build-dir) nil nil 'sort)))
nil) (list (cl-remove-if (doom-rpartial #'gethash straight--profile-cache)
bdirs)
((not (cl-remove-if (doom-rpartial #'straight--checkhash straight--repo-cache)
(or auto-accept-p rdirs)
(y-or-n-p (cl-remove-if-not (doom-rpartial #'straight--checkhash straight--repo-cache)
(format "%s packages will be deleted:\n\n%s\n\nProceed?" rdirs)))
(length packages) (let (success)
(mapconcat (print-group!
(lambda (sym) (if (not builds-p)
(let ((old-backend (doom-package-backend sym 'noerror)) (print! (info "Skipping builds"))
(new-backend (doom-package-recipe-backend sym 'noerror))) (and (/= 0 (doom--packages-purge-builds builds-to-purge auto-accept-p))
(format "+ %s (%s)" sym (setq success t)
(cond ((null new-backend) (straight-prune-build-cache)))
"removed") (if (not elpa-p)
((eq old-backend new-backend) (print! (info "Skipping elpa packages"))
(symbol-name new-backend)) (and (/= 0 (doom--packages-purge-elpa auto-accept-p))
((format "%s -> %s" old-backend new-backend)))))) (setq success t)))
(sort (cl-copy-list packages) #'string-lessp) (if (not repos-p)
"\n"))))) (print! (info "Skipping repos"))
(user-error "Aborted!")) (and (/= 0 (doom--packages-purge-repos repos-to-purge auto-accept-p))
(setq success t))
((let (success) (and (doom--packages-regraft-repos repos-to-regraft auto-accept-p)
(dolist (pkg packages) (setq success t)))
(doom--condition-case! (when success
(let ((result (doom-delete-package pkg t))) (doom--finalize-straight)
(if result (setq success t)) 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)))))

View file

@ -1,9 +1,6 @@
;;; core/cli/patch-macos.el -*- lexical-binding: t; -*- ;;; core/cli/patch-macos.el -*- lexical-binding: t; -*-
(dispatcher! (patch-macos) (defcli! patch-macos () ; DEPRECATED
(doom-patch-macos (or (member "--undo" args)
(member "-u" args))
(doom--find-emacsapp-path))
"Patches Emacs.app to respect your shell environment. "Patches Emacs.app to respect your shell environment.
WARNING: This command is deprecated. Use 'doom env' instead. 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 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 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)) (message "%s successfully unpatched" appdir))
((file-exists-p newbin) ((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 ((user-error "patch-macos has been disabled. Please use 'doom env refresh' instead")))))
(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)))))

View file

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

View file

@ -1,69 +1,66 @@
;;; core/cli/test.el -*- lexical-binding: t; -*- ;;; core/cli/test.el -*- lexical-binding: t; -*-
(dispatcher! test (doom-run-tests args) (defcli! test (&rest targets)
"Run Doom unit tests.") "Run Doom unit tests."
(let (files error)
(unless targets
;; (setq targets
;; Library (cons doom-core-dir
(cl-remove-if-not
(defun doom-run-tests (&optional modules) (lambda (path) (file-in-directory-p path doom-emacs-dir))
"Run all loaded tests, specified by MODULES (a list of module cons cells) or ;; Omit `doom-private-dir', which is always first
command line args following a double dash (each arg should be in the (let (doom-modules)
'module/submodule' format). (load! "test/init" doom-core-dir)
(cdr (doom-module-load-path)))))))
If neither is available, run all tests in all enabled modules." (while targets
;; Core libraries aren't fully loaded in a noninteractive session, so we (let ((target (pop targets)))
;; reload it with `noninteractive' set to nil to force them to. (cond ((equal target ":core")
(let* ((noninteractive t) (appendq! files (nreverse (doom-glob doom-core-dir "test/test-*.el"))))
(doom-modules (doom-modules))) ((file-directory-p target)
(quiet! (doom-reload-autoloads)) (setq target (expand-file-name target))
(let ((target-paths (appendq! files (nreverse (doom-glob target "test/test-*.el"))))
;; Convert targets into a list of string paths, pointing to the root ((file-exists-p target)
;; directory of modules (push target files)))))
(cond ((stringp (car modules)) ; command line (require 'restart-emacs)
(save-match-data (with-temp-buffer
(cl-loop for arg in modules (setenv "DOOMDIR" (concat doom-core-dir "test/"))
if (string= arg ":core") collect doom-core-dir (setenv "DOOMLOCALDIR" (concat doom-local-dir "test/"))
else if (string-match-p "/" arg) (print! (start "Bootstrapping test environment, if necessary..."))
nconc (mapcar (apply-partially #'expand-file-name arg) (if (zerop
doom-modules-dirs) (call-process
else (restart-emacs--get-emacs-binary)
nconc (cl-loop for dir in doom-modules-dirs nil t nil "--batch"
for path = (expand-file-name arg dir) "-l" (concat doom-core-dir "core.el")
if (file-directory-p path) "--eval" (prin1-to-string
nconc (doom-files-in path :type 'dirs :depth 1 :full t :sort nil)) `(progn (doom-initialize 'force)
finally do (setq argv nil)))) (doom-initialize-modules)
(require 'core-cli)
(modules ; cons-cells given to MODULES (unless (package-installed-p 'buttercup)
(cl-loop for (module . submodule) in modules (package-refresh-contents)
if (doom-module-locate-path module submodule) (package-install 'buttercup))
collect it)) (doom-reload-core-autoloads 'force)
(when (doom-packages-install 'auto-accept)
((append (list doom-core-dir) (doom-reload-package-autoloads 'force))))))
(doom-module-load-path)))))) (message "%s" (buffer-string))
;; Load all the unit test files... (message "%s" (buffer-string))
(require 'buttercup) (error "Failed to bootstrap unit tests")))
(mapc (lambda (file) (load file :noerror (not doom-debug-mode))) (dolist (file files)
(doom-files-in (mapcar (apply-partially #'expand-file-name "test/") (if (doom-file-cookie-p file)
target-paths) (with-temp-buffer
:match "\\.el$" :full t)) (unless
;; ... then run them (zerop
(when doom-debug-mode (call-process
(setq buttercup-stack-frame-style 'pretty)) (restart-emacs--get-emacs-binary)
(let ((split-width-threshold 0) nil t nil "--batch"
(split-height-threshold 0) "-l" (concat doom-core-dir "core.el")
(window-min-width 0) "-l" (concat doom-core-dir "test/helpers.el")
(window-min-height 0)) "--eval" (prin1-to-string `(doom-initialize 'force))
(buttercup-run))))) "-l" "buttercup"
"-l" file
"-f" "buttercup-run"))
;; (setq error t))
;; Test library (message "%s" (buffer-string)))
(print! (info "Ignoring %s" (relpath file)))))
(defmacro insert! (&rest text) (if error
"Insert TEXT in buffer, then move cursor to last {0} marker." (error "A test failed")
`(progn t)))
(insert ,@text)
(when (search-backward "{0}" nil t)
(replace-match "" t t))))

View file

@ -1,86 +1,110 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*- ;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
(dispatcher! (upgrade up) (doom-upgrade) (defcli! (upgrade up) (&rest args)
"Checks out the latest Doom on this branch. "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 cd ~/.emacs.d
git pull git pull --rebase
bin/doom clean bin/doom clean
bin/doom refresh 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" (defvar doom-repo-url "https://github.com/hlissner/doom-emacs"
"TODO") "The git repo url for Doom Emacs.")
(defvar doom-repo-remote "_upgrade" (defvar doom-repo-remote "_upgrade"
"TODO") "The name to use as our staging remote.")
(defun doom--working-tree-dirty-p (dir) (defun doom--working-tree-dirty-p (dir)
(with-temp-buffer (cl-destructuring-bind (success . stdout)
(let ((default-directory dir)) (doom-sh "git" "status" "--porcelain" "-uno")
(if (zerop (process-file "git" nil (current-buffer) nil (if (= 0 success)
"status" "--porcelain" "-uno")) (string-match-p "[^ \t\n]" (buffer-string))
(string-match-p "[^ \t\n]" (buffer-string)) (error "Failed to check working tree in %s" dir))))
(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." "Upgrade Doom to the latest version non-destructively."
(require 'vc-git) (require 'vc-git)
(let* ((gitdir (expand-file-name ".git" doom-emacs-dir)) (let ((default-directory doom-emacs-dir)
(branch (vc-git--symbolic-ref doom-emacs-dir)) process-file-side-effects)
(default-directory doom-emacs-dir)) (print! (start "Preparing to upgrade Doom Emacs and its packages..."))
(unless (file-exists-p gitdir)
(error "Couldn't find %s. Was Doom cloned properly?" (let* ((branch (vc-git--symbolic-ref doom-emacs-dir))
(abbreviate-file-name gitdir))) (target-remote (format "%s/%s" doom-repo-remote branch)))
(unless branch (unless branch
(error "Couldn't detect what branch you're using. Is Doom detached?")) (error! (if (file-exists-p! ".git" doom-emacs-dir)
(when (doom--working-tree-dirty-p doom-emacs-dir) "Couldn't find Doom's .git directory. Was Doom cloned properly?"
(user-error "Refusing to upgrade because %S has been modified. Stash or undo your changes" "Couldn't detect what branch you're on. Is Doom detached?")))
(abbreviate-file-name doom-emacs-dir)))
(with-temp-buffer ;; We assume that a dirty .emacs.d is intentional and abort
(let ((buf (current-buffer))) (when (doom--working-tree-dirty-p default-directory)
(condition-case-unless-debug e (if (not force-p)
(progn (user-error! "%s\n\n%s"
(process-file "git" nil buf nil "remote" "remove" doom-repo-remote) (format "Refusing to upgrade because %S has been modified." (path doom-emacs-dir))
(unless (zerop (process-file "git" nil buf nil "remote" "add" "Either stash/undo your changes or run 'doom upgrade -f' to discard local changes.")
doom-repo-remote doom-repo-url)) (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)) (error "Failed to add %s to remotes" doom-repo-remote))
(unless (zerop (process-file "git" nil buf nil "fetch" "--tags" (or (zerop (car (doom-sh "git" "fetch" "--tags" doom-repo-remote branch)))
doom-repo-remote branch))
(error "Failed to fetch from upstream")) (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))))) (let ((this-rev (vc-git--rev-parse "HEAD"))
(unless rev (new-rev (vc-git--rev-parse target-remote)))
(error "Couldn't detect Doom's version. Is %s a repo?" (cond
(abbreviate-file-name doom-emacs-dir))) ((and (null this-rev)
(if (equal current-rev rev) (null new-rev))
(message "Doom is up to date!") (error "Failed to get revisions for %s" target-remote))
(message "Updates for Doom are available!\n\n Old revision: %s\n New revision: %s\n"
current-rev rev) ((equal this-rev new-rev)
(message "Comparision diff: https://github.com/hlissner/doom-emacs/compare/%s...%s\n" (print! (success "Doom is already up-to-date!"))
(substring current-rev 0 10) (substring rev 0 10)) t)
;; TODO Display newsletter diff
(unless (or doom-auto-accept (y-or-n-p "Proceed?")) ((print! (info "A new version of Doom Emacs is available!\n\n Old revision: %s (%s)\n New revision: %s (%s)\n"
(user-error "Aborted")) (substring this-rev 0 10)
(message "Removing byte-compiled files from your config (if any)") (cdr (doom-sh "git" "log" "-1" "--format=%cr" "HEAD"))
(doom-clean-byte-compiled-files) (substring new-rev 0 10)
(unless (zerop (process-file "git" nil buf nil "reset" "--hard" (cdr (doom-sh "git" "log" "-1" "--format=%cr" target-remote))))
(format "%s/%s" doom-repo-remote branch)))
(error "An error occurred while checking out the latest commit\n\n%s" (when (y-or-n-p "View the comparison diff in your browser?")
(buffer-string))) (print! (info "Opened github in your browser."))
(unless (equal (vc-git-working-revision doom-emacs-dir) rev) (browse-url (format "https://github.com/hlissner/doom-emacs/compare/%s...%s"
(error "Failed to checkout latest commit.\n\n%s" (buffer-string)))) this-rev
(doom-refresh 'force-p) new-rev)))
(when (doom-packages-update doom-auto-accept) (if (not (y-or-n-p "Proceed with upgrade?"))
(doom-reload-package-autoloads)) (ignore (print! (error "Aborted")))
(message "Done! Please restart Emacs for changes to take effect"))) (print! (start "Upgrading Doom Emacs..."))
(user-error (print-group!
(message "%s Aborting." (error-message-string e))) (doom-clean-byte-compiled-files)
(error (unless (and (zerop (car (doom-sh "git" "reset" "--hard" target-remote)))
(message "There was an unexpected error.\n\n%s\n\nOutput:\n%s" (equal (vc-git--rev-parse "HEAD") new-rev))
e (buffer-string)))))))) (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))))))

View file

@ -1,54 +1,98 @@
;;; -*- lexical-binding: t; no-byte-compile: t; -*- ;;; -*- lexical-binding: t; no-byte-compile: t; -*-
;; Eagerly load these libraries because this module may be loaded in a session (require 'seq)
;; 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")
;;
;; Dispatcher API
(defvar doom-auto-accept (getenv "YES") (defvar doom-auto-accept (getenv "YES")
"If non-nil, Doom will auto-accept any confirmation prompts during batch "If non-nil, Doom will auto-accept any confirmation prompts during batch
commands like `doom-packages-install', `doom-packages-update' and commands like `doom-packages-install', `doom-packages-update' and
`doom-packages-autoremove'.") `doom-packages-autoremove'.")
(defconst doom--dispatch-command-alist ()) (defvar doom-cli-pre-execute-hook nil
(defconst doom--dispatch-alias-alist ()) "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) (defun doom--dispatch-format (desc &optional short)
(with-temp-buffer (with-temp-buffer
(let ((fill-column 72)) (let ((fill-column 72))
(insert desc) (save-excursion
(goto-char (point-min)) (insert desc)
(while (re-search-forward "\n\n[^ \n]" nil t) (while (re-search-backward "\n\n[^ \n]" nil t)
(fill-paragraph))) (fill-paragraph))))
(if (not short) (if (not short)
(buffer-string) (buffer-string)
(goto-char (point-min)) (buffer-substring (line-beginning-position)
(buffer-substring-no-properties (line-end-position)))))
(line-beginning-position)
(line-end-position)))))
(defun doom--dispatch-help (&optional command desc &rest args) (defun doom--dispatch-help-1 (command)
"Display help documentation for a dispatcher command. If COMMAND and DESC are (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." omitted, show all available commands, their aliases and brief descriptions."
(if command (if fn
(princ (doom--dispatch-format desc)) (princ (documentation fn))
(print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description")) (print! (bold "%-11s\t%s\t%s" "Command:" "Alias" "Description"))
(dolist (spec (cl-sort doom--dispatch-command-alist #'string-lessp (print-group!
:key #'car)) (dolist (group (seq-group-by (lambda (key) (plist-get (gethash key doom--cli-commands) :group))
(cl-destructuring-bind (command &key desc _body) spec (hash-table-keys doom--cli-commands)))
(let ((aliases (cl-loop for (alias . cmd) in doom--dispatch-alias-alist (if (null (car group))
if (eq cmd command) (mapc #'doom--dispatch-help-1 (cdr group))
collect (symbol-name alias)))) (print! "%-30s\t%s" (bold (car group)) (gethash (car group) doom--cli-groups))
(print! " %-10s\t%s\t%s" (print-group!
command (if aliases (string-join aliases ",") "") (mapc #'doom--dispatch-help-1 (cdr group))))
(doom--dispatch-format desc t))))))) (terpri)))))
(defun doom-dispatch (cmd args &optional show-help) (defun doom-dispatch (cmd args &optional show-help)
"Parses ARGS and invokes a dispatcher. "Parses ARGS and invokes a dispatcher.
@ -59,17 +103,33 @@ If SHOW-HELP is non-nil, show the documentation for said dispatcher."
(when args (when args
(setq cmd (car args) (setq cmd (car args)
args (cdr args)))) args (cdr args))))
(cl-destructuring-bind (command &key desc body) (let ((fn (doom--dispatch-command cmd)))
(let ((sym (intern cmd))) (unless (fboundp fn)
(or (assq sym doom--dispatch-command-alist) (user-error "%S is not any command *I* know!" cmd))
(assq (cdr (assq sym doom--dispatch-alias-alist))
doom--dispatch-command-alist)
(user-error "Invalid command: %s" sym)))
(if show-help (if show-help
(apply #'doom--dispatch-help command desc args) (doom--dispatch-help fn args)
(funcall body 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 "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 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 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." BODY will be run when this dispatcher is called."
(declare (indent defun) (doc-string 3)) (declare (indent defun) (doc-string 3))
(cl-destructuring-bind (cmd &rest aliases) (let* ((names (mapcar #'symbol-name (doom-enlist names)))
(doom-enlist command) (fn (intern (format "doom-cli-%s" (car names))))
(plist (cl-loop while (keywordp (car body))
collect (pop body)
collect (pop body))))
(macroexp-progn (macroexp-progn
(append (reverse
(when aliases `((let ((plist ',plist))
`((dolist (alias ',aliases) (setq plist (plist-put plist :aliases ',(cdr names)))
(setf (alist-get alias doom--dispatch-alias-alist) ',cmd)))) (unless (or (plist-member plist :group)
`((setf (alist-get ',cmd doom--dispatch-command-alist) (null doom--cli-group))
(list :desc ,docstring (plist-put plist :group doom--cli-group))
:body (lambda (args) (ignore args) ,form)))))))) (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 ;; Load all of our subcommands
;; documentation for them, so... (defcli! (refresh re) (&rest args)
"Ensure Doom is properly set up.
(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.
This is the equivalent of running autoremove, install, autoloads, then This is the equivalent of running autoremove, install, autoloads, then
recompile. Run this whenever you: 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 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 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) (provide 'core-cli)
;;; core-cli.el ends here ;;; core-cli.el ends here

View file

@ -1,66 +1,89 @@
;;; core-editor.el -*- lexical-binding: t; -*- ;;; core-editor.el -*- lexical-binding: t; -*-
(defvar doom-large-file-size 2 (defvar doom-detect-indentation-excluded-modes '(fundamental-mode)
"Size (in MB) above which the user will be prompted to open the file literally "A list of major modes in which indentation should be automatically
to avoid performance issues. Opening literally means that no major or minor detected.")
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-local doom-inhibit-indent-detection nil (defvar-local doom-inhibit-indent-detection nil
"A buffer-local flag that indicates whether `dtrt-indent' should try to detect "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 indentation settings or not. This should be set by editorconfig if it
successfully sets indent_style/indent_size.") 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 ;;; File handling
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)
;; Remove hscroll-margin in shells, otherwise it causes jumpiness ;; Resolve symlinks when opening files, so that any operations are conducted
(setq-hook! '(eshell-mode-hook term-mode-hook) hscroll-margin 0) ;; from the file's true directory (like `find-file').
(setq find-file-visit-truename t)
(defun doom*optimize-literal-mode-for-large-files (buffer) ;; Disable the warning "X and Y are the same file". It's fine to ignore this
(with-current-buffer buffer ;; warning as it will redirect you to the existing buffer anyway.
(when find-file-literally (setq find-file-suppress-same-file-warnings t)
(setq buffer-read-only t)
(buffer-disable-undo)) ;; Create missing directories when we open a file that doesn't exist under a
buffer)) ;; directory tree that may not exist.
(advice-add #'find-file-noselect-1 :filter-return #'doom*optimize-literal-mode-for-large-files) (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 ;;; Built-in plugins
(def-package! autorevert (use-package! autorevert
;; revert buffers when their files/state have changed ;; revert buffers when their files/state have changed
:hook (focus-in . doom|auto-revert-buffers) :hook (focus-in . doom-auto-revert-buffers-h)
:hook (after-save . doom|auto-revert-buffers) :hook (after-save . doom-auto-revert-buffers-h)
:hook (doom-switch-buffer . doom|auto-revert-buffer) :hook (doom-switch-buffer . doom-auto-revert-buffer-h)
:hook (doom-switch-window . doom|auto-revert-buffer) :hook (doom-switch-window . doom-auto-revert-buffer-h)
:config :config
(setq auto-revert-verbose t ; let us know when it happens (setq auto-revert-verbose t ; let us know when it happens
auto-revert-use-notify nil 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 ;; 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 ;; 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. ;; 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)." "Auto revert's stale buffers (that are visible)."
(unless auto-revert-mode (unless auto-revert-mode
(dolist (buf (doom-visible-buffers)) (dolist (buf (doom-visible-buffers))
(with-current-buffer buf (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 ;; Keep track of recently opened files
:defer-incrementally (easymenu tree-widget timer) :defer-incrementally easymenu tree-widget timer
:after-call after-find-file :after-call after-find-file
:commands recentf-open-files :commands recentf-open-files
:config :config
@ -126,27 +155,27 @@ detected.")
file)) file))
(setq recentf-filename-handlers '(doom--recent-file-truename abbreviate-file-name)) (setq recentf-filename-handlers '(doom--recent-file-truename abbreviate-file-name))
(defun doom|recentf-touch-buffer () (add-hook! '(doom-switch-window-hook write-file-functions)
"Bump file in recent file list when it is switched or written to." (defun doom--recentf-touch-buffer-h ()
(when buffer-file-name "Bump file in recent file list when it is switched or written to."
(recentf-add-file buffer-file-name)) (when buffer-file-name
;; Return nil for `write-file-functions' (recentf-add-file buffer-file-name))
nil) ;; Return nil for `write-file-functions'
(add-hook 'doom-switch-window-hook #'doom|recentf-touch-buffer) nil))
(add-hook 'write-file-functions #'doom|recentf-touch-buffer)
(defun doom|recentf-add-dired-directory () (add-hook! 'dired-mode-hook
"Add dired directory to recentf file list." (defun doom--recentf-add-dired-directory-h ()
(recentf-add-file default-directory)) "Add dired directory to recentf file list."
(add-hook 'dired-mode-hook #'doom|recentf-add-dired-directory) (recentf-add-file default-directory)))
(unless noninteractive (unless noninteractive
(add-hook 'kill-emacs-hook #'recentf-cleanup) (add-hook 'kill-emacs-hook #'recentf-cleanup)
(quiet! (recentf-mode +1)))) (quiet! (recentf-mode +1))))
(def-package! savehist
(use-package! savehist
;; persist variables across sessions ;; persist variables across sessions
:defer-incrementally (custom) :defer-incrementally custom
:after-call post-command-hook :after-call post-command-hook
:config :config
(setq savehist-file (concat doom-cache-dir "savehist") (setq savehist-file (concat doom-cache-dir "savehist")
@ -155,32 +184,32 @@ detected.")
savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
(savehist-mode +1) (savehist-mode +1)
(defun doom|unpropertize-kill-ring () (add-hook! 'kill-emacs-hook
"Remove text properties from `kill-ring' in the interest of shrinking the (defun doom-unpropertize-kill-ring-h ()
savehist file." "Remove text properties from `kill-ring' for a smaller savehist file."
(setq kill-ring (cl-loop for item in kill-ring (setq kill-ring (cl-loop for item in kill-ring
if (stringp item) if (stringp item)
collect (substring-no-properties item) collect (substring-no-properties item)
else if item collect it))) else if item collect it)))))
(add-hook 'kill-emacs-hook #'doom|unpropertize-kill-ring))
(def-package! saveplace
(use-package! saveplace
;; persistent point location in buffers ;; persistent point location in buffers
:after-call (after-find-file dired-initial-position-hook) :after-call after-find-file dired-initial-position-hook
:config :config
(setq save-place-file (concat doom-cache-dir "saveplace") (setq save-place-file (concat doom-cache-dir "saveplace")
save-place-forget-unreadable-files t save-place-forget-unreadable-files t
save-place-limit 200) 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." "Recenter on cursor when loading a saved place."
:after-while #'save-place-find-file-hook
(if buffer-file-name (ignore-errors (recenter)))) (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)) (save-place-mode +1))
(def-package! server
(use-package! server
:when (display-graphic-p) :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 :init
(when-let (name (getenv "EMACS_SERVER_NAME")) (when-let (name (getenv "EMACS_SERVER_NAME"))
(setq server-name name)) (setq server-name name))
@ -192,8 +221,8 @@ savehist file."
;; ;;
;;; Packages ;;; Packages
(def-package! better-jumper (use-package! better-jumper
:after-call (pre-command-hook) :after-call pre-command-hook
:init :init
(global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward) (global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward)
(global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward)
@ -202,14 +231,14 @@ savehist file."
(better-jumper-mode +1) (better-jumper-mode +1)
(add-hook 'better-jumper-post-jump-hook #'recenter) (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." "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))) (better-jumper-set-jump (if (markerp (car args)) (car args)))
(let ((evil--jumps-jumping t) (let ((evil--jumps-jumping t)
(better-jumper--jumping t)) (better-jumper--jumping t))
(apply orig-fn args))) (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." "Set a jump point if ORIG-FN returns non-nil."
(let ((origin (point-marker)) (let ((origin (point-marker))
(result (result
@ -224,13 +253,13 @@ savehist file."
origin)))) origin))))
result)) result))
(defun doom|set-jump () (defun doom-set-jump-h ()
"Run `better-jumper-set-jump' but return nil, for short-circuiting hooks." "Run `better-jumper-set-jump' but return nil, for short-circuiting hooks."
(better-jumper-set-jump) (better-jumper-set-jump)
nil)) nil))
(def-package! command-log-mode (use-package! command-log-mode
:commands global-command-log-mode :commands global-command-log-mode
:config :config
(setq command-log-mode-auto-show t (setq command-log-mode-auto-show t
@ -239,21 +268,20 @@ savehist file."
command-log-mode-window-size 50)) command-log-mode-window-size 50))
(def-package! dtrt-indent (use-package! dtrt-indent
;; Automatic detection of indent settings ;; Automatic detection of indent settings
:unless noninteractive :unless noninteractive
:defer t :defer t
:init :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) (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 :config
(setq dtrt-indent-run-after-smie t) (setq dtrt-indent-run-after-smie t)
@ -261,9 +289,10 @@ savehist file."
(push '(t tab-width) dtrt-indent-hook-generic-mapping-list) (push '(t tab-width) dtrt-indent-hook-generic-mapping-list)
(defvar dtrt-indent-run-after-smie) (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 "Some smie modes throw errors when trying to guess their indentation, like
`nim-mode'. This prevents them from leaving Emacs in a broken state." `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)) (let ((dtrt-indent-run-after-smie dtrt-indent-run-after-smie))
(cl-letf* ((old-smie-config-guess (symbol-function 'smie-config-guess)) (cl-letf* ((old-smie-config-guess (symbol-function 'smie-config-guess))
((symbol-function 'smie-config-guess) ((symbol-function 'smie-config-guess)
@ -273,11 +302,10 @@ savehist file."
(message "[WARNING] Indent detection: %s" (message "[WARNING] Indent detection: %s"
(error-message-string e)) (error-message-string e))
(message "")))))) ; warn silently (message "")))))) ; warn silently
(funcall orig-fn arg)))) (funcall orig-fn arg)))))
(advice-add #'dtrt-indent-mode :around #'doom*fix-broken-smie-modes))
(def-package! helpful (use-package! helpful
;; a better *help* buffer ;; a better *help* buffer
:commands helpful--read-symbol :commands helpful--read-symbol
:init :init
@ -288,6 +316,12 @@ savehist file."
[remap describe-key] #'helpful-key [remap describe-key] #'helpful-key
[remap describe-symbol] #'doom/describe-symbol) [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 (after! apropos
;; patch apropos buttons to call helpful instead of help ;; patch apropos buttons to call helpful instead of help
(dolist (fun-bt '(apropos-function apropos-macro apropos-command)) (dolist (fun-bt '(apropos-function apropos-macro apropos-command))
@ -306,44 +340,69 @@ savehist file."
(add-hook 'imenu-after-jump-hook #'recenter) (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, ;; Auto-close delimiters and blocks as you type. It's more powerful than that,
;; but that is all Doom uses it for. ;; but that is all Doom uses it for.
:after-call (doom-switch-buffer-hook after-find-file) :after-call doom-switch-buffer-hook after-find-file
:commands (sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string) :commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string
:config :config
;; Load default smartparens rules for various languages
(require 'smartparens-config) (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 (setq sp-highlight-pair-overlay nil
sp-highlight-wrap-overlay nil sp-highlight-wrap-overlay nil
sp-highlight-wrap-tag-overlay nil sp-highlight-wrap-tag-overlay nil)
sp-show-pair-from-inside t ;; But if someone does want overlays enabled, evil users will be stricken with
sp-cancel-autoskip-on-backward-movement nil ;; an off-by-one issue where smartparens assumes you're outside the pair when
sp-show-pair-delay 0.1 ;; you're really at the last character in insert mode. We must correct this
sp-max-pair-length 4 ;; vile injustice.
sp-max-prefix-length 50 (setq sp-show-pair-from-inside t)
sp-escape-quotes-after-insert nil) ; not smart enough ;; ...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' ;; Silence some harmless but annoying echo-area spam
(defun doom|init-smartparens-in-eval-expression () (dolist (key '(:unmatched-expression :no-matching-tag))
"Enable `smartparens-mode' in the minibuffer, during `eval-expression' or (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'." `evil-ex'."
(when (memq this-command '(eval-expression evil-ex)) (when (memq this-command '(eval-expression evil-ex))
(smartparens-mode))) (smartparens-mode))))
(add-hook 'minibuffer-setup-hook #'doom|init-smartparens-in-eval-expression)
;; 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)
(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-entry-hook #'turn-off-smartparens-mode)
(add-hook 'evil-replace-state-exit-hook #'turn-on-smartparens-mode) (add-hook 'evil-replace-state-exit-hook #'turn-on-smartparens-mode)
(smartparens-global-mode +1)) (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 ;; Branching & persistent undo
:after-call (doom-switch-buffer-hook after-find-file) :after-call doom-switch-buffer-hook after-find-file
:config :config
(setq undo-tree-auto-save-history nil ; disable because unstable (setq undo-tree-auto-save-history nil ; disable because unstable
;; undo-in-region is known to cause undo history corruption, which can ;; 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/")))) `(("." . ,(concat doom-cache-dir "undo-tree-hist/"))))
(when (executable-find "zstd") (when (executable-find "zstd")
(defun doom*undo-tree-make-history-save-file-name (file) (defadvice! doom--undo-tree-make-history-save-file-name-a (file)
(concat file ".zst")) :filter-return #'undo-tree-make-history-save-file-name
(advice-add #'undo-tree-make-history-save-file-name :filter-return (concat file ".zst")))
#'doom*undo-tree-make-history-save-file-name))
(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) (dolist (item buffer-undo-list)
(and (consp item) (and (consp item)
(stringp (car item)) (stringp (car item))
(setcar item (substring-no-properties (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)) (global-undo-tree-mode +1))
(def-package! ws-butler (use-package! ws-butler
;; a less intrusive `delete-trailing-whitespaces' on save ;; a less intrusive `delete-trailing-whitespaces' on save
:after-call (after-find-file) :after-call after-find-file
:config :config
(setq ws-butler-global-exempt-modes (appendq! ws-butler-global-exempt-modes
(append ws-butler-global-exempt-modes '(special-mode comint-mode term-mode eshell-mode))
'(special-mode comint-mode term-mode eshell-mode)))
(ws-butler-global-mode)) (ws-butler-global-mode))
(provide 'core-editor) (provide 'core-editor)

View file

@ -6,26 +6,18 @@
;; entirely for performance reasons). ;; entirely for performance reasons).
(defvar doom-leader-key "SPC" (defvar doom-leader-key "SPC"
"The leader prefix key for Evil users. "The leader prefix key for Evil users.")
This needs to be changed from $DOOMDIR/init.el.")
(defvar doom-leader-alt-key "M-SPC" (defvar doom-leader-alt-key "M-SPC"
"An alternative leader prefix key, used for Insert and Emacs states, and for "An alternative leader prefix key, used for Insert and Emacs states, and for
non-evil users. non-evil users.")
This needs to be changed from $DOOMDIR/init.el.")
(defvar doom-localleader-key "SPC m" (defvar doom-localleader-key "SPC m"
"The localleader prefix key, for major-mode specific commands. "The localleader prefix key, for major-mode specific commands.")
This needs to be changed from $DOOMDIR/init.el.")
(defvar doom-localleader-alt-key "M-SPC m" (defvar doom-localleader-alt-key "M-SPC m"
"The localleader prefix key, for major-mode specific commands. Used for Insert "The localleader prefix key, for major-mode specific commands. Used for Insert
and Emacs states, and for non-evil users. and Emacs states, and for non-evil users.")
This needs to be changed from $DOOMDIR/init.el.")
(defvar doom-leader-map (make-sparse-keymap) (defvar doom-leader-map (make-sparse-keymap)
"An overriding keymap for <leader> keys.") "An overriding keymap for <leader> keys.")
@ -73,13 +65,14 @@ If any hook returns non-nil, all hooks after it are ignored.")
;; ;;
;;; General + leader/localleader keys ;;; General + leader/localleader keys
(require 'general) (use-package general
;; Convenience aliases :init
(defalias 'define-key! #'general-def) ;; Convenience aliases
(defalias 'unmap! #'general-unbind) (defalias 'define-key! #'general-def)
(defalias 'unmap! #'general-unbind))
;; `map!' uses this instead of `define-leader-key!' because it consumes 20-30% ;; HACK `map!' uses this instead of `define-leader-key!' because it consumes
;; more startup time, so we reimplement it ourselves. ;; 20-30% more startup time, so we reimplement it ourselves.
(defmacro doom--define-leader-key (&rest keys) (defmacro doom--define-leader-key (&rest keys)
(let (prefix forms wkforms) (let (prefix forms wkforms)
(while keys (while keys
@ -99,19 +92,16 @@ If any hook returns non-nil, all hooks after it are ignored.")
,bdef) ,bdef)
forms)) forms))
(when-let (desc (cadr (memq :which-key udef))) (when-let (desc (cadr (memq :which-key udef)))
(push `(which-key-add-key-based-replacements (prependq!
(general--concat t doom-leader-alt-key ,key) wkforms `((which-key-add-key-based-replacements
,desc) (general--concat t doom-leader-alt-key ,key)
wkforms) ,desc)
(push `(which-key-add-key-based-replacements (which-key-add-key-based-replacements
(general--concat t doom-leader-key ,key) (general--concat t doom-leader-key ,key)
,desc) ,desc))))))))
wkforms))))))
(macroexp-progn (macroexp-progn
(append (nreverse forms) (cons `(after! which-key ,@(nreverse wkforms))
(when wkforms (nreverse forms)))))
`((after! which-key
,@(nreverse wkforms))))))))
(defmacro define-leader-key! (&rest args) (defmacro define-leader-key! (&rest args)
"Define <leader> keys. "Define <leader> keys.
@ -157,26 +147,26 @@ localleader prefix."
;; Bind `doom-leader-key' and `doom-leader-alt-key' as late as possible to give ;; Bind `doom-leader-key' and `doom-leader-alt-key' as late as possible to give
;; the user a chance to modify them. ;; the user a chance to modify them.
(defun doom|init-leader-keys () (add-hook! 'doom-after-init-modules-hook
"Bind `doom-leader-key' and `doom-leader-alt-key'." (defun doom-init-leader-keys-h ()
(let ((map general-override-mode-map)) "Bind `doom-leader-key' and `doom-leader-alt-key'."
(if (not (featurep 'evil)) (let ((map general-override-mode-map))
(progn (if (not (featurep 'evil))
(cond ((equal doom-leader-alt-key "C-c") (progn
(set-keymap-parent doom-leader-map mode-specific-map)) (cond ((equal doom-leader-alt-key "C-c")
((equal doom-leader-alt-key "C-x") (set-keymap-parent doom-leader-map mode-specific-map))
(set-keymap-parent doom-leader-map ctl-x-map))) ((equal doom-leader-alt-key "C-x")
(define-key map (kbd doom-leader-alt-key) 'doom/leader)) (set-keymap-parent doom-leader-map ctl-x-map)))
(evil-define-key* '(normal visual motion) map (kbd doom-leader-key) 'doom/leader) (define-key map (kbd doom-leader-alt-key) 'doom/leader))
(evil-define-key* '(emacs insert) map (kbd doom-leader-alt-key) 'doom/leader)) (evil-define-key* '(normal visual motion) map (kbd doom-leader-key) 'doom/leader)
(general-override-mode +1))) (evil-define-key* '(emacs insert) map (kbd doom-leader-alt-key) 'doom/leader))
(add-hook 'doom-after-init-modules-hook #'doom|init-leader-keys) (general-override-mode +1))))
;; ;;
;;; Packages ;;; Packages
(def-package! which-key (use-package! which-key
:defer 1 :defer 1
:after-call pre-command-hook :after-call pre-command-hook
:init :init
@ -198,10 +188,6 @@ localleader prefix."
(which-key-mode +1)) (which-key-mode +1))
;;;###package hydra
(setq lv-use-seperator t)
;; ;;
;;; `map!' macro ;;; `map!' macro
@ -216,7 +202,7 @@ localleader prefix."
(?g . global)) (?g . global))
"A list of cons cells that map a letter to a evil state symbol.") "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. "Convert a KEYWORD into a list of evil state symbols.
For example, :nvi will map to (list 'normal 'visual 'insert). See 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 :leader 'lisp-indent-function 'defun)
(put :localleader 'lisp-indent-function 'defun) (put :localleader 'lisp-indent-function 'defun)
(put :map 'lisp-indent-function 'defun) (put :map 'lisp-indent-function 'defun)
(put :keymap 'lisp-indent-function 'defun)
(put :mode 'lisp-indent-function 'defun) (put :mode 'lisp-indent-function 'defun)
(put :prefix 'lisp-indent-function 'defun) (put :prefix 'lisp-indent-function 'defun)
(put :prefix-map 'lisp-indent-function 'defun) (put :prefix-map 'lisp-indent-function 'defun)
(put :unless 'lisp-indent-function 'defun)
(put :when 'lisp-indent-function 'defun)
;; specials ;; specials
(defvar doom--map-forms nil) (defvar doom--map-forms nil)
@ -271,7 +254,7 @@ For example, :nvi will map to (list 'normal 'visual 'insert). See
(setq rest nil)) (setq rest nil))
(:desc (:desc
(setq desc (pop rest))) (setq desc (pop rest)))
((or :map :map* :keymap) (:map
(doom--map-set :keymaps `(quote ,(doom-enlist (pop rest))))) (doom--map-set :keymaps `(quote ,(doom-enlist (pop rest)))))
(:mode (:mode
(push (cl-loop for m in (doom-enlist (pop rest)) (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))) doom--map-forms)))
(_ (_
(condition-case _ (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
(error "Not a valid `map!' property: %s" key))) (error "Not a valid `map!' property: %s" key)))
(setq desc nil)))) (setq desc nil))))
@ -414,7 +399,6 @@ Properties
:localleader [...] bind to localleader; requires a keymap :localleader [...] bind to localleader; requires a keymap
:mode [MODE(s)] [...] inner keybinds are applied to major MODE(s) :mode [MODE(s)] [...] inner keybinds are applied to major MODE(s)
:map [KEYMAP(s)] [...] inner keybinds are applied to KEYMAP(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 :prefix [PREFIX] [...] set keybind prefix for following keys. PREFIX
can be a cons cell: (PREFIX . DESCRIPTION) can be a cons cell: (PREFIX . DESCRIPTION)
:prefix-map [PREFIX] [...] same as :prefix, but defines a prefix keymap :prefix-map [PREFIX] [...] same as :prefix, but defines a prefix keymap

View file

@ -1,6 +1,6 @@
;;; core-lib.el -*- lexical-binding: t; -*- ;;; core-lib.el -*- lexical-binding: t; -*-
(let ((load-path doom-site-load-path)) (let ((load-path doom--initial-load-path))
(require 'subr-x) (require 'subr-x)
(require 'cl-lib)) (require 'cl-lib))
@ -12,56 +12,36 @@
;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we alias ;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we alias
;; them for 25 users. ;; them for 25 users.
(defalias 'if-let* #'if-let) (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 ;;; 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) (defun doom--resolve-hook-forms (hooks)
"Converts a list of modes into a list of hook symbols. "Converts a list of modes into a list of hook symbols.
@ -76,20 +56,25 @@ list is returned as-is."
collect (cadr hook) collect (cadr hook)
else collect (intern (format "%s-hook" (symbol-name hook))))))) else collect (intern (format "%s-hook" (symbol-name hook)))))))
(defun doom--assert-stage-p (stage macro) (defun doom--setq-hook-fns (hooks rest &optional singles)
(unless (or (bound-and-true-p byte-compile-current-file) (unless (or singles (= 0 (% (length rest) 2)))
;; Don't complain if we're being evaluated on-the-fly. Since forms (signal 'wrong-number-of-arguments (list #'evenp (length rest))))
;; are often evaluated (by `eval-region') or expanded (by (cl-loop with vars = (let ((args rest)
;; macroexpand) in a temp buffer in `emacs-lisp-mode'... vars)
(eq major-mode 'emacs-lisp-mode)) (while args
(cl-assert (eq stage doom--stage) (push (if singles
nil (list (pop args))
"Found %s call in non-%s.el file (%s)" (cons (pop args) (pop args)))
macro (symbol-name stage) vars))
(let ((path (FILE!))) (nreverse vars))
(if (file-in-directory-p path doom-emacs-dir) for hook in (doom--resolve-hook-forms hooks)
(file-relative-name path doom-emacs-dir) for mode = (string-remove-suffix "-hook" (symbol-name hook))
(abbreviate-file-name path)))))) 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) (defun doom-keyword-name (keyword)
"Returns the string name of KEYWORD (`keywordp') minus the leading colon." "Returns the string name of KEYWORD (`keywordp') minus the leading colon."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(cl-check-type :test keyword) (cl-check-type keyword keyword)
(substring (symbol-name keyword) 1)) (substring (symbol-name keyword) 1))
(defmacro doom-log (format-string &rest args) (defmacro doom-log (format-string &rest args)
@ -136,57 +121,239 @@ Accepts the same arguments as `message'."
format-string) format-string)
,@args)))) ,@args))))
(defun FILE! () (defalias 'doom-partial #'apply-partially)
"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))))
(defun DIR! () (defun doom-rpartial (fn &rest args)
"Returns the directory of the emacs lisp file this macro is called from." "Return a function that is a partial application of FUN to right-hand ARGS.
(let ((file (FILE!)))
(and file (file-name-directory file)))) 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) (defmacro λ! (&rest body)
"Expands to (lambda () (interactive) ,@body)." "Expands to (lambda () (interactive) ,@body)."
(declare (doc-string 1)) (declare (doc-string 1))
`(lambda () (interactive) ,@body)) `(lambda () (interactive) ,@body))
(defalias 'lambda! 'λ!)
(defmacro λ!! (command &optional arg) (defun λ!! (command &optional arg)
"Expands to a command that interactively calls COMMAND with prefix ARG." "Expands to a command that interactively calls COMMAND with prefix ARG."
(declare (doc-string 1)) (declare (doc-string 1))
`(lambda () (interactive) (lambda () (interactive)
(let ((current-prefix-arg ,arg)) (let ((current-prefix-arg arg))
(call-interactively ,command)))) (call-interactively command))))
(defalias 'lambda! 'λ!)
(defalias 'lambda!! 'λ!!) (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) (defmacro pushnew! (place &rest values)
"Like `cl-pushnew', but will prepend VALUES to PLACE. "Push VALUES sequentially into PLACE, if they aren't already present.
The order VALUES is preserved." This is a variadic `cl-pushnew'."
`(dolist (--value-- (nreverse (list ,@values))) (let ((var (make-symbol "result")))
(cl-pushnew --value-- ,place))) `(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) (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 `(setq ,list
(delq ,(if fetcher (delq ,(if fetcher
`(funcall ,fetcher ,elt ,list) `(funcall ,fetcher ,elt ,list)
elt) elt)
,list))) ,list)))
(defmacro cond! (&rest clauses) (defmacro delete! (elt list)
"An anaphoric `cond', which stores the conditional value in `it'." "Delete ELT from LIST in-place."
`(let (it) `(setq ,list (delete ,elt ,list)))
(cond ,@(cl-loop for (cond . body) in clauses
collect `((setq it ,cond) (defmacro add-transient-hook! (hook-or-function &rest forms)
,@body))))) "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) (defmacro defer-until! (condition &rest body)
"Run BODY when CONDITION is true (checks on `after-load-functions'). Meant to "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)) (declare (indent defun) (debug t))
`(if ,condition `(if ,condition
(progn ,@body) (progn ,@body)
,(let ((fun (make-symbol "doom|delay-form-"))) ,(let ((fn (intern (format "doom--delay-form-%s-h" (sxhash (cons condition body))))))
`(progn `(progn
(fset ',fun (lambda (&rest args) (fset ',fn (lambda (&rest args)
(when ,(or condition t) (when ,(or condition t)
(remove-hook 'after-load-functions #',fun) (remove-hook 'after-load-functions #',fn)
(unintern ',fun nil) (unintern ',fn nil)
(ignore args) (ignore args)
,@body))) ,@body)))
(put ',fun 'permanent-local-hook t) (put ',fn 'permanent-local-hook t)
(add-hook 'after-load-functions #',fun))))) (add-hook 'after-load-functions #',fn)))))
(defmacro defer-feature! (feature &optional mode) (defmacro defer-feature! (feature &optional mode)
"Pretend FEATURE hasn't been loaded yet, until FEATURE-hook is triggered. "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, 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 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." 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))) (mode (or mode feature)))
`(progn `(progn
(setq features (delq ',feature features)) (setq features (delq ',feature features))
@ -250,291 +417,32 @@ writes to `standard-output'."
(save-silently t)) (save-silently t))
(prog1 ,@forms (message "")))))) (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 (defmacro defadvice! (symbol arglist &optional docstring &rest body)
advised)." "Define an advice called SYMBOL and add it to PLACES.
(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 add-hook! (&rest args) ARGLIST is as in `defun'. WHERE is a keyword as passed to `advice-add', and
"A convenience macro for adding N functions to M hooks. 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'. \(fn SYMBOL ARGLIST &optional DOCSTRING &rest [WHERE PLACES...] BODY\)"
(declare (doc-string 3) (indent defun))
This macro accepts, in order: (unless (stringp docstring)
(push docstring body)
1. Optional properties :local and/or :append, which will make the hook (setq docstring nil))
buffer-local or append to the list of hooks (respectively), (let (where-alist)
2. The hook(s) to be added to: either an unquoted mode, an unquoted list of (while (keywordp (car body))
modes, a quoted hook variable or a quoted list of hook variables. If (push `(cons ,(pop body) (doom-enlist ,(pop body)))
unquoted, '-hook' will be appended to each symbol. where-alist))
3. The function(s) to be added: this can be one function, a list thereof, or `(progn
body forms (implicitly wrapped in a closure). (defun ,symbol ,arglist ,docstring ,@body)
,(when where-alist
Examples: `(dolist (targets (list ,@(nreverse where-alist)))
(add-hook! 'some-mode-hook 'enable-something) (same as `add-hook') (dolist (target (cdr targets))
(add-hook! some-mode '(enable-something and-another)) (advice-add target (car targets) #',symbol)))))))
(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))
(provide 'core-lib) (provide 'core-lib)
;;; core-lib.el ends here ;;; core-lib.el ends here

View file

@ -11,9 +11,6 @@
doom-modules-dir) doom-modules-dir)
"A list of module root directories. Order determines priority.") "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 (defconst doom-obsolete-modules
'((:feature (version-control (:emacs vc) (:ui vc-gutter)) '((:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell)) (spellcheck (:tools flyspell))
@ -34,7 +31,8 @@
(term (:term term))) (term (:term term)))
(:ui (doom-modeline (:ui modeline)) (:ui (doom-modeline (:ui modeline))
(fci (:ui fill-column)) (fci (:ui fill-column))
(evil-goggles (:ui ophints))) (evil-goggles (:ui ophints))
(tabbar (:ui tabs)))
(:app (email (:email mu4e)) (:app (email (:email mu4e))
(notmuch (:email notmuch)))) (notmuch (:email notmuch))))
"A tree alist that maps deprecated modules to their replacement(s). "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 your `doom!' block, a warning is emitted before replacing it with :emacs vc and
:ui vc-gutter.") :ui vc-gutter.")
(defvar doom--current-module nil) (defvar doom-inhibit-module-warnings (not noninteractive)
(defvar doom--current-flags nil) "If non-nil, don't emit deprecated or missing module warnings at startup.")
(defvar doom--modules-cache ())
;;
;;; Custom hooks ;;; Custom hooks
(defvar doom-before-init-modules-hook nil (defvar doom-before-init-modules-hook nil
"A list of hooks to run before Doom's modules' config.el files are loaded, but "A list of hooks to run before Doom's modules' config.el files are loaded, but
after their init.el files are loaded.") 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) (defvaralias 'doom-after-init-modules-hook 'after-init-hook)
(define-obsolete-variable-alias 'doom-post-init-hook 'doom-init-modules-hook "2.1.0") (defvar doom--current-module nil)
(define-obsolete-variable-alias 'doom-init-hook 'doom-before-init-modules-hook "2.1.0") (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 "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 session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil." non-nil."
(when (and (or force-p (when (or force-p (not doom-init-modules-p))
(not doom-init-modules-p)) (setq doom-init-modules-p t
(not (setq doom-modules nil)) doom-modules nil)
(load! "init" doom-private-dir t)) (when (load! "init" doom-private-dir t)
(setq doom-init-modules-p t) (when doom-modules
(unless (hash-table-p doom-modules) (maphash (lambda (key plist)
(setq doom-modules (make-hash-table :test 'equal))) (let ((doom--current-module key)
(maphash (lambda (key plist) (doom--current-flags (plist-get plist :flags)))
(let ((doom--current-module key) (load! "init" (plist-get plist :path) t)))
(doom--current-flags (plist-get plist :flags))) doom-modules))
(load! "init" (plist-get plist :path) t))) (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
doom-modules) (unless noninteractive
(run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) (when doom-modules
(unless noninteractive (maphash (lambda (key plist)
(maphash (lambda (key plist) (let ((doom--current-module key)
(let ((doom--current-module key) (doom--current-flags (plist-get plist :flags)))
(doom--current-flags (plist-get plist :flags))) (load! "config" (plist-get plist :path) t)))
(load! "config" (plist-get plist :path) t))) doom-modules))
doom-modules) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (load! "config" doom-private-dir t)))))
(load! "config" doom-private-dir t))))
;; ;;
@ -108,12 +101,11 @@ non-nil."
(defun doom-module-p (category module &optional flag) (defun doom-module-p (category module &optional flag)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(when (hash-table-p doom-modules) (let ((plist (gethash (cons category module) doom-modules)))
(let ((plist (gethash (cons category module) doom-modules))) (and plist
(and plist (or (null flag)
(or (null flag) (memq flag (plist-get plist :flags)))
(memq flag (plist-get plist :flags))) t)))
t))))
(defun doom-module-get (category module &optional property) (defun doom-module-get (category module &optional property)
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
@ -128,7 +120,7 @@ non-nil."
of PROPERTY and VALUEs. of PROPERTY and VALUEs.
\(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" \(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 (progn
(when plist (when plist
(when (cl-oddp (length 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 If the category isn't enabled this will always return nil. For finding disabled
modules use `doom-module-locate-path'." modules use `doom-module-locate-path'."
(let ((path (doom-module-get category module :path)) (let ((path (doom-module-get category module :path)))
file-name-handler-alist) (if file
(if file (expand-file-name file path) (let (file-name-handler-alist)
(expand-file-name file path))
path))) path)))
(defun doom-module-locate-path (category &optional module file) (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) if (file-exists-p path)
return (expand-file-name path))) return (expand-file-name path)))
(defun doom-module-from-path (&optional path) (defun doom-module-from-path (&optional path enabled-only)
"Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)." "Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path).
(or doom--current-module If ENABLED-ONLY, return nil if the containing module isn't enabled."
(let* (file-name-handler-alist (if (null path)
(path (or path (FILE!)))) (if doom--current-module
(save-match-data (if enabled-only
(setq path (file-truename path)) (and (doom-module-p (car doom--current-module)
(when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path) (cdr doom--current-module))
(when-let* ((category (match-string 1 path)) doom--current-module)
(module (match-string 2 path))) doom--current-module)
(cons (doom-keyword-intern category) (doom-module-from-path (file!)))
(intern module)))))))) (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) (defun doom-module-load-path (&optional module-dirs)
"Return an unsorted list of absolute file paths to activated modules. "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)) (declare (pure t) (side-effect-free t))
(append (if all-p (append (list doom-private-dir)
(doom-files-in doom-modules-dirs (if module-dirs
(doom-files-in (if (listp module-dirs)
module-dirs
doom-modules-dirs)
:type 'dirs :type 'dirs
:mindepth 1 :mindepth 1
:depth 1 :depth 1)
:full t
:sort nil)
(cl-loop for plist being the hash-values of (doom-modules) (cl-loop for plist being the hash-values of (doom-modules)
collect (plist-get plist :path))) collect (plist-get plist :path)))
(list doom-private-dir))) nil))
(defun doom-modules (&optional refresh-p) (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) (or (unless refresh-p doom-modules)
(let ((noninteractive t) (let ((noninteractive t)
doom-modules 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-minimum-reported-time (if doom-debug-mode 0 0.1)
use-package-expand-minimally (not noninteractive))) 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: ;; expand its lazy-loading capabilities. They are:
;; ;;
;; :after-call SYMBOL|LIST ;; Check out `use-package!'s documentation for more about these two.
;; :defer-incrementally SYMBOL|LIST|t ;; :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)) (defvar doom--deferred-packages-alist '(t))
(with-eval-after-load 'use-package-core (with-eval-after-load 'use-package-core
;; Macros are already fontified, no need for this ;; Macros are already fontified, no need for this
(font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords) (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 ;; Register all new keywords
;; 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
(dolist (keyword '(:defer-incrementally :after-call)) (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 (setq use-package-keywords
(use-package-list-insert keyword use-package-keywords :after))) (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) (defalias 'use-package-normalize/:defer-incrementally #'use-package-normalize-symlist)
(defun use-package-handler/:defer-incrementally (name _keyword targets rest state) (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) (defun use-package-handler/:after-call (name _keyword hooks rest state)
(if (plist-get state :demand) (if (plist-get state :demand)
(use-package-process-keywords name rest state) (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 (use-package-concat
`((fset ',fn `((fset ',fn
(lambda (&rest _) (lambda (&rest _)
(doom-log "Loading deferred package %s from %s" ',name ',fn) (doom-log "Loading deferred package %s from %s" ',name ',fn)
(condition-case e (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) ((debug error)
(message "Failed to load deferred package %s: %s" ',name e))) (message "Failed to load deferred package %s: %s" ',name e)))
(when-let (deferral-list (assq ',name doom--deferred-packages-alist)) (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 ;;; 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) (defmacro doom! (&rest modules)
"Bootstraps DOOM Emacs and its 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' 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 for a list of all recognized module trees. Order defines precedence (from most
to least)." to least)."
(if doom--modules-cache (unless (keywordp (car modules))
(progn (setq modules (eval modules t)))
(setq doom-modules doom--modules-cache) (let ((doom-modules
(doom-log "Using `doom-modules' cache")) (make-hash-table :test 'equal
(unless doom-modules :size (if modules (length modules) 150)
(setq doom-modules :rehash-threshold 1.0))
(make-hash-table :test 'equal (inhibit-message doom-inhibit-module-warnings)
:size (if modules (length modules) 150) category m)
:rehash-threshold 1.0))) (while modules
(let ((inhibit-message doom-inhibit-module-warnings) (setq m (pop modules))
category m) (cond ((keywordp m) (setq category m))
(while modules ((not category) (error "No module category specified for %s" m))
(setq m (pop modules)) ((and (listp m)
(cond ((keywordp m) (setq category m)) (keywordp (car m)))
((not category) (error "No module category specified for %s" m)) (pcase (car m)
((catch 'doom-modules (:cond
(let* ((module (if (listp m) (car m) m)) (cl-loop for (cond . mods) in (cdr m)
(flags (if (listp m) (cdr m)))) if (eval cond t)
(when-let* ((obsolete (assq category doom-obsolete-modules)) return (prependq! modules mods)))
(new (assq module obsolete))) (:if (if (eval (cadr m) t)
(let ((newkeys (cdr new))) (push (caddr m) modules)
(if (null newkeys) (prependq! modules (cdddr m))))
(message "WARNING %s module was removed" key) (fn (if (or (eval (cadr m) t)
(if (cdr newkeys) (eq fn :unless))
(message "WARNING %s module was removed and split into the %s modules" (prependq! modules (cddr m))))))
(list category module) (mapconcat #'prin1-to-string newkeys ", ")) ((catch 'doom-modules
(message "WARNING %s module was moved to %s" (let* ((module (if (listp m) (car m) m))
(list category module) (car newkeys))) (flags (if (listp m) (cdr m))))
(push category modules) (when-let* ((obsolete (assq category doom-obsolete-modules))
(dolist (key newkeys) (new (assq module obsolete)))
(push (if flags (let ((newkeys (cdr new)))
(nconc (cdr key) flags) (if (null newkeys)
(cdr key)) (message "WARNING %s module was removed" key)
modules) (if (cdr newkeys)
(push (car key) modules)) (message "WARNING %s module was removed and split into the %s modules"
(throw 'doom-modules t)))) (list category module) (mapconcat #'prin1-to-string newkeys ", "))
(if-let (path (doom-module-locate-path category module)) (message "WARNING %s module was moved to %s"
(doom-module-set category module :flags flags :path path) (list category module) (car newkeys)))
(message "WARNING Couldn't find the %s %s module" category module))))))))) (push category modules)
(when noninteractive (dolist (key newkeys)
(setq doom-inhibit-module-warnings t)) (push (if flags
`(setq doom-modules ',doom-modules)) (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) (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. "Declares and configures a package.
This is a thin wrapper around `use-package', and is ignored if the NAME 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 The first time any of these functions or hooks are executed, the package is
loaded. e.g. loaded. e.g.
(def-package! projectile (use-package! projectile
:after-call (pre-command-hook after-find-file dired-before-readin-hook) :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 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. specify it. A value of `t' implies NAME, e.g.
(def-package! abc (use-package! abc
;; This is equivalent to :defer-incrementally (abc) ;; This is equivalent to :defer-incrementally (abc)
:defer-incrementally t :defer-incrementally t
...)" ...)"
(declare (indent 1))
(unless (or (memq name doom-disabled-packages) (unless (or (memq name doom-disabled-packages)
;; At compile-time, use-package will forcibly load packages to ;; At compile-time, use-package will forcibly load packages to
;; prevent compile-time errors. However, if a Doom user has ;; prevent compile-time errors. However, if a Doom user has
@ -420,8 +458,9 @@ two extra properties:
(not (locate-library (symbol-name name))))) (not (locate-library (symbol-name name)))))
`(use-package ,name ,@plist))) `(use-package ,name ,@plist)))
(defmacro def-package-hook! (package when &rest body) (define-obsolete-function-alias 'def-package-hook! 'use-package-hook!) ; DEPRECATED
"Reconfigures a package's `def-package!' block. (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. 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 :pre-init :post-init :pre-config :post-config
WARNING: If :pre-init or :pre-config hooks return nil, the original 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)." to have them return non-nil (or exploit that to overwrite Doom's config)."
(declare (indent defun)) (declare (indent defun))
(doom--assert-stage-p 'init #'package!)
(unless (memq when '(:pre-init :post-init :pre-config :post-config)) (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 `(progn
(setq use-package-inject-hooks t) (setq use-package-inject-hooks t)
(add-hook! (add-hook ',(intern (format "use-package--%s--%s-hook"
',(intern (format "use-package--%s--%s-hook" package
package (substring (symbol-name when) 1)))
(substring (symbol-name when) 1))) (lambda () ,@body)
,@body))) 'append)))
(defmacro require! (category module &rest flags) (defmacro require! (category module &rest flags)
"Loads the CATEGORY MODULE module with FLAGS. "Loads the CATEGORY MODULE module with FLAGS.
@ -470,8 +508,7 @@ module."
(let ((doom--current-module ',(cons category module)) (let ((doom--current-module ',(cons category module))
(doom--current-flags ',flags)) (doom--current-flags ',flags))
(load! "init" module-path :noerror) (load! "init" module-path :noerror)
(let ((doom--stage 'config)) (load! "config" module-path :noerror))
(load! "config" module-path :noerror)))
('error ('error
(lwarn 'doom-modules :error (lwarn 'doom-modules :error
"%s in '%s %s' -> %s" "%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))) (and (cond (flag (memq flag (doom-module-get category module :flags)))
(module (doom-module-p category module)) (module (doom-module-p category module))
(doom--current-flags (memq category doom--current-flags)) (doom--current-flags (memq category doom--current-flags))
((let ((module-pair ((let ((module (doom-module-from-path)))
(or doom--current-module (unless module
(doom-module-from-path (FILE!))))) (error "featurep! couldn't figure out what module it was called from (in %s)"
(unless module-pair (file!)))
(error "featurep! call couldn't auto-detect what module its in (from %s)" (FILE!))) (memq category (doom-module-get (car module) (cdr module) :flags)))))
(memq category (doom-module-get (car module-pair) (cdr module-pair) :flags)))))
t)) t))
(defmacro after! (targets &rest body) (defmacro after! (package &rest body)
"Evaluate BODY after TARGETS (packages) have loaded. "Evaluate BODY after PACKAGE have loaded.
This is a wrapper around `with-eval-after-load' that: PACKAGE is a symbol or list of them. These are package names, not modes,
functions or variables. It can be:
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:
- An unquoted package symbol (the name of a package) - An unquoted package symbol (the name of a package)
(after! helm BODY...) (after! helm BODY...)
- An unquoted list of package symbols (i.e. BODY is evaluated once both magit - An unquoted list of package symbols (i.e. BODY is evaluated once both magit
and git-gutter have loaded) and git-gutter have loaded)
(after! (magit git-gutter) BODY...) (after! (magit git-gutter) BODY...)
- An unquoted, nested list of compound package lists, using :or/:any and/or - An unquoted, nested list of compound package lists, using any combination of
:and/:all :or/:any and :and/:all
(after! (:or package-a package-b ...) BODY...) (after! (:or package-a package-b ...) BODY...)
(after! (:and package-a package-b ...) BODY...) (after! (:and package-a package-b ...) BODY...)
(after! (:and package-a (:or package-b package-c) ...) BODY...) (after! (:and package-a (:or package-b package-c) ...) BODY...)
Without :or/:any/:and/:all, :and/:all are implied.
Note that: This is a wrapper around `eval-after-load' that:
- :or and :any are equivalent
- :and and :all are equivalent 1. Suppresses warnings for disabled packages at compile-time
- If these are omitted, :and is implied." 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)) (declare (indent defun) (debug t))
(unless (and (symbolp targets) (if (symbolp package)
(memq targets (bound-and-true-p doom-disabled-packages))) (unless (memq package (bound-and-true-p doom-disabled-packages))
(list (if (or (not (bound-and-true-p byte-compile-current-file)) (list (if (or (not (bound-and-true-p byte-compile-current-file))
(dolist (next (doom-enlist targets)) (require package nil 'noerror))
(unless (keywordp next) #'progn
(if (symbolp next) #'with-no-warnings)
(require next nil :no-error) (let ((body (macroexp-progn body)))
(load next :no-message :no-error))))) `(if (featurep ',package)
#'progn ,body
#'with-no-warnings) ;; We intentionally avoid `with-eval-after-load' to prevent
(if (symbolp targets) ;; eager macro expansion from pulling (or failing to pull) in
`(with-eval-after-load ',targets ,@body) ;; autoloaded macros/packages.
(pcase (car-safe targets) (eval-after-load ',package ',body)))))
((or :or :any) (let ((p (car package)))
(macroexp-progn (cond ((not (keywordp p))
(cl-loop for next in (cdr targets) `(after! (:and ,@package) ,@body))
collect `(after! ,next ,@body)))) ((memq p '(:or :any))
((or :and :all) (macroexp-progn
(dolist (next (cdr targets)) (cl-loop for next in (cdr package)
(setq body `((after! ,next ,@body)))) collect `(after! ,next ,@body))))
(car body)) ((memq p '(:and :all))
(_ `(after! (:and ,@targets) ,@body))))))) (dolist (next (cdr package))
(setq body `((after! ,next ,@body))))
(car body))))))
(provide 'core-modules) (provide 'core-modules)
;;; core-modules.el ends here ;;; core-modules.el ends here

View file

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

View file

@ -1,154 +1,205 @@
;;; core/core-packages.el -*- lexical-binding: t; -*- ;;; core/core-packages.el -*- lexical-binding: t; -*-
;; Emacs package management is opinionated, and so am I. I've bound together ;; Emacs package management is opinionated, and so is Doom. Doom uses `straight'
;; `use-package', `quelpa' and package.el to create my own, rolling-release, ;; to create a declarative, lazy-loaded and optionally rolling-release package
;; lazily-loaded package management system for Emacs. ;; 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. ;; ANyhow, interacting with this package management system is done through the
;; + `bin/doom update`: Updates packages that are out-of-date. ;; bin/doom script included with Doom Emacs. You'll find more about it by
;; + `bin/doom autoremove`: Uninstalls packages that are no longer needed. ;; 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 ;; + `bin/doom install`: a wizard that guides you through setting up Doom and
;; in `doom-core-dir'). These contain `package!' blocks that tell DOOM what ;; 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. ;; plugins to install and where from.
;; ;;
;; Why all the trouble? Because: ;; All that said, you can still use package.el's commands, but 'bin/doom
;; 1. *Scriptability:* I live in the command line. I want a shell-scriptable ;; refresh' will purge ELPA packages.
;; interface for updating and installing Emacs packages.
;; 2. *Reach:* I want packages from sources other than ELPA (like github or (defvar doom-init-packages-p nil
;; gitlab). Some plugins are out-of-date through official channels, have "If non-nil, Doom's package management system has been initialized.")
;; 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.
(defvar doom-packages () (defvar doom-packages ()
"A list of enabled packages. Each element is a sublist, whose CAR is the "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's name as a symbol, and whose CDR is the plist supplied to its
`package!' declaration. Set by `doom-initialize-packages'.") `package!' declaration. Set by `doom-initialize-packages'.")
(defvar doom-core-packages (defvar doom-core-packages '(straight use-package async)
'(persistent-soft use-package quelpa async)
"A list of packages that must be installed (and will be auto-installed if "A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.") missing) and shouldn't be deleted.")
(defvar doom-disabled-packages () (defvar doom-core-package-sources
"A list of packages that should be ignored by `def-package!' and `after!'.") '((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 (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-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 ;; I omit Marmalade because its packages are manually submitted rather
;; than pulled, so packages are often out of date with upstream. ;; than pulled, so packages are often out of date with upstream.
package-archives package-archives
`(("gnu" . "https://elpa.gnu.org/packages/") (let ((proto (if gnutls-verify-error "https" "http")))
("melpa" . "https://melpa.org/packages/") `(("gnu" . ,(concat proto "://elpa.gnu.org/packages/"))
("org" . "https://orgmode.org/elpa/"))) ("melpa" . ,(concat proto "://melpa.org/packages/"))
("org" . ,(concat proto "://orgmode.org/elpa/")))))
;; Don't save `package-selected-packages' to `custom-file' ;; Don't save `package-selected-packages' to `custom-file'
(advice-add #'package--save-selected-packages :override (defadvice! doom--package-inhibit-custom-file-a (&optional value)
(lambda (&optional value) (if value (setq package-selected-packages value)))) :override #'package--save-selected-packages
(if value (setq package-selected-packages value)))
(when (or (not gnutls-verify-error) ;;; straight
(not (ignore-errors (gnutls-available-p)))) (setq straight-base-dir doom-local-dir
(dolist (archive package-archives) straight-repository-branch "develop"
(setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil)))) 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 ;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch!
(setq quelpa-dir (expand-file-name "quelpa" doom-packages-dir) (defadvice! doom--straight-use-local-dir-a (orig-fn &rest args)
quelpa-verbose doom-debug-mode :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 (defun doom--finalize-straight ()
quelpa-checkout-melpa-p nil (mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist)))
quelpa-update-melpa-p nil (setq straight--transaction-alist nil))
quelpa-melpa-recipe-stores nil
quelpa-self-upgrade-p nil)
;; ;;
;;; Bootstrapper ;;; Bootstrapper
(defun doom-initialize-packages (&optional force-p) (defun doom-initialize-packages (&optional force-p)
"Ensures that Doom's package management system, package.el and quelpa are "Ensures that Doom's package system and straight.el are initialized.
initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
populated, if they aren't already.
If FORCE-P is non-nil, do it anyway. 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 This ensure `doom-packages' is populated, if isn't aren't already. Use this
ensure all the necessary package metadata is initialized and available for before any of straight's or Doom's package management's API to ensure all the
them." necessary package metadata is initialized and available for them."
(let ((load-prefer-newer t)) ; reduce stale code issues (unless doom-init-packages-p
;; package.el and quelpa handle themselves if their state changes during the (setq force-p t))
;; 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."
(when (or force-p (not (bound-and-true-p package--initialized))) (when (or force-p (not (bound-and-true-p package--initialized)))
(doom-log "Initializing package.el")
(require 'package) (require 'package)
(setq package-activated-list nil (package-initialize))
package--initialized nil) (when (or force-p (not doom-packages))
(let (byte-compile-warnings) (doom-log "Initializing straight")
(condition-case _ (setq doom-init-packages-p t)
(package-initialize) (unless (fboundp 'straight--reset-caches)
('error (package-refresh-contents) (doom-ensure-straight)
(setq doom--refreshed-p t) (require 'straight))
(package-initialize)))))) (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 () (defun doom-ensure-straight ()
"Make sure `doom-core-packages' are installed." "Ensure `straight' is installed and was compiled with this version of Emacs."
(when-let (core-packages (cl-remove-if #'package-installed-p doom-core-packages)) (defvar bootstrap-version)
(message "Installing core packages") (let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of
(unless doom--refreshed-p ;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d.
(package-refresh-contents)) (user-emacs-directory straight-base-dir)
(dolist (package core-packages) (bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el"))
(let ((inhibit-message t)) (bootstrap-version 5))
(package-install package)) (make-directory (doom-path straight-base-dir "straight/build") 'parents)
(if (package-installed-p package) (unless (featurep 'straight)
(message "✓ Installed %s" package) (unless (or (require 'straight nil t)
(error "✕ Couldn't install %s" package))) (file-readable-p bootstrap-file))
(message "Installing core packages...done"))) (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). "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 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 :recipe RECIPE
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example); Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
for packages to be installed from external sources. 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 :disable BOOL
Do not install or update this package AND disable all of its `def-package!' Do not install or update this package AND disable all of its `def-package!'
blocks. blocks.
@ -179,63 +227,55 @@ Accepts the following properties:
Returns t if package is successfully registered, and nil if it was disabled Returns t if package is successfully registered, and nil if it was disabled
elsewhere." elsewhere."
(declare (indent defun)) (declare (indent defun))
(doom--assert-stage-p 'packages #'package!)
(let ((old-plist (cdr (assq name doom-packages)))) (let ((old-plist (cdr (assq name doom-packages))))
(when recipe ;; Add current module to :modules
(when (cl-evenp (length recipe))
(setq plist (plist-put plist :recipe (cons name recipe))))
(setq pin nil
plist (plist-put plist :pin nil)))
(let ((module-list (plist-get old-plist :modules)) (let ((module-list (plist-get old-plist :modules))
(module (or doom--current-module (module (doom-module-from-path)))
(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)))))))
(unless (member module module-list) (unless (member module module-list)
(setq module-list (append module-list (list module) nil) (plist-put! plist :modules
plist (plist-put plist :modules module-list)))) (append module-list
(when built-in (list module)
(doom-log "Ignoring built-in package %S" name) nil))))
(when (equal built-in '(quote prefer))
(setq built-in `(locate-library ,(symbol-name name) nil doom-site-load-path)))) ;; Handle :built-in
(setq plist (plist-put plist :ignore (or built-in ignore))) (unless ignore
(while plist (when built-in
(unless (null (cadr plist)) (doom-log "Ignoring built-in package %S" name)
(setq old-plist (plist-put old-plist (car plist) (cadr plist)))) (when (equal built-in '(quote prefer))
(pop plist) (setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path))))
(pop plist)) (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) (setq plist old-plist)
;; TODO Add `straight-use-package-pre-build-function' support
(macroexp-progn (macroexp-progn
(append (when pin (append `((setf (alist-get ',name doom-packages) ',plist))
(doom-log "Pinning package '%s' to '%s'" name pin)
`((setf (alist-get ',name package-pinned-packages) ,pin)))
`((setf (alist-get ',name doom-packages) ',plist))
(when disable (when disable
(doom-log "Disabling package '%s'" name) `((doom-log "Disabling package %S" ',name)
`((add-to-list 'doom-disabled-packages ',name nil 'eq) (add-to-list 'doom-disabled-packages ',name nil 'eq)
nil)))))) 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) (defmacro disable-packages! (&rest packages)
"A convenience macro like `package!', but allows you to disable multiple "A convenience macro for disabling packages in bulk.
packages at once. Only use this macro in a module's (or your private) packages.el file."
Only use this macro in a module's packages.el file."
(doom--assert-stage-p 'packages #'disable-packages!)
(macroexp-progn (macroexp-progn
(cl-loop for pkg in packages (cl-loop for p in packages
collect (macroexpand `(package! ,pkg :disable t))))) collect `(package! ,p :disable t))))
(provide 'core-packages) (provide 'core-packages)
;;; core-packages.el ends here ;;; core-packages.el ends here

View file

@ -11,7 +11,8 @@ Emacs.")
"If non-nil, non-projects are purged from the cache on `kill-emacs-hook'.") "If non-nil, non-projects are purged from the cache on `kill-emacs-hook'.")
(defvar doom-projectile-fd-binary (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") "name of `fd-find' executable binary")
(defvar doom-projectile-cache-timer-file (concat doom-cache-dir "projectile.timers") (defvar doom-projectile-cache-timer-file (concat doom-cache-dir "projectile.timers")
@ -21,8 +22,8 @@ Emacs.")
;; ;;
;;; Packages ;;; Packages
(def-package! projectile (use-package! projectile
:after-call (after-find-file dired-before-readin-hook minibuffer-setup-hook) :after-call after-find-file dired-before-readin-hook minibuffer-setup-hook
:commands (projectile-project-root :commands (projectile-project-root
projectile-project-name projectile-project-name
projectile-project-p projectile-project-p
@ -44,59 +45,42 @@ Emacs.")
(global-set-key [remap find-tag] #'projectile-find-tag) (global-set-key [remap find-tag] #'projectile-find-tag)
:config :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) (projectile-mode +1)
;; a more generic project root file ;; a more generic project root file
(push ".project" projectile-project-root-files-bottom-up) (push ".project" projectile-project-root-files-bottom-up)
(push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories) (push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories)
(defun doom*projectile-default-generic-command (orig-fn &rest args) ;; Treat current directory in dired as a "file in a project" and track it
"If projectile can't tell what kind of project you're in, it issues an error (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook)
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)
;; Accidentally indexing big directories like $HOME or / will massively bloat ;; Accidentally indexing big directories like $HOME or / will massively bloat
;; projectile's cache (into the hundreds of MBs). This purges those entries ;; projectile's cache (into the hundreds of MBs). This purges those entries
;; when exiting Emacs to prevent slowdowns/freezing when cache files are ;; when exiting Emacs to prevent slowdowns/freezing when cache files are
;; loaded or written to. ;; loaded or written to.
(defun doom|cleanup-project-cache () (add-hook! 'kill-emacs-hook
"Purge projectile cache entries that: (defun doom-cleanup-project-cache-h ()
"Purge projectile cache entries that:
a) have too many files (see `doom-projectile-cache-limit'), a) have too many files (see `doom-projectile-cache-limit'),
b) represent blacklisted directories that are too big, change too often or are b) represent blacklisted directories that are too big, change too often or are
private. (see `doom-projectile-cache-blacklist'), private. (see `doom-projectile-cache-blacklist'),
c) are not valid projectile projects." c) are not valid projectile projects."
(when (bound-and-true-p projectile-projects-cache) (when (and (bound-and-true-p projectile-projects-cache)
(cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist) (not noninteractive))
for proot in (hash-table-keys projectile-projects-cache) (cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist)
if (or (not (stringp proot)) for proot in (hash-table-keys projectile-projects-cache)
(>= (length (gethash proot projectile-projects-cache)) if (or (not (stringp proot))
doom-projectile-cache-limit) (>= (length (gethash proot projectile-projects-cache))
(member (substring proot 0 -1) blacklist) doom-projectile-cache-limit)
(and doom-projectile-cache-purge-non-projects (member (substring proot 0 -1) blacklist)
(not (doom-project-p proot)))) (and doom-projectile-cache-purge-non-projects
do (doom-log "Removed %S from projectile cache" proot) (not (doom-project-p proot))))
and do (remhash proot projectile-projects-cache) do (doom-log "Removed %S from projectile cache" proot)
and do (remhash proot projectile-projects-cache-time) and do (remhash proot projectile-projects-cache)
and do (remhash proot projectile-project-type-cache)) and do (remhash proot projectile-projects-cache-time)
(projectile-serialize-cache))) and do (remhash proot projectile-project-type-cache))
(unless noninteractive (projectile-serialize-cache))))
(add-hook 'kill-emacs-hook #'doom|cleanup-project-cache))
;; It breaks projectile's project root resolution if HOME is a project (e.g. ;; 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 ;; 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)
projectile-project-root-files-bottom-up nil))) 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 (cond
;; If fd exists, use it for git and generic projects. fd is a rust program ;; 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 ;; that is significantly faster than git ls-files or find, and it respects
;; .gitignore. This is recommended in the projectile docs. ;; .gitignore. This is recommended in the projectile docs.
(doom-projectile-fd-binary ((executable-find doom-projectile-fd-binary)
(setq projectile-git-command (concat (setq projectile-git-command (concat
doom-projectile-fd-binary doom-projectile-fd-binary
" . --color=never --type f -0 -H -E .git") " . --color=never --type f -0 -H -E .git")
@ -143,10 +117,42 @@ c) are not valid projectile projects."
projectile-indexing-method 'alien) projectile-indexing-method 'alien)
;; fix breakage on windows in git projects ;; fix breakage on windows in git projects
(unless (executable-find "tr") (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 (defvar doom-project-hook nil
"Hook run when a project is enabled. The name of the project's mode and its "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'." Relevant: `doom-project-hook'."
(declare (indent 1)) (declare (indent 1))
(let ((init-var (intern (format "%s-init" name)))) (let ((init-var (intern (format "%s-init" name))))
`(progn (macroexp-progn
,(if on-load `(defvar ,init-var nil)) (append
(define-minor-mode ,name (when on-load
"A project minor mode generated by `def-project-mode!'." `((defvar ,init-var nil)))
:init-value nil `((define-minor-mode ,name
:lighter "" "A project minor mode generated by `def-project-mode!'."
:keymap (make-sparse-keymap) :init-value nil
(if (not ,name) :lighter ""
,on-exit :keymap (make-sparse-keymap)
(run-hook-with-args 'doom-project-hook ',name ,name) (if (not ,name)
,(when on-load ,on-exit
`(unless ,init-var (run-hook-with-args 'doom-project-hook ',name ,name)
,on-load ,(when on-load
(setq ,init-var t))) `(unless ,init-var
,on-enter)) ,on-load
,@(cl-loop for hook in add-hooks (setq ,init-var t)))
collect `(add-hook ',(intern (format "%s-hook" name)) ,on-enter))
#',hook)) (dolist (hook ,add-hooks)
,(when (or modes match files when) (add-hook ',(intern (format "%s-hook" name)) hook)))
`(associate! ,name (cond ((or files modes when)
:modes ,modes (cl-check-type files (or null list string))
:match ,match (let ((fn
:files ,files `(lambda ()
:when ,when))))) (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) (provide 'core-projects)
;;; core-projects.el ends here ;;; core-projects.el ends here

View file

@ -51,10 +51,6 @@ examples.
It is recommended you don't set specify a font-size, as to inherit `doom-font's It is recommended you don't set specify a font-size, as to inherit `doom-font's
size.") 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 ;;; 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-window nil)
(defvar doom--last-frame nil) (defvar doom--last-frame nil)
(defun doom|run-switch-window-hooks () (defun doom-run-switch-window-hooks-h ()
(let ((gc-cons-threshold doom-gc-cons-upper-limit)) (let ((gc-cons-threshold most-positive-fixnum))
(unless (or doom-inhibit-switch-window-hooks (unless (or doom-inhibit-switch-window-hooks
(eq doom--last-window (selected-window)) (eq doom--last-window (selected-window))
(minibufferp)) (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) (run-hooks 'doom-switch-window-hook)
(setq doom--last-window (selected-window)))))) (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 (unless (or doom-inhibit-switch-frame-hooks
(eq doom--last-frame (selected-frame)) (eq doom--last-frame (selected-frame))
(frame-parameter nil 'parent-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) (run-hooks 'doom-switch-frame-hook)
(setq doom--last-frame (selected-frame))))) (setq doom--last-frame (selected-frame)))))
(defun doom*run-switch-buffer-hooks (orig-fn buffer-or-name &rest args) (defun doom-run-switch-buffer-hooks-a (orig-fn buffer-or-name &rest args)
(let ((gc-cons-threshold doom-gc-cons-upper-limit)) (let ((gc-cons-threshold most-positive-fixnum))
(if (or doom-inhibit-switch-buffer-hooks (if (or doom-inhibit-switch-buffer-hooks
(eq (current-buffer) (get-buffer buffer-or-name)) (eq (current-buffer) (get-buffer buffer-or-name))
(and (eq orig-fn #'switch-to-buffer) (car args))) (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)) (run-hooks 'doom-switch-buffer-hook))
buffer))))) buffer)))))
(defun doom*run-switch-to-next-prev-buffer-hooks (orig-fn &rest args) (defun doom-run-switch-to-next-prev-buffer-hooks-a (orig-fn &rest args)
(let ((gc-cons-threshold doom-gc-cons-upper-limit)) (let ((gc-cons-threshold most-positive-fixnum))
(if doom-inhibit-switch-buffer-hooks (if doom-inhibit-switch-buffer-hooks
(apply orig-fn args) (apply orig-fn args)
(let ((doom-inhibit-switch-buffer-hooks t)) (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)) (run-hooks 'doom-switch-buffer-hook))
buffer))))) buffer)))))
(defun doom*run-load-theme-hooks (theme &optional _no-confirm no-enable) (defun doom-protect-fallback-buffer-h ()
"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 ()
"Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'." "Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'."
(not (eq (current-buffer) (doom-fallback-buffer)))) (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. "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 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) (set (make-local-variable 'whitespace-style)
(let ((style (if indent-tabs-mode '(indentation) '(tabs tab-mark)))) (let ((style (if indent-tabs-mode '(indentation) '(tabs tab-mark))))
(if whitespace-mode (if whitespace-mode
(cl-union style whitespace-style) (cl-union style whitespace-active-style)
`(face ,@style)))) style)))
(add-to-list 'whitespace-style 'face) (cl-pushnew 'face whitespace-style)
(whitespace-mode +1))) (whitespace-mode +1)))
;; ;;
;;; General configuration ;;; General UX
(setq-default ;; Simpler confirmation prompt when killing Emacs
ansi-color-for-comint-mode t (setq confirm-kill-emacs #'doom-quit-p)
bidi-display-reordering nil ; disable bidirectional text for tiny performance boost
blink-matching-paren nil ; don't blink--too distracting (setq uniquify-buffer-name-style 'forward
compilation-always-kill t ; kill compilation process before starting another ;; no beeping or blinking please
compilation-ask-about-save nil ; save all buffers on `compile' ring-bell-function #'ignore
compilation-scroll-output 'first-error visible-bell nil)
confirm-nonexistent-file-or-buffer t
confirm-kill-emacs #'doom-quit-p ; custom confirmation when killing Emacs ;; middle-click paste at point, not at click
cursor-in-non-selected-windows nil ; hide cursors in other windows (setq mouse-yank-at-point t)
custom-theme-directory (expand-file-name "themes/" doom-private-dir)
display-line-numbers-width 3 ;; Enable mouse in terminal Emacs
echo-keystrokes 0.02 (add-hook 'tty-setup-hook #'xterm-mouse-mode)
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 ;;; Scrolling
fringe-indicator-alist
(delq (assq 'continuation fringe-indicator-alist) (setq hscroll-margin 2
fringe-indicator-alist) hscroll-step 1
highlight-nonselected-windows nil scroll-conservatively 10
image-animate-loop t scroll-margin 0
indicate-buffer-boundaries nil scroll-preserve-screen-position t
indicate-empty-lines nil ;; mouse
max-mini-window-height 0.3 mouse-wheel-scroll-amount '(5 ((shift) . 2))
mode-line-default-help-echo nil ; disable mode-line mouseovers mouse-wheel-progressive-speed nil) ; don't accelerate scrolling
mouse-yank-at-point t ; middle-click paste at point, not at click
show-help-function nil ; hide :help-echo text ;; Remove hscroll-margin in shells, otherwise it causes jumpiness
use-dialog-box nil ; always avoid GUI (setq-hook! '(eshell-mode-hook term-mode-hook) hscroll-margin 0)
uniquify-buffer-name-style 'forward
visible-cursor nil (when IS-MAC
x-stretch-cursor nil ;; sane trackpad/mouse scroll settings
;; Favor vertical splits (setq mac-redisplay-dont-reset-vscroll t
split-width-threshold 160 mac-mouse-wheel-smooth-scroll nil))
split-height-threshold nil
;; `pos-tip' defaults
pos-tip-internal-border-width 6 ;;
pos-tip-border-width 1 ;;; Cursor
;; no beeping or blinking please
ring-bell-function #'ignore ;; Don't blink the cursor, it's too distracting.
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
(blink-cursor-mode -1) (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 ;; Don't blink the paren matching the one at point, it's too distracting.
;; faster (implemented in C). But try to only enable it in editing buffers. (setq blink-matching-paren nil)
(setq-default show-trailing-whitespace nil)
(setq-hook! '(prog-mode-hook text-mode-hook conf-mode-hook) show-trailing-whitespace t) (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, ;; The native border "consumes" a pixel of the fringe on righter-most splits,
;; `window-divider' does not. Available since Emacs 25.1. ;; `window-divider' does not. Available since Emacs 25.1.
(setq-default window-divider-default-places t (setq window-divider-default-places t
window-divider-default-bottom-width 1 window-divider-default-bottom-width 1
window-divider-default-right-width 1) window-divider-default-right-width 1)
(add-hook 'doom-init-ui-hook #'window-divider-mode) (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 ;;; 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 :defer t
:init :init
(setq ediff-diff-options "-w" ; turn off whitespace checking (setq ediff-diff-options "-w" ; turn off whitespace checking
@ -252,23 +379,21 @@ read-only or not file-visiting."
:config :config
(defvar doom--ediff-saved-wconf nil) (defvar doom--ediff-saved-wconf nil)
;; Restore window config after quitting ediff ;; Restore window config after quitting ediff
(defun doom|ediff-save-wconf () (add-hook! 'ediff-before-setup-hook
(setq doom--ediff-saved-wconf (current-window-configuration))) (defun doom-ediff-save-wconf-h ()
(add-hook 'ediff-before-setup-hook #'doom|ediff-save-wconf) (setq doom--ediff-saved-wconf (current-window-configuration))))
(add-hook! '(ediff-quit-hook ediff-suspend-hook) :append
(defun doom|ediff-restore-wconf () (defun doom-ediff-restore-wconf-h ()
(when (window-configuration-p doom--ediff-saved-wconf) (when (window-configuration-p doom--ediff-saved-wconf)
(set-window-configuration 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))
(def-package! hl-line (use-package! hl-line
;; Highlights the current line ;; Highlights the current line
:hook ((prog-mode text-mode conf-mode) . hl-line-mode) :hook ((prog-mode text-mode conf-mode) . hl-line-mode)
:config :config
;; I don't need hl-line showing in other windows. This also offers a small ;; Not having to render the hl-line overlay in multiple buffers offers a tiny
;; speed boost when buffer is displayed in multiple windows. ;; performance boost. I also don't need to see it in other buffers.
(setq hl-line-sticky-flag nil (setq hl-line-sticky-flag nil
global-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. ;; selection region harder to see while in evil visual mode.
(after! evil (after! evil
(defvar doom-buffer-hl-line-mode nil) (defvar doom-buffer-hl-line-mode nil)
(add-hook! 'evil-visual-state-entry-hook
(defun doom|disable-hl-line () (defun doom-disable-hl-line-h ()
(when hl-line-mode (when hl-line-mode
(setq-local doom-buffer-hl-line-mode t) (setq-local doom-buffer-hl-line-mode t)
(hl-line-mode -1))) (hl-line-mode -1))))
(add-hook 'evil-visual-state-entry-hook #'doom|disable-hl-line) (add-hook! 'evil-visual-state-exit-hook
(defun doom-enable-hl-line-maybe-h ()
(defun doom|enable-hl-line-maybe () (when doom-buffer-hl-line-mode
(when doom-buffer-hl-line-mode (hl-line-mode +1))))))
(hl-line-mode +1)))
(add-hook 'evil-visual-state-exit-hook #'doom|enable-hl-line-maybe)))
(def-package! winner (use-package! winner
;; undo/redo changes to Emacs' window layout ;; 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) :preface (defvar winner-dont-bind-my-keys t)
:config (winner-mode +1)) ; I'll bind keys myself :config (winner-mode +1)) ; I'll bind keys myself
(def-package! paren (use-package! paren
;; highlight matching delimiters ;; highlight matching delimiters
:after-call (after-find-file doom-switch-buffer-hook) :after-call after-find-file doom-switch-buffer-hook
:config :config
(setq show-paren-delay 0.1 (setq show-paren-delay 0.1
show-paren-highlight-openparen t show-paren-highlight-openparen t
@ -316,56 +439,63 @@ read-only or not file-visiting."
(newline-mark ?\n [ ?\n]) (newline-mark ?\n [ ?\n])
(space-mark ?\ [] [?.]))) (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 ;;; Third party packages
(def-package! all-the-icons (use-package! all-the-icons
:commands (all-the-icons-octicon all-the-icons-faicon all-the-icons-fileicon :commands (all-the-icons-octicon
all-the-icons-wicon all-the-icons-material all-the-icons-alltheicon) all-the-icons-faicon
all-the-icons-fileicon
all-the-icons-wicon
all-the-icons-material
all-the-icons-alltheicon)
:init :init
(defun doom*disable-all-the-icons-in-tty (orig-fn &rest args) (defadvice! doom--disable-all-the-icons-in-tty-a (orig-fn &rest args)
(if (display-graphic-p) "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) (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 ;;;###package hide-mode-line-mode
(add-hook 'completion-list-mode-hook #'hide-mode-line-mode) (add-hook! '(completion-list-mode-hook Man-mode-hook)
(add-hook 'Man-mode-hook #'hide-mode-line-mode) #'hide-mode-line-mode)
;; Better fontification of number literals in code ;; Better fontification of number literals in code
(def-package! highlight-numbers (use-package! highlight-numbers
:hook ((prog-mode conf-mode) . highlight-numbers-mode) :hook ((prog-mode conf-mode) . highlight-numbers-mode)
:config (setq highlight-numbers-generic-regexp "\\_<[[:digit:]]+\\(?:\\.[0-9]*\\)?\\_>")) :config (setq highlight-numbers-generic-regexp "\\_<[[:digit:]]+\\(?:\\.[0-9]*\\)?\\_>"))
;;;###package image
(setq image-animate-loop t)
;;;###package rainbow-delimiters ;;;###package rainbow-delimiters
;; Helps us distinguish stacked delimiter pairs, especially in parentheses-drunk ;; Helps us distinguish stacked delimiter pairs, especially in parentheses-drunk
;; languages like Lisp. ;; languages like Lisp.
(setq rainbow-delimiters-max-face-count 3) (setq rainbow-delimiters-max-face-count 3)
;;;###package pos-tip
(setq pos-tip-internal-border-width 6
pos-tip-border-width 1)
;; ;;
;;; Line numbers ;;; Line numbers
(setq-default display-line-numbers-width 3)
;; line numbers in most modes ;; 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-enable-line-numbers-h () (display-line-numbers-mode +1))
(defun doom|disable-line-numbers () (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. ;; DEPRECATED `nlinum' is used for Emacs 25 users; 26+ has native line numbers.
(def-package! nlinum (use-package! nlinum
;; Line number column. A faster (or equivalent, in the worst case) line number ;; Line number column. A faster (or equivalent, in the worst case) line number
;; plugin than `linum-mode'. ;; plugin than `linum-mode'.
:unless EMACS26+ :unless EMACS26+
@ -407,14 +537,14 @@ character that looks like a space that `whitespace-mode' won't affect.")
str)) str))
(setq nlinum-format-function #'doom-nlinum-format-fn) (setq nlinum-format-function #'doom-nlinum-format-fn)
(defun doom|init-nlinum-width () (add-hook! 'nlinum-mode-hook
"Calculate line number column width beforehand (optimization)." (defun doom-init-nlinum-width-h ()
(setq nlinum--width "Calculate line number column width beforehand (optimization)."
(length (save-excursion (goto-char (point-max)) (setq nlinum--width
(format-mode-line "%l"))))) (length (save-excursion (goto-char (point-max))
(add-hook 'nlinum-mode-hook #'doom|init-nlinum-width)) (format-mode-line "%l")))))))
(def-package! nlinum-hl (use-package! nlinum-hl
;; Fixes disappearing line numbers in nlinum and other quirks ;; Fixes disappearing line numbers in nlinum and other quirks
:unless EMACS26+ :unless EMACS26+
:after nlinum :after nlinum
@ -430,7 +560,7 @@ character that looks like a space that `whitespace-mode' won't affect.")
;; forces them to resize. ;; forces them to resize.
(add-hook 'after-setting-font-hook #'nlinum-hl-flush-all-windows)) (add-hook 'after-setting-font-hook #'nlinum-hl-flush-all-windows))
(def-package! nlinum-relative (use-package! nlinum-relative
:unless EMACS26+ :unless EMACS26+
:defer t :defer t
:config :config
@ -441,7 +571,14 @@ character that looks like a space that `whitespace-mode' won't affect.")
;; ;;
;;; Theme & font ;;; 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. "Loads fonts.
Fonts are specified by `doom-font', `doom-variable-pitch-font', 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) ((display-graphic-p)
(setq doom-font (face-attribute 'default :font)))) (setq doom-font (face-attribute 'default :font))))
(when doom-serif-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 (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) ((debug error)
(if (string-prefix-p "Font not available: " (error-message-string e)) (if (string-prefix-p "Font not available: " (error-message-string e))
(lwarn 'doom-ui :warning (lwarn 'doom-ui :warning
@ -468,56 +607,61 @@ Fonts are specified by `doom-font', `doom-variable-pitch-font',
(font-get (caddr e) :family)) (font-get (caddr e) :family))
(signal 'doom-error e))))) (signal 'doom-error e)))))
(defun doom|init-emoji-fonts (frame) (defun doom-init-theme-h (&optional 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)
"Load the theme specified by `doom-theme' in FRAME." "Load the theme specified by `doom-theme' in FRAME."
(when (and doom-theme (not (memq doom-theme custom-enabled-themes))) (when (and doom-theme (not (memq doom-theme custom-enabled-themes)))
(with-selected-frame (or frame (selected-frame)) (with-selected-frame (or frame (selected-frame))
(let ((doom--prefer-theme-elc t)) (let ((doom--prefer-theme-elc t))
(load-theme doom-theme 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 ;;; Bootstrap
(defun doom|init-ui () (defun doom-init-ui-h ()
"Initialize Doom's user interface by applying all its advice and hooks." "Initialize Doom's user interface by applying all its advice and hooks."
(run-hook-wrapped 'doom-init-ui-hook #'doom-try-run-hook) (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-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) (add-hook 'after-change-major-mode-hook #'doom-highlight-non-default-indentation-h 'append)
;; Initialize custom switch-{buffer,window,frame} hooks: ;; Initialize custom switch-{buffer,window,frame} hooks:
;; + `doom-switch-buffer-hook' ;; + `doom-switch-buffer-hook'
;; + `doom-switch-window-hook' ;; + `doom-switch-window-hook'
;; + `doom-switch-frame-hook' ;; + `doom-switch-frame-hook'
(add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(add-hook 'focus-in-hook #'doom|run-switch-frame-hooks) (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(advice-add! '(switch-to-next-buffer switch-to-prev-buffer) (dolist (fn '(switch-to-next-buffer switch-to-prev-buffer))
:around #'doom*run-switch-to-next-prev-buffer-hooks) (advice-add fn :around #'doom-run-switch-to-next-prev-buffer-hooks-a))
(advice-add! '(switch-to-buffer display-buffer) (dolist (fn '(switch-to-buffer display-buffer))
:around #'doom*run-switch-buffer-hooks)) (advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
;; Apply `doom-theme' ;; Apply `doom-theme'
(add-hook (if (daemonp) (add-hook 'doom-init-ui-hook #'doom-init-theme-h)
'after-make-frame-functions
'doom-init-ui-hook)
#'doom|init-theme)
;; Apply `doom-font' et co ;; Apply `doom-font' et co
(add-hook 'doom-after-init-modules-hook #'doom|init-fonts) (add-hook 'doom-after-init-modules-hook #'doom-init-fonts-h)
;; 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 '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) (unless (fboundp 'define-fringe-bitmap)
(defun define-fringe-bitmap (&rest _))) (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 (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 "`whitespace-mode' inundates child frames with whitspace markers, so disable
it to fix all that visual noise." it to fix all that visual noise."
(unless (frame-parameter nil 'parent-frame) (unless (frame-parameter nil 'parent-frame)
(funcall orig-fn))) (funcall orig-fn)))
(add-function :around whitespace-enable-predicate #'doom*disable-whitespace-mode-in-childframes) (add-function :around whitespace-enable-predicate #'doom-disable-whitespace-mode-in-childframes-a))
(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)
;; Don't display messages in the minibuffer when using the minibuffer ;; Don't display messages in the minibuffer when using the minibuffer
(defmacro doom-silence-motion-key (command key) (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 "<backspace>") (doom-silence-motion-key backward-delete-char "<backspace>")
(doom-silence-motion-key delete-char "<delete>") (doom-silence-motion-key delete-char "<delete>")
;; 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) (provide 'core-ui)
;;; core-ui.el ends here ;;; core-ui.el ends here

View file

@ -1,9 +1,10 @@
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
(eval-when-compile (defvar doom-init-p nil
(and (version< emacs-version "25.3") "Non-nil if Doom has been initialized.")
(error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher"
emacs-version))) (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) (defvar doom-debug-mode (or (getenv "DEBUG") init-file-debug)
"If non-nil, Doom will log more. "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 Use `doom/toggle-debug-mode' to toggle it. The --debug-init flag and setting the
DEBUG envvar will enable this at startup.") 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 ;;; Constants
(defconst doom-version "2.0.9" (defconst doom-version "2.0.9"
"Current version of Doom Emacs.") "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-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix))) (defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix)))
;;; Directories/files
;;
(defvar doom-emacs-dir (defvar doom-emacs-dir
(eval-when-compile (file-truename user-emacs-directory)) (eval-when-compile (file-truename user-emacs-directory))
"The path to the currently loaded .emacs.d directory. Must end with a slash.") "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/") (defvar doom-modules-dir (concat doom-emacs-dir "modules/")
"The root directory for Doom's modules. Must end with a slash.") "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. "Root directory for local storage.
Use this as a storage location for this system's installation of Doom Emacs. 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.") 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. "Where package.el and quelpa plugins (and their caches) are stored.
Must end with a slash.") 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.") whichever is found first. Must end in a slash.")
(defvar doom-autoload-file (concat doom-local-dir "autoloads.el") (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 This file is responsible for informing Emacs where to find all of Doom's
autoloaded core functions (in core/autoload/*.el).") autoloaded core functions (in core/autoload/*.el).")
(defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.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 This file is compiled from the autoloads files of all installed packages
combined.") 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 \(easily) be launched from the correct shell session (particularly for MacOS
users).") 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 ;;; Custom error types
(define-error 'doom-error "Error in Doom Emacs core") (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-hook-error "Error in a Doom startup hook" 'doom-error)
(define-error 'doom-autoload-error "Error in an autoloads file" '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) (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 ;;; 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 ;; UTF-8 as the default coding system
(when (fboundp 'set-charset-priority) (when (fboundp 'set-charset-priority)
(set-charset-priority 'unicode)) ; pretty (set-charset-priority 'unicode)) ; pretty
(prefer-coding-system 'utf-8) ; pretty (prefer-coding-system 'utf-8) ; pretty
(setq locale-coding-system 'utf-8) ; please (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 (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 ;; Disable warnings from legacy advice system. They aren't useful, and we can't
ad-redefinition-action 'accept ; silence redefined function warnings ;; often do anything about them besides changing packages upstream
apropos-do-all t ; make `apropos' more useful (setq ad-redefinition-action 'accept)
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/"))
(defun doom*symbol-file (orig-fn symbol &optional type) ;; Make apropos omnipotent. It's more useful this way.
"If a `doom-file' symbol property exists on SYMBOL, use that instead of the (setq apropos-do-all t)
original value of `symbol-file'."
(or (if (symbolp symbol) (get symbol 'doom-file)) ;; Don't make a second case-insensitive pass over `auto-mode-alist'. If it has
(funcall orig-fn symbol type))) ;; to, it's our (the user's) failure. One case for all!
(advice-add #'symbol-file :around #'doom*symbol-file) (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\<NAME>) 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 '() ;; Disable bidirectional text rendering for a modest performance boost. Of
"Alist mapping filename patterns to corresponding minor mode functions, like ;; course, this renders Emacs unable to detect/display right-to-left languages
`auto-mode-alist'. All elements of this alist are checked, meaning you can ;; (sorry!), but for us left-to-right language speakers/writers, it's a boon.
enable multiple minor modes for the same regexp.") (setq-default bidi-display-reordering 'left-to-right)
(defun doom|enable-minor-mode-maybe () ;; Reduce rendering/line scan work for Emacs by not rendering cursors or regions
"Check file name against `doom-auto-minor-mode-alist'." ;; in non-focused windows.
(when (and buffer-file-name doom-auto-minor-mode-alist) (setq-default cursor-in-non-selected-windows nil)
(let ((name buffer-file-name) (setq highlight-nonselected-windows nil)
(remote-id (file-remote-p buffer-file-name))
(alist doom-auto-minor-mode-alist)) ;; More performant rapid scrolling over unfontified regions. May cause brief
;; Remove backup-suffixes from file name. ;; spells of inaccurate fontification immediately after scrolling.
(setq name (file-name-sans-versions name)) (setq fast-but-imprecise-scrolling t)
;; Remove remote file name identification.
(when (and (stringp remote-id) ;; Resizing the Emacs frame can be a terribly expensive part of changing the
(string-match (regexp-quote remote-id) name)) ;; font. By inhibiting this, we easily halve startup times with fonts that are
(setq name (substring name (match-end 0)))) ;; larger than the system default.
(while (and alist (caar alist) (cdar alist)) (setq frame-inhibit-implied-resize t)
(if (string-match-p (caar alist) name)
(funcall (cdar alist) 1)) ;; Don't ping things that look like domain names.
(setq alist (cdr alist)))))) (setq ffap-machine-p-known 'reject)
(add-hook 'find-file-hook #'doom|enable-minor-mode-maybe)
;; 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 ;; 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 ;; have run. If you want hook functions to be aware of these customizations, add
;; them to MODE-local-vars-hook instead. ;; 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 MODE-local-vars-hook after local variables are initialized."
(run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode)) (run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode))
#'doom-try-run-hook)) #'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 ;; If `enable-local-variables' is disabled, then `hack-local-variables-hook' is
;; never triggered. ;; never triggered.
(defun doom|run-local-var-hooks-if-necessary () (defun doom-run-local-var-hooks-if-necessary-h ()
"Run `doom|run-local-var-hooks' if `enable-local-variables' is disabled." "Run `doom-run-local-var-hooks-h' if `enable-local-variables' is disabled."
(unless enable-local-variables (unless enable-local-variables
(doom|run-local-var-hooks))) (doom-run-local-var-hooks-h)))
(add-hook 'after-change-major-mode-hook #'doom|run-local-var-hooks-if-necessary 'append) (add-hook 'after-change-major-mode-hook
#'doom-run-local-var-hooks-if-necessary-h
(defun doom|create-non-existent-directories () 'append)
"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)
;; ;;
@ -298,7 +319,7 @@ broken up into:
This is already done by the lang/org module, however. This is already done by the lang/org module, however.
If you want to disable incremental loading altogether, either remove 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.") `doom-incremental-first-idle-timer' to nil.")
(defvar doom-incremental-first-idle-timer 2 (defvar doom-incremental-first-idle-timer 2
@ -317,13 +338,19 @@ intervals."
(if (not now) (if (not now)
(nconc doom-incremental-packages packages) (nconc doom-incremental-packages packages)
(when packages (when packages
(let ((gc-cons-threshold doom-gc-cons-upper-limit) (let ((gc-cons-threshold most-positive-fixnum)
(reqs (cl-delete-if #'featurep packages)) (file-name-handler-alist nil)
file-name-handler-alist) (reqs (cl-delete-if #'featurep packages)))
(when-let (req (if reqs (ignore-errors (pop reqs)))) (when-let (req (if reqs (pop reqs)))
(doom-log "Incrementally loading %s" req) (doom-log "Incrementally loading %s" req)
(condition-case e (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)) (push req reqs))
((error debug) ((error debug)
(message "Failed to load '%s' package incrementally, because: %s" (message "Failed to load '%s' package incrementally, because: %s"
@ -334,7 +361,7 @@ intervals."
reqs t) reqs t)
(doom-log "Finished incremental loading"))))))) (doom-log "Finished incremental loading")))))))
(defun doom|load-packages-incrementally () (defun doom-load-packages-incrementally-h ()
"Begin incrementally loading packages in `doom-incremental-packages'. "Begin incrementally loading packages in `doom-incremental-packages'.
If this is a daemon session, load them all immediately instead." 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 nil #'doom-load-packages-incrementally
(cdr doom-incremental-packages) t)))) (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 ;; return nil so `run-hook-wrapped' won't short circuit
nil) nil)
(defun doom-ensure-same-emacs-version-p () (defun doom-display-benchmark-h (&optional return-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)
"Display a benchmark, showing number of packages and modules, and how quickly "Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup. they were loaded at startup.
If RETURN-P, return the message as a string instead of displaying it." If RETURN-P, return the message as a string instead of displaying it."
(funcall (if return-p #'format #'message) (funcall (if return-p #'format #'message)
"Doom loaded %s packages across %d modules in %.03fs" "Doom loaded %d packages across %d modules in %.03fs"
(length package-activated-list) (- (length load-path) (length doom--initial-load-path))
(if doom-modules (hash-table-count doom-modules) 0) (if doom-modules (hash-table-count doom-modules) 0)
(or doom-init-time (or doom-init-time
(setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) (setq doom-init-time (float-time (time-subtract (current-time) before-init-time))))))
(defun doom|run-all-startup-hooks () (defun doom-load-autoloads-file (file)
"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)
"Tries to load FILE (an autoloads file). Return t on success, throws an error "Tries to load FILE (an autoloads file). Return t on success, throws an error
in interactive sessions, nil otherwise (but logs a warning)." in interactive sessions, nil otherwise (but logs a warning)."
(condition-case e (condition-case e
(load (file-name-sans-extension file) 'noerror 'nomessage) (let (command-switch-alist)
(load (substring file 0 -3) 'noerror 'nomessage))
((debug error) ((debug error)
(if noninteractive (if noninteractive
(message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) (message "Autoload file warning: %s -> %s" (car e) (error-message-string e))
(signal 'doom-autoload-error (list (file-name-nondirectory file) 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." "Read and set envvars in FILE."
(if (not (file-readable-p 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 (with-temp-buffer
(insert-file-contents file) (insert-file-contents file)
(search-forward "\n\n" nil t) (search-forward "\n\n" nil t)
@ -443,11 +431,12 @@ in interactive sessions, nil otherwise (but logs a warning)."
(line-beginning-position)) (line-beginning-position))
(point-max)))))) (point-max))))))
(setenv var value))))) (setenv var value)))))
(setq exec-path (append (split-string (getenv "PATH") (setq-default
(if IS-WINDOWS ";" ":")) exec-path (append (split-string (getenv "PATH")
(list exec-directory)) (if IS-WINDOWS ";" ":"))
shell-file-name (or (getenv "SHELL") (list exec-directory))
shell-file-name)) shell-file-name (or (getenv "SHELL")
shell-file-name))
t)) t))
(defun doom-initialize (&optional force-p) (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' 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 for a list of all recognized module trees. Order defines precedence (from most
to least)." to least)."
(add-to-list 'load-path doom-core-dir)
(require 'core-lib)
(when (or force-p (not doom-init-p)) (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 ;; Reset as much state as possible, so `doom-initialize' can be treated like
(setq exec-path doom-site-exec-path ;; a reset function. Particularly useful for reloading the config.
load-path doom-site-load-path (setq exec-path doom--initial-exec-path
process-environment doom-site-process-environment load-path doom--initial-load-path
shell-file-name doom-site-shell-file-name) process-environment doom--initial-process-environment)
;; `doom-autoload-file' tells Emacs where to load all its autoloaded (require 'core-lib)
;; functions from. This includes everything in core/autoload/*.el and all (require 'core-modules)
;; 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-packages) ;; Load shell environment, optionally generated from 'doom env'
(doom-ensure-packages-initialized force-p) (when (and (or (display-graphic-p)
(doom-ensure-core-packages) (daemonp))
(file-exists-p doom-env-file))
(doom-load-envvars-file doom-env-file))
(unless (or force-p noninteractive) (let (;; `doom-autoload-file' tells Emacs where to load all its functions
(user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them"))) ;; 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 (if (and core-autoloads-p (not force-p))
;; autoloads file and caches `load-path', `auto-mode-alist', ;; In case we want to use package.el or straight via M-x
;; `Info-directory-list', `doom-disabled-packages' and (progn
;; `package-activated-list'. A big reduction in startup time. (with-eval-after-load 'package
(let (command-switch-alist) (require 'core-packages))
(unless (or force-p (with-eval-after-load 'straight
(doom-initialize-autoloads doom-package-autoload-file) (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) 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 (defun doom-initialize-core ()
(unless noninteractive "Load Doom's core files for an interactive session."
(doom-load-env-vars doom-env-file))) (require 'core-keybinds)
(require 'core-ui)
(require 'core-modules) (require 'core-projects)
(require 'core-os) (require 'core-editor))
(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))
(provide 'core) (provide 'core)
;;; core.el ends here ;;; core.el ends here

View file

@ -18,7 +18,7 @@
(/ size 1024)) (/ size 1024))
(explain! "Consider deleting it from your system (manually)")))) (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") (warn! "Couldn't find the `fd' binary; project file searches will be slightly slower")
(unless (executable-find "rg") (unless (executable-find "rg")
(warn! "Couldn't find the `rg' binary either; project file searches will be even slower"))) (warn! "Couldn't find the `rg' binary either; project file searches will be even slower")))

View file

@ -3,12 +3,7 @@
;; core.el ;; core.el
(package! dotenv-mode) (package! dotenv-mode)
(package! auto-minor-mode)
;; core-os.el
(if (not IS-MAC)
(package! xclip)
(package! osx-clipboard)
(package! ns-auto-titlebar))
;; core-ui.el ;; core-ui.el
(package! all-the-icons) (package! all-the-icons)
@ -26,10 +21,16 @@
(package! command-log-mode) (package! command-log-mode)
(package! dtrt-indent) (package! dtrt-indent)
(package! helpful) (package! helpful)
(package! ns-auto-titlebar :ignore (not IS-MAC))
(package! pcre2el) (package! pcre2el)
(package! smartparens) (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! undo-tree)
(package! ws-butler) (package! ws-butler)
(package! xclip :ignore IS-LINUX)
;; core-projects.el ;; core-projects.el
(package! projectile) (package! projectile)
@ -37,13 +38,6 @@
;; core-keybinds.el ;; core-keybinds.el
(package! general) (package! general)
(package! which-key) (package! which-key)
(package! hydra)
;; core-packages.el
(package! gnu-elpa-keyring-update)
;; autoload/debug.el ;; autoload/debug.el
(package! esup) (package! esup)
;; cli/test.el
(package! buttercup)

9
core/test/helpers.el Normal file
View file

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

View file

@ -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 (doom! :completion
company company

View file

@ -62,13 +62,15 @@
(setq a (switch-to-buffer (get-buffer-create "a")) (setq a (switch-to-buffer (get-buffer-create "a"))
b (get-buffer-create "b")) b (get-buffer-create "b"))
(spy-on 'hook) (spy-on 'hook)
(add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(add-hook 'focus-in-hook #'doom|run-switch-frame-hooks) (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(advice-add! '(switch-to-buffer display-buffer) :around #'doom*run-switch-buffer-hooks)) (dolist (fn '(switch-to-buffer display-buffer))
(advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
(after-each (after-each
(remove-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) (remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(remove-hook 'focus-in-hook #'doom|run-switch-frame-hooks) (remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(advice-remove! '(switch-to-buffer display-buffer) #'doom*run-switch-buffer-hooks) (dolist (fn '(switch-to-buffer display-buffer))
(advice-remove fn #'doom-run-switch-buffer-hooks-a))
(kill-buffer a) (kill-buffer a)
(kill-buffer b)) (kill-buffer b))

150
docs/api.org Normal file
View file

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

View file

@ -4,17 +4,19 @@
;; before package and UI initialization happens. ;; before package and UI initialization happens.
;; Defer garbage collection further back in the startup process ;; 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 ;; In Emacs 27+, package initialization occurs before `user-init-file' is
;; loaded, but after `early-init-file'. Doom handles package ;; loaded, but after `early-init-file'. Doom handles package initialization, so
;; initialization, so we must prevent Emacs from doing it early! ;; we must prevent Emacs from doing it early!
(setq package-enable-at-startup nil) (setq package-enable-at-startup nil)
;; Prevent the glimpse of un-styled Emacs by setting these early. ;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
(add-to-list 'default-frame-alist '(tool-bar-lines . 0)) (push '(menu-bar-lines . 0) default-frame-alist)
(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) (push '(tool-bar-lines . 0) default-frame-alist)
(add-to-list 'default-frame-alist '(vertical-scroll-bars)) (push '(vertical-scroll-bars) default-frame-alist)
;; One less file to load at startup ;; Resizing the Emacs frame can be a terribly expensive part of changing the
(setq site-run-file nil) ;; font. By inhibiting this, we easily halve startup times with fonts that are
;; larger than the system default.
(setq frame-inhibit-implied-resize t)

74
init.el
View file

@ -27,61 +27,33 @@
;; ;;
;;; License: MIT ;;; License: MIT
(defvar doom-gc-cons-threshold 16777216 ; 16mb (when (version< emacs-version "25.3")
"The default value to use for `gc-cons-threshold'. If you experience freezing, (error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher"
decrease this. If you experience stuttering, increase this.") emacs-version))
(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))
;; Ensure Doom is running out of this file's directory ;; Ensure Doom is running out of this file's directory
(setq user-emacs-directory (file-name-directory load-file-name)) (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 ;; In noninteractive sessions, prioritize non-byte-compiled source files to
;; prevent stale, byte-compiled code from running. However, if you're getting ;; prevent the use of stale byte-code. Otherwise, it saves us a little IO time
;; recursive load errors, it may help to set this to nil. ;; to skip the mtime checks on every *.elc file we load.
(setq load-prefer-newer noninteractive) (setq load-prefer-newer noninteractive)
;; Load the heart of Doom Emacs
;; Let 'er rip!
(require 'core (concat user-emacs-directory "core/core")) (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)

View file

@ -1,9 +1,9 @@
;;; init.el -*- lexical-binding: t; -*- ;;; init.el -*- lexical-binding: t; -*-
;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom ;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install'
;; quickstart' will do this for you). The `doom!' block below controls what ;; will do this for you). The `doom!' block below controls what modules are
;; modules are enabled and in what order they will be loaded. Remember to run ;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
;; 'doom refresh' after modifying it. ;; after modifying it.
;; ;;
;; More information about these modules (and what flags they support) can be ;; More information about these modules (and what flags they support) can be
;; found in modules/README.org. ;; found in modules/README.org.
@ -24,7 +24,8 @@
doom-dashboard ; a nifty splash screen for Emacs doom-dashboard ; a nifty splash screen for Emacs
doom-quit ; DOOM quit-message prompts when you quit Emacs doom-quit ; DOOM quit-message prompts when you quit Emacs
;;fill-column ; a `fill-column' indicator ;;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 ;;indent-guides ; highlighted indent columns
modeline ; snazzy, Atom-inspired modeline, plus API modeline ; snazzy, Atom-inspired modeline, plus API
nav-flash ; blink the current line after jumping nav-flash ; blink the current line after jumping
@ -34,8 +35,8 @@
+all ; catch all popups that start with an asterix +all ; catch all popups that start with an asterix
+defaults) ; default popup rules +defaults) ; default popup rules
;;pretty-code ; replace bits of code with pretty symbols ;;pretty-code ; replace bits of code with pretty symbols
;;tabbar ; FIXME an (incomplete) tab bar for Emacs ;;tabs ; an tab bar for Emacs
treemacs ; a project drawer, like neotree but cooler ;;treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages ;;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe vc-gutter ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB vi-tilde-fringe ; fringe tildes to mark beyond EOB
@ -53,12 +54,10 @@
;;parinfer ; turn lisp into python, sort of ;;parinfer ; turn lisp into python, sort of
rotate-text ; cycle region at point between text candidates rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to snippets ; my elves. They type so I don't have to
;;word-wrap ; soft wrapping with language-aware indent
:emacs :emacs
(dired ; making dired pretty [functional] dired ; making dired pretty [functional]
;;+ranger ; bringing the goodness of ranger to dired
;;+icons ; colorful icons for dired-mode
)
electric ; smarter, keyword-based electric-indent electric ; smarter, keyword-based electric-indent
vc ; version-control and Emacs, sitting in a tree vc ; version-control and Emacs, sitting in a tree
@ -159,17 +158,13 @@
;; should be loaded late. ;; should be loaded late.
:app :app
;;calendar ;;calendar
;;irc ; how neckbeards socialize ;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader ;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought ;;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 ;; +wordnut ; wordnet (wn) search
;; +langtool) ; a proofreader (grammar/style check) for Emacs ;; +langtool) ; a proofreader (grammar/style check) for Emacs
:collab
;;floobits ; peer programming for a price
;;impatient-mode ; show off code over HTTP
:config :config
;; For literate config users. This will tangle+compile a config.org ;; For literate config users. This will tangle+compile a config.org
;; literate config in your `doom-private-dir' whenever it changes. ;; literate config in your `doom-private-dir' whenever it changes.

View file

@ -10,7 +10,6 @@
- [[#lang][:lang]] - [[#lang][:lang]]
- [[#email][:email]] - [[#email][:email]]
- [[#app][:app]] - [[#app][:app]]
- [[#collab][:collab]]
- [[#config][:config]] - [[#config][:config]]
* :completion * :completion
@ -38,7 +37,7 @@ Aesthetic modules that affect the Emacs interface or user experience.
+ [[file:ui/ophints/README.org][ophints]]: + [[file:ui/ophints/README.org][ophints]]:
+ [[file:ui/popup/README.org][popup]] =+all +defaults=: Makes temporary/disposable windows less intrusive + [[file:ui/popup/README.org][popup]] =+all +defaults=: Makes temporary/disposable windows less intrusive
+ pretty-code: + pretty-code:
+ [[file:ui/tabbar/README.org][tabbar]]: + [[file:ui/tabs/README.org][tabs]]:
+ treemacs: + treemacs:
+ [[file:ui/unicode/README.org][unicode]]: + [[file:ui/unicode/README.org][unicode]]:
+ vc-gutter: + vc-gutter:
@ -58,6 +57,7 @@ Modules that affect and augment your ability to manipulate or insert text.
+ [[file:editor/parinfer/README.org][parinfer]]: + [[file:editor/parinfer/README.org][parinfer]]:
+ rotate-text: + rotate-text:
+ [[file:editor/snippets/README.org][snippets]]: Snippet expansion for lazy typists + [[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 * :emacs
Modules that reconfigure or augment packages or features built into 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: + ansible:
+ debugger: A (nigh-)universal debugger in Emacs + debugger: A (nigh-)universal debugger in Emacs
+ [[file:tools/docker/README.org][docker]]: + [[file:tools/docker/README.org][docker]]:
+ [[file:tools/direnv/README.org][direnv]]:
+ [[file:tools/editorconfig/README.org][editorconfig]]: + [[file:tools/editorconfig/README.org][editorconfig]]:
+ [[file:tools/ein/README.org][ein]]: + [[file:tools/ein/README.org][ein]]:
+ [[file:tools/eval/README.org][eval]]: REPL & code evaluation support for a variety of languages + [[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: + qt:
+ racket: + racket:
+ [[file:lang/rest/README.org][rest]]: + [[file:lang/rest/README.org][rest]]:
+ ruby =+lsp=: + ruby =+lsp +rvm +rbenv=:
+ [[file:lang/rust/README.org][rust]] =+lsp=: + [[file:lang/rust/README.org][rust]] =+lsp=:
+ scala: + scala:
+ [[file:lang/sh/README.org][sh]] =+fish +lsp=: + [[file:lang/sh/README.org][sh]] =+fish +lsp=:
@ -166,12 +167,6 @@ Doom-specific porcelains.
+ twitter: + twitter:
+ [[file:app/write/README.org][write]] =+wordnut +langtool=: + [[file:app/write/README.org][write]] =+wordnut +langtool=:
* :collab
Modules that enable collaborative programming over the internet.
+ floobits:
+ impatient-mode:
* :config * :config
Modules that configure Emacs one way or another, or focus on making it easier Modules that configure Emacs one way or another, or focus on making it easier
for you to customize it yourself. for you to customize it yourself.

View file

@ -46,7 +46,7 @@
))) )))
;;;###autoload ;;;###autoload
(defun +calendar*cfw:render-button (title command &optional state) (defun +calendar-cfw:render-button-a (title command &optional state)
"render-button "render-button
TITLE TITLE
COMMAND COMMAND

View file

@ -7,8 +7,8 @@
;; ;;
;; Packages ;; Packages
(def-package! calfw (use-package! calfw
:commands (cfw:open-calendar-buffer) :commands cfw:open-calendar-buffer
:config :config
;; better frame for calendar ;; better frame for calendar
(setq cfw:face-item-separator-color nil (setq cfw:face-item-separator-color nil
@ -24,20 +24,20 @@
(define-key cfw:calendar-mode-map "q" #'+calendar/quit) (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) (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 :commands (cfw:open-org-calendar
cfw:org-create-source cfw:org-create-source
cfw:open-org-calendar-withkevin cfw:open-org-calendar-withkevin
my-open-calendar)) my-open-calendar))
(def-package! org-gcal (use-package! org-gcal
:commands (org-gcal-sync :commands (org-gcal-sync
org-gcal-fetch org-gcal-fetch
org-gcal-post-at-point org-gcal-post-at-point
@ -48,4 +48,4 @@
(message "org-gcal::%s - %s" title mes))) (message "org-gcal::%s - %s" title mes)))
;; (def-package! alert) ;; (use-package! alert)

View file

@ -97,3 +97,27 @@ argument) is non-nil only show channels in current server."
(interactive) (interactive)
(when (derived-mode-p 'circe-mode) (when (derived-mode-p 'circe-mode)
(tracking-next-buffer))) (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)))))

View file

@ -46,8 +46,8 @@ playback.")
;; ;;
;; Packages ;; Packages
(def-package! circe (use-package! circe
:commands (circe circe-server-buffers) :commands circe circe-server-buffers
:init (setq circe-network-defaults nil) :init (setq circe-network-defaults nil)
:config :config
(setq circe-default-quit-message nil (setq circe-default-quit-message nil
@ -90,48 +90,32 @@ playback.")
circe-format-server-lurker-activity circe-format-server-lurker-activity
(+irc--pad "Lurk" "{nick} joined {joindelta} ago")) (+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 '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) (defadvice! +irc--circe-run-disconnect-hook-a (&rest _)
"Fontify known bots and mark them to not be tracked." "Runs `+irc-disconnect-hook' after circe disconnects."
(when (member nick +irc-bot-list) :after #'circe--irc-conn-disconnected
'((text-properties . (face circe-fool-face lui-do-not-track t))))) (run-hooks '+irc-disconnect-hook))
(add-hook 'circe-message-option-functions #'+irc|circe-message-option-bot)
(defun +irc|add-circe-buffer-to-persp () (add-hook! 'lui-pre-output-hook
(let ((persp (get-current-persp)) (defun +irc-circe-truncate-nicks-h ()
(buf (current-buffer))) "Truncate long nicknames in chat output non-destructively."
;; Add a new circe buffer to irc workspace when we're in another workspace (when-let (beg (text-property-any (point-min) (point-max) 'lui-format-argument 'nick))
(unless (eq (safe-persp-name persp) +irc--workspace-name) (goto-char beg)
;; Add new circe buffers to the persp containing circe buffers (let ((end (next-single-property-change beg 'lui-format-argument))
(persp-add-buffer buf (persp-get-by-name +irc--workspace-name)) (nick (plist-get (plist-get (text-properties-at beg) 'lui-keywords)
;; Remove new buffer from accidental workspace :nick)))
(persp-remove-buffer buf persp)))) (when (> (length nick) +irc-left-padding)
(add-hook 'circe-mode-hook #'+irc|add-circe-buffer-to-persp) (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 ;; Let `+irc/quit' and `circe' handle buffer cleanup
(define-key circe-mode-map [remap kill-buffer] #'bury-buffer) (define-key circe-mode-map [remap kill-buffer] #'bury-buffer)
@ -152,14 +136,14 @@ playback.")
"n" #'circe-command-NAMES))) "n" #'circe-command-NAMES)))
(def-package! circe-color-nicks (use-package! circe-color-nicks
:hook (circe-channel-mode . enable-circe-color-nicks) :hook (circe-channel-mode . enable-circe-color-nicks)
:config :config
(setq circe-color-nicks-min-constrast-ratio 4.5 (setq circe-color-nicks-min-constrast-ratio 4.5
circe-color-nicks-everywhere t)) circe-color-nicks-everywhere t))
(def-package! circe-new-day-notifier (use-package! circe-new-day-notifier
:after circe :after circe
:config :config
(enable-circe-new-day-notifier) (enable-circe-new-day-notifier)
@ -167,7 +151,7 @@ playback.")
(+irc--pad "Day" "Date changed [{day}]"))) (+irc--pad "Day" "Date changed [{day}]")))
(def-package! circe-notifications (use-package! circe-notifications
:commands enable-circe-notifications :commands enable-circe-notifications
:init :init
(if +irc-defer-notifications (if +irc-defer-notifications
@ -181,10 +165,11 @@ playback.")
circe-notifications-emacs-focused nil circe-notifications-emacs-focused nil
circe-notifications-alert-style circe-notifications-alert-style
(cond (IS-MAC 'osx-notifier) (cond (IS-MAC 'osx-notifier)
(IS-LINUX 'libnotify)))) (IS-LINUX 'libnotify)
(circe-notifications-alert-style))))
(def-package! lui (use-package! lui
:commands lui-mode :commands lui-mode
:config :config
(define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line) (define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line)
@ -194,20 +179,21 @@ playback.")
(setq lui-flyspell-p t)) (setq lui-flyspell-p t))
(after! evil (after! evil
(defun +irc|evil-insert () (defun +irc-evil-insert-h ()
"Ensure entering insert mode will put us at the prompt, unless editing "Ensure entering insert mode will put us at the prompt, unless editing
after prompt marker." after prompt marker."
(when (> (marker-position lui-input-marker) (point)) (when (> (marker-position lui-input-marker) (point))
(goto-char (point-max)))) (goto-char (point-max))))
(add-hook! 'lui-mode-hook (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)) (mapc (lambda (cmd) (push cmd +irc-scroll-to-bottom-on-commands))
'(evil-paste-after evil-paste-before evil-open-above evil-open-below))) '(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. "Go to the end of the buffer in all windows showing it.
Courtesy of esh-mode.el" Courtesy of esh-mode.el"
(when (memq this-command +irc-scroll-to-bottom-on-commands) (when (memq this-command +irc-scroll-to-bottom-on-commands)
@ -224,28 +210,26 @@ Courtesy of esh-mode.el"
nil t))))) nil t)))))
(add-hook! 'lui-mode-hook (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 ;; 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 () (add-hook! 'lui-mode-hook
(setq lui-time-stamp-position 'right-margin (defun +irc-init-lui-margins-h ()
lui-time-stamp-format +irc-time-stamp-format (setq lui-time-stamp-position 'right-margin
right-margin-width (length (format-time-string lui-time-stamp-format)))) lui-time-stamp-format +irc-time-stamp-format
right-margin-width (length (format-time-string lui-time-stamp-format))))
(defun +irc|init-lui-wrapping () (defun +irc-init-lui-wrapping-a ()
(setq fringes-outside-margins t (setq fringes-outside-margins t
word-wrap t word-wrap t
wrap-prefix (make-string (+ +irc-left-padding 3) ? ))) wrap-prefix (make-string (+ +irc-left-padding 3) ? )))))
(add-hook! 'lui-mode-hook #'(+irc|init-lui-margins +irc|init-lui-wrapping)))
(def-package! lui-logging (use-package! lui-logging
:after lui :after lui
:config (enable-lui-logging)) :config (enable-lui-logging))
(def-package! lui-autopaste (use-package! lui-autopaste
:hook (circe-channel-mode . enable-lui-autopaste)) :hook (circe-channel-mode . enable-lui-autopaste))

View file

@ -46,7 +46,7 @@
;; Hooks ;; Hooks
;;;###autoload ;;;###autoload
(defun +rss|elfeed-wrap () (defun +rss-elfeed-wrap-h ()
"Enhances an elfeed entry's readability by wrapping it to a width of "Enhances an elfeed entry's readability by wrapping it to a width of
`fill-column'." `fill-column'."
(let ((inhibit-read-only t) (let ((inhibit-read-only t)
@ -57,7 +57,7 @@
(set-buffer-modified-p nil))) (set-buffer-modified-p nil)))
;;;###autoload ;;;###autoload
(defun +rss|cleanup () (defun +rss-cleanup-h ()
"Clean up after an elfeed session. Kills all elfeed and elfeed-org files." "Clean up after an elfeed session. Kills all elfeed and elfeed-org files."
(interactive) (interactive)
;; `delete-file-projectile-remove-from-cache' slows down `elfeed-db-compact' ;; `delete-file-projectile-remove-from-cache' slows down `elfeed-db-compact'
@ -75,7 +75,7 @@
(kill-buffer buf))) (kill-buffer buf)))
(dolist (b search-buffers) (dolist (b search-buffers)
(with-current-buffer b (with-current-buffer b
(remove-hook 'kill-buffer-hook #'+rss|cleanup :local) (remove-hook 'kill-buffer-hook #'+rss-cleanup-h :local)
(kill-buffer b))) (kill-buffer b)))
(mapc #'kill-buffer show-buffers))) (mapc #'kill-buffer show-buffers)))
@ -99,7 +99,7 @@
collect url))) collect url)))
;;;###autoload ;;;###autoload
(defun +rss-put-sliced-image (spec alt &optional flags) (defun +rss-put-sliced-image-fn (spec alt &optional flags)
"TODO" "TODO"
(cl-letf (((symbol-function #'insert-image) (cl-letf (((symbol-function #'insert-image)
(lambda (image &optional alt _area _slice) (lambda (image &optional alt _area _slice)
@ -108,7 +108,7 @@
(shr-put-image spec alt flags))) (shr-put-image spec alt flags)))
;;;###autoload ;;;###autoload
(defun +rss-render-image-tag-without-underline (dom &optional url) (defun +rss-render-image-tag-without-underline-fn (dom &optional url)
"TODO" "TODO"
(let ((start (point))) (let ((start (point)))
(shr-tag-img dom url) (shr-tag-img dom url)

View file

@ -19,7 +19,7 @@ easier to scroll through.")
;; ;;
;; Packages ;; Packages
(def-package! elfeed (use-package! elfeed
:commands elfeed :commands elfeed
:config :config
(setq elfeed-search-filter "@2-week-ago " (setq elfeed-search-filter "@2-week-ago "
@ -36,21 +36,21 @@ easier to scroll through.")
(make-directory elfeed-db-directory t) (make-directory elfeed-db-directory t)
;; Ensure elfeed buffers are treated as real ;; Ensure elfeed buffers are treated as real
(defun +rss-buffer-p (buf) (add-hook! 'doom-real-buffer-functions
(string-match-p "^\\*elfeed" (buffer-name buf))) (defun +rss-buffer-p (buf)
(add-to-list 'doom-real-buffer-functions #'+rss-buffer-p nil #'eq) (string-match-p "^\\*elfeed" (buffer-name buf))))
;; Enhance readability of a post ;; 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! '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 ;; Large images are annoying to scroll through, because scrolling follows the
;; cursor, so we force shr to insert images in slices. ;; cursor, so we force shr to insert images in slices.
(when +rss-enable-sliced-images (when +rss-enable-sliced-images
(setq-hook! 'elfeed-show-mode-hook (setq-hook! 'elfeed-show-mode-hook
shr-put-image-function #'+rss-put-sliced-image shr-put-image-function #'+rss-put-sliced-image-fn
shr-external-rendering-functions '((img . +rss-render-image-tag-without-underline)))) shr-external-rendering-functions '((img . +rss-render-image-tag-without-underline-fn))))
;; Keybindings ;; Keybindings
(after! elfeed-show (after! elfeed-show
@ -64,11 +64,11 @@ easier to scroll through.")
(kbd "M-RET") #'elfeed-search-browse-url))) (kbd "M-RET") #'elfeed-search-browse-url)))
(def-package! elfeed-org (use-package! elfeed-org
:when (featurep! +org) :when (featurep! +org)
:after elfeed :after elfeed
:config :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))) (mapcar #'expand-file-name +rss-elfeed-files)))
(elfeed-org)) (elfeed-org))

View file

@ -4,7 +4,7 @@
"The name to use for the twitter workspace.") "The name to use for the twitter workspace.")
;;;###autoload ;;;###autoload
(defun +twitter-display-buffer (buf) (defun +twitter-display-buffer-fn (buf)
"A replacement display-buffer command for `twittering-pop-to-buffer-function' "A replacement display-buffer command for `twittering-pop-to-buffer-function'
that works with the feature/popup module." that works with the feature/popup module."
(let ((win (selected-window))) (let ((win (selected-window)))

View file

@ -1,9 +1,10 @@
;;; app/twitter/config.el -*- lexical-binding: t; -*- ;;; app/twitter/config.el -*- lexical-binding: t; -*-
(def-package! twittering-mode (use-package! twittering-mode
:commands twit :commands twit
:config :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-use-master-password t
twittering-request-confirmation-on-posting t twittering-request-confirmation-on-posting t
;; twittering-icon-mode t ;; twittering-icon-mode t
@ -35,24 +36,16 @@
(add-hook 'doom-real-buffer-functions #'+twitter-buffer-p) (add-hook 'doom-real-buffer-functions #'+twitter-buffer-p)
(when (featurep! :ui popup) (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 (after! solaire-mode
(add-hook 'twittering-mode-hook #'solaire-mode)) (add-hook 'twittering-mode-hook #'solaire-mode))
;; Custom header-line for twitter buffers ;; Custom header-line for twitter buffers
(defun +twitter|switch-mode-and-header-line () (add-hook! 'twittering-mode-hook
(setq header-line-format mode-line-format (defun +twitter-switch-mode-and-header-line-h ()
mode-line-format nil)) (setq header-line-format mode-line-format
(add-hook 'twittering-mode-hook #'+twitter|switch-mode-and-header-line) mode-line-format nil)))
(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))))
;; `epa--decode-coding-string' isn't defined in later versions of Emacs 27 ;; `epa--decode-coding-string' isn't defined in later versions of Emacs 27
(unless (fboundp 'epa--decode-coding-string) (unless (fboundp 'epa--decode-coding-string)

View file

@ -9,7 +9,7 @@
;; ;;
;; Packages ;; Packages
(def-package! langtool (use-package! langtool
:when (featurep! +langtool) :when (featurep! +langtool)
:commands (langtool-check :commands (langtool-check
langtool-check-done langtool-check-done
@ -23,7 +23,7 @@
(locate-file "libexec/languagetool-commandline.jar" (locate-file "libexec/languagetool-commandline.jar"
(doom-files-in "/usr/local/Cellar/languagetool" (doom-files-in "/usr/local/Cellar/languagetool"
:type 'dirs :type 'dirs
:depth 1))) :depth 2)))
(IS-LINUX (IS-LINUX
"/usr/share/java/languagetool/languagetool-commandline.jar"))))) "/usr/share/java/languagetool/languagetool-commandline.jar")))))

View file

@ -1,4 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; collab/foobits/packages.el
(package! floobits)

View file

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

View file

@ -1,5 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; collab/impatient-mode/packages.el
(package! htmlize)
(package! impatient-mode)

View file

@ -65,15 +65,15 @@ Examples:
;;; Hooks ;;; Hooks
;;;###autoload ;;;###autoload
(defun +company|init-backends () (defun +company-init-backends-h ()
"Set `company-backends' for the current buffer." "Set `company-backends' for the current buffer."
(if (not company-mode) (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) (unless (eq major-mode 'fundamental-mode)
(setq-local company-backends (+company--backends))) (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 (`candidates
(all-completions (all-completions
arg arg
(split-string (delete-dups
(replace-regexp-in-string (split-string
"^[\t\s]+" "" (replace-regexp-in-string
(concat (buffer-substring-no-properties (point-min) (line-beginning-position)) "^[\t\s]+" ""
(buffer-substring-no-properties (line-end-position) (point-max)))) (concat (buffer-substring-no-properties (point-min) (line-beginning-position))
"\\(\r\n\\|[\n\r]\\)" t))))) (buffer-substring-no-properties (line-end-position) (point-max))))
"\\(\r\n\\|[\n\r]\\)" t))))))
;;;###autoload ;;;###autoload
(defun +company/dict-or-keywords () (defun +company/dict-or-keywords ()

View file

@ -1,8 +1,8 @@
;;; completion/company/config.el -*- lexical-binding: t; -*- ;;; completion/company/config.el -*- lexical-binding: t; -*-
(def-package! company (use-package! company
:commands (company-complete-common company-manual-begin company-grab-line) :commands company-complete-common company-manual-begin company-grab-line
:after-call (evil-insert-state-entry-hook evil-emacs-state-entry-hook) :after-call evil-insert-state-entry-hook evil-emacs-state-entry-hook
:init :init
(setq company-minimum-prefix-length 2 (setq company-minimum-prefix-length 2
company-tooltip-limit 14 company-tooltip-limit 14
@ -27,16 +27,16 @@
;; Allow users to switch between backends on the fly. E.g. C-x C-s followed ;; 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 ;; by C-x C-n, will switch from `company-yasnippet' to
;; `company-dabbrev-code'. ;; `company-dabbrev-code'.
(defun +company*abort-previous (&rest _) (company-abort)) (defadvice! +company--abort-previous-a (&rest _)
(advice-add #'company-begin-backend :before #'+company*abort-previous)) :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)) (global-company-mode +1))
(def-package! company-tng (use-package! company-tng
:when (featurep! +tng) :when (featurep! +tng)
:defer 2
:after-call post-self-insert-hook :after-call post-self-insert-hook
:config :config
(add-to-list 'company-frontends 'company-tng-frontend) (add-to-list 'company-frontends 'company-tng-frontend)
@ -51,7 +51,7 @@
;; ;;
;; Packages ;; Packages
(def-package! company-prescient (use-package! company-prescient
:hook (company-mode . company-prescient-mode) :hook (company-mode . company-prescient-mode)
:config :config
;; NOTE prescient config duplicated with `ivy' ;; NOTE prescient config duplicated with `ivy'
@ -59,7 +59,7 @@
(prescient-persist-mode +1)) (prescient-persist-mode +1))
(def-package! company-box (use-package! company-box
:when (and EMACS26+ (featurep! +childframe)) :when (and EMACS26+ (featurep! +childframe))
:hook (company-mode . company-box-mode) :hook (company-mode . company-box-mode)
:config :config
@ -68,7 +68,10 @@
company-box-max-candidates 50 company-box-max-candidates 50
company-box-icons-alist 'company-box-icons-all-the-icons company-box-icons-alist 'company-box-icons-all-the-icons
company-box-icons-functions 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 company-box-icons-all-the-icons
`((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple)) `((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)) (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)) (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)))) (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) (when (get-text-property 0 'yas-annotation candidate)
'Yasnippet)) 'Yasnippet))
(defun +company-box-icons--elisp (candidate) (defun +company-box-icons--elisp-fn (candidate)
(when (derived-mode-p 'emacs-lisp-mode) (when (derived-mode-p 'emacs-lisp-mode)
(let ((sym (intern candidate))) (let ((sym (intern candidate)))
(cond ((fboundp sym) 'ElispFunction) (cond ((fboundp sym) 'ElispFunction)
@ -116,14 +119,13 @@
((facep sym) 'ElispFace)))))) ((facep sym) 'ElispFace))))))
(def-package! company-dict (use-package! company-dict
:defer t :defer t
:config :config
(setq company-dict-dir (expand-file-name "dicts" doom-private-dir)) (setq company-dict-dir (expand-file-name "dicts" doom-private-dir))
(defun +company|enable-project-dicts (mode &rest _) (add-hook! 'doom-project-hook
"Enable per-project dictionaries." (defun +company-enable-project-dicts-h (mode &rest _)
(if (symbol-value mode) "Enable per-project dictionaries."
(add-to-list 'company-dict-minor-mode-list mode nil #'eq) (if (symbol-value mode)
(setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list)))) (add-to-list 'company-dict-minor-mode-list mode nil #'eq)
(add-hook 'doom-project-hook #'+company|enable-project-dicts)) (setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list))))))

View file

@ -1,9 +1,7 @@
;;; completion/helm/autoload/posframe.el -*- lexical-binding: t; -*- ;;; completion/helm/autoload/posframe.el -*- lexical-binding: t; -*-
(add-hook 'helm-cleanup-hook #'+helm|posframe-cleanup)
;;;###autoload ;;;###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 "Display the child frame in the center of the frame, slightly closer to the
bottom, which is easier on the eyes on big displays." bottom, which is easier on the eyes on big displays."
(let ((parent-frame (plist-get info :parent-frame)) (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) (defvar +helm--posframe-buffer nil)
;;;###autoload ;;;###autoload
(defun +helm-posframe-display (buffer &optional _resume) (defun +helm-posframe-display-fn (buffer &optional _resume)
"TODO" "TODO"
(setq helm--buffer-in-new-frame-p t) (setq helm--buffer-in-new-frame-p t)
(let ((solaire-p (bound-and-true-p solaire-mode)) (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))))) (text-scale-set +helm-posframe-text-scale)))))
;;;###autoload ;;;###autoload
(defun +helm|posframe-cleanup () (defun +helm-posframe-cleanup-h ()
"TODO" "TODO"
;; Ensure focus is properly returned to the underlying window, by forcing a ;; Ensure focus is properly returned to the underlying window. This gives the
;; chance in buffer/window focus. This gives the modeline a chance to refresh. ;; modeline a chance to refresh.
(switch-to-buffer +helm--posframe-buffer t) (switch-to-buffer +helm--posframe-buffer t))
;;
(posframe-delete +helm--posframe-buffer))
(add-hook 'helm-cleanup-hook #'+helm-posframe-cleanup-h)
;;;###autoload
(defun +helm*fix-get-font-height (orig-fn position)
(ignore-errors (funcall orig-fn position)))

View file

@ -11,8 +11,7 @@ silently ignored.
This falls back to git-grep (then grep) if none of these available.") This falls back to git-grep (then grep) if none of these available.")
;; Posframe (requires +childframe) ;; Posframe (requires +childframe)
(defvar +helm-posframe-handler (defvar +helm-posframe-handler #'+helm-poshandler-frame-center-near-bottom-fn
#'+helm-poshandler-frame-center-near-bottom
"The function that determines the location of the childframe. It should return "The function that determines the location of the childframe. It should return
a cons cell representing the X and Y coordinates. See a cons cell representing the X and Y coordinates. See
`posframe-poshandler-frame-center' as a reference.") `posframe-poshandler-frame-center' as a reference.")
@ -33,7 +32,7 @@ be negative.")
;; ;;
;;; Packages ;;; Packages
(def-package! helm-mode (use-package! helm-mode
:defer t :defer t
:after-call pre-command-hook :after-call pre-command-hook
:init :init
@ -58,7 +57,7 @@ be negative.")
(add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil))) (add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil)))
(def-package! helm (use-package! helm
:after helm-mode :after helm-mode
:preface :preface
(setq helm-candidate-number-limit 50 (setq helm-candidate-number-limit 50
@ -83,9 +82,7 @@ be negative.")
:init :init
(when (and EMACS26+ (featurep! +childframe)) (when (and EMACS26+ (featurep! +childframe))
(setq helm-display-function #'+helm-posframe-display) (setq helm-display-function #'+helm-posframe-display-fn))
;; Fix "Specified window is not displaying the current buffer" error
(advice-add #'posframe--get-font-height :around #'+helm*fix-get-font-height))
(let ((fuzzy (featurep! +fuzzy))) (let ((fuzzy (featurep! +fuzzy)))
(setq helm-M-x-fuzzy-match fuzzy (setq helm-M-x-fuzzy-match fuzzy
@ -110,27 +107,28 @@ be negative.")
:config :config
(set-popup-rule! "^\\*helm" :vslot -100 :size 0.22 :ttl nil) (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 ;; Hide the modeline
(defun +helm|hide-mode-line (&rest _) (defun +helm--hide-mode-line (&rest _)
(with-current-buffer (helm-buffer-get) (with-current-buffer (helm-buffer-get)
(unless helm-mode-line-string (unless helm-mode-line-string
(hide-mode-line-mode +1)))) (hide-mode-line-mode +1))))
(add-hook 'helm-after-initialize-hook #'+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-display-mode-line :override #'+helm--hide-mode-line)
(advice-add #'helm-ag-show-status-default-mode-line :override #'ignore) (advice-add #'helm-ag-show-status-default-mode-line :override #'ignore)
;; TODO Find a better way ;; Use helpful instead of describe-* to display documentation
(defun +helm*use-helpful (orig-fn arg) (dolist (fn '(helm-describe-variable helm-describe-function))
(cl-letf (((symbol-function #'describe-function) (advice-add fn :around #'doom-use-helpful-a)))
(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))
(def-package! helm-flx (use-package! helm-flx
:when (featurep! +fuzzy) :when (featurep! +fuzzy)
:hook (helm-mode . helm-flx-mode) :hook (helm-mode . helm-flx-mode)
:config (helm-flx-mode +1)) :config (helm-flx-mode +1))
@ -142,9 +140,9 @@ be negative.")
(define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort) (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) (set-popup-rule! "^\\*helm-ag-edit" :size 0.35 :ttl 0 :quit nil)
;; Recenter after jumping to match ;; 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 ;; 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 ;;;###package helm-bookmark
@ -164,7 +162,7 @@ be negative.")
;;;###package helm-projectile ;;;###package helm-projectile
(def-package! helm-projectile (use-package! helm-projectile
:commands (helm-projectile-find-file :commands (helm-projectile-find-file
helm-projectile-recentf helm-projectile-recentf
helm-projectile-switch-project helm-projectile-switch-project

View file

@ -5,7 +5,7 @@
(package! helm-ag) (package! helm-ag)
(package! helm-c-yasnippet) (package! helm-c-yasnippet)
(package! helm-company) (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! helm-projectile)
(package! swiper-helm) (package! swiper-helm)
(when (featurep! +fuzzy) (when (featurep! +fuzzy)

View file

@ -1,6 +1,6 @@
;;; completion/ido/config.el -*- lexical-binding: t; -*- ;;; completion/ido/config.el -*- lexical-binding: t; -*-
(defun +ido|init () (defun +ido-init-h ()
(setq ido-ignore-buffers (setq ido-ignore-buffers
'("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer" '("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer"
"^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-" "^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-"
@ -28,8 +28,9 @@
(insert "~/") (insert "~/")
(call-interactively #'self-insert-command)))) (call-interactively #'self-insert-command))))
(defun +ido*sort-mtime () (defadvice! +ido--sort-mtime-a ()
"Sort ido filelist by mtime instead of alphabetically." "Sort ido filelist by mtime instead of alphabetically."
:override #'ido-sort-mtime
(setq ido-temp-list (setq ido-temp-list
(sort ido-temp-list (sort ido-temp-list
(lambda (a b) (lambda (a b)
@ -40,8 +41,8 @@
(cl-loop for x in ido-temp-list (cl-loop for x in ido-temp-list
if (char-equal (string-to-char x) ?.) if (char-equal (string-to-char x) ?.)
collect x))) collect x)))
(advice-add #'ido-sort-mtime :override #'+ido*sort-mtime) (add-hook! '(ido-make-file-list-hook ido-make-dir-list-hook)
(add-hook! (ido-make-file-list ido-make-dir-list) #'+ido*sort-mtime) #'ido-sort-mtime)
;; ;;
(ido-mode 1) (ido-mode 1)
@ -52,7 +53,7 @@
(crm-custom-mode +1) (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)

View file

@ -1,32 +1,33 @@
;;; completion/ivy/autoload/hydras.el -*- lexical-binding: t; -*- ;;; completion/ivy/autoload/hydras.el -*- lexical-binding: t; -*-
;;;###if (featurep! :ui hydra)
;;;###autoload (eval-when-compile (require 'ivy-hydra))
(after! ivy-hydra
(with-no-warnings ;;;###autoload (autoload 'hydra-ivy/body "completion/ivy/autoload/hydras" nil nil)
(defhydra+ hydra-ivy (:hint nil :color pink) (defhydra+ hydra-ivy (:hint nil :color pink)
" "
Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name) 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 _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 ^^ _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 _G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow
" "
;; arrows ;; arrows
("l" ivy-alt-done) ("l" ivy-alt-done)
("h" ivy-backward-delete-char) ("h" ivy-backward-delete-char)
("g" ivy-beginning-of-buffer) ("g" ivy-beginning-of-buffer)
("G" ivy-end-of-buffer) ("G" ivy-end-of-buffer)
("d" ivy-scroll-up-command) ("d" ivy-scroll-up-command)
("u" ivy-scroll-down-command) ("u" ivy-scroll-down-command)
("e" ivy-scroll-down-command) ("e" ivy-scroll-down-command)
;; actions ;; actions
("q" keyboard-escape-quit :exit t) ("q" keyboard-escape-quit :exit t)
("<escape>" keyboard-escape-quit :exit t) ("<escape>" keyboard-escape-quit :exit t)
("TAB" ivy-alt-done :exit nil) ("TAB" ivy-alt-done :exit nil)
("RET" ivy-done :exit t) ("RET" ivy-done :exit t)
("C-SPC" ivy-call-and-recenter :exit nil) ("C-SPC" ivy-call-and-recenter :exit nil)
("f" ivy-call) ("f" ivy-call)
("c" ivy-toggle-calling) ("c" ivy-toggle-calling)
("m" ivy-toggle-fuzzy) ("m" ivy-toggle-fuzzy)
("t" (setq truncate-lines (not truncate-lines))) ("t" (setq truncate-lines (not truncate-lines)))
("o" ivy-occur :exit t)))) ("o" ivy-occur :exit t))

View file

@ -2,12 +2,12 @@
;;;###if (featurep! +childframe) ;;;###if (featurep! +childframe)
;;;###autoload ;;;###autoload
(defun +ivy-display-at-frame-center-near-bottom (str) (defun +ivy-display-at-frame-center-near-bottom-fn (str)
"TODO" "TODO"
(ivy-posframe--display str #'+ivy-poshandler-frame-center-near-bottom)) (ivy-posframe--display str #'+ivy-poshandler-frame-center-near-bottom-fn))
;;;###autoload ;;;###autoload
(defun +ivy-poshandler-frame-center-near-bottom (info) (defun +ivy-poshandler-frame-center-near-bottom-fn (info)
"TODO" "TODO"
(let ((parent-frame (plist-get info :parent-frame)) (let ((parent-frame (plist-get info :parent-frame))
(pos (posframe-poshandler-frame-center info))) (pos (posframe-poshandler-frame-center info)))

View file

@ -43,7 +43,7 @@ immediately runs it on the current candidate (ending the ivy session)."
;; ;;
;;; Packages ;;; Packages
(def-package! ivy (use-package! ivy
:defer 1 :defer 1
:after-call pre-command-hook :after-call pre-command-hook
:init :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') ;; enable ability to select prompt (alternative to `ivy-immediate-done')
ivy-use-selectable-prompt t) 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 ;; Ensure a jump point is registered before jumping to new locations with ivy
(defvar +ivy--origin nil) (defvar +ivy--origin nil)
(defun +ivy--record-position-maybe-fn ()
(defun +ivy|record-position-maybe ()
(with-ivy-window (with-ivy-window
(setq +ivy--origin (point-marker)))) (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 () (add-hook! 'minibuffer-exit-hook
(when (and (markerp +ivy--origin) (defun +ivy--set-jump-point-maybe-h ()
(not (equal (with-ivy-window (point-marker)) +ivy--origin))) (with-demoted-errors "Ivy error: %s"
(with-current-buffer (marker-buffer +ivy--origin) (when (and (markerp +ivy--origin)
(better-jumper-set-jump +ivy--origin))) (not (equal (with-ivy-window (point-marker))
(setq +ivy--origin nil)) +ivy--origin)))
(add-hook 'minibuffer-exit-hook #'+ivy|set-jump-point-maybe) (with-current-buffer (marker-buffer +ivy--origin)
(better-jumper-set-jump +ivy--origin)))
(setq +ivy--origin nil))))
(after! yasnippet (after! yasnippet
(add-to-list 'yas-prompt-functions #'+ivy-yas-prompt nil #'eq)) (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 "`ivy-completion-in-region' struggles with completing certain
evil-ex-specific constructs, so we disable it solely in evil-ex." evil-ex-specific constructs, so we disable it solely in evil-ex."
:around #'evil-ex
(let ((completion-in-region-function #'completion--in-region)) (let ((completion-in-region-function #'completion--in-region))
(apply orig-fn args))) (apply orig-fn args)))
(advice-add #'evil-ex :around #'+ivy*inhibit-ivy-in-evil-ex)
(define-key! ivy-mode-map (define-key! ivy-mode-map
[remap switch-to-buffer] #'+ivy/switch-buffer [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) (ivy-mode +1)
(def-package! ivy-hydra (use-package! ivy-hydra
:commands (ivy-dispatching-done-hydra ivy--matcher-desc ivy-hydra/body) :commands (ivy-dispatching-done-hydra ivy--matcher-desc ivy-hydra/body)
:init :init
(define-key! ivy-minibuffer-map (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))) (define-key ivy-minibuffer-map (kbd "M-o") #'hydra-ivy/body)))
(def-package! ivy-rich (use-package! ivy-rich
:after ivy :after ivy
:config :config
(when (featurep! +icons) (when (featurep! +icons)
@ -159,7 +185,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(ivy-rich-mode +1)) (ivy-rich-mode +1))
(def-package! all-the-icons-ivy (use-package! all-the-icons-ivy
:when (featurep! +icons) :when (featurep! +icons)
:after ivy :after ivy
:config :config
@ -175,7 +201,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(all-the-icons-ivy-setup)))) (all-the-icons-ivy-setup))))
(def-package! counsel (use-package! counsel
:commands counsel-describe-face :commands counsel-describe-face
:init :init
(map! [remap apropos] #'counsel-apropos (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")))) '(("O" +ivy-git-grep-other-window-action "open in other window"))))
(def-package! counsel-projectile (use-package! counsel-projectile
:defer t :defer t
:init :init
(map! [remap projectile-find-file] #'+ivy/projectile-find-file (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)) (ivy-set-display-transformer #'counsel-projectile-find-file nil))
(def-package! wgrep (use-package! wgrep
:commands wgrep-change-to-wgrep-mode :commands wgrep-change-to-wgrep-mode
:config (setq wgrep-auto-save-buffer t)) :config (setq wgrep-auto-save-buffer t))
(def-package! ivy-posframe (use-package! ivy-posframe
:when (and EMACS26+ (featurep! +childframe)) :when (and EMACS26+ (featurep! +childframe))
:hook (ivy-mode . ivy-posframe-mode) :hook (ivy-mode . ivy-posframe-mode)
:config :config
@ -277,14 +303,16 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(min-height . ,ivy-height))) (min-height . ,ivy-height)))
;; default to posframe display function ;; 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 ;; posframe doesn't work well with async sources
(dolist (fn '(swiper counsel-ag counsel-grep counsel-git-grep)) (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) :when (and (featurep! +fuzzy)
(not (featurep! +prescient))) (not (featurep! +prescient)))
:defer t ; is loaded by ivy :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)) ivy-flx-limit 10000))
(def-package! ivy-prescient (use-package! ivy-prescient
:hook (ivy-mode . ivy-prescient-mode) :hook (ivy-mode . ivy-prescient-mode)
:when (featurep! +prescient) :when (featurep! +prescient)
:init :init
@ -323,5 +351,5 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(prescient-persist-mode +1)) (prescient-persist-mode +1))
;; Used by `counsel-M-x' ;;;###package amx
(setq amx-save-file (concat doom-cache-dir "amx-items")) (setq amx-save-file (concat doom-cache-dir "amx-items")) ; used by `counsel-M-x'

View file

@ -150,6 +150,7 @@
:desc "Initialize repo" "r" #'magit-init :desc "Initialize repo" "r" #'magit-init
:desc "Clone repo" "R" #'+magit/clone :desc "Clone repo" "R" #'+magit/clone
:desc "Commit" "c" #'magit-commit-create :desc "Commit" "c" #'magit-commit-create
:desc "Fixup" "f" #'magit-commit-fixup
:desc "Issue" "i" #'forge-create-issue :desc "Issue" "i" #'forge-create-issue
:desc "Pull request" "p" #'forge-create-pullreq))) :desc "Pull request" "p" #'forge-create-pullreq)))

View file

@ -9,15 +9,14 @@
(setq shift-select-mode t) (setq shift-select-mode t)
(delete-selection-mode +1) (delete-selection-mode +1)
(def-package! expand-region (use-package! expand-region
:commands (er/contract-region er/mark-symbol er/mark-word) :commands (er/contract-region er/mark-symbol er/mark-word)
:config :config
(defun doom*quit-expand-region () (defadvice! doom--quit-expand-region-a ()
"Properly abort an expand-region region." "Properly abort an expand-region region."
:before '(evil-escape doom/escape)
(when (memq last-command '(er/expand-region er/contract-region)) (when (memq last-command '(er/expand-region er/contract-region))
(er/contract-region 0))) (er/contract-region 0))))
(advice-add #'evil-escape :before #'doom*quit-expand-region)
(advice-add #'doom/escape :before #'doom*quit-expand-region))
;; ;;

View file

@ -4,10 +4,15 @@
;; Don't let evil-collection interfere with certain keys ;; Don't let evil-collection interfere with certain keys
(setq evil-collection-key-blacklist (setq evil-collection-key-blacklist
(list "C-j" "C-k" "gd" "gf" "K" "[" "]" "gz" "<escape>" (list "gd" "gf" "K" "[" "]" "gz" "<escape>"
doom-leader-key doom-localleader-key doom-leader-key doom-localleader-key
doom-leader-alt-key doom-localleader-alt-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 ;;; Global keybindings
@ -53,6 +58,9 @@
;; misc ;; misc
:n "C-S-f" #'toggle-frame-fullscreen :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 ;; ported from vim
:m "]m" #'+evil/next-beginning-of-method :m "]m" #'+evil/next-beginning-of-method
@ -140,7 +148,6 @@
"C-r" #'winner-redo "C-r" #'winner-redo
"o" #'doom/window-enlargen "o" #'doom/window-enlargen
;; Delete window ;; Delete window
"c" #'+workspace/close-window-or-workspace
"C-C" #'ace-delete-window) "C-C" #'ace-delete-window)
;; Plugins ;; Plugins
@ -162,8 +169,8 @@
:bind ((evil-snipe-scope 'buffer) :bind ((evil-snipe-scope 'buffer)
(evil-snipe-enable-highlight) (evil-snipe-enable-highlight)
(evil-snipe-enable-incremental-highlight))) (evil-snipe-enable-incremental-highlight)))
"SPC" (λ!! #'avy-goto-char-timer t) "SPC" (λ!! #'evil-avy-goto-char-timer t)
"/" #'avy-goto-char-timer) "/" #'evil-avy-goto-char-timer)
;; text object plugins ;; text object plugins
:textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr :textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr
@ -520,6 +527,7 @@
:desc "Switch workspace buffer" "," #'persp-switch-to-buffer :desc "Switch workspace buffer" "," #'persp-switch-to-buffer
:desc "Switch buffer" "<" #'switch-to-buffer) :desc "Switch buffer" "<" #'switch-to-buffer)
:desc "Switch to last buffer" "`" #'evil-switch-to-windows-last-buffer
:desc "Resume last search" "'" :desc "Resume last search" "'"
(cond ((featurep! :completion ivy) #'ivy-resume) (cond ((featurep! :completion ivy) #'ivy-resume)
((featurep! :completion helm) #'helm-resume)) ((featurep! :completion helm) #'helm-resume))
@ -580,7 +588,9 @@
:desc "Switch buffer" "B" #'switch-to-buffer) :desc "Switch buffer" "B" #'switch-to-buffer)
(:unless (featurep! :ui workspaces) (:unless (featurep! :ui workspaces)
:desc "Switch buffer" "b" #'switch-to-buffer) :desc "Switch buffer" "b" #'switch-to-buffer)
:desc "Kill buffer" "d" #'kill-current-buffer
:desc "Kill buffer" "k" #'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 "Next buffer" "n" #'next-buffer
:desc "New empty buffer" "N" #'evil-buffer-new :desc "New empty buffer" "N" #'evil-buffer-new
:desc "Kill other buffers" "o" #'doom/kill-other-buffers :desc "Kill other buffers" "o" #'doom/kill-other-buffers
@ -674,6 +684,7 @@
:desc "Initialize repo" "r" #'magit-init :desc "Initialize repo" "r" #'magit-init
:desc "Clone repo" "R" #'+magit/clone :desc "Clone repo" "R" #'+magit/clone
:desc "Commit" "c" #'magit-commit-create :desc "Commit" "c" #'magit-commit-create
:desc "Fixup" "f" #'magit-commit-fixup
:desc "Branch" "b" #'magit-branch-and-checkout :desc "Branch" "b" #'magit-branch-and-checkout
:desc "Issue" "i" #'forge-create-issue :desc "Issue" "i" #'forge-create-issue
:desc "Pull request" "p" #'forge-create-pullreq))) :desc "Pull request" "p" #'forge-create-pullreq)))
@ -715,6 +726,9 @@
(:when (featurep! :ui treemacs) (:when (featurep! :ui treemacs)
:desc "Project sidebar" "p" #'+treemacs/toggle :desc "Project sidebar" "p" #'+treemacs/toggle
:desc "Find file in project sidebar" "P" #'+treemacs/find-file) :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) (:when (featurep! :term term)
:desc "Toggle terminal popup" "t" #'+term/toggle :desc "Toggle terminal popup" "t" #'+term/toggle
:desc "Open terminal here" "T" #'+term/here) :desc "Open terminal here" "T" #'+term/here)
@ -724,16 +738,6 @@
(:when (featurep! :term eshell) (:when (featurep! :term eshell)
:desc "Toggle eshell popup" "e" #'+eshell/toggle :desc "Toggle eshell popup" "e" #'+eshell/toggle
:desc "Open eshell here" "E" #'+eshell/here) :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) (:when (featurep! :tools macos)
:desc "Reveal in Finder" "o" #'+macos/reveal-in-finder :desc "Reveal in Finder" "o" #'+macos/reveal-in-finder
:desc "Reveal project in Finder" "O" #'+macos/reveal-project-in-finder :desc "Reveal project in Finder" "O" #'+macos/reveal-project-in-finder
@ -795,11 +799,11 @@
(:prefix-map ("s" . "snippets") (:prefix-map ("s" . "snippets")
:desc "View snippet for mode" "/" #'+snippets/find-for-current-mode :desc "View snippet for mode" "/" #'+snippets/find-for-current-mode
:desc "View snippet (global)" "?" #'+snippets/find :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 "View private snippet" "f" #'+snippets/find-private
:desc "Insert snippet" "i" #'yas-insert-snippet :desc "Insert snippet" "i" #'yas-insert-snippet
:desc "New snippet" "n" #'+snippet/new :desc "New snippet" "n" #'+snippets/new
:desc "New snippet alias" "N" #'+snippet/new-alias :desc "New snippet alias" "N" #'+snippets/new-alias
:desc "Reload snippets" "r" #'yas-reload-all :desc "Reload snippets" "r" #'yas-reload-all
:desc "Create temporary snippet" "s" #'aya-create :desc "Create temporary snippet" "s" #'aya-create
:desc "Expand temporary snippet" "e" #'aya-expand)) :desc "Expand temporary snippet" "e" #'aya-expand))
@ -810,10 +814,10 @@
:desc "Flycheck" "f" #'flycheck-mode :desc "Flycheck" "f" #'flycheck-mode
:desc "Frame fullscreen" "F" #'toggle-frame-fullscreen :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
:desc "Evil goggles" "g" #'evil-goggles-mode :desc "Evil goggles" "g" #'evil-goggles-mode
:desc "Impatient mode" "h" #'+impatient-mode/toggle
:desc "Indent guides" "i" #'highlight-indent-guides-mode :desc "Indent guides" "i" #'highlight-indent-guides-mode
:desc "Indent style" "I" #'doom/toggle-indent-style :desc "Indent style" "I" #'doom/toggle-indent-style
:desc "Line numbers" "l" #'doom/toggle-line-numbers :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 "org-tree-slide mode" "p" #'+org-present/start
:desc "Flyspell" "s" #'flyspell-mode)) :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) (defmacro set-repeater! (command next-func prev-func)
"Makes ; and , the universal repeat-keys in evil-mode. "Makes ; and , the universal repeat-keys in evil-mode.
To change these keys see `+default-repeat-keys'." 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 `(progn
(defun ,fn-sym (&rest _) (defun ,fn-sym (&rest _)
(evil-define-key* 'motion 'local (evil-define-key* 'motion 'local

View file

@ -119,6 +119,7 @@ languages)."
(if (and (sp-point-in-comment) (if (and (sp-point-in-comment)
comment-line-break-function) comment-line-break-function)
(funcall comment-line-break-function) (funcall comment-line-break-function)
(delete-horizontal-space t)
(newline nil t) (newline nil t)
(indent-according-to-mode))) (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. "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." If prefix ARG is set, prompt for a known project to search from."
(interactive (interactive
(list current-prefix-arg (thing-at-point 'symbol t))) (list current-prefix-arg (or (thing-at-point 'symbol t) "")))
(let ((default-directory (let ((default-directory
(if arg (if arg
(if-let* ((projects (projectile-relevant-known-projects))) (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 () (defun +default/org-notes-headlines ()
"Jump to an Org headline in `org-agenda-files'." "Jump to an Org headline in `org-agenda-files'."
(interactive) (interactive)
(completing-read (doom-completing-read-org-headings
"Jump to org headline: " "Jump to org headline: " org-agenda-files 3 t))
(doom-completing-read-org-headings org-agenda-files 3 t)))

View file

@ -44,13 +44,18 @@
;; or specific :post-handlers with: ;; or specific :post-handlers with:
;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC"))) ;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC")))
(after! smartparens (after! smartparens
;; Smartparens is broken in `cc-mode' as of Emacs 27. See
;; <https://github.com/Fuco1/smartparens/issues/963>.
(unless EMACS27+
(pushnew! sp--special-self-insert-commands 'c-electric-paren 'c-electric-brace))
;; Smartparens' navigation feature is neat, but does not justify how ;; 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 ;; 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! ;; be reactivated for non-evil users though. Needs more testing!
(defun doom|disable-smartparens-navigate-skip-match () (add-hook! 'after-change-major-mode-hook
(setq sp-navigate-skip-match nil (defun doom-disable-smartparens-navigate-skip-match-h ()
sp-navigate-consider-sgml-tags nil)) (setq sp-navigate-skip-match nil
(add-hook 'after-change-major-mode-hook #'doom|disable-smartparens-navigate-skip-match) sp-navigate-consider-sgml-tags nil)))
;; Autopair quotes more conservatively; if I'm next to a word/before another ;; Autopair quotes more conservatively; if I'm next to a word/before another
;; quote, I likely don't want to open a new pair. ;; quote, I likely don't want to open a new pair.
@ -101,10 +106,27 @@
;; intelligently. The result isn't very intelligent (causes redundant ;; intelligently. The result isn't very intelligent (causes redundant
;; characters), so just do it ourselves. ;; characters), so just do it ourselves.
(define-key! c++-mode-map "<" nil ">" nil) (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 ;; ...and leave it to smartparens
(sp-local-pair '(c++-mode objc-mode) (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"))) :post-handlers '(("| " "SPC")))
(sp-local-pair '(c-mode c++-mode objc-mode java-mode) (sp-local-pair '(c-mode c++-mode objc-mode java-mode)
@ -138,6 +160,27 @@
:actions '(insert) :actions '(insert)
:post-handlers '(("| " "SPC") ("|\n[i]*)[d-2]" "RET"))))) :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: ;; Highjacks backspace to:
;; a) balance spaces inside brackets/parentheses ( | ) -> (|) ;; a) balance spaces inside brackets/parentheses ( | ) -> (|)

View file

@ -4,7 +4,7 @@
(defalias '+literate/reload #'doom/reload) (defalias '+literate/reload #'doom/reload)
;;;###autoload ;;;###autoload
(defun +literate|recompile-maybe () (defun +literate-recompile-maybe-h ()
"Recompile config.org if we're editing an org file in our DOOMDIR. "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 We assume any org file in `doom-private-dir' is connected to your literate

View file

@ -1,11 +1,11 @@
;;; config/literate/init.el -*- lexical-binding: t; -*- ;;; config/literate/init.el -*- lexical-binding: t; -*-
(defvar +literate-config-file (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.") "The file path of your literate config file.")
(defvar +literate-config-cache-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 "The file path that `+literate-config-file' will be tangled to, then
byte-compiled from.") byte-compiled from.")
@ -13,32 +13,34 @@ byte-compiled from.")
;; ;;
(defun +literate-tangle (&optional force-p) (defun +literate-tangle (&optional force-p)
"Tangles `+literate-config-file' if it has changed." "Tangles `+literate-config-file' if it has changed."
(let ((default-directory doom-private-dir) (let ((default-directory doom-private-dir))
(org +literate-config-file)) (when (or (file-newer-than-file-p +literate-config-file
(when (or force-p (file-newer-than-file-p org +literate-config-cache-file)) +literate-config-cache-file)
force-p)
(message "Compiling your literate config...") (message "Compiling your literate config...")
(let* ((org (file-truename +literate-config-file)) (let* ((org (file-truename +literate-config-file))
(dest (concat (file-name-sans-extension org) ".el"))) (dest (concat (file-name-sans-extension org) ".el"))
(or (and (if (fboundp 'org-babel-tangle-file) (output (get-buffer-create "*org-tangle*")))
(org-babel-tangle-file org dest "emacs-lisp") (unwind-protect
;; We tangle in a separate, blank process because loading it ;; We tangle in a separate, blank process because loading it here
;; here would load all of :lang org (very expensive!). ;; would load all of :lang org (very expensive!).
(zerop (call-process (or (and (zerop (call-process
"emacs" nil nil nil "emacs" nil output nil
"-q" "--batch" "-l" "ob-tangle" "--eval" "-q" "--batch"
(format "(org-babel-tangle-file %S %S \"emacs-lisp\")" "-l" "ob-tangle"
org dest)))) "--eval" (format "(org-babel-tangle-file %S %S)"
;; Write the cache file to serve as our mtime cache org dest)))
(with-temp-file +literate-config-cache-file (with-current-buffer output
(message "Done!"))) (message "%s" (buffer-string))
(warn "There was a problem tangling your literate config!")))))) 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! ;; Let 'er rip!
(when noninteractive
(require 'ob-tangle nil t))
(+literate-tangle (or doom-reloading-p noninteractive)) (+literate-tangle (or doom-reloading-p noninteractive))
;; No need to load the resulting file. Doom will do this for us after all ;; No need to load the resulting file. Doom will do this for us after all
;; modules have finished loading. ;; modules have finished loading.
@ -46,4 +48,4 @@ byte-compiled from.")
;; Recompile our literate config if we modify it ;; Recompile our literate config if we modify it
(after! org (after! org
(add-hook 'after-save-hook #'+literate|recompile-maybe)) (add-hook 'after-save-hook #'+literate-recompile-maybe-h))

View file

@ -1,100 +1,6 @@
;;; editor/evil/+commands.el -*- lexical-binding: t; -*- ;;; 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 "<sh><!>")
(+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 "<sh><!>")
(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 "<r>")
(reverse-region beg end))
(evil-define-command +evil:cd (&optional path)
"Change `default-directory' with `cd'."
(interactive "<f>")
(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 "<a>")
(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 "<!><a>")
(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 ;;; Custom commands
;; Editing ;; Editing
(evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me (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]o" #'doom/kill-other-buffers)
(evil-ex-define-cmd "k[ill]b" #'doom/kill-buried-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 "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) (evil-ex-define-cmd "pop[up]" #'doom/popup-this-buffer)
;;; Project navigation ;;; Project navigation

View file

@ -9,11 +9,10 @@
;; 2. This ensures a predictable load order, versus lazy loading using :defer or ;; 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 ;; :after-call. This means users can use (after! org ...) and be sure that
;; their changes will override evil-collection's. ;; their changes will override evil-collection's.
;; 3. Eventually, I'd like to remove evil-collection. It changes too often, ;; 3. Ideally, we'd do away with evil-collection entirely. It changes too often,
;; introduces breaking bugs too frequently, and I don't always agree with ;; introduces breaking bugs too frequently, and I don't agree with all their
;; their design choices. Regardless, there are useful tidbits I'd like to ;; design choices. Regardless, it does mork than it causes trouble, so it may
;; keep. This will be a slow transition, but this file is where most of it ;; be here to stay.
;; will happen.
;; 4. Adds `+evil-collection-disabled-list', to make it easier for users to ;; 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 ;; 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). ;; `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) (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. ;; 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 (defvar evil-collection-mode-list
`(2048-game `(2048-game
ag ag
@ -152,7 +161,7 @@ variable for an explanation of the defaults (in comments). See
rtags rtags
simple simple
slime slime
(term term ansi-term) (term term ansi-term multi-term)
tetris tetris
tide tide
transmission transmission
@ -173,15 +182,15 @@ variable for an explanation of the defaults (in comments). See
youtube-dl youtube-dl
(ztree ztree-diff))) (ztree ztree-diff)))
(defun +evil-collection-init (module) (defun +evil-collection-init (module &optional disabled-list)
"Initialize evil-collection-MODULE. "Initialize evil-collection-MODULE.
Unlike `evil-collection-init', this respects `+evil-collection-disabled-list', Unlike `evil-collection-init', this respects `+evil-collection-disabled-list',
and complains if a module is loaded too early (during startup)." and complains if a module is loaded too early (during startup)."
(unless (memq (or (car-safe module) module) +evil-collection-disabled-list) (unless (memq (or (car-safe module) module) disabled-list)
(let ((module-sym (or (car-safe module) module))) (doom-log "Initialized evil-collection-%s %s"
(doom-log "Initialized evil-collection-%s %s" (or (car-safe module) module)
module-sym (if doom-init-time "" "(too early!)"))) (if doom-init-time "" "(too early!)"))
(with-demoted-errors "evil-collection error: %s" (with-demoted-errors "evil-collection error: %s"
(evil-collection-init (list module))))) (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 ;; 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 to load immediately. We avoid this by loading them after
;; evil-collection has first loaded... ;; evil-collection has first loaded...
(after! evil-collection (with-eval-after-load 'evil-collection
(let (+evil-collection-disabled-list) (mapc #'+evil-collection-init '(comint custom help)))
(mapc #'+evil-collection-init '(comint custom help))))
;; ...or on first invokation of their associated major/minor modes. ;; ...or on first invokation of their associated major/minor modes.
(add-transient-hook! 'Buffer-menu-mode (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 (mode evil-collection-mode-list)
(dolist (req (or (cdr-safe mode) (list mode))) (dolist (req (or (cdr-safe mode) (list mode)))
(with-eval-after-load req (with-eval-after-load req
(+evil-collection-init mode)))) (+evil-collection-init mode +evil-collection-disabled-list))))

View file

@ -1,76 +1,13 @@
;;; editor/evil/autoload/advice.el -*- lexical-binding: t; -*- ;;; editor/evil/autoload/advice.el -*- lexical-binding: t; -*-
(defun +evil--insert-newline (&optional above _noextranewline) ;;;###autoload
(let ((pos (save-excursion (beginning-of-line-text) (point))) (defun +evil-escape-a (&rest _)
comment-auto-fill-only-comments) "Call `doom/escape' if `evil-force-normal-state' is called interactively."
(require 'smartparens) (when (called-interactively-p 'any)
(evil-narrow-to-field (call-interactively #'doom/escape)))
(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 ;;;###autoload
(defun +evil*insert-newline-below-and-respect-comments (orig-fn count) (defun +evil-resolve-vim-path-a (file-name)
(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)
"Take a path and resolve any vim-like filename modifiers in it. This adds "Take a path and resolve any vim-like filename modifiers in it. This adds
support for most vim file modifiers, as well as: support for most vim file modifiers, as well as:
@ -148,8 +85,77 @@ more information on modifiers."
path file-name t t 1)))) path file-name t t 1))))
(replace-regexp-in-string regexp "\\1" file-name t))) (replace-regexp-in-string regexp "\\1" file-name t)))
;;;###autoload (autoload '+evil*window-split "editor/evil/autoload/advice" nil t) (defun +evil--insert-newline (&optional above _noextranewline)
(evil-define-command +evil*window-split (&optional count file) (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." "Same as `evil-window-split', but focuses (and recenters) the new split."
:repeat nil :repeat nil
(interactive "P<f>") (interactive "P<f>")
@ -164,8 +170,8 @@ more information on modifiers."
(balance-windows (window-parent))) (balance-windows (window-parent)))
(if file (evil-edit file))) (if file (evil-edit file)))
;;;###autoload (autoload '+evil*window-vsplit "editor/evil/autoload/advice" nil t) ;;;###autoload (autoload '+evil-window-vsplit-a "editor/evil/autoload/advice" nil t)
(evil-define-command +evil*window-vsplit (&optional count file) (evil-define-command +evil-window-vsplit-a (&optional count file)
"Same as `evil-window-vsplit', but focuses (and recenters) the new split." "Same as `evil-window-vsplit', but focuses (and recenters) the new split."
:repeat nil :repeat nil
(interactive "P<f>") (interactive "P<f>")
@ -181,18 +187,12 @@ more information on modifiers."
(if file (evil-edit file))) (if file (evil-edit file)))
;;;###autoload ;;;###autoload
(defun +evil*escape (&rest _) (defun +evil--make-numbered-markers-global-a (orig-fn char)
"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)
(or (and (>= char ?2) (<= char ?9)) (or (and (>= char ?2) (<= char ?9))
(funcall orig-fn char))) (funcall orig-fn char)))
;;;###autoload ;;;###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 "Make `try-expand-dabbrev' from `hippie-expand' work in minibuffer. See
`he-dabbrev-beg', so we need to redefine syntax for '/'." `he-dabbrev-beg', so we need to redefine syntax for '/'."
(set-syntax-table (let* ((table (make-syntax-table))) (set-syntax-table (let* ((table (make-syntax-table)))

View file

@ -1,5 +1,4 @@
;; editor/evil/autoload/evil.el -*- lexical-binding: t; -*- ;; editor/evil/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil)
;;;###autodef ;;;###autodef
(defun set-evil-initial-state! (modes state) (defun set-evil-initial-state! (modes state)
@ -12,72 +11,6 @@
(evil-set-initial-state modes state)))) (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 ;;; Interactive commands
@ -167,28 +100,6 @@ integration."
t)) t))
prefix))))) 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 "<r><//g><!>")
(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 "<r><//g><!>")
(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) ;;;###autoload (autoload '+evil:apply-macro "editor/evil/autoload/evil" nil t)
(evil-define-operator +evil:apply-macro (beg end) (evil-define-operator +evil:apply-macro (beg end)
"Apply macro to each line." "Apply macro to each line."

View file

@ -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 "<r><//>")
(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 "<r><//>")
(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 "<r><//><!>"))
;;;###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 "<sh><!>")
(+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 "<sh><!>")
(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 "<r>")
(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 "<f>")
(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 "<a>")
(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 "<!><a>")
(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)))))

View file

@ -44,8 +44,8 @@
(user-error "Must be called from a file-visiting buffer")) (user-error "Must be called from a file-visiting buffer"))
(let* ((directory (file-name-directory buffer-file-name)) (let* ((directory (file-name-directory buffer-file-name))
(filename (file-name-nondirectory buffer-file-name)) (filename (file-name-nondirectory buffer-file-name))
(files (doom-files-in directory :depth 0 :sort t :match "/[^._][^/]*$")) (files (doom-glob (file-name-directory buffer-file-name) "[!.]*"))
(index (cl-position filename files :test #'string=))) (index (cl-position filename files :test #'file-equal-p)))
(when (null index) (when (null index)
(user-error "Couldn't find this file in current directory")) (user-error "Couldn't find this file in current directory"))
(let ((index (+ index n))) (let ((index (+ index n)))

View file

@ -18,8 +18,9 @@ directives. By default, this only recognizes C directives.")
(defvar evil-want-C-u-scroll t) (defvar evil-want-C-u-scroll t)
(defvar evil-want-C-w-scroll t) (defvar evil-want-C-w-scroll t)
(defvar evil-want-Y-yank-to-eol 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) :hook (doom-init-modules . evil-mode)
:demand t :demand t
:preface :preface
@ -36,9 +37,9 @@ directives. By default, this only recognizes C directives.")
;; more vim-like behavior ;; more vim-like behavior
evil-symbol-word-search t evil-symbol-word-search t
;; cursor appearance ;; cursor appearance
evil-default-cursor '+evil-default-cursor evil-default-cursor '+evil-default-cursor-fn
evil-normal-state-cursor 'box 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-insert-state-cursor 'bar
evil-visual-state-cursor 'hollow evil-visual-state-cursor 'hollow
;; must be set before evil/evil-collection is loaded ;; 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) (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 ;; Start help-with-tutorial in emacs state
(advice-add #'help-with-tutorial :after (lambda (&rest _) (evil-emacs-state +1))) (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 ;; Done in a hook to ensure the popup rules load as late as possible
(defun +evil|init-popup-rules () (add-hook! 'doom-init-modules-hook
(set-popup-rules! (defun +evil--init-popup-rules-h ()
'(("^\\*evil-registers" :size 0.3) (set-popup-rules!
("^\\*Command Line" :size 8)))) '(("^\\*evil-registers" :size 0.3)
(add-hook 'doom-init-modules-hook #'+evil|init-popup-rules) ("^\\*Command Line" :size 8)))))
;; Change the cursor color in emacs state. We do it this roundabout way ;; 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 ;; 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--default-cursor-color "#ffffff")
(defvar +evil--emacs-cursor-color "#ff9999") (defvar +evil--emacs-cursor-color "#ff9999")
(defun +evil|update-cursor-color () (add-hook! 'doom-load-theme-hook
(setq +evil--default-cursor-color (face-background 'cursor) (defun +evil-update-cursor-color-h ()
+evil--emacs-cursor-color (face-foreground 'warning))) (setq +evil--default-cursor-color (face-background 'cursor)
(add-hook 'doom-load-theme-hook #'+evil|update-cursor-color) +evil--emacs-cursor-color (face-foreground 'warning))))
(defun +evil-default-cursor () (defun +evil-default-cursor-fn ()
(evil-set-cursor-color +evil--default-cursor-color)) (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)) (evil-set-cursor-color +evil--emacs-cursor-color))
(defun +evil|update-shift-width () (setq-hook! 'after-change-major-mode-hook evil-shift-width tab-width)
(setq evil-shift-width tab-width))
(add-hook 'after-change-major-mode-hook #'+evil|update-shift-width)
;; --- keybind fixes ---------------------- ;; --- keybind fixes ----------------------
@ -86,92 +91,101 @@ directives. By default, this only recognizes C directives.")
;; `evil-delete' in wgrep buffers. ;; `evil-delete' in wgrep buffers.
(define-key wgrep-mode-map [remap evil-delete] #'+evil-delete)) (define-key wgrep-mode-map [remap evil-delete] #'+evil-delete))
(defun +evil|disable-highlights () (add-hook! 'doom-escape-hook
"Disable ex search buffer highlights." (defun +evil-disable-ex-highlights-h ()
(when (evil-ex-hl-active-p 'evil-ex-search) "Disable ex search buffer highlights."
(evil-ex-nohighlight) (when (evil-ex-hl-active-p 'evil-ex-search)
t)) (evil-ex-nohighlight)
(add-hook 'doom-escape-hook #'+evil|disable-highlights) t)))
;; --- evil hacks ------------------------- ;; --- 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 (unless noninteractive
(setq save-silently t) (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'. ;; 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 ;; 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 ;; 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, ;; file modifiers like %:p:h. This adds support for most of vim's modifiers,
;; and one custom one: %:P (expand to the project root). ;; 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 ;; 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 ;; Focus and recenter new splits
(advice-add #'evil-window-split :override #'+evil*window-split) (advice-add #'evil-window-split :override #'+evil-window-split-a)
(advice-add #'evil-window-vsplit :override #'+evil*window-vsplit) (advice-add #'evil-window-vsplit :override #'+evil-window-vsplit-a)
;; In evil, registers 2-9 are buffer-local. In vim, they're global, so... ;; 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') ;; 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-above :around #'+evil--insert-newline-above-and-respect-comments-a)
(advice-add #'evil-open-below :around #'+evil*insert-newline-below-and-respect-comments) (advice-add #'evil-open-below :around #'+evil--insert-newline-below-and-respect-comments-a)
;; Recenter screen after most searches ;; Recenter screen after most searches
(advice-add! '(evil-visualstar/begin-search-forward (dolist (fn '(evil-visualstar/begin-search-forward
evil-visualstar/begin-search-backward evil-visualstar/begin-search-backward
evil-ex-search-word-backward evil-ex-search-word-backward
evil-ex-search-word-backward evil-ex-search-word-backward
evil-ex-search-forward evil-ex-search-forward
evil-ex-search-backward) evil-ex-search-backward))
:after #'doom*recenter) (advice-add fn :after #'doom-recenter-a))
;; --- custom interactive codes ----------- ;; --- custom interactive codes -----------
;; These arg types will highlight matches in the current buffer ;; 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 regexp-match
(evil-ex-define-argument-type global-match :runner +evil-ex-global-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 ;; Other commands can make use of this
(evil-define-interactive-code "<//>" (evil-define-interactive-code "<//>"
:ex-arg buffer-match (list (if (evil-ex-p) evil-ex-argument))) :ex-arg regexp-match
(evil-define-interactive-code "<//g>" (+evil--regexp-match-args evil-ex-argument))
:ex-arg global-match (list (if (evil-ex-p) evil-ex-argument)))
;; By default :g[lobal] doesn't highlight matches in the current buffer. I've (evil-define-interactive-code "<//!>"
;; got to write my own argument type and interactive code to get it to do so. :ex-arg regexp-global-match
(evil-ex-define-argument-type global-delim-match :runner +evil-ex-global-delim-match) (+evil--regexp-match-args evil-ex-argument))
(dolist (sym '(evil-ex-global evil-ex-global-inverted))
(evil-set-command-property sym :ex-arg 'global-delim-match))
;; Forward declare these so that ex completion works, even if the autoloaded ;; Forward declare these so that ex completion works, even if the autoloaded
;; functions aren't loaded yet. ;; functions aren't loaded yet.
(evil-set-command-properties (evil-add-command-properties '+evil:align :ex-arg 'regexp-match)
'+evil:align :move-point t :ex-arg 'buffer-match :ex-bang t :keep-visual t :suppress-operator t) (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' ;; `evil-collection'
(when (and (featurep! +everywhere) (when (and (featurep! +everywhere)
(not doom-reloading-p)) (not doom-reloading-p))
(load! "+everywhere")) (load! "+everywhere"))
;; Custom evil ex commands ;; Lazy load evil ex commands
(load! "+commands")) (delq! 'evil-ex features)
(add-transient-hook! 'evil-ex (provide 'evil-ex))
(after! evil-ex (load! "+commands")))
;; ;;
;; Packages ;; Packages
(def-package! evil-commentary (use-package! evil-commentary
:commands (evil-commentary :commands (evil-commentary
evil-commentary-yank evil-commentary-yank
evil-commentary-yank-line evil-commentary-yank-line
@ -179,8 +193,8 @@ directives. By default, this only recognizes C directives.")
:config (evil-commentary-mode 1)) :config (evil-commentary-mode 1))
(def-package! evil-easymotion (use-package! evil-easymotion
:commands (evilem-create evilem-default-keybindings) :commands evilem-create evilem-default-keybindings
:config :config
;; Use evil-search backend, instead of isearch ;; Use evil-search backend, instead of isearch
(evilem-make-motion evilem-motion-search-next #'evil-ex-search-next (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)))) :bind ((evil-ex-search-highlight-all nil))))
(def-package! evil-embrace (use-package! evil-embrace
:commands (embrace-add-pair embrace-add-pair-regexp) :commands embrace-add-pair embrace-add-pair-regexp
:hook (LaTeX-mode . embrace-LaTeX-mode-hook) :hook (LaTeX-mode . embrace-LaTeX-mode-hook)
:hook (org-mode . embrace-org-mode-hook) :hook (org-mode . embrace-org-mode-hook)
:hook ((ruby-mode enh-ruby-mode) . embrace-ruby-mode-hook) :hook ((ruby-mode enh-ruby-mode) . embrace-ruby-mode-hook)
:hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook)
:hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode) :hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode)
. +evil|embrace-lisp-mode-hook) . +evil-embrace-lisp-mode-hook-h)
:hook ((org-mode LaTeX-mode) . +evil|embrace-latex-mode-hook) :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) :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 :init
(after! evil-surround (after! evil-surround
(evil-embrace-enable-evil-surround-integration)) (evil-embrace-enable-evil-surround-integration))
:config :config
(setq evil-embrace-show-help-p nil) (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)) (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 (push (cons ?f (make-embrace-pair-struct
:key ?f :key ?f
:read-function #'+evil--embrace-elisp-fn :read-function #'+evil--embrace-elisp-fn
@ -222,7 +236,7 @@ directives. By default, this only recognizes C directives.")
:right-regexp ")")) :right-regexp ")"))
embrace--pairs-list)) 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) (set (make-local-variable 'evil-embrace-evil-surround-keys)
(delq ?< evil-embrace-evil-surround-keys)) (delq ?< evil-embrace-evil-surround-keys))
(push (cons ?< (make-embrace-pair-struct (push (cons ?< (make-embrace-pair-struct
@ -241,9 +255,9 @@ directives. By default, this only recognizes C directives.")
:right-regexp "\\[]})]"))) :right-regexp "\\[]})]")))
(def-package! evil-escape (use-package! evil-escape
:commands (evil-escape) :commands evil-escape
:after-call (evil-normal-state-exit-hook) :after-call evil-normal-state-exit-hook
:init :init
(setq evil-escape-excluded-states '(normal visual multiedit emacs motion) (setq evil-escape-excluded-states '(normal visual multiedit emacs motion)
evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode) 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)) (evil-escape-mode +1))
(def-package! evil-exchange (use-package! evil-exchange
:commands evil-exchange :commands evil-exchange
:config :config
(defun +evil|escape-exchange () (add-hook! 'doom-escape-hook
(when evil-exchange--overlays (defun +evil--escape-exchange-h ()
(evil-exchange-cancel) (when evil-exchange--overlays
t)) (evil-exchange-cancel)
(add-hook 'doom-escape-hook #'+evil|escape-exchange)) t))))
(def-package! evil-snipe (use-package! evil-snipe
:commands (evil-snipe-mode evil-snipe-override-mode :commands (evil-snipe-mode
evil-snipe-local-mode evil-snipe-override-local-mode) evil-snipe-override-mode
evil-snipe-local-mode
evil-snipe-override-local-mode)
:after-call pre-command-hook :after-call pre-command-hook
:init :init
(setq evil-snipe-smart-case t (setq evil-snipe-smart-case t
@ -282,7 +298,7 @@ directives. By default, this only recognizes C directives.")
(evil-snipe-override-mode +1)) (evil-snipe-override-mode +1))
(def-package! evil-surround (use-package! evil-surround
:commands (global-evil-surround-mode :commands (global-evil-surround-mode
evil-surround-edit evil-surround-edit
evil-Surround-edit evil-Surround-edit
@ -290,8 +306,17 @@ directives. By default, this only recognizes C directives.")
:config (global-evil-surround-mode 1)) :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 # ;; Allows you to use the selection for * and #
(def-package! evil-visualstar (use-package! evil-visualstar
:commands (evil-visualstar/begin-search :commands (evil-visualstar/begin-search
evil-visualstar/begin-search-forward evil-visualstar/begin-search-forward
evil-visualstar/begin-search-backward) 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 (use-package! exato
:commands (evil-outer-xml-attr evil-inner-xml-attr)) :commands evil-outer-xml-attr evil-inner-xml-attr)

View file

@ -9,14 +9,14 @@
(package! evil-escape) (package! evil-escape)
(package! evil-exchange) (package! evil-exchange)
(package! evil-indent-plus) (package! evil-indent-plus)
(package! evil-numbers :recipe (:fetcher github :repo "janpath/evil-numbers")) (package! evil-numbers :recipe (:host github :repo "janpath/evil-numbers"))
(package! evil-textobj-anyblock)
(package! evil-snipe) (package! evil-snipe)
(package! evil-surround) (package! evil-surround)
(package! evil-textobj-anyblock)
(package! evil-traces)
(package! evil-visualstar) (package! evil-visualstar)
(package! exato) (package! exato)
;; ;;
(when (featurep! +everywhere) (when (featurep! +everywhere)
;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this ;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this

View file

@ -10,10 +10,10 @@
(after-all (after-all
(unload-feature 'evil t)) (unload-feature 'evil t))
(before-each (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))) (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" (describe "file modifiers"
(it "supports basic vim file modifiers" (it "supports basic vim file modifiers"
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el") (let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")

View file

@ -114,11 +114,12 @@ information.")
(or (file-in-directory-p file doom-private-dir) (or (file-in-directory-p file doom-private-dir)
(file-in-directory-p file doom-emacs-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 "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 must be non-read-only, empty, and there must be a rule in
`+file-templates-alist' that applies to it." `+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) (bobp) (eobp)
(not (string-match-p "^ *\\*" (buffer-name)))) (not (string-match-p "^ *\\*" (buffer-name))))
(when-let (rule (cl-find-if #'+file-template-p +file-templates-alist)) (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))) (yas-reload-all)))
;; ;;
(add-hook 'find-file-hook #'+file-templates|check) (add-hook 'find-file-hook #'+file-templates-check-h)

View file

@ -6,12 +6,12 @@
:group 'doom-themes) :group 'doom-themes)
;;;###autoload ;;;###autoload
(defun +fold-hideshow-haml-forward-sexp (arg) (defun +fold-hideshow-haml-forward-sexp-fn (arg)
(haml-forward-sexp arg) (haml-forward-sexp arg)
(move-beginning-of-line 1)) (move-beginning-of-line 1))
;;;###autoload ;;;###autoload
(defun +fold-hideshow-forward-block-by-indent (_arg) (defun +fold-hideshow-forward-block-by-indent-fn (_arg)
(let ((start (current-indentation))) (let ((start (current-indentation)))
(forward-line) (forward-line)
(unless (= start (current-indentation)) (unless (= start (current-indentation))
@ -20,7 +20,7 @@
(end-of-line))))) (end-of-line)))))
;;;###autoload ;;;###autoload
(defun +fold-hideshow-set-up-overlay (ov) (defun +fold-hideshow-set-up-overlay-fn (ov)
(when (eq 'code (overlay-get ov 'hs)) (when (eq 'code (overlay-get ov 'hs))
(when (featurep 'vimish-fold) (when (featurep 'vimish-fold)
(overlay-put (overlay-put

View file

@ -17,21 +17,22 @@
;; ;;
;; Packages ;; Packages
(def-package! hideshow ; built-in (use-package! hideshow ; built-in
:commands (hs-toggle-hiding hs-hide-block hs-hide-level hs-show-all hs-hide-all) :commands (hs-toggle-hiding
hs-hide-block
hs-hide-level
hs-show-all
hs-hide-all)
:config :config
(setq hs-hide-comments-when-hiding-all nil (setq hs-hide-comments-when-hiding-all nil
;; Nicer code-folding overlays (with fringe indicators) ;; 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." "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) (unless (bound-and-true-p hs-minor-mode)
(hs-minor-mode +1))) (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 ;; extra folding support for more languages
(unless (assq 't hs-special-modes-alist) (unless (assq 't hs-special-modes-alist)
@ -41,8 +42,8 @@
(yaml-mode "\\s-*\\_<\\(?:[^:]+\\)\\_>" (yaml-mode "\\s-*\\_<\\(?:[^:]+\\)\\_>"
"" ""
"#" "#"
+fold-hideshow-forward-block-by-indent nil) +fold-hideshow-forward-block-by-indent-fn nil)
(haml-mode "[#.%]" "\n" "/" +fold-hideshow-haml-forward-sexp nil) (haml-mode "[#.%]" "\n" "/" +fold-hideshow-haml-forward-sexp-fn nil)
(ruby-mode "class\\|d\\(?:ef\\|o\\)\\|module\\|[[{]" (ruby-mode "class\\|d\\(?:ef\\|o\\)\\|module\\|[[{]"
"end\\|[]}]" "end\\|[]}]"
"#\\|=begin" "#\\|=begin"
@ -61,7 +62,7 @@
'((t)))))) '((t))))))
(def-package! evil-vimish-fold (use-package! evil-vimish-fold
:when (featurep! :editor evil) :when (featurep! :editor evil)
:commands (evil-vimish-fold/next-fold evil-vimish-fold/previous-fold :commands (evil-vimish-fold/next-fold evil-vimish-fold/previous-fold
evil-vimish-fold/delete evil-vimish-fold/delete-all evil-vimish-fold/delete evil-vimish-fold/delete-all

View file

@ -98,7 +98,7 @@ Stolen shamelessly from go-mode"
(if fmt (cons (intern fmt) t)))) (if fmt (cons (intern fmt) t))))
;;;###autoload ;;;###autoload
(defun +format*probe (orig-fn) (defun +format-probe-a (orig-fn)
"Use `+format-with' instead, if it is set." "Use `+format-with' instead, if it is set."
(if +format-with (if +format-with
(list +format-with t) (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. position of the first change in the buffer.
See `+format/buffer' for the interactive version of this function, and 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) (if (not formatter)
'no-formatter 'no-formatter
(let ((f-function (gethash formatter format-all--format-table)) (let ((f-function (gethash formatter format-all--format-table))
@ -228,12 +228,12 @@ is selected)."
;; Hooks ;; Hooks
;;;###autoload ;;;###autoload
(defun +format|enable-on-save () (defun +format-enable-on-save-h ()
"Enables formatting on save." "Enables formatting on save."
(add-hook 'before-save-hook #'+format|buffer nil t)) (add-hook 'before-save-hook #'+format-buffer-h nil t))
;;;###autoload ;;;###autoload
(defalias '+format|buffer #'+format/buffer (defalias '+format-buffer-h #'+format/buffer
"Format the source code in the current buffer with minimal feedback. "Format the source code in the current buffer with minimal feedback.
Meant for `before-save-hook'.") Meant for `before-save-hook'.")

View file

@ -25,7 +25,7 @@ Indentation is always preserved when formatting regions.")
;; ;;
;;; Bootstrap ;;; Bootstrap
(defun +format|enable-on-save-maybe () (defun +format-enable-on-save-maybe-h ()
"Enable formatting on save in certain major modes. "Enable formatting on save in certain major modes.
This is controlled by `+format-on-save-enabled-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))) (format-all-mode +1)))
(when (featurep! +onsave) (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 ;; Allow a specific formatter to be used by setting `+format-with', either
;; buffer-locally or let-bound. ;; 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 ;; Doom uses a modded `format-all-buffer', which
;; 1. Enables partial reformatting (while preserving leading indentation), ;; 1. Enables partial reformatting (while preserving leading indentation),

View file

@ -1,6 +1,6 @@
;;; editor/lispy/config.el -*- lexical-binding: t; -*- ;;; editor/lispy/config.el -*- lexical-binding: t; -*-
(def-package! lispy (use-package! lispy
:hook ((common-lisp-mode . lispy-mode) :hook ((common-lisp-mode . lispy-mode)
(emacs-lisp-mode . lispy-mode) (emacs-lisp-mode . lispy-mode)
(scheme-mode . lispy-mode) (scheme-mode . lispy-mode)
@ -12,7 +12,7 @@
(setq lispy-close-quotes-at-end-p t) (setq lispy-close-quotes-at-end-p t)
(add-hook 'lispy-mode-hook #'turn-off-smartparens-mode)) (add-hook 'lispy-mode-hook #'turn-off-smartparens-mode))
(def-package! lispyville (use-package! lispyville
:when (featurep! :editor evil) :when (featurep! :editor evil)
:hook (lispy-mode . lispyville-mode) :hook (lispy-mode . lispyville-mode)
:config :config

View file

@ -1,18 +1,27 @@
;;; editor/multiple-cursors/config.el -*- lexical-binding: t; -*- ;;; editor/multiple-cursors/config.el -*- lexical-binding: t; -*-
(def-package! evil-mc (use-package! evil-mc
:when (featurep! :editor evil) :when (featurep! :editor evil)
:commands (evil-mc-make-cursor-here evil-mc-make-all-cursors :commands (evil-mc-make-cursor-here
evil-mc-undo-all-cursors evil-mc-pause-cursors evil-mc-make-all-cursors
evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor 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-and-goto-last-cursor
evil-mc-make-cursor-move-next-line evil-mc-make-cursor-move-next-line
evil-mc-make-cursor-move-prev-line evil-mc-make-cursor-at-pos evil-mc-make-cursor-move-prev-line
evil-mc-has-cursors-p evil-mc-make-and-goto-next-cursor evil-mc-make-cursor-at-pos
evil-mc-skip-and-goto-next-cursor evil-mc-make-and-goto-prev-cursor evil-mc-has-cursors-p
evil-mc-skip-and-goto-prev-cursor evil-mc-make-and-goto-next-match evil-mc-make-and-goto-next-cursor
evil-mc-skip-and-goto-next-match evil-mc-skip-and-goto-next-match evil-mc-skip-and-goto-next-cursor
evil-mc-make-and-goto-prev-match evil-mc-skip-and-goto-prev-match) 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 :init
(defvar evil-mc-key-map (make-sparse-keymap)) (defvar evil-mc-key-map (make-sparse-keymap))
:config :config
@ -39,13 +48,13 @@
;; disable evil-escape in evil-mc; causes unwanted text on invocation ;; disable evil-escape in evil-mc; causes unwanted text on invocation
(add-to-list 'evil-mc-incompatible-minor-modes 'evil-escape-mode nil #'eq) (add-to-list 'evil-mc-incompatible-minor-modes 'evil-escape-mode nil #'eq)
(defun +multiple-cursors|escape-multiple-cursors () (add-hook! 'doom-escape-hook
"Clear evil-mc cursors and restore state." (defun +multiple-cursors-escape-multiple-cursors-h ()
(when (evil-mc-has-cursors-p) "Clear evil-mc cursors and restore state."
(evil-mc-undo-all-cursors) (when (evil-mc-has-cursors-p)
(evil-mc-resume-cursors) (evil-mc-undo-all-cursors)
t)) (evil-mc-resume-cursors)
(add-hook 'doom-escape-hook #'+multiple-cursors|escape-multiple-cursors) t)))
;; Forward declare these so that ex completion and evil-mc support is ;; Forward declare these so that ex completion and evil-mc support is
;; recognized before the autoloaded functions are loaded. ;; recognized before the autoloaded functions are loaded.
@ -71,45 +80,45 @@
(defvar +mc--compat-evil-prev-state nil) (defvar +mc--compat-evil-prev-state nil)
(defvar +mc--compat-mark-was-active nil) (defvar +mc--compat-mark-was-active nil)
(defun +multiple-cursors|compat-switch-to-emacs-state () (add-hook! 'multiple-cursors-mode-enabled-hook
(when (and (bound-and-true-p evil-mode) (defun +multiple-cursors-compat-switch-to-emacs-state-h ()
(not (memq evil-state '(insert emacs)))) (when (and (bound-and-true-p evil-mode)
(setq +mc--compat-evil-prev-state evil-state) (not (memq evil-state '(insert emacs))))
(when (region-active-p) (setq +mc--compat-evil-prev-state evil-state)
(setq +mc--compat-mark-was-active t)) (when (region-active-p)
(let ((mark-before (mark)) (setq +mc--compat-mark-was-active t))
(point-before (point))) (let ((mark-before (mark))
(evil-emacs-state 1) (point-before (point)))
(when (or +mc--compat-mark-was-active (region-active-p)) (evil-emacs-state 1)
(goto-char point-before) (when (or +mc--compat-mark-was-active (region-active-p))
(set-mark mark-before))))) (goto-char point-before)
(add-hook 'multiple-cursors-mode-enabled-hook #'+multiple-cursors|compat-switch-to-emacs-state) (set-mark mark-before))))))
(defun +multiple-cursors|compat-back-to-previous-state () (add-hook! 'multiple-cursors-mode-disabled-hook
(when +mc--compat-evil-prev-state (defun +multiple-cursors-compat-back-to-previous-state-h ()
(unwind-protect (when +mc--compat-evil-prev-state
(case +mc--compat-evil-prev-state (unwind-protect
((normal visual) (evil-force-normal-state)) (case +mc--compat-evil-prev-state
(t (message "Don't know how to handle previous state: %S" ((normal visual) (evil-force-normal-state))
+mc--compat-evil-prev-state))) (t (message "Don't know how to handle previous state: %S"
(setq +mc--compat-evil-prev-state nil) +mc--compat-evil-prev-state)))
(setq +mc--compat-mark-was-active nil)))) (setq +mc--compat-evil-prev-state nil)
(add-hook 'multiple-cursors-mode-disabled-hook #'+multiple-cursors|compat-back-to-previous-state) (setq +mc--compat-mark-was-active nil)))))
;; When running edit-lines, point will return (position + 1) as a ;; When running edit-lines, point will return (position + 1) as a result of
;; result of how evil deals with regions ;; how evil deals with regions
(defun +multiple-cursors*adjust-mark-for-evil (&rest _) (defadvice! +multiple--cursors-adjust-mark-for-evil-a (&rest _)
:before #'mc/edit-lines
(when (and (bound-and-true-p evil-mode) (when (and (bound-and-true-p evil-mode)
(not (memq evil-state '(insert emacs)))) (not (memq evil-state '(insert emacs))))
(if (> (point) (mark)) (if (> (point) (mark))
(goto-char (1- (point))) (goto-char (1- (point)))
(push-mark (1- (mark)))))) (push-mark (1- (mark))))))
(advice-add #'mc/edit-lines :before #'+multiple-cursors*adjust-mark-for-evil)
(defun +multiple-cursors|evil-compat-rect-switch-state () (add-hook! 'rectangular-region-mode-hook
(if rectangular-region-mode (defun +multiple-cursors-evil-compat-rect-switch-state-h ()
(+multiple-cursors|compat-switch-to-emacs-state) (if rectangular-region-mode
(setq +mc--compat-evil-prev-state nil))) (+multiple-cursors-compat-switch-to-emacs-state-h)
(add-hook 'rectangular-region-mode-hook '+multiple-cursors|evil-compat-rect-switch-state) (setq +mc--compat-evil-prev-state nil))))
(defvar mc--default-cmds-to-run-once nil))) (defvar mc--default-cmds-to-run-once nil)))

View file

@ -1,9 +1,8 @@
;;; editor/objed/config.el -*- lexical-binding: t; -*- ;;; editor/objed/config.el -*- lexical-binding: t; -*-
(def-package! objed (use-package! objed
:after-call pre-command-hook :after-call pre-command-hook
:config :config
;; Prevent undo actions from exiting edit state ;; Prevent undo actions from exiting edit state
(add-to-list 'objed-keeper-commands 'undo-tree-undo) (add-to-list 'objed-keeper-commands 'undo-tree-undo)
(add-to-list 'objed-keeper-commands 'undo-tree-redo) (add-to-list 'objed-keeper-commands 'undo-tree-redo)
@ -11,21 +10,20 @@
(defvar +objed--extra-face-remaps nil) (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." "Add extra face remaps when objed activates."
:after 'objed--init
(when (memq 'objed-hl (assq 'hl-line face-remapping-alist)) (when (memq 'objed-hl (assq 'hl-line face-remapping-alist))
(push (face-remap-add-relative 'solaire-hl-line-face 'objed-hl) (push (face-remap-add-relative 'solaire-hl-line-face 'objed-hl)
+objed--extra-face-remaps))) +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." "Remove extra face remaps when objed de-activates."
:after 'objed--reset
(unless (memq 'objed-hl (assq 'hl-line face-remapping-alist)) (unless (memq 'objed-hl (assq 'hl-line face-remapping-alist))
(dolist (remap +objed--extra-face-remaps) (dolist (remap +objed--extra-face-remaps)
(face-remap-remove-relative remap)) (face-remap-remove-relative remap))
(setq +objed--extra-face-remaps nil))) (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) (unless (featurep! +manual)
(objed-mode +1))) (objed-mode +1)))

View file

@ -1,6 +1,6 @@
;;; editor/parinfer/config.el -*- lexical-binding: t; -*- ;;; 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) :hook ((emacs-lisp-mode clojure-mode scheme-mode lisp-mode) . parinfer-mode)
:init :init
(setq parinfer-extensions (setq parinfer-extensions

View file

@ -10,7 +10,7 @@
`rotate-text' will cycle through." `rotate-text' will cycle through."
(declare (indent defun)) (declare (indent defun))
(dolist (mode (doom-enlist modes)) (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 (fset fn-name
(lambda () (lambda ()
(setq-local rotate-text-local-symbols symbols) (setq-local rotate-text-local-symbols symbols)

Some files were not shown because too many files have changed in this diff Show more