Merge branch 'develop' into fix/add-add-featurep-condition-to-global-bindings

This commit is contained in:
Henrik Lissner 2019-10-03 14:57:19 -04:00 committed by GitHub
commit a5d9079c43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
184 changed files with 2868 additions and 1922 deletions

View file

@ -10,9 +10,6 @@ before_install:
- export PATH="/home/travis/.evm/bin:$PATH"
- evm config path /tmp
- evm install $EVM_EMACS --use --skip
- mkdir -p ~/.config/doom
- cp init.test.el ~/.config/doom/init.el
- bin/doom -y -i install
env:
- EVM_EMACS=emacs-25.3-travis
- EVM_EMACS=emacs-26.1-travis
@ -22,5 +19,5 @@ matrix:
- env: EVM_EMACS=emacs-git-snapshot-travis
script:
- bin/doom version
- bin/doom -d test
- bin/doom test
- bin/doom -y compile

View file

@ -82,7 +82,7 @@
;; Bootstrap Doom
(if (not noninteractive)
(progn
(let ((doom-interactive-mode t))
(load (expand-file-name "init.el" user-emacs-directory)
nil 'nomessage)
(doom-run-all-startup-hooks-h))
@ -91,14 +91,13 @@
(doom-initialize 'force-p)
(doom-initialize-modules)
(cond ((and (not (cdr args))
(member (car args) '("help" "h")))
(usage))
((not args)
(print! (error "No command detected.\n"))
(cond ((or (not args)
(and (not (cdr args))
(member (car args) '("help" "h"))))
(unless args
(print! (error "No command detected.\n")))
(usage))
((require 'core-cli)
(let ((default-directory user-emacs-directory))
(setq argv nil)
(condition-case e
(doom-dispatch (car args) (cdr args))
@ -119,4 +118,4 @@
"Emacs outputs to standard error, so you'll need to redirect stderr to\n"
"stdout to pipe this to a file or clipboard!\n\n"
" e.g. doom -d install 2>&1 | clipboard-program"))
(signal 'doom-error e)))))))))
(signal 'doom-error e))))))))

View file

@ -1,7 +1,7 @@
#!/usr/bin/env sh
":"; command -v emacs >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; } # -*-emacs-lisp-*-
":"; VERSION=$(emacs --version | head -n1)
":"; case $VERSION in *\ 2[0-2].[0-1].[0-9]) echo "You're running $VERSION"; echo "That version is too old to run the doctor. Check your PATH"; echo; exit 2 ;; esac
":"; case $VERSION in *\ 2[0-2].[0-1].[0-9]) echo "You're running $VERSION"; echo "That version is too old to run the doctor (25.3 minimum). Check your PATH"; echo; exit 2 ;; esac
":"; exec emacs --quick --script "$0"; exit 0
;; The Doom doctor is essentially one big, self-contained elisp shell script
@ -43,7 +43,7 @@
(defun sh (cmd &rest args)
(ignore-errors
(string-trim-right
(shell-command-to-string (apply #'format cmd args)))))
(shell-command-to-string (if args (apply #'format cmd args) cmd)))))
(defun elc-check-dir (dir)
(dolist (file (directory-files-recursively dir "\\.elc$"))
@ -139,18 +139,6 @@
(concat "\nMacOS users should use homebrew (https://brew.sh) to install Emacs\n"
" brew install emacs --with-modules --with-imagemagick --with-cocoa"))))
(section! "Checking if your version of Emacs has changed recently...")
(let ((version-file (expand-file-name ".local/emacs-version.el" user-emacs-directory))
doom--last-emacs-version)
(when (and (load version-file 'noerror 'nomessage 'nosuffix)
(not (equal emacs-version doom--last-emacs-version)))
(warn! "Your version of Emacs has changed from %S to %S. Recompile your packages!"
doom--last-emacs-version
emacs-version)
(explain! "Byte-code compiled in one version of Emacs may not work in another version."
"It is recommended that you reinstall your plugins or recompile them with"
"`bin/doom rebuild'.")))
(section! "Checking for Emacs config conflicts...")
(when (file-exists-p "~/.emacs")
(warn! "Detected an ~/.emacs file, which may prevent Doom from loading")

View file

@ -16,7 +16,7 @@ IF NOT [%1]==[] (
)
IF [%command%]==[run] (
start runemacs -Q %args% -l ..\init.el -f "doom|run-all-startup-hooks"
start runemacs -Q %args% -l ..\init.el -f "doom-run-all-startup-hooks-h"
) ELSE (
emacs --quick --script .\doom -- %*
)

View file

@ -74,7 +74,7 @@ line."
Uses the same mechanism as 'bin/doom env reload'."
(interactive)
(compile (format "%s env refresh" (expand-file-name "bin/doom" doom-emacs-dir)))
(compile (format "%s env" (expand-file-name "bin/doom" doom-emacs-dir)))
(while compilation-in-progress
(sit-for 1))
(unless (file-readable-p doom-env-file)

View file

@ -39,7 +39,12 @@ ready to be pasted in a bug report on github."
(version . ,emacs-version)
(features ,@system-configuration-features)
(build . ,(format-time-string "%b %d, %Y" emacs-build-time))
(buildopts ,system-configuration-options))
(buildopts ,system-configuration-options)
(windowsys . ,(if noninteractive 'batch window-system))
(daemonp . ,(cond ((daemonp) 'daemon)
((and (require 'server)
(server-running-p))
'server-running))))
(doom
(version . ,doom-version)
(build . ,(sh "git log -1 --format=\"%D %h %ci\"")))
@ -63,11 +68,11 @@ ready to be pasted in a bug report on github."
(modules
,@(or (cl-loop with cat = nil
for key being the hash-keys of doom-modules
if (or (not cat) (not (eq cat (car key))))
if (or (not cat)
(not (eq cat (car key))))
do (setq cat (car key))
and collect cat
and collect (cdr key)
else collect
collect
(let ((flags (doom-module-get cat (cdr key) :flags)))
(if flags
`(,(cdr key) ,@flags)
@ -75,18 +80,20 @@ ready to be pasted in a bug report on github."
'("n/a")))
(packages
,@(or (ignore-errors
(require 'core-packages)
(doom-initialize-packages)
(cl-loop for (name . plist) in doom-packages
if (doom-package-private-p name)
(let ((doom-interactive-mode t)
doom-packages
doom-disabled-packages)
(doom--read-module-packages-file
(doom-path doom-private-dir "packages.el")
nil t)
(cl-loop for (name . plist) in (nreverse doom-packages)
collect
(format
"%s" (if-let (splist (doom-plist-delete (copy-sequence plist)
(if-let (splist (doom-plist-delete (copy-sequence plist)
:modules))
(cons name splist)
(prin1-to-string (cons name splist))
name))))
'("n/a")))
(elpa-packages
(elpa
,@(or (ignore-errors
(cl-loop for (name . _) in package-alist
collect (format "%s" name)))
@ -203,24 +210,23 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(setq-default buffer-undo-tree (make-undo-tree))))
(pcase mode
(`vanilla-doom+ ; Doom core + modules - private config
`((setq doom-private-dir "/tmp/does/not/exist")
`((setq doom-init-modules-p t)
(load-file ,user-init-file)
(setq doom-modules ',doom-modules)
(maphash (lambda (key plist)
(let ((doom--current-module key)
(doom--current-flags (plist-get plist :flags)))
(load! "init" (plist-get plist :path) t)))
(load! "init" (doom-module-locate-path (car key) (cdr key)) t)))
doom-modules)
(maphash (lambda (key plist)
(let ((doom--current-module key)
(doom--current-flags (plist-get plist :flags)))
(load! "config" (plist-get plist :path) t)))
(load! "config" (doom-module-locate-path (car key) (cdr key)) t)))
doom-modules)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(doom-run-all-startup-hooks-h)))
(`vanilla-doom ; only Doom core
`((setq doom-private-dir "/tmp/does/not/exist"
doom-init-modules-p t)
`((setq doom-init-modules-p t)
(load-file ,user-init-file)
(doom-run-all-startup-hooks-h)))
(`vanilla ; nothing loaded
@ -378,42 +384,6 @@ will be automatically appended to the result."
(profiler-stop))
(setq doom--profiler (not doom--profiler)))
;;;###autoload
(defun doom/profile-emacs ()
"Profile the startup time of Emacs in the background with ESUP.
If INIT-FILE is non-nil, profile that instead of USER-INIT-FILE."
(interactive)
(require 'esup)
(let ((init-file esup-user-init-file))
(message "Starting esup...")
(esup-reset)
(setq esup-server-process (esup-server-create (esup-select-port)))
(setq esup-server-port (process-contact esup-server-process :service))
(message "esup process started on port %s" esup-server-port)
(let ((process-args
(append `("*esup-child*"
"*esup-child*"
,esup-emacs-path
"-Q"
"--eval=(setq after-init-time nil)"
"-L" ,esup-load-path)
(when (bound-and-true-p early-init-file)
`("-l" ,early-init-file))
`("-l" "esup-child"
,(format "--eval=(let ((load-file-name \"%s\")) (esup-child-run \"%s\" \"%s\" %d))"
init-file
init-file
esup-server-port
esup-depth)
"--eval=(doom-run-all-startup-hooks-h)"))))
(when esup-run-as-batch-p
(setq process-args (append process-args '("--batch"))))
(setq esup-child-process (apply #'start-process process-args)))
(set-process-sentinel esup-child-process 'esup-child-process-sentinel)))
;;;###autoload
(advice-add #'esup :override #'doom/profile-emacs)
;;;###autoload
(defun doom/toggle-debug-mode (&optional arg)
"Toggle `debug-on-error' and `doom-debug-mode' for verbose logging."

View file

@ -72,7 +72,8 @@ See `doom-init-fonts-h'."
(interactive)
(when doom-font
(set-frame-font doom-font t))
(mapc #'doom-init-fonts-h (frame-list)))
(doom-init-fonts-h)
(mapc #'doom-init-extra-fonts-h (frame-list)))
;;;###autoload
(defun doom/increase-font-size (count)

View file

@ -91,6 +91,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.")
;;
;;; Library
;;;###autoload
(defun doom--format (output)
(if (string-empty-p (string-trim output))
""
@ -99,6 +100,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.")
"\n" (concat "\n" (make-string doom-format-indent 32))
output t t))))
;;;###autoload
(defun doom--format-print (output)
(unless (string-empty-p output)
(if (not noninteractive)
@ -107,6 +109,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.")
(terpri)) ; newline
t))
;;;###autoload
(defun doom--format-indent (width text &optional prefix)
"Indent TEXT by WIDTH spaces. If ARGS, format TEXT with them."
(with-temp-buffer
@ -121,6 +124,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.")
(insert prefix)))
(buffer-string)))
;;;###autoload
(defun doom--format-autofill (&rest msgs)
"Ensure MSG is split into lines no longer than `fill-column'."
(with-temp-buffer
@ -131,6 +135,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.")
(fill-region (point-min) (point-max))
(buffer-string))))
;;;###autoload
(defun doom--format-color (style format &rest args)
"Apply STYLE to formatted MESSAGE with ARGS.
@ -159,6 +164,7 @@ Otherwise, it maps colors to a term-color-* face."
((cddr (assq style doom-format-ansi-alist)))))))
(_ message))))
;;;###autoload
(defun doom--format-class (class format &rest args)
"Apply CLASS to formatted format with ARGS.
@ -172,6 +178,7 @@ transformative logic."
(args (apply #'format format args))
(format))))
;;;###autoload
(defun doom--format-apply (forms &optional sub)
"Replace color-name functions with calls to `doom--format-color'."
(cond ((null forms) nil)

View file

@ -147,7 +147,7 @@ selection of all minor-modes, active or not."
(list (or (+org-get-property "TITLE")
(file-relative-name buffer-file-name))))
path
(list (replace-regexp-in-string org-any-link-re "\\4" text)))
(list (replace-regexp-in-string org-link-any-re "\\4" text)))
" > ")
tags)
" ")
@ -366,15 +366,6 @@ current file is in, or d) the module associated with the current major mode (see
(recenter)
(message "Couldn't find the config block"))))))))
(defvar doom--help-packages-list nil)
(defun doom--help-packages-list (&optional refresh)
(or (unless refresh
doom--help-packages-list)
(setq doom--help-packages-list
(append (cl-loop for package in doom-core-packages
collect (list package :modules '((:core internal))))
(doom-package-list 'all)))))
(defun doom--help-package-configs (package)
;; TODO Add git checks, in case ~/.emacs.d isn't a git repo
(let ((default-directory doom-emacs-dir))
@ -394,16 +385,16 @@ defined and configured.
If prefix arg is present, refresh the cache."
(interactive
(let* ((guess (or (function-called-at-point)
(let ((guess (or (function-called-at-point)
(symbol-at-point))))
(require 'finder-inf nil t)
(require 'package)
(unless package--initialized
(package-initialize t))
(let ((packages (cl-delete-duplicates
(append (mapcar 'car package-alist)
(mapcar 'car package--builtins)
(mapcar 'car (doom--help-packages-list))
(require 'core-packages)
(doom-initialize-packages)
(let ((packages (delete-dups
(append (mapcar #'car package-alist)
(mapcar #'car package--builtins)
(mapcar #'intern (hash-table-keys straight--build-cache))
(mapcar #'car (doom-package-list 'all))
nil))))
(unless (memq guess packages)
(setq guess nil))
@ -415,6 +406,8 @@ If prefix arg is present, refresh the cache."
"Describe package: ")
packages nil t nil nil
(if guess (symbol-name guess))))))))
(require 'core-packages)
(doom-initialize-packages)
(if (or (package-desc-p package)
(and (symbolp package)
(or (assq package package-alist)
@ -425,8 +418,7 @@ If prefix arg is present, refresh the cache."
(with-help-window (help-buffer)))
(save-excursion
(with-current-buffer (help-buffer)
(let ((doom-packages (doom--help-packages-list))
(inhibit-read-only t)
(let ((inhibit-read-only t)
(indent (make-string 13 ? )))
(goto-char (point-max))
(if (re-search-forward "^ *Status: " nil t)
@ -441,7 +433,10 @@ If prefix arg is present, refresh the cache."
(package--print-help-section "Source")
(insert (or (pcase (doom-package-backend package)
(`straight
(format! "Straight\n%s"
(format! "Straight (%s)\n%s"
(let ((default-directory (straight--build-dir (symbol-name package))))
(string-trim
(shell-command-to-string "git log -1 --format=\"%D %h %ci\"")))
(indent
13 (string-trim
(pp-to-string
@ -453,7 +448,7 @@ If prefix arg is present, refresh the cache."
"unknown")
"\n")
(when (assq package doom-packages)
(when (gethash (symbol-name package) straight--build-cache)
(package--print-help-section "Modules")
(insert "Declared by the following Doom modules:\n")
(dolist (m (doom-package-get package :modules))
@ -548,7 +543,7 @@ If prefix arg is present, refresh the cache."
;;;###autoload
(defun doom/help-package-config (package)
"Jump to any `def-package!', `after!' or ;;;###package block for PACKAGE.
"Jump to any `use-package!', `after!' or ;;;###package block for PACKAGE.
This only searches `doom-emacs-dir' (typically ~/.emacs.d) and does not include
config blocks in your private config."

View file

@ -131,10 +131,10 @@ was installed with."
;;
;;; Package list getters
(defun doom--read-module-packages-file (file &optional eval noerror)
(defun doom--read-module-packages-file (file &optional noeval noerror)
(with-temp-buffer ; prevent buffer-local settings from propagating
(condition-case e
(if (not eval)
(if (not noeval)
(load file noerror t t)
(when (file-readable-p file)
(insert-file-contents file)
@ -165,7 +165,7 @@ This excludes core packages listed in `doom-core-packages'.
If ALL-P, gather packages unconditionally across all modules, including disabled
ones."
(let ((noninteractive t)
(let ((doom-interactive-mode t)
(doom-modules (doom-modules))
doom-packages
doom-disabled-packages)

View file

@ -92,8 +92,8 @@
(let ((session-file (doom-session-file)))
(list (or (read-file-name "Session to restore: "
(file-name-directory session-file)
nil t
(file-name-nondirectory session-file))
(file-name-nondirectory session-file)
t)
(user-error "No session selected. Aborting")))))
(unless file
(error "No session file selected"))
@ -107,7 +107,6 @@
(let ((session-file (doom-session-file)))
(list (or (read-file-name "Save session to: "
(file-name-directory session-file)
nil nil
(file-name-nondirectory session-file))
(user-error "No session selected. Aborting")))))
(unless file

View file

@ -4,7 +4,7 @@
(cond ((listp (car spec))
(cl-loop for face in (car spec)
collect
(doom--custom-theme-set-face `(,face ,(cdr spec)))))
(car (doom--custom-theme-set-face (cons face (cdr spec))))))
((keywordp (cadr spec))
`((,(car spec) ((t ,(cdr spec))))))
(`((,(car spec) ,(cdr spec))))))
@ -44,7 +44,5 @@ face format."
(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)))
(load-theme doom-theme 'noconfirm)
(doom/reload-font)))

View file

@ -169,35 +169,67 @@ OPACITY is an integer between 0 to 100, inclusive."
100))))
(set-frame-parameter nil 'alpha opacity))
(defvar-local doom--buffer-narrowed-origin nil)
(defvar-local doom--buffer-narrowed-window-start nil)
(defvar doom--narrowed-base-buffer nil)
;;;###autoload
(defun doom/clone-and-narrow-buffer (beg end &optional clone-p)
"Restrict editing in this buffer to the current region, indirectly. With CLONE-P,
clone the buffer and hard-narrow the selection. If mark isn't active, then widen
the buffer (if narrowed).
(defun doom/narrow-buffer-indirectly (beg end)
"Restrict editing in this buffer to the current region, indirectly.
This recursively creates indirect clones of the current buffer so that the
narrowing doesn't affect other windows displaying the same buffer. Call
`doom/widen-indirectly-narrowed-buffer' to undo it (incrementally).
Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/"
(interactive
(list (or (bound-and-true-p evil-visual-beginning) (region-beginning))
(or (bound-and-true-p evil-visual-end) (region-end))
current-prefix-arg))
(cond ((or (region-active-p)
(not (buffer-narrowed-p)))
(unless (region-active-p)
(setq beg (line-beginning-position)
end (line-end-position)))
(setq deactivate-mark t)
(when clone-p
(let ((old-buf (current-buffer)))
(switch-to-buffer (clone-indirect-buffer nil nil))
(setq doom--buffer-narrowed-origin old-buf)))
(setq doom--buffer-narrowed-window-start (window-start))
(narrow-to-region beg end))
(doom--buffer-narrowed-origin
(kill-current-buffer)
(switch-to-buffer doom--buffer-narrowed-origin)
(setq doom--buffer-narrowed-origin nil))
(t
(deactivate-mark)
(let ((orig-buffer (current-buffer)))
(with-current-buffer (switch-to-buffer (clone-indirect-buffer nil nil))
(narrow-to-region beg end)
(setq-local doom--narrowed-base-buffer orig-buffer))))
;;;###autoload
(defun doom/widen-indirectly-narrowed-buffer (&optional arg)
"Widens narrowed buffers.
This command will incrementally kill indirect buffers (under the assumption they
were created by `doom/narrow-buffer-indirectly') and switch to their base
buffer.
If ARG, then kill all indirect buffers, return the base buffer and widen it.
If the current buffer is not an indirect buffer, it is `widen'ed."
(interactive "P")
(unless (buffer-narrowed-p)
(user-error "Buffer isn't narrowed"))
(let ((orig-buffer (current-buffer))
(base-buffer doom--narrowed-base-buffer))
(cond ((or (not base-buffer)
(not (buffer-live-p base-buffer)))
(widen))
(arg
(let ((buffer orig-buffer)
(buffers-to-kill (list orig-buffer)))
(while (setq buffer (buffer-local-value 'doom--narrowed-base-buffer buffer))
(push buffer buffers-to-kill))
(switch-to-buffer (buffer-base-buffer))
(mapc #'kill-buffer (remove (current-buffer) buffers-to-kill))))
((switch-to-buffer base-buffer)
(kill-buffer orig-buffer)))))
;;;###autoload
(defun doom/toggle-narrow-buffer (beg end)
"Narrow the buffer to BEG END. If narrowed, widen it."
(interactive
(list (or (bound-and-true-p evil-visual-beginning) (region-beginning))
(or (bound-and-true-p evil-visual-end) (region-end))))
(if (buffer-narrowed-p)
(widen)
(set-window-start nil doom--buffer-narrowed-window-start))))
(unless (region-active-p)
(setq beg (line-beginning-position)
end (line-end-position)))
(narrow-to-region beg end)))

View file

@ -27,6 +27,8 @@ byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file'
It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
`package-activated-list' and `auto-mode-alist'."
;; REVIEW Can we avoid calling `straight-check-all' everywhere?
(straight-check-all)
(doom-reload-autoloads nil 'force))
@ -357,7 +359,7 @@ This should be run whenever your `doom!' block or update your packages."
(print-group!
(if (and (not force-p)
(file-exists-p doom-package-autoload-file)
(not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file))
(not (file-newer-than-file-p package-user-dir doom-package-autoload-file))
(not (cl-loop for dir in (straight--directory-files (straight--build-dir))
if (cl-find-if
(lambda (dir)
@ -377,7 +379,7 @@ This should be run whenever your `doom!' block or update your packages."
(noninteractive t)
(backup-inhibited t)
(version-control 'never)
(case-fold-search nil) ; reduce magit
(case-fold-search nil) ; reduce magic
(autoload-timestamps nil))
(print! (start "Regenerating package autoloads file"))

View file

@ -92,10 +92,9 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
;; But first we must be sure that Doom and your private config have been
;; fully loaded. Which usually aren't so in an noninteractive session.
(let (noninteractive)
(let ((doom-interactive-mode 'byte-compile))
(doom-initialize 'force)
(doom-initialize-core)
(doom-initialize-modules 'force))
(doom-initialize-core))
;;
(unless target-dirs

View file

@ -22,22 +22,23 @@ This file is automatically regenerated when you run this command or 'doom
refresh'. However, 'doom refresh' will only regenerate this file if it exists.
Use the -c or --clear switch to delete your envvar file."
(let ((default-directory doom-emacs-dir))
(when (member "clear" args) ; DEPRECATED
(message "'doom env clear' is deprecated. Use 'doom env -c' or 'doom env --clear' instead")
(push "-c" args))
(let ((env-file (or (cadr (member "-o" args))
doom-env-file)))
(cond ((or (member "-c" args)
(member "--clear" args))
(unless (file-exists-p doom-env-file)
(unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared"
(relpath doom-env-file)))
(delete-file doom-env-file)
(path env-file)))
(delete-file env-file)
(print! (success "Successfully deleted %S")
(relpath doom-env-file)))
(path env-file)))
((null args)
(doom-reload-env-file 'force))
((or (null args)
(member "-o" args))
(doom-reload-env-file 'force env-file))
((user-error "I don't understand 'doom env %s'"
(string-join args " "))))))
@ -58,6 +59,7 @@ Use the -c or --clear switch to delete your envvar file."
"^INSECURE$"
"^DEBUG$"
"^YES$"
"^TERM$"
"^__")
"Environment variables to not save in `doom-env-file'.
@ -79,21 +81,23 @@ It is rare that you'll need to change this.")
This is a list of strings. Each entry is run separately and in sequence with
`doom-env-executable' to scrape envvars from your shell environment.")
;; Borrows heavily from Spacemacs' `spacemacs//init-spacemacs-env'.
(defun doom-reload-env-file (&optional force-p)
(defun doom-reload-env-file (&optional force-p env-file)
"Generates `doom-env-file', if it doesn't exist (or if FORCE-P).
This scrapes the variables from your shell environment by running
`doom-env-executable' through `shell-file-name' with `doom-env-switches'. By
default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
`doom-env-ignored-vars' are removed."
(when (or force-p (not (file-exists-p doom-env-file)))
(with-temp-file doom-env-file
(let ((env-file (if env-file
(expand-file-name env-file)
doom-env-file)))
(when (or force-p (not (file-exists-p env-file)))
(with-temp-file env-file
(print! (start "%s envvars file at %S")
(if (file-exists-p doom-env-file)
(if (file-exists-p env-file)
"Regenerating"
"Generating")
(relpath doom-env-file doom-emacs-dir))
(path env-file))
(let ((process-environment doom--initial-process-environment))
(let ((shell-command-switch doom-env-switches)
(error-buffer (get-buffer-create "*env errors*")))
@ -106,7 +110,7 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
(print-group!
(let ((errors (with-current-buffer error-buffer (buffer-string))))
(unless (string-empty-p errors)
(print! (info "Error output:\n\n%s") (indent 4 errors))))
(print! (info "Warnings:\n\n%s") (indent 4 errors))))
;; Remove undesireable variables
(insert
(concat
@ -121,8 +125,9 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"# 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"
"# `doom refresh` is executed. Alternatively, create your own env file with\n"
"# `doom env -o ~/.doom.d/myenv`, then load it with (doom-load-envvars-file FILE)\n"
"# in your private config.el.\n"
"# ---------------------------------------------------------------------------\n\n"))
(goto-char (point-min))
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
@ -138,5 +143,5 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
(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)))))
(path env-file))
t))))))

View file

@ -8,15 +8,15 @@ See 'doom help install' instead."
(apply #'doom-cli-install args))
(defcli! (install i) (&rest args)
"A wizard for installing Doom for the first time.
"Installs and sets up Doom Emacs 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),
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 (same as 'doom env'),
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.
@ -27,7 +27,7 @@ DOOMDIR environment variable. e.g.
doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom install
install understands the following switches:
The following switches are recognized:
--no-config Don't create DOOMDIR or dummy files therein
--no-install Don't auto-install packages

View file

@ -3,6 +3,7 @@
(defmacro doom--ensure-autoloads-while (&rest body)
`(progn
(straight-check-all)
(doom-reload-core-autoloads)
(when (progn ,@body)
(doom-reload-package-autoloads 'force-p))
@ -19,11 +20,16 @@ This works by fetching all installed package repos and checking the distance
between HEAD and FETCH_HEAD. This can take a while.
This excludes packages whose `package!' declaration contains a non-nil :freeze
or :ignore property."
or :ignore property.
Switches:
-t/--timeout TTL Seconds until a thread is timed out (default: 45)
--threads N How many threads to use (default: 8)"
(doom--ensure-autoloads-while
(straight-check-all)
(doom-packages-update
doom-auto-accept
(when-let (threads (cadr (member "--threads" args)))
(string-to-number threads))
(when-let (timeout (cadr (or (member "--timeout" args)
(member "-t" args))))
(string-to-number timeout)))))
@ -32,26 +38,33 @@ or :ignore property."
"Rebuilds all installed packages.
This ensures that all needed files are symlinked from their package repo and
their elisp files are byte-compiled."
their elisp files are byte-compiled.
Switches:
-f Forcibly rebuild autoloads files, even if they're up-to-date"
(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.
By default, this does not purge ELPA packages or repos. It is a good idea to run
'doom purge --all' once in a while, to stymy build-up of repos and ELPA
packages that could be taking up precious space.
Available options:
--no-elpa Don't purge ELPA packages
Switches:
--no-builds Don't purge unneeded (built) packages
--repos Purge unused repos"
-e / --elpa Don't purge ELPA packages
-r / --repos Purge unused repos
--all Purge builds, elpa packages and repos"
(doom--ensure-autoloads-while
(straight-check-all)
(doom-packages-purge (not (member "--no-elpa" args))
(doom-packages-purge (or (member "-e" args)
(member "--elpa" args)
(member "--all" args))
(not (member "--no-builds" args))
(or (member "-r" args)
(member "--repos" args))
(member "--repos" args)
(member "--all" args))
doom-auto-accept)))
;; (defcli! rollback () ; TODO rollback
@ -147,12 +160,11 @@ a list of packages that will be installed."
(condition-case e
(let (packages errors)
(load ,(concat doom-core-dir "core.el"))
(doom-initialize 'force-p)
(doom-initialize 'force)
(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)
(package local-repo nonrecursive upstream-remote upstream-repo upstream-host branch)
(condition-case e
(let ((default-directory (straight--repos-dir local-repo)))
;; HACK We normalize packages to avoid certain scenarios
@ -162,7 +174,7 @@ a list of packages that will be installed."
;; 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" "reset" "--hard" branch)
(straight--get-call "git" "clean" "-ffd")
(unless nonrecursive
(shell-command-to-string "git submodule update --init --recursive"))
@ -208,7 +220,7 @@ a list of packages that will be installed."
(cons 'error e))))))
(defun doom-packages-update (&optional auto-accept-p timeout)
(defun doom-packages-update (&optional auto-accept-p threads timeout)
"Updates packages.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
@ -217,14 +229,17 @@ a list of packages that will be updated."
(print-group!
(when timeout
(print! (info "Using %S as timeout value" timeout)))
(when threads
(print! (info "Limiting to %d thread(s)" threads)))
;; REVIEW Does this fail gracefully enough? Is it error tolerant?
;; TODO Add version-lock checks; don't want to spend all this effort on
;; packages that shouldn't be updated
(let* ((futures
;; REVIEW We can do better "thread" management here
(or (cl-loop for group
in (seq-partition (hash-table-values straight--repo-cache)
(/ (hash-table-count straight--repo-cache)
16))
(or threads 8)))
for future = (doom--packages-remove-outdated-f group)
if (processp future)
collect (cons future group)
@ -422,18 +437,21 @@ a list of packages that will be updated."
(defun doom--packages-purge-elpa (&optional auto-accept-p)
(unless (bound-and-true-p package--initialized)
(package-initialize))
(let ((packages (cl-loop for (package desc) in package-alist
for dir = (package-desc-dir desc)
if (file-in-directory-p dir package-user-dir)
collect (cons package dir))))
(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
(mapcar #'car packages) 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)))
(mapcar #'cdr packages))
(length packages))))
(defun doom-packages-purge (&optional elpa-p builds-p repos-p auto-accept-p)
"Auto-removes orphaned packages and repos.

View file

@ -1,72 +0,0 @@
;;; core/cli/patch-macos.el -*- lexical-binding: t; -*-
(defcli! patch-macos () ; DEPRECATED
"Patches Emacs.app to respect your shell environment.
WARNING: This command is deprecated. Use 'doom env' instead.
A common issue with GUI Emacs on MacOS is that it launches in an environment
independent of your shell configuration, including your PATH and any other
utilities like rbenv, rvm or virtualenv.
This patch fixes this by patching Emacs.app (in /Applications or
~/Applications). It will:
1. Move Contents/MacOS/Emacs to Contents/MacOS/RunEmacs
2. And replace Contents/MacOS/Emacs with the following wrapper script:
#!/user/bin/env bash
args=\"$@\"
pwd=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\"; pwd -P)\"
exec \"$SHELL\" -l -c \"$pwd/RunEmacs $args\"
This ensures that Emacs is always aware of your shell environment, regardless of
how it is launched.
It can be undone with the --undo or -u options.
Alternatively, you can install the exec-path-from-shell Emacs plugin, which will
scrape your shell environment remotely, at startup. However, this can be slow
depending on your shell configuration and isn't always reliable."
:hidden t
(doom-patch-macos (or (member "--undo" args)
(member "-u" args))
(doom--find-emacsapp-path)))
;;
;; Library
(defun doom--find-emacsapp-path ()
(or (getenv "EMACS_APP_PATH")
(cl-loop for dir in (list "/usr/local/opt/emacs"
"/usr/local/opt/emacs-plus"
"/Applications"
"~/Applications")
for appdir = (concat dir "/Emacs.app")
if (file-directory-p appdir)
return appdir)
(user-error "Couldn't find Emacs.app")))
(defun doom-patch-macos (undo-p appdir)
"Patches Emacs.app to respect your shell environment."
(unless IS-MAC
(user-error "You don't seem to be running MacOS"))
(unless (file-directory-p appdir)
(user-error "Couldn't find '%s'" appdir))
(let ((oldbin (expand-file-name "Contents/MacOS/Emacs" appdir))
(newbin (expand-file-name "Contents/MacOS/RunEmacs" appdir)))
(cond (undo-p
(unless (file-exists-p newbin)
(user-error "Emacs.app is not patched"))
(copy-file newbin oldbin 'ok-if-already-exists nil nil 'preserve-permissions)
(unless (file-exists-p oldbin)
(error "Failed to copy %s to %s" newbin oldbin))
(delete-file newbin)
(message "%s successfully unpatched" appdir))
((file-exists-p newbin)
(user-error "%s is already patched. Use 'doom patch-macos --undo' to unpatch it"
appdir))
((user-error "patch-macos has been disabled. Please use 'doom env refresh' instead")))))

View file

@ -1,5 +1,12 @@
;;; core/cli/test.el -*- lexical-binding: t; -*-
(defun doom--emacs-binary ()
(let ((emacs-binary-path (doom-path invocation-directory invocation-name))
(runemacs-binary-path (if IS-WINDOWS (doom-path invocation-directory "runemacs.exe"))))
(if (and runemacs-binary-path (file-exists-p runemacs-binary-path))
runemacs-binary-path
emacs-binary-path)))
(defcli! test (&rest targets)
"Run Doom unit tests."
(let (files error)
@ -14,6 +21,7 @@
(cdr (doom-module-load-path)))))))
(while targets
(let ((target (pop targets)))
;; FIXME Module targets don't work
(cond ((equal target ":core")
(appendq! files (nreverse (doom-glob doom-core-dir "test/test-*.el"))))
((file-directory-p target)
@ -21,23 +29,21 @@
(appendq! files (nreverse (doom-glob target "test/test-*.el"))))
((file-exists-p target)
(push target files)))))
(require 'restart-emacs)
(with-temp-buffer
(setenv "DOOMDIR" (concat doom-core-dir "test/"))
(setenv "DOOMLOCALDIR" (concat doom-local-dir "test/"))
(print! (start "Bootstrapping test environment, if necessary..."))
(if (zerop
(call-process
(restart-emacs--get-emacs-binary)
(doom--emacs-binary)
nil t nil "--batch"
"-l" (concat doom-core-dir "core.el")
"--eval" (prin1-to-string
`(progn (doom-initialize 'force)
`(progn
(setq doom-emacs-dir ,doom-emacs-dir
doom-local-dir ,(concat doom-local-dir "test/")
doom-private-dir ,(concat doom-core-dir "test/"))
(require 'core ,(locate-library "core"))
(doom-initialize 'force)
(doom-initialize-modules)
(require 'core-cli)
(unless (package-installed-p 'buttercup)
(package-refresh-contents)
(package-install 'buttercup))
(doom-reload-core-autoloads 'force)
(when (doom-packages-install 'auto-accept)
(doom-reload-package-autoloads 'force))))))
@ -49,18 +55,21 @@
(with-temp-buffer
(unless
(zerop
(call-process
(restart-emacs--get-emacs-binary)
(apply #'call-process
(doom--emacs-binary)
nil t nil "--batch"
"-l" (concat doom-core-dir "core.el")
"-l" (concat doom-core-dir "test/helpers.el")
"--eval" (prin1-to-string `(doom-initialize 'force))
"-l" "buttercup"
(append (list
"-L" doom-core-dir
"-l" "core"
"-l" (concat doom-core-dir "test/helpers.el"))
(when (file-in-directory-p file doom-modules-dir)
(list "-f" "doom-initialize-core"))
(list
"-l" file
"-f" "buttercup-run"))
"-f" "buttercup-run"))))
(setq error t))
(message "%s" (buffer-string)))
(print! (info "Ignoring %s" (relpath file)))))
(if error
(error "A test failed")
(user-error "A test failed")
t)))

View file

@ -10,15 +10,26 @@ following shell commands:
git pull --rebase
bin/doom clean
bin/doom refresh
bin/doom update"
(and (doom-upgrade (or (member "-f" args)
bin/doom update
Switches:
-t/--timeout TTL Seconds until a thread is timed out (default: 45)
--threads N How many threads to use (default: 8)"
(and (doom-upgrade doom-auto-accept
(or (member "-f" args)
(member "--force" args)))
(doom-packages-update doom-auto-accept)
(doom-packages-update
doom-auto-accept
(when-let (threads (cadr (member "--threads" args)))
(string-to-number threads))
(when-let (timeout (cadr (or (member "--timeout" args)
(member "-t" args))))
(string-to-number timeout)))
(doom-reload-package-autoloads 'force-p)))
;;
;;; Library
;;; library
(defvar doom-repo-url "https://github.com/hlissner/doom-emacs"
"The git repo url for Doom Emacs.")
@ -33,7 +44,7 @@ following shell commands:
(error "Failed to check working tree in %s" dir))))
(defun doom-upgrade (&optional force-p)
(defun doom-upgrade (&optional auto-accept-p force-p)
"Upgrade Doom to the latest version non-destructively."
(require 'vc-git)
(let ((default-directory doom-emacs-dir)
@ -82,12 +93,15 @@ following shell commands:
(substring new-rev 0 10)
(cdr (doom-sh "git" "log" "-1" "--format=%cr" target-remote))))
(when (y-or-n-p "View the comparison diff in your browser?")
(when (and (not auto-accept-p)
(y-or-n-p "View the comparison diff in your browser?"))
(print! (info "Opened github in your browser."))
(browse-url (format "https://github.com/hlissner/doom-emacs/compare/%s...%s"
this-rev
new-rev)))
(if (not (y-or-n-p "Proceed with upgrade?"))
(if (not (or auto-accept-p
(y-or-n-p "Proceed with upgrade?")))
(ignore (print! (error "Aborted")))
(print! (start "Upgrading Doom Emacs..."))
(print-group!
@ -97,6 +111,7 @@ following shell commands:
(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-delete-autoloads-file doom-package-autoload-file)
(doom-cli-refresh "-f")
t)

View file

@ -186,7 +186,7 @@ stale."
(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)
(and (doom-packages-purge nil 'builds-p nil doom-auto-accept)
(setq success t)))
(doom-reload-package-autoloads (or success force-p))
(doom-byte-compile nil 'recompile))
@ -212,8 +212,7 @@ enabled modules.")
(load! "cli/env")
(load! "cli/upgrade")
(load! "cli/packages")
(load! "cli/autoloads")
(load! "cli/patch-macos"))
(load! "cli/autoloads"))
(defcligroup! "Byte compilation"
"For byte-compiling Doom and your config"

View file

@ -111,8 +111,8 @@ successfully sets indent_style/indent_size.")
;;
;; This is because autorevert abuses the heck out of inotify handles which can
;; grind Emacs to a halt if you do expensive IO (outside of Emacs) on the
;; files you have open (like compression). We only really need revert changes
;; when we switch to a buffer or when we focus the Emacs frame.
;; files you have open (like compression). We only really need to revert
;; changes when we switch to a buffer or when we focus the Emacs frame.
(defun doom-auto-revert-buffer-h ()
"Auto revert current buffer, if necessary."
(unless auto-revert-mode
@ -168,7 +168,7 @@ successfully sets indent_style/indent_size.")
"Add dired directory to recentf file list."
(recentf-add-file default-directory)))
(unless noninteractive
(when doom-interactive-mode
(add-hook 'kill-emacs-hook #'recentf-cleanup)
(quiet! (recentf-mode +1))))
@ -198,12 +198,22 @@ successfully sets indent_style/indent_size.")
:after-call after-find-file dired-initial-position-hook
:config
(setq save-place-file (concat doom-cache-dir "saveplace")
save-place-forget-unreadable-files t
save-place-limit 200)
save-place-limit 100)
(defadvice! doom--recenter-on-load-saveplace-a (&rest _)
"Recenter on cursor when loading a saved place."
:after-while #'save-place-find-file-hook
(if buffer-file-name (ignore-errors (recenter))))
(defadvice! doom--dont-prettify-saveplace-cache-a (orig-fn)
"`save-place-alist-to-file' uses `pp' to prettify the contents of its cache.
`pp' can be expensive for longer lists, and there's no reason to prettify cache
files, so we replace calls to `pp' with the much faster `prin1'."
:around #'save-place-alist-to-file
(cl-letf (((symbol-function #'pp)
(symbol-function #'prin1)))
(funcall orig-fn)))
(save-place-mode +1))
@ -223,6 +233,10 @@ successfully sets indent_style/indent_size.")
(use-package! better-jumper
:after-call pre-command-hook
:preface
;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at
;; startup. This can be removed once gilbertw1/better-jumper#2 is merged.
(defvar better-jumper-local-mode nil)
:init
(global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward)
(global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward)
@ -259,18 +273,9 @@ successfully sets indent_style/indent_size.")
nil))
(use-package! command-log-mode
:commands global-command-log-mode
:config
(setq command-log-mode-auto-show t
command-log-mode-open-log-turns-on-mode nil
command-log-mode-is-global t
command-log-mode-window-size 50))
(use-package! dtrt-indent
;; Automatic detection of indent settings
:unless noninteractive
:when doom-interactive-mode
:defer t
:init
(add-hook! '(change-major-mode-after-body-hook read-only-mode-hook)
@ -389,8 +394,17 @@ successfully sets indent_style/indent_size.")
(sp-local-pair 'minibuffer-inactive-mode "`" nil :actions nil)
;; Smartparens breaks evil-mode's replace state
(add-hook 'evil-replace-state-entry-hook #'turn-off-smartparens-mode)
(add-hook 'evil-replace-state-exit-hook #'turn-on-smartparens-mode)
(defvar doom-buffer-smartparens-mode nil)
(add-hook! 'evil-replace-state-exit-hook
(defun doom-enable-smartparens-mode-maybe-h ()
(when doom-buffer-smartparens-mode
(turn-on-smartparens-mode)
(kill-local-variable 'doom-buffer-smartparens-mode))))
(add-hook! 'evil-replace-state-entry-hook
(defun doom-disable-smartparens-mode-maybe-h ()
(when smartparens-mode
(setq-local doom-buffer-smartparens-mode t)
(turn-off-smartparens-mode))))
(smartparens-global-mode +1))
@ -412,11 +426,20 @@ successfully sets indent_style/indent_size.")
undo-tree-history-directory-alist
`(("." . ,(concat doom-cache-dir "undo-tree-hist/"))))
;; Compress undo-tree history files with zstd, if available. File size isn't
;; the (only) concern here: the file IO barrier is slow for Emacs to cross;
;; reading a tiny file and piping it in-memory through zstd is *slightly*
;; faster than Emacs reading the entire undo-tree file from the get go (on
;; SSDs). Whether or not that's true in practice, we still enjoy zstd's ~80%
;; file savings (these files add up over time and zstd is so incredibly fast).
(when (executable-find "zstd")
(defadvice! doom--undo-tree-make-history-save-file-name-a (file)
:filter-return #'undo-tree-make-history-save-file-name
(concat file ".zst")))
;; Strip text properties from undo-tree data to stave off bloat. File size
;; isn't the concern here; undo cache files bloat easily, which can cause
;; freezing, crashes, GC-induced stuttering or delays when opening files.
(defadvice! doom--undo-tree-strip-text-properties-a (&rest _)
:before #'undo-list-transfer-to-tree
(dolist (item buffer-undo-list)

View file

@ -23,6 +23,14 @@ and Emacs states, and for non-evil users.")
"An overriding keymap for <leader> keys.")
;;
;;; Keybind settings
(when IS-MAC
(setq mac-command-modifier 'super
mac-option-modifier 'meta))
;;
;;; Universal, non-nuclear escape
@ -411,18 +419,7 @@ Properties
:unless [CONDITION] [...]
Any of the above properties may be nested, so that they only apply to a
certain group of keybinds.
Example
(map! :map magit-mode-map
:m \"C-r\" 'do-something ; C-r in motion state
:nv \"q\" 'magit-mode-quit-window ; q in normal+visual states
\"C-x C-r\" 'a-global-keybind
:g \"C-x C-r\" 'another-global-keybind ; same as above
(:when IS-MAC
:n \"M-s\" 'some-fn
:i \"M-o\" (lambda (interactive) (message \"Hi\"))))"
certain group of keybinds."
(doom--map-process rest))
(provide 'core-keybinds)

View file

@ -1,8 +1,7 @@
;;; core-lib.el -*- lexical-binding: t; -*-
(let ((load-path doom--initial-load-path))
(require 'cl-lib)
(require 'subr-x)
(require 'cl-lib))
;; Polyfills
(unless EMACS26+
@ -175,7 +174,7 @@ at the values with which this function was called."
"Push VALUES sequentially into PLACE, if they aren't already present.
This is a variadic `cl-pushnew'."
(let ((var (make-symbol "result")))
`(dolist (,var (list ,@values))
`(dolist (,var (list ,@values) (with-no-warnings ,place))
(cl-pushnew ,var ,place :test #'equal))))
(defmacro prependq! (sym &rest lists)
@ -243,7 +242,7 @@ This macro accepts, in order:
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)"
\(fn HOOKS [:append :local] FUNCTIONS)"
(declare (indent (lambda (indent-point state)
(goto-char indent-point)
(when (looking-at-p "\\s-*(")
@ -295,17 +294,13 @@ 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)"
\(fn HOOKS [:append :local] 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
@ -338,7 +333,10 @@ If NOERROR is non-nil, don't throw an error if the file doesn't exist."
(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)))
(let ((file (if path
`(let (file-name-handler-alist)
(expand-file-name ,filename ,path))
filename)))
`(condition-case e
(load ,file ,noerror ,(not doom-debug-mode))
((debug doom-error) (signal (car e) (cdr e)))
@ -372,36 +370,37 @@ serve as a predicated alternative to `after!'."
(put ',fn 'permanent-local-hook t)
(add-hook 'after-load-functions #',fn)))))
(defmacro defer-feature! (feature &optional mode)
"Pretend FEATURE hasn't been loaded yet, until FEATURE-hook is triggered.
(defmacro defer-feature! (feature &optional fn)
"Pretend FEATURE hasn't been loaded yet, until FEATURE-hook or FN runs.
Some packages (like `elisp-mode' and `lisp-mode') are loaded immediately at
startup, which will prematurely trigger `after!' (and `with-eval-after-load')
blocks. To get around this we make Emacs believe FEATURE hasn't been loaded yet,
then wait until FEATURE-hook (or MODE-hook, if MODE is provided) is triggered to
then wait until FEATURE-hook (or MODE-hook, if FN is provided) is triggered to
reverse this and trigger `after!' blocks at a more reasonable time."
(let ((advice-fn (intern (format "doom--defer-feature-%s-a" feature)))
(mode (or mode feature)))
(fn (or fn feature)))
`(progn
(setq features (delq ',feature features))
(advice-add #',mode :before #',advice-fn)
(advice-add #',fn :before #',advice-fn)
(defun ,advice-fn (&rest _)
;; Some plugins (like yasnippet) will invoke a mode early to parse
;; Some plugins (like yasnippet) will invoke a fn early to parse
;; code, which would prematurely trigger this. In those cases, well
;; behaved plugins will use `delay-mode-hooks', which we can check for:
(when (and ,(intern (format "%s-hook" mode))
(when (and ,(intern (format "%s-hook" fn))
(not delay-mode-hooks))
;; ...Otherwise, announce to the world this package has been loaded,
;; so `after!' handlers can react.
(provide ',feature)
(advice-remove #',mode #',advice-fn))))))
(advice-remove #',fn #',advice-fn))))))
(defmacro quiet! (&rest forms)
"Run FORMS without generating any output.
This silences calls to `message', `load-file', `write-region' and anything that
writes to `standard-output'."
`(cond (noninteractive
`(cond (doom-debug-mode ,@forms)
((not doom-interactive-mode)
(let ((old-fn (symbol-function 'write-region)))
(cl-letf ((standard-output (lambda (&rest _)))
((symbol-function 'load-file) (lambda (file) (load file nil t)))
@ -411,8 +410,6 @@ writes to `standard-output'."
(unless visit (setq visit 'no-message))
(funcall old-fn start end filename append visit lockname mustbenew))))
,@forms)))
((or doom-debug-mode debug-on-error debug-on-quit)
,@forms)
((let ((inhibit-message t)
(save-silently t))
(prog1 ,@forms (message ""))))))

View file

@ -48,7 +48,7 @@ syntax-checker modules obsolete. e.g. If :feature version-control is found in
your `doom!' block, a warning is emitted before replacing it with :emacs vc and
:ui vc-gutter.")
(defvar doom-inhibit-module-warnings (not noninteractive)
(defvar doom-inhibit-module-warnings doom-interactive-mode
"If non-nil, don't emit deprecated or missing module warnings at startup.")
;;; Custom hooks
@ -84,7 +84,7 @@ non-nil."
(load! "init" (plist-get plist :path) t)))
doom-modules))
(run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
(unless noninteractive
(when doom-interactive-mode
(when doom-modules
(maphash (lambda (key plist)
(let ((doom--current-module key)
@ -224,7 +224,7 @@ those directories. The first returned path is always `doom-private-dir'."
"Minimally initialize `doom-modules' (a hash table) and return it.
This value is cached. If REFRESH-P, then don't use the cached value."
(or (unless refresh-p doom-modules)
(let ((noninteractive t)
(let (doom-interactive-mode
doom-modules
doom-init-modules-p)
(load! "init" doom-private-dir t)
@ -243,30 +243,16 @@ This value is cached. If REFRESH-P, then don't use the cached value."
(setq use-package-compute-statistics doom-debug-mode
use-package-verbose doom-debug-mode
use-package-minimum-reported-time (if doom-debug-mode 0 0.1)
use-package-expand-minimally (not noninteractive)))
use-package-expand-minimally doom-interactive-mode))
;; Adds four new keywords to `use-package' (and consequently, `use-package!') to
;; expand its lazy-loading capabilities. They are:
;;
;; Check out `use-package!'s documentation for more about these two.
;; :after-call SYMBOL|LIST
;; :defer-incrementally SYMBOL|LIST|t
;;
;; Provided by `auto-minor-mode' package:
;; :minor
;; :magic-minor
;;
(defvar doom--deferred-packages-alist '(t))
(with-eval-after-load 'use-package-core
;; Macros are already fontified, no need for this
(font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords)
;; Register all new keywords
(dolist (keyword '(:defer-incrementally :after-call))
(push keyword use-package-deferring-keywords)
(setq use-package-keywords
(use-package-list-insert keyword use-package-keywords :after)))
;; We define :minor and :magic-minor from the `auto-minor-mode' package here
;; so we don't have to load `auto-minor-mode' so early.
(dolist (keyword '(:minor :magic-minor))
(setq use-package-keywords
(use-package-list-insert keyword use-package-keywords :commands)))
@ -279,6 +265,17 @@ This value is cached. If REFRESH-P, then don't use the cached value."
(defun use-package-handler/:magic-minor (name _ arg rest state)
(use-package-handle-mode name 'auto-minor-mode-magic-alist arg rest state))
;; Adds two keywords to `use-package' to expand its lazy-loading capabilities:
;;
;; :after-call SYMBOL|LIST
;; :defer-incrementally SYMBOL|LIST|t
;;
;; Check out `use-package!'s documentation for more about these two.
(dolist (keyword '(:defer-incrementally :after-call))
(push keyword use-package-deferring-keywords)
(setq use-package-keywords
(use-package-list-insert keyword use-package-keywords :after)))
(defalias 'use-package-normalize/:defer-incrementally #'use-package-normalize-symlist)
(defun use-package-handler/:defer-incrementally (name _keyword targets rest state)
(use-package-concat
@ -410,7 +407,7 @@ to least)."
(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
(unless doom-interactive-mode
(setq doom-inhibit-module-warnings t))
`(setq doom-modules ',doom-modules)))
@ -428,26 +425,17 @@ two extra properties:
:after-call SYMBOL|LIST
Takes a symbol or list of symbols representing functions or hook variables.
The first time any of these functions or hooks are executed, the package is
loaded. e.g.
(use-package! projectile
:after-call (pre-command-hook after-find-file dired-before-readin-hook)
...)
loaded.
:defer-incrementally SYMBOL|LIST|t
Takes a symbol or list of symbols representing packages that will be loaded
incrementally at startup before this one. This is helpful for large packages
like magit or org, which load a lot of dependencies on first load. This lets
you load them piece-meal during idle periods, so that when you finally do need
the package, it'll load quicker. e.g.
the package, it'll load quicker.
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.
(use-package! abc
;; This is equivalent to :defer-incrementally (abc)
:defer-incrementally t
...)"
specify it. A value of `t' implies NAME."
(declare (indent 1))
(unless (or (memq name doom-disabled-packages)
;; At compile-time, use-package will forcibly load packages to
@ -462,7 +450,8 @@ two extra properties:
(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.
This macro must be used *before* PACKAGE's `use-package!' block. Often, this
means using it from your DOOMDIR/init.el.
Under the hood, this uses use-package's `use-package-inject-hooks'.
@ -470,9 +459,13 @@ PACKAGE is a symbol; the package's name.
WHEN should be one of the following:
:pre-init :post-init :pre-config :post-config
WARNING: If :pre-init or :pre-config hooks return nil, the original
`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)."
WARNINGS:
- The use of this macro is more often than not a code smell. Use it as last
resort. There is almost always a better alternative.
- If you are using this solely for :post-config, stop! `after!' is much better.
- If :pre-init or :pre-config hooks return nil, the original `use-package!''s
:init/:config block (respectively) is overwritten, so remember to have them
return non-nil (or exploit that to overwrite Doom's config)."
(declare (indent defun))
(unless (memq when '(:pre-init :post-init :pre-config :post-config))
(error "'%s' isn't a valid hook for use-package-hook!" when))
@ -493,7 +486,7 @@ CATEGORY is a keyword, MODULE is a symbol and FLAGS are symbols.
This is for testing and internal use. This is not the correct way to enable a
module."
`(let ((doom-modules ,doom-modules)
`(let ((doom-modules (or ,doom-modules (doom-modules)))
(module-path (doom-module-locate-path ,category ',module)))
(doom-module-set
,category ',module

View file

@ -74,8 +74,8 @@ missing) and shouldn't be deleted.")
;; shouldn't be using it, but it may be convenient for quick package testing.
(setq package--init-file-ensured t
package-enable-at-startup nil
package-user-dir doom-elpa-dir
package-gnupghome-dir (expand-file-name "gpg" doom-elpa-dir)
package-user-dir (concat doom-local-dir "elpa/")
package-gnupghome-dir (expand-file-name "gpg" package-user-dir)
;; I omit Marmalade because its packages are manually submitted rather
;; than pulled, so packages are often out of date with upstream.
package-archives
@ -112,12 +112,6 @@ missing) and shouldn't be deleted.")
;; we just don't have to deal with them at all.
autoload-compute-prefixes nil)
;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch!
(defadvice! doom--straight-use-local-dir-a (orig-fn &rest args)
:around #'straight--emacs-dir
(let ((user-emacs-directory doom-local-dir))
(apply orig-fn args)))
(defun doom--finalize-straight ()
(mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist)))
(setq straight--transaction-alist nil))
@ -155,8 +149,6 @@ necessary package metadata is initialized and available for them."
: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))
@ -171,7 +163,9 @@ necessary package metadata is initialized and available for them."
(if-let (recipe (plist-get plist :recipe))
(let ((plist (straight-recipes-retrieve pkg)))
`(,pkg ,@(doom-plist-merge recipe (cdr plist))))
pkg)))))
pkg))))
(unless doom-interactive-mode
(add-hook 'kill-emacs-hook #'doom--finalize-straight))))
(defun doom-ensure-straight ()
"Ensure `straight' is installed and was compiled with this version of Emacs."
@ -214,7 +208,7 @@ Accepts the following properties:
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
for packages to be installed from external sources.
: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 `use-package!'
blocks.
:ignore FORM
Do not install this package.

View file

@ -30,7 +30,7 @@ Emacs.")
projectile-add-known-project) ; TODO PR autoload upstream
:init
(setq projectile-cache-file (concat doom-cache-dir "projectile.cache")
projectile-enable-caching (not noninteractive)
projectile-enable-caching doom-interactive-mode
projectile-known-projects-file (concat doom-cache-dir "projectile.projects")
projectile-require-project-root t
projectile-globally-ignored-files '(".DS_Store" "Icon " "TAGS")
@ -51,6 +51,11 @@ Emacs.")
(push ".project" projectile-project-root-files-bottom-up)
(push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories)
;; Disable commands that won't work, as is, and that Doom already provides a
;; better alternative for.
(put 'projectile-ag 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/ag instead")
(put 'projectile-ripgrep 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/rg instead")
;; Treat current directory in dired as a "file in a project" and track it
(add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook)
@ -67,7 +72,7 @@ b) represent blacklisted directories that are too big, change too often or are
private. (see `doom-projectile-cache-blacklist'),
c) are not valid projectile projects."
(when (and (bound-and-true-p projectile-projects-cache)
(not noninteractive))
doom-interactive-mode)
(cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist)
for proot in (hash-table-keys projectile-projects-cache)
if (or (not (stringp proot))
@ -239,6 +244,7 @@ Relevant: `doom-project-hook'."
,(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
@ -246,7 +252,8 @@ Relevant: `doom-project-hook'."
(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 (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)))))))))

View file

@ -344,7 +344,7 @@ treat Emacs as a non-application window."
(setq mode-line-default-help-echo nil
show-help-function nil)
;; y/n is easier to type than yes/no
;; Typing yes/no is obnoxious when y/n will do
(fset #'yes-or-no-p #'y-or-n-p)
;; Try really hard to keep the cursor from getting stuck in the read-only prompt
@ -372,11 +372,11 @@ treat Emacs as a non-application window."
(use-package! ediff
:defer t
:init
:config
(setq ediff-diff-options "-w" ; turn off whitespace checking
ediff-split-window-function #'split-window-horizontally
ediff-window-setup-function #'ediff-setup-windows-plain)
:config
(defvar doom--ediff-saved-wconf nil)
;; Restore window config after quitting ediff
(add-hook! 'ediff-before-setup-hook
@ -579,12 +579,7 @@ character that looks like a space that `whitespace-mode' won't affect.")
behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.")
(defun doom-init-fonts-h ()
"Loads fonts.
Fonts are specified by `doom-font', `doom-variable-pitch-font',
`doom-serif-font' and `doom-unicode-font'."
(condition-case e
(progn
"Loads `doom-font'."
(cond (doom-font
(add-to-list
'default-frame-alist
@ -593,12 +588,17 @@ Fonts are specified by `doom-font', `doom-variable-pitch-font',
((fontp doom-font) (font-xlfd-name doom-font))
((signal 'wrong-type-argument (list '(fontp stringp) doom-font)))))))
((display-graphic-p)
(setq doom-font (face-attribute 'default :font))))
(setq doom-font (face-attribute 'default :font)))))
(defun doom-init-extra-fonts-h (&optional frame)
"Loads `doom-variable-pitch-font',`doom-serif-font' and `doom-unicode-font'."
(condition-case e
(with-selected-frame (or frame (selected-frame))
(when doom-serif-font
(set-face-attribute 'fixed-pitch-serif t :font doom-serif-font))
(set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font))
(when doom-variable-pitch-font
(set-face-attribute 'variable-pitch t :font doom-variable-pitch-font))
(when doom-unicode-font
(set-face-attribute 'variable-pitch nil :font doom-variable-pitch-font))
(when (and doom-unicode-font (fboundp 'set-fontset-font))
(set-fontset-font t 'unicode doom-unicode-font nil 'prepend)))
((debug error)
(if (string-prefix-p "Font not available: " (error-message-string e))
@ -656,10 +656,15 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil."
(dolist (fn '(switch-to-buffer display-buffer))
(advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
;; Apply `doom-theme'
(add-hook 'doom-init-ui-hook #'doom-init-theme-h)
;; Apply `doom-font' et co
(add-hook 'doom-after-init-modules-hook #'doom-init-fonts-h)
(add-hook 'doom-load-theme-hook #'doom-init-extra-fonts-h)
;; Apply `doom-theme'
(add-hook (if (daemonp)
'after-make-frame-functions
'doom-init-ui-hook)
#'doom-init-theme-h)
(add-hook 'window-setup-hook #'doom-init-ui-h)
@ -669,7 +674,7 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil."
;; doesn't exist in terminal Emacs; we define it to prevent errors
(unless (fboundp 'define-fringe-bitmap)
(defun define-fringe-bitmap (&rest _)))
(fset 'define-fringe-bitmap #'ignore))
(after! whitespace
(defun doom-disable-whitespace-mode-in-childframes-a (orig-fn)

View file

@ -1,5 +1,16 @@
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
(defconst doom-version "2.0.9"
"Current version of Doom Emacs.")
(defconst EMACS26+ (> emacs-major-version 25))
(defconst EMACS27+ (> emacs-major-version 26))
(defconst IS-MAC (eq system-type 'darwin))
(defconst IS-LINUX (eq system-type 'gnu/linux))
(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix)))
;;
(defvar doom-init-p nil
"Non-nil if Doom has been initialized.")
@ -12,22 +23,13 @@
Use `doom/toggle-debug-mode' to toggle it. The --debug-init flag and setting the
DEBUG envvar will enable this at startup.")
(defvar doom-interactive-mode (not noninteractive)
"If non-nil, Emacs is in interactive mode.")
(defvar doom-gc-cons-threshold 16777216 ; 16mb
"The default value to use for `gc-cons-threshold'. If you experience freezing,
decrease this. If you experience stuttering, increase this.")
;;; Constants
(defconst doom-version "2.0.9"
"Current version of Doom Emacs.")
(defconst EMACS26+ (> emacs-major-version 25))
(defconst EMACS27+ (> emacs-major-version 26))
(defconst IS-MAC (eq system-type 'darwin))
(defconst IS-LINUX (eq system-type 'gnu/linux))
(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix)))
;;; Directories/files
(defvar doom-emacs-dir
(eval-when-compile (file-truename user-emacs-directory))
@ -59,11 +61,6 @@ dependencies or long-term shared data. Must end with a slash.")
Use this for files that change often, like cache files. Must end with a slash.")
(defvar doom-elpa-dir (concat doom-local-dir "elpa/")
"Where package.el and quelpa plugins (and their caches) are stored.
Must end with a slash.")
(defvar doom-docs-dir (concat doom-emacs-dir "docs/")
"Where Doom's documentation files are stored. Must end with a slash.")
@ -146,9 +143,6 @@ users).")
;; to, it's our (the user's) failure. One case for all!
(setq auto-mode-case-fold nil)
;; Enable all disabled commands.
(setq disabled-command-function nil)
;; Display the bare minimum at startup. We don't need all that noise. The
;; dashboard/empty scratch buffer is good enough.
(setq inhibit-startup-message t
@ -202,6 +196,13 @@ users).")
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/"))
;; HACK
(with-eval-after-load 'x-win
(defun emacs-session-filename (session-id)
"Construct a filename to save a session based on SESSION-ID.
Doom Emacs overrides this function to stop sessions from littering the user
directory. The session files are placed by default in `doom-cache-dir'"
(concat doom-cache-dir "emacs-session." session-id)))
;;
@ -222,8 +223,8 @@ users).")
(setq fast-but-imprecise-scrolling t)
;; Resizing the Emacs frame can be a terribly expensive part of changing the
;; font. By inhibiting this, we easily halve startup times with fonts that are
;; larger than the system default.
;; font. By inhibiting this, we halve startup times, particularly when we use
;; fonts that are larger than the system default (which would resize the frame).
(setq frame-inhibit-implied-resize t)
;; Don't ping things that look like domain names.
@ -320,7 +321,8 @@ This is already done by the lang/org module, however.
If you want to disable incremental loading altogether, either remove
`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. Incremental loading does not occur
in daemon sessions (they are loaded immediately at startup).")
(defvar doom-incremental-first-idle-timer 2
"How long (in idle seconds) until incremental loading starts.
@ -401,7 +403,8 @@ If RETURN-P, return the message as a string instead of displaying it."
(- (length load-path) (length doom--initial-load-path))
(if doom-modules (hash-table-count doom-modules) 0)
(or doom-init-time
(setq doom-init-time (float-time (time-subtract (current-time) before-init-time))))))
(setq doom-init-time
(float-time (time-subtract (current-time) before-init-time))))))
(defun doom-load-autoloads-file (file)
"Tries to load FILE (an autoloads file). Return t on success, throws an error
@ -410,7 +413,7 @@ in interactive sessions, nil otherwise (but logs a warning)."
(let (command-switch-alist)
(load (substring file 0 -3) 'noerror 'nomessage))
((debug error)
(if noninteractive
(if doom-interactive-mode
(message "Autoload file warning: %s -> %s" (car e) (error-message-string e))
(signal 'doom-autoload-error (list (file-name-nondirectory file) e))))))
@ -419,25 +422,27 @@ in interactive sessions, nil otherwise (but logs a warning)."
(if (not (file-readable-p file))
(unless noerror
(signal 'file-error (list "Couldn't read envvar file" file)))
(let (vars)
(with-temp-buffer
(insert-file-contents file)
(search-forward "\n\n" nil t)
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
(while (re-search-forward "\n *\\([^#][^= \n]+\\)=" nil t)
(save-excursion
(let ((var (match-string 1))
(let ((var (string-trim-left (match-string 1)))
(value (buffer-substring-no-properties
(point)
(1- (or (when (re-search-forward "^\\([^= ]+\\)=" nil t)
(line-beginning-position))
(point-max))))))
(push (cons var value) vars)
(setenv var value)))))
(when vars
(setq-default
exec-path (append (split-string (getenv "PATH")
(if IS-WINDOWS ";" ":"))
(list exec-directory))
shell-file-name (or (getenv "SHELL")
shell-file-name))
t))
(nreverse vars)))))
(defun doom-initialize (&optional force-p)
"Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).
@ -495,7 +500,7 @@ to least)."
;; `Info-directory-list', and `doom-disabled-packages'. A big
;; reduction in startup time.
(pkg-autoloads-p
(unless noninteractive
(when doom-interactive-mode
(doom-load-autoloads-file doom-package-autoload-file))))
(if (and core-autoloads-p (not force-p))
@ -507,17 +512,16 @@ to least)."
(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).
;; Eagerly load these libraries because we may be in a session that
;; hasn't been fully initialized (e.g. 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))
doom-cache-dir))
(unless (file-directory-p dir)
(make-directory dir 'parents)))
@ -528,12 +532,13 @@ to least)."
(unless (or (and core-autoloads-p pkg-autoloads-p)
force-p
noninteractive)
(not doom-interactive-mode))
(unless core-autoloads-p
(message "Your Doom core autoloads file is missing"))
(warn "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")))))
(warn "Your package autoloads file is missing"))
(signal 'doom-autoload-error "Run `bin/doom refresh' to generate them")))
t))
(defun doom-initialize-core ()
"Load Doom's core files for an interactive session."

View file

@ -9,6 +9,8 @@
(package! all-the-icons)
(package! hide-mode-line)
(package! highlight-numbers)
;; Some early 26.x builds of Emacs do not have `display-line-numbers' yet, so
;; check for it instead of Emacs' version.
(unless (locate-library "display-line-numbers")
(package! nlinum)
(package! nlinum-hl)
@ -18,7 +20,6 @@
;; core-editor.el
(package! better-jumper)
(package! command-log-mode)
(package! dtrt-indent)
(package! helpful)
(package! ns-auto-titlebar :ignore (not IS-MAC))
@ -26,11 +27,14 @@
(package! smartparens)
(package! so-long
:built-in 'prefer
:recipe (:repo "https://git.savannah.gnu.org/git/so-long.git"))
;; REVIEW so-long is slated to be published to ELPA eventually, but until then
;; I've created my own mirror for it because git.savannah.gnu.org runs on a
;; potato.
:recipe (:host github :repo "hlissner/emacs-so-long"))
(package! osx-clipboard :ignore (not IS-MAC))
(package! undo-tree)
(package! ws-butler)
(package! xclip :ignore IS-LINUX)
(package! xclip :ignore (not IS-LINUX))
;; core-projects.el
(package! projectile)
@ -38,6 +42,3 @@
;; core-keybinds.el
(package! general)
(package! which-key)
;; autoload/debug.el
(package! esup)

View file

@ -1,6 +1,64 @@
;; -*- no-byte-compile: t; -*-
;; -*- lexical-binding: t; no-byte-compile: t; -*-
;;; core/test/helpers.el
(eval-and-compile
(setq doom-interactive-mode 'test)
(doom-initialize 'force)
(require 'buttercup)
(setq split-width-threshold 0
split-height-threshold 0
window-min-width 0
window-min-height 0))
;;
;;; Buttercup extensions
(buttercup-define-matcher :to-expand-into (form expected)
(cl-destructuring-bind (form expected)
(mapcar #'funcall (list form expected))
(let ((expanded (macroexpand-1 form)))
(if (equal expanded expected)
(cons t (format "Expected `%S' to not expand to `%S'"
form expected))
(cons nil (format "Expected `%S' to not expand to `%S', but got `%S' instead"
form expected expanded))))))
(buttercup-define-matcher :to-output (form &optional expected-output)
(let ((expected-output (and (functionp expected-output)
(funcall expected-output)))
output)
(with-current-buffer (get-buffer "*Messages*")
(let ((standard-output (current-buffer))
(start (point)))
(let ((inhibit-message t))
(funcall form))
(setq output (buffer-substring-no-properties start (point-max)))
(with-silent-modifications (erase-buffer))))
(cond ((null expected-output)
(if (string-empty-p output)
(cons nil (format "Expected output %S, but got none"
expected-output))
(cons t (format "Expected no output, but got %S"
output))))
((not (equal expected-output output))
(cons nil (format "Expected output %S, but got %S instead"
expected-output output)))
((cons t (format "Expected to not get %S as output"
expected-output))))))
(buttercup-define-matcher :to-contain-items (items expected)
(cl-destructuring-bind (items expected)
(mapcar #'funcall (list items expected))
(if-let (missing (cl-set-difference expected items))
(cons nil (format "Expected list to contain %S, but it was missing %S"
expected missing))
(cons t (format "Expected list to not contain %S, but it did: %S"
expected items)))))
;;
;;; Helper macros
(defmacro insert!! (&rest text)
"Insert TEXT in buffer, then move cursor to last {0} marker."
`(progn

4
core/test/packages.el Normal file
View file

@ -0,0 +1,4 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/packages.el
(package! buttercup)

View file

@ -1,16 +1,12 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-autoload-buffers.el
(describe "core/autoload/buffers"
:var (a b c d)
(require 'core-projects)
(load! "autoload/buffers" doom-core-dir)
;;
(describe "core/autoload/buffers"
:var (a b c d)
(before-all
(spy-on 'buffer-list :and-call-fake
(lambda (&optional _)
(cl-remove-if-not #'buffer-live-p (list a b c d)))))
(before-each
(delete-other-windows)
(setq a (switch-to-buffer (get-buffer-create "a"))
@ -25,7 +21,7 @@
(describe "buffer-list"
(it "should only see four buffers"
(expect (doom-buffer-list) :to-have-same-items-as (list a b c d))))
(expect (doom-buffer-list) :to-contain-items (list a b c d))))
(describe "project-buffer-list"
:var (projectile-projects-cache-time projectile-projects-cache)
@ -34,7 +30,7 @@
(before-each
(with-current-buffer a (setq default-directory doom-emacs-dir))
(with-current-buffer b (setq default-directory doom-emacs-dir))
(with-current-buffer b (setq default-directory doom-core-dir))
(with-current-buffer c (setq default-directory "/tmp/"))
(with-current-buffer d (setq default-directory "~"))
(projectile-mode +1))
@ -44,7 +40,7 @@
(it "returns buffers in the same project"
(with-current-buffer a
(expect (doom-project-buffer-list)
:to-have-same-items-as (list a b))))
:to-contain-items (list a b))))
(it "returns all buffers if not in a project"
(with-current-buffer c
@ -53,7 +49,10 @@
(describe "fallback-buffer"
(it "returns a live buffer"
(expect (buffer-live-p (doom-fallback-buffer)))))
(expect (buffer-live-p (doom-fallback-buffer))))
(it "returns the scratch buffer"
(expect (doom-fallback-buffer) :to-equal (get-buffer "*scratch*"))))
(describe "real buffers"
(before-each
@ -72,7 +71,7 @@
(describe "real-buffer-list"
(it "returns only real buffers"
(expect (doom-real-buffer-list) :to-have-same-items-as (list a b)))))
(expect (doom-real-buffer-list) :to-contain-items (list a b)))))
(describe "buffer/window management"
(describe "buffer search methods"
@ -85,14 +84,17 @@
(it "can match buffers by regexp"
(expect (doom-matching-buffers "^[ac]$") :to-have-same-items-as (list a c)))
(it "can match buffers by major-mode"
(expect (doom-buffers-in-mode 'text-mode) :to-have-same-items-as (list b c)))
(it "can find all buried buffers"
(expect (doom-buried-buffers)
:to-have-same-items-as (list c d)))
(expect (doom-buried-buffers) :to-contain-items (list c d)))
(it "can find all visible buffers"
(expect (doom-visible-buffers)
:to-have-same-items-as (list a b)))
(it "can find all visible windows"
(expect (doom-visible-windows)
:to-have-same-items-as
@ -109,7 +111,7 @@
(expect (length (doom-visible-windows)) :to-be 1)))
;; TODO
(describe "kill-all-buffers")
(describe "kill-other-buffers")
(describe "kill-matching-buffers")
(describe "cleanup-session")))
(xdescribe "kill-all-buffers")
(xdescribe "kill-other-buffers")
(xdescribe "kill-matching-buffers")
(xdescribe "cleanup-session")))

View file

@ -1,11 +1,109 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-autoload-files.el
;;;
(describe "core/autoload/files"
(load! "autoload/files" doom-core-dir)
(xdescribe "doom-glob")
(xdescribe "doom-path")
(xdescribe "doom-dir")
(xdescribe "doom-files-in")
(describe "library"
(describe "file-exists-p!"
(it "is a (quasi) drop-in replacement for `file-exists-p'"
(let ((default-directory doom-emacs-dir)
(init-file "init.el"))
(expect (file-exists-p "init.el"))
(expect (and (file-exists-p! "init.el")
(file-exists-p "init.el")))
(expect (and (file-exists-p! init-file)
(file-exists-p init-file)))
(expect (and (file-exists-p! doom-emacs-dir)
(file-exists-p doom-emacs-dir)))
(expect (and (not (file-exists-p! "/cant/possibly/exist/please/dont/exist"))
(not (file-exists-p "/cant/possibly/exist/please/dont/exist"))))))
(it "returns the file path if it exists"
(expect (file-exists-p! "init.example.el"
doom-emacs-dir)
:to-equal (expand-file-name "init.example.el" doom-emacs-dir)))
(it "understands compound statements"
(let ((default-directory doom-emacs-dir))
(expect (file-exists-p! (and "init.el" "init.example.el")))
(expect (file-exists-p! (or "doesnotexist" "init.example.el")))
(expect (not (file-exists-p! (or "doesnotexist" "DOESNOTEXIST")))))
(expect (file-exists-p! (and "init.el" "init.example.el")
doom-emacs-dir))
(expect (file-exists-p! (and "init.el" "init.example.el")
doom-emacs-dir))
(expect (file-exists-p! (or "doesnotexist" "init.example.el")
doom-emacs-dir))
(expect (not (file-exists-p! (or "doesnotexist" "DOESNOTEXIST")
doom-emacs-dir))))
(it "understands nested compound statements"
(expect (file-exists-p! (and "init.el" "init.example.el"
(or "doesnotexist" "LICENSE"))
doom-emacs-dir))
(expect (file-exists-p! (and "init.el" "init.example.el"
(and "LICENSE" "README.md"
(or "doesnotexist"
"early-init.el")))
doom-emacs-dir))
(expect (file-exists-p! (and "init.el" "init.example.el"
(or "edoesnotexist" "DOESNOTEXIST"
(and "idontexist"
"doanyofusexist?")))
doom-emacs-dir)
:to-be nil))
(it "returns the last form if a compound file check succeeds"
(expect (file-exists-p! (and "init.el" "init.example.el"
(or "doesnotexist" "LICENSE"))
doom-emacs-dir)
:to-equal (expand-file-name "LICENSE" doom-emacs-dir))
(expect (file-exists-p! (and "init.el" "init.example.el"
(or (or "doesnotexist" "DOESNOTEXIST")
"doanyofusreallyexist"
(or "cantexist" "LICENSE")))
doom-emacs-dir)
:to-equal (expand-file-name "LICENSE" doom-emacs-dir)))
(it "disregards the directory argument if given absolute path"
(expect (file-exists-p! "/tmp" "/directory/that/doesnt/exist"))
(expect (file-exists-p! doom-core-dir "/directory/that/doesnt/exist"))
(expect (file-exists-p! (and "/tmp" doom-core-dir) "/directory/that/doesnt/exist"))
(expect (file-exists-p! (or "/tmp" doom-core-dir) "/directory/that/doesnt/exist")))
(it "interpolates variables"
(let ((file-1 "init.el")
(file-2 "init.example.el")
(file-3 "LICENSE")
(file-404 "doesnotexistlikenoreally"))
(expect (file-exists-p! file-1 doom-emacs-dir))
(expect (file-exists-p! (and file-1 file-2) doom-emacs-dir))
(expect (file-exists-p! (and file-1 (or file-404 file-2)) doom-emacs-dir))
(expect (file-exists-p! (or (and file-404 file-2) (and file-3 file-1))
doom-emacs-dir))))
(it "interpolates forms"
(cl-letf (((symbol-function 'getfilename)
(lambda () "init.example.el")))
(expect (file-exists-p! (and (or (if nil "init.el" "doesnotexist")
(getfilename))
"LICENSE")
doom-emacs-dir)
:to-equal (expand-file-name "LICENSE" doom-emacs-dir))))))
(describe "interactive file operations"
:var (src dest projectile-projects-cache-time projectile-projects-cache)
(require 'core-projects)
(require 'projectile)
(describe "core/autoload/files"
:var (src dest projectile-projects-cache-time projectile-projects-cache)
(before-each
(setq src (make-temp-file "test-src")
existing (make-temp-file "test-existing")
@ -51,4 +149,4 @@
(expect (file-exists-p existing) :to-be nil))
(it "prompts to delete any existing file"
(quiet! (doom/delete-this-file existing))
(expect 'y-or-n-p :to-have-been-called-times 1))))
(expect 'y-or-n-p :to-have-been-called-times 1)))))

View file

@ -1,11 +1,11 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-autoload-message.el
(describe "core/autoload/message"
(describe "core/autoload/format"
(describe "format!"
:var (doom-message-backend)
:var (doom-format-backend)
(before-all
(setq doom-message-backend 'ansi))
(setq doom-format-backend 'ansi))
(it "should be a drop-in replacement for `format'"
(expect (format! "Hello %s" "World")
@ -16,7 +16,7 @@
:to-equal "Hello World"))
(it "supports text properties in interactive sessions"
(let ((doom-message-backend 'text-properties))
(let ((doom-format-backend 'text-properties))
(expect (get-text-property 0 'face (format! (red "Hello %s") "World"))
:to-equal (list :foreground (face-foreground 'term-color-red)))))
@ -35,4 +35,10 @@
(expect (format! (color 'red "Hello %s") "World")
:to-equal (format! (red "Hello %s") "World"))
(expect (format! (color (if nil 'red 'blue) "Hello %s") "World")
:to-equal (format! (blue "Hello %s") "World")))))
:to-equal (format! (blue "Hello %s") "World"))))
(xdescribe "insert!")
(xdescribe "print!")
(xdescribe "print-group!")
(xdescribe "error!")
(xdescribe "user-error!"))

View file

@ -1,10 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-autoload-help.el
;; (load! "autoload/help" doom-core-dir)
;;
;; (describe "core/autoload/help"
;; :var (a)
;; (before-each (setq a (switch-to-buffer (get-buffer-create "a"))))
;; (after-each (kill-buffer a)))

View file

@ -1,138 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-autoload-package.el
;;;###if nil
(describe "core/autoload/packages"
:var (package-alist
package-archive-contents
package-selected-packages
doom-packages
quelpa-cache
quelpa-initialized-p
doom-packages-dir
doom-core-packages
package-user-dir
quelpa-dir
pkg)
(before-all
(fset 'pkg
(lambda (name version &optional reqs)
(package-desc-create
:name name :version version :reqs reqs
:dir (expand-file-name (format "%s/" name) package-user-dir))))
(require 'package)
(require 'quelpa)
(setq doom-packages-dir (expand-file-name "packages/" (file-name-directory load-file-name))
package-user-dir (expand-file-name "elpa" doom-packages-dir)
quelpa-dir (expand-file-name "quelpa" doom-packages-dir)
quelpa-initialized-p t
doom-core-packages nil)
(spy-on #'package--user-installed-p :and-call-fake (lambda (_p) t))
(spy-on #'doom-initialize-packages :and-call-fake (lambda (&optional _)))
(spy-on #'package-refresh-contents :and-call-fake (lambda (&optional _)))
(spy-on #'quelpa-checkout :and-call-fake
(lambda (rcp _dir)
(when (eq (car rcp) 'doom-quelpa-dummy)
"20170405.1234"))))
(after-all
(unload-feature 'package t)
(unload-feature 'quelpa t))
(before-each
(setq package-alist
`((doom-dummy ,(pkg 'doom-dummy '(20160405 1234)))
(doom-uptodate-dummy ,(pkg 'doom-uptodate-dummy '(20160605 1234)))
(doom-unwanted-dummy ,(pkg 'doom-unwanted-dummy '(20160605 1234)))
(doom-quelpa-dummy ,(pkg 'doom-quelpa-dummy '(20160405 1234)))
(doom-noquelpa-dummy ,(pkg 'doom-noquelpa-dummy '(20160405 1234))))
package-archive-contents
`((doom-dummy ,(pkg 'doom-dummy '(20170405 1234)))
(doom-uptodate-dummy ,(pkg 'doom-uptodate-dummy '(20160605 1234))))
doom-packages
'((doom-dummy)
(doom-uptodate-dummy)
(doom-missing-dummy)
(doom-noquelpa-dummy)
(doom-disabled-dummy :disable t)
(doom-private-dummy :modules ((:private)))
(doom-disabled-private-dummy :modules ((:private)) :disable t)
(doom-quelpa-dummy :recipe (doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist")))
quelpa-cache
'((doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist")
(doom-noquelpa-dummy :fetcher github :repo "hlissner/does-not-exist-3")
(doom-new-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist-2"))
package-selected-packages (mapcar #'car doom-packages)))
(describe "package-backend"
(it "determines the correct backend of a package"
(expect (doom-package-backend 'doom-dummy) :to-be 'elpa)
(expect (doom-package-backend 'doom-quelpa-dummy) :to-be 'quelpa)
(expect (doom-package-backend 'org) :to-be 'emacs))
(it "errors out if package isn't installed"
(expect (doom-package-backend 'xyz) :to-throw)))
(describe "package-outdated-p (elpa)"
(it "detects outdated ELPA packages and returns both versions"
(expect (doom-package-outdated-p 'doom-dummy)
:to-equal '(doom-dummy (20160405 1234) (20170405 1234))))
(it "ignores up-to-date ELPA packages"
(expect (doom-package-outdated-p 'doom-uptodate-dummy) :to-be nil))
(it "detects outdated QUELPA packages and returns both versions"
(expect (doom-package-outdated-p 'doom-quelpa-dummy)
:to-equal '(doom-quelpa-dummy (20160405 1234) (20170405 1234))))
(it "ignores up-to-date QUELPA packages"
(expect (doom-package-outdated-p 'doom-uptodate-dummy) :to-be nil))
(it "returns nil if package isn't installed"
(expect (doom-package-outdated-p 'xyz) :to-be nil)))
(describe "get-packages"
(before-all
;; In addition to `package-installed-p', `doom-package-installed-p' does
;; file existence checks which won't work here, so we simplify it
(spy-on #'doom-package-installed-p :and-call-fake #'package-installed-p))
(it "returns all packages"
(expect (mapcar #'car (doom-find-packages))
:to-have-same-items-as
(mapcar #'car doom-packages)))
(it "returns only disabled packages"
(expect (mapcar #'car (doom-find-packages :disabled t))
:to-have-same-items-as
'(doom-disabled-dummy doom-disabled-private-dummy)))
(it "returns only non-disabled packages"
(expect (mapcar #'car (doom-find-packages :disabled nil))
:to-have-same-items-as
'(doom-dummy doom-uptodate-dummy doom-quelpa-dummy doom-missing-dummy doom-noquelpa-dummy doom-private-dummy)))
(it "returns only installed packages"
(expect (mapcar #'car (doom-find-packages :disabled nil :installed t))
:to-have-same-items-as
'(doom-dummy doom-uptodate-dummy doom-quelpa-dummy doom-noquelpa-dummy)))
(it "returns only non-installed packages"
(expect (mapcar #'car (doom-find-packages :disabled nil :installed nil))
:to-have-same-items-as
'(doom-missing-dummy doom-private-dummy)))
(it "returns only private packages"
(expect (mapcar #'car (doom-find-packages :private t))
:to-have-same-items-as
'(doom-private-dummy doom-disabled-private-dummy)))
(it "returns only disabled and private packages"
(expect (mapcar #'car (doom-find-packages :disabled t :private t))
:to-have-same-items-as
'(doom-disabled-private-dummy))))
(describe "get-orphaned-packages"
(it "returns orphaned packages"
(expect (doom-get-orphaned-packages) :to-contain 'doom-unwanted-dummy))
(it "returns packages that have changed backends"
(expect (doom-get-orphaned-packages) :to-contain 'doom-noquelpa-dummy)))
(describe "get-missing-packages"
(it "returns packages that haven't been installed"
(expect (mapcar #'car (doom-get-missing-packages))
:to-contain 'doom-missing-dummy))
(it "returns packages that have changed backends"
(expect (mapcar #'car (doom-get-missing-packages))
:to-contain 'doom-noquelpa-dummy))))
(xdescribe "core/autoload/packages")

View file

@ -1,21 +1,14 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-keybinds.el
(describe "core/keybinds"
(require 'core-keybinds)
(buttercup-define-matcher :to-expand-into (src result)
(let ((src (funcall src))
(result (funcall result)))
(or (equal (macroexpand-1 src) result)
(error "'%s' expanded into '%s' instead of '%s'"
src (macroexpand-1 src) result))))
(describe "core/keybinds"
(describe "map!"
:var (doom--map-evil-p doom-map-states)
:var (doom--map-evil-p states-alist)
(before-each
(setq doom--map-evil-p t
doom-map-states '((:n . normal)
states-alist '((:n . normal)
(:v . visual)
(:i . insert)
(:e . emacs)
@ -25,16 +18,17 @@
(describe "Single keybinds"
(it "binds a global key"
(expect '(map! "C-." #'a) :to-expand-into '(general-define-key "C-." #'a)))
(expect '(map! "C-." #'a)
:to-expand-into '(general-define-key "C-." #'a)))
(it "binds a key in one evil state"
(dolist (state doom-map-states)
(dolist (state states-alist)
(expect `(map! ,(car state) "C-." #'a)
:to-expand-into
`(general-define-key :states ',(cdr state) "C-." #'a))))
(it "binds a key in multiple evil states"
(expect `(map! :nvi "C-." #'a)
(expect '(map! :nvi "C-." #'a)
:to-expand-into
'(progn (general-define-key :states 'insert "C-." #'a)
(general-define-key :states 'visual "C-." #'a)
@ -54,7 +48,7 @@
'(general-define-key "C-." #'a "C-," #'b "C-/" #'c)))
(it "binds multiple keybinds in an evil state and preserve order"
(dolist (state doom-map-states)
(dolist (state states-alist)
(expect `(map! ,(car state) "a" #'a
,(car state) "b" #'b
,(car state) "c" #'c)

View file

@ -1,28 +1,130 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-lib.el
(require 'core-lib)
(describe "core-lib"
(before-all
(require 'core-lib))
(describe "core/lib"
;; --- Helpers ----------------------------
(describe "doom-unquote"
(it "unquotes a quoted form"
(expect (doom-unquote '(quote hello)) :to-be 'hello))
(it "unquotes nested-quoted forms"
(it "unquotes nested quoted forms"
(expect (doom-unquote '(quote (quote (a b c)))) :to-equal '(a b c)))
(it "unquotes function-quoted forms"
(expect (doom-unquote '(function a)) :to-be 'a))
(it "does nothing to unquoted forms"
(expect (doom-unquote 'hello) :to-be 'hello)))
(expect (doom-unquote 'hello) :to-be 'hello)
(expect (doom-unquote 5) :to-be 5)
(expect (doom-unquote t) :to-be t)))
(describe "doom-enlist"
(it "returns nil if given nil"
(expect (doom-enlist nil) :to-be nil))
(it "creates a list out of non-lists"
(expect (doom-enlist 'a) :to-equal '(a)))
(it "does nothing to lists"
(it "returns lists as-is"
(expect (doom-enlist '(a)) :to-equal '(a))))
(describe "doom-keyword-intern"
(it "returns a keyword"
(expect (doom-keyword-intern "test") :to-equal :test))
(it "errors if given anything but a string"
(expect (doom-keyword-intern t) :to-throw 'wrong-type-argument)))
(describe "doom-keyword-name"
(it "returns the string name of a keyword"
(expect (doom-keyword-name :test) :to-equal "test"))
(it "errors if given anything but a keyword"
(expect (doom-keyword-name "test") :to-throw 'wrong-type-argument)))
(describe "doom-partial"
(it "returns a closure"
(expect (functionp (doom-partial #'+ 1))))
(it "returns a partial closure"
(expect (funcall (doom-partial #'+ 1) 2) :to-be 3)))
(describe "doom-rpartial"
(it "returns a closure"
(expect (functionp (doom-rpartial #'+ 1))))
(it "returns a partial closure with right-aligned arguments"
(expect (funcall (doom-rpartial #'/ 2) 10) :to-be 5)))
;; --- Sugars -----------------------------
(describe "lambda!"
(it "returns an interactive function"
(expect (commandp (lambda!)))
(expect (funcall (lambda! 5)) :to-equal 5)))
(describe "lambda!!"
(it "returns an interactive function with a prefix argument"
(expect (commandp (lambda! #'ignore t)))
(expect (funcall (lambda!! (lambda (arg)
(interactive "P")
arg)
5))
:to-equal 5)))
(describe "file!"
(it "returns the executing file"
(expect (eval-and-compile (file!))
:to-equal (expand-file-name "test/test-core-lib.el"
doom-core-dir))))
(describe "dir!"
(it "returns the executing directory"
(expect (eval-and-compile (dir!))
:to-equal (expand-file-name "test" doom-core-dir))))
(describe "pushnew!"
(it "pushes values onto a list symbol, in order"
(let ((a '(1 2 3)))
(expect (pushnew! a 9 8 7)
:to-equal '(7 8 9 1 2 3))))
(it "only adds values that aren't already in the list"
(let ((a '(1 symbol 3.14 "test")))
(expect (pushnew! a "test" 'symbol 3.14 1)
:to-equal '(1 symbol 3.14 "test")))))
(describe "prependq!"
(it "prepends a list to a list symbol"
(let ((list '(a b c)))
(expect (prependq! list '(d e f))
:to-equal '(d e f a b c)))))
(describe "append!"
(it "appends a list to a list symbol"
(let ((list '(a b c)))
(expect (appendq! list '(d e f))
:to-equal '(a b c d e f)))))
(describe "nconcq!"
(it "nconc's a list to a list symbol"
(let ((list '(a b c)))
(expect (nconcq! list '(d e f))
:to-equal '(a b c d e f)))))
(describe "delq!"
(it "delete's a symbol from a list"
(let ((list '(a b c)))
(delq! 'b list)
(expect list :to-equal '(a c))))
(it "delete's an element from an alist by key"
(let ((alist '((a 1) (b 2) (c 3))))
(delq! 'b alist 'assq)
(expect alist :to-equal '((a 1) (c 3))))))
(describe "delete!"
(it "delete's a string from a list"
(let ((list '("a" "b" "c")))
(delete! "b" list)
(expect list :to-equal '("a" "c"))))
(it "delete's an element from an alist by key"
(let ((alist '(("a" 1) ("b" 2) ("c" 3))))
(delete! (assoc "b" alist) alist)
(expect alist :to-equal '(("a" 1) ("c" 3))))))
;; --- Macros -----------------------------
(describe "hooks"
(describe "add-hook!"
:var (fake-mode-hook other-mode-hook some-mode-hook)
@ -31,15 +133,22 @@
other-mode-hook nil
some-mode-hook '(first-hook second-hook)))
(it "resolves quoted hooks literally"
(expect '(add-hook! 'fake-mode-hook #'ignore) :to-expand-into
`(add-hook 'fake-mode-hook #'ignore nil nil)))
(it "resolves unquoted modes to their hook variables"
(expect '(add-hook! fake-mode #'ignore) :to-expand-into
`(add-hook 'fake-mode-hook #'ignore nil nil)))
(it "adds one-to-one hook"
(add-hook! fake-mode #'hook-2)
(add-hook! 'fake-mode-hook #'hook-1)
(expect fake-mode-hook :to-equal '(hook-1 hook-2 first-hook)))
(it "adds many-to-one hook"
(it "adds one-to-many hook"
(add-hook! (fake-mode other-mode some-mode) #'hook-2)
(add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) #'hook-1)
(add-hook! :append (fake-mode other-mode some-mode) #'last-hook)
(add-hook! (fake-mode other-mode some-mode) :append #'last-hook)
(expect fake-mode-hook :to-equal '(hook-1 hook-2 first-hook last-hook))
(expect other-mode-hook :to-equal '(hook-1 hook-2 last-hook))
(expect some-mode-hook :to-equal '(hook-1 hook-2 first-hook second-hook last-hook)))
@ -47,7 +156,7 @@
(it "adds many-to-many hooks and preserve provided order"
(add-hook! (fake-mode other-mode some-mode) #'(hook-3 hook-4))
(add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) #'(hook-1 hook-2))
(add-hook! :append '(fake-mode-hook other-mode-hook some-mode-hook) #'(last-hook-1 last-hook-2))
(add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) :append #'(last-hook-1 last-hook-2))
(expect fake-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 first-hook last-hook-1 last-hook-2))
(expect other-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 last-hook-1 last-hook-2))
(expect some-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 first-hook second-hook last-hook-1 last-hook-2)))
@ -55,10 +164,18 @@
(it "adds implicit lambda to one hook"
(add-hook! fake-mode (progn))
(add-hook! 'other-mode-hook (ignore))
(add-hook! :append 'some-mode-hook (ignore))
(add-hook! 'some-mode-hook :append (ignore))
(expect (caar fake-mode-hook) :to-be 'lambda)
(expect (caar other-mode-hook) :to-be 'lambda)
(expect (caar (last other-mode-hook)) :to-be 'lambda)))
(expect (caar (last other-mode-hook)) :to-be 'lambda))
(it "handles inline defuns as hook symbols"
(add-hook! fake-mode (defun hook-a ()))
(add-hook! 'other-mode-hook
(defun hook-b ())
(defun hook-c ()))
(expect (car fake-mode-hook) :to-be 'hook-a)
(expect other-mode-hook :to-equal '(hook-b hook-c))))
(describe "remove-hook!"
:var (fake-mode-hook)
@ -71,7 +188,7 @@
(it "removes multiple hooks"
(remove-hook! fake-mode #'(first-hook third-hook))
(remove-hook! 'fake-mode-hook #'(second-hook fourth-hook))
(expect fake-mode-hook :to-be nil))))
(expect fake-mode-hook :to-be nil)))
(describe "add-transient-hook!"
(it "adds a transient function to hooks"
@ -89,4 +206,74 @@
(ignore t)
(expect value))))
(xdescribe "associate!")) ; TODO
(describe "(un)setq-hook!"
:var (fake-hook x y z)
(before-each
(setq x 10 y 20 z 30))
(it "sets variables buffer-locally"
(setq-hook! 'fake-hook x 1)
(with-temp-buffer
(run-hooks 'fake-hook)
(expect (local-variable-p 'x))
(expect (= x 1)))
(expect (= x 10)))
(it "overwrites earlier hooks"
(setq-hook! 'fake-hook x 1 y 0)
(setq-hook! 'fake-hook x 5 y -1)
(with-temp-buffer
(run-hooks 'fake-hook)
(expect (= x 5))
(expect (= y -1))))
(it "unset setq hooks"
(setq-hook! 'fake-hook x 1 y 0)
(unsetq-hook! 'fake-hook y)
(with-temp-buffer
(run-hooks 'fake-hook)
(expect (local-variable-p 'x))
(expect (= x 1))
(expect (not (local-variable-p 'y)))
(expect (= y 20))))))
(describe "load!"
(before-each
(spy-on 'load :and-return-value t))
(it "loads a file relative to the current directory"
(load! "path")
(expect 'load :to-have-been-called)
(expect 'load :to-have-been-called-with (expand-file-name "path" (eval-when-compile (dir!))) nil t))
(it "loads a file relative to a specified directory"
(load! "path" doom-etc-dir)
(expect 'load :to-have-been-called-with (expand-file-name "path" doom-etc-dir) nil t)))
(describe "quiet!"
(it "suppresses output from message"
(expect (message "hello world") :to-output "hello world\n")
(expect (message "hello world") :to-output)
(let (doom-interactive-mode)
(expect (quiet! (message "hello world")) :not :to-output))
(let ((doom-interactive-mode t))
(expect (quiet! inhibit-message))
(expect (quiet! save-silently))))
(it "suppresses load messages from `load' & `load-file'"
(let ((tmpfile (make-temp-file "test" nil ".el")))
(with-temp-file tmpfile)
(let (doom-interactive-mode)
(expect (load-file tmpfile) :to-output (format "Loading %s (source)...\n" tmpfile))
(expect (quiet! (load-file tmpfile)) :not :to-output))
(delete-file tmpfile)))
(it "won't suppress output in debug mode"
(let ((doom-debug-mode t)
(tmpfile (make-temp-file "test" nil ".el")))
(dolist (doom-interactive-mode (list t nil))
(expect (quiet! (message "hello world"))
:to-output "hello world\n")
(with-temp-file tmpfile)
(expect (quiet! (load-file tmpfile))
:to-output (format "Loading %s (source)...\n" tmpfile)))))))

View file

@ -1,6 +1,7 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-modules.el
;;;###if nil
;; (require 'core-modules)
(describe "core-modules")
(xdescribe "core-modules")

View file

@ -1,4 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-packages.el
;;;###if nil
(describe "core-packages")
(xdescribe "core-packages")

View file

@ -1,12 +1,17 @@
;; -*- no-byte-compile: t; -*-
;;; ../core/test/test-core-projects.el
;;; core/test/test-core-projects.el
(describe "core/projects"
:var (projectile-enable-caching)
(require 'core-projects)
(require 'projectile)
(describe "core/projects"
(before-each (projectile-mode +1))
(after-each (projectile-mode -1))
(before-each
(setq projectile-enable-caching nil)
(projectile-mode +1))
(after-each
(projectile-mode -1))
(describe "project-p"
(it "Should detect when in a valid project"

View file

@ -1,17 +1,106 @@
;; -*- no-byte-compile: t; -*-
;;; ../core/test/test-core-ui.el
(require 'core-ui)
(describe "core/ui"
(describe "doom|protect-fallback-buffer"
:var (kill-buffer-query-functions a b)
(before-all
(setq kill-buffer-query-functions '(doom|protect-fallback-buffer)))
(with-demoted-errors "Import error: %s"
(require 'core-ui)))
(describe "doom-protect-fallback-buffer-h"
:var (kill-buffer-query-functions)
(before-all
(setq kill-buffer-query-functions '(doom-protect-fallback-buffer-h)))
(it "should kill other buffers"
(expect (kill-buffer (get-buffer-create "a"))))
(it "shouldn't kill the fallback buffer"
(expect (not (kill-buffer (doom-fallback-buffer)))))))
(expect (not (kill-buffer (doom-fallback-buffer))))))
(describe "custom hooks"
(describe "switch hooks"
:var (before-hook after-hook a b)
(before-each
(setq a (switch-to-buffer (get-buffer-create "a"))
b (get-buffer-create "b"))
(spy-on 'hook)
(add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(dolist (fn '(switch-to-buffer display-buffer))
(advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
(after-each
(remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(dolist (fn '(switch-to-buffer display-buffer))
(advice-remove fn #'doom-run-switch-buffer-hooks-a))
(kill-buffer a)
(kill-buffer b))
(describe "switch-buffer"
:var (doom-switch-buffer-hook)
(before-each
(setq doom-switch-buffer-hook '(hook)))
(after-each
(setq doom-switch-buffer-hook nil))
(it "should trigger when switching buffers"
(switch-to-buffer b)
(switch-to-buffer a)
(switch-to-buffer b)
(expect 'hook :to-have-been-called-times 3))
(it "should trigger only once on the same buffer"
(switch-to-buffer b)
(switch-to-buffer b)
(switch-to-buffer a)
(expect 'hook :to-have-been-called-times 2)))
(describe "switch-window"
:var (doom-switch-window-hook x y)
(before-each
(delete-other-windows)
(setq x (get-buffer-window a)
y (save-selected-window (split-window)))
(with-selected-window y
(switch-to-buffer b))
(select-window x)
(spy-calls-reset 'hook)
(setq doom-switch-window-hook '(hook)))
(it "should trigger when switching windows"
(select-window y)
(select-window x)
(select-window y)
(expect 'hook :to-have-been-called-times 3))
(it "should trigger only once on the same window"
(select-window y)
(select-window y)
(select-window x)
(expect 'hook :to-have-been-called-times 2)))
(xdescribe "switch-frame"
:var (doom-switch-frame-hook x y)
(before-each
(delete-other-windows)
(setq x (get-buffer-window a)
y (save-selected-window (split-window)))
(with-selected-window y
(switch-to-buffer b))
(select-window x)
(spy-calls-reset 'hook)
(setq doom-switch-window-hook '(hook)))
(it "should trigger when switching windows"
(select-window y)
(select-window x)
(select-window y)
(expect 'hook :to-have-been-called-times 3))
(it "should trigger only once on the same window"
(select-window y)
(select-window y)
(select-window x)
(expect 'hook :to-have-been-called-times 2))))))

View file

@ -2,118 +2,116 @@
;;; core/test/test-core.el
(describe "core"
(xdescribe "initialize"
:var (doom-init-p doom-init-modules-p doom-private-dir)
:var (doom-interactive-mode)
(before-each
(setq doom-init-p nil
doom-init-modules-p nil
doom-private-dir doom-emacs-dir)
(setq doom-interactive-mode nil))
(spy-on 'require)
(spy-on 'load)
(spy-on 'doom-reload-doom-autoloads)
(spy-on 'doom-reload-package-autoloads)
(spy-on 'doom-initialize-autoloads)
(spy-on 'doom-ensure-core-directories)
(spy-on 'doom-ensure-core-packages)
(spy-on 'doom-ensure-packages-initialized)
(spy-on 'doom-ensure-same-emacs-version-p))
(describe "in interactive session"
:var (noninteractive)
(before-each (setq noninteractive t))
(it "initializes once, unless forced")
(it "does not initialize on consecutive invokations")
(it "loads all core libraries" )
(it "loads autoloads file" )
(it "does not load autoloads file if forced" )
(it "regenerates missing autoloads" ))
(describe "in non-interactive session"
:var (noninteractive)
(before-each (setq noninteractive nil))
(it "initializes once, unless forced")
(it "does not initialize on consecutive invokations")
(it "does not load all core libraries" )
(it "loads autoloads file" )
(it "does not load autoloads file if forced" )
(it "does not regenerate missing autoloads" )))
(xdescribe "initialize-packages"
(before-each (spy-on 'quelpa-setup-p))
(it "initializes package.el once, unless forced" )
(it "initializes quelpa once, unless forced" )
(it "initializes doom-packages once, unless forced" ))
(xdescribe "initialize-modules"
(it "loads private init.el once, unless forced" ))
(xdescribe "initialize-autoloads"
(it "loads autoloads file" )
(it "ignores autoloads file if cleared" ))
(describe "custom hooks"
(describe "switch hooks"
:var (before-hook after-hook a b)
(describe "initialization"
(describe "doom-initialize"
:var (doom-init-p)
(before-each
(setq a (switch-to-buffer (get-buffer-create "a"))
b (get-buffer-create "b"))
(spy-on 'hook)
(add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(dolist (fn '(switch-to-buffer display-buffer))
(advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
(setq doom-init-p nil))
(it "initializes once"
(expect (doom-initialize))
(expect (not (doom-initialize)))
(expect (not (doom-initialize)))
(expect doom-init-p))
(it "initializes multiple times, if forced"
(expect (doom-initialize))
(expect (not (doom-initialize)))
(expect (doom-initialize 'force)))
(describe "package initialization"
(before-each
(spy-on 'doom-initialize-packages :and-return-value t))
(it "initializes packages if core autoload file doesn't exist"
(let ((doom-autoload-file "doesnotexist"))
(doom-initialize))
(expect 'doom-initialize-packages :to-have-been-called))
(it "doesn't initialize packages if core autoload file was loaded"
(let ((doom-interactive-mode t))
(spy-on 'doom-load-autoloads-file :and-return-value t)
(doom-initialize)
(expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file)
(expect 'doom-initialize-packages :to-have-been-called)))
(it "initializes packages when forced"
(doom-initialize 'force)
(expect 'doom-initialize-packages :to-have-been-called)))
(describe "autoloads files"
(before-each
(spy-on 'doom-load-autoloads-file)
(spy-on 'warn :and-return-value t))
(it "loads autoloads file"
(let ((doom-interactive-mode t))
(ignore-errors (doom-initialize)))
(expect 'doom-load-autoloads-file
:to-have-been-called-with doom-autoload-file)
(expect 'doom-load-autoloads-file
:to-have-been-called-with doom-package-autoload-file))
(it "does not load package autoloads file if noninteractive"
(doom-initialize)
(expect 'doom-load-autoloads-file
:to-have-been-called-with doom-autoload-file)
(expect 'doom-load-autoloads-file
:not :to-have-been-called-with doom-package-autoload-file))
(it "throws doom-autoload-error in interactive session where autoload files don't exist"
(let ((doom-interactive-mode t)
(doom-autoload-file "doesnotexist")
(doom-package-autoload-file "doesnotexist"))
(expect (doom-initialize) :to-throw 'doom-autoload-error)))))
(describe "doom-initialize-core"
(before-each
(spy-on 'require))
(it "loads all doom core libraries"
(doom-initialize-core)
(expect 'require :to-have-been-called-with 'core-keybinds)
(expect 'require :to-have-been-called-with 'core-ui)
(expect 'require :to-have-been-called-with 'core-projects)
(expect 'require :to-have-been-called-with 'core-editor))))
(describe "doom-load-autoloads-file"
(before-each
(spy-on 'load :and-return-value t))
(it "loads the autoloads file"
(doom-load-autoloads-file doom-autoload-file)
(expect 'load :to-have-been-called-with (file-name-sans-extension doom-autoload-file)
'noerror 'nomessage)))
(describe "doom-load-envvars-file"
:var (envvarfile process-environment)
(before-each
(setq process-environment (copy-sequence process-environment))
(with-temp-file doom-env-file
(insert "\n\n\nA=1\nB=2\nC=3\n")))
(after-each
(remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(dolist (fn '(switch-to-buffer display-buffer))
(advice-remove fn #'doom-run-switch-buffer-hooks-a))
(kill-buffer a)
(kill-buffer b))
(delete-file doom-env-file))
(describe "switch-buffer"
:var (doom-switch-buffer-hook)
(before-each
(setq doom-switch-buffer-hook '(hook)))
(after-each
(setq doom-switch-buffer-hook nil))
(it "throws a file-error if file doesn't exist"
(expect (doom-load-envvars-file "/tmp/envvardoesnotexist")
:to-throw 'file-error))
(it "should trigger when switching buffers"
(switch-to-buffer b)
(switch-to-buffer a)
(switch-to-buffer b)
(expect 'hook :to-have-been-called-times 3))
(it "to fail silently if NOERROR is non-nil"
(expect (doom-load-envvars-file "/tmp/envvardoesnotexist" 'noerror)
:not :to-throw))
(it "should trigger only once on the same buffer"
(switch-to-buffer b)
(switch-to-buffer b)
(switch-to-buffer a)
(expect 'hook :to-have-been-called-times 2)))
(it "loads a well-formed envvar file"
(expect (getenv "A") :not :to-be-truthy)
(expect (doom-load-envvars-file doom-env-file)
:to-equal '(("A" . "1") ("B" . "2") ("C" . "3")))
(expect (getenv "A") :to-equal "1"))
(describe "switch-window"
:var (doom-switch-window-hook x y)
(before-each
(delete-other-windows)
(setq x (get-buffer-window a)
y (save-selected-window (split-window)))
(with-selected-window y
(switch-to-buffer b))
(select-window x)
(spy-calls-reset 'hook)
(setq doom-switch-window-hook '(hook)))
(it "should trigger when switching windows"
(select-window y)
(select-window x)
(select-window y)
(expect 'hook :to-have-been-called-times 3))
(it "should trigger only once on the same window"
(select-window y)
(select-window y)
(select-window x)
(expect 'hook :to-have-been-called-times 2))))))
(it "fails on an invalid envvar file"
(with-temp-file doom-env-file (insert "A=1\nB=2\nC=3\n"))
(expect (doom-load-envvars-file doom-env-file) :to-throw))))

View file

@ -1,9 +1,40 @@
#+TITLE: API Demos
#+PROPERTY: header-args:elisp :results pp
This appendix serves as a reference on how to use Doom Emacs' standard library.
It is integrated into Helpful, in Doom.
* add-hook!
* Table of Contents :TOC_3:
- [[#examples-for-dooms-core-library][Examples for Doom's core library]]
- [[#core-lib][core-lib]]
- [[#add-hook][add-hook!]]
- [[#add-transient-hook][add-transient-hook!]]
- [[#after][after!]]
- [[#custom-set-faces][custom-set-faces!]]
- [[#custom-theme-set-faces][custom-theme-set-faces!]]
- [[#defer-feature][defer-feature!]]
- [[#defer-until][defer-until!]]
- [[#disable-packages][disable-packages!]]
- [[#doom][doom!]]
- [[#file-exists-p][file-exists-p!]]
- [[#lambda][lambda!]]
- [[#lambda-1][lambda!!]]
- [[#load][load!]]
- [[#map][map!]]
- [[#package][package!]]
- [[#pushnew][pushnew!]]
- [[#quiet][quiet!]]
- [[#remove-hook][remove-hook!]]
- [[#setq-hook][setq-hook!]]
- [[#unsetq-hook][unsetq-hook!]]
- [[#use-package][use-package!]]
- [[#interesting-snippets][Interesting snippets]]
- [[#persist-emacs-initial-frame-size-across-sessions][Persist Emacs' initial frame size across sessions]]
- [[#persist-emacs-initial-frame-position-dimensions-andor-full-screen-state-across-sessions][Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions]]
* Examples for Doom's core library
** core-lib
*** 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.
@ -31,7 +62,69 @@ It is integrated into Helpful, in Doom.
...))
#+END_SRC
* custom-theme-set-faces!
*** TODO add-transient-hook!
*** after!
#+BEGIN_SRC elisp :eval no
;;; `after!' will take:
;; An unquoted package symbol (the name of a package)
(after! helm ...)
;; An unquoted list of package symbols (i.e. BODY is evaluated once both magit
;; and git-gutter have loaded)
(after! (magit git-gutter) ...)
;; An unquoted, nested list of compound package lists, using any combination of
;; :or/:any and :and/:all
(after! (:or package-a package-b ...) ...)
(after! (:and package-a package-b ...) ...)
(after! (:and package-a (:or package-b package-c) ...) ...)
;; (Without :or/:any/:and/:all, :and/:all are implied.)
;; A common mistake is to pass it the names of major or minor modes, e.g.
(after! rustic-mode ...)
(after! python-mode ...)
;; But the code in them will never run! rustic-mode is in the `rustic' package
;; and python-mode is in the `python' package. This is what you want:
(after! rustic ...)
(after! python ...)
#+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)))
;; If you want to make use of the `doom-themes' package API (e.g. `doom-color',
;; `doom-lighten', `doom-darken', etc.), you must use `custom-set-faces!'
;; *after* the theme has been loaded. e.g.
(load-theme 'doom-one t)
(custom-set-faces!
`(outline-1 :foreground ,(doom-color 'red))
`(outline-2 :background ,(doom-color 'blue)))
#+END_SRC
*** custom-theme-set-faces!
#+BEGIN_SRC elisp :eval no
(custom-theme-set-faces! 'doom-one-theme
'(outline-1 :weight normal)
@ -55,35 +148,25 @@ It is integrated into Helpful, in Doom.
:weight normal)
`(,red-bg-faces
:background "red" :weight bold)))
;; If you want to make use of the `doom-themes' package API (e.g. `doom-color',
;; `doom-lighten', `doom-darken', etc.), you must use `custom-set-faces!'
;; *after* the theme has been loaded. e.g.
(load-theme 'doom-one t)
(custom-theme-set-faces! 'doom-one
`(outline-1 :foreground ,(doom-color 'red))
`(outline-2 :background ,(doom-color 'blue)))
#+END_SRC
* custom-set-faces!
*** TODO defer-feature!
*** TODO defer-until!
*** disable-packages!
#+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)))
;; Disable packages enabled by DOOM
(disable-packages! some-package second-package)
#+END_SRC
* doom!
*** doom!
#+BEGIN_SRC elisp :eval no
(doom! :completion
company
@ -113,7 +196,7 @@ It is integrated into Helpful, in Doom.
(default +bindings +smartparens))
#+END_SRC
* file-exists-p!
*** file-exists-p!
#+BEGIN_SRC elisp
(file-exists-p! "init.el" doom-emacs-dir)
#+END_SRC
@ -130,7 +213,86 @@ It is integrated into Helpful, in Doom.
#+RESULTS:
: /home/hlissner/.emacs.d/LICENSE
* remove-hook!
*** TODO lambda!
*** TODO lambda!!
*** load!
#+BEGIN_SRC elisp :eval no
;;; Lets say we're in ~/.doom.d/config.el
(load! "lisp/module") ; loads ~/.doom.d/lisp/module.el
(load! "somefile" doom-emacs-dir) ; loads ~/.emacs.d/somefile.el
(load! "anotherfile" doom-private-dir) ; loads ~/.doom.d/anotherfile.el
;; If you don't want a `load!' call to throw an error if the file doesn't exist:
(load! "~/.maynotexist" nil t)
#+END_SRC
*** map!
#+BEGIN_SRC elisp :eval no
(map! :map magit-mode-map
:m "C-r" 'do-something ; C-r in motion state
:nv "q" 'magit-mode-quit-window ; q in normal+visual states
"C-x C-r" 'a-global-keybind
:g "C-x C-r" 'another-global-keybind ; same as above
(:when IS-MAC
:n "M-s" 'some-fn
:i "M-o" (lambda (interactive) (message "Hi"))))
(map! (:when (featurep! :completion company) ; Conditional loading
:i "C-@" #'+company/complete
(:prefix "C-x" ; Use a prefix key
:i "C-l" #'+company/whole-lines)))
(map! (:when (featurep! :lang latex) ; local conditional
(:map LaTeX-mode-map
:localleader ; Use local leader
:desc "View" "v" #'TeX-view)) ; Add which-key description
:leader ; Use leader key from now on
:desc "Eval expression" ";" #'eval-expression)
#+END_SRC
*** package!
#+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources
;; specified in `doom-core-package-sources':
(package! evil)
(package! js2-mode)
(package! rainbow-delimiters)
;; To disable a package included with Doom (which will no-op all its `after!'
;; and `use-package!' blocks):
(package! evil :disable t)
(package! rainbow-delimiters :disable t)
;; To install a package from a github repo
(package! so-long :recipe (:host github :repo "hlissner/emacs-so-long"))
;; If a package is particularly big and comes with submodules you don't need,
;; you can tell the package manager not to clone the repo recursively:
(package! ansible :recipe (:nonrecursive t))
;; To install a particular branch, commit or tag:
(package! evil
;; if :host and :fetcher aren't specified, the package manager will fall back
;; to evil's default source provided by their (M)ELPA recipes:
:recipe (:commit "e7bc39de2f961505e8e112da8c1b315ae8afce52"))
(package! evil :recipe (:branch "stable"))
(package! evil :recipe (:tag "1.2.9"))
;; If you share your config between two computers, and don't want bin/doom
;; refresh to delete packages used only on one system, use :ignore
(package! evil :ignore (not (equal system-name "my-desktop")))
#+END_SRC
*** TODO pushnew!
*** quiet!
#+BEGIN_SRC elisp :eval no
;; Enters recentf-mode without extra output
(quiet! (recentf-mode +1))
#+END_SRC
*** 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.
@ -148,3 +310,78 @@ It is integrated into Helpful, in Doom.
;; 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
*** setq-hook!
#+BEGIN_SRC elisp :eval no
;; Set multiple variables after a hook
(setq-hook! 'markdown-mode-hook
line-spacing 2
fill-column 80)
;; Set variables after multiple hooks
(setq-hook! '(eshell-mode-hook term-mode-hook)
hscroll-margin 0)
#+END_SRC
*** unsetq-hook!
#+BEGIN_SRC elisp :eval no
(unsetq-hook! 'markdown-mode-hook line-spacing)
;; Removes the following variable hook
(setq-hook! 'markdown-mode-hook line-spacing 2)
;; Removing N variables from M hooks
(unsetq-hook! some-mode enable-something and-another)
(unsetq-hook! some-mode (enable-something and-another))
(unsetq-hook! '(one-mode-hook second-mode-hook) enable-something)
(unsetq-hook! (one-mode second-mode) enable-something)
#+END_SRC
*** use-package!
#+BEGIN_SRC elisp :eval no
;; Use after-call to load package before hook
(use-package! projectile
:after-call (pre-command-hook after-find-file dired-before-readin-hook))
;; defer recentf packages one by one
(use-package! recentf
:defer-incrementally easymenu tree-widget timer
:after-call after-find-file)
;; This is equivalent to :defer-incrementally (abc)
(use-package! abc
:defer-incrementally t)
#+END_SRC
* Interesting snippets
** Persist Emacs' initial frame size across sessions
#+BEGIN_SRC elisp
(let ((display-height (display-pixel-height))
(display-width (display-pixel-width)))
(add-to-list 'initial-frame-alist
`((left . ,(/ new-frame-width 2))
(top . ,(/ new-frame-height 2))
(width . ,(/ display-width 2))
(height . ,(/ display-height 2)))))
#+END_SRC
** Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions
#+BEGIN_SRC elisp
;; add to ~/.doom.d/config.el
(when-let* ((dims (doom-cache-get 'last-frame-size)))
(cl-destructuring-bind ((left . top) width height fullscreen) dims
(setq initial-frame-alist
(append initial-frame-alist
`((left . ,left)
(top . ,top)
(width . ,width)
(height . ,height)
(fullscreen . ,fullscreen))))))
(defun save-frame-dimensions ()
(doom-cache-set 'last-frame-size
(list (frame-position)
(frame-width)
(frame-height)
(frame-parameter nil 'fullscreen))))
(add-hook 'kill-emacs-hook #'save-frame-dimensions)
#+END_SRC

View file

@ -20,3 +20,8 @@
;; font. By inhibiting this, we easily halve startup times with fonts that are
;; larger than the system default.
(setq frame-inhibit-implied-resize t)
;; Ignore X resources; its settings would be redundant with the other settings
;; in this file and can conflict with later config (particularly where the
;; cursor color is concerned).
(advice-add #'x-apply-session-resources :override #'ignore)

18
init.el
View file

@ -27,13 +27,6 @@
;;
;;; License: MIT
(when (version< emacs-version "25.3")
(error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher"
emacs-version))
;; Ensure Doom is running out of this file's directory
(setq user-emacs-directory (file-name-directory load-file-name))
;; A big contributor to startup times is garbage collection. We up the gc
;; threshold to temporarily prevent it from running, then reset it later with
;; `doom-restore-garbage-collection-h'. Not resetting it will cause
@ -45,8 +38,17 @@
;; to skip the mtime checks on every *.elc file we load.
(setq load-prefer-newer noninteractive)
(let (file-name-handler-alist)
(when (version< emacs-version "25.3")
(error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher"
emacs-version))
;; Ensure Doom is running out of this file's directory
(setq user-emacs-directory (file-name-directory load-file-name)))
;; Load the heart of Doom Emacs
(require 'core (concat user-emacs-directory "core/core"))
(load (concat user-emacs-directory "core/core")
nil 'nomessage)
;; And let 'er rip!
(add-hook 'window-setup-hook #'doom-display-benchmark-h)

View file

@ -118,6 +118,7 @@
;;julia ; a better, faster MATLAB
;;kotlin ; a better, slicker Java(Script)
;;latex ; writing papers in Emacs has never been so fun
;;lean
;;ledger ; an accounting system in Emacs
;;lua ; one-based indices? one-based indices
markdown ; writing docs for people to ignore
@ -125,10 +126,10 @@
;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(org ; organize your plain life in plain text
+dragndrop ; file drag & drop support
+ipython ; ipython support for babel
+pandoc ; pandoc integration into org's exporter
+present) ; using Emacs for presentations
+dragndrop ; drag & drop files/images into org buffers
+ipython ; ipython/jupyter support for babel
+pandoc ; export-with-pandoc support
+present) ; using org-mode for presentations
;;perl ; write code no one else can comprehend
;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more

View file

@ -42,7 +42,7 @@ Aesthetic modules that affect the Emacs interface or user experience.
+ [[file:ui/unicode/README.org][unicode]]:
+ vc-gutter:
+ vi-tilde-fringe:
+ [[file:ui/window-select/README.org][window-select]]:
+ [[file:ui/window-select/README.org][window-select]] =+switch-window +numbers=:
+ [[file:ui/workspaces/README.org][workspaces]]: Isolated workspaces
* :editor
@ -64,7 +64,6 @@ Modules that reconfigure or augment packages or features built into Emacs.
+ dired =+ranger +icons=:
+ electric:
+ imenu:
+ vc:
* :term

View file

@ -34,7 +34,7 @@ https://assets.doomemacs.org/completion/company/overlay.png
+ [[https://github.com/company-mode/company-mode][company-mode]]
+ [[https://github.com/hlissner/emacs-company-dict][company-dict]]
+ [[https://github.com/raxod502/prescient.el][company-prescient]]
+ [[https://github.com/sebastiencs/company-box][company-box]]
+ [[https://github.com/sebastiencs/company-box][company-box]]* (=+childframe=)
* Prerequisites
This module has no direct prerequisites.

View file

@ -41,6 +41,7 @@ be negative.")
[remap bookmark-jump] #'helm-bookmarks
[remap execute-extended-command] #'helm-M-x
[remap find-file] #'helm-find-files
[remap locate] #'helm-locate
[remap imenu] #'helm-semantic-or-imenu
[remap noop-show-kill-ring] #'helm-show-kill-ring
[remap persp-switch-to-buffer] #'+helm/workspace-mini
@ -158,7 +159,23 @@ be negative.")
;;;###package helm-locate
(defvar helm-generic-files-map (make-sparse-keymap))
(after! helm-locate (set-keymap-parent helm-generic-files-map helm-map))
(after! helm-locate
(when (and IS-MAC
(null helm-locate-command)
(executable-find "mdfind"))
(setq helm-locate-command "mdfind -name %s"))
(set-keymap-parent helm-generic-files-map helm-map))
;;;###package helm-org
(use-package! helm-org
:when (featurep! :lang org)
:defer t
:init
(after! helm-mode
(pushnew! helm-completing-read-handlers-alist
'(org-capture . helm-org-completing-read-tags)
'(org-set-tags . helm-org-completing-read-tags))))
;;;###package helm-projectile

View file

@ -12,3 +12,5 @@
(package! helm-flx))
(when (and EMACS26+ (featurep! +childframe))
(package! posframe))
(when (featurep! :lang org)
(package! helm-org))

View file

@ -12,6 +12,7 @@
- [[#install][Install]]
- [[#macos][MacOS]]
- [[#arch-linux][Arch Linux]]
- [[#opensuse][openSUSE]]
- [[#features][Features]]
- [[#jump-to-file-project-navigation][Jump-to-file project navigation]]
- [[#project-search--replace][Project search & replace]]
@ -90,6 +91,11 @@ brew install ripgrep the_silver_searcher
sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install ripgrep the_silver_searcher
#+END_SRC
* Features
Ivy and its ilk are large plugins. Covering everything about them is outside of
this documentation's scope, so only Doom-specific Ivy features are listed here:

View file

@ -214,7 +214,7 @@ If ARG (universal argument), open selection in other-window."
(defun +ivy--tasks-open-action (x)
"Jump to the file and line of the current task."
(cl-destructuring-bind (label type file line) x
(cl-destructuring-bind (_label type file line) x
(with-ivy-window
(find-file (expand-file-name file (doom-project-root)))
(goto-char (point-min))

View file

@ -62,8 +62,6 @@ immediately runs it on the current candidate (ending the ivy session)."
projectile-completion-system 'ivy
;; Don't use ^ as initial input
ivy-initial-inputs-alist nil
;; highlight til EOL
ivy-format-function #'ivy-format-function-line
;; disable magic slash on non-match
ivy-magic-slash-non-match-action nil
;; don't show recent files in switch-buffer
@ -75,6 +73,9 @@ immediately runs it on the current candidate (ending the ivy session)."
;; enable ability to select prompt (alternative to `ivy-immediate-done')
ivy-use-selectable-prompt t)
(setf (alist-get 't ivy-format-functions-alist)
#'ivy-format-function-line)
;; 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)
@ -220,10 +221,13 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
[remap org-capture] #'counsel-org-capture
[remap swiper] #'counsel-grep-or-swiper
[remap evil-ex-registers] #'counsel-evil-registers
[remap yank-pop] #'counsel-yank-pop)
[remap yank-pop] #'counsel-yank-pop
[remap locate] #'counsel-locate)
:config
(set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil)
(when IS-MAC
(setq counsel-locate-cmd #'counsel-locate-cmd-mdfind))
(setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)"
counsel-describe-function-function #'helpful-callable
counsel-describe-variable-function #'helpful-variable

View file

@ -20,20 +20,7 @@
;;; Leader keys
(map! :leader
:desc "Find file in project" "C-f" #'projectile-find-file
:desc "Evaluate line/region" "e" #'+eval/line-or-region
:desc "Open scratch buffer" "x" #'doom/open-scratch-buffer
:desc "Open project scratch buffer" "X" #'doom/switch-to-scratch-buffer
(:when (featurep! :term term)
:desc "Toggle term popup" "`" #'+term/toggle
:desc "Open term here" "~" #'+term/here)
(:when (featurep! :term vterm)
:desc "Toggle vterm popup" "`" #'+vterm/toggle
:desc "Open vterm here" "~" #'+vterm/here)
(:when (featurep! :term eshell)
:desc "Toggle eshell popup" "`" #'+eshell/toggle
:desc "Open eshell here" "~" #'+eshell/here)
(:prefix ("l" . "<localleader>")) ; bound locally
(:prefix ("!" . "checkers")) ; bound by flycheck
@ -49,6 +36,8 @@
:desc "Browse emacs.d" "E" #'+default/browse-emacsd
:desc "Find file from here" "f" (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file)
:desc "Find file in other project" "F" #'doom/browse-in-other-project
:desc "Delete this file" "K" #'doom/delete-this-file
:desc "Move this file" "m" #'doom/move-this-file
:desc "Find file in project" "p" #'projectile-find-file
:desc "Find file in other project" "P" #'doom/find-file-in-other-project
:desc "Recent files" "r" #'recentf-open-files
@ -56,11 +45,25 @@
:desc "Sudo this file" "s" #'doom/sudo-this-file
:desc "Sudo find file" "S" #'doom/sudo-find-file
:desc "Yank filename" "y" #'+default/yank-buffer-filename
:desc "Delete this file" "X" #'doom/delete-this-file)
:desc "Open scratch buffer" "x" #'doom/open-scratch-buffer
:desc "Open project scratch buffer" "X" #'doom/switch-to-scratch-buffer)
;;; <leader> g --- lookup
(:when (featurep! :tools lookup)
(:prefix-map ("g" . "lookup")
"k" #'+lookup/documentation
"d" #'+lookup/definition
"D" #'+lookup/references
"f" #'+lookup/file
"o" #'+lookup/online-select
"i" #'+lookup/in-docsets
"I" #'+lookup/in-all-docsets))
;;; <leader> o --- org
"o" nil ; we need to unbind it first as Org claims this
(:prefix-map ("o". "org")
:desc "Do what I mean" "o" #'+org/dwim-at-point
:desc "Display inline images" "i" #'org-display-inline-images
:desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point
(:prefix ("a" . "org agenda")
:desc "Agenda" "a" #'org-agenda
@ -104,6 +107,17 @@
:desc "Create Temp Template" "c" #'aya-create
:desc "Use Temp Template" "e" #'aya-expand)
(:prefix-map ("t" . "terminal")
(:when (featurep! :term term)
:desc "Toggle term popup" "t" #'+term/toggle
:desc "Open term here" "T" #'+term/here)
(:when (featurep! :term vterm)
:desc "Toggle vterm popup" "t" #'+vterm/toggle
:desc "Open vterm here" "T" #'+vterm/here)
(:when (featurep! :term eshell)
:desc "Toggle eshell popup" "t" #'+eshell/toggle
:desc "Open eshell here" "T" #'+eshell/here))
;;; <leader> v --- versioning
(:prefix-map ("v" . "versioning")
:desc "Git revert file" "R" #'vc-revert

View file

@ -54,7 +54,7 @@
[remap quit-window] #'kill-current-buffer)
(:map (help-mode-map helpful-mode-map)
:n "o" 'ace-link-help)
:n "o" #'ace-link-help)
;; misc
:n "C-S-f" #'toggle-frame-fullscreen
@ -116,6 +116,8 @@
:v "g+" #'evil-numbers/inc-at-pt
;; custom evil keybinds
:n "zn" #'+evil:narrow-buffer
:n "zN" #'doom/widen-indirectly-narrowed-buffer
:n "zx" #'kill-current-buffer
:n "ZX" #'bury-buffer
;; repeat in visual mode (FIXME buggy)
@ -544,6 +546,7 @@
:desc "Search buffer" "b" #'swiper
:desc "Search current directory" "d" #'+default/search-cwd
:desc "Search other directory" "D" #'+default/search-other-cwd
:desc "Locate file" "f" #'locate
:desc "Jump to symbol" "i" #'imenu
:desc "Jump to link" "l" #'ace-link
:desc "Look up online" "o" #'+lookup/online
@ -581,7 +584,7 @@
;;; <leader> b --- buffer
(:prefix-map ("b" . "buffer")
:desc "Toggle narrowing" "-" #'doom/clone-and-narrow-buffer
:desc "Toggle narrowing" "-" #'doom/toggle-narrow-buffer
:desc "Previous buffer" "[" #'previous-buffer
:desc "Next buffer" "]" #'next-buffer
(:when (featurep! :ui workspaces)
@ -594,7 +597,7 @@
:desc "Switch to last buffer" "l" #'evil-switch-to-windows-last-buffer
:desc "Next buffer" "n" #'next-buffer
:desc "New empty buffer" "N" #'evil-buffer-new
:desc "Kill other buffers" "o" #'doom/kill-other-buffers
:desc "Kill other buffers" "O" #'doom/kill-other-buffers
:desc "Previous buffer" "p" #'previous-buffer
:desc "Save buffer" "s" #'save-buffer
:desc "Sudo edit this file" "S" #'doom/sudo-this-file
@ -615,7 +618,9 @@
:desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace
:desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines
(:when (featurep! :tools flycheck)
:desc "List errors" "x" #'flycheck-list-errors))
:desc "List errors" "x" #'flycheck-list-errors)
(:unless (featurep! :tools flycheck)
:desc "List errors" "x" #'flymake-show-diagnostics-buffer))
;;; <leader> f --- file
(:prefix-map ("f" . "file")
@ -629,6 +634,7 @@
:desc "Find file in emacs.d" "e" #'+default/find-in-emacsd
:desc "Browse emacs.d" "E" #'+default/browse-emacsd
:desc "Find file from here" "f" #'find-file
:desc "Locate file" "l" #'locate
:desc "Move/rename file" "m" #'doom/move-this-file
:desc "Find file in private config" "p" #'doom/find-file-in-private-config
:desc "Browse private config" "P" #'doom/open-private-config
@ -642,6 +648,8 @@
;;; <leader> g --- git
(:prefix-map ("g" . "git")
:desc "Git revert file" "R" #'vc-revert
:desc "Copy git link" "y" #'git-link
:desc "Copy git link to homepage" "Y" #'git-link-homepage
(:when (featurep! :ui vc-gutter)
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk
:desc "Git stage hunk" "s" #'git-gutter:stage-hunk
@ -667,7 +675,7 @@
:desc "Find issue" "i" #'forge-visit-issue
:desc "Find pull request" "p" #'forge-visit-pullreq)
(:prefix ("o" . "open in browser")
:desc "Browse region or line" "." #'+vc/git-browse-region-or-line
:desc "Browse region or line" "o" #'+vc/git-browse-region-or-line
:desc "Browse remote" "r" #'forge-browse-remote
:desc "Browse commit" "c" #'forge-browse-commit
:desc "Browse an issue" "i" #'forge-browse-issue
@ -707,7 +715,12 @@
:desc "Search org agenda headlines" "h" #'+default/org-notes-headlines
:desc "Find file in notes" "n" #'+default/find-in-notes
:desc "Browse notes" "N" #'+default/browse-notes
:desc "Org store link" "l" #'org-store-link)
:desc "Org store link" "l" #'org-store-link
(:when (featurep! :lang org +journal)
(:prefix ("j" . "journal")
:desc "New Entry" "j" #'org-journal-new-entry
:desc "Search Forever" "s" #'org-journal-search-forever)))
;;; <leader> o --- open
(:prefix-map ("o" . "open")
@ -813,6 +826,10 @@
;;; <leader> t --- toggle
(:prefix-map ("t" . "toggle")
:desc "Big mode" "b" #'doom-big-font-mode
(:when (featurep! :tools flycheck)
:desc "Flycheck" "f" #'flycheck-mode)
(:unless (featurep! :tools flycheck)
:desc "Flymake" "f" #'flymake-mode)
:desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
:desc "Evil goggles" "g" #'evil-goggles-mode
:desc "Indent style" "I" #'doom/toggle-indent-style

View file

@ -1,9 +1,9 @@
;;; config/default/+evil.el -*- lexical-binding: t; -*-
(defun +default|disable-delete-selection-mode ()
(defun +default-disable-delete-selection-mode-h ()
(delete-selection-mode -1))
(add-hook 'evil-insert-state-entry-hook #'delete-selection-mode)
(add-hook 'evil-insert-state-exit-hook #'+default|disable-delete-selection-mode)
(add-hook 'evil-insert-state-exit-hook #'+default-disable-delete-selection-mode-h)
;;

View file

@ -109,7 +109,7 @@ If ARG (universal argument), runs `compile' from the current directory."
((error "No kill-ring search backend available. Enable ivy or helm!")))))
;;;###autoload
(defun +default*newline-indent-and-continue-comments ()
(defun +default--newline-indent-and-continue-comments-a ()
"A replacement for `newline-and-indent'.
Continues comments if executed from a commented line, with special support for
@ -159,7 +159,7 @@ possible, or just one char if that's not possible."
((delete-char -1)))))
;;;###autoload
(defun +default*delete-backward-char (n &optional killflag)
(defun +default--delete-backward-char-a (n &optional killflag)
"Same as `delete-backward-char', but preforms these additional checks:
+ If point is surrounded by (balanced) whitespace and a brace delimiter ({} []
@ -283,11 +283,11 @@ If prefix ARG is set, prompt for a known project to search from."
((rgrep (regexp-quote symbol))))))
;;;###autoload
(defun +default/search-notes-for-symbol-at-point (&optional arg symbol)
(defun +default/search-notes-for-symbol-at-point (&optional symbol)
"Conduct a text search in the current project for symbol at point. If prefix
ARG is set, prompt for a known project to search from."
(interactive
(list current-prefix-arg (thing-at-point 'symbol t)))
(list (thing-at-point 'symbol t)))
(require 'org)
(let ((default-directory org-directory))
(+default/search-project-for-symbol-at-point

View file

@ -34,6 +34,10 @@
;; prompt for the key passphrase.
epa-pinentry-mode 'loopback))
;;;###package tramp
(unless IS-WINDOWS
(setq tramp-default-method "ssh")) ; faster than the default scp
;;
;;; Smartparens config
@ -195,10 +199,10 @@
;; e) properly delete smartparen pairs when they are encountered, without
;; the need for strict mode.
;; f) do none of this when inside a string
(advice-add #'delete-backward-char :override #'+default*delete-backward-char))
(advice-add #'delete-backward-char :override #'+default--delete-backward-char-a))
;; Makes `newline-and-indent' continue comments (and more reliably)
(advice-add #'newline-and-indent :override #'+default*newline-indent-and-continue-comments))
(advice-add #'newline-and-indent :override #'+default--newline-indent-and-continue-comments-a))
;;
@ -260,7 +264,6 @@
(define-key! help-map
;; new keybinds
"'" #'describe-char
"B" #'doom/open-bug-report
"D" #'doom/help
"E" #'doom/sandbox
"M" #'doom/describe-active-minor-mode
@ -312,8 +315,6 @@
"F" #'describe-face
;; replaces `view-hello-file' b/c annoying
"h" #'doom/help
;; replaces `describe-language-environment' b/c remapped to C-l
"L" #'global-command-log-mode
;; replaces `view-emacs-news' b/c it's on C-n too
"n" #'doom/help-news
;; replaces `finder-by-keyword'

View file

@ -53,7 +53,12 @@ variable for an explanation of the defaults (in comments). See
;; (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))))
;; (let ((list (sexp-at-point)))
;; ;; Fixes
;; (when (assq 'pdf list)
;; (setf (alist-get 'pdf list) '(pdf-tools)))
;; (kill-new (prin1-to-string list)))))
(defvar evil-collection-mode-list
`(2048-game
ag
@ -143,7 +148,7 @@ variable for an explanation of the defaults (in comments). See
p4
(package-menu package)
pass
(pdf pdf-view)
(pdf pdf-tools)
popup
proced
process-menu

View file

@ -135,10 +135,18 @@ integration."
;;;###autoload (autoload '+evil:narrow-buffer "editor/evil/autoload/evil" nil t)
(evil-define-operator +evil:narrow-buffer (beg end &optional bang)
"Wrapper around `doom/clone-and-narrow-buffer'."
"Narrow the buffer to region between BEG and END.
Widens narrowed buffers first. If BANG, use indirect buffer clones instead."
:move-point nil
(interactive "<r><!>")
(doom/clone-and-narrow-buffer beg end bang))
(if (not bang)
(if (buffer-narrowed-p)
(widen)
(narrow-to-region beg end))
(when (buffer-narrowed-p)
(doom/widen-indirectly-narrowed-buffer t))
(doom/narrow-buffer-indirectly beg end)))
;;;###autoload
(defun +evil/next-beginning-of-method (count)
@ -192,13 +200,13 @@ See `+evil/next-preproc-directive' for details."
(dotimes (_ (abs count))
(cond ((> count 0)
(while (and (not (eobp)) (sp-point-in-comment))
(next-line))
(forward-line 1))
(unless (comment-search-forward (point-max) 'noerror)
(goto-char orig-pt)
(user-error "No comment after point")))
(t
(while (and (not (bobp)) (sp-point-in-comment))
(previous-line))
(forward-line -1))
(unless (comment-search-backward nil 'noerror)
(goto-char orig-pt)
(user-error "No comment before point")))))))

View file

@ -177,7 +177,7 @@ If BANG, search Doom documentation."
'module (list cat mod))))
(module (completing-read "Describe module: " modules nil t query))
(key (get-text-property 0 'module module)))
(doom/help-modules key)))
(doom/help-modules (car key) (cdr key))))
((and (string-match-p "\\(?:SPC\\|[CMsSH]-[^ ]\\|<[^>]+>\\)" query)
(helpful-key (kbd (string-trim query)))))
((apropos query t)))))

View file

@ -85,31 +85,31 @@
;;; ]u / [u
;;;###autoload (autoload '+evil:url-encode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:url-encode (count &optional beg end type)
(evil-define-operator +evil:url-encode (_count &optional beg end)
"TODO"
(interactive "<c><R>")
(interactive "<c><r>")
(+evil--encode beg end #'url-encode-url))
;;;###autoload (autoload '+evil:url-decode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:url-decode (count &optional beg end type)
(evil-define-operator +evil:url-decode (_count &optional beg end)
"TODO"
(interactive "<c><R>")
(interactive "<c><r>")
(+evil--encode beg end #'url-unhex-string))
;;; ]y / [y
;;;###autoload (autoload '+evil:c-string-encode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:c-string-encode (count &optional beg end type)
(evil-define-operator +evil:c-string-encode (_count &optional beg end)
"TODO"
(interactive "<c><R>")
(interactive "<c><r>")
(+evil--encode
beg end
(lambda (text)
(replace-regexp-in-string "[\"\\]" (lambda (ch) (concat "\\" ch)) text))))
;;;###autoload (autoload '+evil:c-string-decode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:c-string-decode (count &optional beg end type)
(evil-define-operator +evil:c-string-decode (_count &optional beg end)
"TODO"
(interactive "<c><R>")
(interactive "<c><r>")
(+evil--encode
beg end
(lambda (text)

View file

@ -257,12 +257,12 @@ directives. By default, this only recognizes C directives.")
(use-package! evil-escape
:commands evil-escape
:after-call evil-normal-state-exit-hook
:after-call pre-command-hook
:init
(setq evil-escape-excluded-states '(normal visual multiedit emacs motion)
evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode)
evil-escape-key-sequence "jk"
evil-escape-delay 0.25)
evil-escape-delay 0.15)
(evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape)
:config
;; no `evil-escape' in minibuffer

View file

@ -3,12 +3,11 @@
(describe "feature/evil"
:var (resv project-root)
(before-all
(require! :editor evil)
(require 'evil)
(load! "../autoload/evil"))
(after-all
(unload-feature 'evil t))
(load! "../autoload/evil")
(before-each
(fset 'resv #'+evil-resolve-vim-path-a)
(spy-on 'doom-project-root :and-call-fake (lambda () project-root)))

View file

@ -5,6 +5,8 @@
(defun +multiple-cursors/evil-mc-toggle-cursors ()
"Toggle frozen state of evil-mc cursors."
(interactive)
(unless (evil-mc-has-cursors-p)
(user-error "No cursors exist to be toggled"))
(setq evil-mc-frozen (not (and (evil-mc-has-cursors-p)
evil-mc-frozen)))
(if evil-mc-frozen
@ -45,12 +47,14 @@ pauses cursors."
(evil-mc-make-cursor-here))))
;;;###autoload (autoload '+multiple-cursors:evil-mc "editor/multiple-cursors/autoload/evil-mc" nil t)
(evil-define-command +multiple-cursors:evil-mc (beg end type pattern &optional bang)
"Create mc cursors at each match of PATTERN within BEG and END, and leave the
cursor at the final match. If BANG, then treat PATTERN as literal."
(evil-define-command +multiple-cursors:evil-mc (beg end type pattern &optional flags bang)
"Create mc cursors at each match of PATTERN within BEG and END.
This leaves the cursor at the final match. If BANG, then treat PATTERN as
literal. PATTERN is a delimited regexp (the same that :g or :s uses)."
:move-point nil
:evil-mc t
(interactive "<R><//g><!>")
(interactive "<R><//!><!>")
(unless (and (stringp pattern)
(not (string-empty-p pattern)))
(user-error "A regexp pattern is required"))
@ -59,9 +63,19 @@ cursor at the final match. If BANG, then treat PATTERN as literal."
(cons (evil-ex-make-search-pattern
(if bang (regexp-quote pattern) pattern))
(list beg end type)))
(save-excursion
(evil-with-restriction beg end
(evil-mc-make-cursors-for-all)))
(let ((point (point)))
(save-excursion
(goto-char (point-min))
(while (eq (evil-ex-find-next (evil-mc-get-pattern) 'forward t) t)
(goto-char (1- (point)))
(when (/= point (point))
(evil-mc-run-cursors-before)
(evil-mc-make-cursor-at-pos (point)))
(goto-char
(if (memq ?g flags)
(line-beginning-position 2)
(1+ (point))))))))
(evil-exit-visual-state)
(evil-mc-goto-cursor
(if (= (evil-visual-direction) 1)

View file

@ -59,11 +59,7 @@
;; Forward declare these so that ex completion and evil-mc support is
;; recognized before the autoloaded functions are loaded.
(evil-add-command-properties '+evil:align :evil-mc t)
(evil-set-command-properties '+multiple-cursors:evil-mc
:move-point nil
:ex-arg 'global-match
:ex-bang t
:evil-mc t))
(evil-add-command-properties '+multiple-cursors:evil-mc :evil-mc t))
(after! multiple-cursors-core

View file

@ -5,6 +5,6 @@
"Register minor MODES (one mode symbol or a list of them) with yasnippet so it
can have its own snippets category, if the folder exists."
(dolist (mode (doom-enlist modes))
(let ((fn (intern (format "+snippets|register-%s" mode))))
(let ((fn (intern (format "+snippets-register-%s-h" mode))))
(fset fn (lambda () (yas-activate-extra-mode mode)))
(add-hook (intern (format "%s-hook" mode)) fn))))

View file

@ -225,7 +225,7 @@ You will be prompted for a snippet to alias."
"# condition: t}\n"
"# type: command\n"
"# --\n"
"(%alias \"${4:" (or template "uuid") "}\")"))
"(%alias \"${4:" (or template-uuid "uuid") "}\")"))
(when (bound-and-true-p evil-local-mode)
(evil-insert-state)))))

View file

@ -24,7 +24,7 @@
;; Use GNU ls as `gls' from `coreutils' if available. Add `(setq
;; dired-use-ls-dired nil)' to your config to suppress the Dired warning
;; when not using GNU ls.
(if-let* ((gls (executable-find "gls")))
(if-let (gls (executable-find "gls"))
(setq insert-directory-program gls)
;; BSD ls doesn't support --group-directories-first
(setq args (delete "--group-directories-first" args))))
@ -45,30 +45,12 @@
:hook (dired-mode . diredfl-mode))
(use-package! dired-k
:hook (dired-initial-position . dired-k)
:hook (dired-after-readin . dired-k-no-revert)
(use-package! diff-hl
:hook (dired-mode . diff-hl-dired-mode)
:hook (magit-post-refresh . diff-hl-magit-post-refresh)
:config
(setq dired-k-style 'git
dired-k-padding 1)
;; Don't highlight based on mtime, this interferes with diredfl and is more
;; confusing than helpful.
(advice-add #'dired-k--highlight-by-file-attribyte :override #'ignore)
(defadvice! +dired--interrupt-process-a (orig-fn &rest args)
"Fixes dired-k killing git processes too abruptly, leaving behind disruptive
.git/index.lock files."
:around #'dired-k--start-git-status
(cl-letf (((symbol-function #'kill-process)
(symbol-function #'interrupt-process)))
(apply orig-fn args)))
(defadvice! +dired--dired-k-highlight-a (orig-fn &rest args)
"Butt out if the requested directory is remote (i.e. through tramp)."
:around #'dired-k--highlight
(unless (file-remote-p default-directory)
(apply orig-fn args))))
;; use margin instead of fringe
(diff-hl-margin-mode))
(use-package! ranger
@ -117,6 +99,7 @@ we have to clean it up ourselves."
(use-package! dired-x
:unless (featurep! +ranger)
:hook (dired-mode . dired-omit-mode)
:config
(setq dired-omit-verbose nil)

View file

@ -2,7 +2,7 @@
;;; emacs/dired/packages.el
(package! diredfl)
(package! dired-k)
(package! diff-hl)
(package! dired-rsync)
(when (featurep! +ranger)
(package! ranger))

View file

@ -4,16 +4,18 @@
#+STARTUP: inlineimages
* Table of Contents :TOC:
- [[Description][Description]]
- [[Module Flags][Module Flags]]
- [[Plugins][Plugins]]
- [[Prerequisites][Prerequisites]]
- [[MacOS][MacOS]]
- [[Arch Linux][Arch Linux]]
- [[Features][Features]]
- [[Configuration][Configuration]]
- [[offlineimap][offlineimap]]
- [[mbsync][mbsync]]
- [[#description][Description]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#prerequisites][Prerequisites]]
- [[#macos][MacOS]]
- [[#arch-linux][Arch Linux]]
- [[#nixos][NixOS]]
- [[#opensuse][openSUSE]]
- [[#features][Features]]
- [[#configuration][Configuration]]
- [[#offlineimap][offlineimap]]
- [[#mbsync][mbsync]]
* Description
This module makes Emacs an email client, using ~mu4e~.
@ -66,6 +68,15 @@ environment.systemPackages = with pkgs; [
[[https://github.com/Emiller88/dotfiles/blob/master/modules/shell/mail.nix][An example of setting up mbsync with home-manager]]
** openSUSE
Remove ~#~ in ~#sync_program=offlineimap~ to choose ~offlineimap~ instead of ~mbsync~.
#+BEGIN_SRC sh :dir /sudo::
sync_program=isync # mbsync
#sync_program=offlineimap
sudo zypper install maildir-utils $sync_programm
#+END_SRC
* TODO Features
* Configuration

View file

@ -36,15 +36,15 @@
when exporting org-mode to html."
:filter-args #'org-html-paragraph
(cl-destructuring-bind (paragraph contents info) args
(let* ((fix-regexp "[[:multibyte:]a-zA-Z0-9]")
(origin-contents contents)
(let* ((fix-regexp "[[:multibyte:]]")
(origin-contents
(replace-regexp-in-string
"<[Bb][Rr] */>"
""
contents))
(fixed-contents
(replace-regexp-in-string
(concat "\\("
fix-regexp
"\\) *\\(<[Bb][Rr] */>\\)?\n *\\("
fix-regexp
"\\)")
"\\1\\3"
(concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)")
"\\1\\2"
origin-contents)))
(list paragraph fixed-contents info))))

View file

@ -22,7 +22,7 @@
(after! helm (helm-migemo-mode +1)))))
(use-package pangu-spacing
(use-package! pangu-spacing
:hook (text-mode . pangu-spacing-mode)
:init
;; replacing `chinese-two-byte' by `japanese'
@ -46,15 +46,15 @@
when exporting org-mode to html."
:filter-args #'org-html-paragraph
(cl-destructuring-bind (paragraph contents info) args
(let* ((fix-regexp "[[:multibyte:]a-zA-Z0-9]")
(origin-contents contents)
(let* ((fix-regexp "[[:multibyte:]]")
(origin-contents
(replace-regexp-in-string
"<[Bb][Rr] */>"
""
contents))
(fixed-contents
(replace-regexp-in-string
(concat "\\("
fix-regexp
"\\) *\\(<[Bb][Rr] */>\\)?\n *\\("
fix-regexp
"\\)")
"\\1\\3"
(concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)")
"\\1\\2"
origin-contents)))
(list paragraph fixed-contents info))))

View file

@ -1,9 +1,4 @@
#+TITLE: :lang agda
This module adds support for the [[http://wiki.portal.chalmers.se/agda/pmwiki.php][agda]] programming language.
Emacs support is included in the agda release (you can find installation
instructions [[https://agda.readthedocs.io/en/latest/getting-started/installation.html][here]]). This module attempts to find the location of ~agda2.el~ via
the ~agda-mode locate~ command that comes with the agda release. Users can set
this manually by setting the ~+agda2-dir~ variable.
This module adds support for the [[http://wiki.portal.chalmers.se/agda/pmwiki.php][agda]] programming language. The Emacs support
exists directly in the agda repository but not in melpa.

View file

@ -1,17 +1,7 @@
;;; lang/agda/config.el -*- lexical-binding: t; -*-
(defvar +agda-dir
(when (executable-find "agda-mode")
(file-name-directory (shell-command-to-string "agda-mode locate"))))
(use-package! agda2
:when +agda-dir
:load-path +agda-dir)
(use-package! agda2-mode
:defer t
:config
(map! :map agda2-mode-map
(map! :after agda2-mode
:map agda2-mode-map
:localleader
"?" #'agda2-show-goals
"." #'agda2-goal-and-context-and-inferred
@ -38,4 +28,4 @@
"d" #'agda2-remove-annotations
"h" #'agda2-display-implicit-arguments
"q" #'agda2-quit
"r" #'agda2-restart)))
"r" #'agda2-restart))

View file

@ -1,5 +0,0 @@
;; -*- lexical-binding: t; no-byte-compile: t; -*-
;;; lang/agda/doctor.el
(unless (executable-find "agda-mode")
(warn! "Couldn't find agda-mode. Agda support won't work"))

View file

@ -0,0 +1,15 @@
;; -*- no-byte-compile: t; -*-
;;; lang/agda/packages.el
(package! agda-input
:recipe
(:host github :repo "agda/agda"
:files ("src/data/emacs-mode/agda-input.el")))
(package! agda2-mode
:recipe
(:host github :repo "agda/agda"
:files
("src/data/emacs-mode/*.el"
(:exclude "agda-input.el"))))

View file

@ -11,6 +11,7 @@
- [[#irony-server][irony-server]]
- [[#macos][MacOS]]
- [[#arch-linux][Arch Linux]]
- [[#opensuse][openSUSE]]
- [[#rtags][rtags]]
- [[#configure][Configure]]
- [[#project-compile-settings][Project compile settings]]
@ -93,6 +94,11 @@ rm -rf irony-mode
pacman -S clang cmake
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install clang cmake
#+END_SRC
** rtags
Code navigation requires an [[https://github.com/Andersbakken/rtags][rtags]] server (~rdm~) installed. This should be
available through your OS's package manager.

View file

@ -103,7 +103,7 @@ simpler."
(rtags-call-rc :silent t "-J" (or (doom-project-root) default-directory))))
;; then irony
(when (and (featurep 'irony) irony-mode)
(+cc|irony-init-compile-options)))
(+cc-init-irony-compile-options-h)))
;;;###autoload
(defun +cc/imenu ()

View file

@ -1,22 +1,22 @@
;;; lang/clojure/config.el -*- lexical-binding: t; -*-
;;;###package clojure-mode
(after! clojure-mode
(add-hook 'clojure-mode-hook #'rainbow-delimiters-mode)
(set-repl-handler! 'clojure-mode #'+clojure/repl)
(set-eval-handler! 'clojure-mode #'cider-eval-region))
(use-package! cider
;; NOTE: if you don't have an org directory set (the dir doesn't exist),
;; cider jack in won't work.
:commands (cider-jack-in cider-jack-in-clojurescript)
:commands cider-jack-in cider-jack-in-clojurescript
:hook (clojure-mode-local-vars . cider-mode)
:init
(set-repl-handler! 'clojure-mode #'+clojure/repl)
(set-eval-handler! 'clojure-mode #'cider-eval-region)
:config
(add-hook 'cider-mode-hook #'eldoc-mode)
(set-lookup-handlers! 'cider-mode
:definition #'+clojure-cider-lookup-definition
:documentation #'cider-doc)
(add-hook 'cider-mode-hook #'eldoc-mode)
:config
(set-popup-rules!
'(("^\\*cider-error*" :ignore t)
("^\\*cider-repl" :quit nil)
@ -134,10 +134,9 @@
(use-package! clj-refactor
:hook (clojure-mode . clj-refactor-mode)
:init
:config
(set-lookup-handlers! 'clj-refactor-mode
:references #'cljr-find-usages)
:config
(map! :map clojure-mode-map
:localleader
:desc "refactor" "R" #'hydra-cljr-help-menu/body))

View file

@ -1,4 +1,15 @@
;;; lang/coq/autoload.el -*- lexical-binding: t; -*-
;; HACK `proof-general' ascertains its own library path at compile time in its
;; autoloads file using `byte-compile-current-file' (and stores it in
;; `pg-init--script-full-path'). This means that when
;; `doom-package-autoload-file' is created and byte-compiled,
;; `pg-init--script-full-path' will be wrong, causing file-missing errors as it
;; tries to load `proof-site'. We prevent this by defining these two variables
;; early, in our own autoloads file.
;;;###autoload
(setq pg-init--script-full-path (locate-library "proof-general")
pg-init--pg-root (file-name-directory pg-init--script-full-path))
;;;###autoload
(add-hook 'coq-mode-hook #'company-coq-mode)

View file

@ -7,6 +7,45 @@
;; library included with Doom).
(setq coq-mode-abbrev-table '())
(map! :after coq-mode
:map coq-mode-map
:localleader
"]" #'proof-assert-next-command-interactive
"[" #'proof-undo-last-successful-command
"." #'proof-goto-point
(:prefix ("l" . "layout")
"c" #'pg-response-clear-displays
"l" #'proof-layout-windows
"p" #'proof-prf)
(:prefix ("p" . "proof")
"i" #'proof-interrupt-process
"p" #'proof-process-buffer
"q" #'proof-shell-exit
"r" #'proof-retract-buffer)
(:prefix ("a" . "about/print/check")
"a" #'coq-Print
"A" #'coq-Print-with-all
"b" #'coq-About
"B" #'coq-About-with-all
"c" #'coq-Check
"C" #'coq-Check-show-all
"f" #'proof-find-theorems
(:prefix ("i" . "implicits")
"b" #'coq-About-with-implicits
"c" #'coq-Check-show-implicits
"i" #'coq-Print-with-implicits))
(:prefix ("g" . "goto")
"e" #'proof-goto-command-end
"l" #'proof-goto-end-of-locked
"s" #'proof-goto-command-start)
(:prefix ("i" . "insert")
"c" #'coq-insert-command
"e" #'coq-end-Section
"i" #'coq-insert-intros
"r" #'coq-insert-requires
"s" #'coq-insert-section-or-module
"t" #'coq-insert-tactic
"T" #'coq-insert-tactical))
(after! company-coq
(set-popup-rule! "^\\*\\(?:response\\|goals\\)\\*" :ignore t)
@ -15,4 +54,15 @@
:references #'company-coq-grep-symbol
:documentation #'company-coq-doc)
(unless (featurep! :completion company)
(setq company-coq-disabled-features '(company company-defaults))))
(setq company-coq-disabled-features '(company company-defaults)))
(map! :map coq-mode-map
:localleader
(:prefix ("i" . "insert")
"l" #'company-coq-lemma-from-goal
"m" #'company-coq-insert-match-construct)
"ao" #'company-coq-occur
(:prefix ("h" . "help")
"e" #'company-coq-document-error
"E" #'company-coq-browse-error-messages
"h" #'company-coq-doc)))

View file

@ -1,28 +1,39 @@
#+TITLE: :lang csharp
This module adds C# support to Emacs.
#+begin_quote
I don't use C# for much else than Unity3D and, seldomly, for Mono game
development on Linux.
#+end_quote
* Table of Contents :TOC:
- [[Install][Install]]
- [[MacOS][MacOS]]
- [[Arch Linux][Arch Linux]]
- [[#description][Description]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#prerequisites][Prerequisites]]
- [[#macos][MacOS]]
- [[#arch-linux][Arch Linux]]
- [[#nixos][NixOS]]
* Install
* Description
This module adds C# support to Emacs. Powered by omnisharp (directly or through
LSP).
** Module Flags
+ =+lsp= Enables omnisharp through LSP support (requires omnisharp).
+ =+unity= Enables special support for the [[https://unity.com/][Unity game engine]] (particularly,
support for HLSL shaders).
** Plugins
+ [[https://github.com/josteink/csharp-mode][csharp-mode]]
+ [[https://github.com/OmniSharp/omnisharp-emacs][omnisharp]]* (not =+lsp=)
+ [[https://github.com/midnightSuyama/shader-mode][shader-mode]]* (=+unity=)
* Prerequisites
This module needs:
+ omnisharp-roslyn (install with ~M-x omnisharp-install-server~)
+ omnisharp (with the ~+lsp~ flag, this must be installed externally. Without
it, use ~M-x omnisharp-install-server~)
+ .NET SDKs (on Windows)
+ Mono (on UNIX platforms)
** MacOS
=TODO=
** TODO MacOS
** Arch Linux
#+BEGIN_SRC sh :dir /sudo:: :tangle (if (doom-system-os 'arch) "yes")
#+BEGIN_SRC sh
sudo pacman --needed --noconfirm -S mono
#+END_SRC
** TODO NixOS

View file

@ -3,6 +3,9 @@
(after! csharp-mode
(add-hook 'csharp-mode-hook #'rainbow-delimiters-mode)
(when (featurep! +lsp)
(add-hook 'csharp-mode-local-vars-hook #'lsp!))
(set-electric! 'csharp-mode :chars '(?\n ?\}))
(set-rotate-patterns! 'csharp-mode
:symbols '(("public" "protected" "private")
@ -13,6 +16,7 @@
(use-package! omnisharp
:unless (featurep! +lsp)
:hook (csharp-mode . omnisharp-mode)
:commands omnisharp-install-server
:preface
@ -56,9 +60,10 @@
"b" #'omnisharp-unit-test-buffer)))
;;;###package shader-mode
(when (featurep! +unity)
;; `shader-mode' --- unity shaders
(add-to-list 'auto-mode-alist '("\\.shader$" . shader-mode))
;; Unity shaders
(add-to-list 'auto-mode-alist '("\\.shader\\'" . shader-mode))
(def-project-mode! +csharp-unity-mode
:modes '(csharp-mode shader-mode)

View file

@ -2,7 +2,9 @@
;;; lang/csharp/packages.el
(package! csharp-mode)
(package! omnisharp)
(unless (featurep! +lsp)
(package! omnisharp))
(when (featurep! +unity)
(package! shader-mode))

View file

@ -18,7 +18,7 @@
;;
;;; Third-party plugins
;; `csv-mode'
;;;###package csv-mode
(map! :after csv-mode
:localleader
:map csv-mode-map
@ -37,13 +37,12 @@
:config
(set-electric! 'json-mode :chars '(?\n ?: ?{ ?})))
(use-package! jsonnet-mode
:defer t
:config
(after! jsonnet-mode
(set-electric! 'jsonnet-mode :chars '(?\n ?: ?{ ?})))
;;
;; Frameworks
;;; Frameworks
(def-project-mode! +data-vagrant-mode
:files ("Vagrantfile"))

View file

@ -11,6 +11,7 @@
- [[#with-asdf][With ~asdf~]]
- [[#arch-linux][Arch Linux]]
- [[#gentoo-linux][Gentoo Linux]]
- [[#opensuse][openSUSE]]
- [[#features][Features]]
* Description
@ -47,6 +48,11 @@ sudo pacman -S elixir
#+BEGIN_SRC sh :dir /sudo::
sudo emerge -v dev-lang/elixir
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install elixir
#+END_SRC
* Features
- Code completion (~:completion company~)
- Documentation lookup (~:tools lookup~)

View file

@ -32,18 +32,6 @@
(when (featurep! +lsp)
(add-hook 'elixir-mode-local-vars-hook #'lsp!))
(use-package! alchemist-company
:when (featurep! :completion company)
:commands alchemist-company
:init
(set-company-backend! 'elixir-mode '(alchemist-company company-yasnippet))
:config
;; Alchemist doesn't use hook symbols to add these backends, so we have to
;; use the entire closure to get rid of it.
(let ((fn (byte-compile (lambda () (add-to-list (make-local-variable 'company-backends) 'alchemist-company)))))
(remove-hook 'alchemist-mode-hook fn)
(remove-hook 'alchemist-iex-mode-hook fn)))
(use-package! flycheck-credo
:when (featurep! :tools flycheck)
:config (flycheck-credo-setup)))
@ -51,9 +39,24 @@
(use-package! alchemist
:hook (elixir-mode . alchemist-mode)
:config
:init
(after! elixir-mode
(set-lookup-handlers! 'elixir-mode
:definition #'alchemist-goto-definition-at-point
:documentation #'alchemist-help-search-at-point)
(set-eval-handler! 'elixir-mode #'alchemist-eval-region)
(set-repl-handler! 'elixir-mode #'alchemist-iex-project-run))
(set-repl-handler! 'elixir-mode #'alchemist-iex-project-run)))
(use-package! alchemist-company
:when (featurep! :completion company)
:commands alchemist-company
:init
(after! elixir-mode
(set-company-backend! 'elixir-mode '(alchemist-company company-yasnippet)))
:config
;; Alchemist doesn't use hook symbols to add these backends, so we have to use
;; the entire closure to get rid of it.
(let ((fn (byte-compile (lambda () (add-to-list (make-local-variable 'company-backends) 'alchemist-company)))))
(remove-hook 'alchemist-mode-hook fn)
(remove-hook 'alchemist-iex-mode-hook fn)))

View file

@ -1,8 +1,5 @@
;;; lang/elm/config.el -*- lexical-binding: t; -*-
;; `elm-mode'
(setq elm-format-on-save t)
(after! elm-mode
(add-hook 'elm-mode-hook #'rainbow-delimiters-mode)

View file

@ -135,7 +135,7 @@ if it's callable, `apropos' otherwise."
`(("Section" "^[ \t]*;;;;*[ \t]+\\([^\n]+\\)" 1)
("Evil commands" "^\\s-*(evil-define-\\(?:command\\|operator\\|motion\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1)
("Unit tests" "^\\s-*(\\(?:ert-deftest\\|describe\\) +\"\\([^\")]+\\)\"" 1)
("Package" "^\\s-*(\\(?:;;;###package\\|def-package!\\|package!\\|use-package\\|after!\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1)
("Package" "^\\s-*(\\(?:;;;###package\\|package!\\|use-package!?\\|after!\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1)
("Major modes" "^\\s-*(define-derived-mode +\\([^ ()\n]+\\)" 1)
("Minor modes" "^\\s-*(define-\\(?:global\\(?:ized\\)?-minor\\|generic\\|minor\\)-mode +\\([^ ()\n]+\\)" 1)
("Modelines" "^\\s-*(def-modeline! +\\([^ ()\n]+\\)" 1)
@ -174,3 +174,15 @@ verbosity when editing a file in `doom-private-dir' or `doom-emacs-dir'."
" "
(default-value 'flycheck-emacs-lisp-check-form)
")"))))
;;;###autoload
(defun +emacs-lisp/edebug-instrument-defun-on ()
"Toggle on instrumentalisation for the function under `defun'."
(interactive)
(eval-defun 'edebugit))
;;;###autoload
(defun +emacs-lisp/edebug-instrument-defun-off ()
"Toggle off instrumentalisation for the function under `defun'."
(interactive)
(eval-defun nil))

View file

@ -77,8 +77,10 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.")
(map! :localleader
:map emacs-lisp-mode-map
"e" #'macrostep-expand))
"e" #'macrostep-expand
(:prefix ("d" . "debug")
("f" #'+emacs-lisp/edebug-instrument-defun-on)
("F" #'+emacs-lisp/edebug-instrument-defun-off))))
;;
;;; Packages
@ -112,8 +114,20 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.")
"Add Doom's own demos to help buffers."
:around #'elisp-demos--search
(or (funcall orig-fn symbol)
(when-let* ((elisp-demos--elisp-demos.org (doom-glob doom-docs-dir "api.org")))
(funcall orig-fn symbol)))))
(when-let (demos-file (doom-glob doom-docs-dir "api.org"))
(with-temp-buffer
(insert-file-contents demos-file)
(goto-char (point-min))
(when (re-search-forward
(format "^\\*\\*\\* %s$" (regexp-quote (symbol-name symbol)))
nil t)
(let (beg end)
(forward-line 1)
(setq beg (point))
(if (re-search-forward "^\\*" nil t)
(setq end (line-beginning-position))
(setq end (point-max)))
(string-trim (buffer-substring-no-properties beg end)))))))))
(use-package! buttercup

View file

@ -1,9 +1,9 @@
;;; lang/erlang/config.el -*- lexical-binding: t; -*-
(use-package! erlang
:mode ("\\.erlang$" . erlang-mode)
:mode ("/rebar\\.config\\(?:\\.script\\)?$" . erlang-mode)
:mode ("/\\(?:app\\|sys\\)\\.config$" . erlang-mode))
:mode ("\\.erlang\\'" . erlang-mode)
:mode ("/rebar\\.config\\(?:\\.script\\)?\\'" . erlang-mode)
:mode ("/\\(?:app\\|sys\\)\\.config\\'" . erlang-mode))
(use-package! flycheck-rebar3

View file

@ -1,7 +1,7 @@
;;; lang/ess/config.el -*- lexical-binding: t; -*-
(use-package! ess
:commands (stata SAS)
:commands stata SAS
:init
(setq ess-smart-S-assign-key nil)
(unless (featurep! :lang julia)

View file

@ -4,15 +4,15 @@
#+STARTUP: inlineimages
* Table of Contents :TOC:
- [[Description][Description]]
- [[Module Flags][Module Flags]]
- [[Plugins][Plugins]]
- [[Prerequisites][Prerequisites]]
- [[Go][Go]]
- [[Dependencies][Dependencies]]
- [[Features][Features]]
- [[Configuration][Configuration]]
- [[Troubleshooting][Troubleshooting]]
- [[#description][Description]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#prerequisites][Prerequisites]]
- [[#go][Go]]
- [[#dependencies][Dependencies]]
- [[#features][Features]]
- [[#configuration][Configuration]]
- [[#troubleshooting][Troubleshooting]]
* Description
This module adds [[https://golang.org][Go]] support.
@ -35,6 +35,7 @@ This module provides no flags.
+ [[https://github.com/syohex/emacs-go-eldoc][go-eldoc]]
+ [[https://github.com/dominikh/go-mode.el][go-guru]]
+ [[https://github.com/manute/gorepl-mode][gorepl-mode]]
+ [[https://github.com/syohex/emacs-go-add-tags][go-add-tags]]
+ [[https://github.com/mdempsky/gocode][company-go]]*
* Prerequisites
@ -51,6 +52,11 @@ brew install go
sudo pacman -S go
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install go
#+END_SRC
** Dependencies
This module requires a valid ~GOPATH~, and the following Go packages:

View file

@ -6,17 +6,21 @@
(defvar +go-test-last nil
"The last test run.")
(defun +go--run-tests (args)
(require 'async)
(defun +go--spawn (cmd)
(save-selected-window
(async-shell-command (concat "go test " args))))
(compile cmd)))
(defun +go--run-tests (args)
(let ((cmd (concat "go test " args)))
(setq +go-test-last (concat "cd " default-directory ";" cmd))
(+go--spawn cmd)))
;;;###autoload
(defun +go/test-rerun ()
(interactive)
(if +go-test-last
(funcall +go-test-last)
(+go/run-all-tests)))
(+go--spawn +go-test-last)
(+go/test-all)))
;;;###autoload
(defun +go/test-all ()

View file

@ -1,7 +1,7 @@
;;; lang/go/config.el -*- lexical-binding: t; -*-
;;
;; Packages
;;; Packages
(after! go-mode
(set-docsets! 'go-mode "Go")
@ -25,6 +25,7 @@
(map! :map go-mode-map
:localleader
"a" #'go-add-tags
"e" #'+go/play-buffer-or-region
"i" #'go-goto-imports ; Go to imports
(:prefix ("h" . "help")
@ -58,8 +59,8 @@
(use-package! company-go
:when (and (featurep! :completion company)
(not (featurep! +lsp)))
:when (featurep! :completion company)
:unless (featurep! +lsp)
:after go-mode
:config
(set-company-backend! 'go-mode 'company-go)

View file

@ -5,6 +5,7 @@
(package! go-guru)
(package! go-mode)
(package! gorepl-mode)
(package! go-add-tags)
(when (featurep! :completion company)
(package! company-go))

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