diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7d04c1f39..e189bc259 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -16,9 +16,9 @@ jobs:
strategy:
matrix:
emacs_version:
- - 25.3
- 26.1
- - snapshot
+ - 26.2
+ - 26.3
include:
- emacs_version: 26.3
lint_ignore: 1
diff --git a/README.md b/README.md
index 7645d96e9..4ad52f31c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
diff --git a/bin/doom b/bin/doom
index bd381e5ab..834e56afd 100755
--- a/bin/doom
+++ b/bin/doom
@@ -1,105 +1,79 @@
#!/usr/bin/env sh
-":"; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*-
-":"; command -v $EMACS >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; }
-":"; 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 Doom. Check your PATH"; echo; exit 2 ;; esac
-":"; DOOMBASE=$(dirname "$0")/..
-":"; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; }
-":"; [ "$1" = doc ] || [ "$1" = doctor ] && { cd "$DOOMBASE"; shift; exec $EMACS --script bin/doom-doctor "$@"; exit 0; }
-":"; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; }
-":"; exec $EMACS --script "$0" -- "$@"
-":"; exit 0
+:; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*-
+:; command -v $EMACS >/dev/null || { >&2 echo "Can't find emacs in your PATH"; exit 1; }
+:; VERSION=$($EMACS --version | head -n1)
+:; case "$VERSION" in *\ 2[0-5].[0-9]) echo "Detected Emacs $VERSION"; echo "Doom only supports Emacs 26.1 and newer"; echo; exit 2 ;; esac
+:; DOOMBASE="$(dirname "$0")/.."
+:; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; }
+:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; }
+:; exec $EMACS --script "$0" -- "$@"
+:; exit 0
-(defconst user-emacs-directory
- (or (getenv "EMACSDIR")
- (expand-file-name "../" (file-name-directory (file-truename load-file-name)))))
+(let* ((loaddir (file-name-directory (file-truename load-file-name)))
+ (emacsdir (getenv "EMACSDIR"))
+ (user-emacs-directory (or emacsdir (expand-file-name "../" loaddir)))
+ (load-prefer-newer t))
-(defun usage ()
- (with-temp-buffer
- (insert (format! "%s %s [COMMAND] [ARGS...]\n"
- (bold "Usage:")
- (file-name-nondirectory load-file-name))
- "\n"
- "A command line interface for managing Doom Emacs; including\n"
- "package management, diagnostics, unit tests, and byte-compilation.\n"
- "\n"
- "This tool also makes it trivial to launch Emacs out of a different\n"
- "folder or with a different private module.\n"
- "\n"
- (format! (bold "Example:\n"))
- " doom install\n"
- " doom help update\n"
- " doom compile :core lang/php lang/python\n"
- " doom run\n"
- " doom run -nw file.txt file2.el\n"
- " doom run -p ~/.other.doom.d -e ~/.other.emacs.d -nw file.txt\n"
- "\n"
- (format! (bold "Options:\n"))
- " -h --help\t\tSame as help command\n"
- " -d --debug\t\tTurns on doom-debug-mode (and debug-on-error)\n"
- " -e --emacsd DIR\tUse the emacs config at DIR (e.g. ~/.emacs.d)\n"
- " -i --insecure\t\tDisable TLS/SSL validation (not recommended)\n"
- " -l --local DIR\tUse DIR as your local storage directory\n"
- " -p --private DIR\tUse the private module at DIR (e.g. ~/.doom.d)\n"
- " -y --yes\t\tAuto-accept all confirmation prompts\n\n")
- (princ (buffer-string)))
- (doom--dispatch-help))
+ (push (expand-file-name "core" user-emacs-directory) load-path)
+ (require 'core)
+ (require 'core-cli)
-;;
-(let ((args (cdr (cdr (cdr command-line-args)))))
- ;; Parse options
- (while (ignore-errors (string-prefix-p "-" (car args)))
- (pcase (pop args)
- ((or "-h" "--help")
- (push "help" args))
- ((or "-d" "--debug")
- (setenv "DEBUG" "1")
- (message "Debug mode on"))
- ((or "-i" "--insecure")
- (setenv "INSECURE" "1")
- (message "Insecure mode on"))
- ((or "-p" "--private")
- (setq doom-private-dir (expand-file-name (concat (pop args) "/")))
- (setenv "DOOMDIR" doom-private-dir)
- (message "DOOMDIR changed to %s" doom-private-dir)
- (or (file-directory-p doom-private-dir)
- (message "Warning: %s does not exist"
- (abbreviate-file-name doom-private-dir))))
- ((or "-l" "--local")
- (setq doom-local-dir (expand-file-name (concat (pop args) "/")))
- (setenv "DOOMLOCALDIR" doom-local-dir)
- (message "DOOMLOCALDIR changed to %s" doom-local-dir))
- ((or "-e" "--emacsd")
- (setq user-emacs-directory (expand-file-name (concat (pop args) "/")))
- (message "Emacs directory changed to %s" user-emacs-directory))
- ((or "-y" "--yes")
- (setenv "YES" "1")
- (message "Auto-yes mode on"))))
+ (defcli! :main
+ ((help-p ["-h" "--help"] "Same as help command")
+ (debug-p ["-d" "--debug"] "Turns on doom-debug-mode (and debug-on-error)")
+ (yes-p ["-y" "--yes"] "Auto-accept all confirmation prompts")
+ (emacsdir ["--emacsdir" dir] "Use the emacs config at DIR (e.g. ~/.emacs.d)")
+ (doomdir ["--doomdir" dir] "Use the private module at DIR (e.g. ~/.doom.d)")
+ (localdir ["--localdir" dir] "Use DIR as your local storage directory")
+ &optional command &rest args)
+ "A command line interface for managing Doom Emacs.
- (unless (file-directory-p user-emacs-directory)
- (error "%s does not exist" user-emacs-directory))
+Includes package management, diagnostics, unit tests, and byte-compilation.
- ;; Bootstrap Doom
- (if (not noninteractive)
- (let ((doom-interactive-mode t))
- (load (expand-file-name "init.el" user-emacs-directory)
- nil 'nomessage)
- (doom-run-all-startup-hooks-h))
- (load (expand-file-name "core/core.el" user-emacs-directory)
- nil 'nomessage)
- (doom-initialize 'force-p)
- (doom-initialize-modules)
+This tool also makes it trivial to launch Emacs out of a different folder or
+with a different private module."
+ :bare t
+ (when emacsdir
+ (setq user-emacs-directory (file-name-as-directory emacsdir))
+ (print! (info "EMACSDIR=%s") localdir))
+ (when doomdir
+ (setenv "DOOMDIR" doomdir)
+ (print! (info "DOOMDIR=%s") localdir))
+ (when localdir
+ (setenv "DOOMLOCALDIR" localdir)
+ (print! (info "DOOMLOCALDIR=%s") localdir))
+ (when debug-p
+ (setenv "DEBUG" "1")
+ (setq doom-debug-mode t)
+ (print! (info "Debug mode on")))
+ (when yes-p
+ (setenv "YES" "1")
+ (setq doom-auto-accept t)
+ (print! (info "Auto-yes on")))
+ (when help-p
+ (push command args)
+ (setq command "help"))
- (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)
- (setq argv nil)
- (condition-case e
- (doom-dispatch (car args) (cdr args))
+ ;; Reload core in case any of the directories were changed.
+ (when (or emacsdir doomdir localdir)
+ (load! "core/core.el" user-emacs-directory))
+
+ (cond ((not noninteractive)
+ (print! "Doom launched out of %s (test mode)" (path user-emacs-directory))
+ (load! "init.el" user-emacs-directory)
+ (doom-run-all-startup-hooks-h))
+
+ ((null command)
+ (doom-cli-execute "help"))
+
+ ((condition-case e
+ (let ((start-time (current-time)))
+ (and (doom-cli-execute command args)
+ (terpri)
+ (print! (success "Finished! (%.4fs)")
+ (float-time
+ (time-subtract (current-time)
+ start-time)))))
(user-error
(print! (error "%s\n") (error-message-string e))
(print! (yellow "See 'doom help %s' for documentation on this command.") (car args)))
@@ -116,5 +90,8 @@
"report, please include it!\n\n"
"Emacs outputs to standard error, so you'll need to redirect stderr to\n"
"stdout to pipe this to a file or clipboard!\n\n"
- " e.g. doom -d install 2>&1 | clipboard-program"))
- (signal 'doom-error e))))))))
+ " e.g. doom -d install 2>&1 | clipboard-program\n"))
+ (signal 'doom-error e)))))))
+
+ (doom-cli-execute :main (cdr (member "--" argv)))
+ (setq argv nil))
diff --git a/bin/doom-doctor b/bin/doom-doctor
deleted file mode 100755
index 701c22707..000000000
--- a/bin/doom-doctor
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/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 (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
-;; that uses a series of simple heuristics to diagnose common issues on your
-;; system. Issues that could intefere with Doom Emacs.
-;;
-;; Doom modules may optionally have a doctor.el file to run their own heuristics
-;; in. Doctor scripts may run in versions of Emacs as old as Emacs 23, so make
-;; no assumptions about what's available in the standard library (e.g. avoid
-;; cl/cl-lib, subr-x, map, seq, etc).
-
-
-;; Ensure Doom doctor always runs out of the current Emacs directory (optionally
-;; specified by the EMACSDIR envvar)
-(setq user-emacs-directory
- (or (getenv "EMACSDIR")
- (expand-file-name "../" (file-name-directory (file-truename load-file-name))))
- default-directory user-emacs-directory)
-
-(unless (file-directory-p user-emacs-directory)
- (error "Couldn't find a Doom config!"))
-(unless noninteractive
- (error "This script must not be run from an interactive session."))
-(when (getenv "DEBUG")
- (setq debug-on-error t))
-
-(require 'subr-x)
-(require 'pp)
-(load (expand-file-name "core/autoload/format" user-emacs-directory) nil t)
-
-
-(defvar doom-init-p nil)
-(defvar doom-warnings 0)
-(defvar doom-errors 0)
-
-
-;;; Helpers
-
-(defun sh (cmd &rest args)
- (ignore-errors
- (string-trim-right
- (shell-command-to-string (if args (apply #'format cmd args) cmd)))))
-
-(defun elc-check-dir (dir)
- (dolist (file (directory-files-recursively dir "\\.elc$"))
- (when (file-newer-than-file-p (concat (file-name-sans-extension file) ".el")
- file)
- (warn! "%s is out-of-date" (abbreviate-file-name file)))))
-
-(defmacro assert! (condition message &rest args)
- `(unless ,condition
- (error! ,message ,@args)))
-
-
-;;; Logging
-
-(defvar indent 0)
-(defvar prefix "")
-
-(defmacro msg! (msg &rest args)
- `(print!
- (indent indent
- (format (concat prefix ,msg)
- ,@args))))
-
-(defmacro error! (&rest args)
- `(progn (msg! (red ,@args))
- (setq doom-errors (+ doom-errors 1))))
-(defmacro warn! (&rest args)
- `(progn (msg! (yellow ,@args))
- (setq doom-warnings (+ doom-warnings 1))))
-(defmacro success! (&rest args) `(msg! (green ,@args)))
-(defmacro section! (&rest args) `(msg! (bold (blue ,@args))))
-
-(defmacro explain! (&rest args)
- `(msg! (indent (+ indent 2) (autofill ,@args))))
-
-
-;;; Polyfills
-;; early versions of emacs won't have this
-(unless (fboundp 'string-match-p)
- (defun string-match-p (regexp string &optional start)
- (save-match-data
- (string-match regexp string &optional start))))
-
-;; subr-x don't exist in older versions of Emacs
-(unless (fboundp 'string-trim-right)
- (defsubst string-trim-right (string &optional regexp)
- (if (string-match (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'") string)
- (replace-match "" t t string)
- string)))
-
-
-;;
-;;; Basic diagnostics
-
-(msg! (bold "Doom Doctor"))
-(msg! "Emacs v%s" emacs-version)
-(msg! "Doom v%s (%s)"
- (or (let ((core-file (expand-file-name "core/core.el" user-emacs-directory)))
- (and (file-exists-p core-file)
- (ignore-errors
- (with-temp-buffer
- (insert-file-contents-literally core-file)
- (goto-char (point-min))
- (when (search-forward "doom-version" nil t)
- (forward-char)
- (sexp-at-point))))))
- "???")
- (if (and (executable-find "git")
- (file-directory-p (expand-file-name ".git" user-emacs-directory)))
- (sh "git log -1 --format=\"%D %h %ci\"")
- "n/a"))
-(msg! "shell: %s%s"
- (getenv "SHELL")
- (if (equal (getenv "SHELL") (sh "echo $SHELL"))
- ""
- (red " (mismatch)")))
-(when (boundp 'system-configuration-features)
- (msg! "Compiled with:\n%s" (indent 2 system-configuration-features)))
-(msg! "uname -msrv:\n%s\n" (indent 2 (sh "uname -msrv")))
-
-
-;;
-;;; Check if Emacs is set up correctly
-
-(section! "Checking Emacs")
-(let ((indent 2))
- (section! "Checking your Emacs version is 25.3 or newer...")
- (when (version< emacs-version "25.3")
- (error! "Important: Emacs %s detected [%s]" emacs-version (executable-find "emacs"))
- (explain!
- "DOOM only supports >= 25.3. Perhaps your PATH wasn't set up properly."
- (when (eq system-type 'darwin)
- (concat "\nMacOS users should use homebrew (https://brew.sh) to install Emacs\n"
- " brew install emacs --with-modules --with-imagemagick --with-cocoa"))))
-
- (section! "Checking for Emacs config conflicts...")
- (when (file-exists-p "~/.emacs")
- (warn! "Detected an ~/.emacs file, which may prevent Doom from loading")
- (explain! "If Emacs finds an ~/.emacs file, it will ignore ~/.emacs.d, where Doom is "
- "typically installed. If you're seeing a vanilla Emacs splash screen, this "
- "may explain why. If you use Chemacs, you may ignore this warning."))
-
- (section! "Checking for private config conflicts...")
- (let ((xdg-dir (concat (or (getenv "XDG_CONFIG_HOME")
- "~/.config")
- "/doom/"))
- (doom-dir (or (getenv "DOOMDIR")
- "~/.doom.d/")))
- (when (and (not (file-equal-p xdg-dir doom-dir))
- (file-directory-p xdg-dir)
- (file-directory-p doom-dir))
- (warn! "Detected two private configs, in %s and %s"
- (abbreviate-file-name xdg-dir)
- doom-dir)
- (explain! "The second directory will be ignored, as it has lower precedence.")))
-
- (section! "Checking for stale elc files...")
- (elc-check-dir user-emacs-directory))
-
-
-;;
-;;; Check if system environment is set up correctly
-
-(section! "Checking your system...")
-(let ((indent 2))
- ;; on windows?
- (when (memq system-type '(windows-nt ms-dos cygwin))
- (warn! "Warning: Windows detected")
- (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")))
-
-
-;;
-;;; Check if Doom Emacs is set up correctly
-
-(condition-case-unless-debug ex
- (let ((after-init-time (current-time))
- (doom-format-backend 'ansi)
- noninteractive)
- (section! "Checking DOOM Emacs...")
- (load (concat user-emacs-directory "core/core.el") nil t)
- (unless (file-directory-p doom-private-dir)
- (error "No DOOMDIR was found, did you run `doom install` yet?"))
-
- (let ((indent 2))
- ;; Make sure Doom is initialized and loaded
- (doom-initialize 'force)
- (doom-initialize-core)
- (success! "Initialized Doom Emacs %s" doom-version)
-
- (doom-initialize-modules)
- (if (hash-table-p doom-modules)
- (success! "Initialized %d modules" (hash-table-count doom-modules))
- (warn! "Failed to load any modules. Do you have an private init.el?"))
-
- (doom-initialize-packages)
- (success! "Initialized %d packages" (length doom-packages))
-
- (section! "Checking Doom core for irregularities...")
- (let ((indent (+ indent 2)))
- (load (expand-file-name "doctor.el" doom-core-dir) nil 'nomessage))
-
- (section! "Checking for stale elc files in your DOOMDIR...")
- (when (file-directory-p doom-private-dir)
- (let ((indent (+ indent 2)))
- (elc-check-dir doom-private-dir)))
-
- (when doom-modules
- (section! "Checking your enabled modules...")
- (let ((indent (+ indent 2)))
- (advice-add #'require :around #'doom-shut-up-a)
- (maphash
- (lambda (key plist)
- (let ((prefix (format! (bold "(%s %s) " (car key) (cdr key)))))
- (condition-case-unless-debug ex
- (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el"))
- (packages-file (doom-module-path (car key) (cdr key) "packages.el")))
- (cl-loop for name in (let (doom-packages
- doom-disabled-packages)
- (load packages-file 'noerror 'nomessage)
- (mapcar #'car doom-packages))
- unless (or (doom-package-get name :disable)
- (eval (doom-package-get name :ignore))
- (doom-package-built-in-p name)
- (doom-package-installed-p name))
- do (error! "%s is not installed" name))
- (load doctor-file 'noerror 'nomessage))
- (file-missing (error! "%s" (error-message-string ex)))
- (error (error! "Syntax error: %s" ex)))))
- doom-modules)))))
- (error
- (warn! "Attempt to load DOOM failed\n %s\n"
- (or (cdr-safe ex) (car ex)))
- (setq doom-modules nil)))
-
-
-;;
-;;; Final report
-
-(message "")
-(dolist (msg (list (list doom-errors "error" 'red)
- (list doom-warnings "warning" 'yellow)))
- (when (> (car msg) 0)
- (msg! (color (nth 2 msg)
- (if (= (car msg) 1)
- "There is %d %s!"
- "There are %d %ss!")
- (car msg) (nth 1 msg)))))
-
-(when (and (zerop doom-errors)
- (zerop doom-warnings))
- (success! "Everything seems fine, happy Emacs'ing!"))
diff --git a/core/autoload/buffers.el b/core/autoload/buffers.el
index f393a570d..4f355417a 100644
--- a/core/autoload/buffers.el
+++ b/core/autoload/buffers.el
@@ -289,6 +289,7 @@ belong to the current project."
(when (memq (current-buffer) buffer-list)
(switch-to-buffer (doom-fallback-buffer)))
(mapc #'doom-kill-buffer-and-windows buffer-list)
+ (delete-other-windows)
(when interactive
(message "Killed %s buffers"
(- (length buffer-list)
diff --git a/core/autoload/cli.el b/core/autoload/cli.el
index f547a289b..0c455f741 100644
--- a/core/autoload/cli.el
+++ b/core/autoload/cli.el
@@ -124,89 +124,3 @@ Warning: freezes indefinitely on any stdin prompt."
(sit-for 0.1))
(process-exit-status process))
(string-trim (buffer-string)))))
-
-(defun doom--cli-normalize (args specs)
- (let* ((args (cl-remove-if-not #'stringp args))
- (optspec (cl-remove-if-not #'listp specs))
- (argspec (cl-remove-if #'listp specs))
- (options (mapcar #'list (mapcar #'car-safe optspec)))
- extra
- arguments)
- (dolist (spec optspec)
- (setf (nth 1 spec) (doom-enlist (nth 1 spec))))
- (while args
- (let ((arg (pop args)))
- (cl-check-type arg string)
- (if (not (string-prefix-p "-" arg))
- (push arg arguments)
- (if-let (specs (cl-remove-if-not
- (if (string-prefix-p "--" arg)
- (doom-partial #'member arg)
- (lambda (flags)
- (cl-loop for switch in (split-string (string-remove-prefix "-" arg) "" t)
- if (member (concat "-" switch) flags)
- return t)))
- optspec
- :key #'cadr))
- (pcase-dolist (`(,sym ,flags ,type) specs)
- (setf (alist-get sym options)
- (list
- (let ((value (if type (pop args))))
- (pcase type
- (`&string value)
- (`&int `(truncate (read ,value)))
- (`&float `(float (read ,value)))
- (`&path `(expand-file-name ,value))
- (`&directory
- `(let ((path (expand-file-name ,value)))
- (unless (file-directory-p path)
- (error "Directory does not exist: %s" path))
- path))
- (`&file
- `(let ((path (expand-file-name ,value)))
- (unless (file-exists-p path)
- (error "File does not exist: %s" path))
- path))
- (`&sexp `(read ,value))
- ((or `nil `t) arg)
- (_ (error "Not a valid type: %S" type)))))))
- (push arg extra)))))
- (list optspec (nreverse options)
- argspec (nreverse arguments))))
-
-;;;###autoload
-(defun doom-cli-getopts (args specs)
- "TODO"
- (cl-destructuring-bind (optspec options argspec arguments)
- (doom--cli-normalize args specs)
- (let ((i 0)
- optional-p
- noerror-p)
- (cl-dolist (spec argspec)
- (cond ((eq spec '&rest)
- (push (list (cadr (member '&rest specs))
- `(quote
- ,(reverse
- (butlast (reverse arguments) i))))
- options)
- (cl-return))
- ((eq spec '&all)
- (push (list (cadr (member '&all specs))
- `(quote ,args))
- options))
- ((eq spec '&noerror) (setq noerror-p t))
- ((eq spec '&optional) (setq optional-p t))
- ((and (>= i (length arguments)) (not optional-p))
- (signal 'wrong-number-of-arguments
- (list argspec (length arguments))))
- ((push (list spec (nth i arguments)) options)
- (cl-incf i)))))
- (nreverse options)))
-
-;;;###autoload
-(defmacro let-cliopts! (args spec &rest body)
- "Run BODY with command line ARGS parsed according to SPEC."
- (declare (indent 2))
- `(eval (append (list 'let (doom-cli-getopts ,args ',spec))
- (quote ,body))
- t))
diff --git a/core/autoload/debug.el b/core/autoload/debug.el
index f56eef252..14ae8fc00 100644
--- a/core/autoload/debug.el
+++ b/core/autoload/debug.el
@@ -8,10 +8,10 @@
emacs -Q -l init.el -f doom-run-all-startup-hooks-h"
(run-hook-wrapped 'after-init-hook #'doom-try-run-hook)
(setq after-init-time (current-time))
- (dolist (hook (list 'delayed-warnings-hook
- 'emacs-startup-hook 'term-setup-hook
- 'window-setup-hook))
- (run-hook-wrapped hook #'doom-try-run-hook)))
+ (mapc (doom-rpartial #'run-hook-wrapped #'doom-try-run-hook)
+ (list 'delayed-warnings-hook
+ 'emacs-startup-hook 'tty-setup-hook
+ 'window-setup-hook)))
;;
@@ -32,9 +32,8 @@ ready to be pasted in a bug report on github."
(doom-modules (doom-modules)))
(cl-letf
(((symbol-function 'sh)
- (lambda (format)
- (string-trim
- (shell-command-to-string format)))))
+ (lambda (&rest args)
+ (cdr (apply #'doom-call-process args)))))
`((emacs
(version . ,emacs-version)
(features ,@system-configuration-features)
@@ -47,14 +46,14 @@ ready to be pasted in a bug report on github."
'server-running))))
(doom
(version . ,doom-version)
- (build . ,(sh "git log -1 --format=\"%D %h %ci\"")))
+ (build . ,(sh "git" "log" "-1" "--format=%D %h %ci")))
(system
(type . ,system-type)
(config . ,system-configuration)
(shell . ,shell-file-name)
(uname . ,(if IS-WINDOWS
"n/a"
- (sh "uname -msrv")))
+ (sh "uname" "-msrv")))
(path . ,(mapcar #'abbreviate-file-name exec-path)))
(config
(envfile
@@ -117,7 +116,7 @@ branch and commit."
"n/a")
(or (vc-git-working-revision doom-core-dir)
"n/a")
- (or (string-trim (shell-command-to-string "git log -1 --format=%ci"))
+ (or (cdr (doom-call-process "git" "log" "-1" "--format=%ci"))
"n/a"))))
;;;###autoload
@@ -302,34 +301,6 @@ to reproduce bugs and determine if Doom is to blame."
;;
;;; Reporting bugs
-(defun doom--report-bug ()
- "TODO"
- (interactive)
- (let ((url "https://github.com/hlissner/doom-emacs/issues/new?body="))
- ;; TODO Refactor me
- (save-restriction
- (widen)
- (goto-char (point-min))
- (re-search-forward "^-------------------------------------------------------------------\n" nil t)
- (skip-chars-forward " \n\t")
- (condition-case e
- (progn
- (save-excursion
- (when (and (re-search-backward "\\+ [ ] " nil t)
- (not (y-or-n-p "You haven't checked all the boxes. Continue anyway?")))
- (error "Aborted submit")))
- (narrow-to-region (point) (point-max))
- (let ((text (buffer-string)))
- ;; `url-encode-url' doesn't encode ampersands
- (setq text (replace-regexp-in-string "&" "%26" text))
- (setq url (url-encode-url (concat url text)))
- ;; HACK: encode some characters according to HTML URL Encoding Reference
- ;; http://www.w3schools.com/tags/ref_urlencode.asp
- (setq url (replace-regexp-in-string "#" "%23" url))
- (setq url (replace-regexp-in-string ";" "%3B" url))
- (browse-url url)))
- (error (signal (car e) (car e)))))))
-
;;;###autoload
(defun doom/report-bug ()
"Open a markdown buffer destinated to populate the New Issue page on Doom
@@ -338,36 +309,7 @@ Emacs' issue tracker.
If called when a backtrace buffer is present, it and the output of `doom-info'
will be automatically appended to the result."
(interactive)
- ;; TODO Refactor me
- (let ((backtrace
- (when (get-buffer "*Backtrace*")
- (with-current-buffer "*Backtrace*"
- (string-trim
- (buffer-substring-no-properties
- (point-min)
- (min (point-max) 1000))))))
- (buf (get-buffer-create "*doom:sandbox*")))
- (with-current-buffer buf
- (erase-buffer)
- (condition-case _ (gfm-mode)
- (error (text-mode)))
- (doom-template-insert "SUBMIT_BUG_REPORT")
- (goto-char (point-max))
- (let ((pos (point)))
- (save-excursion
- (insert
- "\n" (doom-info) "\n"
- (if (and backtrace (not (string-empty-p backtrace)))
- (format "\n\nBacktrace
\n\n```\n%s\n```\n \n"
- backtrace)
- "")))
- (local-set-key (kbd "C-c C-c") #'doom--report-bug)
- (local-set-key (kbd "C-c C-k") #'kill-current-buffer)
- (setq header-line-format "C-c C-c to submit / C-c C-k to close")
- ;;
- (narrow-to-region (point-min) pos)
- (goto-char (point-min)))
- (pop-to-buffer buf))))
+ (browse-url "https://github.com/hlissner/doom-emacs/issues/new/choose"))
;;
diff --git a/core/autoload/files.el b/core/autoload/files.el
index 7365641f6..fd48fa0e7 100644
--- a/core/autoload/files.el
+++ b/core/autoload/files.el
@@ -169,8 +169,10 @@ single file or nested compound statement of `and' and `or' statements."
;;;###autoload
(defun doom-file-size (file &optional dir)
- "Returns the size of FILE (in DIR) in kilobytes."
- (when-let (file (file-exists-p! file dir))
+ "Returns the size of FILE (in DIR) in bytes."
+ (let ((file (expand-file-name file dir)))
+ (unless (file-exists-p file)
+ (error "Couldn't find file %S" file))
(unless (file-readable-p file)
(error "File %S is unreadable; can't acquire its filesize"
file))
@@ -179,6 +181,8 @@ single file or nested compound statement of `and' and `or' statements."
;;;###autoload
(defun doom-directory-size (dir)
"Returns the size of FILE (in DIR) in kilobytes."
+ (unless (file-directory-p dir)
+ (error "Directory %S does not exist" dir))
(if (executable-find "du")
(/ (string-to-number (cdr (doom-call-process "du" "-sb" dir)))
1024.0)
@@ -313,18 +317,26 @@ file if it exists, without confirmation."
(`aborted (message "Aborted"))
(_ t)))
+(defun doom--sudo-file (file)
+ (let ((host (or (file-remote-p file 'host) "localhost")))
+ (concat "/" (when (file-remote-p file)
+ (concat (file-remote-p file 'method) ":"
+ (if-let (user (file-remote-p file 'user))
+ (concat user "@" host)
+ host)
+ "|"))
+ "sudo:root@" host
+ ":" (or (file-remote-p file 'localname)
+ file))))
+
;;;###autoload
(defun doom/sudo-find-file (file)
"Open FILE as root."
(interactive "FOpen file as root: ")
- (when (file-writable-p file)
- (user-error "File is user writeable, aborting sudo"))
- (find-file (if (file-remote-p file)
- (concat "/" (file-remote-p file 'method) ":" (file-remote-p file 'user) "@" (file-remote-p file 'host) "|sudo:root@" (file-remote-p file 'host) ":" (file-remote-p file 'localname))
- (concat "/sudo:root@localhost:" file))))
+ (find-file (doom--sudo-file file)))
;;;###autoload
(defun doom/sudo-this-file ()
"Open the current file as root."
(interactive)
- (doom/sudo-find-file (file-truename buffer-file-name)))
+ (find-alternate-file (doom--sudo-file buffer-file-name)))
diff --git a/core/autoload/help.el b/core/autoload/help.el
index 085976af6..731c4737e 100644
--- a/core/autoload/help.el
+++ b/core/autoload/help.el
@@ -395,13 +395,15 @@ current file is in, or d) the module associated with the current major mode (see
(message "Couldn't find the config block"))))))))
(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))
+ ;; TODO Use ripgrep instead
(split-string
- (shell-command-to-string
- (format "git grep --no-break --no-heading --line-number '%s %s\\($\\| \\)' ':(exclude)*.org'"
- "\\(^;;;###package\\|(after!\\|(use-package!\\)"
- package))
+ (cdr (doom-call-process
+ "git" "grep" "--no-break" "--no-heading" "--line-number"
+ (format "%s %s\\($\\| \\)"
+ "\\(^;;;###package\\|(after!\\|(use-package!\\)"
+ package)
+ ":(exclude)*.org"))
"\n" t)))
;;;###autoload
@@ -463,8 +465,8 @@ If prefix arg is present, refresh the cache."
(`straight
(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\"")))
+ (cdr
+ (doom-call-process "git" "log" "-1" "--format=%D %h %ci")))
(indent
13 (string-trim
(pp-to-string
@@ -608,18 +610,12 @@ Uses the symbol at point or the current selection, if available."
(list (read-string
(format "Search load-path (default: %s): " query)
nil 'git-grep query))))
- ;; REVIEW Replace with deadgrep or ivy/helm interface when we drop ag/git-grep
- ;; support later
+ ;; REVIEW Replace with deadgrep
(grep-find
(mapconcat
#'shell-quote-argument
- (cond ((executable-find "rg")
- `("rg" "-L" "--search-zip" "--no-heading" "--color=never"
- ,query ,@(cl-remove-if-not #'file-directory-p load-path)))
- ((executable-find "ag")
- `("ag" "--search-zip" "--nogroup" "--nocolor"
- ,query ,@(cl-remove-if-not #'file-directory-p load-path)))
- ((user-error "This command requires ripgrep or the_silver_searcher to be installed on your system")))
+ (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query)
+ (cl-remove-if-not #'file-directory-p load-path))
" ")))
;; TODO factor our the duplicate code between this and the above
@@ -637,18 +633,13 @@ Uses the symbol at point or the current selection, if available."
(list (read-string
(format "Search load-path (default: %s): " query)
nil 'git-grep query))))
+ (unless (executable-find "rg")
+ (user-error "Can't find ripgrep on your system"))
(require 'elisp-refs)
- ;; REVIEW Replace with deadgrep or ivy/helm interface when we drop ag/git-grep
- ;; support later
+ ;; REVIEW Replace with deadgrep
(grep-find
(mapconcat
#'shell-quote-argument
- (let ((search (elisp-refs--loaded-paths)))
- (cond ((executable-find "rg")
- `("rg" "-L" "--search-zip" "--no-heading" "--color=never"
- ,query ,@(cl-remove-if-not #'file-directory-p search)))
- ((executable-find "ag")
- `("ag" "--search-zip" "--nogroup" "--nocolor"
- ,query ,@(cl-remove-if-not #'file-directory-p search)))
- ((user-error "This command requires ripgrep or the_silver_searcher to be installed on your system"))))
+ (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query)
+ (cl-remove-if-not #'file-directory-p (elisp-refs--loaded-paths)))
" ")))
diff --git a/core/autoload/line-numbers.el b/core/autoload/line-numbers.el
deleted file mode 100644
index df144180a..000000000
--- a/core/autoload/line-numbers.el
+++ /dev/null
@@ -1,92 +0,0 @@
-;;; core/autoload/line-numbers.el -*- lexical-binding: t; -*-
-;;;###if (version< emacs-version "26.1")
-
-;; DEPRECATED This was lifted out of the display-line-numbers library in Emacs
-;; 26.1 and modified to use nlinum for Emacs 25.x users. It should be removed
-;; should Emacs 25 support be removed.
-
-;;;###autoload
-(defvar display-line-numbers t
- "Non-nil means display line numbers.
-
-If the value is t, display the absolute number of each line of a buffer
-shown in a window. Absolute line numbers count from the beginning of
-the current narrowing, or from buffer beginning. If the value is
-relative, display for each line not containing the window's point its
-relative number instead, i.e. the number of the line relative to the
-line showing the window's point.
-
-In either case, line numbers are displayed at the beginning of each
-non-continuation line that displays buffer text, i.e. after each newline
-character that comes from the buffer. The value visual is like
-relative but counts screen lines instead of buffer lines. In practice
-this means that continuation lines count as well when calculating the
-relative number of a line.
-
-Lisp programs can disable display of a line number of a particular
-buffer line by putting the display-line-numbers-disable text property
-or overlay property on the first visible character of that line.")
-
-(defgroup display-line-numbers nil "Display line number preferences"
- :group 'emacs)
-
-;;;###autoload
-(defcustom display-line-numbers-type t
- "The default type of line numbers to use in `display-line-numbers-mode'.
-See `display-line-numbers' for value options."
- :type '(choice (const :tag "Relative line numbers" relative)
- (const :tag "Relative visual line numbers" visual)
- (other :tag "Absolute line numbers" t)))
-
-;;;###autoload
-(defcustom display-line-numbers-grow-only nil
- "If non-nil, do not shrink line number width."
- :type 'boolean)
-
-;;;###autoload
-(defcustom display-line-numbers-width-start nil
- "If non-nil, count number of lines to use for line number width.
-When `display-line-numbers-mode' is turned on,
-`display-line-numbers-width' is set to the minimum width necessary
-to display all line numbers in the buffer."
- :type 'boolean)
-
-;;;###autoload
-(defun line-number-display-width (&optional _)
- "Return the width used for displaying line numbers in the
-selected window."
- (length (save-excursion (goto-char (point-max))
- (format-mode-line "%l"))))
-
-(defun display-line-numbers-update-width ()
- "Prevent the line number width from shrinking."
- (let ((width (line-number-display-width)))
- (when (> width (or display-line-numbers-width 1))
- (setq display-line-numbers-width width))))
-
-;;;###autoload
-(define-minor-mode display-line-numbers-mode
- "Toggle display of line numbers in the buffer.
-This uses `display-line-numbers' internally.
-
-To change the type of line numbers displayed by default,
-customize `display-line-numbers-type'. To change the type while
-the mode is on, set `display-line-numbers' directly."
- :lighter nil
- (cond ((null display-line-numbers-type))
- ((eq display-line-numbers-type 'relative)
- (if display-line-numbers-mode
- (nlinum-relative-off)
- (nlinum-relative-on)))
- ((nlinum-mode (if display-line-numbers-mode +1 -1)))))
-
-(defun display-line-numbers--turn-on ()
- "Turn on `display-line-numbers-mode'."
- (unless (or (minibufferp)
- ;; taken from linum.el
- (and (daemonp) (null (frame-parameter nil 'client))))
- (display-line-numbers-mode)))
-
-;;;###autoload
-(define-globalized-minor-mode global-display-line-numbers-mode
- display-line-numbers-mode display-line-numbers--turn-on)
diff --git a/core/autoload/packages.el b/core/autoload/packages.el
index b5c0b0c1c..7406dddc1 100644
--- a/core/autoload/packages.el
+++ b/core/autoload/packages.el
@@ -81,7 +81,7 @@
Excludes packages that have a non-nil :built-in property."
(when-let (plist (doom-package-get package))
- (not (plist-get plist :ignore) t)))
+ (not (plist-get plist :ignore))))
;;;###autoload
(defun doom-package-private-p (package)
diff --git a/core/autoload/plist.el b/core/autoload/plist.el
index b99351dd6..aeb9c7f9a 100644
--- a/core/autoload/plist.el
+++ b/core/autoload/plist.el
@@ -9,7 +9,7 @@
Evaluate BODY with either ARGLIST bound to (cons PROP VAL) or, if ARGLIST is a
list, the pair is destructured into (CAR . CDR)."
- (declare (indent defun))
+ (declare (indent 1))
(let ((plist-var (make-symbol "plist")))
`(let ((,plist-var (copy-sequence ,plist)))
(while ,plist-var
diff --git a/core/autoload/projects.el b/core/autoload/projects.el
index 364941ab5..6decf3501 100644
--- a/core/autoload/projects.el
+++ b/core/autoload/projects.el
@@ -117,8 +117,7 @@ If DIR is not a project, it will be indexed (but not cached)."
#'projectile-find-file)))
((fboundp 'counsel-file-jump) ; ivy only
(call-interactively #'counsel-file-jump))
- ((and (fboundp 'project-find-file-in) ; emacs 26.1+ only
- (project-current))
+ ((project-current)
(project-find-file-in nil (list default-directory) nil))
((fboundp 'helm-find-files)
(call-interactively #'helm-find-files))
diff --git a/core/autoload/ui.el b/core/autoload/ui.el
index f5a8ba544..7a0b8a447 100644
--- a/core/autoload/ui.el
+++ b/core/autoload/ui.el
@@ -72,21 +72,14 @@ visual-line-mode is on, this skips relative and uses visual instead.
See `display-line-numbers' for what these values mean."
(interactive)
(defvar doom--line-number-style display-line-numbers-type)
- ;; DEPRECATED
- (let* ((styles `(t ,(if (and EMACS26+ visual-line-mode) 'visual 'relative) nil))
+ (let* ((styles `(t ,(if visual-line-mode 'visual 'relative) nil))
(order (cons display-line-numbers-type (remq display-line-numbers-type styles)))
(queue (memq doom--line-number-style order))
(next (if (= (length queue) 1)
(car order)
(car (cdr queue)))))
(setq doom--line-number-style next)
- ;; DEPRECATED
- (if EMACS26+
- (setq display-line-numbers next)
- (pcase next
- (`t (nlinum-relative-off) (nlinum-mode +1))
- (`relative (nlinum-relative-on))
- (`nil (nlinum-mode -1))))
+ (setq display-line-numbers next)
(message "Switched to %s line numbers"
(pcase next
(`t "normal")
diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el
index cdd1000e6..1e5906cf2 100644
--- a/core/cli/autoloads.el
+++ b/core/cli/autoloads.el
@@ -1,14 +1,11 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
-(require 'autoload)
-
-
(defvar doom-autoload-excluded-packages '("gh")
"Packages that have silly or destructive autoload files that try to load
everyone in the universe and their dog, causing errors that make babies cry. No
one wants that.")
-;; external variables
+;; externs
(defvar autoload-timestamps)
(defvar generated-autoload-load-name)
(defvar generated-autoload-file)
@@ -27,15 +24,14 @@ 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))
+ (doom-cli-reload-autoloads nil 'force))
;;
;;; Helpers
-(defun doom-delete-autoloads-file (file)
+(defun doom--cli-delete-autoloads-file (file)
"Delete FILE (an autoloads file) and accompanying *.elc file, if any."
(cl-check-type file string)
(when (file-exists-p file)
@@ -47,29 +43,29 @@ It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
(ignore-errors (delete-file (byte-compile-dest-file file)))
t))
-(defun doom--warn-refresh-session-h ()
+(defun doom--cli-warn-refresh-session-h ()
(message "Restart or reload Doom Emacs for changes to take effect:\n")
(message " M-x doom/restart-and-restore")
(message " M-x doom/restart")
(message " M-x doom/reload"))
-(defun doom--byte-compile-file (file)
+(defun doom--cli-byte-compile-file (file)
(let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings))
(byte-compile-dynamic t)
(byte-compile-dynamic-docstrings t))
(condition-case-unless-debug e
(when (byte-compile-file file)
- (prog1 (load file 'noerror 'nomessage)
+ (prog1 (load file 'noerror 'nomessage 'nosuffix)
(when noninteractive
- (add-hook 'doom-cli-post-success-execute-hook #'doom--warn-refresh-session-h))))
+ (add-hook 'doom-cli-post-success-execute-hook #'doom--cli-warn-refresh-session-h))))
((debug error)
(let ((backup-file (concat file ".bk")))
(print! (warn "Copied backup to %s") (relpath backup-file))
(copy-file file backup-file 'overwrite))
- (doom-delete-autoloads-file file)
+ (doom--cli-delete-autoloads-file file)
(signal 'doom-autoload-error (list file e))))))
-(defun doom-reload-autoloads (&optional file force-p)
+(defun doom-cli-reload-autoloads (&optional file force-p)
"Reloads FILE (an autoload file), if it needs reloading.
FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If
@@ -80,23 +76,23 @@ even if it doesn't need reloading!"
(signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file)
(cond ((file-equal-p file doom-autoload-file)
- (doom-reload-core-autoloads force-p))
+ (doom-cli-reload-core-autoloads force-p))
((file-equal-p file doom-package-autoload-file)
- (doom-reload-package-autoloads force-p))
+ (doom-cli-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file)))
- (doom-reload-core-autoloads force-p)
- (doom-reload-package-autoloads force-p)))
+ (doom-cli-reload-core-autoloads force-p)
+ (doom-cli-reload-package-autoloads force-p)))
;;
;;; Doom autoloads
-(defun doom--generate-header (func)
+(defun doom--cli-generate-header (func)
(goto-char (point-min))
(insert ";; -*- lexical-binding:t; -*-\n"
";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
-(defun doom--generate-autoloads (targets)
+(defun doom--cli-generate-autoloads (targets)
(let ((n 0))
(dolist (file targets)
(insert
@@ -115,7 +111,7 @@ even if it doesn't need reloading!"
"Scanned %d file(s)")
n)))
-(defun doom--expand-autoload-paths (&optional allow-internal-paths)
+(defun doom--cli-expand-autoload-paths (&optional allow-internal-paths)
(let ((load-path
;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files
;; will be unable to declare autoloads for the built-in autoload.el
@@ -140,7 +136,7 @@ even if it doesn't need reloading!"
path)
t t nil 1)))))
-(defun doom--generate-autodefs-1 (path &optional member-p)
+(defun doom--cli-generate-autodefs-1 (path &optional member-p)
(let (forms)
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
(let* ((sexp (sexp-at-point))
@@ -202,7 +198,7 @@ even if it doesn't need reloading!"
(member-p (push sexp forms)))))
forms))
-(defun doom--generate-autodefs (targets enabled-targets)
+(defun doom--cli-generate-autodefs (targets enabled-targets)
(goto-char (point-max))
(search-backward ";;;***" nil t)
(save-excursion (insert "\n"))
@@ -210,17 +206,17 @@ even if it doesn't need reloading!"
(insert
(with-temp-buffer
(insert-file-contents path)
- (if-let (forms (doom--generate-autodefs-1 path (member path enabled-targets)))
+ (if-let (forms (doom--cli-generate-autodefs-1 path (member path enabled-targets)))
(concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
"\n")
"")))))
-(defun doom--cleanup-autoloads ()
+(defun doom--cli-cleanup-autoloads ()
(goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t t)))
-(defun doom-reload-core-autoloads (&optional force-p)
+(defun doom-cli-reload-core-autoloads (&optional force-p)
"Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
@@ -228,6 +224,7 @@ modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates
`doom-autoload-file'.
Run this whenever your `doom!' block, or a module autoload file, is modified."
+ (require 'autoload)
(let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))
@@ -269,37 +266,38 @@ Run this whenever your `doom!' block, or a module autoload file, is modified."
(ignore
(print! (success "Skipping core autoloads, they are up-to-date"))
(doom-load-autoloads-file doom-autoload-file))
- (print! (start "Regenerating core autoloads file"))
-
- (if (doom-delete-autoloads-file doom-autoload-file)
+ (if (doom--cli-delete-autoloads-file doom-autoload-file)
(print! (success "Deleted old %s") (filename doom-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t))
- (with-temp-file doom-autoload-file
- (doom--generate-header 'doom-reload-core-autoloads)
- (save-excursion
- (doom--generate-autoloads active-targets)
- (print! (success "Generated new autoloads.el")))
- ;; Replace autoload paths (only for module autoloads) with absolute
- ;; paths for faster resolution during load and simpler `load-path'
- (save-excursion
- (doom--expand-autoload-paths 'allow-internal-paths)
- (print! (success "Expanded module autoload paths")))
- ;; Generates stub definitions for functions/macros defined in disabled
- ;; modules, so that you will never get a void-function when you use
- ;; them.
- (save-excursion
- (doom--generate-autodefs targets (reverse active-targets))
- (print! (success "Generated autodefs")))
- ;; Remove byte-compile-inhibiting file variables so we can byte-compile
- ;; the file, and autoload comments.
- (doom--cleanup-autoloads)
- (print! (success "Clean up autoloads")))
+ (print! (start "Regenerating core autoloads file"))
+ (print-group!
+ (with-temp-file doom-autoload-file
+ (doom--cli-generate-header 'doom-cli-reload-core-autoloads)
+ (save-excursion
+ (doom--cli-generate-autoloads active-targets)
+ (print! (success "Generated new autoloads.el")))
+ ;; Replace autoload paths (only for module autoloads) with absolute
+ ;; paths for faster resolution during load and simpler `load-path'
+ (save-excursion
+ (doom--cli-expand-autoload-paths 'allow-internal-paths)
+ (print! (success "Expanded module autoload paths")))
+ ;; Generates stub definitions for functions/macros defined in disabled
+ ;; modules, so that you will never get a void-function when you use
+ ;; them.
+ (save-excursion
+ (doom--cli-generate-autodefs targets (reverse active-targets))
+ (print! (success "Generated autodefs")))
+ ;; Remove byte-compile-inhibiting file variables so we can byte-compile
+ ;; the file, and autoload comments.
+ (doom--cli-cleanup-autoloads)
+ (print! (success "Cleaned up autoloads"))))
;; Byte compile it to give the file a chance to reveal errors (and buy us a
;; few marginal performance boosts)
(print! "> Byte-compiling %s..." (relpath doom-autoload-file))
- (when (doom--byte-compile-file doom-autoload-file)
- (print! (success "Finished compiling %s") (relpath doom-autoload-file))))
+ (when (doom--cli-byte-compile-file doom-autoload-file)
+ (print-group!
+ (print! (success "Compiled %s") (relpath doom-autoload-file)))))
t)))
@@ -346,7 +344,7 @@ served no purpose but to waste cycles."
(goto-char (match-beginning 1))
(kill-sexp)))
-(defun doom-reload-package-autoloads (&optional force-p)
+(defun doom-cli-reload-package-autoloads (&optional force-p)
"Compiles `doom-package-autoload-file' from the autoloads files of all
installed packages. It also caches `load-path', `Info-directory-list',
`doom-disabled-packages', `package-activated-list' and `auto-mode-alist'.
@@ -355,6 +353,7 @@ Will do nothing if none of your installed packages have been modified. If
FORCE-P (universal argument) is non-nil, regenerate it anyway.
This should be run whenever your `doom!' block or update your packages."
+ (require 'autoload)
(print! (start "Checking package autoloads file"))
(print-group!
(if (and (not force-p)
@@ -381,37 +380,39 @@ This should be run whenever your `doom!' block or update your packages."
(version-control 'never)
(case-fold-search nil) ; reduce magic
(autoload-timestamps nil))
- (print! (start "Regenerating package autoloads file"))
- (if (doom-delete-autoloads-file doom-package-autoload-file)
+ (if (doom--cli-delete-autoloads-file doom-package-autoload-file)
(print! (success "Deleted old %s") (filename doom-package-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t))
- (with-temp-file doom-package-autoload-file
- (doom--generate-header 'doom-reload-package-autoloads)
+ (print! (start "Regenerating package autoloads file"))
+ (print-group!
+ (with-temp-file doom-package-autoload-file
+ (doom--cli-generate-header 'doom-cli-reload-package-autoloads)
- (save-excursion
- ;; Cache important and expensive-to-initialize state here.
- (doom--generate-var-cache)
- (print! (success "Cached package state"))
- ;; Concatenate the autoloads of all installed packages.
- (doom--generate-package-autoloads)
- (print! (success "Package autoloads included")))
+ (save-excursion
+ ;; Cache important and expensive-to-initialize state here.
+ (doom--generate-var-cache)
+ (print! (success "Cached package state"))
+ ;; Concatenate the autoloads of all installed packages.
+ (doom--generate-package-autoloads)
+ (print! (success "Package autoloads included")))
- ;; Replace autoload paths (only for module autoloads) with absolute
- ;; paths for faster resolution during load and simpler `load-path'
- (save-excursion
- (doom--expand-autoload-paths)
- (print! (success "Expanded module autoload paths")))
+ ;; Replace autoload paths (only for module autoloads) with absolute
+ ;; paths for faster resolution during load and simpler `load-path'
+ (save-excursion
+ (doom--cli-expand-autoload-paths)
+ (print! (success "Expanded module autoload paths")))
- ;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
- ;; at least); they are cached later, so all those membership checks are
- ;; unnecessary overhead.
- (doom--cleanup-package-autoloads)
- (print! (success "Removed load-path/auto-mode-alist entries")))
+ ;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
+ ;; at least); they are cached later, so all those membership checks are
+ ;; unnecessary overhead.
+ (doom--cleanup-package-autoloads)
+ (print! (success "Removed load-path/auto-mode-alist entries"))))
;; Byte compile it to give the file a chance to reveal errors (and buy us a
;; few marginal performance boosts)
(print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file))
- (when (doom--byte-compile-file doom-package-autoload-file)
- (print! (success "Finished compiling %s") (relpath doom-package-autoload-file)))))
+ (when (doom--cli-byte-compile-file doom-package-autoload-file)
+ (print-group!
+ (print! (success "Compiled %s") (relpath doom-package-autoload-file))))))
t))
diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el
index 94c6e69c5..916a11ba9 100644
--- a/core/cli/byte-compile.el
+++ b/core/cli/byte-compile.el
@@ -1,6 +1,8 @@
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*-
-(defcli! (compile c) (&rest targets)
+(defcli! (compile c)
+ ((recompile-p ["-r" "--recompile"])
+ &rest targets)
"Byte-compiles your config or selected modules.
compile [TARGETS...]
@@ -10,14 +12,11 @@
Accepts :core and :private as special arguments, which target Doom's core files
and your private config files, respectively. To recompile your packages, use
'doom rebuild' instead."
- (doom-byte-compile targets))
-
-(defcli! (recompile rc) (&rest targets)
- "Re-byte-compiles outdated *.elc files."
- (doom-byte-compile targets 'recompile))
+ (doom-cli-byte-compile targets recompile-p))
(defcli! clean ()
"Delete all *.elc files."
+ :bare t
(doom-clean-byte-compiled-files))
@@ -31,7 +30,7 @@ and your private config files, respectively. To recompile your packages, use
(not (equal (file-name-extension path) "el"))
(member filename (list "packages.el" "doctor.el")))))
-(cl-defun doom-byte-compile (&optional modules recompile-p)
+(cl-defun doom-cli-byte-compile (&optional modules recompile-p)
"Byte compiles your emacs configuration.
init.el is always byte-compiled by this.
@@ -149,7 +148,7 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
(unless recompile-p
(doom-clean-byte-compiled-files))
- (dolist (target (delete-dups targets))
+ (dolist (target (delete-dups (delq nil targets)))
(cl-incf
(if (not (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target)))
@@ -183,6 +182,7 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
(defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private
module. This does not include your byte-compiled, third party packages.'"
+ (require 'core-modules)
(print! (start "Cleaning .elc files"))
(print-group!
(cl-loop with default-directory = doom-emacs-dir
diff --git a/core/cli/debug.el b/core/cli/debug.el
index 278140fdc..d36f73dc6 100644
--- a/core/cli/debug.el
+++ b/core/cli/debug.el
@@ -1,21 +1,11 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*-
-(load! "autoload/debug" doom-core-dir)
-
-
;;
;;; Commands
-(defcli! info (&optional format)
- "Output system info in markdown for bug reports.
-
-Will print in the following formats:
-
- --json
- --md / --markdown
- --lisp
-
-If no arguments are given, --raw is assumed."
+(defcli! info
+ ((format ["--json" "--md" "--lisp"] "What format to dump info into"))
+ "Output system info in markdown for bug reports."
(pcase format
("--json"
(require 'json)
@@ -23,7 +13,7 @@ If no arguments are given, --raw is assumed."
(insert (json-encode (doom-info)))
(json-pretty-print-buffer)
(print! (buffer-string))))
- ((or "--md" "--markdown")
+ ("--md"
(doom/info))
((or `nil "--lisp")
(doom/info 'raw))
@@ -33,6 +23,7 @@ If no arguments are given, --raw is assumed."
nil)
(defcli! (version v) ()
- "Reports the version of Doom and Emacs."
+ "Show version information for Doom & Emacs."
+ :bare t
(doom/version)
nil)
diff --git a/core/cli/doctor.el b/core/cli/doctor.el
new file mode 100644
index 000000000..d50cfb564
--- /dev/null
+++ b/core/cli/doctor.el
@@ -0,0 +1,203 @@
+;;; core/cli/doctor.el -*- lexical-binding: t; -*-
+
+(defvar doom-warnings ())
+(defvar doom-errors ())
+
+;;; Helpers
+(defun elc-check-dir (dir)
+ (dolist (file (directory-files-recursively dir "\\.elc$"))
+ (when (file-newer-than-file-p (concat (file-name-sans-extension file) ".el")
+ file)
+ (warn! "%s is out-of-date" (abbreviate-file-name file)))))
+
+(defmacro assert! (condition message &rest args)
+ `(unless ,condition
+ (error! ,message ,@args)))
+
+
+;;; Logging
+(defmacro error! (&rest args)
+ `(progn (unless inhibit-message (print! (error ,@args)))
+ (push (format! (error ,@args)) doom-errors)))
+(defmacro warn! (&rest args)
+ `(progn (unless inhibit-message (print! (warn ,@args)))
+ (push (format! (warn ,@args)) doom-warnings)))
+(defmacro success! (&rest args)
+ `(print! (green ,@args)))
+(defmacro section! (&rest args)
+ `(print! (bold (blue ,@args))))
+
+(defmacro explain! (&rest args)
+ `(print-group! (print! (autofill ,@args))))
+
+
+;;
+;;; CLI commands
+
+(defcli! (doctor doc) ()
+ "Diagnoses common issues on your system.
+
+The Doom doctor is essentially one big, self-contained elisp shell script that
+uses a series of simple heuristics to diagnose common issues on your system.
+Issues that could intefere with Doom Emacs.
+
+Doom modules may optionally have a doctor.el file to run their own heuristics
+in."
+ :bare t
+ (print! "The doctor will see you now...\n")
+
+ ;; REVIEW Refactor me
+ (print! (start "Checking your Emacs version..."))
+ (when EMACS27+
+ (warn! "Emacs %s detected. Emacs HEAD is unstable and may cause errors."
+ emacs-version))
+
+ (print! (start "Checking for Emacs config conflicts..."))
+ (when (file-exists-p "~/.emacs")
+ (warn! "Detected an ~/.emacs file, which may prevent Doom from loading")
+ (explain! "If Emacs finds an ~/.emacs file, it will ignore ~/.emacs.d, where Doom is "
+ "typically installed. If you're seeing a vanilla Emacs splash screen, this "
+ "may explain why. If you use Chemacs, you may ignore this warning."))
+
+ (print! (start "Checking for private config conflicts..."))
+ (let ((xdg-dir (concat (or (getenv "XDG_CONFIG_HOME")
+ "~/.config")
+ "/doom/"))
+ (doom-dir (or (getenv "DOOMDIR")
+ "~/.doom.d/")))
+ (when (and (not (file-equal-p xdg-dir doom-dir))
+ (file-directory-p xdg-dir)
+ (file-directory-p doom-dir))
+ (print! (warn "Detected two private configs, in %s and %s")
+ (abbreviate-file-name xdg-dir)
+ doom-dir)
+ (explain! "The second directory will be ignored, as it has lower precedence.")))
+
+ (print! (start "Checking for stale elc files..."))
+ (elc-check-dir user-emacs-directory)
+
+ (print! (start "Checking Doom Emacs..."))
+ (condition-case-unless-debug ex
+ (print-group!
+ (let ((doom-interactive-mode 'doctor))
+ (doom-initialize 'force)
+ (doom-initialize-core)
+ (doom-initialize-modules))
+
+ (print! (success "Initialized Doom Emacs %s") doom-version)
+ (print!
+ (if (hash-table-p doom-modules)
+ (success "Detected %d modules" (hash-table-count doom-modules))
+ (warn "Failed to load any modules. Do you have an private init.el?")))
+
+ (print! (success "Detected %d packages") (length doom-packages))
+
+ (print! (start "Checking Doom core for irregularities..."))
+ (print-group!
+ ;; Check for oversized problem files in cache that may cause unusual/tremendous
+ ;; delays or freezing. This shouldn't happen often.
+ (dolist (file (list "savehist" "projectile.cache"))
+ (when-let (size (ignore-errors (doom-file-size file doom-cache-dir)))
+ (when (> size 1048576) ; larger than 1mb
+ (warn! "%s is too large (%.02fmb). This may cause freezes or odd startup delays"
+ file (/ size 1024))
+ (explain! "Consider deleting it from your system (manually)"))))
+
+ (unless (ignore-errors (executable-find doom-projectile-fd-binary))
+ (warn! "Couldn't find the `fd' binary; project file searches will be slightly slower")
+ (unless (executable-find "rg")
+ (warn! "Couldn't find the `rg' binary either; project file searches will be even slower")))
+
+ (require 'projectile)
+ (when (projectile-project-root "~")
+ (warn! "Your $HOME is recognized as a project root")
+ (explain! "Doom will disable bottom-up root search, which may reduce the accuracy of project\n"
+ "detection."))
+
+ ;; There should only be one
+ (when (and (file-equal-p doom-private-dir "~/.config/doom")
+ (file-directory-p "~/.doom.d"))
+ (print! (warn "Both %S and '~/.doom.d' exist on your system")
+ (path doom-private-dir))
+ (explain! "Doom will only load one of these (~/.config/doom takes precedence). Possessing\n"
+ "both is rarely intentional; you should one or the other."))
+
+ ;; Check for fonts
+ (if (not (fboundp 'find-font))
+ (progn
+ (warn! "Warning: unable to detect font")
+ (explain! "The `find-font' function is missing. This could indicate the incorrect "
+ "version of Emacs is being used!"))
+ ;; all-the-icons fonts
+ (when (and (pcase system-type
+ (`gnu/linux (concat (or (getenv "XDG_DATA_HOME")
+ "~/.local/share")
+ "/fonts/"))
+ (`darwin "~/Library/Fonts/"))
+ (require 'all-the-icons nil t))
+ (with-temp-buffer
+ (insert (cdr (doom-call-process "fc-list")))
+ (dolist (font all-the-icons-font-names)
+ (if (save-excursion (re-search-backward font nil t))
+ (success! "Found font %s" font)
+ (print! (warn "Warning: couldn't find %S font") font)
+ (explain! "You can install it by running `M-x all-the-icons-install-fonts' within Emacs.\n\n"
+ "This could also mean you've installed them in non-standard locations, in which "
+ "case feel free to ignore this warning.")))))))
+
+ (print! (start "Checking for stale elc files in your DOOMDIR..."))
+ (when (file-directory-p doom-private-dir)
+ (print-group!
+ (elc-check-dir doom-private-dir)))
+
+ (when doom-modules
+ (print! (start "Checking your enabled modules..."))
+ (advice-add #'require :around #'doom-shut-up-a)
+ (maphash (lambda (key plist)
+ (let (doom-local-errors
+ doom-local-warnings)
+ (let (doom-errors
+ doom-warnings)
+ (condition-case-unless-debug ex
+ (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el"))
+ (packages-file (doom-module-path (car key) (cdr key) "packages.el")))
+ (cl-loop for name in (let (doom-packages
+ doom-disabled-packages)
+ (load packages-file 'noerror 'nomessage)
+ (mapcar #'car doom-packages))
+ unless (or (doom-package-get name :disable)
+ (eval (doom-package-get name :ignore))
+ (doom-package-built-in-p name)
+ (doom-package-installed-p name))
+ do (print! (error "%s is not installed") name))
+ (let ((inhibit-message t))
+ (load doctor-file 'noerror 'nomessage)))
+ (file-missing (error! "%s" (error-message-string ex)))
+ (error (error! "Syntax error: %s" ex)))
+ (when (or doom-errors doom-warnings)
+ (print-group!
+ (print! (start (bold "%s %s")) (car key) (cdr key))
+ (print! "%s" (string-join (append doom-errors doom-warnings) "\n")))
+ (setq doom-local-errors doom-errors
+ doom-local-warnings doom-warnings)))
+ (appendq! doom-errors doom-local-errors)
+ (appendq! doom-warnings doom-local-warnings)))
+ doom-modules)))
+ (error
+ (warn! "Attempt to load DOOM failed\n %s\n"
+ (or (cdr-safe ex) (car ex)))
+ (setq doom-modules nil)))
+
+ ;; Final report
+ (message "")
+ (dolist (msg (list (list doom-errors "error" 'red)
+ (list doom-warnings "warning" 'yellow)))
+ (when (car msg)
+ (print! (color (nth 2 msg)
+ (if (cdr msg)
+ "There are %d %ss!"
+ "There is %d %s!")
+ (length (car msg)) (nth 1 msg)))))
+ (unless (or doom-errors doom-warnings)
+ (success! "Everything seems fine, happy Emacs'ing!"))
+ t)
diff --git a/core/cli/env.el b/core/cli/env.el
index 83cc678c9..4368f948d 100644
--- a/core/cli/env.el
+++ b/core/cli/env.el
@@ -1,13 +1,14 @@
;;; core/cli/env.el -*- lexical-binding: t; -*-
-(defcli! env (&rest args)
+(defcli! env
+ ((clear-p ["-c" "--clear"] "Clear and delete your envvar file")
+ (outputfile ["-o" PATH]
+ "Generate the envvar file at PATH. Note that envvar files that aren't in
+`doom-env-file' won't be loaded automatically at startup. You will need to
+load them manually from your private config with the `doom-load-envvars-file'
+function."))
"Creates or regenerates your envvars file.
- doom env [-c|--clear]
-
-This is meant to be a faster and more comprehensive alternative to
-exec-path-from-shell. See the FAQ in the documentation for an explanation why.
-
The envvars file is created by scraping your (interactive) shell environment
into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env'
(or '$SHELL -c set' on windows). Doom loads this file at startup (if it exists)
@@ -21,14 +22,23 @@ app launchers on Linux).
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."
- (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))
+Why this over exec-path-from-shell?
+
+ 1. `exec-path-from-shell' spawns (at least) one process at startup to scrape
+ your shell environment. This can be arbitrarily slow depending on the
+ user's shell configuration. A single program (like pyenv or nvm) or config
+ framework (like oh-my-zsh) could undo all of Doom's startup optimizations
+ in one fell swoop.
+
+ 2. `exec-path-from-shell' only scrapes some state from your shell. You have to
+ be proactive in order to get it to capture all the envvars relevant to your
+ development environment.
+
+ I'd rather it inherit your shell environment /correctly/ (and /completely/)
+ or not at all. It frontloads the debugging process rather than hiding it
+ until it you least want to deal with it."
+ (let ((env-file (expand-file-name (or outputfile doom-env-file))))
+ (cond (clear-p
(unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared"
(path env-file)))
@@ -36,12 +46,11 @@ Use the -c or --clear switch to delete your envvar file."
(print! (success "Successfully deleted %S")
(path env-file)))
- ((or (null args)
- (member "-o" args))
- (doom-reload-env-file 'force env-file))
+ (args
+ (user-error "I don't understand 'doom env %s'"
+ (string-join args " ")))
- ((user-error "I don't understand 'doom env %s'"
- (string-join args " "))))))
+ ((doom-cli-reload-env-file 'force env-file)))))
;;
@@ -66,22 +75,7 @@ Use the -c or --clear switch to delete your envvar file."
Each string is a regexp, matched against variable names to omit from
`doom-env-file'.")
-(defvar doom-env-executable
- (if IS-WINDOWS
- "set"
- (executable-find "env"))
- "The program to use to scrape your shell environment with.
-It is rare that you'll need to change this.")
-
-(defvar doom-env-switches
- (if IS-WINDOWS
- "-c"
- "-ic") ; Execute in an interactive shell
- "The `shell-command-switch'es to use on `doom-env-executable'.
-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.")
-
-(defun doom-reload-env-file (&optional force-p env-file)
+(defun doom-cli-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
@@ -99,49 +93,37 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"Generating")
(path env-file))
(let ((process-environment doom--initial-process-environment))
- (let ((shell-command-switch doom-env-switches)
- (error-buffer (get-buffer-create "*env errors*")))
- (print! (info "Scraping shell environment with '%s %s %s'")
- (filename shell-file-name)
- shell-command-switch
- (filename doom-env-executable))
- (save-excursion
- (shell-command doom-env-executable (current-buffer) error-buffer))
- (print-group!
- (let ((errors (with-current-buffer error-buffer (buffer-string))))
- (unless (string-empty-p errors)
- (print! (info "Warnings:\n\n%s") (indent 4 errors))))
- ;; Remove undesireable variables
- (insert
- (concat
- "# -*- mode: dotenv -*-\n"
- (format "# Generated with: %s %s %s\n"
- shell-file-name
- doom-env-switches
- doom-env-executable)
- "# ---------------------------------------------------------------------------\n"
- "# This file was auto-generated by `doom env'. It contains a list of environment\n"
- "# variables scraped from your default shell (excluding variables blacklisted\n"
- "# in doom-env-ignored-vars).\n"
- "#\n"
- "# It is NOT safe to edit this file. Changes will be overwritten next time that\n"
- "# `doom refresh` is executed. Alternatively, create your own env file 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)
- (save-excursion
- (let* ((valend (or (save-match-data
- (when (re-search-forward "^\\([^= ]+\\)=" nil t)
- (line-beginning-position)))
- (point-max)))
- (var (match-string 1)))
- (when (cl-loop for regexp in doom-env-ignored-vars
- if (string-match-p regexp var)
- return t)
- (print! (info "Ignoring %s") var)
- (delete-region (match-beginning 0) (1- valend)))))))
- (print! (success "Successfully generated %S")
- (path env-file))
- t))))))
+ (print! (info "Scraping shell environment"))
+ (print-group!
+ (when doom-interactive-mode
+ (user-error "'doom env' must be run on the command line, not an interactive session"))
+ (goto-char (point-min))
+ (insert
+ (concat
+ "# -*- mode: dotenv -*-\n"
+ (format "# Generated from a %s shell environent\n" shell-file-name)
+ "# ---------------------------------------------------------------------------\n"
+ "# This file was auto-generated by `doom env'. It contains a list of environment\n"
+ "# variables scraped from your default shell (excluding variables blacklisted\n"
+ "# in doom-env-ignored-vars).\n"
+ "#\n"
+ (if (file-equal-p env-file doom-env-file)
+ (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n"
+ "# run 'doom refresh'. To create a safe-to-edit envvar file use:\n#\n"
+ "# doom env -o ~/.doom.d/myenv\n#\n"
+ "# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n")
+ (concat "# This file is safe to edit by hand, but needs to be loaded manually with:\n#\n"
+ "# (doom-load-envvars-file \"path/to/this/file\")\n#\n"
+ "# Use 'doom env -o path/to/this/file' to regenerate it."))
+ "# ---------------------------------------------------------------------------\n\n"))
+ ;; We assume that this noninteractive session was spawned from the
+ ;; user's interactive shell, therefore we just dump
+ ;; `process-environment' to a file.
+ (dolist (env process-environment)
+ (if (cl-find-if (doom-rpartial #'string-match-p env)
+ doom-env-ignored-vars)
+ (print! (info "Ignoring %s") env)
+ (insert env "\n")))
+ (print! (success "Successfully generated %S")
+ (path env-file))
+ t))))))
diff --git a/core/cli/help.el b/core/cli/help.el
new file mode 100644
index 000000000..13e82ea77
--- /dev/null
+++ b/core/cli/help.el
@@ -0,0 +1,101 @@
+;;; core/cli/help.el -*- lexical-binding: t; -*-
+
+(defun doom--cli-print-signature (cli)
+ (print! (bold "Usage: doom %s%s%s")
+ (if (doom-cli-internal-p cli)
+ ""
+ (concat (doom-cli-name cli) " "))
+ (if-let* ((optlist (doom-cli-optlist cli))
+ (flags (cl-loop for opt in optlist
+ append (doom-cli-option-flags opt)))
+ (fn (doom-partial #'string-prefix-p "--")))
+ (concat (when-let (short-flags (cl-remove-if fn flags))
+ ;; TODO Show arguments of short flags
+ (format "[-%s]"
+ (string-join (mapcar (doom-rpartial #'substring 1 nil) short-flags)
+ "")))
+ ;; TODO Show long flags
+ ;; (when-let (long-flags (cl-remove-if-not fn flags))
+ ;; (concat " " (string-join long-flags " ")))
+ " ")
+ "")
+ (if-let (arglist (doom-cli-arglist cli))
+ (string-join (append (cl-loop for arg in arglist
+ until (memq arg cl--lambda-list-keywords)
+ collect (upcase (symbol-name arg)))
+ (cl-loop for arg in (cdr (memq '&optional arglist))
+ until (memq arg cl--lambda-list-keywords)
+ collect (format "[%s]" (upcase (symbol-name arg)))))
+ " ")
+ "")))
+
+(defun doom--cli-print-desc (cli &optional short)
+ (print! "%s"
+ (if short
+ (car (split-string (doom-cli-desc cli) "\n"))
+ (doom-cli-desc cli))))
+
+(defun doom--cli-print-short-desc (cli)
+ (doom--cli-print-desc cli 'short))
+
+(defun doom--cli-print-options (cli)
+ (when-let (optlist (doom-cli-optlist cli))
+ (print! (bold "Options:"))
+ (print-group!
+ (cl-loop for opt in optlist
+ for flags = (doom-cli-option-flags opt)
+ for desc = (doom-cli-option-desc opt)
+ for args = (doom-cli-option-args opt)
+ for flagstr = (string-join (doom-cli-option-flags opt) ", ")
+ do
+ ;; TODO Adjust columns dynamically
+ (print! "%-18s"
+ (concat flagstr
+ (when-let (arg (car args))
+ (concat " " (upcase (symbol-name arg))))))
+ (print-group!
+ (print! (autofill "%s") desc))))))
+
+
+(defun doom--cli-print (cli)
+ (doom--cli-print-signature cli)
+ (terpri)
+ (doom--cli-print-desc cli)
+ (terpri)
+ (doom--cli-print-options cli))
+
+
+;;
+;;; Commands
+
+(defcli! (help h) (&optional command)
+ "Describe a command or list them all."
+ :bare t
+ (if command
+ (doom--cli-print (doom-cli-get (intern command)))
+ (doom--cli-print (doom-cli-get :main))
+ (terpri)
+ (print! (bold "Commands:"))
+ (print-group!
+ (dolist (group (seq-group-by (lambda (cli)
+ (plist-get (doom-cli-plist cli) :group))
+ (cl-loop for name being the hash-keys of doom--cli-commands
+ for cli = (gethash name doom--cli-commands)
+ if (and (doom-cli-p cli)
+ (not (doom-cli-internal-p cli))
+ (not (plist-get (doom-cli-plist cli) :hidden)))
+ collect cli)))
+ (if (null (car group))
+ (dolist (cli (cdr group))
+ (print! "%-16s %s"
+ (doom-cli-name cli)
+ (car (split-string (doom-cli-desc cli) "\n"))))
+ (print! "%-26s %s"
+ (bold (concat (car group) ":"))
+ (gethash (car group) doom--cli-groups))
+ (print-group!
+ (dolist (cli (cdr group))
+ (print! "%-16s %s"
+ (doom-cli-name cli)
+ (car (split-string (doom-cli-desc cli) "\n"))))))
+ (terpri)))))
diff --git a/core/cli/install.el b/core/cli/install.el
index b50b1522f..f4bf12126 100644
--- a/core/cli/install.el
+++ b/core/cli/install.el
@@ -1,13 +1,11 @@
;;; core/cli/install.el -*- lexical-binding: t; -*-
-(defcli! quickstart (&rest args) ; DEPRECATED
- "This is a deprecated alias for 'doom install'.
-
-See 'doom help install' instead."
- :hidden t
- (apply #'doom-cli-install args))
-
-(defcli! (install i) (&rest args)
+(defcli! (install i)
+ ((noconfig-p ["--no-config"] "Don't create DOOMDIR or dummy files therein")
+ (noenv-p ["--no-env"] "Don't generate an envvars file (see 'doom help env')")
+ (noinstall-p ["--no-install"] "Don't auto-install packages")
+ (nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts")
+ &rest args)
"Installs and sets up Doom Emacs for the first time.
This command does the following:
@@ -25,23 +23,17 @@ The location of DOOMDIR can be changed with the -p option, or by setting the
DOOMDIR environment variable. e.g.
doom -p ~/.config/doom install
- DOOMDIR=~/.config/doom doom install
-
-The following switches are recognized:
-
- --no-config Don't create DOOMDIR or dummy files therein
- --no-install Don't auto-install packages
- --no-env Don't generate an envvars file (see `doom help env`)
- --no-fonts Don't install (or prompt to install) all-the-icons fonts
- -y / --yes Auto-accept any confirmation prompts"
+ DOOMDIR=~/.config/doom doom install"
+ :bare t
(print! (green "Installing Doom Emacs!\n"))
(let ((default-directory (doom-path "~")))
;; Create `doom-private-dir'
- (if (member "--no-config" args)
+ (if noconfig-p
(print! (warn "Not copying private config template, as requested"))
- (print! "> Creating %s" (relpath doom-private-dir))
+ (print! (start "Creating %s") (relpath doom-private-dir))
(make-directory doom-private-dir 'parents)
- (print! (success "Created %s") (relpath doom-private-dir))
+ (print-group!
+ (print! (success "Created %s") (relpath doom-private-dir)))
;; Create init.el, config.el & packages.el
(mapc (lambda (file)
@@ -71,26 +63,29 @@ The following switches are recognized:
;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install)
- (doom-initialize-packages 'force-p)
+ (doom-initialize 'force)
+ (doom-initialize-modules)
- ;; Ask if Emacs.app should be patched
- (if (member "--no-env" args)
- (print! (warn "- Not generating envvars file, as requested"))
- (when (or doom-auto-accept
- (y-or-n-p "Generate an env file? (see `doom help env` for details)"))
- (doom-reload-env-file 'force-p)))
+ ;; Ask if user would like an envvar file generated
+ (if noenv-p
+ (print! (warn "Not generating envvars file, as requested"))
+ (if (file-exists-p doom-env-file)
+ (print! (info "Envvar file already exists, skipping"))
+ (when (or doom-auto-accept
+ (y-or-n-p "Generate an env file? (see `doom help env` for details)"))
+ (doom-cli-reload-env-file 'force-p))))
;; Install Doom packages
- (if (member "--no-install" args)
- (print! (warn "- Not installing plugins, as requested"))
+ (if noinstall-p
+ (print! (warn "Not installing plugins, as requested"))
(print! "Installing plugins")
- (doom-packages-install doom-auto-accept))
+ (doom-cli-packages-install))
(print! "Regenerating autoloads files")
- (doom-reload-autoloads nil 'force-p)
+ (doom-cli-reload-autoloads nil 'force-p)
- (if (member "--no-fonts" args)
- (print! (warn "- Not installing fonts, as requested"))
+ (if nofonts-p
+ (print! (warn "Not installing fonts, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons)
@@ -98,6 +93,9 @@ The following switches are recognized:
(IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes))))
+ (when (file-exists-p "~/.emacs")
+ (print! (warn "A ~/.emacs file was detected. This conflicts with Doom and should be deleted!")))
+
(print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO")
diff --git a/core/cli/packages.el b/core/cli/packages.el
index 2b2faf15d..b25ce2194 100644
--- a/core/cli/packages.el
+++ b/core/cli/packages.el
@@ -1,73 +1,55 @@
;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el
-(defmacro doom--ensure-autoloads-while (&rest body)
- `(progn
- (straight-check-all)
- (doom-reload-core-autoloads)
- (when (progn ,@body)
- (doom-reload-package-autoloads 'force-p))
- t))
-
-
-;;
-;;; Dispatchers
-
-(defcli! (update u) (&rest args)
+(defcli! (update u) ()
"Updates packages.
This works by fetching all installed package repos and checking the distance
between HEAD and FETCH_HEAD. This can take a while.
This excludes packages whose `package!' declaration contains a non-nil :freeze
-or :ignore property.
+or :ignore property."
+ (straight-check-all)
+ (doom-cli-reload-core-autoloads)
+ (when (doom-cli-packages-update)
+ (doom-cli-reload-package-autoloads 'force-p))
+ t)
-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
- (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)))))
-
-(defcli! (rebuild build b) (&rest args)
- "Rebuilds all installed packages.
+(defcli! (build b)
+ ((rebuild-p ["-r"] "Only rebuild packages that need rebuilding"))
+ "Byte-compiles & symlinks 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. This is especially necessary if you upgrade
+Emacs (as byte-code is generally not forward-compatible)."
+ (when (doom-cli-packages-build (not rebuild-p))
+ (doom-cli-reload-package-autoloads 'force-p))
+ t)
-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)
+ ((nobuilds-p ["-b" "--no-builds"] "Don't purge unneeded (built) packages")
+ (noelpa-p ["-p" "--no-elpa"] "Don't purge ELPA packages")
+ (norepos-p ["-r" "--no-repos"] "Don't purge unused straight repos")
+ (regraft-p ["-g" "--regraft"] "Regraft git repos (ie. compact them)"))
+ "Deletes orphaned packages & repos, and compacts them.
-(defcli! (purge p) (&rest args)
- "Deletes any unused ELPA packages, straight builds, and (optionally) repos.
+Purges all installed ELPA packages (as they are considered temporary). Purges
+all orphaned package repos and builds. If -g/--regraft is supplied, the git
+repos among them will be regrafted and compacted to ensure they are as small as
+possible.
-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.
+It is a good idea to occasionally run this doom purge -g to ensure your package
+list remains lean."
+ (straight-check-all)
+ (when (doom-cli-packages-purge
+ (not noelpa-p)
+ (not norepos-p)
+ (not nobuilds-p)
+ regraft-p)
+ (doom-cli-reload-package-autoloads 'force-p))
+ t)
-Switches:
- --no-builds Don't purge unneeded (built) packages
- -e / --elpa Don't purge ELPA packages
- -r / --repos Purge unused repos
- --all Purge builds, elpa packages and repos"
- (doom--ensure-autoloads-while
- (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 "--all" args))
- doom-auto-accept)))
-
-;; (defcli! rollback () ; TODO rollback
+;; (defcli! rollback () ; TODO doom rollback
;; ""
;; (user-error "Not implemented yet, sorry!"))
@@ -75,15 +57,12 @@ Switches:
;;
;;; Library
-(defun doom-packages-install (&optional auto-accept-p)
+(defun doom-cli-packages-install ()
"Installs missing packages.
This function will install any primary package (i.e. a package with a `package!'
-declaration) or dependency thereof that hasn't already been.
-
-Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
-a list of packages that will be installed."
- (print! "> Installing & building packages...")
+declaration) or dependency thereof that hasn't already been."
+ (print! (start "Installing & building packages..."))
(print-group!
(let ((n 0))
(dolist (package (hash-table-keys straight--recipe-cache))
@@ -91,7 +70,7 @@ a list of packages that will be installed."
(local-repo)
(let ((existed-p (file-directory-p (straight--repos-dir package))))
(condition-case-unless-debug e
- (and (straight-use-package (intern package) nil nil " ")
+ (and (straight-use-package (intern package) nil nil (make-string (1- (or doom-format-indent 1)) 32))
(not existed-p)
(file-directory-p (straight--repos-dir package))
(cl-incf n))
@@ -104,17 +83,18 @@ a list of packages that will be installed."
t))))
-(defun doom-packages-rebuild (&optional auto-accept-p all)
+(defun doom-cli-packages-build (&optional force-p)
"(Re)build all packages."
- (print! (start "(Re)building %spackages...") (if all "all " ""))
+ (print! (start "(Re)building %spackages...") (if force-p "all " ""))
(print-group!
(let ((n 0))
- (if all
+ (if force-p
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(dolist (package (hash-table-keys straight--recipe-cache))
(straight-use-package
- (intern package) nil (lambda (_) (cl-incf n) nil) " ")))
+ (intern package) nil (lambda (_) (cl-incf n) nil)
+ (make-string (1- (or doom-format-indent 1)) 32))))
(dolist (recipe (hash-table-values straight--recipe-cache))
(straight--with-plist recipe (package local-repo no-build)
(unless (or no-build (null local-repo))
@@ -139,7 +119,9 @@ a list of packages that will be installed."
(lambda (&rest _) (cl-incf n)))
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
- (straight-use-package (intern package) nil nil " "))
+ (straight-use-package
+ (intern package) nil nil
+ (make-string (or doom-format-indent 0) 32)))
(straight--byte-compile-package recipe)
(dolist (dep (straight--get-dependencies package))
(when-let (recipe (gethash dep straight--recipe-cache))
@@ -151,268 +133,107 @@ a list of packages that will be installed."
t))))
-(defun doom--packages-remove-outdated-f (packages)
- (async-start
- `(lambda ()
- (setq load-path ',load-path
- doom-modules ',doom-modules
- user-emacs-directory ',user-emacs-directory)
- (condition-case e
- (let (packages errors)
- (load ,(concat doom-core-dir "core.el"))
- (doom-initialize 'force)
- (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)
- (condition-case e
- (let ((default-directory (straight--repos-dir local-repo)))
- ;; HACK We normalize packages to avoid certain scenarios
- ;; where `straight-fetch-package' will create an
- ;; interactive popup prompting for action (which will
- ;; cause this async process to block indefinitely). We
- ;; can't use `straight-normalize-package' because could
- ;; create popup prompts too, so we do it manually:
- (shell-command-to-string "git merge --abort")
- (straight--get-call "git" "reset" "--hard" branch)
- (straight--get-call "git" "clean" "-ffd")
- (unless nonrecursive
- (shell-command-to-string "git submodule update --init --recursive"))
- (when upstream-repo
- (let ((desired-url (straight-vc-git--encode-url upstream-repo upstream-host))
- (actual-url (condition-case nil
- (straight--get-call "git" "remote" "get-url" upstream-remote)
- (error nil))))
- (unless (straight-vc-git--urls-compatible-p actual-url desired-url)
- (straight--get-call "git" "remote" "remove" upstream-remote)
- (straight--get-call "git" "remote" "add" upstream-remote desired-url)
- (straight--get-call "git" "fetch" upstream-remote))))
- (straight-fetch-package package)
- ;; REVIEW Is there no better way to get this information?
- (let ((n (length
- (split-string
- (straight--get-call "git" "rev-list" "--left-right" "HEAD..@{u}")
- "\n" t)))
- (pretime
- (string-to-number
- (shell-command-to-string "git log -1 --format=%at HEAD")))
- (time
- (string-to-number
- ;; HACK `straight--get-call' has a higher failure
- ;; rate when querying FETCH_HEAD; not sure why.
- ;; Doing this manually, with
- ;; `shell-command-to-string' works fine.
- (shell-command-to-string "git log -1 --format=%at FETCH_HEAD"))))
- (with-current-buffer (straight--process-get-buffer)
- (with-silent-modifications
- (print! (debug (autofill "%s") (indent 2 (buffer-string))))
- (erase-buffer)))
- (when (> n 0)
- (push (list n pretime time recipe)
- packages))))
- (error
- (push (list package e (string-trim (or (straight--process-get-output) "")))
- errors))))))
- (if errors
- (cons 'error errors)
- (cons 'ok (nreverse packages))))
- (error
- (cons 'error e))))))
-
-
-(defun doom-packages-update (&optional auto-accept-p threads timeout)
- "Updates packages.
-
-Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
-a list of packages that will be updated."
- (print! (start "Scanning for outdated packages (this may take a while)..."))
- (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)
- (or threads 8)))
- for future = (doom--packages-remove-outdated-f group)
- if (processp future)
- collect (cons future group)
- else
- do (print! (warn "Failed to create thread for:\n\n%s\n\nReason: %s"
- group future)))
- (error! "Failed to create any threads")))
- (total (length futures))
- (timeout (or timeout 45)))
- (condition-case-unless-debug e
- (let (specs)
- (while futures
- (print! ". %.0f%%" (* (/ (- total (length futures))
- (float total))
- 100))
- (let ((time 0))
- (catch 'timeout
- (while (not (async-ready (caar futures)))
- (when (> time timeout)
- (print! (warn "A thread has timed out. The following packages were skipped: %s"
- (mapconcat (lambda (p) (plist-get p :package))
- (cdar futures)
- ", ")))
- (throw 'timeout (pop futures)))
- (sleep-for 1)
- (when (cl-evenp time)
- (print! "."))
- (cl-incf time))
- (cl-destructuring-bind (status . result)
- (or (async-get (car (pop futures)))
- (cons nil nil))
- (cond ((null status)
- (error "Thread returned an invalid result: %S" errors))
- ((eq status 'error)
- (error "There were errors:\n\n%s"
- (cond ((and (listp result)
- (symbolp (car result)))
- (prin1-to-string result))
- ((stringp result)
- result)
- ((mapconcat (lambda (e)
- (format! " - %s: %s" (yellow (car e)) (cdr e)))
- result
- "\n")))))
- ((eq status 'ok)
- (print! (debug "Appended %S to package list") (or result "nothing"))
- (appendq! specs result))
- ((error "Thread returned a non-standard status: %s\n\n%s"
- status result)))))))
- (print! ". 100%%")
- (terpri)
- (if-let (specs (delq nil specs))
- (if (not
- (or auto-accept-p
- (y-or-n-p
- (format!
- "%s\n\nThere %s %d package%s available to update. Update them?"
- (mapconcat
- (lambda (spec)
- (cl-destructuring-bind (n pretime time recipe) spec
- (straight--with-plist recipe (package)
- (format! "+ %-33s %s commit(s) behind %s -> %s"
- (yellow package) (yellow n)
- (format-time-string "%Y%m%d" pretime)
- (format-time-string "%Y%m%d" time)))))
- specs
- "\n")
- (if (cdr specs) "are" "is")
- (length specs)
- (if (cdr specs) "s" "")))))
- (ignore (print! (info "Aborted update")))
- (terpri)
- (straight--make-package-modifications-available)
- (let ((straight--packages-to-rebuild (make-hash-table :test #'equal))
- (straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
- (dolist (spec specs)
- (cl-destructuring-bind (n pretime time recipe) spec
- (straight--with-plist recipe (local-repo package)
- (let ((default-directory (straight--repos-dir local-repo)))
- (print! (start "Updating %S") package)
- (straight-merge-package package)
- ;; HACK `straight-rebuild-package' doesn't pick up that
- ;; this package has changed, so we do it manually. Is
- ;; there a better way?
- (ignore-errors
- (delete-directory (straight--build-dir package) 'recursive))
- (puthash package t straight--packages-to-rebuild)
- (cl-incf n))
- (with-current-buffer (straight--process-get-buffer)
- (with-silent-modifications
- (print! (debug (autofill "%s") (indent 2 (buffer-string))))
- (erase-buffer))))))
- (doom--finalize-straight)
- (doom-packages-rebuild auto-accept-p))
- t)
- (print! (success "No packages to update"))
- nil))
- (error
- (message "Output:\n%s" (straight--process-get-output))
- (signal (car e) (error-message-string e)))))))
+(defun doom-cli-packages-update ()
+ "Updates packages."
+ (print! (start "Updating packages (this may take a while)..."))
+ (let ((straight--packages-to-rebuild (make-hash-table :test #'equal))
+ (total (hash-table-count straight--repo-cache))
+ (i 1)
+ errors)
+ (print-group!
+ (dolist (recipe (hash-table-values straight--repo-cache))
+ (straight--with-plist recipe (package type local-repo)
+ (condition-case-unless-debug e
+ (let* ((default-directory (straight--repos-dir local-repo))
+ (commit (straight-vc-get-commit type local-repo)))
+ (if (not (straight-vc-fetch-from-remote recipe))
+ (print! (warn "(%d/%d) Failed to fetch %s" i total package))
+ (let ((output (straight--process-get-output)))
+ (straight-merge-package package)
+ (let ((newcommit (straight-vc-get-commit type local-repo)))
+ (if (string= commit newcommit)
+ (print! (info "(%d/%d) %s is up-to-date") i total package)
+ (ignore-errors
+ (delete-directory (straight--build-dir package) 'recursive))
+ (puthash package t straight--packages-to-rebuild)
+ (print! (success "(%d/%d) %s updated (%s -> %s)") i total package
+ (substring commit 0 7)
+ (substring newcommit 0 7))
+ (unless (string-empty-p output)
+ (print-group!
+ (print! (info "%s") output)
+ (when (eq type 'git)
+ (straight--call "git" "log" "--oneline" newcommit (concat "^" commit))
+ (print-group!
+ (print! "%s" (straight--process-get-output))))))))))
+ (cl-incf i))
+ (user-error
+ (signal 'user-error (error-message-string e)))
+ (error
+ (print! (warn "(%d/%d) Encountered error with %s" i total package))
+ (print-group!
+ (print! (error "%s" e))
+ (print-group! (print! (info "%s" (straight--process-get-output)))))
+ (push package errors)))))
+ (when errors
+ (print! (error "There were %d errors, the offending packages are: %s")
+ (length errors) (string-join errors ", ")))
+ (if (hash-table-empty-p straight--packages-to-rebuild)
+ (ignore
+ (print! (success "All %d packages are up-to-date")
+ (hash-table-count straight--repo-cache)))
+ (let ((count (hash-table-count straight--packages-to-rebuild))
+ (packages (hash-table-keys straight--packages-to-rebuild)))
+ (sort packages #'string-lessp)
+ (doom--finalize-straight)
+ (doom-cli-packages-build)
+ (print! (success "Updated %d package(s)") count))
+ t))))
;;; PURGE (for the emperor)
-(defun doom--prompt-p (list-fn list preamble postamble)
- (or (y-or-n-p (format "%s%s\n\n%s"
- (if preamble (concat preamble "\n\n") "")
- (mapconcat list-fn list "\n")
- (or postamble "")))
- (user-error! "Aborted")))
-
-(defun doom--prompt-columns-p (row-fn list preamble postamble)
- (doom--prompt-p (lambda (row)
- (mapconcat row-fn row ""))
- (seq-partition (cl-sort (copy-sequence list) #'string-lessp)
- 3)
- preamble
- postamble))
-
-(defun doom--packages-purge-build (build)
+(defun doom--cli-packages-purge-build (build)
(let ((build-dir (straight--build-dir build)))
- (print! (start "Purging build/%s..." build))
(delete-directory build-dir 'recursive)
(if (file-directory-p build-dir)
(ignore (print! (error "Failed to purg build/%s" build)))
(print! (success "Purged build/%s" build))
t)))
-(defun doom--packages-purge-builds (builds &optional auto-accept-p)
+(defun doom--cli-packages-purge-builds (builds)
(if (not builds)
(progn (print! (info "No builds to purge"))
0)
- (or auto-accept-p
- (doom--prompt-columns-p
- (lambda (p) (format " + %-20.20s" p)) builds nil
- (format! "Found %d orphaned package builds. Purge them?"
- (length builds))))
(length
- (delq nil (mapcar #'doom--packages-purge-build builds)))))
+ (delq nil (mapcar #'doom--cli-packages-purge-build builds)))))
-(defun doom--packages-regraft-repo (repo)
+(defun doom--cli-packages-regraft-repo (repo)
(let ((default-directory (straight--repos-dir repo)))
(if (not (file-directory-p ".git"))
(ignore (print! (warn "repos/%s is not a git repo, skipping" repo)))
- (print! (debug "Regrafting repos/%s..." repo))
- (straight--call "git" "reset" "--hard")
- (straight--call "git" "clean" "--ffd")
- (straight--call "git" "replace" "--graft" "HEAD")
- (straight--call "git" "gc")
- (print! (debug "%s" (straight--process-get-output)))
- (print! (success "Regrafted repos/%s" repo))
+ (let ((before-size (doom-directory-size default-directory)))
+ (straight--call "git" "reset" "--hard")
+ (straight--call "git" "clean" "-ffd")
+ (if (not (car (straight--call "git" "replace" "--graft" "HEAD")))
+ (print! (info "repos/%s is already compact" repo))
+ (straight--call "git" "gc")
+ (print! (success "Regrafted repos/%s (from %0.1fKB to %0.1fKB)")
+ repo before-size (doom-directory-size default-directory))
+ (print-group! (print! "%s" (straight--process-get-output)))))
t)))
-(defun doom--packages-regraft-repos (repos &optional auto-accept-p)
+(defun doom--cli-packages-regraft-repos (repos)
(if (not repos)
(progn (print! (info "No repos to regraft"))
0)
- (or auto-accept-p
- (y-or-n-p (format! "Preparing to regraft all %d repos. Continue?"
- (length repos)))
- (user-error! "Aborted!"))
- (if (executable-find "du")
- (cl-destructuring-bind (status . size)
- (doom-sh "du" "-sh" (straight--repos-dir))
- (prog1 (delq nil (mapcar #'doom--packages-regraft-repo repos))
- (cl-destructuring-bind (status . newsize)
- (doom-sh "du" "-sh" (straight--repos-dir))
- (print! (success "Finshed regrafted. Size before: %s and after: %s"
- (car (split-string size "\t"))
- (car (split-string newsize "\t")))))))
- (delq nil (mapcar #'doom--packages-regraft-repo repos)))))
+ (let ((before-size (doom-directory-size (straight--repos-dir))))
+ (prog1 (print-group! (delq nil (mapcar #'doom--cli-packages-regraft-repo repos)))
+ (let ((after-size (doom-directory-size (straight--repos-dir))))
+ (print! (success "Finished regrafting. Size before: %0.1fKB and after: %0.1fKB (%0.1fKB)")
+ before-size after-size
+ (- after-size before-size)))))))
-(defun doom--packages-purge-repo (repo)
- (print! (debug "Purging repos/%s..." repo))
+(defun doom--cli-packages-purge-repo (repo)
(let ((repo-dir (straight--repos-dir repo)))
(delete-directory repo-dir 'recursive)
(ignore-errors
@@ -422,19 +243,14 @@ a list of packages that will be updated."
(print! (success "Purged repos/%s" repo))
t)))
-(defun doom--packages-purge-repos (repos &optional auto-accept-p)
+(defun doom--cli-packages-purge-repos (repos)
(if (not repos)
(progn (print! (info "No repos to purge"))
0)
- (or auto-accept-p
- (doom--prompt-columns-p
- (lambda (p) (format " + %-20.20s" p)) repos nil
- (format! "Found %d orphaned repos. Purge them?"
- (length repos))))
(length
- (delq nil (mapcar #'doom--packages-purge-repo repos)))))
+ (delq nil (mapcar #'doom--cli-packages-purge-repo repos)))))
-(defun doom--packages-purge-elpa (&optional auto-accept-p)
+(defun doom--cli-packages-purge-elpa ()
(unless (bound-and-true-p package--initialized)
(package-initialize))
(let ((packages (cl-loop for (package desc) in package-alist
@@ -444,16 +260,11 @@ a list of packages that will be updated."
(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 packages) nil
- (format! "Found %d orphaned ELPA packages. Purge them?"
- (length package-alist)))
(mapc (doom-rpartial #'delete-directory 'recursive)
(mapcar #'cdr packages))
(length packages))))
-(defun doom-packages-purge (&optional elpa-p builds-p repos-p auto-accept-p)
+(defun doom-cli-packages-purge (&optional elpa-p builds-p repos-p regraft-repos-p)
"Auto-removes orphaned packages and repos.
An orphaned package is a package that isn't a primary package (i.e. doesn't have
@@ -461,10 +272,7 @@ a `package!' declaration) or isn't depended on by another primary package.
If BUILDS-P, include straight package builds.
If REPOS-P, include straight repos.
-If ELPA-P, include packages installed with package.el (M-x package-install).
-
-Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
-a list of packages that will be removed."
+If ELPA-P, include packages installed with package.el (M-x package-install)."
(print! (start "Searching for orphaned packages to purge (for the emperor)..."))
(cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort))
@@ -479,18 +287,20 @@ a list of packages that will be removed."
(print-group!
(if (not builds-p)
(print! (info "Skipping builds"))
- (and (/= 0 (doom--packages-purge-builds builds-to-purge auto-accept-p))
+ (and (/= 0 (doom--cli-packages-purge-builds builds-to-purge))
(setq success t)
(straight-prune-build-cache)))
(if (not elpa-p)
(print! (info "Skipping elpa packages"))
- (and (/= 0 (doom--packages-purge-elpa auto-accept-p))
+ (and (/= 0 (doom--cli-packages-purge-elpa))
(setq success t)))
(if (not repos-p)
(print! (info "Skipping repos"))
- (and (/= 0 (doom--packages-purge-repos repos-to-purge auto-accept-p))
- (setq success t))
- (and (doom--packages-regraft-repos repos-to-regraft auto-accept-p)
+ (and (/= 0 (doom--cli-packages-purge-repos repos-to-purge))
+ (setq success t)))
+ (if (not regraft-repos-p)
+ (print! (info "Skipping regrafting"))
+ (and (doom--cli-packages-regraft-repos repos-to-regraft)
(setq success t)))
(when success
(doom--finalize-straight)
diff --git a/core/cli/test.el b/core/cli/test.el
index e052365c6..4def753a3 100644
--- a/core/cli/test.el
+++ b/core/cli/test.el
@@ -7,17 +7,21 @@
runemacs-binary-path
emacs-binary-path)))
+
(defcli! test (&rest targets)
"Run Doom unit tests."
- (let (files error)
+ :bare t
+ (doom-initialize 'force)
+ (require 'ansi-color)
+ (let (files error read-files)
(unless targets
(setq targets
(cons doom-core-dir
(cl-remove-if-not
- (lambda (path) (file-in-directory-p path doom-emacs-dir))
+ (doom-rpartial #'file-in-directory-p doom-emacs-dir)
;; Omit `doom-private-dir', which is always first
(let (doom-modules)
- (load! "test/init" doom-core-dir)
+ (load (expand-file-name "test/init" doom-core-dir) nil t)
(cdr (doom-module-load-path)))))))
(while targets
(let ((target (pop targets)))
@@ -29,47 +33,72 @@
(appendq! files (nreverse (doom-glob target "test/test-*.el"))))
((file-exists-p target)
(push target files)))))
+ (setenv "DOOMLOCALDIR" (concat doom-local-dir "test/"))
+ (setenv "DOOMDIR" (concat doom-core-dir "test/"))
(with-temp-buffer
(print! (start "Bootstrapping test environment, if necessary..."))
- (if (zerop
- (call-process
- (doom--emacs-binary)
- nil t nil "--batch"
- "--eval" (prin1-to-string
- `(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)
- (doom-reload-core-autoloads 'force)
- (when (doom-packages-install 'auto-accept)
- (doom-reload-package-autoloads 'force))))))
- (message "%s" (buffer-string))
- (message "%s" (buffer-string))
- (error "Failed to bootstrap unit tests")))
- (dolist (file files)
- (if (doom-file-cookie-p file "if" t)
- (with-temp-buffer
- (unless
- (zerop
- (apply #'call-process
- (doom--emacs-binary)
- nil t nil "--batch"
- (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"))))
- (setq error t))
- (message "%s" (buffer-string)))
- (print! (info "Ignoring %s" (relpath file)))))
- (if error
- (user-error "A test failed")
+ (cl-destructuring-bind (status . output)
+ (doom-exec-process
+ (doom--emacs-binary)
+ "--batch"
+ "--eval"
+ (prin1-to-string
+ `(progn
+ (setq user-emacs-directory ,doom-emacs-dir
+ doom-auto-accept t)
+ (require 'core ,(locate-library "core"))
+ (require 'core-cli)
+ (doom-initialize 'force)
+ (doom-initialize-modules)
+ (doom-cli-reload-core-autoloads 'force)
+ (when (doom-cli-packages-install)
+ (doom-cli-reload-package-autoloads 'force)))))
+ (unless (zerop status)
+ (error "Failed to bootstrap unit tests"))))
+ (with-temp-buffer
+ (dolist (file files)
+ (if (doom-file-cookie-p file "if" t)
+ (cl-destructuring-bind (_status . output)
+ (apply #'doom-exec-process
+ (doom--emacs-binary)
+ "--batch"
+ "-l" (concat doom-core-dir "core.el")
+ "-l" (concat doom-core-dir "test/helpers.el")
+ (append (when (file-in-directory-p file doom-modules-dir)
+ (list "-f" "doom-initialize-core"))
+ (list "-l" file
+ "-f" "buttercup-run")))
+ (insert (replace-regexp-in-string ansi-color-control-seq-regexp "" output))
+ (push file read-files))
+ (print! (info "Ignoring %s" (relpath file)))))
+ (let ((total 0)
+ (total-failed 0)
+ (i 0))
+ (print! "\n----------------------------------------\nTests finished")
+ (print-group!
+ (goto-char (point-min))
+ (while (re-search-forward "^Ran \\([0-9]+\\) specs, \\([0-9]+\\) failed," nil t)
+ (let ((ran (string-to-number (match-string 1)))
+ (failed (string-to-number (match-string 2))))
+ (when (> failed 0)
+ (terpri)
+ (print! (warn "(%s) Failed %d/%d tests")
+ (path (nth i read-files))
+ failed ran)
+ (save-excursion
+ (print-group!
+ (print!
+ "%s" (string-trim
+ (buffer-substring
+ (match-beginning 0)
+ (dotimes (_ failed (point))
+ (search-backward "========================================"))))))))
+ (cl-incf total ran)
+ (cl-incf total-failed failed)
+ (cl-incf i))))
+ (terpri)
+ (if (= total-failed 0)
+ (print! (success "Ran %d tests successfully." total total-failed))
+ (print! (error "Ran %d tests, %d failed") total total-failed)
+ (kill-emacs 1)))
t)))
diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el
index 893012f41..7b9912ecf 100644
--- a/core/cli/upgrade.el
+++ b/core/cli/upgrade.el
@@ -1,6 +1,7 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
-(defcli! (upgrade up) (&rest args)
+(defcli! (upgrade up)
+ ((force-p ["-f" "--force"]))
"Updates Doom and packages.
This requires that ~/.emacs.d is a git repo, and is the equivalent of the
@@ -10,22 +11,14 @@ following shell commands:
git pull --rebase
bin/doom clean
bin/doom refresh
- 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
- (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)))
+ bin/doom update"
+ :bare t
+ (when (doom-cli-upgrade doom-auto-accept force-p)
+ (require 'core-packages)
+ (doom-initialize)
+ (doom-initialize-packages)
+ (when (doom-cli-packages-update)
+ (doom-cli-reload-package-autoloads 'force))))
;;
@@ -38,13 +31,13 @@ Switches:
(defun doom--working-tree-dirty-p (dir)
(cl-destructuring-bind (success . stdout)
- (doom-sh "git" "status" "--porcelain" "-uno")
+ (doom-call-process "git" "status" "--porcelain" "-uno")
(if (= 0 success)
(string-match-p "[^ \t\n]" (buffer-string))
(error "Failed to check working tree in %s" dir))))
-(defun doom-upgrade (&optional auto-accept-p force-p)
+(defun doom-cli-upgrade (&optional auto-accept-p force-p)
"Upgrade Doom to the latest version non-destructively."
(require 'vc-git)
(let ((default-directory doom-emacs-dir)
@@ -65,15 +58,15 @@ Switches:
(format "Refusing to upgrade because %S has been modified." (path doom-emacs-dir))
"Either stash/undo your changes or run 'doom upgrade -f' to discard local changes.")
(print! (info "You have local modifications in Doom's source. Discarding them..."))
- (doom-sh "git" "reset" "--hard" (format "origin/%s" branch))
- (doom-sh "git" "clean" "-ffd")))
+ (doom-call-process "git" "reset" "--hard" (format "origin/%s" branch))
+ (doom-call-process "git" "clean" "-ffd")))
- (doom-sh "git" "remote" "remove" doom-repo-remote)
+ (doom-call-process "git" "remote" "remove" doom-repo-remote)
(unwind-protect
(progn
- (or (zerop (car (doom-sh "git" "remote" "add" doom-repo-remote doom-repo-url)))
+ (or (zerop (car (doom-call-process "git" "remote" "add" doom-repo-remote doom-repo-url)))
(error "Failed to add %s to remotes" doom-repo-remote))
- (or (zerop (car (doom-sh "git" "fetch" "--tags" doom-repo-remote branch)))
+ (or (zerop (car (doom-call-process "git" "fetch" "--tags" doom-repo-remote branch)))
(error "Failed to fetch from upstream"))
(let ((this-rev (vc-git--rev-parse "HEAD"))
@@ -89,9 +82,9 @@ Switches:
((print! (info "A new version of Doom Emacs is available!\n\n Old revision: %s (%s)\n New revision: %s (%s)\n"
(substring this-rev 0 10)
- (cdr (doom-sh "git" "log" "-1" "--format=%cr" "HEAD"))
+ (cdr (doom-call-process "git" "log" "-1" "--format=%cr" "HEAD"))
(substring new-rev 0 10)
- (cdr (doom-sh "git" "log" "-1" "--format=%cr" target-remote))))
+ (cdr (doom-call-process "git" "log" "-1" "--format=%cr" target-remote))))
(when (and (not auto-accept-p)
(y-or-n-p "View the comparison diff in your browser?"))
@@ -106,15 +99,13 @@ Switches:
(print! (start "Upgrading Doom Emacs..."))
(print-group!
(doom-clean-byte-compiled-files)
- (unless (and (zerop (car (doom-sh "git" "reset" "--hard" target-remote)))
+ (unless (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote)))
(equal (vc-git--rev-parse "HEAD") new-rev))
(error "Failed to check out %s" (substring new-rev 0 10)))
(print! (success "Finished upgrading Doom Emacs")))
- (doom-delete-autoloads-file doom-autoload-file)
- (doom-delete-autoloads-file doom-package-autoload-file)
- (doom-cli-refresh "-f")
+ (doom-cli-execute "refresh" (if auto-accept-p '("-y")))
t)
(print! (success "Done! Restart Emacs for changes to take effect."))))))
(ignore-errors
- (doom-sh "git" "remote" "remove" doom-repo-remote))))))
+ (doom-call-process "git" "remote" "remove" doom-repo-remote))))))
diff --git a/core/core-cli.el b/core/core-cli.el
index d3c4bb4e3..e1ca98b87 100644
--- a/core/core-cli.el
+++ b/core/core-cli.el
@@ -2,155 +2,221 @@
(require 'seq)
+;; 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 nil (not doom-debug-mode) 'nosuffix)
+ (file-expand-wildcards (concat doom-core-dir "autoload/*.el")))
+
+
+;;
+;;; Variables
(defvar doom-auto-accept (getenv "YES")
"If non-nil, Doom will auto-accept any confirmation prompts during batch
-commands like `doom-packages-install', `doom-packages-update' and
+commands like `doom-cli-packages-install', `doom-cli-packages-update' and
`doom-packages-autoremove'.")
-(defvar doom-cli-pre-execute-hook nil
- "TODO")
-(defvar doom-cli-post-success-execute-hook nil
- "TODO")
-
+(defvar doom--cli-p nil)
(defvar doom--cli-commands (make-hash-table :test 'equal))
(defvar doom--cli-groups (make-hash-table :test 'equal))
(defvar doom--cli-group nil)
+(cl-defstruct
+ (doom-cli
+ (:constructor nil)
+ (:constructor
+ make-doom-cli
+ (name &key desc aliases optlist arglist plist fn
+ &aux
+ (optlist
+ (cl-loop for (symbol options desc) in optlist
+ for ((_ . options) (_ . params))
+ = (seq-group-by #'stringp options)
+ collect
+ (make-doom-cli-option :symbol symbol
+ :flags options
+ :args params
+ :desc desc))))))
+ (name nil :read-only t)
+ (desc "TODO")
+ aliases
+ optlist
+ arglist
+ plist
+ (fn (lambda (_) (print! "But nobody came!"))))
-;;
-;;; Dispatcher API
+(cl-defstruct doom-cli-option
+ (symbol)
+ (flags ())
+ (args ())
+ (desc "TODO"))
-(defun doom-sh (command &rest args)
- "Execute COMMAND with ARGS in the shell and return (STATUS . OUTPUT).
+(defun doom--cli-get-option (cli flag)
+ (cl-find-if (doom-partial #'member flag)
+ (doom-cli-optlist cli)
+ :key #'doom-cli-option-flags))
-STATUS is a boolean"
- (let ((output (get-buffer-create "*doom-sh-output*")))
- (unwind-protect
- (cons (or (apply #'call-process command nil output nil args)
- -1)
- (with-current-buffer output
- (string-trim (buffer-string))))
- (kill-buffer output))))
+(defun doom--cli-process (cli args)
+ (let* ((args (copy-sequence args))
+ (arglist (copy-sequence (doom-cli-arglist cli)))
+ (expected (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords)
+ arglist)
+ (length arglist)))
+ (got 0)
+ restvar
+ rest
+ alist)
+ (catch 'done
+ (while args
+ (let ((arg (pop args)))
+ (cond ((eq (car arglist) '&rest)
+ (setq restvar (cadr arglist)
+ rest (cons arg args))
+ (throw 'done t))
-(defun doom--dispatch-command (command)
- (when (symbolp command)
- (setq command (symbol-name command)))
- (cl-check-type command string)
- (intern-soft
- (format "doom-cli-%s"
- (if (gethash command doom--cli-commands)
- command
- (cl-loop for key
- being the hash-keys in doom--cli-commands
- for aliases = (plist-get (gethash key doom--cli-commands) :aliases)
- if (member command aliases)
- return key)))))
+ ((string-match "^\\(--\\([a-zA-Z0-9][a-zA-Z0-9-_]*\\)\\)\\(?:=\\(.+\\)\\)?$" arg)
+ (let* ((fullflag (match-string 1 arg))
+ (opt (doom--cli-get-option cli fullflag)))
+ (unless opt
+ (user-error "Unrecognized switch %S" (concat "--" (match-string 2 arg))))
+ (setf (alist-get (doom-cli-option-symbol opt) alist)
+ (or (if (doom-cli-option-args opt)
+ (or (match-string 3 arg)
+ (pop args)
+ (user-error "%S expected an argument, but got none"
+ fullflag))
+ (if (match-string 3 arg)
+ (user-error "%S was not expecting an argument, but got %S"
+ fullflag (match-string 3 arg))
+ fullflag))))))
-(defun doom--dispatch-format (desc &optional short)
- (with-temp-buffer
- (let ((fill-column 72))
- (save-excursion
- (insert desc)
- (while (re-search-backward "\n\n[^ \n]" nil t)
- (fill-paragraph))))
- (if (not short)
- (buffer-string)
- (buffer-substring (line-beginning-position)
- (line-end-position)))))
+ ((string-match "^\\(-\\([a-zA-Z0-9]+\\)\\)$" arg)
+ (let ((fullflag (match-string 1 arg))
+ (flag (match-string 2 arg)))
+ (dolist (switch (split-string flag "" t))
+ (if-let (opt (doom--cli-get-option cli (concat "-" switch)))
+ (setf (alist-get (doom-cli-option-symbol opt) alist)
+ (if (doom-cli-option-args opt)
+ (or (pop args)
+ (user-error "%S expected an argument, but got none"
+ fullflag))
+ fullflag))
+ (user-error "Unrecognized switch %S" (concat "-" switch))))))
-(defun doom--dispatch-help-1 (command)
- (cl-destructuring-bind (&key aliases hidden _group)
- (gethash command doom--cli-commands)
- (unless hidden
- (print! "%-11s\t%s\t%s"
- command (if aliases (string-join aliases ",") "")
- (doom--dispatch-format
- (documentation (doom--dispatch-command command))
- t)))))
+ (arglist
+ (cl-incf got)
+ (let ((spec (pop arglist)))
+ (when (eq spec '&optional)
+ (setq spec (pop arglist)))
+ (setf (alist-get spec alist) arg))
+ (when (null arglist)
+ (throw 'done t)))
-(defun doom--dispatch-help (&optional fn &rest args)
- "Display help documentation for a dispatcher command. If fn and DESC are
-omitted, show all available commands, their aliases and brief descriptions."
- (if fn
- (princ (documentation fn))
- (print! (bold "%-11s\t%s\t%s" "Command:" "Alias" "Description"))
- (print-group!
- (dolist (group (seq-group-by (lambda (key) (plist-get (gethash key doom--cli-commands) :group))
- (hash-table-keys doom--cli-commands)))
- (if (null (car group))
- (mapc #'doom--dispatch-help-1 (cdr group))
- (print! "%-30s\t%s" (bold (car group)) (gethash (car group) doom--cli-groups))
- (print-group!
- (mapc #'doom--dispatch-help-1 (cdr group))))
- (terpri)))))
+ (t
+ (push arg args)
+ (throw 'done t))))))
+ (when (< got expected)
+ (error "Expected %d arguments, got %d" expected got))
+ (when rest
+ (setf (alist-get restvar alist) rest))
+ alist))
-(defun doom-dispatch (cmd args &optional show-help)
- "Parses ARGS and invokes a dispatcher.
+(defun doom-cli-get (command)
+ "Return a CLI object associated by COMMAND name (string)."
+ (cond ((null command) nil)
+ ((doom-cli-p command) command)
+ ((doom-cli-get
+ (gethash (cond ((symbolp command) command)
+ ((stringp command) (intern command))
+ (command))
+ doom--cli-commands)))))
-If SHOW-HELP is non-nil, show the documentation for said dispatcher."
- (when (equal cmd "help")
- (setq show-help t)
- (when args
- (setq cmd (car args)
- args (cdr args))))
- (let ((fn (doom--dispatch-command cmd)))
- (unless (fboundp fn)
- (user-error "%S is not any command *I* know!" cmd))
- (if show-help
- (doom--dispatch-help fn args)
- (let ((start-time (current-time)))
- (run-hooks 'doom-cli-pre-execute-hook)
- (unwind-protect
- (when-let (ret (apply fn args))
- (print!
- "\n%s"
- (success "Finished! (%.4fs)"
- (float-time
- (time-subtract (current-time)
- start-time))))
- (run-hooks 'doom-cli-post-execute-hook)
- ret)
- (run-hooks 'doom-cli-post-error-execute-hook))))))
+(defun doom-cli-internal-p (cli)
+ "Return non-nil if CLI is an internal (non-public) command."
+ (string-prefix-p ":" (doom-cli-name cli)))
+
+(defun doom-cli-execute (command &optional args)
+ "Execute COMMAND (string) with ARGS (list of strings).
+
+Executes a cli defined with `defcli!' with the name or alias specified by
+COMMAND, and passes ARGS to it."
+ (if-let (cli (doom-cli-get command))
+ (funcall (doom-cli-fn cli)
+ (doom--cli-process cli args))
+ (user-error "Couldn't find any %S command" command)))
+
+(defmacro defcli! (name speclist &optional docstring &rest body)
+ "Defines a CLI command.
+
+COMMAND is a symbol or a list of symbols representing the aliases for this
+command. DOCSTRING is a string description; its first line should be short
+(under 60 characters), as it will be used as a summary for 'doom help'.
+
+SPECLIST is a specification for options and arguments, which can be a list
+specification for an option/switch in the following format:
+
+ (VAR [FLAGS... ARGS...] DESCRIPTION)
+
+Otherwise, SPECLIST accepts the same argument specifiers as `defun'.
+
+BODY will be run when this dispatcher is called."
+ (declare (indent 2) (doc-string 3))
+ (unless (stringp docstring)
+ (push docstring body)
+ (setq docstring "TODO"))
+ (let ((names (doom-enlist name))
+ (optlist (cl-remove-if-not #'listp speclist))
+ (arglist (cl-remove-if #'listp speclist))
+ (plist (cl-loop for (key val) on body by #'cddr
+ if (keywordp key)
+ nconc (list key val) into plist
+ else return plist)))
+ `(let ((name ',(car names))
+ (aliases ',(cdr names))
+ (plist ',plist))
+ (when doom--cli-group
+ (setq plist (plist-put plist :group doom--cli-group)))
+ (puthash
+ name
+ (make-doom-cli (symbol-name name)
+ :desc ,docstring
+ :aliases (mapcar #'symbol-name aliases)
+ :arglist ',arglist
+ :optlist ',optlist
+ :plist plist
+ :fn
+ (lambda (--alist--)
+ (let ,(cl-loop for opt in speclist
+ for optsym = (if (listp opt) (car opt) opt)
+ unless (memq optsym cl--lambda-list-keywords)
+ collect (list optsym `(cdr (assq ',optsym --alist--))))
+ ,@(unless (plist-get plist :bare)
+ '((unless doom-init-p
+ (doom-initialize 'force)
+ (doom-initialize-modules))))
+ ,@body)))
+ doom--cli-commands)
+ (when aliases
+ (mapc (doom-rpartial #'puthash name doom--cli-commands)
+ aliases)))))
(defmacro defcligroup! (name docstring &rest body)
- "TODO"
+ "Declare all enclosed cli commands are part of the NAME group."
(declare (indent defun) (doc-string 2))
`(let ((doom--cli-group ,name))
(puthash doom--cli-group ,docstring doom--cli-groups)
,@body))
-(defmacro defcli! (names arglist docstring &rest body)
- "Define a dispatcher command. COMMAND is a symbol or a list of symbols
-representing the aliases for this command. DESC is a string description. The
-first line should be short (under 60 letters), as it will be displayed for
-bin/doom help.
-
-BODY will be run when this dispatcher is called."
- (declare (indent defun) (doc-string 3))
- (let* ((names (mapcar #'symbol-name (doom-enlist names)))
- (fn (intern (format "doom-cli-%s" (car names))))
- (plist (cl-loop while (keywordp (car body))
- collect (pop body)
- collect (pop body))))
- (macroexp-progn
- (reverse
- `((let ((plist ',plist))
- (setq plist (plist-put plist :aliases ',(cdr names)))
- (unless (or (plist-member plist :group)
- (null doom--cli-group))
- (plist-put plist :group doom--cli-group))
- (puthash ,(car names) plist doom--cli-commands))
- (defun ,fn ,arglist
- ,docstring
- ,@body))))))
-
;;
-;;; Dispatch commands
+;;; CLI Commands
-;; Load all of our subcommands
-(defcli! (refresh re) (&rest args)
+(load! "cli/help")
+(load! "cli/install")
+
+(defcli! (refresh re)
+ ((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary"))
"Ensure Doom is properly set up.
This is the equivalent of running autoremove, install, autoloads, then
@@ -165,36 +231,25 @@ It will ensure that unneeded packages are removed, all needed packages are
installed, autoloads files are up-to-date and no byte-compiled files have gone
stale."
(print! (green "Initiating a refresh of Doom Emacs...\n"))
- (let ((force-p (or (member "-f" args)
- (member "--force" args)))
- success)
+ (let (success)
(when (file-exists-p doom-env-file)
- (doom-reload-env-file 'force))
- (doom-reload-core-autoloads force-p)
+ (doom-cli-reload-env-file 'force))
+ (doom-cli-reload-core-autoloads (not if-necessary-p))
(unwind-protect
(progn
- (and (doom-packages-install doom-auto-accept)
+ (and (doom-cli-packages-install)
(setq success t))
- (and (doom-packages-rebuild doom-auto-accept)
+ (and (doom-cli-packages-build)
(setq success t))
- (and (doom-packages-purge nil 'builds-p nil doom-auto-accept)
+ (and (doom-cli-packages-purge nil 'builds-p nil)
(setq success t)))
- (doom-reload-package-autoloads (or success force-p))
- (doom-byte-compile nil 'recompile))
+ (doom-cli-reload-package-autoloads (or success (not if-necessary-p)))
+ (doom-cli-byte-compile nil 'recompile))
t))
-
-;; Load all of our subcommands
-(load! "cli/install")
-
(defcligroup! "Diagnostics"
"For troubleshooting and diagnostics"
- (defcli! (doctor doc) ()
- "Checks for issues with your environment & Doom config.
-
-Use the doctor to diagnose common problems or list missing dependencies in
-enabled modules.")
-
+ (load! "cli/doctor")
(load! "cli/debug")
(load! "cli/test"))
@@ -205,8 +260,8 @@ enabled modules.")
(load! "cli/packages")
(load! "cli/autoloads"))
-(defcligroup! "Byte compilation"
- "For byte-compiling Doom and your config"
+(defcligroup! "Compilation"
+ "For compiling Doom and your config"
(load! "cli/byte-compile"))
(defcligroup! "Utilities"
@@ -214,7 +269,7 @@ enabled modules.")
(defcli! run ()
"Run Doom Emacs from bin/doom's parent directory.
-All arguments are passed on to Emacs (except for -p and -e).
+All arguments are passed on to Emacs.
doom run
doom run -nw init.el
diff --git a/core/core-lib.el b/core/core-lib.el
index f605a626c..caed77b53 100644
--- a/core/core-lib.el
+++ b/core/core-lib.el
@@ -3,41 +3,6 @@
(require 'cl-lib)
(require 'subr-x)
-;; DEPRECATED Polyfills
-(unless EMACS26+
- (with-no-warnings
- ;; `kill-current-buffer' was introduced in Emacs 26
- (defalias 'kill-current-buffer #'kill-this-buffer)
- ;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we alias
- ;; them for 25 users.
- (defalias 'if-let* #'if-let)
- (defalias 'when-let* #'when-let)
-
- ;; `mapcan' was introduced in 26.1. `cl-mapcan' isn't a perfect replacement,
- ;; but it's close enough.
- (defalias 'mapcan #'cl-mapcan)
-
- (defun alist-get (key alist &optional default remove testfn)
- "Return the value associated with KEY in ALIST.
-If KEY is not found in ALIST, return DEFAULT.
-Use TESTFN to lookup in the alist if non-nil. Otherwise, use `assq'.
-
-This is a generalized variable suitable for use with `setf'.
-When using it to set a value, optional argument REMOVE non-nil
-means to remove KEY from ALIST if the new value is `eql' to DEFAULT."
- (ignore remove) ;;Silence byte-compiler.
- (let ((x (if (not testfn)
- (assq key alist)
- ;; In Emacs<26, `assoc' has no testfn arg, so we have to
- ;; implement it ourselves
- (if testfn
- (cl-loop for entry in alist
- if (funcall testfn key entry)
- return entry)
- (assoc key alist)))))
- (if x (cdr x) default)))))
-
-
;;
;;; Helpers
diff --git a/core/core-packages.el b/core/core-packages.el
index 04c941adc..fd5bb0a1a 100644
--- a/core/core-packages.el
+++ b/core/core-packages.el
@@ -161,15 +161,20 @@ missing) and shouldn't be deleted.")
(push func options)
(print! "%2s) %s" (length options) desc)))))
(terpri)
- (let ((answer
- (read-number (format! "How to proceed? (%s) "
- (mapconcat #'number-to-string
- (number-sequence 1 (length options))
- ", "))))
- fn)
- (setq options (nreverse options))
- (while (not (setq fn (nth (1- answer) options)))
- (print! "%s is not a valid answer, try again." answer))
+ (let ((options (nreverse options))
+ answer fn)
+ (while
+ (not
+ (setq
+ fn (ignore-errors
+ (nth (1- (setq answer
+ (read-number
+ (format! "How to proceed? (%s) "
+ (mapconcat #'number-to-string
+ (number-sequence 1 (length options))
+ ", ")))))
+ options))))
+ (print! (warn "%s is not a valid answer, try again.") answer))
(funcall fn))))))
@@ -249,7 +254,7 @@ necessary package metadata is initialized and available for them."
;;; Module package macros
(cl-defmacro package!
- (name &rest plist &key built-in _recipe disable ignore _freeze)
+ (name &rest plist &key built-in recipe ignore _disable _freeze)
"Declares a package and how to install it (if applicable).
This macro is declarative and does not load nor install packages. It is used to
@@ -277,6 +282,13 @@ Accepts the following properties:
Returns t if package is successfully registered, and nil if it was disabled
elsewhere."
(declare (indent defun))
+ (when (and recipe (keywordp (car-safe recipe)))
+ (plist-put! plist :recipe `(quote ,recipe)))
+ (when built-in
+ (when (and (not ignore) (equal built-in '(quote prefer)))
+ (setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path)))
+ (plist-delete! plist :built-in)
+ (plist-put! plist :ignore built-in))
`(let* ((name ',name)
(plist (cdr (assq name doom-packages))))
(let ((module-list (plist-get plist :modules))
@@ -287,33 +299,23 @@ elsewhere."
(list module)
nil))))
- ;; Handle :built-in
- (let ((built-in ,built-in))
- (unless ,ignore
- (when built-in
- (doom-log "Ignoring built-in package %S" name)
- (when (eq built-in 'prefer)
- (setq built-in (locate-library (symbol-name name) nil doom--initial-load-path))))
- (plist-put! plist :ignore built-in)))
-
- ;; DEPRECATED Translate :fetcher to :host
- (with-plist! plist (recipe)
- (with-plist! recipe (fetcher)
- (when fetcher
- (message "%s\n%s"
- (format "WARNING: The :fetcher property was used for the %S package."
- name)
- "This property is deprecated. Replace it with :host.")
- (plist-put! recipe :host fetcher)
- (plist-delete! recipe :fetcher))
- (plist-put! plist :recipe recipe)))
-
- (doplist! ((prop val) ',plist plist)
+ (doplist! ((prop val) (list ,@plist) plist)
(unless (null val)
(plist-put! plist prop val)))
+ ;; Some basic key validation; error if you're not using a valid key
+ (condition-case e
+ (cl-destructuring-bind
+ (&key _local-repo _files _flavor _no-build
+ _type _repo _host _branch _remote _nonrecursive _fork _depth)
+ (plist-get plist :recipe))
+ (error
+ (signal 'doom-package-error
+ (cons ,(symbol-name name)
+ (error-message-string e)))))
+
(setf (alist-get name doom-packages) plist)
- (if (not ,disable) t
+ (if (not (plist-get plist :disable)) t
(doom-log "Disabling package %S" name)
(cl-pushnew name doom-disabled-packages)
nil)))
diff --git a/core/core-projects.el b/core/core-projects.el
index f37babe59..e2920934b 100644
--- a/core/core-projects.el
+++ b/core/core-projects.el
@@ -72,9 +72,9 @@ Emacs.")
;; 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")
- (put 'projectile-grep 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/grep instead")
+ (put 'projectile-ag 'disabled "Use +{ivy,helm}/project-search instead")
+ (put 'projectile-ripgrep 'disabled "Use +{ivy,helm}/project-search instead")
+ (put 'projectile-grep 'disabled "Use +{ivy,helm}/project-search 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)
diff --git a/core/core-ui.el b/core/core-ui.el
index eb82cb3e5..4ac1f96ff 100644
--- a/core/core-ui.el
+++ b/core/core-ui.el
@@ -366,9 +366,7 @@ treat Emacs as a non-application window."
(setq ansi-color-for-comint-mode t)
-(use-package! compile
- :defer t
- :config
+(after! compile
(setq compilation-always-kill t ; kill compilation process before starting another
compilation-ask-about-save nil ; save all buffers on `compile'
compilation-scroll-output 'first-error)
@@ -376,9 +374,7 @@ treat Emacs as a non-application window."
(add-hook 'compilation-filter-hook #'doom-apply-ansi-color-to-compilation-buffer-h))
-(use-package! ediff
- :defer t
- :config
+(after! ediff
(setq ediff-diff-options "-w" ; turn off whitespace checking
ediff-split-window-function #'split-window-horizontally
ediff-window-setup-function #'ediff-setup-windows-plain)
@@ -512,79 +508,6 @@ treat Emacs as a non-application window."
(defun doom-enable-line-numbers-h () (display-line-numbers-mode +1))
(defun doom-disable-line-numbers-h () (display-line-numbers-mode -1))
-;; DEPRECATED `nlinum' is used for Emacs 25 users; 26+ has native line numbers.
-(use-package! nlinum
- ;; Line number column. A faster (or equivalent, in the worst case) line number
- ;; plugin than `linum-mode'.
- :unless EMACS26+
- :defer t
- :init
- (defvar doom-line-number-lpad 4
- "How much padding to place before line numbers.")
- (defvar doom-line-number-rpad 1
- "How much padding to place after line numbers.")
- (defvar doom-line-number-pad-char 32
- "Character to use for padding line numbers.
-
-By default, this is a space character. If you use `whitespace-mode' with
-`space-mark', the whitespace in line numbers will be affected (this can look
-ugly). In this case, you can change this to ?\u2002, which is a unicode
-character that looks like a space that `whitespace-mode' won't affect.")
- :config
- (setq nlinum-highlight-current-line t)
-
- ;; Fix lingering hl-line overlays (caused by nlinum)
- (add-hook! 'hl-line-mode-hook
- (remove-overlays (point-min) (point-max) 'face 'hl-line))
-
- (defun doom-nlinum-format-fn (line _width)
- "A more customizable `nlinum-format-function'. See `doom-line-number-lpad',
-`doom-line-number-rpad' and `doom-line-number-pad-char'. Allows a fix for
-`whitespace-mode' space-marks appearing inside the line number."
- (let ((str (number-to-string line)))
- (setq str (concat (make-string (max 0 (- doom-line-number-lpad (length str)))
- doom-line-number-pad-char)
- str
- (make-string doom-line-number-rpad doom-line-number-pad-char)))
- (put-text-property 0 (length str) 'face
- (if (and nlinum-highlight-current-line
- (= line nlinum--current-line))
- 'nlinum-current-line
- 'linum)
- str)
- str))
- (setq nlinum-format-function #'doom-nlinum-format-fn)
-
- (add-hook! 'nlinum-mode-hook
- (defun doom-init-nlinum-width-h ()
- "Calculate line number column width beforehand (optimization)."
- (setq nlinum--width
- (length (save-excursion (goto-char (point-max))
- (format-mode-line "%l")))))))
-
-(use-package! nlinum-hl
- ;; Fixes disappearing line numbers in nlinum and other quirks
- :unless EMACS26+
- :after nlinum
- :config
- ;; With `markdown-fontify-code-blocks-natively' enabled in `markdown-mode',
- ;; line numbers tend to vanish next to code blocks.
- (advice-add #'markdown-fontify-code-block-natively
- :after #'nlinum-hl-do-markdown-fontify-region)
- ;; When using `web-mode's code-folding an entire range of line numbers will
- ;; vanish in the affected area.
- (advice-add #'web-mode-fold-or-unfold :after #'nlinum-hl-do-generic-flush)
- ;; Changing fonts can leave nlinum line numbers in their original size; this
- ;; forces them to resize.
- (add-hook 'after-setting-font-hook #'nlinum-hl-flush-all-windows))
-
-(use-package! nlinum-relative
- :unless EMACS26+
- :defer t
- :config
- (setq nlinum-format " %d ")
- (add-hook 'evil-mode-hook #'nlinum-relative-setup-evil))
-
;;
;;; Theme & font
diff --git a/core/core.el b/core/core.el
index 5321799fd..6bc56a172 100644
--- a/core/core.el
+++ b/core/core.el
@@ -1,14 +1,13 @@
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
-(when (version< emacs-version "25.3")
- (error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher"
+(when (version< emacs-version "26.1")
+ (error "Detected Emacs %s. Doom only supports Emacs 26.1 and higher"
emacs-version))
(defconst doom-version "2.0.9"
"Current version of Doom Emacs.")
-(defconst EMACS26+ (> emacs-major-version 25))
-(defconst EMACS27+ (> emacs-major-version 26))
+(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)))
@@ -29,6 +28,8 @@
;; Load the bare necessities
(require 'core-lib)
+(autoload 'doom-initialize-packages "core-packages")
+
;;
;;; Global variables
@@ -209,6 +210,7 @@ users).")
tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/")
tramp-backup-directory-alist backup-directory-alist
tramp-persistency-file-name (concat doom-cache-dir "tramp-persistency.el")
+ tramp-histfile-override (concat doom-cache-dir "tramp-histfile.el")
url-cache-directory (concat doom-cache-dir "url/")
url-configuration-directory (concat doom-etc-dir "url/")
gamegrid-user-score-file-directory (concat doom-etc-dir "games/"))
@@ -426,35 +428,35 @@ in interactive sessions, nil otherwise (but logs a warning)."
(let (command-switch-alist)
(load (substring file 0 -3) 'noerror 'nomessage))
((debug error)
- (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))))))
+ (message "Autoload file error: %s -> %s" (file-name-nondirectory file) e)
+ nil)))
(defun doom-load-envvars-file (file &optional noerror)
"Read and set envvars from FILE."
(if (not (file-readable-p file))
(unless noerror
(signal 'file-error (list "Couldn't read envvar file" file)))
- (let (vars)
+ (let (environment)
(with-temp-buffer
- (insert-file-contents file)
- (while (re-search-forward "\n *\\([^#][^= \n]+\\)=" nil t)
- (save-excursion
- (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
+ (save-excursion
+ (insert "\n")
+ (insert-file-contents file))
+ (while (re-search-forward "\n *\\([^#= \n]*\\)=" nil t)
+ (push (buffer-substring
+ (match-beginning 1)
+ (1- (or (save-excursion
+ (when (re-search-forward "^\\([^= ]+\\)=" nil t)
+ (line-beginning-position)))
+ (point-max))))
+ environment)))
+ (when environment
(setq-default
+ process-environment (nreverse environment)
exec-path (append (parse-colon-path (getenv "PATH"))
(list exec-directory))
shell-file-name (or (getenv "SHELL")
shell-file-name))
- (nreverse vars)))))
+ process-environment))))
(defun doom-initialize (&optional force-p)
"Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).
@@ -504,16 +506,12 @@ to least)."
(let (;; `doom-autoload-file' tells Emacs where to load all its functions
;; from. This includes everything in core/autoload/*.el and autoload
;; files in enabled modules.
- (core-autoloads-p
- (with-demoted-errors "Core autoload error: %s"
- (doom-load-autoloads-file doom-autoload-file)))
+ (core-autoloads-p (doom-load-autoloads-file doom-autoload-file))
;; Loads `doom-package-autoload-file', which loads a concatenated
;; package autoloads file which caches `load-path', `auto-mode-alist',
;; `Info-directory-list', and `doom-disabled-packages'. A big
;; reduction in startup time.
- (pkg-autoloads-p
- (with-demoted-errors "Package autoload error: %s"
- (doom-load-autoloads-file doom-package-autoload-file))))
+ (pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file)))
(if (and core-autoloads-p pkg-autoloads-p (not force-p))
;; In case we want to use package.el or straight via M-x
@@ -524,12 +522,6 @@ to least)."
(require 'core-packages)
(doom-initialize-packages)))
- ;; 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
diff --git a/core/doctor.el b/core/doctor.el
deleted file mode 100644
index 1a5f5fc0e..000000000
--- a/core/doctor.el
+++ /dev/null
@@ -1,61 +0,0 @@
-;;; core/doctor.el -*- lexical-binding: t; -*-
-
-(defun file-size (file &optional dir)
- (setq file (expand-file-name file dir))
- (when (file-exists-p file)
- (/ (nth 7 (file-attributes file))
- 1024.0)))
-
-;; Check for oversized problem files in cache that may cause unusual/tremendous
-;; delays or freezing. This shouldn't happen often.
-(dolist (file (list "savehist"
- "projectile.cache"))
- (let* ((path (expand-file-name file doom-cache-dir))
- (size (file-size path)))
- (when (and (numberp size) (> size 2000))
- (warn! "%s is too large (%.02fmb). This may cause freezes or odd startup delays"
- (file-relative-name path doom-core-dir)
- (/ size 1024))
- (explain! "Consider deleting it from your system (manually)"))))
-
-(unless (ignore-errors (executable-find doom-projectile-fd-binary))
- (warn! "Couldn't find the `fd' binary; project file searches will be slightly slower")
- (unless (executable-find "rg")
- (warn! "Couldn't find the `rg' binary either; project file searches will be even slower")))
-
-(let ((default-directory "~"))
- (require 'projectile)
- (when (cl-find-if #'projectile-file-exists-p projectile-project-root-files-bottom-up)
- (warn! "Your $HOME is recognized as a project root")
- (explain! "Doom will disable bottom-up root search, which may reduce the accuracy of project\n"
- "detection.")))
-
-;; There should only be one
-(when (and (file-equal-p doom-private-dir "~/.config/doom")
- (file-directory-p "~/.doom.d"))
- (warn! "Both %S and '~/.doom.d' exist on your system"
- (abbreviate-file-name doom-private-dir))
- (explain! "Doom will only load one of these (~/.config/doom takes precedence). Possessing\n"
- "both is rarely intentional; you should one or the other."))
-
-;; Check for fonts
-(if (not (fboundp 'find-font))
- (progn
- (warn! "Warning: unable to detect font")
- (explain! "The `find-font' function is missing. This could indicate the incorrect "
- "version of Emacs is being used!"))
- ;; all-the-icons fonts
- (let ((font-dest (pcase system-type
- (`gnu/linux (concat (or (getenv "XDG_DATA_HOME")
- "~/.local/share")
- "/fonts/"))
- (`darwin "~/Library/Fonts/"))))
- (when (and font-dest (require 'all-the-icons nil t))
- (dolist (font all-the-icons-font-families)
- (if (sh "fc-list | grep %s" font)
- (success! "Found font %s" font)
- (warn! "Warning: couldn't find %s font in %s"
- font font-dest)
- (explain! "You can install it by running `M-x all-the-icons-install-fonts' within Emacs.\n\n"
- "This could also mean you've installed them in non-standard locations, in which "
- "case feel free to ignore this warning."))))))
diff --git a/core/packages.el b/core/packages.el
index 84504da62..ff2ee9115 100644
--- a/core/packages.el
+++ b/core/packages.el
@@ -9,12 +9,6 @@
(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)
- (package! nlinum-relative))
(package! rainbow-delimiters)
(package! restart-emacs)
diff --git a/core/test/helpers.el b/core/test/helpers.el
index a4d9986eb..618087fb4 100644
--- a/core/test/helpers.el
+++ b/core/test/helpers.el
@@ -13,6 +13,8 @@
;;
;;; Buttercup extensions
+(buttercup-define-matcher-for-binary-function :to-equal-file file-equal-p)
+
(buttercup-define-matcher :to-expand-into (form expected)
(cl-destructuring-bind (form expected)
(mapcar #'funcall (list form expected))
diff --git a/core/test/test-autoload-buffers.el b/core/test/test-autoload-buffers.el
index 5c44ce232..f452783c0 100644
--- a/core/test/test-autoload-buffers.el
+++ b/core/test/test-autoload-buffers.el
@@ -19,11 +19,23 @@
(kill-buffer c)
(kill-buffer d))
- (describe "buffer-list"
- (it "should only see four buffers"
- (expect (doom-buffer-list) :to-contain-items (list a b c d))))
+ (describe "buffer lists"
+ (describe "doom-buffer-list"
+ (it "should only see four buffers"
+ (expect (doom-buffer-list) :to-contain-items (list a b c d)))))
- (describe "project-buffer-list"
+ ;; TODO predicate tests
+ (xdescribe "predicate functions"
+ (describe "doom-dired-buffer-p")
+ (describe "doom-special-buffer-p")
+ (describe "doom-temp-buffer-p")
+ (describe "doom-visible-buffer-p")
+ (describe "doom-buried-buffer-p")
+ (describe "doom-non-file-visiting-buffer-p")
+ (describe "doom-dired-buffer-p")
+ (describe "doom-buffer-frame-predicate"))
+
+ (describe "doom-project-buffer-list"
:var (projectile-projects-cache-time projectile-projects-cache)
(before-all (require 'projectile))
(after-all (unload-feature 'projectile t))
@@ -47,7 +59,7 @@
(expect (doom-project-buffer-list)
:to-have-same-items-as (buffer-list)))))
- (describe "fallback-buffer"
+ (describe "doom-fallback-buffer"
(it "returns a live buffer"
(expect (buffer-live-p (doom-fallback-buffer))))
@@ -56,12 +68,22 @@
(describe "real buffers"
(before-each
- (doom-set-buffer-real a t)
(with-current-buffer b (setq buffer-file-name "x"))
(with-current-buffer c (rename-buffer "*C*")))
- (describe "real-buffer-p"
+ (describe "doom-mark-buffer-as-real-h"
+ (with-current-buffer a
+ (doom-mark-buffer-as-real-h)
+ (expect (buffer-local-value 'doom-real-buffer-p a))))
+
+ (describe "doom-set-buffer-real"
+ (it "sets `doom-real-buffer-p' buffer-locally"
+ (doom-set-buffer-real a t)
+ (expect (buffer-local-value 'doom-real-buffer-p a))))
+
+ (describe "doom-real-buffer-p"
(it "returns t for buffers manually marked real"
+ (doom-set-buffer-real a t)
(expect (doom-real-buffer-p a)))
(it "returns t for file-visiting buffers"
(expect (doom-real-buffer-p b)))
@@ -69,7 +91,16 @@
(expect (doom-real-buffer-p c) :to-be nil)
(expect (doom-real-buffer-p d) :to-be nil)))
- (describe "real-buffer-list"
+ (describe "doom-unreal-buffer-p"
+ (it "returns t for unreal buffers"
+ (expect (doom-unreal-buffer-p c))
+ (expect (doom-unreal-buffer-p d)))
+ (it "returns nil for real buffers"
+ (doom-set-buffer-real a t)
+ (expect (not (doom-unreal-buffer-p a)))
+ (expect (not (doom-unreal-buffer-p b)))))
+
+ (describe "doom-real-buffer-list"
(it "returns only real buffers"
(expect (doom-real-buffer-list) :to-contain-items (list a b)))))
@@ -82,36 +113,48 @@
(split-window)
(switch-to-buffer b))
- (it "can match buffers by regexp"
- (expect (doom-matching-buffers "^[ac]$") :to-have-same-items-as (list a c)))
+ (describe "doom-matching-buffers"
+ (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)))
+ (describe "doom-buffers-in-mode"
+ (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-contain-items (list c d)))
+ (describe "doom-buried-buffers"
+ (it "can find all buried buffers"
+ (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)))
+ (describe "doom-visible-buffers"
+ (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
- (mapcar #'get-buffer-window (list a b)))))
+ (describe "doom-visible-windows"
+ (it "can find all visible windows"
+ (expect (doom-visible-windows)
+ :to-have-same-items-as
+ (mapcar #'get-buffer-window (list a b))))))
- (describe "kill-buffer-and-windows"
- (before-each
- (split-window) (switch-to-buffer b)
- (split-window) (switch-to-buffer a))
+ (describe "killing buffers/windows"
+ (describe "doom-kill-buffer-and-windows"
+ (before-each
+ (split-window) (switch-to-buffer b)
+ (split-window) (switch-to-buffer a))
- (it "kills the selected buffers and all its windows"
- (doom-kill-buffer-and-windows a)
- (expect (buffer-live-p a) :to-be nil)
- (expect (length (doom-visible-windows)) :to-be 1)))
+ (it "kills the selected buffers and all its windows"
+ (doom-kill-buffer-and-windows a)
+ (expect (buffer-live-p a) :to-be nil)
+ (expect (length (doom-visible-windows)) :to-be 1)))
- ;; TODO
- (xdescribe "kill-all-buffers")
- (xdescribe "kill-other-buffers")
- (xdescribe "kill-matching-buffers")
- (xdescribe "cleanup-session")))
+ ;; TODO
+ (xdescribe "doom-fixup-windows")
+ (xdescribe "doom-kill-buffer-fixup-windows")
+ (xdescribe "doom-kill-buffers-fixup-windows"))
+
+ (xdescribe "commands"
+ (describe "doom/kill-all-buffers")
+ (describe "doom/kill-other-buffers")
+ (describe "doom/kill-matching-buffers")
+ (describe "doom/kill-buried-buffers")
+ (describe "doom/kill-project-buffers"))))
diff --git a/core/test/test-autoload-files.el b/core/test/test-autoload-files.el
index 87a63edde..7429eb3c1 100644
--- a/core/test/test-autoload-files.el
+++ b/core/test/test-autoload-files.el
@@ -5,11 +5,6 @@
(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'"
@@ -96,7 +91,16 @@
(getfilename))
"LICENSE")
doom-emacs-dir)
- :to-equal (expand-file-name "LICENSE" doom-emacs-dir))))))
+ :to-equal (expand-file-name "LICENSE" doom-emacs-dir)))))
+
+ ;; TODO
+ (xdescribe "doom-glob")
+ (xdescribe "doom-path")
+ (xdescribe "doom-dir")
+ (xdescribe "doom-files-in")
+ (xdescribe "doom-file-size")
+ (xdescribe "doom-directory-size")
+ (xdescribe "doom-file-cookie-p"))
(describe "interactive file operations"
:var (src dest projectile-projects-cache-time projectile-projects-cache)
@@ -149,4 +153,12 @@
(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))))
+
+ (xdescribe "sudo {this,find} file"
+ (before-each
+ (spy-on 'find-file :and-return-value nil)
+ (spy-on 'find-alternate-file :and-return-value nil))
+
+ (describe "doom/sudo-find-file")
+ (describe "doom/sudo-this-file")))
diff --git a/core/test/test-core-keybinds.el b/core/test/test-core-keybinds.el
index 5482205b2..52fe7238d 100644
--- a/core/test/test-core-keybinds.el
+++ b/core/test/test-core-keybinds.el
@@ -4,6 +4,7 @@
(describe "core/keybinds"
(require 'core-keybinds)
+ ;; FIXME test against their side effects rather than their implementation
(describe "map!"
:var (doom--map-evil-p states-alist)
(before-each
diff --git a/core/test/test-core-lib.el b/core/test/test-core-lib.el
index 82eaa0fc1..5391070d1 100644
--- a/core/test/test-core-lib.el
+++ b/core/test/test-core-lib.el
@@ -69,13 +69,15 @@
(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))))
+ :to-equal
+ (eval-and-compile load-file-name))))
(describe "dir!"
(it "returns the executing directory"
(expect (eval-and-compile (dir!))
- :to-equal (expand-file-name "test" doom-core-dir))))
+ :to-equal
+ (eval-and-compile
+ (directory-file-name (file-name-directory load-file-name))))))
(describe "pushnew!"
(it "pushes values onto a list symbol, in order"
@@ -234,13 +236,19 @@
(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))
+ (expect 'load :to-have-been-called-with
+ (expand-file-name "path" (eval-when-compile (dir!))) nil 'nomessage))
(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)))
+ (expect 'load :to-have-been-called-with
+ (expand-file-name "path" doom-etc-dir) nil 'nomessage)))
(describe "quiet!"
+ :var (doom-debug-mode)
+ (before-each
+ (setq doom-debug-mode nil))
+
(it "suppresses output from message"
(expect (message "hello world") :to-output "hello world\n")
(expect (message "hello world") :to-output)
diff --git a/core/test/test-core-modules.el b/core/test/test-core-modules.el
index 9263f305a..f8c8966ce 100644
--- a/core/test/test-core-modules.el
+++ b/core/test/test-core-modules.el
@@ -1,7 +1,23 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-modules.el
-;;;###if nil
-;; (require 'core-modules)
+(xdescribe "core-modules"
+ (require 'core-modules)
-(xdescribe "core-modules")
+ (describe "doom!")
+ (describe "doom-modules")
+
+ (describe "doom-module-p")
+ (describe "doom-module-get")
+ (describe "doom-module-put")
+ (describe "doom-module-set")
+ (describe "doom-module-path")
+ (describe "doom-module-locate-path")
+ (describe "doom-module-from-path")
+ (describe "doom-module-load-path")
+
+ (describe "require!")
+ (describe "featurep!")
+ (describe "after!")
+ (describe "use-package!")
+ (describe "use-package-hook!"))
diff --git a/core/test/test-core-projects.el b/core/test/test-core-projects.el
index 8febc5cdd..c64304f47 100644
--- a/core/test/test-core-projects.el
+++ b/core/test/test-core-projects.el
@@ -21,14 +21,14 @@
(describe "project-root"
(it "should resolve to the project's root"
- (expect (doom-project-root doom-core-dir) :to-equal doom-emacs-dir))
+ (expect (doom-project-root doom-core-dir) :to-equal-file doom-emacs-dir))
(it "should return nil if not in a project"
(expect (doom-project-root (expand-file-name "~")) :to-be nil)))
(describe "project-expand"
(it "expands to a path relative to the project root"
- (expect (doom-project-expand "init.el" doom-core-dir)
- :to-equal (expand-file-name "init.el" (doom-project-root doom-core-dir)))))
+ (expect (doom-project-expand "init.el" doom-core-dir) :to-equal-file
+ (expand-file-name "init.el" (doom-project-root doom-core-dir)))))
(describe "project-file-exists-p!"
(let ((default-directory doom-core-dir))
diff --git a/core/test/test-core.el b/core/test/test-core.el
index 22892b4fd..0207390b4 100644
--- a/core/test/test-core.el
+++ b/core/test/test-core.el
@@ -48,21 +48,13 @@
(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)))
+ (it "loads autoloads files"
+ (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")
@@ -81,20 +73,37 @@
(expect 'require :to-have-been-called-with 'core-editor))))
(describe "doom-load-autoloads-file"
+ :var (doom-autoload-file doom-alt-autoload-file result)
(before-each
- (spy-on 'load :and-return-value t))
+ (setq doom-autoload-file (make-temp-file "doom-autoload" nil ".el"))
+ (with-temp-file doom-autoload-file)
+ (byte-compile-file doom-autoload-file))
+ (after-each
+ (delete-file doom-autoload-file)
+ (delete-file (byte-compile-dest-file doom-autoload-file)))
- (it "loads the autoloads file"
+ (it "loads the byte-compiled autoloads file if available"
(doom-load-autoloads-file doom-autoload-file)
- (expect 'load :to-have-been-called-with (file-name-sans-extension doom-autoload-file)
- 'noerror 'nomessage)))
+ (expect (caar load-history) :to-equal-file
+ (byte-compile-dest-file doom-autoload-file))
+
+ (delete-file (byte-compile-dest-file doom-autoload-file))
+ (doom-load-autoloads-file doom-autoload-file)
+ (expect (caar load-history) :to-equal-file doom-autoload-file))
+
+ (it "returns non-nil if successful"
+ (expect (doom-load-autoloads-file doom-autoload-file)))
+
+ (it "returns nil on failure or error, non-fatally"
+ (expect (doom-load-autoloads-file "/does/not/exist") :to-be nil)))
(describe "doom-load-envvars-file"
- :var (envvarfile process-environment)
+ :var (doom-env-file process-environment)
(before-each
- (setq process-environment (copy-sequence process-environment))
+ (setq process-environment nil
+ doom-env-file (make-temp-file "doom-env"))
(with-temp-file doom-env-file
- (insert "\n\n\nA=1\nB=2\nC=3\n")))
+ (insert "A=1\nB=2\nC=3\n")))
(after-each
(delete-file doom-env-file))
@@ -106,12 +115,14 @@
(expect (doom-load-envvars-file "/tmp/envvardoesnotexist" 'noerror)
:not :to-throw))
- (it "loads a well-formed envvar file"
- (expect (getenv "A") :not :to-be-truthy)
+ (it "returns the new value for `process-environment'"
(expect (doom-load-envvars-file doom-env-file)
- :to-equal '(("A" . "1") ("B" . "2") ("C" . "3")))
- (expect (getenv "A") :to-equal "1"))
+ :to-equal '("A=1" "B=2" "C=3")))
- (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))))
+ (it "alters environment variables"
+ (dolist (key '("A" "B" "C"))
+ (expect (getenv key) :not :to-be-truthy))
+ (expect (doom-load-envvars-file doom-env-file))
+ (expect (getenv "A") :to-equal "1")
+ (expect (getenv "B") :to-equal "2")
+ (expect (getenv "C") :to-equal "3"))))
diff --git a/docs/api.org b/docs/api.org
index 1d436128b..578418981 100644
--- a/docs/api.org
+++ b/docs/api.org
@@ -252,6 +252,76 @@ It is integrated into Helpful, in Doom.
:desc "Eval expression" ";" #'eval-expression)
#+END_SRC
+These are side-by-side comparisons, showing how to bind keys with and without
+~map!~:
+
+#+BEGIN_SRC elisp :eval no
+;; bind a global key
+(global-set-key (kbd "C-x y") #'do-something)
+(map! "C-x y" #'do-something)
+
+;; bind a key on a keymap
+(define-key emacs-lisp-mode (kbd "C-c p") #'do-something)
+(map! :map emacs-lisp-mode "C-c p" #'do-something)
+
+;; unbind a key defined elsewhere
+(define-key lua-mode-map (kbd "SPC m b") nil)
+(map! :map lua-mode-map "SPC m b" nil)
+
+;; bind multiple keys
+(global-set-key "C-x x" #'do-something)
+(global-set-key "C-x y" #'do-something-else)
+(global-set-key "C-x z" #'do-another-thing)
+(map! "C-x x" #'do-something
+ "C-x y" #'do-something-else
+ "C-x z" #'do-another-thing)
+
+;; bind global keys in normal mode
+(evil-define-key* 'normal 'global
+ (kbd "C-x x") #'do-something
+ (kbd "C-x y") #'do-something-else
+ (kbd "C-x z") #'do-another-thing)
+(map! :n "C-x x" #'do-something
+ :n "C-x y" #'do-something-else
+ :n "C-x z" #'do-another-thing)
+
+;; or on a deferred keymap
+(evil-define-key 'normal emacs-lisp-mode-map
+ (kbd "C-x x") #'do-something
+ (kbd "C-x y") #'do-something-else
+ (kbd "C-x z") #'do-another-thing)
+(map! :map emacs-lisp-mode-map
+ :n "C-x x" #'do-something
+ :n "C-x y" #'do-something-else
+ :n "C-x z" #'do-another-thing)
+
+;; or multiple maps
+(dolist (map (list emacs-lisp-mode go-mode-map ivy-minibuffer-map))
+ (evil-define-key '(normal insert) map
+ "a" #'a
+ "b" #'b
+ "c" #'c))
+(map! :map (emacs-lisp-mode go-mode-map ivy-minibuffer-map)
+ :ni "a" #'a
+ :ni "b" #'b
+ :ni "c" #'c)
+
+;; or in multiple states (order of states doesn't matter)
+(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
+(evil-define-key* 'insert emacs-lisp-mode-map (kbd "C-x x") #'do-something-else)
+(evil-define-key* '(visual normal insert emacs) emacs-lisp-mode-map (kbd "C-x z") #'do-another-thing)
+(map! :map emacs-lisp-mode
+ :nv "C-x x" #'do-something ; normal+visual
+ :i "C-x y" #'do-something-else ; insert
+ :vnie "C-x z" #'do-another-thing) ; visual+normal+insert+emacs
+
+;; You can nest map! calls:
+(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
+(evil-define-key* 'normal go-lisp-mode-map (kbd "C-x x") #'do-something-else)
+(map! (:map emacs-lisp-mode :nv "C-x x" #'do-something)
+ (:map go-lisp-mode :n "C-x x" #'do-something-else))
+#+END_SRC
+
*** package!
#+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources
diff --git a/docs/contributing.org b/docs/contributing.org
index e8850ecf7..46feb468f 100644
--- a/docs/contributing.org
+++ b/docs/contributing.org
@@ -59,8 +59,8 @@ things you should try first:
+ Make sure your configuration (or Doom Emacs) is *not* byte-compiled. Run ~doom
clean~ to ensure it isn't. *Byte-compilation interferes with debugging!*
-+ Run ~bin/doom refresh -f~ to ensure all plugins are installed and autoload
- files generated.
++ Run ~bin/doom refresh~ to ensure all plugins are installed and autoload files
+ generated.
+ Run ~bin/doom doctor~ to diagnose common issues with your system.
+ Check [[file:faq.org::*Common%20Issues][Common Issues]] in the FAQ to see if yours is a known issue.
+ If you happen to know what module(s) are relevant to your issue, check their
diff --git a/docs/faq.org b/docs/faq.org
index 608078e69..695ee890c 100644
--- a/docs/faq.org
+++ b/docs/faq.org
@@ -1,7 +1,7 @@
#+TITLE: Frequently Asked Questions
#+STARTUP: nofold
-* Table of Contents :TOC:
+* Table of Contents :TOC:
- [[#general][General]]
- [[#why-is-it-called-doom][Why is it called Doom?]]
- [[#does-doom-work-on-windows][Does Doom work on Windows?]]
@@ -392,7 +392,7 @@ What's more, I don't like using more tools than I need. We should not need a
second program just to make the first run comfortably.
** How do I use Doom alongside other Emacs configs?
-I recommend [[https://github.com/plexus/chemacs][Chemacs]]. You can think of it as a bootloader for Emacs. You'll [[file:getting_started.org::Alongside%20other%20Emacs%20configs%20(with%20Chemacs)][find
+I recommend [[https://github.com/plexus/chemacs][Chemacs]]. You can think of it as a bootloader for Emacs. You'll [[file:getting_started.org::*Alongside other Emacs configs (with Chemacs)][find
instructions on how to use it with Doom in the user manual]].
If you only want to try it out without affecting your current config, it is safe
@@ -439,45 +439,7 @@ summons to fight for custody of your kids.
Also, Doom's fast yo.
** What is the meaning behind Doom's naming conventions?
-Doom has a number of naming conventions that it uses in addition to the standard
-lisp conventions. Third party packages may use their own conventions as well,
-but this guide focuses only on what Doom Emacs uses:
-
-*** Lisp Naming Conventions
-The lisp conventions are simple. Symbols follow ~NAMESPACE-SYMBOLNAME~ for
-public variables/functions (e.g. ~bookmark-default-file~ or
-~electric-indent-mode~) and ~NAMESPACE--SYMBOLNAME~ for private ones (e.g.
-~byte-compile--lexical-environment~ and ~yas--tables~).
-
-~NAMESPACE~ is usually the name of the containing file or package. E.g. the
-~company~ plugin prefixes all its variables/functions with ~company-~.
-
-*** Doom Naming Conventions
-+ ~doom/NAME~ or ~+MODULE/NAME~ :: Denotes a public command designed to be used
- interactively, via =M-x= or a keybinding. e.g. ~doom/info~, ~+popup/other~,
- ~+ivy/rg~.
-+ ~doom:NAME~ :: A public evil operator, motion or command. e.g. ~+evil:align~,
- ~+ivy:rg~.
-+ ~doom-[-]NAME-h~ or ~+MODULE-[-]NAME-h~ :: A non-interactive function meant to
- be used (exclusively) as a hook. e.g. ~+cc-fontify-constants-h~,
- ~+flycheck-buffer-h~.
-+ ~doom-[-]NAME-a~ or ~+MODULE-[-]NAME-a~ :: Functions designed to be used as
- advice for other functions. e.g. ~doom-set-jump-a~,
- ~doom--fix-broken-smie-modes-a~, ~+org--babel-lazy-load-library-a~
-+ ~doom-[-]NAME-fn~ or ~+MODULE-[-]NAME-fn~ :: Indicates an [[https://en.wikipedia.org/wiki/Strategy_pattern][strategy]] function. A
- good rule of thumb for what makes a strategy function is: is it
- interchangeable? Can it be replaced with another function with a matching
- signature? e.g. ~+lookup-dumb-jump-backend-fn~, ~+magit-display-buffer-fn~,
- ~+workspaces-set-project-action-fn~
-+ ~abc!~ :: A public Doom "autodef" function or macro. An autodef should always
- be defined, even if its containing module is disabled (i.e. they will not
- throw a void-function error). The purpose of this is to avoid peppering module
- configs with conditionals or `after!` blocks before using their APIs. They
- should noop if their module is disabled, and should be zero-cost in the case
- their module is disabled.
-
- Autodefs usually serve to configure Doom or a module. e.g. ~after!~,
- ~set-company-backends!~, ~set-evil-initial-state!~
+You'll find [[file:contributing.org::*Conventions][an overview of Doom's code conventions]] in the [[file:contributing.org][contributing guide]].
** How can I contribute to/support Doom?
Take a look at the [[file:contributing.org][Contributing guide]].
@@ -503,7 +465,11 @@ Canonically, your private config is kept in =~/.doom.d/= or =~/.config/doom/=.
Doom will prioritize =~/.config/doom=, if it exists. This directory is referred
to as your ~$DOOMDIR~.
-TL;DR. Put all your private configuration in =$DOOMDIR/config.el=.
+Your private config is typically comprised of an =init.el=, =config.el= and
+=packages.el= file. Put all your config in =config.el=, install packages by
+adding ~package!~ declarations to =packagse.el=, and enable/disable modules in
+you ~doom!~ block, which should have been created in your =init.el= when you
+first ran ~doom install~.
Check out the [[file:getting_started.org::Customize][Customize section]] in the [[file:getting_started.org][Getting Started]] guide for details.
@@ -523,7 +489,7 @@ semicolons:
Remember to run ~bin/doom refresh~ afterwards, on the command line, to sync your
module list with Doom.
-You can find a comprehensive list of modules in the [[file:../modules/README.org][Module Index]].
+You can find a comprehensive list of modules in the [[file:index.org::*Module list][Module Index]].
** How do I install a package from ELPA?
Add a ~package!~ declaration to =~/.doom.d/packages.el= for each package you
@@ -539,8 +505,8 @@ You'll find more information in the "[[file:getting_started.org::*Installing%20p
Started]] guide.
** How do I install a package from github/another source?
-The ~package!~ macro can be passed a MELPA style ~:recipe~, allowing you to
-install packages from just about anywhere:
+The ~package!~ macro can be passed a MELPA style recipe, allowing you to install
+packages from just about anywhere:
#+BEGIN_SRC elisp
(package! evil :recipe (:host github :repo "hlissner/my-evil-fork"))
@@ -562,14 +528,15 @@ You'll find more information in the "[[file:getting_started.org::*Installing%20p
section of the [[file:getting_started.org][Getting Started]] guide.
** How do I change where an existing package is installed from?
-~package!~ declarations in your private config have precedence over modules
-(even your own). Simply add a new one for that package with the new recipe.
+~package!~ declarations in your private =packages.el= file have precedence over
+modules (even your own). Simply add a new one for that package with the new
+recipe.
You'll find more information in the "[[file:getting_started.org::*Changing%20a%20built-in%20recipe%20for%20a%20package][Changing a built-in recipe for a package]]"
section of the [[file:getting_started.org][Getting Started]] guide.
** How do I disable a package completely?
-The ~package!~ macro has a ~:disable~ property:
+With the ~package!~ macro's ~:disable~ property:
#+BEGIN_SRC elisp
;;; in DOOMDIR/packages.el
@@ -630,8 +597,8 @@ At the moment, the only difference between the two is that ~doom-theme~ is
loaded when Emacs has finished initializing at startup and ~load-theme~ loads
the theme immediately. Which you choose depends on your needs, but I recommend
setting ~doom-theme~ because, if I later discover a better way to load themes, I
-can easily change how Doom uses ~doom-theme~, but I can't control how you use
-the ~load-theme~ function.
+can easily change how Doom uses ~doom-theme~, but I can't (easily) control how
+you use the ~load-theme~ function.
*** Installing a third party theme
To install a theme from a third party plugin, say, [[https://github.com/bbatsov/solarized-emacs][solarized]], you need only
@@ -670,60 +637,12 @@ e.g.
#+END_SRC
** How do I bind my own keys (or change existing ones)?
-The ~map!~ macro is recommended; it is a convenience macro that acts as a
-wrapper around ~define-key~, ~global-set-key~, ~local-set-key~ and
-~evil-define-key~.
+The ~map!~ macro is recommended; it is a convenience macro that wraps around
+Emacs' (and evil's) keybinding API, i.e. ~define-key~, ~global-set-key~,
+~local-set-key~ and ~evil-define-key~.
-#+BEGIN_SRC emacs-lisp
-;; bind a global key
-(global-set-key (kbd "C-x y") #'do-something)
-(map! "C-x y" #'do-something)
-
-;; bind a key on a keymap
-(define-key emacs-lisp-mode (kbd "C-c p") #'do-something)
-(map! :map emacs-lisp-mode "C-c p" #'do-something)
-
-;; unbind a key defined elsewhere
-(define-key lua-mode-map (kbd "SPC m b") nil)
-(map! :map lua-mode-map "SPC m b" nil)
-
-;; bind multiple keys
-(map! "C-x x" #'do-something
- "C-x y" #'do-something-else
- "C-x z" #'do-another-thing)
-#+END_SRC
-
-Evil users can also define keys in particular evil states:
-
-#+BEGIN_SRC emacs-lisp
-;; bind global keys in normal mode
-(map! :n "C-x x" #'do-something
- :n "C-x y" #'do-something-else
- :n "C-x z" #'do-another-thing)
-
-;; or on a keymap
-(map! :map emacs-lisp-mode
- :n "C-x x" #'do-something
- :n "C-x y" #'do-something-else
- :n "C-x z" #'do-another-thing)
-
-;; or multiple maps
-(map! :map (emacs-lisp-mode go-mode-map ivy-minibuffer-map) ...)
-
-;; or in multiple states (order of states doesn't matter)
-(map! :map emacs-lisp-mode
- :nv "C-x x" #'do-something ; normal+visual
- :i "C-x y" #'do-something-else ; insert
- :vnie "C-x z" #'do-another-thing) ; visual+normal+insert+emacs
-
-;; You can nest map! calls:
-(map! (:map emacs-lisp-mode :nv "C-x x" #'do-something)
- (:map go-lisp-mode :n "C-x x" #'do-something-else))
-#+END_SRC
-
-~map!~ supports other properties, like ~:after~, ~:when~, ~:prefix~ and ~:desc~.
-Look at ~map!~'s documentation for more information (=SPC h f map!= for evil
-users, =C-h f map!= otherwise).
+You'll find comprehensive examples of ~map!~'s usage in its documentation (via
+=SPC h f map!= or =C-h f map!= -- also found [[file:api.org][in docs/api]]).
You'll find a more comprehensive example of ~map!~'s usage in
[[file:../modules/config/default/+evil-bindings.el][config/default/+evil-bindings.el]].
@@ -845,10 +764,10 @@ tools for experienced Emacs users to skirt around it (most of the time):
Doom provides ~M-x doom/reload~ for your convenience, which will run ~doom
refresh~, restart the Doom initialization process, and re-evaluate your
personal config, but this won't clear pre-existing state. That may or may not
- be a problem, this hasn't be thoroughly tested, and Doom cannot anticipate
+ be a problem, this hasn't be thoroughly tested and Doom cannot anticipate
complications arising from your private config.
- If you intend to use ~doom/reload~, try to design your config to be
+ If you intend to use ~doom/reload~, you must design your config to be
idempotent.
- Many ~bin/doom~ commands are available as elisp commands with the ~doom//*~
prefix. e.g. ~doom//refresh~, ~doom//update~, etc. Feel free to use them, but
@@ -868,36 +787,38 @@ deprecated. It forwards to ~bin/doom~ anyway.
a shell script incapable of sentience and thus incapable of retaining, much less
divulging, your secrets to others).
-You can run ~bin/doom help~ to see what it's capable of, but here are the
-highlights:
+You can run ~bin/doom help~ to see what it's capable of, but here are some
+commands that you may find particularly useful:
+ ~doom doctor~ :: Diagnose common issues in your environment and list missing
external dependencies for your enabled modules.
-+ ~doom install~ :: Install any missing packages.
-+ ~doom update~ :: Update all packages that Doom's (enabled) modules use.
+ ~doom refresh~ :: Ensures that all missing packages are installed, orphaned
packages are removed, and metadata properly generated.
++ ~doom install~ :: Install any missing packages.
++ ~doom update~ :: Update all packages that Doom's (enabled) modules use.
+ ~doom env~ :: Regenerates your envvar file, which contains a snapshot of your
shell environment for Doom Emacs to load on startup. You need to run this for
changes to your shell environment to take effect.
-+ ~doom purge~ :: Purge orphaned packages (i.e. ones that aren't needed anymore)
- and regraft your repos.
++ ~doom purge -g~ :: Purge orphaned packages (i.e. ones that aren't needed
+ anymore) and regraft your repos.
+ ~doom upgrade~ :: Upgrade Doom to the latest version (then update your
packages). This is equivalent to:
- #+BEGIN_SRC bash
- git pull
- doom refresh
- doom update
- #+END_SRC
+ #+BEGIN_SRC bash
+ git pull
+ doom refresh
+ doom update
+ #+END_SRC
** When to run ~doom refresh~
As a rule of thumb you should run ~doom refresh~ whenever you:
-+ Update Doom (with ~git pull~),
++ Update Doom with ~git pull~ instead of ~doom upgrade~,
+ Change your ~doom!~ block in =$DOOMDIR/init.el=,
-+ Change the autoload files in any module (or =$DOOMDIR=),
++ Change autoload files in any module (or =$DOOMDIR=),
+ Or change the packages.el file in any module (or =$DOOMDIR=).
++ Install an Emacs package or dependency outside of Emacs (i.e. through your OS
+ package manager).
If anything is misbehaving, it's a good idea to run ~doom refresh~ first. ~doom
refresh~ is responsible for regenerating your autoloads file (which tells Doom
@@ -1066,12 +987,12 @@ manually (e.g. by double-clicking each file in explorer).
** ~void-variable~ and ~void-function~ errors on startup
The most common culprit for these types of errors are:
-1. An out-of-date autoloads file. To regenerate it, run ~doom refresh -f~.
+1. An out-of-date autoloads file. To regenerate it, run ~doom refresh~.
To avoid this issue, remember to run ~doom refresh~ whenever you modify your
~doom!~ block in =~/.doom.d/init.el=, or add ~package!~ declarations to
- =~/.doom.d/packages.el=. If you modify =~/.emacs.d/.local= by hand, for
- whatever reason, run ~doom refresh -f~ to bypass caches and modify-checks.
+ =~/.doom.d/packages.el=. Or if you modify =~/.emacs.d/.local= by hand, for
+ whatever reason.
See ~doom help refresh~ for details on what this command does and when you
should use it.
diff --git a/docs/getting_started.org b/docs/getting_started.org
index cb9940b6a..54de52c43 100644
--- a/docs/getting_started.org
+++ b/docs/getting_started.org
@@ -59,8 +59,8 @@
- [[#variables-functions-faces-etc][Variables, functions, faces, etc.]]
- [[#for-doom-modules-packages-autodefs-etc][For Doom Modules, packages, autodefs, etc.]]
- [[#how-to-extract-a-backtrace-from-an-error][How to extract a backtrace from an error]]
- - [[#enabling-debug-on-error][Enabling `debug-on-error`]]
- - [[#a-backtrace-from-bindoom][A backtrace from `bin/doom`]]
+ - [[#enabling-debug-on-error][Enabling ~debug-on-error~]]
+ - [[#a-backtrace-from-bindoom][A backtrace from ~bin/doom~]]
- [[#evaluating-elisp-on-the-fly][Evaluating Elisp on-the-fly]]
- [[#how-to-determine-the-origin-of-a-bug][How to determine the origin of a bug]]
- [[#testing-in-dooms-sandbox][Testing in Doom's sandbox]]
@@ -187,8 +187,8 @@ cause you issues later on. Do not use them:
+ XEmacs
*** On Windows
-*Support for Windows is immature,* so your mileage will vary. [[https://www.reddit.com/r/emacs/comments/6bw35d/doom_an_emacsd_to_espouse_and_surpass_vim_in_any/dhtw32t/][Some have reported
-success]] with installing Doom via WSL, chocolatey on git-bash or cygwin.
+*Support for Windows is immature,* so your mileage will vary. Some have reported
+success with installing Doom via WSL, chocolatey on git-bash or cygwin.
#+BEGIN_QUOTE
If you manage to get Doom on Windows and found this wasn't enough, or could be
@@ -1080,24 +1080,23 @@ You can also evaluate code with ~eval-expression~ (=M-;= or =SPC ;=).
** How to extract a backtrace from an error
If you encounter an error while using Doom Emacs, you're probably about to head
-off and file a bug report (or request help on [[[https://discord.gg/bcZ6P3y][our Discord server]]. Before you do,
-please generate a backtrace to include with it.
+off and file a bug report (or request help on [[https://discord.gg/bcZ6P3y][our Discord server]]). Before you
+do, please generate a backtrace to include with it.
To do so you must enable ~debug-on-error~ then recreate the error.
-*** Enabling `debug-on-error`
-There are three ways to enable `debug-on-error`:
+*** Enabling ~debug-on-error~
+There are three ways to enable ~debug-on-error~:
-1. Start Emacs with `emacs --debug-init`. Use this for errors that occur at
+1. Start Emacs with ~emacs --debug-init~. Use this for errors that occur at
startup.
-2. Evil users can press SPC h d d and non-evil users can press
- =C-h d d=.
-3. If the above don't work, there's always: `M-x toggle-debug-on-error`
+2. Evil users can press =SPC h d d= and non-evil users can press =C-h d d=.
+3. If the above don't work, there's always: ~M-x toggle-debug-on-error~
-Now that `debug-on-error` is on, recreate the error. A window should pop up with
+Now that ~debug-on-error~ is on, recreate the error. A window should pop up with
a backtrace.
-*** A backtrace from `bin/doom`
+*** A backtrace from ~bin/doom~
If the error you've encountered is emitted from ~bin/doom~, you can re-run the
same command with the ~-d~ or ~--debug~ switches to force it to emit a backtrace
when an error occurs. The ~DEBUG~ environment variable will work to.
diff --git a/docs/index.org b/docs/index.org
index 9b34c9800..eadfd4f4c 100644
--- a/docs/index.org
+++ b/docs/index.org
@@ -7,9 +7,10 @@ for your own Emacs configuration or a resource for enthusiasts to learn more
about our favorite OS.
#+begin_quote
-Doom's documentation can be viewed from within Emacs by pressing = d h=
-(== is =SPC h= for evil users and =C-h= for vanilla users), or searched
-with = d /=.
+Github fails to render org links to sub-sections, so it is recommended that you
+view the documentation from within Doom Emacs by pressing = d h= (==
+is =SPC h= for evil users and =C-h= for vanilla users) or searching it with
+= d /=.
#+end_quote
* Table of Contents :TOC:
diff --git a/init.el b/init.el
index a4c8c0a88..4ddc67d78 100644
--- a/init.el
+++ b/init.el
@@ -48,7 +48,8 @@
;; And let 'er rip!
(doom-initialize)
-(unless noninteractive
+(if noninteractive
+ (doom-initialize-packages)
(doom-initialize-core)
(doom-initialize-modules)
(add-hook 'window-setup-hook #'doom-display-benchmark-h)
diff --git a/init.example.el b/init.example.el
index cebc1f191..7965ffdab 100644
--- a/init.example.el
+++ b/init.example.el
@@ -143,6 +143,7 @@
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;rest ; Emacs as a REST client
+ ;;rst ; ReST in peace
;;ruby ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good
@@ -154,28 +155,17 @@
;;web ; the tubes
:email
- ;;(mu4e +gmail) ; WIP
- ;;notmuch ; WIP
- ;;(wanderlust +gmail) ; WIP
+ ;;(mu4e +gmail)
+ ;;notmuch
+ ;;(wanderlust +gmail)
- ;; Applications are complex and opinionated modules that transform Emacs
- ;; toward a specific purpose. They may have additional dependencies and
- ;; should be loaded late.
:app
;;calendar
;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought
- ;;(write ; emacs for writers (fiction, notes, papers, etc.)
- ;; +wordnut ; wordnet (wn) search
- ;; +langtool) ; a proofreader (grammar/style check) for Emacs
+ ;;write ; emacs for writers (fiction, notes, papers, etc.)
:config
- ;; For literate config users. This will tangle+compile a config.org
- ;; literate config in your `doom-private-dir' whenever it changes.
;;literate
-
- ;; The default module sets reasonable defaults for Emacs. It also
- ;; provides a Spacemacs-inspired keybinding scheme and a smartparens
- ;; config. Use it as a reference for your own modules.
(default +bindings +smartparens))
diff --git a/modules/completion/company/config.el b/modules/completion/company/config.el
index ef84516a1..a98bcca9c 100644
--- a/modules/completion/company/config.el
+++ b/modules/completion/company/config.el
@@ -62,7 +62,7 @@
(use-package! company-box
- :when (and EMACS26+ (featurep! +childframe))
+ :when (featurep! +childframe)
:hook (company-mode . company-box-mode)
:config
(setq company-box-show-single-candidate t
diff --git a/modules/completion/company/packages.el b/modules/completion/company/packages.el
index 1e2b7a64b..91b9e88a7 100644
--- a/modules/completion/company/packages.el
+++ b/modules/completion/company/packages.el
@@ -4,5 +4,5 @@
(package! company)
(package! company-dict)
(package! company-prescient)
-(when (and EMACS26+ (featurep! +childframe))
+(when (featurep! +childframe)
(package! company-box))
diff --git a/modules/completion/helm/autoload/evil.el b/modules/completion/helm/autoload/evil.el
index da37452fc..fbcf0a9db 100644
--- a/modules/completion/helm/autoload/evil.el
+++ b/modules/completion/helm/autoload/evil.el
@@ -1,46 +1,17 @@
;;; completion/helm/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil)
-;;
-;; Project searching
-
-;;;###autoload (autoload '+helm:grep "completion/helm/autoload/evil" nil t)
-(evil-define-command +helm:grep (all-files-p query)
+;;;###autoload (autoload '+helm:project-search "completion/helm/autoload/evil" nil t)
+(evil-define-command +helm:project-search (all-files-p query)
"Ex interface for `+helm/grep'"
(interactive "")
- (+helm/grep all-files-p query))
+ (+helm/project-search all-files-p query))
-;;;###autoload (autoload '+helm:ag "completion/helm/autoload/evil" nil t)
-(evil-define-command +helm:ag (all-files-p query)
- "Ex interface for `+helm/ag'"
- (interactive "")
- (+helm/ag all-files-p query))
-
-;;;###autoload (autoload '+helm:rg "completion/helm/autoload/evil" nil t)
-(evil-define-command +helm:rg (all-files-p query)
- "Ex interface for `+helm/rg'"
- (interactive "")
- (+helm/rg all-files-p query))
-
-
-;;;###autoload (autoload '+helm:grep-from-cwd "completion/helm/autoload/evil" nil t)
-(evil-define-command +helm:grep-from-cwd (query &optional recurse-p)
+;;;###autoload (autoload '+helm:project-search-from-cwd "completion/helm/autoload/evil" nil t)
+(evil-define-command +helm:project-search-from-cwd (query &optional recurse-p)
"Ex interface for `+helm/grep-from-cwd'."
(interactive "")
- (+helm/grep-from-cwd (not recurse-p) query))
-
-;;;###autoload (autoload '+helm:ag-from-cwd "completion/helm/autoload/evil" nil t)
-(evil-define-command +helm:ag-from-cwd (query &optional recurse-p)
- "Ex interface for `+helm/ag-from-cwd'."
- (interactive "")
- (+helm/ag-from-cwd (not recurse-p) query))
-
-;;;###autoload (autoload '+helm:rg-from-cwd "completion/helm/autoload/evil" nil t)
-(evil-define-command +helm:rg-from-cwd (query &optional recurse-p)
- "Ex interface for `+helm/rg-from-cwd'."
- (interactive "")
- (+helm/rg-from-cwd (not recurse-p) query))
-
+ (+helm/project-search-from-cwd (not recurse-p) query))
;;;###autoload
(defun +helm--set-prompt-display (pos)
diff --git a/modules/completion/helm/autoload/helm.el b/modules/completion/helm/autoload/helm.el
index c132b29a2..70105d239 100644
--- a/modules/completion/helm/autoload/helm.el
+++ b/modules/completion/helm/autoload/helm.el
@@ -1,11 +1,5 @@
;;; completion/helm/autoload/helm.el -*- lexical-binding: t; -*-
-;;;###autoload
-(defun +helm/tasks (&optional _arg)
- (interactive "P")
- ;; TODO Implement `+helm/tasks'
- (error "Not implemented yet"))
-
;;;###autoload
(defun +helm/projectile-find-file ()
"Call `helm-find-files' if called from HOME, otherwise
@@ -41,70 +35,11 @@ workspace."
;;
-;; Project search
-
-(defun +helm-ag-search-args (all-files-p recursive-p)
- (list (concat "ag " (if IS-WINDOWS "--vimgrep" "--nocolor --nogroup"))
- "-S"
- (if all-files-p "-z -a")
- (unless recursive-p "--depth 1")))
-
-(defun +helm-rg-search-args (all-files-p recursive-p)
- (list "rg --no-heading --line-number --color never"
- "-S"
- (when all-files-p "-z -uu")
- (unless recursive-p "--maxdepth 1")))
-
-;;
-(defun +helm--grep-source ()
- (require 'helm-projectile)
- (helm-build-async-source (capitalize (helm-grep-command t))
- :header-name (lambda (_name) "Helm Projectile Grep (C-c ? Help)")
- :candidates-process #'helm-grep-collect-candidates
- :filter-one-by-one #'helm-grep-filter-one-by-one
- :candidate-number-limit 9999
- :nohighlight t
- :keymap helm-grep-map
- :history 'helm-grep-history
- :action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions)
- :persistent-action 'helm-grep-persistent-action
- :persistent-help "Jump to line (`C-u' Record in mark ring)"
- :requires-pattern 2))
-
-(defun +helm--grep-search (directory query prompt &optional all-files-p recursive-p)
- (let* ((default-directory directory)
- (helm-ff-default-directory directory)
- (helm-grep-in-recurse recursive-p)
- (helm-grep-ignored-files
- (unless all-files-p
- (cl-union (projectile-ignored-files-rel) grep-find-ignored-files)))
- (helm-grep-ignored-directories
- (unless all-files-p
- (cl-union (mapcar 'directory-file-name (projectile-ignored-directories-rel))
- grep-find-ignored-directories)))
- (helm-grep-default-command
- (if (and nil (eq (projectile-project-vcs) 'git))
- (format "git --no-pager grep --no-color -n%%c -e %%p %s -- %%f"
- (if recursive-p "" "--max-depth 1 "))
- (format "grep -si -a%s %%e -n%%cH -e %%p %%f %s"
- (if recursive-p " -R" "")
- (if recursive-p "." "./*"))))
- (helm-grep-default-recurse-command helm-grep-default-command))
- (setq helm-source-grep (+helm--grep-source))
- (helm :sources 'helm-source-grep
- :input query
- :prompt prompt
- :buffer "*helm grep*"
- :default-directory directory
- :keymap helm-grep-map
- :history 'helm-grep-history
- :truncate-lines helm-grep-truncate-lines)))
+;;; Project search
;;;###autoload
-(cl-defun +helm-file-search (engine &key query in all-files (recursive t))
- "Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and
-grep. If omitted, ENGINE will default to the first one it detects, in that
-order.
+(cl-defun +helm-file-search (&key query in all-files (recursive t))
+ "Conduct a file search using ripgrep.
:query STRING
Determines the initial input to search for.
@@ -114,6 +49,8 @@ order.
:recursive BOOL
Whether or not to search files recursively from the base directory."
(declare (indent defun))
+ (unless (executable-find "rg")
+ (user-error "Couldn't find ripgrep in your PATH"))
(require 'helm-ag)
(helm-ag--init-state)
(let* ((project-root (or (doom-project-root) default-directory))
@@ -121,13 +58,6 @@ order.
(default-directory directory)
(helm-ag--default-directory directory)
(helm-ag--default-target (list directory))
- (engine (or engine
- (cl-find-if #'executable-find +helm-project-search-engines
- :key #'symbol-name)
- (and (or (executable-find "grep")
- (executable-find "git"))
- 'grep)
- (user-error "No search engine specified (is ag, rg, or git installed?)")))
(query (or query
(when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
@@ -135,23 +65,20 @@ order.
(when (> (abs (- end beg)) 1)
(rxt-quote-pcre (buffer-substring-no-properties beg end)))))
""))
- (prompt (format "[%s %s] "
- (symbol-name engine)
+ (prompt (format "[rg %s] "
(cond ((file-equal-p directory project-root)
(projectile-project-name))
((file-equal-p directory default-directory)
"./")
((file-relative-name directory project-root)))))
(command
- (pcase engine
- (`ag (+helm-ag-search-args all-files recursive))
- (`rg (+helm-rg-search-args all-files recursive))
- ('grep (+helm--grep-search directory query prompt all-files recursive)
- (cl-return t))))
- (helm-ag-base-command (string-join command " ")))
+ (list "rg --no-heading --line-number --color never"
+ "-S"
+ (when all-files "-z -uu")
+ (unless recursive "--maxdepth 1")))
+ (helm-ag-base-command (string-join (delq nil command) " ")))
;; TODO Define our own sources instead
- (helm-attrset 'name (format "[%s %s] Searching %s"
- engine
+ (helm-attrset 'name (format "[rg %s] Searching %s"
(string-join (delq nil (cdr command)) " ")
(abbreviate-file-name directory))
helm-source-do-ag)
@@ -159,82 +86,39 @@ order.
(cl-letf (((symbol-function 'helm-do-ag--helm)
(lambda () (helm :sources '(helm-source-do-ag)
:prompt prompt
- :buffer "*helm-ag*"
+ :buffer "*helm-rg*"
:keymap helm-do-ag-map
:input query
:history 'helm-ag--helm-history))))
(helm-do-ag directory))))
-(defun +helm--get-command (format)
- (cl-loop for tool in (cl-remove-duplicates +helm-project-search-engines :from-end t)
- if (executable-find (symbol-name tool))
- return (intern (format format tool))))
-
;;;###autoload
(defun +helm/project-search (&optional arg initial-query directory)
- "Performs a project search from the project root.
+ "Performs a project search from the project root with ripgrep.
-Uses the first available search backend from `+helm-project-search-engines'. If
ARG (universal argument), include all files, even hidden or compressed ones, in
the search."
(interactive "P")
- (funcall (or (+helm--get-command "+helm/%s")
- #'+helm/grep)
- arg
- initial-query
- directory))
+ (+helm-file-search
+ :query initial-query
+ :in directory
+ :all-files (and (not (null arg))
+ (listp arg))))
;;;###autoload
(defun +helm/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory.
-Uses the first available search backend from `+helm-project-search-engines'. If
-ARG (universal argument), include all files, even hidden or compressed ones."
+If ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P")
- (funcall (or (+helm--get-command "+helm/%s-from-cwd")
- #'+helm/grep-from-cwd)
- arg
- initial-query))
+ (+helm-file-search
+ :query initial-query
+ :in default-directory
+ :all-files (and (not (null arg))
+ (listp arg))))
-
-;;;###autoload (autoload '+helm/rg "completion/helm/autoload/helm" nil t)
-;;;###autoload (autoload '+helm/rg-from-cwd "completion/helm/autoload/helm" nil t)
-;;;###autoload (autoload '+helm/ag "completion/helm/autoload/helm" nil t)
-;;;###autoload (autoload '+helm/ag-from-cwd "completion/helm/autoload/helm" nil t)
-;;;###autoload (autoload '+helm/grep "completion/helm/autoload/helm" nil t)
-;;;###autoload (autoload '+helm/grep-from-cwd "completion/helm/autoload/helm" nil t)
-
-(dolist (engine `(,@(cl-remove-duplicates +helm-project-search-engines :from-end t) grep))
- (defalias (intern (format "+helm/%s" engine))
- (lambda (arg &optional query directory)
- (interactive "P")
- (+helm-file-search engine
- :query query
- :in directory
- :all-files (and (not (null arg))
- (listp arg))))
- (format "Perform a project file search using %s.
-
-QUERY is a regexp. If omitted, the current selection is used. If no selection is
-active, the last known search is used.
-
-ARG is the universal argument. If a number is passed through it, e.g. C-u 3, then
-
-If ALL-FILES-P, search compressed and hidden files as well."
- engine))
-
- (defalias (intern (format "+helm/%s-from-cwd" engine))
- (lambda (arg &optional query)
- (interactive "P")
- (+helm-file-search engine
- :query query
- :in default-directory
- :all-files (and (not (null arg))
- (listp arg))))
- (format "Perform a project file search from the current directory using %s.
-
-QUERY is a regexp. If omitted, the current selection is used. If no selection is
-active, the last known search is used.
-
-If ALL-FILES-P, search compressed and hidden files as well."
- engine)))
+;;;###autoload
+(defun +helm/jump-list ()
+ "TODO"
+ (interactive)
+ (error "not implemented yet"))
diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el
index b559b34d7..071c33335 100644
--- a/modules/completion/helm/config.el
+++ b/modules/completion/helm/config.el
@@ -1,15 +1,5 @@
;;; completion/helm/config.el -*- lexical-binding: t; -*-
-(defvar +helm-project-search-engines '(rg ag)
- "What search tools for `+helm/project-search' (and `+helm-file-search' when no
-ENGINE is specified) to try, and in what order.
-
-To disable a particular tool, remove it from this list. To prioritize a tool
-over others, move it to the front of the list. Later duplicates in this list are
-silently ignored.
-
-This falls back to git-grep (then grep) if none of these available.")
-
;; Posframe (requires +childframe)
(defvar +helm-posframe-handler #'+helm-poshandler-frame-center-near-bottom-fn
"The function that determines the location of the childframe. It should return
@@ -82,7 +72,7 @@ be negative.")
(setq helm-default-prompt-display-function #'+helm--set-prompt-display))
:init
- (when (and EMACS26+ (featurep! +childframe))
+ (when (featurep! +childframe)
(setq helm-display-function #'+helm-posframe-display-fn))
(let ((fuzzy (featurep! +fuzzy)))
@@ -135,7 +125,6 @@ be negative.")
:config (helm-flx-mode +1))
-;;;###package helm-ag
(after! helm-ag
(map! :map helm-ag-edit-map :n "RET" #'compile-goto-error)
(define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort)
@@ -150,14 +139,12 @@ be negative.")
(setq helm-bookmark-show-location t)
-;;;###package helm-files
(after! helm-files
(setq helm-boring-file-regexp-list
(append (list "\\.projects$" "\\.DS_Store$")
helm-boring-file-regexp-list)))
-;;;###package helm-locate
(defvar helm-generic-files-map (make-sparse-keymap))
(after! helm-locate
(when (and IS-MAC
@@ -167,7 +154,6 @@ be negative.")
(set-keymap-parent helm-generic-files-map helm-map))
-;;;###package helm-org
(use-package! helm-org
:when (featurep! :lang org)
:defer t
@@ -178,7 +164,6 @@ be negative.")
'(org-set-tags . helm-org-completing-read-tags))))
-;;;###package helm-projectile
(use-package! helm-projectile
:commands (helm-projectile-find-file
helm-projectile-recentf
@@ -191,7 +176,6 @@ be negative.")
(set-keymap-parent helm-projectile-find-file-map helm-map))
-;;;###package swiper-helm
(after! swiper-helm
(setq swiper-helm-display-function
(lambda (buf &optional _resume) (pop-to-buffer buf)))
diff --git a/modules/completion/helm/packages.el b/modules/completion/helm/packages.el
index 870759b66..eb343b566 100644
--- a/modules/completion/helm/packages.el
+++ b/modules/completion/helm/packages.el
@@ -10,7 +10,7 @@
(package! swiper-helm)
(when (featurep! +fuzzy)
(package! helm-flx))
-(when (and EMACS26+ (featurep! +childframe))
+(when (featurep! +childframe)
(package! posframe))
(when (featurep! :lang org)
(package! helm-org))
diff --git a/modules/completion/ivy/README.org b/modules/completion/ivy/README.org
index 84c1afc9d..0bc51166f 100644
--- a/modules/completion/ivy/README.org
+++ b/modules/completion/ivy/README.org
@@ -17,7 +17,6 @@
- [[#jump-to-file-project-navigation][Jump-to-file project navigation]]
- [[#project-search--replace][Project search & replace]]
- [[#in-buffer-searching][In-buffer searching]]
- - [[#task-lookup][Task lookup]]
- [[#ivy-integration-for-various-completing-commands][Ivy integration for various completing commands]]
- [[#general][General]]
- [[#jump-to-files-buffers-or-projects][Jump to files, buffers or projects)]]
@@ -29,8 +28,7 @@
* Description
This module provides Ivy integration for a variety of Emacs commands, as well as
-a unified interface for project search and replace, powered by ag, rg,
-git-grep & grep (whichever is available).
+a unified interface for project search and replace, powered by ripgrep.
#+begin_quote
I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's
@@ -41,7 +39,6 @@ lighter, simpler and faster in many cases.
+ =+fuzzy= Enables fuzzy completion for Ivy searches.
+ =+prescient= Enables prescient filtering and sorting for Ivy searches.
+ =+childframe= Causes Ivy to display in a floating child frame, above Emacs.
- *This requires GUI Emacs 26.1+*
+ =+icons= Enables file icons for switch-{buffer,project}/find-file counsel
commands.
@@ -67,32 +64,24 @@ lighter, simpler and faster in many cases.
command)
* Prerequisites
-This module optionally depends on one of:
+This module depends on:
+ [[https://github.com/BurntSushi/ripgrep][ripgrep]] (rg)
-+ [[https://github.com/ggreer/the_silver_searcher][the_silver_searcher]] (ag)
-
-Ripgrep is recommended, but the order of its results aren't deterministic and it
-doesn't support full PCRE (at the time of writing). The_silver_searcher is a
-good alternative if either of these bother you.
-
-If none of these are installed, file search commands will use git-grep (falling
-back to grep, otherwise).
** Install
*** MacOS
#+BEGIN_SRC sh
-brew install ripgrep the_silver_searcher
+brew install ripgrep
#+END_SRC
*** Arch Linux
#+BEGIN_SRC sh :dir /sudo::
-sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher
+sudo pacman --needed --noconfirm -S ripgrep
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
-sudo zypper install ripgrep the_silver_searcher
+sudo zypper install ripgrep
#+END_SRC
* Features
@@ -108,57 +97,38 @@ https://assets.doomemacs.org/completion/ivy/projectile.png
| Keybind | Description |
|----------------------+-------------------------------------|
-| =SPC f /=, =SPC SPC= | Jump to file in project |
-| =SPC f .=, =SPC .= | Jump to file from current directory |
+| =SPC p f=, =SPC SPC= | Jump to file in project |
+| =SPC f f=, =SPC .= | Jump to file from current directory |
** Project search & replace
-This module provides interactive text search and replace using the first search
-program available on your system (rg, ag, git-grep or grep).
+This module provides interactive text search and replace using ripgrep.
| Keybind | Description |
|-----------+---------------------------------|
-| =SPC / b= | Search the current buffer |
-| =SPC / p= | Search project |
-| =SPC / d= | Search this directory |
+| =SPC s b= | Search the current buffer |
+| =SPC s p= | Search project |
+| =SPC s d= | Search this directory |
| =SPC p t= | List all TODO/FIXMEs in project |
https://assets.doomemacs.org/completion/ivy/search.png
-The ~+ivy-project-search-engines~ variable is consulted to determine which
-underlying program to check for (and in what order). It's default value is ~'(rg
-ag pt)~. If none of these are available, it will resort to =git-grep= (falling
-back to =grep= after that).
-
-To use a specific program, the following engine-specific commands are available
-(but not bound to any key by default) for searching from the project root or the
-current directory (recursively), respectively:
-
-+ ~+ivy/ag~ / ~+ivy/ag-from-cwd~
-+ ~+ivy/rg~ / ~+ivy/rg-from-cwd~
-+ ~+ivy/grep~ / ~+ivy/grep-from-cwd~
-
The universal argument (=SPC u= for evil users; =C-u= otherwise) changes the
behavior of these commands, instructing the underlying search engine to include
ignored files.
This module also provides Ex Commands for evil users:
-| Ex command | Description |
-|-----------------------+------------------------------------------------|
-| ~:ag[!] [QUERY]~ | Search project w/ ag[fn:1] |
-| ~:rg[!] [QUERY]~ | Search project w/ rg[fn:1] |
-| ~:grep[!] [QUERY]~ | Search project w/ git-grep/grep[fn:1] |
-| ~:agcwd[!] [QUERY]~ | Search this directory w/ the_silver_searcher |
-| ~:rgcwd[!] [QUERY]~ | Search this directory w/ ripgrep |
-| ~:grepcwd[!] [QUERY]~ | Search this directory w/ git-grep/grep |
+| Ex command | Description |
+|------------------------+------------------------------------------------------------------|
+| ~:pg[rep][!] [QUERY]~ | Search project (if ~!~, include hidden files) |
+| ~:pg[rep]d[!] [QUERY]~ | Search from current directory (if ~!~, don't search recursively) |
The optional BANG functions is equivalent to the universal argument for the
previous commands.
-----
-While in a search (e.g. invoked from ~+ivy:ag~ or ~:rg~), these extra
-keybindings are available to you:
+While in a search these extra keybindings are available to you:
| Keybind | Description |
|-----------+-----------------------------------------------|
@@ -176,22 +146,14 @@ https://assets.doomemacs.org/completion/ivy/search-replace.png
The =swiper= package provides an interactive buffer search powered by ivy. It
can be invoked with:
-+ =SPC / b=
++ =SPC s s=
++ =SPC s S= (uses thing at point as initial input)
+ ~:sw[iper] [QUERY]~
https://assets.doomemacs.org/completion/ivy/swiper.png
A wgrep buffer can be opened from swiper with =C-c C-e=.
-** Task lookup
-Some projects have TODO's and FIXME's littered across them. The ~+ivy/tasks~
-command allows you to search and jump to them. It can be invoked with:
-
-+ =SPC p t= (C-u = restrict search to current file)
-+ ~:todo[!]~ (BANG = restrict search to current file)
-
-https://assets.doomemacs.org/completion/ivy/todo.png
-
** Ivy integration for various completing commands
*** General
| Keybind | Description |
@@ -200,25 +162,27 @@ https://assets.doomemacs.org/completion/ivy/todo.png
| =SPC '= | Resume last ivy session |
*** Jump to files, buffers or projects)
-| Keybind | Description |
-|---------------------------------+---------------------------------------|
-| =SPC RET= | Find bookmark |
-| =SPC f .=, =SPC .= | Browse from current directory |
-| =SPC f /=, =SPC p /=, =SPC SPC= | Find file in project |
-| =SPC f r= | Find recently opened file |
-| =SPC p p= | Open another project |
-| =SPC b b=, =SPC ,= | Switch to buffer in current workspace |
-| =SPC b B=, =SPC <= | Switch to buffer |
+| Keybind | Description |
+|----------------------+---------------------------------------|
+| =SPC RET= | Find bookmark |
+| =SPC f f=, =SPC .= | Browse from current directory |
+| =SPC p f=, =SPC SPC= | Find file in project |
+| =SPC f r= | Find recently opened file |
+| =SPC p p= | Open another project |
+| =SPC b b=, =SPC ,= | Switch to buffer in current workspace |
+| =SPC b B=, =SPC <= | Switch to buffer |
*** Search
-| Keybind | Description |
-|-----------+------------------------------------------|
-| =SPC / i= | Search for symbol in current buffer |
-| =SPC / I= | Search for symbol in all similar buffers |
-| =SPC / b= | Search the current buffer |
-| =SPC / p= | Search project |
-| =SPC / d= | Search this directory |
-| =SPC p t= | List all TODO/FIXMEs in project |
+| Keybind | Description |
+|-----------+-------------------------------------------|
+| =SPC p t= | List all TODO/FIXMEs in project |
+| =SPC s b= | Search the current buffer |
+| =SPC s d= | Search this directory |
+| =SPC s D= | Search another directory |
+| =SPC s i= | Search for symbol in current buffer |
+| =SPC s p= | Search project |
+| =SPC s P= | Search another project |
+| =SPC s s= | Search the current buffer (incrementally) |
* Configuration
** TODO Enable fuzzy/non-fuzzy search for specific commands
diff --git a/modules/completion/ivy/autoload/evil.el b/modules/completion/ivy/autoload/evil.el
index 43faf2a6b..8a8125536 100644
--- a/modules/completion/ivy/autoload/evil.el
+++ b/modules/completion/ivy/autoload/evil.el
@@ -1,55 +1,14 @@
;; completion/ivy/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil)
-;;;###autoload (autoload '+ivy:swiper "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:swiper (&optional search)
- "Invoke `swiper' with SEARCH, otherwise with the symbol at point."
- (interactive "")
- (swiper search))
-
-;;;###autoload (autoload '+ivy:todo "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:todo (&optional bang)
- "An ex wrapper around `+ivy/tasks'."
- (interactive "")
- (+ivy/tasks bang))
-
-
-;;
-;; Project searching
-
-;;;###autoload (autoload '+ivy:grep "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:grep (all-files-p query)
- "Ex interface for `+ivy/grep'"
- (interactive "")
- (+ivy/grep all-files-p query))
-
-;;;###autoload (autoload '+ivy:ag "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:ag (all-files-p query)
- "Ex interface for `+ivy/ag'"
- (interactive "")
- (+ivy/ag all-files-p query))
-
-;;;###autoload (autoload '+ivy:rg "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:rg (all-files-p query)
- "Ex interface for `+ivy/rg'"
- (interactive "")
- (+ivy/rg all-files-p query))
-
-
-;;;###autoload (autoload '+ivy:grep-from-cwd "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:grep-from-cwd (query &optional recurse-p)
- "Ex interface for `+ivy/grep-from-cwd'."
+;;;###autoload (autoload '+ivy:project-search "completion/ivy/autoload/evil" nil t)
+(evil-define-command +ivy:project-search (query &optional all-files-p)
+ "Ex interface for `+ivy/project-search'."
(interactive "")
- (+ivy/grep-from-cwd (not recurse-p) query))
+ (+ivy/project-search all-files-p query))
-;;;###autoload (autoload '+ivy:ag-from-cwd "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:ag-from-cwd (query &optional recurse-p)
- "Ex interface for `+ivy/ag-from-cwd'."
+;;;###autoload (autoload '+ivy:project-search-from-cwd "completion/ivy/autoload/evil" nil t)
+(evil-define-command +ivy:project-search-from-cwd (query &optional recurse-p)
+ "Ex interface for `+ivy/project-search-from-cwd'."
(interactive "")
- (+ivy/ag-from-cwd (not recurse-p) query))
-
-;;;###autoload (autoload '+ivy:rg-from-cwd "completion/ivy/autoload/evil" nil t)
-(evil-define-command +ivy:rg-from-cwd (query &optional recurse-p)
- "Ex interface for `+ivy/rg-from-cwd'."
- (interactive "")
- (+ivy/rg-from-cwd (not recurse-p) query))
+ (+ivy/project-search-from-cwd (not recurse-p) query))
diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el
index c264f8337..c596e4168 100644
--- a/modules/completion/ivy/autoload/ivy.el
+++ b/modules/completion/ivy/autoload/ivy.el
@@ -157,88 +157,6 @@ If ARG (universal argument), open selection in other-window."
(interactive)
(+ivy--switch-buffer nil t))
-(defun +ivy--tasks-candidates (tasks)
- "Generate a list of task tags (specified by `+ivy-task-tags') for
-`+ivy/tasks'."
- (let* ((max-type-width
- (cl-loop for task in +ivy-task-tags maximize (length (car task))))
- (max-desc-width
- (cl-loop for task in tasks maximize (length (cl-cdadr task))))
- (max-width (max (+ max-desc-width 3)
- 25)))
- (cl-loop
- with fmt = (format "%%-%ds %%-%ds%%s:%%s" max-type-width max-width)
- for alist in tasks
- collect
- (let-alist alist
- (list (format fmt
- (propertize .type 'face (cdr (assoc .type +ivy-task-tags)))
- (substring .desc 0 (min max-desc-width (length .desc)))
- (propertize (abbreviate-file-name .file) 'face 'font-lock-keyword-face)
- (propertize .line 'face 'font-lock-constant-face))
- .type .file .line)))))
-
-(defun +ivy--tasks (target)
- (let* (case-fold-search
- (task-tags (mapcar #'car +ivy-task-tags))
- (cmd
- (format "%s -H -S --no-heading -- %s %s"
- (or (when-let (bin (executable-find "rg"))
- (concat bin " --line-number"))
- (when-let (bin (executable-find "ag"))
- (concat bin " --numbers"))
- (error "ripgrep & the_silver_searcher are unavailable"))
- (shell-quote-argument
- (concat "\\s("
- (string-join task-tags "|")
- ")([\\s:]|\\([^)]+\\):?)"))
- target)))
- (save-match-data
- (cl-loop with out = (shell-command-to-string cmd)
- for x in (and out (split-string out "\n" t))
- when (condition-case-unless-debug ex
- (string-match
- (concat "^\\([^:]+\\):\\([0-9]+\\):.+\\("
- (string-join task-tags "\\|")
- "\\):?\\s-*\\(.+\\)")
- x)
- (error
- (print! (red "Error matching task in file: (%s) %s")
- (error-message-string ex)
- (car (split-string x ":")))
- nil))
- collect `((type . ,(match-string 3 x))
- (desc . ,(match-string 4 x))
- (file . ,(match-string 1 x))
- (line . ,(match-string 2 x)))))))
-
-(defun +ivy--tasks-open-action (x)
- "Jump to the file and line of the current task."
- (cl-destructuring-bind (_label type file line) x
- (with-ivy-window
- (find-file (expand-file-name file (doom-project-root)))
- (goto-char (point-min))
- (forward-line (1- (string-to-number line)))
- (when (search-forward type (line-end-position) t)
- (backward-char (length type)))
- (recenter))))
-
-;;;###autoload
-(defun +ivy/tasks (&optional arg)
- "Search through all TODO/FIXME tags in the current project. If ARG, only
-search current file. See `+ivy-task-tags' to customize what this searches for."
- (interactive "P")
- (ivy-read (format "Tasks (%s): "
- (if arg
- (concat "in: " (file-relative-name buffer-file-name))
- "project"))
- (let ((tasks (+ivy--tasks (if arg buffer-file-name (doom-project-root)))))
- (unless tasks
- (user-error "No tasks in your project! Good job!"))
- (+ivy--tasks-candidates tasks))
- :action #'+ivy--tasks-open-action
- :caller '+ivy/tasks))
-
;;;###autoload
(defun +ivy/woccur ()
"Invoke a wgrep buffer on the current ivy results, if supported."
@@ -293,7 +211,7 @@ search current file. See `+ivy-task-tags' to customize what this searches for."
;;
-;; File searching
+;;; File searching
;;;###autoload
(defun +ivy/projectile-find-file ()
@@ -318,25 +236,9 @@ The point of this is to avoid Emacs locking up indexing massive file trees."
(#'counsel-file-jump))))
-(defvar +ivy-file-search-shell
- (or (executable-find "dash")
- (executable-find "sh")
- shell-file-name)
- "The SHELL to invoke ag/rg/pt/git-grep/grep searchs from.
-
-This only affects `+ivy/*' search commands (e.g. `+ivy/rg' and
-`+ivy/project-search').
-
-By default, this the most basic, uncustomized shell, to prevent interference
-caused by slow shell configs at the cost of isolating these programs from
-envvars that may have been set in the user's shell config to change their
-behavior. If this bothers you, change this to `shell-file-name'.")
-
;;;###autoload
-(cl-defun +ivy-file-search (engine &key query in all-files (recursive t))
- "Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and
-grep. If omitted, ENGINE will default to the first one it detects, in that
-order.
+(cl-defun +ivy-file-search (&key query in all-files (recursive t))
+ "Conduct a file search using ripgrep.
:query STRING
Determines the initial input to search for.
@@ -346,131 +248,56 @@ order.
:recursive BOOL
Whether or not to search files recursively from the base directory."
(declare (indent defun))
- (let* ((project-root (or (doom-project-root) default-directory))
+ (unless (executable-find "rg")
+ (user-error "Couldn't find ripgrep in your PATH"))
+ (require 'counsel)
+ (let* ((ivy-more-chars-alist '((t . 1)))
+ (project-root (or (doom-project-root) default-directory))
(directory (or in project-root))
(default-directory directory)
- (engine (or engine
- (cl-loop for tool in +ivy-project-search-engines
- if (executable-find (symbol-name tool))
- return tool)
- (and (or (executable-find "grep")
- (executable-find "git"))
- 'grep)
- (error "No search engine specified (is ag, rg, pt or git installed?)")))
- (query
- (or (if query query)
- (when (use-region-p)
- (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
- (end (or (bound-and-true-p evil-visual-end) (region-end))))
- (when (> (abs (- end beg)) 1)
- (let ((query (buffer-substring-no-properties beg end)))
- ;; Escape characters that are special to ivy searches
- (replace-regexp-in-string "[! |]" (lambda (substr)
- (cond ((and (string= substr " ")
- (not (featurep! +fuzzy)))
- " ")
- ((and (string= substr "|")
- (eq engine 'rg))
- "\\\\\\\\|")
- ((concat "\\\\" substr))))
- (rxt-quote-pcre query))))))))
- (prompt
- (format "%s%%s %s"
- (symbol-name engine)
- (cond ((equal directory default-directory)
- "./")
- ((equal directory project-root)
- (projectile-project-name))
- ((file-relative-name directory project-root))))))
- (require 'counsel)
- (let ((ivy-more-chars-alist
- (if query '((t . 1)) ivy-more-chars-alist))
- (shell-file-name +ivy-file-search-shell))
- (pcase engine
- (`grep
- (let ((counsel-projectile-grep-initial-input query))
- (cl-letf (((symbol-function #'counsel-locate-git-root)
- (lambda () directory)))
- (if all-files
- (cl-letf (((symbol-function #'projectile-ignored-directories-rel)
- (symbol-function #'ignore))
- ((symbol-function #'projectile-ignored-files-rel)
- (symbol-function #'ignore)))
- (counsel-projectile-grep))
- (counsel-projectile-grep)))))
- (`ag
- (let ((args (concat (if all-files " -a")
- (unless recursive " --depth 1"))))
- (counsel-ag query directory args (format prompt args))))
- (`rg
- (let ((args (concat (if all-files " -uu")
- (unless recursive " --maxdepth 1"))))
- (counsel-rg query directory args (format prompt args))))
- (_ (error "No search engine specified"))))))
-
-(defun +ivy--get-command (format)
- (cl-loop for tool in (cl-remove-duplicates +ivy-project-search-engines :from-end t)
- if (executable-find (symbol-name tool))
- return (intern (format format tool))))
+ (args (concat (if all-files " -uu")
+ (unless recursive " --maxdepth 1"))))
+ (counsel-rg
+ (or (if query query)
+ (when (use-region-p)
+ (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
+ (end (or (bound-and-true-p evil-visual-end) (region-end))))
+ (when (> (abs (- end beg)) 1)
+ (let ((query (buffer-substring-no-properties beg end)))
+ ;; Escape characters that are special to ivy searches
+ (replace-regexp-in-string "[! |]" (lambda (substr)
+ (cond ((and (string= substr " ")
+ (not (featurep! +fuzzy)))
+ " ")
+ ((string= substr "|")
+ "\\\\\\\\|")
+ ((concat "\\\\" substr))))
+ (rxt-quote-pcre query)))))))
+ directory args
+ (format "rg%s %s"
+ args
+ (cond ((equal directory default-directory)
+ "./")
+ ((equal directory project-root)
+ (projectile-project-name))
+ ((file-relative-name directory project-root)))))))
;;;###autoload
(defun +ivy/project-search (&optional arg initial-query directory)
- "Performs a project search from the project root.
+ "Performs a live project search from the project root using ripgrep.
-Uses the first available search backend from `+ivy-project-search-engines'. If
-ARG (universal argument), include all files, even hidden or compressed ones, in
-the search."
+If ARG (universal argument), include all files, even hidden or compressed ones,
+in the search."
(interactive "P")
- (funcall (or (+ivy--get-command "+ivy/%s")
- #'+ivy/grep)
- arg
- initial-query
- directory))
+ (+ivy-file-search :query initial-query :in directory :all-files arg))
;;;###autoload
(defun +ivy/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory.
-Uses the first available search backend from `+ivy-project-search-engines'. If
-ARG (universal argument), include all files, even hidden or compressed ones."
+If ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P")
- (funcall (or (+ivy--get-command "+ivy/%s-from-cwd")
- #'+ivy/grep-from-cwd)
- arg
- initial-query))
-
-
-;;;###autoload (autoload '+ivy/rg "completion/ivy/autoload/ivy" nil t)
-;;;###autoload (autoload '+ivy/rg-from-cwd "completion/ivy/autoload/ivy" nil t)
-;;;###autoload (autoload '+ivy/ag "completion/ivy/autoload/ivy" nil t)
-;;;###autoload (autoload '+ivy/ag-from-cwd "completion/ivy/autoload/ivy" nil t)
-;;;###autoload (autoload '+ivy/grep "completion/ivy/autoload/ivy" nil t)
-;;;###autoload (autoload '+ivy/grep-from-cwd "completion/ivy/autoload/ivy" nil t)
-
-(dolist (engine `(,@(cl-remove-duplicates +ivy-project-search-engines :from-end t) grep))
- (defalias (intern (format "+ivy/%s" engine))
- (lambda (all-files-p &optional query directory)
- (interactive "P")
- (+ivy-file-search engine :query query :in directory :all-files all-files-p))
- (format "Perform a project file search using %s.
-
-QUERY is a regexp. If omitted, the current selection is used. If no selection is
-active, the last known search is used.
-
-If ALL-FILES-P, search compressed and hidden files as well."
- engine))
-
- (defalias (intern (format "+ivy/%s-from-cwd" engine))
- (lambda (all-files-p &optional query)
- (interactive "P")
- (+ivy-file-search engine :query query :in default-directory :all-files all-files-p))
- (format "Perform a project file search from the current directory using %s.
-
-QUERY is a regexp. If omitted, the current selection is used. If no selection is
-active, the last known search is used.
-
-If ALL-FILES-P, search compressed and hidden files as well."
- engine)))
+ (+ivy/project-search arg initial-query default-directory))
;;
@@ -513,7 +340,7 @@ If ALL-FILES-P, search compressed and hidden files as well."
(cons (format "%s:%d: %s"
(buffer-name)
(line-number-at-pos)
- (string-trim-right (thing-at-point 'line)))
+ (string-trim-right (or (thing-at-point 'line) "")))
(point-marker)))))))
(cddr (better-jumper-jump-list-struct-ring
(better-jumper-get-jumps (better-jumper--get-current-context))))))))
diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el
index 17ad1c5a6..4cb329f78 100644
--- a/modules/completion/ivy/config.el
+++ b/modules/completion/ivy/config.el
@@ -7,16 +7,6 @@ When nil, don't preview anything.
When non-nil, preview non-virtual buffers.
When 'everything, also preview virtual buffers")
-(defvar +ivy-task-tags
- '(("TODO" . warning)
- ("FIXME" . error)
- ("HACK" . font-lock-constant-face)
- ("REVIEW" . font-lock-keyword-face)
- ("NOTE" . success)
- ("DEPRECATED" . font-lock-doc-face))
- "An alist of tags for `+ivy/tasks' to include in its search, whose CDR is the
-face to render it with.")
-
(defvar +ivy-project-search-engines '(rg ag)
"What search tools for `+ivy/project-search' (and `+ivy-file-search' when no
ENGINE is specified) to try, and in what order.
@@ -69,6 +59,11 @@ results buffer.")
[remap persp-switch-to-buffer] #'+ivy/switch-workspace-buffer
[remap evil-show-jumps] #'+ivy/jump-list)
:config
+ ;; Counsel changes a lot of ivy's state at startup; to control for that, we
+ ;; need to load it as early as possible. Some packages (like `ivy-prescient')
+ ;; require this.
+ (require 'counsel nil t)
+
(setq ivy-height 17
ivy-wrap t
ivy-fixed-height-minibuffer t
@@ -182,7 +177,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! counsel
- :commands counsel-describe-face
+ :defer t
:init
(define-key!
[remap apropos] #'counsel-apropos
@@ -324,7 +319,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! ivy-posframe
- :when (and EMACS26+ (featurep! +childframe))
+ :when (featurep! +childframe)
:hook (ivy-mode . ivy-posframe-mode)
:config
(setq ivy-fixed-height-minibuffer nil
diff --git a/modules/completion/ivy/doctor.el b/modules/completion/ivy/doctor.el
index d730f1552..a79d37c56 100644
--- a/modules/completion/ivy/doctor.el
+++ b/modules/completion/ivy/doctor.el
@@ -1,5 +1,3 @@
;; -*- lexical-binding: t; no-byte-compile: t; -*-
;;; completion/ivy/doctor.el
-(when (and (not EMACS26+) (featurep! +childframe))
- (error! "The +childframe feature requires Emacs 26+"))
diff --git a/modules/completion/ivy/packages.el b/modules/completion/ivy/packages.el
index e804e9ead..5571641fd 100644
--- a/modules/completion/ivy/packages.el
+++ b/modules/completion/ivy/packages.el
@@ -15,7 +15,7 @@
(when (featurep! +fuzzy)
(package! flx)))
-(when (and EMACS26+ (featurep! +childframe))
+(when (featurep! +childframe)
(package! ivy-posframe))
(when (featurep! +icons)
diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el
index 4b6bf75af..e63e544ce 100644
--- a/modules/config/default/+emacs-bindings.el
+++ b/modules/config/default/+emacs-bindings.el
@@ -82,7 +82,7 @@
(:prefix ("p" . "project")
:desc "Find file in other project" "F" #'doom/find-file-in-other-project
:desc "Search project" "s" #'+default/search-project
- :desc "List project tasks" "t" #'+default/project-tasks
+ :desc "List project tasks" "t" #'magit-todos-list
:desc "Open project scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
;; later expanded by projectile
diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el
index e63d2ce0c..855eb6393 100644
--- a/modules/config/default/+evil-bindings.el
+++ b/modules/config/default/+evil-bindings.el
@@ -294,24 +294,6 @@
:desc "Find file in project" "SPC" #'projectile-find-file
:desc "Jump to bookmark" "RET" #'bookmark-jump
- ;;; / --- search
- (:prefix-map ("/" . "search")
- :desc "Search buffer" "/" #'swiper
- :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 "Jump list" "j" #'evil-show-jumps
- :desc "Jump to mark" "m" #'evil-show-marks
- :desc "Look up online" "o" #'+lookup/online
- :desc "Look up online (w/ prompt)" "O" #'+lookup/online-select
- :desc "Look up in local docsets" "k" #'+lookup/in-docsets
- :desc "Look up in all docsets" "K" #'+lookup/in-all-docsets
- :desc "Search project" "p" #'+default/search-project
- :desc "Search other project" "P" #'+default/search-other-project)
-
;;; TAB --- workspace
(:when (featurep! :ui workspaces)
(:prefix-map ("TAB" . "workspace")
@@ -381,25 +363,20 @@
:desc "Send to repl" "s" #'+eval/send-region-to-repl
:desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace
:desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines
+ :desc "List errors" "x" #'flymake-show-diagnostics-buffer
(:when (featurep! :tools flycheck)
- :desc "List errors" "x" #'flycheck-list-errors)
- (:unless (featurep! :tools flycheck)
- :desc "List errors" "x" #'flymake-show-diagnostics-buffer))
+ :desc "List errors" "x" #'flycheck-list-errors))
;;; f --- file
(:prefix-map ("f" . "file")
- :desc "Find file" "." #'find-file
- :desc "Find file from here" "/"
- (if (featurep! :completion ivy)
- #'counsel-file-jump
- (λ! (doom-project-find-file default-directory)))
:desc "Open project editorconfig" "c" #'editorconfig-find-current-editorconfig
:desc "Copy this file" "C" #'doom/copy-this-file
:desc "Find directory" "d" #'dired
:desc "Delete this file" "D" #'doom/delete-this-file
: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 "Find file" "f" #'find-file
+ :desc "Find file from here" "F" #'+default/find-file-under-here
: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
@@ -468,15 +445,16 @@
;;; i --- insert
(:prefix-map ("i" . "insert")
- :desc "From clipboard" "y" #'+default/yank-pop
+ :desc "Current file name" "f" #'+default/insert-file-path
+ :desc "Current file path" "F" (λ!! #'+default/insert-file-path t)
+ :desc "Evil ex path" "p" (λ! (evil-ex "R!echo "))
:desc "From evil register" "r" #'evil-ex-registers
:desc "Snippet" "s" #'yas-insert-snippet
- :desc "Unicode" "u" #'unicode-chars-list-chars)
+ :desc "Unicode" "u" #'unicode-chars-list-chars
+ :desc "From clipboard" "y" #'+default/yank-pop)
;;; n --- notes
(:prefix-map ("n" . "notes")
- :desc "Browse notes" "." #'+default/browse-notes
- :desc "Search notes" "/" #'+default/org-notes-search
:desc "Search notes for symbol" "*" #'+default/search-notes-for-symbol-at-point
:desc "Org agenda" "a" #'org-agenda
:desc "Org capture" "c" #'org-capture
@@ -487,6 +465,7 @@
:desc "Find file in notes" "n" #'+default/find-in-notes
:desc "Browse notes" "N" #'+default/browse-notes
:desc "Todo list" "t" #'org-todo-list
+ :desc "Search notes" "s" #'+default/org-notes-search
:desc "View search" "v" #'org-search-view
:desc "Org export to clipboard" "y" #'+org/export-to-clipboard
:desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text
@@ -542,8 +521,6 @@
(:prefix-map ("p" . "project")
:desc "Browse project" "." #'+default/browse-project
:desc "Browse other project" ">" #'doom/browse-in-other-project
- :desc "Find file in project" "/" #'projectile-find-file
- :desc "Find file in other project" "?" #'doom/find-file-in-other-project
:desc "Run cmd in project root" "!" #'projectile-run-shell-command-in-root
:desc "Add new project" "a" #'projectile-add-known-project
:desc "Switch to project buffer" "b" #'projectile-switch-to-buffer
@@ -552,7 +529,7 @@
:desc "Remove known project" "d" #'projectile-remove-known-project
:desc "Edit project .dir-locals" "e" #'projectile-edit-dir-locals
:desc "Find file in project" "f" #'projectile-find-file
- :desc "Browse project" "F" #'+default/browse-project
+ :desc "Find file in other project" "F" #'doom/find-file-in-other-project
:desc "Configure project" "g" #'projectile-configure-project
:desc "Invalidate project cache" "i" #'projectile-invalidate-cache
:desc "Kill project buffers" "k" #'projectile-kill-buffers
@@ -563,7 +540,7 @@
:desc "Save project files" "s" #'projectile-save-project-buffers
:desc "Pop up scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
- :desc "List project tasks" "t" #'+default/project-tasks
+ :desc "List project tasks" "t" #'magit-todos-list
:desc "Test project" "T" #'projectile-test-project)
;;; q --- quit/session
@@ -591,27 +568,31 @@
:desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler
:desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler))
- ;;; s --- snippets
- (:when (featurep! :editor snippets)
- (:prefix-map ("s" . "snippets")
- :desc "View snippet for mode" "/" #'+snippets/find-for-current-mode
- :desc "View snippet (global)" "?" #'+snippets/find
- :desc "Edit snippet" "c" #'+snippets/edit
- :desc "View private snippet" "f" #'+snippets/find-private
- :desc "Insert snippet" "i" #'yas-insert-snippet
- :desc "New snippet" "n" #'+snippets/new
- :desc "New snippet alias" "N" #'+snippets/new-alias
- :desc "Reload snippets" "r" #'yas-reload-all
- :desc "Create temporary snippet" "s" #'aya-create
- :desc "Expand temporary snippet" "e" #'aya-expand))
+ ;;; s --- search
+ (:prefix-map ("s" . "search")
+ :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 "Jump list" "j" #'evil-show-jumps
+ :desc "Jump to mark" "m" #'evil-show-marks
+ :desc "Look up online" "o" #'+lookup/online
+ :desc "Look up online (w/ prompt)" "O" #'+lookup/online-select
+ :desc "Look up in local docsets" "k" #'+lookup/in-docsets
+ :desc "Look up in all docsets" "K" #'+lookup/in-all-docsets
+ :desc "Search project" "p" #'+default/search-project
+ :desc "Search other project" "P" #'+default/search-other-project
+ :desc "Search buffer" "s" #'swiper-isearch
+ :desc "Search buffer for thing at point" "S" #'swiper-isearch-thing-at-point)
;;; t --- toggle
(:prefix-map ("t" . "toggle")
:desc "Big mode" "b" #'doom-big-font-mode
+ :desc "Flymake" "f" #'flymake-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
(:when (featurep! :ui indent-guides)
diff --git a/modules/config/default/autoload/default.el b/modules/config/default/autoload/default.el
index a6a91a0c1..ad49e2f6d 100644
--- a/modules/config/default/autoload/default.el
+++ b/modules/config/default/autoload/default.el
@@ -73,15 +73,6 @@ If ARG (universal argument), runs `compile' from the current directory."
(with-current-buffer buffer
(funcall (default-value 'major-mode))))))
-;;;###autoload
-(defun +default/project-tasks ()
- "Invokes `+ivy/tasks' or `+helm/tasks', depending on which is available."
- (interactive)
- (cond ((featurep! :tools magit)
- (call-interactively #'magit-todos-list))
- ((featurep! :completion ivy) (+ivy/tasks))
- ((featurep! :completion helm) (+helm/tasks))))
-
;;;###autoload
(defun +default/newline-above ()
"Insert an indented new line before the current one."
@@ -120,7 +111,7 @@ languages)."
(interactive)
(if (and (sp-point-in-comment)
comment-line-break-function)
- (funcall comment-line-break-function)
+ (funcall comment-line-break-function nil)
(delete-horizontal-space t)
(newline nil t)
(indent-according-to-mode)))
@@ -333,3 +324,22 @@ ARG is set, prompt for a known project to search from."
(while (server-running-p)
(sit-for 1))
(server-start))
+
+;;;###autoload
+(defun +default/find-file-under-here ()
+ "Perform a recursive file search from the current directory."
+ (interactive)
+ (if (featurep! :completion ivy)
+ (call-interactively #'counsel-file-jump)
+ (λ! (doom-project-find-file default-directory))))
+
+;;;###autoload
+(defun +default/insert-file-path (arg)
+ "Insert the file name (absolute path if prefix ARG).
+If `buffer-file-name' isn't set, uses `default-directory'."
+ (interactive "P")
+ (let ((path (or buffer-file-name default-directory)))
+ (insert
+ (if arg
+ (abbreviate-file-name path)
+ (file-name-nondirectory path)))))
diff --git a/modules/config/literate/init.el b/modules/config/literate/init.el
index 4dd961090..21898e443 100644
--- a/modules/config/literate/init.el
+++ b/modules/config/literate/init.el
@@ -18,7 +18,7 @@ byte-compiled from.")
+literate-config-cache-file)
force-p)
(message "Compiling your literate config...")
- (let* ((org (file-truename +literate-config-file))
+ (let* ((org (expand-file-name +literate-config-file))
(dest (concat (file-name-sans-extension +literate-config-file) ".el"))
(output (get-buffer-create "*org-tangle*")))
(unwind-protect
diff --git a/modules/editor/evil/+commands.el b/modules/editor/evil/+commands.el
index 8859ebac9..56f36286b 100644
--- a/modules/editor/evil/+commands.el
+++ b/modules/editor/evil/+commands.el
@@ -4,6 +4,7 @@
;;; Custom commands
;; Editing
(evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me
+(evil-ex-define-cmd "R[ead]" #'+evil:read)
(evil-ex-define-cmd "al[ign]" #'+evil:align)
(evil-ex-define-cmd "ral[ign]" #'+evil:align-right)
(evil-ex-define-cmd "enhtml" #'+web:encode-html-entities)
@@ -56,25 +57,19 @@
(evil-ex-define-cmd "cd" #'+evil:cd)
(evil-ex-define-cmd "pwd" #'+evil:pwd)
+(evil-define-command +evil:swiper (&optional search)
+ "Invoke `swiper' with SEARCH, otherwise with the symbol at point."
+ (interactive "")
+ (swiper-isearch search))
+(evil-ex-define-cmd "sw[iper]" #'+evil:swiper)
+
(cond ((featurep! :completion ivy)
- (evil-ex-define-cmd "ag" #'+ivy:ag)
- (evil-ex-define-cmd "agc[wd]" #'+ivy:ag-from-cwd)
- (evil-ex-define-cmd "rg" #'+ivy:rg)
- (evil-ex-define-cmd "rgc[wd]" #'+ivy:rg-from-cwd)
- (evil-ex-define-cmd "grep" #'+ivy:grep)
- (evil-ex-define-cmd "grepc[wd]" #'+ivy:grep-from-cwd)
- (evil-ex-define-cmd "sw[iper]" #'+ivy:swiper)
- (evil-ex-define-cmd "todo" #'+ivy:todo))
+ (evil-ex-define-cmd "pg[rep]" #'+ivy:project-search)
+ (evil-ex-define-cmd "pg[grep]d" #'+ivy:project-search-from-cwd))
((featurep! :completion helm)
- (evil-ex-define-cmd "ag" #'+helm:ag)
- (evil-ex-define-cmd "agc[wd]" #'+helm:ag-from-cwd)
- (evil-ex-define-cmd "rg" #'+helm:rg)
- (evil-ex-define-cmd "rgc[wd]" #'+helm:rg-from-cwd)
- (evil-ex-define-cmd "grep" #'+helm:grep)
- (evil-ex-define-cmd "grepc[wd]" #'+helm:grep-from-cwd)
- ;; (evil-ex-define-cmd "todo" #'+helm:todo) TODO implement `+helm:todo'
- ))
+ (evil-ex-define-cmd "pg[rep]" #'+helm:project-search)
+ (evil-ex-define-cmd "pg[grep]d" #'+helm:project-search-from-cwd)))
;;; Project tools
(evil-ex-define-cmd "compile" #'+evil:compile)
diff --git a/modules/editor/evil/+everywhere.el b/modules/editor/evil/+everywhere.el
index ea8ba4ddc..c5e1312eb 100644
--- a/modules/editor/evil/+everywhere.el
+++ b/modules/editor/evil/+everywhere.el
@@ -220,7 +220,7 @@ and complains if a module is loaded too early (during startup)."
(add-transient-hook! 'emacs-lisp-mode
(+evil-collection-init 'elisp-mode))
(add-transient-hook! 'occur-mode
- (+evil-collection-init (if EMACS26+ 'replace "replace")))
+ (+evil-collection-init 'replace))
(evil-define-key* 'normal process-menu-mode-map
"q" #'kill-current-buffer
diff --git a/modules/editor/evil/autoload/advice.el b/modules/editor/evil/autoload/advice.el
index 2a86105b9..651393858 100644
--- a/modules/editor/evil/autoload/advice.el
+++ b/modules/editor/evil/autoload/advice.el
@@ -80,9 +80,10 @@ more information on modifiers."
(when (and (not (string= path "")) (equal (substring path -1) "/"))
(setq path (substring path 0 -1))))
(setq file-name
- (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)"
- (regexp-quote (string-trim-left (car match))))
- path file-name t t 1))))
+ (replace-regexp-in-string
+ (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)"
+ (regexp-quote (string-trim-left (car match))))
+ path file-name t t 1))))
(replace-regexp-in-string regexp "\\1" file-name t)))
(defun +evil--insert-newline (&optional above _noextranewline)
@@ -99,7 +100,7 @@ more information on modifiers."
;; FIXME oh god why
(save-excursion
(if comment-line-break-function
- (funcall comment-line-break-function)
+ (funcall comment-line-break-function nil)
(comment-indent-new-line))
(when (and (derived-mode-p 'c-mode 'c++-mode 'objc-mode 'java-mode 'js2-mode)
(eq (char-after) ?/))
diff --git a/modules/editor/evil/autoload/ex.el b/modules/editor/evil/autoload/ex.el
index 159af43a2..6ad458106 100644
--- a/modules/editor/evil/autoload/ex.el
+++ b/modules/editor/evil/autoload/ex.el
@@ -182,3 +182,9 @@ non-nil, a search is preformed against Doom's manual (wiht `doom/help-search')."
(evil-ex-completed-binding (match-string 1 query))))
((message "Searching for %S, this may take a while..." query)
(apropos query t))))))
+
+;;;###autoload (autoload '+evil:read "editor/evil/autoload/ex" nil t)
+(evil-define-command +evil:read (count file)
+ "Alternative version of `evil-read' that replaces filename modifiers in FILE."
+ (interactive "P")
+ (evil-read count (evil-ex-replace-special-filenames file)))
diff --git a/modules/editor/evil/config.el b/modules/editor/evil/config.el
index 7b3f22096..0873bea44 100644
--- a/modules/editor/evil/config.el
+++ b/modules/editor/evil/config.el
@@ -479,7 +479,20 @@ To change these keys see `+evil-repeat-keys'."
(:when (featurep! :tools eval)
:nv "gr" #'+eval:region
:n "gR" #'+eval/buffer
- :v "gR" #'+eval:replace-region)
+ :v "gR" #'+eval:replace-region
+ ;; Restore these keybinds, since the blacklisted/overwritten gr/gR will
+ ;; undo them:
+ (:after dired
+ :map dired-mode-map
+ :n "gr" #'revert-buffer)
+ (:after notmuch
+ :map notmuch-common-keymap
+ :n "gr" #'notmuch-refresh-this-buffer
+ :n "gR" #'notmuch-poll-and-refresh-this-buffer)
+ (:after elfeed
+ :map elfeed-search-update--force
+ :n "gr" #'elfeed-search-update--force
+ :n "gR" #'elfeed-search-fetch))
:nv "z=" #'flyspell-correct-word-generic
;; custom evil keybinds
diff --git a/modules/emacs/ibuffer/config.el b/modules/emacs/ibuffer/config.el
index e928358d7..72755c9d3 100644
--- a/modules/emacs/ibuffer/config.el
+++ b/modules/emacs/ibuffer/config.el
@@ -6,7 +6,7 @@
(setq ibuffer-show-empty-filter-groups nil
ibuffer-filter-group-name-face '(:inherit (success bold))
ibuffer-formats
- `((mark modified read-only ,(if EMACS26+ 'locked "")
+ `((mark modified read-only locked
,@(if (featurep! +icons)
`(;; Here you may adjust by replacing :right with :center
;; or :left According to taste, if you want the icon
diff --git a/modules/emacs/vc/config.el b/modules/emacs/vc/config.el
index 68cfe853c..a8a595052 100644
--- a/modules/emacs/vc/config.el
+++ b/modules/emacs/vc/config.el
@@ -66,14 +66,3 @@ otherwise in default state."
(when (and (bound-and-true-p evil-mode)
(bobp) (eolp))
(evil-insert-state)))))
-
-
-(after! smerge-mode
- (unless EMACS26+
- (with-no-warnings
- (defalias #'smerge-keep-upper #'smerge-keep-mine)
- (defalias #'smerge-keep-lower #'smerge-keep-other)
- (defalias #'smerge-diff-base-upper #'smerge-diff-base-mine)
- (defalias #'smerge-diff-upper-lower #'smerge-diff-mine-other)
- (defalias #'smerge-diff-base-lower #'smerge-diff-base-other))))
-
diff --git a/modules/email/notmuch/autoload.el b/modules/email/notmuch/autoload.el
index 211e45957..49fdd745f 100644
--- a/modules/email/notmuch/autoload.el
+++ b/modules/email/notmuch/autoload.el
@@ -100,7 +100,7 @@
(interactive)
(let* ((msg-path (car (plist-get (notmuch-tree-get-message-properties) :filename)))
(temp (make-temp-file "notmuch-message-" nil ".eml")))
- (shell-command-to-string (format "cp '%s' '%s'" msg-path temp))
+ (doom-call-process "cp" msg-path temp)
(start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
;;;###autoload
@@ -108,7 +108,7 @@
(interactive)
(let* ((msg-path (car (plist-get (notmuch-show-get-message-properties) :filename)))
(temp (make-temp-file "notmuch-message-" nil ".eml")))
- (shell-command-to-string (format "cp '%s' '%s'" msg-path temp))
+ (doom-call-process "cp" msg-path temp)
(start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el
index 701acc96a..4698c382f 100644
--- a/modules/lang/cc/config.el
+++ b/modules/lang/cc/config.el
@@ -160,6 +160,10 @@ This is ignored by ccls.")
;;
;; Major modes
+(use-package! cmake-mode
+ :defer t
+ :config (set-docsets! 'cmake-mode "CMake"))
+
(use-package! company-cmake ; for `cmake-mode'
:when (featurep! :completion company)
:after cmake-mode
diff --git a/modules/lang/coq/config.el b/modules/lang/coq/config.el
index 086d3648b..86cd6ee0b 100644
--- a/modules/lang/coq/config.el
+++ b/modules/lang/coq/config.el
@@ -9,7 +9,8 @@
;; tries to load `proof-site'. We prevent this by defining these two variables
;; early, in our own autoloads file.
(setq pg-init--script-full-path (locate-library "proof-general")
- pg-init--pg-root (file-name-directory pg-init--script-full-path))
+ pg-init--pg-root (file-name-directory pg-init--script-full-path)
+ proof-splash-enable nil)
;;;###package coq
@@ -73,14 +74,15 @@
:references #'company-coq-grep-symbol
:documentation #'company-coq-doc)
- (if (not (featurep! :completion company))
- (setq company-coq-disabled-features '(company company-defaults))
+ (setq company-coq-disabled-features '(hello company-defaults))
+
+ (if (featurep! :completion company)
+ (map! :map coq-mode-map [remap company-complete-common]
+ #'company-indent-or-complete-common)
;; `company-coq''s company defaults impose idle-completion on folks, so
;; we'll set up company ourselves.
- (add-to-list 'company-coq-disabled-features 'company-defaults)
;; See https://github.com/cpitclaudel/company-coq/issues/42
- (map! :map coq-mode-map [remap company-complete-common]
- #'company-indent-or-complete-common))
+ (add-to-list 'company-coq-disabled-features 'company))
(map! :map coq-mode-map
:localleader
diff --git a/modules/lang/csharp/config.el b/modules/lang/csharp/config.el
index 66a31acc5..c8bf81259 100644
--- a/modules/lang/csharp/config.el
+++ b/modules/lang/csharp/config.el
@@ -21,7 +21,7 @@
:commands omnisharp-install-server
:preface
(setq omnisharp-auto-complete-want-documentation nil
- omnisharp-cache-directory (concat doom-cache-dir "omnisharp"))
+ omnisharp-cache-directory (concat doom-etc-dir "omnisharp"))
:config
(defun +csharp-cleanup-omnisharp-server-h ()
"Clean up the omnisharp server once you kill the last csharp-mode buffer."
diff --git a/modules/lang/data/packages.el b/modules/lang/data/packages.el
index 8feed097b..9cb3322eb 100644
--- a/modules/lang/data/packages.el
+++ b/modules/lang/data/packages.el
@@ -7,8 +7,5 @@
(package! yaml-mode)
(package! csv-mode)
(package! dhall-mode)
-(package! protobuf-mode :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*")))
-
-;; DEPRECATED `conf-toml-mode' exists in Emacs 26+
-(unless (fboundp 'conf-toml-mode)
- (package! toml-mode))
+(package! protobuf-mode
+ :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*")))
diff --git a/modules/lang/go/README.org b/modules/lang/go/README.org
index 9c33d6738..c1a2f91e7 100644
--- a/modules/lang/go/README.org
+++ b/modules/lang/go/README.org
@@ -36,7 +36,7 @@ This module adds [[https://golang.org][Go]] support.
+ [[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/brantou/emacs-go-tag][go-tag]]
+ [[https://github.com/mdempsky/gocode][company-go]]*
+ [[https://github.com/s-kostyaev/go-gen-test][go-gen-test]]
@@ -69,6 +69,7 @@ This module requires a valid ~GOPATH~, and the following Go packages:
+ ~guru~ (for code navigation & refactoring commands)
+ ~goimports~ (optional: for auto-formatting code on save & fixing imports)
+ ~gotests~ (for generate test code)
++ ~gomodifytags~ (for manipulating tags)
#+BEGIN_SRC sh
export GOPATH=~/work/go
@@ -80,6 +81,7 @@ go get -u golang.org/x/tools/cmd/goimports
go get -u golang.org/x/tools/cmd/gorename
go get -u golang.org/x/tools/cmd/guru
go get -u github.com/cweill/gotests/...
+go get -u github.com/fatih/gomodifytags
#+END_SRC
* TODO Features
diff --git a/modules/lang/go/config.el b/modules/lang/go/config.el
index 0c13afd68..2f656f590 100644
--- a/modules/lang/go/config.el
+++ b/modules/lang/go/config.el
@@ -25,7 +25,8 @@
(map! :map go-mode-map
:localleader
- "a" #'go-add-tags
+ "a" #'go-tag-add
+ "d" #'go-tag-remove
"e" #'+go/play-buffer-or-region
"i" #'go-goto-imports ; Go to imports
(:prefix ("h" . "help")
diff --git a/modules/lang/go/doctor.el b/modules/lang/go/doctor.el
index 37d002263..a179fb5c9 100644
--- a/modules/lang/go/doctor.el
+++ b/modules/lang/go/doctor.el
@@ -14,6 +14,9 @@
(unless (executable-find "gotests")
(warn! "Couldn't find gotests. Generating tests will not work"))
+(unless (executable-find "gomodifytags")
+ (warn! "Couldn't find gomodifytags. Manipulating struct tags will not work"))
+
(when (featurep! :completion company)
(require 'company-go)
(unless (executable-find company-go-gocode-command)
diff --git a/modules/lang/go/packages.el b/modules/lang/go/packages.el
index bbf22ba0b..ab3c2b8c1 100644
--- a/modules/lang/go/packages.el
+++ b/modules/lang/go/packages.el
@@ -5,7 +5,7 @@
(package! go-guru)
(package! go-mode)
(package! gorepl-mode)
-(package! go-add-tags)
+(package! go-tag)
(package! go-gen-test)
(when (featurep! :completion company)
diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el
index 55e8daff1..45168fddf 100644
--- a/modules/lang/javascript/config.el
+++ b/modules/lang/javascript/config.el
@@ -188,7 +188,7 @@ to tide."
:map tide-mode-map
"R" #'tide-restart-server
"f" #'tide-format
- "rs" #'tide-rename-symbol
+ "rrs" #'tide-rename-symbol
"roi" #'tide-organize-imports))
@@ -205,7 +205,25 @@ to tide."
:config
(when (featurep! :editor evil +everywhere)
(let ((js2-refactor-mode-map (evil-get-auxiliary-keymap js2-refactor-mode-map 'normal t t)))
- (js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key)))))
+ (js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key))))
+ (map! :map js2-mode-map
+ :localleader
+ (:prefix ("r" . "refactor")
+ (:prefix ("a" . "add/arguments"))
+ (:prefix ("b" . "barf"))
+ (:prefix ("c" . "contract"))
+ (:prefix ("d" . "debug"))
+ (:prefix ("e" . "expand/extract"))
+ (:prefix ("i" . "inject/inline/introduce"))
+ (:prefix ("l" . "localize/log"))
+ (:prefix ("o" . "organize"))
+ (:prefix ("r" . "rename"))
+ (:prefix ("s" . "slurp/split/string"))
+ (:prefix ("t" . "toggle"))
+ (:prefix ("u" . "unwrap"))
+ (:prefix ("v" . "var"))
+ (:prefix ("w" . "wrap"))
+ (:prefix ("3" . "ternary")))))
(use-package! eslintd-fix
@@ -234,6 +252,9 @@ to tide."
(:after skewer-html
:map skewer-html-mode-map
"e" #'skewer-html-eval-tag))
+(map! :map js2-mode-map
+ :localleader
+ (:prefix ("s" . "skewer")))
;;;###package npm-mode
@@ -242,7 +263,10 @@ to tide."
:config
(map! :localleader
:map npm-mode-keymap
- "n" npm-mode-command-keymap))
+ "n" npm-mode-command-keymap)
+ (map! :map js2-mode-map
+ :localleader
+ (:prefix ("n" . "npm"))))
;;
diff --git a/modules/lang/ocaml/autoload.el b/modules/lang/ocaml/autoload.el
index af6139bac..9ab554c7e 100644
--- a/modules/lang/ocaml/autoload.el
+++ b/modules/lang/ocaml/autoload.el
@@ -1,7 +1,7 @@
;;; lang/ocaml/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
-(defun +ocaml/comment-indent-new-line ()
+(defun +ocaml/comment-indent-new-line (&optional _)
"Break line at point and indent, continuing comment if within one."
(interactive)
(comment-indent-new-line)
diff --git a/modules/lang/org/autoload/contrib-ipython.el b/modules/lang/org/autoload/contrib-ipython.el
index 4e8e82ef3..e729a0711 100644
--- a/modules/lang/org/autoload/contrib-ipython.el
+++ b/modules/lang/org/autoload/contrib-ipython.el
@@ -21,12 +21,12 @@ Make sure your src block has a :session param.")
(defun +org--ob-ipython-generate-local-path-from-remote (session host params)
"Given a remote SESSION with PARAMS and corresponding HOST, copy remote config to local, start a jupyter console to generate a new one."
(let* ((runtime-dir
- (substring (shell-command-to-string (concat "ssh " host " jupyter --runtime-dir")) 0 -1))
+ (cdr
+ (doom-call-process "ssh " host "jupyter" "--runtime-dir")))
(runtime-file (concat runtime-dir "/" "kernel-" session ".json"))
(tramp-path (concat "/ssh:" host ":" runtime-file))
(tramp-copy (concat (or +ob-ipython-local-runtime-dir
- (substring (shell-command-to-string "jupyter --runtime-dir")
- 0 -1))
+ (cdr (doom-call-process "jupyter" "--runtime-dir")))
"/remote-" host "-kernel-" session ".json"))
(local-path
(concat
diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el
index 22c2a1e20..88241662f 100644
--- a/modules/lang/org/autoload/org.el
+++ b/modules/lang/org/autoload/org.el
@@ -1,23 +1,5 @@
;;; lang/org/autoload/org.el -*- lexical-binding: t; -*-
-;; HACK A necessary hack because org requires a compilation step after being
-;; cloned, and during that compilation a org-version.el is generated with these
-;; two functions, which return the output of a 'git describe ...' call in the
-;; repo's root. Of course, this command won't work in a sparse clone, and more
-;; than that, initiating these compilation step is a hassle, so...
-;;;###autoload (defun +org--release-a () "9.3")
-;;;###autoload (fset 'org-release #'+org--release-a)
-;;;###autoload (fset 'org-git-version #'ignore)
-
-;; Org itself may override the above if it's loaded too early by packages that
-;; depend on it, so we have to advise it once again:
-;;;###autoload (advice-add #'org-release :override #'+org--release-a)
-;;;###autoload (advice-add #'org-git-version :override #'ignore)
-;;;###autoload (add-to-list 'load-path (dir!))
-
-;;;###autoload (provide 'org-version)
-
-
;;
;;; Helpers
@@ -90,24 +72,19 @@
org-insert-heading-respect-content)
(goto-char (line-end-position))
(org-end-of-subtree)
- (insert (concat "\n"
- (when (= level 1)
- (if at-eol
- (ignore (cl-incf level))
- "\n"))
- (make-string level ?*)
- " "))))
+ (insert "\n" (make-string level ?*) " ")))
(`above
(org-back-to-heading)
(insert (make-string level ?*) " ")
- (save-excursion
- (insert "\n")
- (if (= level 1) (insert "\n")))))
- (when-let (todo-keyword (org-element-property :todo-keyword context))
- (org-todo (or (car (+org-get-todo-keywords-for todo-keyword))
- 'todo)))))
+ (save-excursion (insert "\n"))))
+ (when-let* ((todo-keyword (org-element-property :todo-keyword context))
+ (todo-type (org-element-property :todo-type context)))
+ (org-todo (cond ((eq todo-type 'done)
+ (car (+org-get-todo-keywords-for todo-keyword)))
+ (todo-keyword)
+ ('todo))))))
- (t (user-error "Not a valid list, heading or table")))
+ ((user-error "Not a valid list, heading or table")))
(when (org-invisible-p)
(org-show-hidden-entry))
diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el
index d89c3de41..a52611d85 100644
--- a/modules/lang/org/config.el
+++ b/modules/lang/org/config.el
@@ -207,19 +207,7 @@ background (and foreground) match the current theme."
;; Fix 'require(...).print is not a function' error from `ob-js' when
;; executing JS src blocks
- (setq org-babel-js-function-wrapper "console.log(require('util').inspect(function(){\n%s\n}()));")
-
- ;; Fix #2010: ob-async needs to initialize Doom Emacs at least minimally for
- ;; its async babel sessions to run correctly. This cannot be a named function
- ;; because it is interpolated directly into a closure to be evaluated on the
- ;; async session.
- (defadvice! +org-init-doom-during-async-executation-a (orig-fn &rest args)
- :around #'ob-async-org-babel-execute-src-block
- (let ((ob-async-pre-execute-src-block-hook
- ;; Ensure our hook is always first
- (cons `(lambda () (load ,(concat doom-emacs-dir "init.el")))
- ob-async-pre-execute-src-block-hook)))
- (apply orig-fn args))))
+ (setq org-babel-js-function-wrapper "console.log(require('util').inspect(function(){\n%s\n}()));"))
(defun +org-init-babel-lazy-loader-h ()
@@ -633,6 +621,7 @@ between the two."
(:when (featurep! :completion helm)
"." #'helm-org-in-buffer-headings
"/" #'helm-org-agenda-files-headings)
+ "A" #'org-archive-subtree
"d" #'org-deadline
"e" #'org-export-dispatch
"f" #'org-footnote-new
@@ -648,26 +637,29 @@ between the two."
"s" #'org-schedule
"t" #'org-todo
"T" #'org-todo-list
- (:prefix ("r" . "refile")
- "." #'+org/refile-to-current-file
- "c" #'+org/refile-to-running-clock
- "l" #'+org/refile-to-last-location
- "o" #'+org/refile-to-other-window
- "O" #'+org/refile-to-other-buffers
- "r" #'org-refile) ; to all `org-refile-targets'
(:prefix ("a" . "attachments")
"a" #'+org-attach/file
"u" #'+org-attach/uri
"f" #'+org-attach/find-file
"s" #'+org-attach/sync)
+ (:prefix ("b" . "tables")
+ "-" #'org-table-insert-hline
+ "a" #'org-table-align
+ "c" #'org-table-create-or-convert-from-region
+ "e" #'org-table-edit-field
+ "h" #'org-table-field-info
+ (:when (featurep! +gnuplot)
+ "p" #'org-plot/gnuplot))
(:prefix ("c" . "clock")
"c" #'org-clock-in
"C" #'org-clock-out
"d" #'org-clock-mark-default-task
"e" #'org-clock-modify-effort-estimate
+ "E" #'org-set-effort
"l" #'org-clock-in-last
"g" #'org-clock-goto
"G" (λ! (org-clock-goto 'select))
+ "r" #'org-clock-report
"x" #'org-clock-cancel
"=" #'org-clock-timestamps-up
"-" #'org-clock-timestamps-down)
@@ -676,21 +668,18 @@ between the two."
(:when (featurep! :completion ivy)
"g" #'counsel-org-goto
"G" #'counsel-org-goto-all)
- "a" #'org-agenda-goto
- "A" #'org-agenda-clock-goto
"c" #'org-clock-goto
"C" (λ! (org-clock-goto 'select))
"i" #'org-id-goto
"r" #'org-refile-goto-last-stored
"x" #'org-capture-goto-last-stored)
- (:prefix ("b" . "tables")
- "-" #'org-table-insert-hline
- "a" #'org-table-align
- "c" #'org-table-create-or-convert-from-region
- "e" #'org-table-edit-field
- "h" #'org-table-field-info
- (:when (featurep! +gnuplot)
- "p" #'org-plot/gnuplot)))
+ (:prefix ("r" . "refile")
+ "." #'+org/refile-to-current-file
+ "c" #'+org/refile-to-running-clock
+ "l" #'+org/refile-to-last-location
+ "o" #'+org/refile-to-other-window
+ "O" #'+org/refile-to-other-buffers
+ "r" #'org-refile)) ; to all `org-refile-targets'
(map! :after org-agenda
:map org-agenda-mode-map
@@ -699,6 +688,13 @@ between the two."
[remap org-agenda-Quit] #'org-agenda-exit
:localleader
"d" #'org-agenda-deadline
+ (:prefix ("c" . "clock")
+ "c" #'org-agenda-clock-in
+ "C" #'org-agenda-clock-out
+ "g" #'org-agenda-clock-goto
+ "r" #'org-agenda-clockreport-mode
+ "s" #'org-agenda-show-clocking-issues
+ "x" #'org-agenda-clock-cancel)
"q" #'org-agenda-set-tags
"r" #'org-agenda-refile
"s" #'org-agenda-schedule
diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el
index 6e04f10b8..5f4a7f17e 100644
--- a/modules/lang/org/packages.el
+++ b/modules/lang/org/packages.el
@@ -6,7 +6,23 @@
(when-let (orglib (locate-library "org" nil doom--initial-load-path))
(setq load-path (delete (substring (file-name-directory orglib) 0 -1)
load-path)))
-(package! org-plus-contrib) ; install cutting-edge version of org-mode
+
+;; HACK A necessary hack because org requires a compilation step after being
+;; cloned, and during that compilation a org-version.el is generated with
+;; these two functions, which return the output of a 'git describe ...'
+;; call in the repo's root. Of course, this command won't work in a sparse
+;; clone, and more than that, initiating these compilation step is a
+;; hassle, so...
+(setq straight-fix-org nil)
+(add-hook! 'straight-use-package-pre-build-functions
+ (defun +org-fix-package-h (package &rest _)
+ (when (member package '("org" "org-plus-contrib"))
+ (with-temp-file (expand-file-name "org-version.el" (straight--repos-dir "org"))
+ (insert "(fset 'org-release (lambda () \"9.3\"))\n"
+ "(fset 'org-git-version #'ignore)\n"
+ "(provide 'org-version)\n")))))
+
+(package! org-plus-contrib) ; install cutting-edge version of org-mode
(package! htmlize)
(package! org-bullets :recipe (:host github :repo "Kaligule/org-bullets"))
@@ -14,12 +30,29 @@
(package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt"))
(package! ox-clip)
(package! toc-org)
+
(when (featurep! :editor evil +everywhere)
(package! evil-org :recipe (:host github :repo "hlissner/evil-org-mode")))
(when (featurep! :tools pdf)
(package! org-pdfview))
(when (featurep! :tools magit)
(package! orgit))
+(when (featurep! +dragndrop)
+ (package! org-download))
+(when (featurep! +gnuplot)
+ (package! gnuplot)
+ (package! gnuplot-mode))
+(when (featurep! +ipython)
+ (package! ob-ipython))
+(when (featurep! +pomodoro)
+ (package! org-pomodoro))
+(when (featurep! +present)
+ (package! centered-window
+ :recipe (:host github :repo "anler/centered-window-mode"))
+ (package! org-tree-slide)
+ (package! org-re-reveal))
+(when (featurep! +journal)
+ (package! org-journal))
;;; Babel
(package! ob-async)
@@ -36,25 +69,11 @@
(when (featurep! :lang rust)
(package! ob-rust))
-;;; Modules
-(when (featurep! +dragndrop)
- (package! org-download))
-(when (featurep! +gnuplot)
- (package! gnuplot)
- (package! gnuplot-mode))
-(when (featurep! +ipython)
- (package! ob-ipython))
+;;; Export
(when (featurep! +pandoc)
(package! ox-pandoc))
-(when (featurep! +pomodoro)
- (package! org-pomodoro))
-(when (featurep! +present)
- (package! centered-window
- :recipe (:host github :repo "anler/centered-window-mode"))
- (package! org-tree-slide)
- (package! org-re-reveal))
-(when (featurep! +journal)
- (package! org-journal))
(when (featurep! +hugo)
(package! ox-hugo
:recipe (:host github :repo "kaushalmodi/ox-hugo" :nonrecursive t)))
+(when (featurep! :lang rst)
+ (package! ox-rst))
diff --git a/modules/lang/org/test/test-org.el b/modules/lang/org/test/test-org.el
index cbe36f62d..ff065b5e4 100644
--- a/modules/lang/org/test/test-org.el
+++ b/modules/lang/org/test/test-org.el
@@ -18,31 +18,19 @@
(kill-buffer (get-buffer "org")))
(describe "headlines"
- (it "appends first-level headlines with an extra newline"
+ (it "opens new headline below"
(insert!! "* {0}Header")
(+org/insert-item-below 1)
(expect (eobp))
(expect (buffer-substring-no-properties (point-min) (point-max))
- :to-equal "* Header\n\n* "))
- (it "prepends first-level headlines with an extra newline"
- (insert!! "* {0}Header")
- (+org/insert-item-above 1)
- (expect (eolp))
- (expect (buffer-substring-no-properties (point-min) (point-max))
- :to-equal "* \n\n* Header"))
+ :to-equal "* Header\n* "))
- (it "appends second-level headlines with an no extra newline"
- (insert!! "** {0}Header")
- (+org/insert-item-below 1)
- (expect (eobp))
- (expect (buffer-substring-no-properties (point-min) (point-max))
- :to-equal "** Header\n** "))
- (it "prepends second-level headlines with an no extra newline"
- (insert!! "** {0}Header")
+ (it "opens new headline above"
+ (insert!! "* {0}Header")
(+org/insert-item-above 1)
(expect (eolp))
(expect (buffer-substring-no-properties (point-min) (point-max))
- :to-equal "** \n** Header"))
+ :to-equal "* \n* Header"))
(it "appends headlines, skipping subtrees"
(insert!! "** {0}First\n"
diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el
index 3b773a7a1..66450c698 100644
--- a/modules/lang/python/config.el
+++ b/modules/lang/python/config.el
@@ -20,6 +20,9 @@ called.")
:init
(setq python-environment-directory doom-cache-dir
python-indent-guess-indent-offset-verbose nil)
+
+ (when (featurep! +lsp)
+ (add-hook 'python-mode-local-vars-hook #'lsp!))
:config
(set-repl-handler! 'python-mode #'+python/open-repl :persist t)
(set-docsets! 'python-mode "Python 3" "NumPy" "SciPy")
@@ -45,9 +48,6 @@ called.")
;; Stop the spam!
(setq python-indent-guess-indent-offset-verbose nil)
- (when (featurep! +lsp)
- (add-hook 'python-mode-local-vars-hook #'lsp!))
-
;; Default to Python 3. Prefer the versioned Python binaries since some
;; systems stupidly make the unversioned one point at Python 2.
(when (and (executable-find "python3")
@@ -88,10 +88,17 @@ called.")
(use-package! anaconda-mode
- :after python
+ :defer t
:init
(setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/")
anaconda-mode-eldoc-as-single-line t)
+
+ (add-hook! 'python-mode-local-vars-hook
+ (defun +python-init-anaconda-mode-maybe-h ()
+ "Enable `anaconda-mode' if `lsp-mode' isn't."
+ (unless (or (bound-and-true-p lsp-mode)
+ (bound-and-true-p lsp--buffer-deferred))
+ (anaconda-mode +1))))
:config
(add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode)
(set-company-backend! 'anaconda-mode '(company-anaconda))
@@ -101,13 +108,6 @@ called.")
:documentation #'anaconda-mode-show-doc)
(set-popup-rule! "^\\*anaconda-mode" :select nil)
- (add-hook! 'python-mode-local-vars-hook :append
- (defun +python-init-anaconda-mode-maybe-h ()
- "Enable `anaconda-mode' if `lsp-mode' isn't."
- (unless (or (bound-and-true-p lsp-mode)
- (bound-and-true-p lsp--buffer-deferred))
- (anaconda-mode +1))))
-
(defun +python-auto-kill-anaconda-processes-h ()
"Kill anaconda processes if this buffer is the last python buffer."
(when (and (eq major-mode 'python-mode)
@@ -115,7 +115,8 @@ called.")
(doom-buffers-in-mode 'python-mode (buffer-list)))))
(anaconda-mode-stop)))
(add-hook! 'python-mode-hook
- (add-hook 'kill-buffer-hook #'+python-auto-kill-anaconda-processes-h nil t))
+ (add-hook 'kill-buffer-hook #'+python-auto-kill-anaconda-processes-h
+ nil 'local))
(when (featurep 'evil)
(add-hook 'anaconda-mode-hook #'evil-normalize-keymaps))
@@ -130,9 +131,10 @@ called.")
(use-package! pyimport
- :after python
- :config
- (map! :map python-mode-map
+ :defer t
+ :init
+ (map! :after python
+ :map python-mode-map
:localleader
(:prefix ("i" . "imports")
:desc "Insert missing imports" "i" #'pyimport-insert-missing
@@ -193,7 +195,18 @@ called.")
(_ (pipenv-project-p)))
(format "PIPENV_MAX_DEPTH=9999 %s run %%c %%o %%s %%a" bin)
"%c %o %s %a")))
- (:description . "Run Python script"))))
+ (:description . "Run Python script")))
+ (map! :map python-mode-map
+ :localleader
+ :prefix "e"
+ :desc "activate" "a" #'pipenv-activate
+ :desc "deactivate" "d" #'pipenv-deactivate
+ :desc "install" "i" #'pipenv-install
+ :desc "lock" "l" #'pipenv-lock
+ :desc "open module" "o" #'pipenv-open
+ :desc "run" "r" #'pipenv-run
+ :desc "shell" "s" #'pipenv-shell
+ :desc "uninstall" "u" #'pipenv-uninstall))
(use-package! pyvenv
@@ -206,18 +219,7 @@ called.")
(add-hook 'python-mode-local-vars-hook #'pyvenv-track-virtualenv)
(add-to-list 'global-mode-string
'(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name " "))
- 'append)
- (map! :map python-mode-map
- :localleader
- :prefix "e"
- :desc "activate" "a" #'pipenv-activate
- :desc "deactivate" "d" #'pipenv-deactivate
- :desc "install" "i" #'pipenv-install
- :desc "lock" "l" #'pipenv-lock
- :desc "open module" "o" #'pipenv-open
- :desc "run" "r" #'pipenv-run
- :desc "shell" "s" #'pipenv-shell
- :desc "uninstall" "u" #'pipenv-uninstall))
+ 'append))
@@ -243,19 +245,19 @@ called.")
;; If none of these work for you, `conda-anaconda-home' must be set
;; explicitly. Afterwards, run M-x `conda-env-activate' to switch between
;; environments
- (unless (cl-loop for dir in (list conda-anaconda-home
- "~/.anaconda"
- "~/.miniconda"
- "~/.miniconda3"
- "~/miniconda3"
- "/usr/bin/anaconda3"
- "/usr/local/anaconda3"
- "/usr/local/miniconda3"
- "/usr/local/Caskroom/miniconda/base")
- if (file-directory-p dir)
- return (setq conda-anaconda-home dir
- conda-env-home-directory dir))
- (message "Cannot find Anaconda installation"))
+ (or (cl-loop for dir in (list conda-anaconda-home
+ "~/.anaconda"
+ "~/.miniconda"
+ "~/.miniconda3"
+ "~/miniconda3"
+ "/usr/bin/anaconda3"
+ "/usr/local/anaconda3"
+ "/usr/local/miniconda3"
+ "/usr/local/Caskroom/miniconda/base")
+ if (file-directory-p dir)
+ return (setq conda-anaconda-home dir
+ conda-env-home-directory dir))
+ (message "Cannot find Anaconda installation"))
;; integration with term/eshell
(conda-env-initialize-interactive-shells)
@@ -297,5 +299,6 @@ called.")
(use-package! flycheck-cython
+ :when (featurep! +cython)
:when (featurep! :tools flycheck)
:after cython-mode)
diff --git a/modules/lang/racket/config.el b/modules/lang/racket/config.el
index 40b2048df..894eb3b39 100644
--- a/modules/lang/racket/config.el
+++ b/modules/lang/racket/config.el
@@ -24,10 +24,8 @@
(add-hook! 'racket-mode-hook
#'rainbow-delimiters-mode
- #'highlight-quoted-mode)
-
- (map! :map (racket-mode-map racket-repl-mode-map)
- :i "[" #'racket-smart-open-bracket)
+ #'highlight-quoted-mode
+ #'racket-smart-open-bracket-mode)
(map! :localleader
:map racket-mode-map
diff --git a/modules/lang/rst/config.el b/modules/lang/rst/config.el
new file mode 100644
index 000000000..34ea7a0b5
--- /dev/null
+++ b/modules/lang/rst/config.el
@@ -0,0 +1,18 @@
+;;; lang/rst/config.el -*- lexical-binding: t; -*-
+
+(use-package! sphinx-mode
+ :hook (rst-mode . sphinx-mode))
+
+(use-package! rst
+ :defer t
+ :config
+ (map! :localleader
+ :map rst-mode-map
+ (:prefix ("a" . "adjust")
+ "a" #'rst-adjust
+ "r" #'rst-adjust-region)
+ (:prefix ("t" . "table of contents")
+ "t" #'rst-toc
+ "i" #'rst-toc-insert
+ "u" #'rst-toc-update
+ "f" #'rst-toc-follow-link)))
diff --git a/modules/lang/rst/packages.el b/modules/lang/rst/packages.el
new file mode 100644
index 000000000..b4f32ff0b
--- /dev/null
+++ b/modules/lang/rst/packages.el
@@ -0,0 +1,4 @@
+;; -*- no-byte-compile: t; -*-
+;;; lang/rst/packages.el
+
+(package! sphinx-mode)
diff --git a/modules/lang/rust/config.el b/modules/lang/rust/config.el
index 6bbe39b04..87f8bb602 100644
--- a/modules/lang/rust/config.el
+++ b/modules/lang/rust/config.el
@@ -7,43 +7,13 @@
;;
;;; Packages
-(use-package! rust-mode
- :defer t
- :config
- (setq rust-indent-method-chain t)
- (add-hook 'rust-mode-hook #'rainbow-delimiters-mode)
-
- ;; This is necessary because both plugins are fighting for supremacy in
- ;; `auto-mode-alist', so rustic-mode *must* load second. It only needs to
- ;; happen once.
- ;;
- ;; rust-mode is still required for `racer'.
- (add-hook! 'rust-mode-hook
- (defun +rust-init-h ()
- "Switch to `rustic-mode', if it's available."
- (when (require 'rustic nil t)
- (rustic-mode))))
-
- (set-docsets! '(rust-mode rustic-mode) "Rust")
- (when (featurep! +lsp)
- (add-hook 'rust-mode-local-vars-hook #'lsp!)))
-
-
-(use-package! racer
- :unless (featurep! +lsp)
- :hook ((rust-mode rustic-mode) . racer-mode)
- :config
- (set-lookup-handlers! 'rust-mode
- :definition '(racer-find-definition :async t)
- :documentation '+rust-racer-lookup-documentation))
-
-
(use-package! rustic
- :when EMACS26+
- :after rust-mode
+ :mode ("\\.rs$" . rustic-mode)
:preface
(setq rustic-rls-pkg (if (featurep! +lsp) 'lsp-mode))
:config
+ (set-docsets! 'rustic-mode "Rust")
+
(setq rustic-indent-method-chain t
rustic-flycheck-setup-mode-line-p nil
;; use :editor format instead
@@ -52,38 +22,35 @@
;; buffers, so we disable it, but only for evil users, because it
;; affects `forward-sexp' and its ilk. See
;; https://github.com/rust-lang/rust-mode/issues/288.
- rustic-match-angle-brackets (not (featurep! :editor evil)))
+ rustic-match-angle-brackets (not (featurep! :editor evil))
+ ;; `rustic-setup-rls' uses `package-installed-p' to determine if
+ ;; lsp-mode/elgot are available. This breaks because Doom doesn't use
+ ;; package.el to begin with (and lazy loads it). This is already handled
+ ;; by the :tools lsp module, so...
+ rustic-lsp-setup-p nil)
(add-hook 'rustic-mode-hook #'rainbow-delimiters-mode)
- (defadvice! +rust--dont-install-packages-p (orig-fn &rest args)
- :around #'rustic-setup-rls
- (cl-letf (;; `rustic-setup-rls' uses `package-installed-p' to determine if
- ;; lsp-mode/elgot are available. This breaks because Doom doesn't
- ;; use package.el to begin with (and lazy loads it).
- ((symbol-function #'package-installed-p)
- (lambda (pkg)
- (require pkg nil t)))
- ;; If lsp/elgot isn't available, it attempts to install lsp-mode
- ;; via package.el. Doom manages its own dependencies so we disable
- ;; that behavior.
- ((symbol-function #'rustic-install-rls-client-p)
- (lambda (&rest _)
- (message "No RLS server running"))))
- (apply orig-fn args))))
+ (when (featurep! +lsp)
+ (add-hook 'rustic-mode-local-vars-hook #'lsp!)))
+
+
+(use-package! racer
+ :unless (featurep! +lsp)
+ :hook (rustic-mode . racer-mode)
+ :config
+ (set-lookup-handlers! 'rustic-mode
+ :definition '(racer-find-definition :async t)
+ :documentation '+rust-racer-lookup-documentation))
;;
;;; Tools
(use-package! cargo
- :after rust-mode
+ :after rustic-mode
:config
- (defvar +rust-keymap
- (if (boundp 'rustic-mode-map)
- rustic-mode-map
- rust-mode-map))
- (map! :map +rust-keymap
+ (map! :map rustic-mode-map
:localleader
(:prefix ("b" . "build")
:desc "cargo add" "a" #'cargo-process-add
diff --git a/modules/lang/rust/packages.el b/modules/lang/rust/packages.el
index 250637459..05fab0577 100644
--- a/modules/lang/rust/packages.el
+++ b/modules/lang/rust/packages.el
@@ -1,9 +1,7 @@
;; -*- no-byte-compile: t; -*-
;;; lang/rust/packages.el
-(when EMACS26+
- (package! rustic))
-(package! rust-mode)
+(package! rustic)
(unless (featurep! +lsp)
(package! racer))
diff --git a/modules/lang/scala/autoload.el b/modules/lang/scala/autoload.el
index d9eff6195..4733c1561 100644
--- a/modules/lang/scala/autoload.el
+++ b/modules/lang/scala/autoload.el
@@ -1,7 +1,7 @@
;;; lang/scala/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
-(defun +scala-comment-indent-new-line (&rest _)
+(defun +scala-comment-indent-new-line (&optional _)
"Continue the commnt on the current line.
Meant to be used for `scala-mode's `comment-line-break-function'."
diff --git a/modules/lang/swift/config.el b/modules/lang/swift/config.el
index 74fd60da3..68470f3c7 100644
--- a/modules/lang/swift/config.el
+++ b/modules/lang/swift/config.el
@@ -22,7 +22,7 @@
(use-package! lsp-sourcekit
:when (featurep! +lsp)
:after swift-mode
- :init (add-hook 'swift-mode-hook #'lsp!)
+ :init (add-hook 'swift-mode-local-vars-hook #'lsp!)
:config
(unless (getenv "SOURCEKIT_TOOLCHAIN_PATH")
(setenv "SOURCEKIT_TOOLCHAIN_PATH" "/Library/Developer/Toolchains/swift-latest.xctoolchain"))
diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el
index 1ef7b6d03..7f8162814 100644
--- a/modules/lang/web/+css.el
+++ b/modules/lang/web/+css.el
@@ -28,12 +28,6 @@
(after! css-mode
;; css-mode hooks apply to scss and less-css modes
(add-hook 'css-mode-hook #'rainbow-delimiters-mode)
- (set-company-backend! '(css-mode scss-mode)
- (if EMACS26+
- ;; DEPRECATED css-mode's built in completion is superior in 26+
- 'company-capf
- 'company-css))
-
(map! :localleader
:map scss-mode-map
"b" #'+css/scss-build
diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el
index 0444fb481..424cad0c3 100644
--- a/modules/lang/web/+html.el
+++ b/modules/lang/web/+html.el
@@ -42,11 +42,8 @@
(cl-loop for pair in (cdr alist)
unless (string-match-p "^[a-z-]" (cdr pair))
collect (cons (car pair)
- ;; TODO Replace with `string-trim-right' (Emacs 26+)
- (let ((string (cdr pair)))
- (if (string-match "\\(?:>\\|]\\|}\\)+\\'" string)
- (replace-match "" t t string)
- string))))))
+ (string-trim-right (cdr pair)
+ "\\(?:>\\|]\\|}\\)+\\'")))))
(delq! nil web-mode-engines-auto-pairs))
(map! :map web-mode-map
diff --git a/modules/lang/web/autoload/css.el b/modules/lang/web/autoload/css.el
index 2389af5b3..e1844b297 100644
--- a/modules/lang/web/autoload/css.el
+++ b/modules/lang/web/autoload/css.el
@@ -44,7 +44,7 @@
(+css--toggle-inline-or-block beg end))))
;;;###autoload
-(defun +css/comment-indent-new-line ()
+(defun +css/comment-indent-new-line (&optional _)
"Continues the comment in an indented new line.
Meant for `comment-line-break-function' in `css-mode' and `scss-mode'."
diff --git a/modules/lang/web/packages.el b/modules/lang/web/packages.el
index 2efb16915..d0933030a 100644
--- a/modules/lang/web/packages.el
+++ b/modules/lang/web/packages.el
@@ -12,7 +12,7 @@
;; +css.el
(package! css-mode :built-in t)
-(package! less-css-mode :built-in (not (version< emacs-version "26.1")))
+(package! less-css-mode :built-in t)
(package! sass-mode)
(package! stylus-mode)
diff --git a/modules/term/eshell/config.el b/modules/term/eshell/config.el
index f07a3d43a..5b05e0810 100644
--- a/modules/term/eshell/config.el
+++ b/modules/term/eshell/config.el
@@ -22,9 +22,9 @@ buffer.")
(defvar +eshell-aliases
'(("q" "exit") ; built-in
("f" "find-file $1")
+ ("d" "dired $1")
("bd" "eshell-up $1")
("rg" "rg --color=always $*")
- ("ag" "ag --color=always $*")
("l" "ls -lh")
("ll" "ls -lah")
("clear" "clear-scrollback")) ; more sensible than default
@@ -132,7 +132,7 @@ You should use `set-eshell-alias!' to change this.")
:n "C" #'+eshell/evil-change-line
:n "d" #'+eshell/evil-delete
:n "D" #'+eshell/evil-delete-line
- :ig "C-d" #'+eshell/quit-or-delete-char
+ :ig "C-d" #'+eshell/quit-or-delete-char
"TAB" #'+eshell/pcomplete
[tab] #'+eshell/pcomplete
"C-s" #'+eshell/search-history
diff --git a/modules/tools/ein/autoload/hydra.el b/modules/tools/ein/autoload/hydra.el
index c06b65f2e..3bbba5fcd 100644
--- a/modules/tools/ein/autoload/hydra.el
+++ b/modules/tools/ein/autoload/hydra.el
@@ -1,7 +1,7 @@
;;; tools/ein/autoload/hydra.el -*- lexical-binding: t; -*-
;;;###if (featurep! :ui hydra)
-;;;###autoload (autoload '+ein/hydra/body "tools/ein/autoload" nil t)
+;;;###autoload (autoload '+ein/hydra/body "tools/ein/autoload/hydra" nil t)
(defhydra +ein/hydra (:hint t :color red)
"
Operations on Cells^^^^^^ Other
diff --git a/modules/tools/eval/autoload/eval.el b/modules/tools/eval/autoload/eval.el
index 3d82bff7d..41be372c4 100644
--- a/modules/tools/eval/autoload/eval.el
+++ b/modules/tools/eval/autoload/eval.el
@@ -56,18 +56,27 @@
(defun +eval/buffer ()
"Evaluate the whole buffer."
(interactive)
- (cond ((assq major-mode +eval-runners)
- (+eval/region (point-min) (point-max)))
- ((quickrun))))
+ (if (or (assq major-mode +eval-runners)
+ (and (fboundp '+eval--ensure-in-repl-buffer)
+ (ignore-errors
+ (get-buffer-window (or (+eval--ensure-in-repl-buffer)
+ t)))))
+ (+eval/region (point-min) (point-max))
+ (quickrun)))
;;;###autoload
(defun +eval/region (beg end)
"Evaluate a region between BEG and END and display the output."
(interactive "r")
(let ((load-file-name buffer-file-name))
- (if-let (runner (cdr (assq major-mode +eval-runners)))
- (funcall runner beg end)
- (quickrun-region beg end))))
+ (if (and (fboundp '+eval--ensure-in-repl-buffer)
+ (ignore-errors
+ (get-buffer-window (or (+eval--ensure-in-repl-buffer)
+ t))))
+ (+eval/send-region-to-repl beg end)
+ (if-let (runner (cdr (assq major-mode +eval-runners)))
+ (funcall runner beg end)
+ (quickrun-region beg end)))))
;;;###autoload
(defun +eval/line-or-region ()
diff --git a/modules/tools/eval/autoload/evil.el b/modules/tools/eval/autoload/evil.el
index 2583e8043..218b12ce6 100644
--- a/modules/tools/eval/autoload/evil.el
+++ b/modules/tools/eval/autoload/evil.el
@@ -6,10 +6,7 @@
"Evaluate selection or sends it to the open REPL, if available."
:move-point nil
(interactive "")
- (if (and (fboundp '+eval--ensure-in-repl-buffer)
- (+eval--ensure-in-repl-buffer))
- (+eval/send-region-to-repl (point-min) (point-max))
- (+eval/region beg end)))
+ (+eval/region beg end))
;;;###autoload (autoload '+eval:replace-region "tools/eval/autoload/evil" nil t)
(evil-define-operator +eval:replace-region (beg end)
diff --git a/modules/tools/eval/autoload/repl.el b/modules/tools/eval/autoload/repl.el
index 09d1bdc22..7db09a522 100644
--- a/modules/tools/eval/autoload/repl.el
+++ b/modules/tools/eval/autoload/repl.el
@@ -115,11 +115,28 @@ immediately after."
(buffer (+eval--ensure-in-repl-buffer)))
(unless buffer
(error "No REPL open"))
- (with-selected-window (get-buffer-window buffer)
- (when (bound-and-true-p evil-local-mode)
- (call-interactively #'evil-append-line))
- (insert (string-trim selection))
- (unless inhibit-auto-execute-p
- ;; `comint-send-input' isn't enough because some REPLs may not use
- ;; comint, so just emulate the keypress.
- (execute-kbd-macro (kbd "RET"))))))
+ (let ((origin-window (selected-window))
+ (selection
+ (with-temp-buffer
+ (insert selection)
+ (goto-char (point-min))
+ (when (> (skip-chars-forward "\n") 0)
+ (delete-region (point-min) (point)))
+ (indent-rigidly (point) (point-max)
+ (- (skip-chars-forward " \t")))
+ (concat (string-trim-right (buffer-string))
+ "\n"))))
+ (with-selected-window (get-buffer-window buffer)
+ (with-current-buffer buffer
+ (dolist (line (split-string selection "\n"))
+ (insert line)
+ (if inhibit-auto-execute-p
+ (insert "\n")
+ ;; `comint-send-input' isn't enough because some REPLs may not use
+ ;; comint, so just emulate the keypress.
+ (execute-kbd-macro (kbd "RET")))
+ (sit-for 0.001)
+ (redisplay 'force)))
+ (when (and (eq origin-window (selected-window))
+ (bound-and-true-p evil-local-mode))
+ (call-interactively #'evil-append-line))))))
diff --git a/modules/tools/flycheck/config.el b/modules/tools/flycheck/config.el
index 1c9fb0f5a..dc77b4790 100644
--- a/modules/tools/flycheck/config.el
+++ b/modules/tools/flycheck/config.el
@@ -63,7 +63,6 @@ errors.")
(use-package! flycheck-posframe
- :when EMACS26+
:when (featurep! +childframe)
:defer t
:init (add-hook 'flycheck-mode-hook #'+flycheck-init-popups-h)
diff --git a/modules/tools/flycheck/packages.el b/modules/tools/flycheck/packages.el
index e5e2f0885..b9bcdfd82 100644
--- a/modules/tools/flycheck/packages.el
+++ b/modules/tools/flycheck/packages.el
@@ -3,5 +3,5 @@
(package! flycheck)
(package! flycheck-popup-tip)
-(when (and EMACS26+ (featurep! +childframe))
+(when (featurep! +childframe)
(package! flycheck-posframe))
diff --git a/modules/tools/lookup/autoload/docsets.el b/modules/tools/lookup/autoload/docsets.el
index a3e20021f..0e20f061b 100644
--- a/modules/tools/lookup/autoload/docsets.el
+++ b/modules/tools/lookup/autoload/docsets.el
@@ -65,9 +65,10 @@ Docsets must be installed with one of the following commands:
+ `dash-docs-async-install-docset-from-file'
Docsets can be searched directly via `+lookup/in-docsets'."
- (when-let (docsets (cl-remove-if-not #'dash-docs-docset-path (dash-docs-buffer-local-docsets)))
- (+lookup/in-docsets nil identifier docsets)
- 'deferred))
+ (when (require 'dash-docs nil t)
+ (when-let (docsets (cl-remove-if-not #'dash-docs-docset-path (dash-docs-buffer-local-docsets)))
+ (+lookup/in-docsets nil identifier docsets)
+ 'deferred)))
;;
diff --git a/modules/tools/lookup/autoload/lookup.el b/modules/tools/lookup/autoload/lookup.el
index 2fd233599..46726eca0 100644
--- a/modules/tools/lookup/autoload/lookup.el
+++ b/modules/tools/lookup/autoload/lookup.el
@@ -208,17 +208,15 @@ This backend prefers \"just working\" over accuracy."
"Conducts a simple project text search for IDENTIFIER.
Uses and requires `+ivy-file-search' or `+helm-file-search'. Will return nil if
-neither is available. These search backends will use ag, rg, or pt (in an order
-dictated by `+ivy-project-search-engines' or `+helm-project-search-engines',
-falling back to git-grep)."
+neither is available. These require ripgrep to be installed."
(unless identifier
(let ((query (rxt-quote-pcre identifier)))
(ignore-errors
(cond ((featurep! :completion ivy)
- (+ivy-file-search nil :query query)
+ (+ivy-file-search :query query)
t)
((featurep! :completion helm)
- (+helm-file-search nil :query query)
+ (+helm-file-search :query query)
t))))))
(defun +lookup-evil-goto-definition-backend-fn (_identifier)
diff --git a/modules/tools/lookup/config.el b/modules/tools/lookup/config.el
index d519f2b23..6dc5adada 100644
--- a/modules/tools/lookup/config.el
+++ b/modules/tools/lookup/config.el
@@ -132,6 +132,7 @@ this list.")
(use-package! dash-docs
:when (featurep! +docsets)
+ :defer t
:init
(add-hook '+lookup-documentation-functions #'+lookup-dash-docsets-backend-fn)
:config
diff --git a/modules/tools/lsp/config.el b/modules/tools/lsp/config.el
index e37dcf8dd..8c323994f 100644
--- a/modules/tools/lsp/config.el
+++ b/modules/tools/lsp/config.el
@@ -52,14 +52,17 @@ auto-killed (which is usually an expensive process)."
(funcall orig-fn))))
lsp--cur-workspace))))
- (defadvice! +lsp-prompt-if-no-project-a (root)
- "Prompt for the project root only if no project was found.
-Also refuses to recognize $HOME as a valid project root."
- :filter-return #'lsp--calculate-root
- (cond ((and root (not (file-equal-p root "~")))
- root)
- (lsp-auto-guess-root
- (lsp--find-root-interactively (lsp-session)))))
+ (defadvice! +lsp-prompt-if-no-project-a (session file-name)
+ "Prompt for the project root only if no project was found."
+ :after-until #'lsp--calculate-root
+ (cond ((not lsp-auto-guess-root)
+ nil)
+ ((cl-find-if (lambda (dir)
+ (and (lsp--files-same-host dir file-name)
+ (file-in-directory-p file-name dir)))
+ (lsp-session-folders-blacklist session))
+ nil)
+ ((lsp--find-root-interactively session))))
(defadvice! +lsp-init-a (&optional arg)
"Enable `lsp-mode' in the current buffer.
@@ -106,8 +109,7 @@ Also logs the resolved project root, if found."
(defun +lsp-init-ui-flycheck-or-flymake-h ()
"Sets up flymake-mode or flycheck-mode, depending on `lsp-prefer-flymake'."
(cond ((eq :none lsp-prefer-flymake))
- ((and (not (version< emacs-version "26.1"))
- lsp-prefer-flymake)
+ (lsp-prefer-flymake
(lsp--flymake-setup))
((require 'flycheck nil t)
(require 'lsp-ui-flycheck)
diff --git a/modules/tools/pass/config.el b/modules/tools/pass/config.el
index 59094000b..d6d78baf8 100644
--- a/modules/tools/pass/config.el
+++ b/modules/tools/pass/config.el
@@ -23,7 +23,6 @@
(buffer-substring-no-properties (point-min) (point-max))))
-;; `pass'
(after! pass
(set-evil-initial-state! 'pass-mode 'emacs)
(set-popup-rule! "^\\*Password-Store" :side 'left :size 0.25 :quit nil)
@@ -36,5 +35,5 @@
;; Is built into Emacs 26+
-(when (and EMACS26+ (featurep! +auth))
+(when (featurep! +auth)
(auth-source-pass-enable))
diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el
index 5aa448a07..7df29520c 100644
--- a/modules/ui/doom-dashboard/config.el
+++ b/modules/ui/doom-dashboard/config.el
@@ -128,8 +128,6 @@ PLIST can have the following properties:
(add-hook 'persp-before-switch-functions #'+doom-dashboard--persp-record-project-h)))
(add-hook 'doom-init-ui-hook #'+doom-dashboard-init-h)
-(unless doom-debug-mode
- (remove-hook 'window-setup-hook #'doom-display-benchmark-h))
;;
@@ -200,8 +198,9 @@ PLIST can have the following properties:
(if (button-at (point))
(forward-button 0)
(backward-button 1)))
- (progn (goto-char (point-min))
- (forward-button 1))))
+ (ignore-errors
+ (goto-char (point-min))
+ (forward-button 1))))
(defun +doom-dashboard-reload-maybe-h ()
"Reload the dashboard or its state.
diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el
index 749757728..f95c73a0d 100644
--- a/modules/ui/doom/config.el
+++ b/modules/ui/doom/config.el
@@ -61,8 +61,8 @@
;; On Emacs 26+, when point is on the last line and solaire-mode is remapping
;; the hl-line face, hl-line's highlight bleeds into the rest of the window
- ;; after eob.
- (when EMACS26+
+ ;; after eob. On Emacs 27 this no longer happens.
+ (unless EMACS27+
(defun +doom--line-range-fn ()
(cons (line-beginning-position)
(cond ((let ((eol (line-end-position)))
diff --git a/modules/ui/hydra/config.el b/modules/ui/hydra/config.el
index b8bfe3f79..40a870fbe 100644
--- a/modules/ui/hydra/config.el
+++ b/modules/ui/hydra/config.el
@@ -8,3 +8,8 @@
;;;###package hydra
(setq lv-use-separator t)
+
+(defadvice! +hydra--inhibit-window-switch-hooks-a (orig-fn)
+ :around #'lv-window
+ (let ((doom-inhibit-switch-window-hooks t))
+ (funcall orig-fn)))
diff --git a/modules/ui/modeline/autoload.el b/modules/ui/modeline/autoload.el
index 514a6d245..7162bd069 100644
--- a/modules/ui/modeline/autoload.el
+++ b/modules/ui/modeline/autoload.el
@@ -28,9 +28,7 @@ Meant for `doom-change-font-size-hook'."
xlfd-regexp-pixelsize-subnum)))
(scale (frame-parameter nil 'font-scale)))
(setq doom-modeline-height (+ default-height (* scale doom-font-increment))))
- (setq doom-modeline-height default-height))
- ;; already has a variable watcher in Emacs 26+
- (unless EMACS26+ (doom-modeline-refresh-bars))))
+ (setq doom-modeline-height default-height))))
;;;###autoload
(defun +modeline-update-env-in-all-windows-h (&rest _)
diff --git a/modules/ui/modeline/config.el b/modules/ui/modeline/config.el
index d26458f56..ef23ab0c3 100644
--- a/modules/ui/modeline/config.el
+++ b/modules/ui/modeline/config.el
@@ -62,7 +62,7 @@
(doom-modeline-def-modeline 'special
'(bar window-number matches buffer-info-simple buffer-position selection-info)
- '(objed-state misc-info persp-name debug input-method irc-buffers buffer-encoding lsp major-mode process checker))
+ '(objed-state misc-info persp-name debug input-method irc buffer-encoding lsp major-mode process checker))
(doom-modeline-def-modeline 'project
'(bar window-number buffer-default-directory)
diff --git a/modules/ui/popup/autoload/popup.el b/modules/ui/popup/autoload/popup.el
index 24202a760..30159d815 100644
--- a/modules/ui/popup/autoload/popup.el
+++ b/modules/ui/popup/autoload/popup.el
@@ -139,11 +139,6 @@ default window parameters for popup windows, clears leftover transient timers
and enables `+popup-buffer-mode'."
(with-selected-window window
(setq alist (delq (assq 'actions alist) alist))
- (when (and alist +popup--populate-wparams)
- ;; Emacs 26+ will automatically map the window-parameters alist entry to
- ;; the popup window, so we need this for Emacs 25.x users
- (dolist (param (cdr (assq 'window-parameters alist)))
- (set-window-parameter window (car param) (cdr param))))
(set-window-parameter window 'popup t)
(set-window-parameter window 'split-window #'+popup--split-window)
(set-window-parameter window 'delete-window #'+popup--delete-window)
@@ -617,94 +612,3 @@ This advice ensures backwards compatibility for Emacs <= 26 users."
(when (and (windowp window) display-buffer-mark-dedicated)
(set-window-dedicated-p window display-buffer-mark-dedicated))
window))
-
-;; DEPRECATED
-(unless EMACS26+
- (defvar window-sides-reversed nil)
-
- (defun window--sides-reverse-on-frame-p (frame)
- "Return non-nil when side windows should appear reversed on FRAME.
-This uses some heuristics to guess the user's intentions when the
-selected window of FRAME is a side window ."
- (cond
- ;; Reverse when `window-sides-reversed' is t. Do not reverse when
- ;; `window-sides-reversed' is nil.
- ((memq window-sides-reversed '(nil t))
- window-sides-reversed)
- ;; Reverse when FRAME's selected window shows a right-to-left buffer.
- ((let ((window (frame-selected-window frame)))
- (when (and (not (window-parameter window 'window-side))
- (or (not (window-minibuffer-p window))
- (setq window (minibuffer-selected-window))))
- (with-current-buffer (window-buffer window)
- (eq bidi-paragraph-direction 'right-to-left)))))
- ;; Reverse when FRAME's `window-sides-main-selected-window' parameter
- ;; specifies a live window showing a right-to-left buffer.
- ((let ((window (frame-parameter
- frame 'window-sides-main-selected-window)))
- (when (window-live-p window)
- (with-current-buffer (window-buffer window)
- (eq bidi-paragraph-direction 'right-to-left)))))
- ;; Reverse when all windows in FRAME's main window show right-to-left
- ;; buffers.
- (t
- (catch 'found
- (walk-window-subtree
- (lambda (window)
- (with-current-buffer (window-buffer window)
- (when (eq bidi-paragraph-direction 'left-to-right)
- (throw 'found nil))))
- (window-main-window frame))
- t))))
-
- (defun window--make-major-side-window (buffer side slot &optional alist)
- "Display BUFFER in a new major side window on the selected frame.
-SIDE must be one of `left', `top', `right' or `bottom'. SLOT
-specifies the slot to use. ALIST is an association list of
-symbols and values as passed to `display-buffer-in-side-window'.
-Return the new window, nil if its creation failed.
-
-This is an auxiliary function of `display-buffer-in-side-window'
-and may be called only if no window on SIDE exists yet."
- (let* ((left-or-right (memq side '(left right)))
- (next-to (window--make-major-side-window-next-to side))
- (on-side (cond
- ((eq side 'top) 'above)
- ((eq side 'bottom) 'below)
- (t side)))
- (window--sides-inhibit-check t)
- ;; The following two bindings will tell `split-window' to take
- ;; the space for the new window from the selected frame's main
- ;; window and not make a new parent window unless needed.
- (window-combination-resize 'side)
- (window-combination-limit nil)
- (window (ignore-errors (split-window next-to nil on-side))))
- (when window
- ;; Initialize `window-side' parameter of new window to SIDE and
- ;; make that parameter persistent.
- (set-window-parameter window 'window-side side)
- (add-to-list 'window-persistent-parameters '(window-side . writable))
- ;; Install `window-slot' parameter of new window and make that
- ;; parameter persistent.
- (set-window-parameter window 'window-slot slot)
- (add-to-list 'window-persistent-parameters '(window-slot . writable))
- ;; Auto-adjust height/width of new window unless a size has been
- ;; explicitly requested.
- (unless (if left-or-right
- (cdr (assq 'window-width alist))
- (cdr (assq 'window-height alist)))
- (setq alist
- (cons
- (cons
- (if left-or-right 'window-width 'window-height)
- (/ (window-total-size (frame-root-window) left-or-right)
- ;; By default use a fourth of the size of the frame's
- ;; root window.
- 4))
- alist)))
- (with-current-buffer buffer
- (setq window--sides-shown t))
- ;; Install BUFFER in new window and return WINDOW.
- (window--display-buffer buffer window 'window alist 'side))))
-
- (advice-add #'window--sides-check :override #'ignore))
diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el
index 3381554ee..f34a49835 100644
--- a/modules/ui/popup/config.el
+++ b/modules/ui/popup/config.el
@@ -24,7 +24,6 @@ Modifying this has no effect, unless done before ui/popup loads.")
"Size of the margins to give popup windows. Set this to nil to disable margin
adjustment.")
-(defvar +popup--populate-wparams (not EMACS26+))
(defvar +popup--inhibit-transient nil)
(defvar +popup--inhibit-select nil)
(defvar +popup--old-display-buffer-alist nil)