Merge branch 'develop' of https://github.com/hlissner/doom-emacs into download-iosevka

This commit is contained in:
James Ravn 2019-11-20 09:47:52 +00:00
commit 825557895c
No known key found for this signature in database
GPG key ID: 52C372C72159D6EE
117 changed files with 1954 additions and 2926 deletions

View file

@ -16,9 +16,9 @@ jobs:
strategy: strategy:
matrix: matrix:
emacs_version: emacs_version:
- 25.3
- 26.1 - 26.1
- snapshot - 26.2
- 26.3
include: include:
- emacs_version: 26.3 - emacs_version: 26.3
lint_ignore: 1 lint_ignore: 1

View file

@ -2,7 +2,7 @@
<img src="https://img.shields.io/github/tag/hlissner/doom-emacs.svg?label=release&color=orange" alt="Made with Doom Emacs"> <img src="https://img.shields.io/github/tag/hlissner/doom-emacs.svg?label=release&color=orange" alt="Made with Doom Emacs">
</a> </a>
<a href="https://emacs.org"> <a href="https://emacs.org">
<img src="https://img.shields.io/badge/Made_for-Emacs_25.3+-blueviolet.svg" alt="Made for Emacs 25.3+"> <img src="https://img.shields.io/badge/Made_for-Emacs_26.1+-blueviolet.svg" alt="Made for Emacs 26.1+">
</a> </a>
<a href="https://github.com/hlissner/doom-emacs/actions"> <a href="https://github.com/hlissner/doom-emacs/actions">
<img src="https://github.com/hlissner/doom-emacs/workflows/CI/badge.svg" alt="Build status: develop"> <img src="https://github.com/hlissner/doom-emacs/workflows/CI/badge.svg" alt="Build status: develop">

165
bin/doom
View file

@ -1,105 +1,79 @@
#!/usr/bin/env sh #!/usr/bin/env sh
":"; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*- :; ( 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; } :; command -v $EMACS >/dev/null || { >&2 echo "Can't find emacs in your PATH"; exit 1; }
":"; VERSION=$($EMACS --version | head -n1) :; 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 :; 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")/.. :; DOOMBASE="$(dirname "$0")/.."
":"; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; } :; [ "$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; }
":"; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; } :; exec $EMACS --script "$0" -- "$@"
":"; exec $EMACS --script "$0" -- "$@" :; exit 0
":"; exit 0
(defconst user-emacs-directory (let* ((loaddir (file-name-directory (file-truename load-file-name)))
(or (getenv "EMACSDIR") (emacsdir (getenv "EMACSDIR"))
(expand-file-name "../" (file-name-directory (file-truename load-file-name))))) (user-emacs-directory (or emacsdir (expand-file-name "../" loaddir)))
(load-prefer-newer t))
(defun usage () (push (expand-file-name "core" user-emacs-directory) load-path)
(with-temp-buffer (require 'core)
(insert (format! "%s %s [COMMAND] [ARGS...]\n" (require 'core-cli)
(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))
;; (defcli! :main
(let ((args (cdr (cdr (cdr command-line-args))))) ((help-p ["-h" "--help"] "Same as help command")
;; Parse options (debug-p ["-d" "--debug"] "Turns on doom-debug-mode (and debug-on-error)")
(while (ignore-errors (string-prefix-p "-" (car args))) (yes-p ["-y" "--yes"] "Auto-accept all confirmation prompts")
(pcase (pop args) (emacsdir ["--emacsdir" dir] "Use the emacs config at DIR (e.g. ~/.emacs.d)")
((or "-h" "--help") (doomdir ["--doomdir" dir] "Use the private module at DIR (e.g. ~/.doom.d)")
(push "help" args)) (localdir ["--localdir" dir] "Use DIR as your local storage directory")
((or "-d" "--debug") &optional command &rest args)
"A command line interface for managing Doom Emacs.
Includes package management, diagnostics, unit tests, and byte-compilation.
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") (setenv "DEBUG" "1")
(message "Debug mode on")) (setq doom-debug-mode t)
((or "-i" "--insecure") (print! (info "Debug mode on")))
(setenv "INSECURE" "1") (when yes-p
(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") (setenv "YES" "1")
(message "Auto-yes mode on")))) (setq doom-auto-accept t)
(print! (info "Auto-yes on")))
(when help-p
(push command args)
(setq command "help"))
(unless (file-directory-p user-emacs-directory) ;; Reload core in case any of the directories were changed.
(error "%s does not exist" user-emacs-directory)) (when (or emacsdir doomdir localdir)
(load! "core/core.el" user-emacs-directory))
;; Bootstrap Doom (cond ((not noninteractive)
(if (not noninteractive) (print! "Doom launched out of %s (test mode)" (path user-emacs-directory))
(let ((doom-interactive-mode t)) (load! "init.el" user-emacs-directory)
(load (expand-file-name "init.el" user-emacs-directory)
nil 'nomessage)
(doom-run-all-startup-hooks-h)) (doom-run-all-startup-hooks-h))
(load (expand-file-name "core/core.el" user-emacs-directory)
nil 'nomessage)
(doom-initialize 'force-p)
(doom-initialize-modules)
(cond ((or (not args) ((null command)
(and (not (cdr args)) (doom-cli-execute "help"))
(member (car args) '("help" "h"))))
(unless args ((condition-case e
(print! (error "No command detected.\n"))) (let ((start-time (current-time)))
(usage)) (and (doom-cli-execute command args)
((require 'core-cli) (terpri)
(setq argv nil) (print! (success "Finished! (%.4fs)")
(condition-case e (float-time
(doom-dispatch (car args) (cdr args)) (time-subtract (current-time)
start-time)))))
(user-error (user-error
(print! (error "%s\n") (error-message-string e)) (print! (error "%s\n") (error-message-string e))
(print! (yellow "See 'doom help %s' for documentation on this command.") (car args))) (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)))
@ -116,5 +90,8 @@
"report, please include it!\n\n" "report, please include it!\n\n"
"Emacs outputs to standard error, so you'll need to redirect stderr to\n" "Emacs outputs to standard error, so you'll need to redirect stderr to\n"
"stdout to pipe this to a file or clipboard!\n\n" "stdout to pipe this to a file or clipboard!\n\n"
" e.g. doom -d install 2>&1 | clipboard-program")) " e.g. doom -d install 2>&1 | clipboard-program\n"))
(signal 'doom-error e)))))))) (signal 'doom-error e)))))))
(doom-cli-execute :main (cdr (member "--" argv)))
(setq argv nil))

View file

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

View file

@ -289,6 +289,7 @@ belong to the current project."
(when (memq (current-buffer) buffer-list) (when (memq (current-buffer) buffer-list)
(switch-to-buffer (doom-fallback-buffer))) (switch-to-buffer (doom-fallback-buffer)))
(mapc #'doom-kill-buffer-and-windows buffer-list) (mapc #'doom-kill-buffer-and-windows buffer-list)
(delete-other-windows)
(when interactive (when interactive
(message "Killed %s buffers" (message "Killed %s buffers"
(- (length buffer-list) (- (length buffer-list)

View file

@ -124,89 +124,3 @@ Warning: freezes indefinitely on any stdin prompt."
(sit-for 0.1)) (sit-for 0.1))
(process-exit-status process)) (process-exit-status process))
(string-trim (buffer-string))))) (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))

View file

@ -8,10 +8,10 @@
emacs -Q -l init.el -f doom-run-all-startup-hooks-h" emacs -Q -l init.el -f doom-run-all-startup-hooks-h"
(run-hook-wrapped 'after-init-hook #'doom-try-run-hook) (run-hook-wrapped 'after-init-hook #'doom-try-run-hook)
(setq after-init-time (current-time)) (setq after-init-time (current-time))
(dolist (hook (list 'delayed-warnings-hook (mapc (doom-rpartial #'run-hook-wrapped #'doom-try-run-hook)
'emacs-startup-hook 'term-setup-hook (list 'delayed-warnings-hook
'window-setup-hook)) 'emacs-startup-hook 'tty-setup-hook
(run-hook-wrapped hook #'doom-try-run-hook))) 'window-setup-hook)))
;; ;;
@ -32,9 +32,8 @@ ready to be pasted in a bug report on github."
(doom-modules (doom-modules))) (doom-modules (doom-modules)))
(cl-letf (cl-letf
(((symbol-function 'sh) (((symbol-function 'sh)
(lambda (format) (lambda (&rest args)
(string-trim (cdr (apply #'doom-call-process args)))))
(shell-command-to-string format)))))
`((emacs `((emacs
(version . ,emacs-version) (version . ,emacs-version)
(features ,@system-configuration-features) (features ,@system-configuration-features)
@ -47,14 +46,14 @@ ready to be pasted in a bug report on github."
'server-running)))) 'server-running))))
(doom (doom
(version . ,doom-version) (version . ,doom-version)
(build . ,(sh "git log -1 --format=\"%D %h %ci\""))) (build . ,(sh "git" "log" "-1" "--format=%D %h %ci")))
(system (system
(type . ,system-type) (type . ,system-type)
(config . ,system-configuration) (config . ,system-configuration)
(shell . ,shell-file-name) (shell . ,shell-file-name)
(uname . ,(if IS-WINDOWS (uname . ,(if IS-WINDOWS
"n/a" "n/a"
(sh "uname -msrv"))) (sh "uname" "-msrv")))
(path . ,(mapcar #'abbreviate-file-name exec-path))) (path . ,(mapcar #'abbreviate-file-name exec-path)))
(config (config
(envfile (envfile
@ -117,7 +116,7 @@ branch and commit."
"n/a") "n/a")
(or (vc-git-working-revision doom-core-dir) (or (vc-git-working-revision doom-core-dir)
"n/a") "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")))) "n/a"))))
;;;###autoload ;;;###autoload
@ -302,34 +301,6 @@ to reproduce bugs and determine if Doom is to blame."
;; ;;
;;; Reporting bugs ;;; 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 ;;;###autoload
(defun doom/report-bug () (defun doom/report-bug ()
"Open a markdown buffer destinated to populate the New Issue page on Doom "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' If called when a backtrace buffer is present, it and the output of `doom-info'
will be automatically appended to the result." will be automatically appended to the result."
(interactive) (interactive)
;; TODO Refactor me (browse-url "https://github.com/hlissner/doom-emacs/issues/new/choose"))
(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<details>\n<summary>Backtrace</summary>\n\n```\n%s\n```\n</details>\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))))
;; ;;

View file

@ -169,8 +169,10 @@ single file or nested compound statement of `and' and `or' statements."
;;;###autoload ;;;###autoload
(defun doom-file-size (file &optional dir) (defun doom-file-size (file &optional dir)
"Returns the size of FILE (in DIR) in kilobytes." "Returns the size of FILE (in DIR) in bytes."
(when-let (file (file-exists-p! file dir)) (let ((file (expand-file-name file dir)))
(unless (file-exists-p file)
(error "Couldn't find file %S" file))
(unless (file-readable-p file) (unless (file-readable-p file)
(error "File %S is unreadable; can't acquire its filesize" (error "File %S is unreadable; can't acquire its filesize"
file)) file))
@ -179,6 +181,8 @@ single file or nested compound statement of `and' and `or' statements."
;;;###autoload ;;;###autoload
(defun doom-directory-size (dir) (defun doom-directory-size (dir)
"Returns the size of FILE (in DIR) in kilobytes." "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") (if (executable-find "du")
(/ (string-to-number (cdr (doom-call-process "du" "-sb" dir))) (/ (string-to-number (cdr (doom-call-process "du" "-sb" dir)))
1024.0) 1024.0)
@ -313,18 +317,26 @@ file if it exists, without confirmation."
(`aborted (message "Aborted")) (`aborted (message "Aborted"))
(_ t))) (_ 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 ;;;###autoload
(defun doom/sudo-find-file (file) (defun doom/sudo-find-file (file)
"Open FILE as root." "Open FILE as root."
(interactive "FOpen file as root: ") (interactive "FOpen file as root: ")
(when (file-writable-p file) (find-file (doom--sudo-file 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))))
;;;###autoload ;;;###autoload
(defun doom/sudo-this-file () (defun doom/sudo-this-file ()
"Open the current file as root." "Open the current file as root."
(interactive) (interactive)
(doom/sudo-find-file (file-truename buffer-file-name))) (find-alternate-file (doom--sudo-file buffer-file-name)))

View file

@ -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")))))))) (message "Couldn't find the config block"))))))))
(defun doom--help-package-configs (package) (defun doom--help-package-configs (package)
;; TODO Add git checks, in case ~/.emacs.d isn't a git repo
(let ((default-directory doom-emacs-dir)) (let ((default-directory doom-emacs-dir))
;; TODO Use ripgrep instead
(split-string (split-string
(shell-command-to-string (cdr (doom-call-process
(format "git grep --no-break --no-heading --line-number '%s %s\\($\\| \\)' ':(exclude)*.org'" "git" "grep" "--no-break" "--no-heading" "--line-number"
(format "%s %s\\($\\| \\)"
"\\(^;;;###package\\|(after!\\|(use-package!\\)" "\\(^;;;###package\\|(after!\\|(use-package!\\)"
package)) package)
":(exclude)*.org"))
"\n" t))) "\n" t)))
;;;###autoload ;;;###autoload
@ -463,8 +465,8 @@ If prefix arg is present, refresh the cache."
(`straight (`straight
(format! "Straight (%s)\n%s" (format! "Straight (%s)\n%s"
(let ((default-directory (straight--build-dir (symbol-name package)))) (let ((default-directory (straight--build-dir (symbol-name package))))
(string-trim (cdr
(shell-command-to-string "git log -1 --format=\"%D %h %ci\""))) (doom-call-process "git" "log" "-1" "--format=%D %h %ci")))
(indent (indent
13 (string-trim 13 (string-trim
(pp-to-string (pp-to-string
@ -608,18 +610,12 @@ Uses the symbol at point or the current selection, if available."
(list (read-string (list (read-string
(format "Search load-path (default: %s): " query) (format "Search load-path (default: %s): " query)
nil 'git-grep query)))) nil 'git-grep query))))
;; REVIEW Replace with deadgrep or ivy/helm interface when we drop ag/git-grep ;; REVIEW Replace with deadgrep
;; support later
(grep-find (grep-find
(mapconcat (mapconcat
#'shell-quote-argument #'shell-quote-argument
(cond ((executable-find "rg") (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query)
`("rg" "-L" "--search-zip" "--no-heading" "--color=never" (cl-remove-if-not #'file-directory-p load-path))
,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")))
" "))) " ")))
;; TODO factor our the duplicate code between this and the above ;; 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 (list (read-string
(format "Search load-path (default: %s): " query) (format "Search load-path (default: %s): " query)
nil 'git-grep query)))) nil 'git-grep query))))
(unless (executable-find "rg")
(user-error "Can't find ripgrep on your system"))
(require 'elisp-refs) (require 'elisp-refs)
;; REVIEW Replace with deadgrep or ivy/helm interface when we drop ag/git-grep ;; REVIEW Replace with deadgrep
;; support later
(grep-find (grep-find
(mapconcat (mapconcat
#'shell-quote-argument #'shell-quote-argument
(let ((search (elisp-refs--loaded-paths))) (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query)
(cond ((executable-find "rg") (cl-remove-if-not #'file-directory-p (elisp-refs--loaded-paths)))
`("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"))))
" "))) " ")))

View file

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

View file

@ -81,7 +81,7 @@
Excludes packages that have a non-nil :built-in property." Excludes packages that have a non-nil :built-in property."
(when-let (plist (doom-package-get package)) (when-let (plist (doom-package-get package))
(not (plist-get plist :ignore) t))) (not (plist-get plist :ignore))))
;;;###autoload ;;;###autoload
(defun doom-package-private-p (package) (defun doom-package-private-p (package)

View file

@ -9,7 +9,7 @@
Evaluate BODY with either ARGLIST bound to (cons PROP VAL) or, if ARGLIST is a Evaluate BODY with either ARGLIST bound to (cons PROP VAL) or, if ARGLIST is a
list, the pair is destructured into (CAR . CDR)." list, the pair is destructured into (CAR . CDR)."
(declare (indent defun)) (declare (indent 1))
(let ((plist-var (make-symbol "plist"))) (let ((plist-var (make-symbol "plist")))
`(let ((,plist-var (copy-sequence ,plist))) `(let ((,plist-var (copy-sequence ,plist)))
(while ,plist-var (while ,plist-var

View file

@ -117,8 +117,7 @@ If DIR is not a project, it will be indexed (but not cached)."
#'projectile-find-file))) #'projectile-find-file)))
((fboundp 'counsel-file-jump) ; ivy only ((fboundp 'counsel-file-jump) ; ivy only
(call-interactively #'counsel-file-jump)) (call-interactively #'counsel-file-jump))
((and (fboundp 'project-find-file-in) ; emacs 26.1+ only ((project-current)
(project-current))
(project-find-file-in nil (list default-directory) nil)) (project-find-file-in nil (list default-directory) nil))
((fboundp 'helm-find-files) ((fboundp 'helm-find-files)
(call-interactively #'helm-find-files)) (call-interactively #'helm-find-files))

View file

@ -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." See `display-line-numbers' for what these values mean."
(interactive) (interactive)
(defvar doom--line-number-style display-line-numbers-type) (defvar doom--line-number-style display-line-numbers-type)
;; DEPRECATED (let* ((styles `(t ,(if visual-line-mode 'visual 'relative) nil))
(let* ((styles `(t ,(if (and EMACS26+ visual-line-mode) 'visual 'relative) nil))
(order (cons display-line-numbers-type (remq display-line-numbers-type styles))) (order (cons display-line-numbers-type (remq display-line-numbers-type styles)))
(queue (memq doom--line-number-style order)) (queue (memq doom--line-number-style order))
(next (if (= (length queue) 1) (next (if (= (length queue) 1)
(car order) (car order)
(car (cdr queue))))) (car (cdr queue)))))
(setq doom--line-number-style next) (setq doom--line-number-style next)
;; DEPRECATED
(if EMACS26+
(setq display-line-numbers next) (setq display-line-numbers next)
(pcase next
(`t (nlinum-relative-off) (nlinum-mode +1))
(`relative (nlinum-relative-on))
(`nil (nlinum-mode -1))))
(message "Switched to %s line numbers" (message "Switched to %s line numbers"
(pcase next (pcase next
(`t "normal") (`t "normal")

View file

@ -1,14 +1,11 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*- ;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
(require 'autoload)
(defvar doom-autoload-excluded-packages '("gh") (defvar doom-autoload-excluded-packages '("gh")
"Packages that have silly or destructive autoload files that try to load "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 everyone in the universe and their dog, causing errors that make babies cry. No
one wants that.") one wants that.")
;; external variables ;; externs
(defvar autoload-timestamps) (defvar autoload-timestamps)
(defvar generated-autoload-load-name) (defvar generated-autoload-load-name)
(defvar generated-autoload-file) (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', It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
`package-activated-list' and `auto-mode-alist'." `package-activated-list' and `auto-mode-alist'."
;; REVIEW Can we avoid calling `straight-check-all' everywhere?
(straight-check-all) (straight-check-all)
(doom-reload-autoloads nil 'force)) (doom-cli-reload-autoloads nil 'force))
;; ;;
;;; Helpers ;;; 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." "Delete FILE (an autoloads file) and accompanying *.elc file, if any."
(cl-check-type file string) (cl-check-type file string)
(when (file-exists-p file) (when (file-exists-p file)
@ -47,29 +43,29 @@ It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
(ignore-errors (delete-file (byte-compile-dest-file file))) (ignore-errors (delete-file (byte-compile-dest-file file)))
t)) 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 "Restart or reload Doom Emacs for changes to take effect:\n")
(message " M-x doom/restart-and-restore") (message " M-x doom/restart-and-restore")
(message " M-x doom/restart") (message " M-x doom/restart")
(message " M-x doom/reload")) (message " M-x doom/reload"))
(defun doom--byte-compile-file (file) (defun doom--cli-byte-compile-file (file)
(let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)) (let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings))
(byte-compile-dynamic t) (byte-compile-dynamic t)
(byte-compile-dynamic-docstrings t)) (byte-compile-dynamic-docstrings t))
(condition-case-unless-debug e (condition-case-unless-debug e
(when (byte-compile-file file) (when (byte-compile-file file)
(prog1 (load file 'noerror 'nomessage) (prog1 (load file 'noerror 'nomessage 'nosuffix)
(when noninteractive (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) ((debug error)
(let ((backup-file (concat file ".bk"))) (let ((backup-file (concat file ".bk")))
(print! (warn "Copied backup to %s") (relpath backup-file)) (print! (warn "Copied backup to %s") (relpath backup-file))
(copy-file file backup-file 'overwrite)) (copy-file file backup-file 'overwrite))
(doom-delete-autoloads-file file) (doom--cli-delete-autoloads-file file)
(signal 'doom-autoload-error (list file e)))))) (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. "Reloads FILE (an autoload file), if it needs reloading.
FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If 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))) (signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file) (if (stringp file)
(cond ((file-equal-p file doom-autoload-file) (cond ((file-equal-p file doom-autoload-file)
(doom-reload-core-autoloads force-p)) (doom-cli-reload-core-autoloads force-p))
((file-equal-p file doom-package-autoload-file) ((file-equal-p file doom-package-autoload-file)
(doom-reload-package-autoloads force-p)) (doom-cli-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file))) ((error "Invalid autoloads file: %s" file)))
(doom-reload-core-autoloads force-p) (doom-cli-reload-core-autoloads force-p)
(doom-reload-package-autoloads force-p))) (doom-cli-reload-package-autoloads force-p)))
;; ;;
;;; Doom autoloads ;;; Doom autoloads
(defun doom--generate-header (func) (defun doom--cli-generate-header (func)
(goto-char (point-min)) (goto-char (point-min))
(insert ";; -*- lexical-binding:t; -*-\n" (insert ";; -*- lexical-binding:t; -*-\n"
";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n")) ";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
(defun doom--generate-autoloads (targets) (defun doom--cli-generate-autoloads (targets)
(let ((n 0)) (let ((n 0))
(dolist (file targets) (dolist (file targets)
(insert (insert
@ -115,7 +111,7 @@ even if it doesn't need reloading!"
"Scanned %d file(s)") "Scanned %d file(s)")
n))) n)))
(defun doom--expand-autoload-paths (&optional allow-internal-paths) (defun doom--cli-expand-autoload-paths (&optional allow-internal-paths)
(let ((load-path (let ((load-path
;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files ;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files
;; will be unable to declare autoloads for the built-in autoload.el ;; will be unable to declare autoloads for the built-in autoload.el
@ -140,7 +136,7 @@ even if it doesn't need reloading!"
path) path)
t t nil 1))))) 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) (let (forms)
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t) (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
(let* ((sexp (sexp-at-point)) (let* ((sexp (sexp-at-point))
@ -202,7 +198,7 @@ even if it doesn't need reloading!"
(member-p (push sexp forms))))) (member-p (push sexp forms)))))
forms)) forms))
(defun doom--generate-autodefs (targets enabled-targets) (defun doom--cli-generate-autodefs (targets enabled-targets)
(goto-char (point-max)) (goto-char (point-max))
(search-backward ";;;***" nil t) (search-backward ";;;***" nil t)
(save-excursion (insert "\n")) (save-excursion (insert "\n"))
@ -210,17 +206,17 @@ even if it doesn't need reloading!"
(insert (insert
(with-temp-buffer (with-temp-buffer
(insert-file-contents path) (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") (concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
"\n") "\n")
""))))) "")))))
(defun doom--cleanup-autoloads () (defun doom--cli-cleanup-autoloads ()
(goto-char (point-min)) (goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t) (when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t t))) (replace-match "" t t)))
(defun doom-reload-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). "Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
@ -228,6 +224,7 @@ modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates
`doom-autoload-file'. `doom-autoload-file'.
Run this whenever your `doom!' block, or a module autoload file, is modified." Run this whenever your `doom!' block, or a module autoload file, is modified."
(require 'autoload)
(let* ((default-directory doom-emacs-dir) (let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules)) (doom-modules (doom-modules))
@ -269,37 +266,38 @@ Run this whenever your `doom!' block, or a module autoload file, is modified."
(ignore (ignore
(print! (success "Skipping core autoloads, they are up-to-date")) (print! (success "Skipping core autoloads, they are up-to-date"))
(doom-load-autoloads-file doom-autoload-file)) (doom-load-autoloads-file doom-autoload-file))
(print! (start "Regenerating core autoloads file")) (if (doom--cli-delete-autoloads-file doom-autoload-file)
(if (doom-delete-autoloads-file doom-autoload-file)
(print! (success "Deleted old %s") (filename doom-autoload-file)) (print! (success "Deleted old %s") (filename doom-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t)) (make-directory (file-name-directory doom-autoload-file) t))
(print! (start "Regenerating core autoloads file"))
(print-group!
(with-temp-file doom-autoload-file (with-temp-file doom-autoload-file
(doom--generate-header 'doom-reload-core-autoloads) (doom--cli-generate-header 'doom-cli-reload-core-autoloads)
(save-excursion (save-excursion
(doom--generate-autoloads active-targets) (doom--cli-generate-autoloads active-targets)
(print! (success "Generated new autoloads.el"))) (print! (success "Generated new autoloads.el")))
;; Replace autoload paths (only for module autoloads) with absolute ;; Replace autoload paths (only for module autoloads) with absolute
;; paths for faster resolution during load and simpler `load-path' ;; paths for faster resolution during load and simpler `load-path'
(save-excursion (save-excursion
(doom--expand-autoload-paths 'allow-internal-paths) (doom--cli-expand-autoload-paths 'allow-internal-paths)
(print! (success "Expanded module autoload paths"))) (print! (success "Expanded module autoload paths")))
;; Generates stub definitions for functions/macros defined in disabled ;; Generates stub definitions for functions/macros defined in disabled
;; modules, so that you will never get a void-function when you use ;; modules, so that you will never get a void-function when you use
;; them. ;; them.
(save-excursion (save-excursion
(doom--generate-autodefs targets (reverse active-targets)) (doom--cli-generate-autodefs targets (reverse active-targets))
(print! (success "Generated autodefs"))) (print! (success "Generated autodefs")))
;; Remove byte-compile-inhibiting file variables so we can byte-compile ;; Remove byte-compile-inhibiting file variables so we can byte-compile
;; the file, and autoload comments. ;; the file, and autoload comments.
(doom--cleanup-autoloads) (doom--cli-cleanup-autoloads)
(print! (success "Clean up autoloads"))) (print! (success "Cleaned up autoloads"))))
;; Byte compile it to give the file a chance to reveal errors (and buy us a ;; Byte compile it to give the file a chance to reveal errors (and buy us a
;; few marginal performance boosts) ;; few marginal performance boosts)
(print! "> Byte-compiling %s..." (relpath doom-autoload-file)) (print! "> Byte-compiling %s..." (relpath doom-autoload-file))
(when (doom--byte-compile-file doom-autoload-file) (when (doom--cli-byte-compile-file doom-autoload-file)
(print! (success "Finished compiling %s") (relpath doom-autoload-file)))) (print-group!
(print! (success "Compiled %s") (relpath doom-autoload-file)))))
t))) t)))
@ -346,7 +344,7 @@ served no purpose but to waste cycles."
(goto-char (match-beginning 1)) (goto-char (match-beginning 1))
(kill-sexp))) (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 "Compiles `doom-package-autoload-file' from the autoloads files of all
installed packages. It also caches `load-path', `Info-directory-list', installed packages. It also caches `load-path', `Info-directory-list',
`doom-disabled-packages', `package-activated-list' and `auto-mode-alist'. `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. FORCE-P (universal argument) is non-nil, regenerate it anyway.
This should be run whenever your `doom!' block or update your packages." This should be run whenever your `doom!' block or update your packages."
(require 'autoload)
(print! (start "Checking package autoloads file")) (print! (start "Checking package autoloads file"))
(print-group! (print-group!
(if (and (not force-p) (if (and (not force-p)
@ -381,14 +380,15 @@ This should be run whenever your `doom!' block or update your packages."
(version-control 'never) (version-control 'never)
(case-fold-search nil) ; reduce magic (case-fold-search nil) ; reduce magic
(autoload-timestamps nil)) (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)) (print! (success "Deleted old %s") (filename doom-package-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t)) (make-directory (file-name-directory doom-autoload-file) t))
(print! (start "Regenerating package autoloads file"))
(print-group!
(with-temp-file doom-package-autoload-file (with-temp-file doom-package-autoload-file
(doom--generate-header 'doom-reload-package-autoloads) (doom--cli-generate-header 'doom-cli-reload-package-autoloads)
(save-excursion (save-excursion
;; Cache important and expensive-to-initialize state here. ;; Cache important and expensive-to-initialize state here.
@ -401,17 +401,18 @@ This should be run whenever your `doom!' block or update your packages."
;; Replace autoload paths (only for module autoloads) with absolute ;; Replace autoload paths (only for module autoloads) with absolute
;; paths for faster resolution during load and simpler `load-path' ;; paths for faster resolution during load and simpler `load-path'
(save-excursion (save-excursion
(doom--expand-autoload-paths) (doom--cli-expand-autoload-paths)
(print! (success "Expanded module autoload paths"))) (print! (success "Expanded module autoload paths")))
;; Remove `load-path' and `auto-mode-alist' modifications (most of them, ;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
;; at least); they are cached later, so all those membership checks are ;; at least); they are cached later, so all those membership checks are
;; unnecessary overhead. ;; unnecessary overhead.
(doom--cleanup-package-autoloads) (doom--cleanup-package-autoloads)
(print! (success "Removed load-path/auto-mode-alist entries"))) (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 ;; Byte compile it to give the file a chance to reveal errors (and buy us a
;; few marginal performance boosts) ;; few marginal performance boosts)
(print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file)) (print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file))
(when (doom--byte-compile-file doom-package-autoload-file) (when (doom--cli-byte-compile-file doom-package-autoload-file)
(print! (success "Finished compiling %s") (relpath doom-package-autoload-file))))) (print-group!
(print! (success "Compiled %s") (relpath doom-package-autoload-file))))))
t)) t))

View file

@ -1,6 +1,8 @@
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*- ;;; 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. "Byte-compiles your config or selected modules.
compile [TARGETS...] compile [TARGETS...]
@ -10,14 +12,11 @@
Accepts :core and :private as special arguments, which target Doom's core files Accepts :core and :private as special arguments, which target Doom's core files
and your private config files, respectively. To recompile your packages, use and your private config files, respectively. To recompile your packages, use
'doom rebuild' instead." 'doom rebuild' instead."
(doom-byte-compile targets)) (doom-cli-byte-compile targets recompile-p))
(defcli! (recompile rc) (&rest targets)
"Re-byte-compiles outdated *.elc files."
(doom-byte-compile targets 'recompile))
(defcli! clean () (defcli! clean ()
"Delete all *.elc files." "Delete all *.elc files."
:bare t
(doom-clean-byte-compiled-files)) (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")) (not (equal (file-name-extension path) "el"))
(member filename (list "packages.el" "doctor.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. "Byte compiles your emacs configuration.
init.el is always byte-compiled by this. 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 (unless recompile-p
(doom-clean-byte-compiled-files)) (doom-clean-byte-compiled-files))
(dolist (target (delete-dups targets)) (dolist (target (delete-dups (delq nil targets)))
(cl-incf (cl-incf
(if (not (or (not recompile-p) (if (not (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target))) (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 () (defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private "Delete all the compiled elc files in your Emacs configuration and private
module. This does not include your byte-compiled, third party packages.'" module. This does not include your byte-compiled, third party packages.'"
(require 'core-modules)
(print! (start "Cleaning .elc files")) (print! (start "Cleaning .elc files"))
(print-group! (print-group!
(cl-loop with default-directory = doom-emacs-dir (cl-loop with default-directory = doom-emacs-dir

View file

@ -1,21 +1,11 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*- ;;; core/cli/debug.el -*- lexical-binding: t; -*-
(load! "autoload/debug" doom-core-dir)
;; ;;
;;; Commands ;;; Commands
(defcli! info (&optional format) (defcli! info
"Output system info in markdown for bug reports. ((format ["--json" "--md" "--lisp"] "What format to dump info into"))
"Output system info in markdown for bug reports."
Will print in the following formats:
--json
--md / --markdown
--lisp
If no arguments are given, --raw is assumed."
(pcase format (pcase format
("--json" ("--json"
(require 'json) (require 'json)
@ -23,7 +13,7 @@ If no arguments are given, --raw is assumed."
(insert (json-encode (doom-info))) (insert (json-encode (doom-info)))
(json-pretty-print-buffer) (json-pretty-print-buffer)
(print! (buffer-string)))) (print! (buffer-string))))
((or "--md" "--markdown") ("--md"
(doom/info)) (doom/info))
((or `nil "--lisp") ((or `nil "--lisp")
(doom/info 'raw)) (doom/info 'raw))
@ -33,6 +23,7 @@ If no arguments are given, --raw is assumed."
nil) nil)
(defcli! (version v) () (defcli! (version v) ()
"Reports the version of Doom and Emacs." "Show version information for Doom & Emacs."
:bare t
(doom/version) (doom/version)
nil) nil)

203
core/cli/doctor.el Normal file
View file

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

View file

@ -1,13 +1,14 @@
;;; core/cli/env.el -*- lexical-binding: t; -*- ;;; 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. "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 The envvars file is created by scraping your (interactive) shell environment
into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env' 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) (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 This file is automatically regenerated when you run this command or 'doom
refresh'. However, 'doom refresh' will only regenerate this file if it exists. refresh'. However, 'doom refresh' will only regenerate this file if it exists.
Use the -c or --clear switch to delete your envvar file." Why this over exec-path-from-shell?
(when (member "clear" args) ; DEPRECATED
(message "'doom env clear' is deprecated. Use 'doom env -c' or 'doom env --clear' instead") 1. `exec-path-from-shell' spawns (at least) one process at startup to scrape
(push "-c" args)) your shell environment. This can be arbitrarily slow depending on the
(let ((env-file (or (cadr (member "-o" args)) user's shell configuration. A single program (like pyenv or nvm) or config
doom-env-file))) framework (like oh-my-zsh) could undo all of Doom's startup optimizations
(cond ((or (member "-c" args) in one fell swoop.
(member "--clear" args))
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) (unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared" (user-error! "%S does not exist to be cleared"
(path env-file))) (path env-file)))
@ -36,12 +46,11 @@ Use the -c or --clear switch to delete your envvar file."
(print! (success "Successfully deleted %S") (print! (success "Successfully deleted %S")
(path env-file))) (path env-file)))
((or (null args) (args
(member "-o" args)) (user-error "I don't understand 'doom env %s'"
(doom-reload-env-file 'force env-file)) (string-join args " ")))
((user-error "I don't understand 'doom env %s'" ((doom-cli-reload-env-file 'force env-file)))))
(string-join args " "))))))
;; ;;
@ -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 Each string is a regexp, matched against variable names to omit from
`doom-env-file'.") `doom-env-file'.")
(defvar doom-env-executable (defun doom-cli-reload-env-file (&optional force-p env-file)
(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)
"Generates `doom-env-file', if it doesn't exist (or if FORCE-P). "Generates `doom-env-file', if it doesn't exist (or if FORCE-P).
This scrapes the variables from your shell environment by running 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") "Generating")
(path env-file)) (path env-file))
(let ((process-environment doom--initial-process-environment)) (let ((process-environment doom--initial-process-environment))
(let ((shell-command-switch doom-env-switches) (print! (info "Scraping shell environment"))
(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! (print-group!
(let ((errors (with-current-buffer error-buffer (buffer-string)))) (when doom-interactive-mode
(unless (string-empty-p errors) (user-error "'doom env' must be run on the command line, not an interactive session"))
(print! (info "Warnings:\n\n%s") (indent 4 errors)))) (goto-char (point-min))
;; Remove undesireable variables
(insert (insert
(concat (concat
"# -*- mode: dotenv -*-\n" "# -*- mode: dotenv -*-\n"
(format "# Generated with: %s %s %s\n" (format "# Generated from a %s shell environent\n" shell-file-name)
shell-file-name
doom-env-switches
doom-env-executable)
"# ---------------------------------------------------------------------------\n" "# ---------------------------------------------------------------------------\n"
"# This file was auto-generated by `doom env'. It contains a list of environment\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" "# variables scraped from your default shell (excluding variables blacklisted\n"
"# in doom-env-ignored-vars).\n" "# in doom-env-ignored-vars).\n"
"#\n" "#\n"
"# It is NOT safe to edit this file. Changes will be overwritten next time that\n" (if (file-equal-p env-file doom-env-file)
"# `doom refresh` is executed. Alternatively, create your own env file with\n" (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n"
"# `doom env -o ~/.doom.d/myenv`, then load it with (doom-load-envvars-file FILE)\n" "# run 'doom refresh'. To create a safe-to-edit envvar file use:\n#\n"
"# in your private config.el.\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")) "# ---------------------------------------------------------------------------\n\n"))
(goto-char (point-min)) ;; We assume that this noninteractive session was spawned from the
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t) ;; user's interactive shell, therefore we just dump
(save-excursion ;; `process-environment' to a file.
(let* ((valend (or (save-match-data (dolist (env process-environment)
(when (re-search-forward "^\\([^= ]+\\)=" nil t) (if (cl-find-if (doom-rpartial #'string-match-p env)
(line-beginning-position))) doom-env-ignored-vars)
(point-max))) (print! (info "Ignoring %s") env)
(var (match-string 1))) (insert env "\n")))
(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") (print! (success "Successfully generated %S")
(path env-file)) (path env-file))
t)))))) t))))))

101
core/cli/help.el Normal file
View file

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

View file

@ -1,13 +1,11 @@
;;; core/cli/install.el -*- lexical-binding: t; -*- ;;; core/cli/install.el -*- lexical-binding: t; -*-
(defcli! quickstart (&rest args) ; DEPRECATED (defcli! (install i)
"This is a deprecated alias for 'doom install'. ((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')")
See 'doom help install' instead." (noinstall-p ["--no-install"] "Don't auto-install packages")
:hidden t (nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts")
(apply #'doom-cli-install args)) &rest args)
(defcli! (install i) (&rest args)
"Installs and sets up Doom Emacs for the first time. "Installs and sets up Doom Emacs for the first time.
This command does the following: 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. DOOMDIR environment variable. e.g.
doom -p ~/.config/doom install doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom install DOOMDIR=~/.config/doom doom install"
:bare t
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"
(print! (green "Installing Doom Emacs!\n")) (print! (green "Installing Doom Emacs!\n"))
(let ((default-directory (doom-path "~"))) (let ((default-directory (doom-path "~")))
;; Create `doom-private-dir' ;; Create `doom-private-dir'
(if (member "--no-config" args) (if noconfig-p
(print! (warn "Not copying private config template, as requested")) (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) (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 ;; Create init.el, config.el & packages.el
(mapc (lambda (file) (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 ;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install) ;; 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 ;; Ask if user would like an envvar file generated
(if (member "--no-env" args) (if noenv-p
(print! (warn "- Not generating envvars file, as requested")) (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 (when (or doom-auto-accept
(y-or-n-p "Generate an env file? (see `doom help env` for details)")) (y-or-n-p "Generate an env file? (see `doom help env` for details)"))
(doom-reload-env-file 'force-p))) (doom-cli-reload-env-file 'force-p))))
;; Install Doom packages ;; Install Doom packages
(if (member "--no-install" args) (if noinstall-p
(print! (warn "- Not installing plugins, as requested")) (print! (warn "Not installing plugins, as requested"))
(print! "Installing plugins") (print! "Installing plugins")
(doom-packages-install doom-auto-accept)) (doom-cli-packages-install))
(print! "Regenerating autoloads files") (print! "Regenerating autoloads files")
(doom-reload-autoloads nil 'force-p) (doom-cli-reload-autoloads nil 'force-p)
(if (member "--no-fonts" args) (if nofonts-p
(print! (warn "- Not installing fonts, as requested")) (print! (warn "Not installing fonts, as requested"))
(when (or doom-auto-accept (when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?")) (y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons) (require 'all-the-icons)
@ -98,6 +93,9 @@ The following switches are recognized:
(IS-LINUX 'x)))) (IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes)))) (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")) (print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer (with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO") (doom-template-insert "QUICKSTART_INTRO")

View file

@ -1,73 +1,55 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el ;;; core/cli/packages.el
(defmacro doom--ensure-autoloads-while (&rest body) (defcli! (update u) ()
`(progn
(straight-check-all)
(doom-reload-core-autoloads)
(when (progn ,@body)
(doom-reload-package-autoloads 'force-p))
t))
;;
;;; Dispatchers
(defcli! (update u) (&rest args)
"Updates packages. "Updates packages.
This works by fetching all installed package repos and checking the distance This works by fetching all installed package repos and checking the distance
between HEAD and FETCH_HEAD. This can take a while. between HEAD and FETCH_HEAD. This can take a while.
This excludes packages whose `package!' declaration contains a non-nil :freeze This excludes packages whose `package!' declaration contains a non-nil :freeze
or :ignore property. or :ignore property."
(straight-check-all)
(doom-cli-reload-core-autoloads)
(when (doom-cli-packages-update)
(doom-cli-reload-package-autoloads 'force-p))
t)
Switches: (defcli! (build b)
-t/--timeout TTL Seconds until a thread is timed out (default: 45) ((rebuild-p ["-r"] "Only rebuild packages that need rebuilding"))
--threads N How many threads to use (default: 8)" "Byte-compiles & symlinks installed packages.
(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.
This ensures that all needed files are symlinked from their package repo and 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: (defcli! (purge p)
-f Forcibly rebuild autoloads files, even if they're up-to-date" ((nobuilds-p ["-b" "--no-builds"] "Don't purge unneeded (built) packages")
(doom--ensure-autoloads-while (noelpa-p ["-p" "--no-elpa"] "Don't purge ELPA packages")
(doom-packages-rebuild doom-auto-accept (member "-f" args)))) (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) Purges all installed ELPA packages (as they are considered temporary). Purges
"Deletes any unused ELPA packages, straight builds, and (optionally) repos. 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 It is a good idea to occasionally run this doom purge -g to ensure your package
'doom purge --all' once in a while, to stymy build-up of repos and ELPA list remains lean."
packages that could be taking up precious space. (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: ;; (defcli! rollback () ; TODO doom rollback
--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
;; "<Not implemented yet>" ;; "<Not implemented yet>"
;; (user-error "Not implemented yet, sorry!")) ;; (user-error "Not implemented yet, sorry!"))
@ -75,15 +57,12 @@ Switches:
;; ;;
;;; Library ;;; Library
(defun doom-packages-install (&optional auto-accept-p) (defun doom-cli-packages-install ()
"Installs missing packages. "Installs missing packages.
This function will install any primary package (i.e. a package with a `package!' This function will install any primary package (i.e. a package with a `package!'
declaration) or dependency thereof that hasn't already been. declaration) or dependency thereof that hasn't already been."
(print! (start "Installing & building packages..."))
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...")
(print-group! (print-group!
(let ((n 0)) (let ((n 0))
(dolist (package (hash-table-keys straight--recipe-cache)) (dolist (package (hash-table-keys straight--recipe-cache))
@ -91,7 +70,7 @@ a list of packages that will be installed."
(local-repo) (local-repo)
(let ((existed-p (file-directory-p (straight--repos-dir package)))) (let ((existed-p (file-directory-p (straight--repos-dir package))))
(condition-case-unless-debug e (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) (not existed-p)
(file-directory-p (straight--repos-dir package)) (file-directory-p (straight--repos-dir package))
(cl-incf n)) (cl-incf n))
@ -104,17 +83,18 @@ a list of packages that will be installed."
t)))) t))))
(defun doom-packages-rebuild (&optional auto-accept-p all) (defun doom-cli-packages-build (&optional force-p)
"(Re)build all packages." "(Re)build all packages."
(print! (start "(Re)building %spackages...") (if all "all " "")) (print! (start "(Re)building %spackages...") (if force-p "all " ""))
(print-group! (print-group!
(let ((n 0)) (let ((n 0))
(if all (if force-p
(let ((straight--packages-to-rebuild :all) (let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal))) (straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(dolist (package (hash-table-keys straight--recipe-cache)) (dolist (package (hash-table-keys straight--recipe-cache))
(straight-use-package (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)) (dolist (recipe (hash-table-values straight--recipe-cache))
(straight--with-plist recipe (package local-repo no-build) (straight--with-plist recipe (package local-repo no-build)
(unless (or no-build (null local-repo)) (unless (or no-build (null local-repo))
@ -139,7 +119,9 @@ a list of packages that will be installed."
(lambda (&rest _) (cl-incf n))) (lambda (&rest _) (cl-incf n)))
(let ((straight--packages-to-rebuild :all) (let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal))) (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) (straight--byte-compile-package recipe)
(dolist (dep (straight--get-dependencies package)) (dolist (dep (straight--get-dependencies package))
(when-let (recipe (gethash dep straight--recipe-cache)) (when-let (recipe (gethash dep straight--recipe-cache))
@ -151,268 +133,107 @@ a list of packages that will be installed."
t)))) t))))
(defun doom--packages-remove-outdated-f (packages) (defun doom-cli-packages-update ()
(async-start "Updates packages."
`(lambda () (print! (start "Updating packages (this may take a while)..."))
(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)) (let ((straight--packages-to-rebuild (make-hash-table :test #'equal))
(straight--packages-not-to-rebuild (make-hash-table :test #'equal))) (total (hash-table-count straight--repo-cache))
(dolist (spec specs) (i 1)
(cl-destructuring-bind (n pretime time recipe) spec errors)
(straight--with-plist recipe (local-repo package) (print-group!
(let ((default-directory (straight--repos-dir local-repo))) (dolist (recipe (hash-table-values straight--repo-cache))
(print! (start "Updating %S") package) (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) (straight-merge-package package)
;; HACK `straight-rebuild-package' doesn't pick up that (let ((newcommit (straight-vc-get-commit type local-repo)))
;; this package has changed, so we do it manually. Is (if (string= commit newcommit)
;; there a better way? (print! (info "(%d/%d) %s is up-to-date") i total package)
(ignore-errors (ignore-errors
(delete-directory (straight--build-dir package) 'recursive)) (delete-directory (straight--build-dir package) 'recursive))
(puthash package t straight--packages-to-rebuild) (puthash package t straight--packages-to-rebuild)
(cl-incf n)) (print! (success "(%d/%d) %s updated (%s -> %s)") i total package
(with-current-buffer (straight--process-get-buffer) (substring commit 0 7)
(with-silent-modifications (substring newcommit 0 7))
(print! (debug (autofill "%s") (indent 2 (buffer-string)))) (unless (string-empty-p output)
(erase-buffer)))))) (print-group!
(doom--finalize-straight) (print! (info "%s") output)
(doom-packages-rebuild auto-accept-p)) (when (eq type 'git)
t) (straight--call "git" "log" "--oneline" newcommit (concat "^" commit))
(print! (success "No packages to update")) (print-group!
nil)) (print! "%s" (straight--process-get-output))))))))))
(cl-incf i))
(user-error
(signal 'user-error (error-message-string e)))
(error (error
(message "Output:\n%s" (straight--process-get-output)) (print! (warn "(%d/%d) Encountered error with %s" i total package))
(signal (car e) (error-message-string e))))))) (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) ;;; PURGE (for the emperor)
(defun doom--prompt-p (list-fn list preamble postamble) (defun doom--cli-packages-purge-build (build)
(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)
(let ((build-dir (straight--build-dir build))) (let ((build-dir (straight--build-dir build)))
(print! (start "Purging build/%s..." build))
(delete-directory build-dir 'recursive) (delete-directory build-dir 'recursive)
(if (file-directory-p build-dir) (if (file-directory-p build-dir)
(ignore (print! (error "Failed to purg build/%s" build))) (ignore (print! (error "Failed to purg build/%s" build)))
(print! (success "Purged build/%s" build)) (print! (success "Purged build/%s" build))
t))) t)))
(defun doom--packages-purge-builds (builds &optional auto-accept-p) (defun doom--cli-packages-purge-builds (builds)
(if (not builds) (if (not builds)
(progn (print! (info "No builds to purge")) (progn (print! (info "No builds to purge"))
0) 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 (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))) (let ((default-directory (straight--repos-dir repo)))
(if (not (file-directory-p ".git")) (if (not (file-directory-p ".git"))
(ignore (print! (warn "repos/%s is not a git repo, skipping" repo))) (ignore (print! (warn "repos/%s is not a git repo, skipping" repo)))
(print! (debug "Regrafting repos/%s..." repo)) (let ((before-size (doom-directory-size default-directory)))
(straight--call "git" "reset" "--hard") (straight--call "git" "reset" "--hard")
(straight--call "git" "clean" "--ffd") (straight--call "git" "clean" "-ffd")
(straight--call "git" "replace" "--graft" "HEAD") (if (not (car (straight--call "git" "replace" "--graft" "HEAD")))
(print! (info "repos/%s is already compact" repo))
(straight--call "git" "gc") (straight--call "git" "gc")
(print! (debug "%s" (straight--process-get-output))) (print! (success "Regrafted repos/%s (from %0.1fKB to %0.1fKB)")
(print! (success "Regrafted repos/%s" repo)) repo before-size (doom-directory-size default-directory))
(print-group! (print! "%s" (straight--process-get-output)))))
t))) t)))
(defun doom--packages-regraft-repos (repos &optional auto-accept-p) (defun doom--cli-packages-regraft-repos (repos)
(if (not repos) (if (not repos)
(progn (print! (info "No repos to regraft")) (progn (print! (info "No repos to regraft"))
0) 0)
(or auto-accept-p (let ((before-size (doom-directory-size (straight--repos-dir))))
(y-or-n-p (format! "Preparing to regraft all %d repos. Continue?" (prog1 (print-group! (delq nil (mapcar #'doom--cli-packages-regraft-repo repos)))
(length repos))) (let ((after-size (doom-directory-size (straight--repos-dir))))
(user-error! "Aborted!")) (print! (success "Finished regrafting. Size before: %0.1fKB and after: %0.1fKB (%0.1fKB)")
(if (executable-find "du") before-size after-size
(cl-destructuring-bind (status . size) (- after-size before-size)))))))
(doom-sh "du" "-sh" (straight--repos-dir))
(prog1 (delq nil (mapcar #'doom--packages-regraft-repo repos))
(cl-destructuring-bind (status . newsize)
(doom-sh "du" "-sh" (straight--repos-dir))
(print! (success "Finshed regrafted. Size before: %s and after: %s"
(car (split-string size "\t"))
(car (split-string newsize "\t")))))))
(delq nil (mapcar #'doom--packages-regraft-repo repos)))))
(defun doom--packages-purge-repo (repo) (defun doom--cli-packages-purge-repo (repo)
(print! (debug "Purging repos/%s..." repo))
(let ((repo-dir (straight--repos-dir repo))) (let ((repo-dir (straight--repos-dir repo)))
(delete-directory repo-dir 'recursive) (delete-directory repo-dir 'recursive)
(ignore-errors (ignore-errors
@ -422,19 +243,14 @@ a list of packages that will be updated."
(print! (success "Purged repos/%s" repo)) (print! (success "Purged repos/%s" repo))
t))) t)))
(defun doom--packages-purge-repos (repos &optional auto-accept-p) (defun doom--cli-packages-purge-repos (repos)
(if (not repos) (if (not repos)
(progn (print! (info "No repos to purge")) (progn (print! (info "No repos to purge"))
0) 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 (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) (unless (bound-and-true-p package--initialized)
(package-initialize)) (package-initialize))
(let ((packages (cl-loop for (package desc) in package-alist (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) (if (not package-alist)
(progn (print! (info "No ELPA packages to purge")) (progn (print! (info "No ELPA packages to purge"))
0) 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) (mapc (doom-rpartial #'delete-directory 'recursive)
(mapcar #'cdr packages)) (mapcar #'cdr packages))
(length 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. "Auto-removes orphaned packages and repos.
An orphaned package is a package that isn't a primary package (i.e. doesn't have An orphaned package is a package that isn't a primary package (i.e. doesn't have
@ -461,10 +272,7 @@ a `package!' declaration) or isn't depended on by another primary package.
If BUILDS-P, include straight package builds. If BUILDS-P, include straight package builds.
If REPOS-P, include straight repos. If REPOS-P, include straight repos.
If ELPA-P, include packages installed with package.el (M-x package-install). If ELPA-P, include packages installed with package.el (M-x package-install)."
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be removed."
(print! (start "Searching for orphaned packages to purge (for the emperor)...")) (print! (start "Searching for orphaned packages to purge (for the emperor)..."))
(cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft) (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort)) (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! (print-group!
(if (not builds-p) (if (not builds-p)
(print! (info "Skipping builds")) (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) (setq success t)
(straight-prune-build-cache))) (straight-prune-build-cache)))
(if (not elpa-p) (if (not elpa-p)
(print! (info "Skipping elpa packages")) (print! (info "Skipping elpa packages"))
(and (/= 0 (doom--packages-purge-elpa auto-accept-p)) (and (/= 0 (doom--cli-packages-purge-elpa))
(setq success t))) (setq success t)))
(if (not repos-p) (if (not repos-p)
(print! (info "Skipping repos")) (print! (info "Skipping repos"))
(and (/= 0 (doom--packages-purge-repos repos-to-purge auto-accept-p)) (and (/= 0 (doom--cli-packages-purge-repos repos-to-purge))
(setq success t)) (setq success t)))
(and (doom--packages-regraft-repos repos-to-regraft auto-accept-p) (if (not regraft-repos-p)
(print! (info "Skipping regrafting"))
(and (doom--cli-packages-regraft-repos repos-to-regraft)
(setq success t))) (setq success t)))
(when success (when success
(doom--finalize-straight) (doom--finalize-straight)

View file

@ -7,17 +7,21 @@
runemacs-binary-path runemacs-binary-path
emacs-binary-path))) emacs-binary-path)))
(defcli! test (&rest targets) (defcli! test (&rest targets)
"Run Doom unit tests." "Run Doom unit tests."
(let (files error) :bare t
(doom-initialize 'force)
(require 'ansi-color)
(let (files error read-files)
(unless targets (unless targets
(setq targets (setq targets
(cons doom-core-dir (cons doom-core-dir
(cl-remove-if-not (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 ;; Omit `doom-private-dir', which is always first
(let (doom-modules) (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))))))) (cdr (doom-module-load-path)))))))
(while targets (while targets
(let ((target (pop targets))) (let ((target (pop targets)))
@ -29,47 +33,72 @@
(appendq! files (nreverse (doom-glob target "test/test-*.el")))) (appendq! files (nreverse (doom-glob target "test/test-*.el"))))
((file-exists-p target) ((file-exists-p target)
(push target files))))) (push target files)))))
(setenv "DOOMLOCALDIR" (concat doom-local-dir "test/"))
(setenv "DOOMDIR" (concat doom-core-dir "test/"))
(with-temp-buffer (with-temp-buffer
(print! (start "Bootstrapping test environment, if necessary...")) (print! (start "Bootstrapping test environment, if necessary..."))
(if (zerop (cl-destructuring-bind (status . output)
(call-process (doom-exec-process
(doom--emacs-binary) (doom--emacs-binary)
nil t nil "--batch" "--batch"
"--eval" (prin1-to-string "--eval"
(prin1-to-string
`(progn `(progn
(setq doom-emacs-dir ,doom-emacs-dir (setq user-emacs-directory ,doom-emacs-dir
doom-local-dir ,(concat doom-local-dir "test/") doom-auto-accept t)
doom-private-dir ,(concat doom-core-dir "test/"))
(require 'core ,(locate-library "core")) (require 'core ,(locate-library "core"))
(require 'core-cli)
(doom-initialize 'force) (doom-initialize 'force)
(doom-initialize-modules) (doom-initialize-modules)
(require 'core-cli) (doom-cli-reload-core-autoloads 'force)
(doom-reload-core-autoloads 'force) (when (doom-cli-packages-install)
(when (doom-packages-install 'auto-accept) (doom-cli-reload-package-autoloads 'force)))))
(doom-reload-package-autoloads 'force)))))) (unless (zerop status)
(message "%s" (buffer-string)) (error "Failed to bootstrap unit tests"))))
(message "%s" (buffer-string)) (with-temp-buffer
(error "Failed to bootstrap unit tests")))
(dolist (file files) (dolist (file files)
(if (doom-file-cookie-p file "if" t) (if (doom-file-cookie-p file "if" t)
(with-temp-buffer (cl-destructuring-bind (_status . output)
(unless (apply #'doom-exec-process
(zerop
(apply #'call-process
(doom--emacs-binary) (doom--emacs-binary)
nil t nil "--batch" "--batch"
(append (list "-l" (concat doom-core-dir "core.el")
"-L" doom-core-dir "-l" (concat doom-core-dir "test/helpers.el")
"-l" "core" (append (when (file-in-directory-p file doom-modules-dir)
"-l" (concat doom-core-dir "test/helpers.el"))
(when (file-in-directory-p file doom-modules-dir)
(list "-f" "doom-initialize-core")) (list "-f" "doom-initialize-core"))
(list (list "-l" file
"-l" file "-f" "buttercup-run")))
"-f" "buttercup-run")))) (insert (replace-regexp-in-string ansi-color-control-seq-regexp "" output))
(setq error t)) (push file read-files))
(message "%s" (buffer-string)))
(print! (info "Ignoring %s" (relpath file))))) (print! (info "Ignoring %s" (relpath file)))))
(if error (let ((total 0)
(user-error "A test failed") (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))) t)))

View file

@ -1,6 +1,7 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*- ;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
(defcli! (upgrade up) (&rest args) (defcli! (upgrade up)
((force-p ["-f" "--force"]))
"Updates Doom and packages. "Updates Doom and packages.
This requires that ~/.emacs.d is a git repo, and is the equivalent of the 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 git pull --rebase
bin/doom clean bin/doom clean
bin/doom refresh bin/doom refresh
bin/doom update bin/doom update"
:bare t
Switches: (when (doom-cli-upgrade doom-auto-accept force-p)
-t/--timeout TTL Seconds until a thread is timed out (default: 45) (require 'core-packages)
--threads N How many threads to use (default: 8)" (doom-initialize)
(and (doom-upgrade doom-auto-accept (doom-initialize-packages)
(or (member "-f" args) (when (doom-cli-packages-update)
(member "--force" args))) (doom-cli-reload-package-autoloads 'force))))
(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)))
;; ;;
@ -38,13 +31,13 @@ Switches:
(defun doom--working-tree-dirty-p (dir) (defun doom--working-tree-dirty-p (dir)
(cl-destructuring-bind (success . stdout) (cl-destructuring-bind (success . stdout)
(doom-sh "git" "status" "--porcelain" "-uno") (doom-call-process "git" "status" "--porcelain" "-uno")
(if (= 0 success) (if (= 0 success)
(string-match-p "[^ \t\n]" (buffer-string)) (string-match-p "[^ \t\n]" (buffer-string))
(error "Failed to check working tree in %s" dir)))) (error "Failed to check working tree in %s" dir))))
(defun doom-upgrade (&optional auto-accept-p force-p) (defun doom-cli-upgrade (&optional auto-accept-p force-p)
"Upgrade Doom to the latest version non-destructively." "Upgrade Doom to the latest version non-destructively."
(require 'vc-git) (require 'vc-git)
(let ((default-directory doom-emacs-dir) (let ((default-directory doom-emacs-dir)
@ -65,15 +58,15 @@ Switches:
(format "Refusing to upgrade because %S has been modified." (path doom-emacs-dir)) (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.") "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...")) (print! (info "You have local modifications in Doom's source. Discarding them..."))
(doom-sh "git" "reset" "--hard" (format "origin/%s" branch)) (doom-call-process "git" "reset" "--hard" (format "origin/%s" branch))
(doom-sh "git" "clean" "-ffd"))) (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 (unwind-protect
(progn (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)) (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")) (error "Failed to fetch from upstream"))
(let ((this-rev (vc-git--rev-parse "HEAD")) (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" ((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) (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) (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) (when (and (not auto-accept-p)
(y-or-n-p "View the comparison diff in your browser?")) (y-or-n-p "View the comparison diff in your browser?"))
@ -106,15 +99,13 @@ Switches:
(print! (start "Upgrading Doom Emacs...")) (print! (start "Upgrading Doom Emacs..."))
(print-group! (print-group!
(doom-clean-byte-compiled-files) (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)) (equal (vc-git--rev-parse "HEAD") new-rev))
(error "Failed to check out %s" (substring new-rev 0 10))) (error "Failed to check out %s" (substring new-rev 0 10)))
(print! (success "Finished upgrading Doom Emacs"))) (print! (success "Finished upgrading Doom Emacs")))
(doom-delete-autoloads-file doom-autoload-file) (doom-cli-execute "refresh" (if auto-accept-p '("-y")))
(doom-delete-autoloads-file doom-package-autoload-file)
(doom-cli-refresh "-f")
t) t)
(print! (success "Done! Restart Emacs for changes to take effect.")))))) (print! (success "Done! Restart Emacs for changes to take effect."))))))
(ignore-errors (ignore-errors
(doom-sh "git" "remote" "remove" doom-repo-remote)))))) (doom-call-process "git" "remote" "remove" doom-repo-remote))))))

View file

@ -2,155 +2,221 @@
(require 'seq) (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") (defvar doom-auto-accept (getenv "YES")
"If non-nil, Doom will auto-accept any confirmation prompts during batch "If non-nil, Doom will auto-accept any confirmation prompts during batch
commands like `doom-packages-install', `doom-packages-update' and commands like `doom-cli-packages-install', `doom-cli-packages-update' and
`doom-packages-autoremove'.") `doom-packages-autoremove'.")
(defvar doom-cli-pre-execute-hook nil (defvar doom--cli-p nil)
"TODO")
(defvar doom-cli-post-success-execute-hook nil
"TODO")
(defvar doom--cli-commands (make-hash-table :test 'equal)) (defvar doom--cli-commands (make-hash-table :test 'equal))
(defvar doom--cli-groups (make-hash-table :test 'equal)) (defvar doom--cli-groups (make-hash-table :test 'equal))
(defvar doom--cli-group nil) (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!"))))
;; (cl-defstruct doom-cli-option
;;; Dispatcher API (symbol)
(flags ())
(args ())
(desc "TODO"))
(defun doom-sh (command &rest args) (defun doom--cli-get-option (cli flag)
"Execute COMMAND with ARGS in the shell and return (STATUS . OUTPUT). (cl-find-if (doom-partial #'member flag)
(doom-cli-optlist cli)
:key #'doom-cli-option-flags))
STATUS is a boolean" (defun doom--cli-process (cli args)
(let ((output (get-buffer-create "*doom-sh-output*"))) (let* ((args (copy-sequence args))
(unwind-protect (arglist (copy-sequence (doom-cli-arglist cli)))
(cons (or (apply #'call-process command nil output nil args) (expected (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords)
-1) arglist)
(with-current-buffer output (length arglist)))
(string-trim (buffer-string)))) (got 0)
(kill-buffer output)))) 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) ((string-match "^\\(--\\([a-zA-Z0-9][a-zA-Z0-9-_]*\\)\\)\\(?:=\\(.+\\)\\)?$" arg)
(when (symbolp command) (let* ((fullflag (match-string 1 arg))
(setq command (symbol-name command))) (opt (doom--cli-get-option cli fullflag)))
(cl-check-type command string) (unless opt
(intern-soft (user-error "Unrecognized switch %S" (concat "--" (match-string 2 arg))))
(format "doom-cli-%s" (setf (alist-get (doom-cli-option-symbol opt) alist)
(if (gethash command doom--cli-commands) (or (if (doom-cli-option-args opt)
command (or (match-string 3 arg)
(cl-loop for key (pop args)
being the hash-keys in doom--cli-commands (user-error "%S expected an argument, but got none"
for aliases = (plist-get (gethash key doom--cli-commands) :aliases) fullflag))
if (member command aliases) (if (match-string 3 arg)
return key))))) (user-error "%S was not expecting an argument, but got %S"
fullflag (match-string 3 arg))
fullflag))))))
(defun doom--dispatch-format (desc &optional short) ((string-match "^\\(-\\([a-zA-Z0-9]+\\)\\)$" arg)
(with-temp-buffer (let ((fullflag (match-string 1 arg))
(let ((fill-column 72)) (flag (match-string 2 arg)))
(save-excursion (dolist (switch (split-string flag "" t))
(insert desc) (if-let (opt (doom--cli-get-option cli (concat "-" switch)))
(while (re-search-backward "\n\n[^ \n]" nil t) (setf (alist-get (doom-cli-option-symbol opt) alist)
(fill-paragraph)))) (if (doom-cli-option-args opt)
(if (not short) (or (pop args)
(buffer-string) (user-error "%S expected an argument, but got none"
(buffer-substring (line-beginning-position) fullflag))
(line-end-position))))) fullflag))
(user-error "Unrecognized switch %S" (concat "-" switch))))))
(defun doom--dispatch-help-1 (command) (arglist
(cl-destructuring-bind (&key aliases hidden _group) (cl-incf got)
(gethash command doom--cli-commands) (let ((spec (pop arglist)))
(unless hidden (when (eq spec '&optional)
(print! "%-11s\t%s\t%s" (setq spec (pop arglist)))
command (if aliases (string-join aliases ",") "") (setf (alist-get spec alist) arg))
(doom--dispatch-format (when (null arglist)
(documentation (doom--dispatch-command command)) (throw 'done t)))
t)))))
(defun doom--dispatch-help (&optional fn &rest args) (t
"Display help documentation for a dispatcher command. If fn and DESC are (push arg args)
omitted, show all available commands, their aliases and brief descriptions." (throw 'done t))))))
(if fn (when (< got expected)
(princ (documentation fn)) (error "Expected %d arguments, got %d" expected got))
(print! (bold "%-11s\t%s\t%s" "Command:" "Alias" "Description")) (when rest
(print-group! (setf (alist-get restvar alist) rest))
(dolist (group (seq-group-by (lambda (key) (plist-get (gethash key doom--cli-commands) :group)) alist))
(hash-table-keys doom--cli-commands)))
(if (null (car group))
(mapc #'doom--dispatch-help-1 (cdr group))
(print! "%-30s\t%s" (bold (car group)) (gethash (car group) doom--cli-groups))
(print-group!
(mapc #'doom--dispatch-help-1 (cdr group))))
(terpri)))))
(defun doom-dispatch (cmd args &optional show-help) (defun doom-cli-get (command)
"Parses ARGS and invokes a dispatcher. "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." (defun doom-cli-internal-p (cli)
(when (equal cmd "help") "Return non-nil if CLI is an internal (non-public) command."
(setq show-help t) (string-prefix-p ":" (doom-cli-name cli)))
(when args
(setq cmd (car args) (defun doom-cli-execute (command &optional args)
args (cdr args)))) "Execute COMMAND (string) with ARGS (list of strings).
(let ((fn (doom--dispatch-command cmd)))
(unless (fboundp fn) Executes a cli defined with `defcli!' with the name or alias specified by
(user-error "%S is not any command *I* know!" cmd)) COMMAND, and passes ARGS to it."
(if show-help (if-let (cli (doom-cli-get command))
(doom--dispatch-help fn args) (funcall (doom-cli-fn cli)
(let ((start-time (current-time))) (doom--cli-process cli args))
(run-hooks 'doom-cli-pre-execute-hook) (user-error "Couldn't find any %S command" command)))
(unwind-protect
(when-let (ret (apply fn args)) (defmacro defcli! (name speclist &optional docstring &rest body)
(print! "Defines a CLI command.
"\n%s"
(success "Finished! (%.4fs)" COMMAND is a symbol or a list of symbols representing the aliases for this
(float-time command. DOCSTRING is a string description; its first line should be short
(time-subtract (current-time) (under 60 characters), as it will be used as a summary for 'doom help'.
start-time))))
(run-hooks 'doom-cli-post-execute-hook) SPECLIST is a specification for options and arguments, which can be a list
ret) specification for an option/switch in the following format:
(run-hooks 'doom-cli-post-error-execute-hook))))))
(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) (defmacro defcligroup! (name docstring &rest body)
"TODO" "Declare all enclosed cli commands are part of the NAME group."
(declare (indent defun) (doc-string 2)) (declare (indent defun) (doc-string 2))
`(let ((doom--cli-group ,name)) `(let ((doom--cli-group ,name))
(puthash doom--cli-group ,docstring doom--cli-groups) (puthash doom--cli-group ,docstring doom--cli-groups)
,@body)) ,@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 (load! "cli/help")
(defcli! (refresh re) (&rest args) (load! "cli/install")
(defcli! (refresh re)
((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary"))
"Ensure Doom is properly set up. "Ensure Doom is properly set up.
This is the equivalent of running autoremove, install, autoloads, then 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 installed, autoloads files are up-to-date and no byte-compiled files have gone
stale." stale."
(print! (green "Initiating a refresh of Doom Emacs...\n")) (print! (green "Initiating a refresh of Doom Emacs...\n"))
(let ((force-p (or (member "-f" args) (let (success)
(member "--force" args)))
success)
(when (file-exists-p doom-env-file) (when (file-exists-p doom-env-file)
(doom-reload-env-file 'force)) (doom-cli-reload-env-file 'force))
(doom-reload-core-autoloads force-p) (doom-cli-reload-core-autoloads (not if-necessary-p))
(unwind-protect (unwind-protect
(progn (progn
(and (doom-packages-install doom-auto-accept) (and (doom-cli-packages-install)
(setq success t)) (setq success t))
(and (doom-packages-rebuild doom-auto-accept) (and (doom-cli-packages-build)
(setq success t)) (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))) (setq success t)))
(doom-reload-package-autoloads (or success force-p)) (doom-cli-reload-package-autoloads (or success (not if-necessary-p)))
(doom-byte-compile nil 'recompile)) (doom-cli-byte-compile nil 'recompile))
t)) t))
;; Load all of our subcommands
(load! "cli/install")
(defcligroup! "Diagnostics" (defcligroup! "Diagnostics"
"For troubleshooting and diagnostics" "For troubleshooting and diagnostics"
(defcli! (doctor doc) () (load! "cli/doctor")
"Checks for issues with your environment & Doom config.
Use the doctor to diagnose common problems or list missing dependencies in
enabled modules.")
(load! "cli/debug") (load! "cli/debug")
(load! "cli/test")) (load! "cli/test"))
@ -205,8 +260,8 @@ enabled modules.")
(load! "cli/packages") (load! "cli/packages")
(load! "cli/autoloads")) (load! "cli/autoloads"))
(defcligroup! "Byte compilation" (defcligroup! "Compilation"
"For byte-compiling Doom and your config" "For compiling Doom and your config"
(load! "cli/byte-compile")) (load! "cli/byte-compile"))
(defcligroup! "Utilities" (defcligroup! "Utilities"
@ -214,7 +269,7 @@ enabled modules.")
(defcli! run () (defcli! run ()
"Run Doom Emacs from bin/doom's parent directory. "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
doom run -nw init.el doom run -nw init.el

View file

@ -3,41 +3,6 @@
(require 'cl-lib) (require 'cl-lib)
(require 'subr-x) (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 ;;; Helpers

View file

@ -161,15 +161,20 @@ missing) and shouldn't be deleted.")
(push func options) (push func options)
(print! "%2s) %s" (length options) desc))))) (print! "%2s) %s" (length options) desc)))))
(terpri) (terpri)
(let ((answer (let ((options (nreverse options))
(read-number (format! "How to proceed? (%s) " answer fn)
(while
(not
(setq
fn (ignore-errors
(nth (1- (setq answer
(read-number
(format! "How to proceed? (%s) "
(mapconcat #'number-to-string (mapconcat #'number-to-string
(number-sequence 1 (length options)) (number-sequence 1 (length options))
", ")))) ", ")))))
fn) options))))
(setq options (nreverse options)) (print! (warn "%s is not a valid answer, try again.") answer))
(while (not (setq fn (nth (1- answer) options)))
(print! "%s is not a valid answer, try again." answer))
(funcall fn)))))) (funcall fn))))))
@ -249,7 +254,7 @@ necessary package metadata is initialized and available for them."
;;; Module package macros ;;; Module package macros
(cl-defmacro package! (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). "Declares a package and how to install it (if applicable).
This macro is declarative and does not load nor install packages. It is used to This macro is declarative and does not load nor install packages. It is used to
@ -277,6 +282,13 @@ Accepts the following properties:
Returns t if package is successfully registered, and nil if it was disabled Returns t if package is successfully registered, and nil if it was disabled
elsewhere." elsewhere."
(declare (indent defun)) (declare (indent defun))
(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) `(let* ((name ',name)
(plist (cdr (assq name doom-packages)))) (plist (cdr (assq name doom-packages))))
(let ((module-list (plist-get plist :modules)) (let ((module-list (plist-get plist :modules))
@ -287,33 +299,23 @@ elsewhere."
(list module) (list module)
nil)))) nil))))
;; Handle :built-in (doplist! ((prop val) (list ,@plist) plist)
(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)
(unless (null val) (unless (null val)
(plist-put! plist prop 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) (setf (alist-get name doom-packages) plist)
(if (not ,disable) t (if (not (plist-get plist :disable)) t
(doom-log "Disabling package %S" name) (doom-log "Disabling package %S" name)
(cl-pushnew name doom-disabled-packages) (cl-pushnew name doom-disabled-packages)
nil))) nil)))

View file

@ -72,9 +72,9 @@ Emacs.")
;; Disable commands that won't work, as is, and that Doom already provides a ;; Disable commands that won't work, as is, and that Doom already provides a
;; better alternative for. ;; better alternative for.
(put 'projectile-ag 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/ag instead") (put 'projectile-ag 'disabled "Use +{ivy,helm}/project-search instead")
(put 'projectile-ripgrep 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/rg instead") (put 'projectile-ripgrep 'disabled "Use +{ivy,helm}/project-search instead")
(put 'projectile-grep 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/grep 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 ;; 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) (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook)

View file

@ -366,9 +366,7 @@ treat Emacs as a non-application window."
(setq ansi-color-for-comint-mode t) (setq ansi-color-for-comint-mode t)
(use-package! compile (after! compile
:defer t
:config
(setq compilation-always-kill t ; kill compilation process before starting another (setq compilation-always-kill t ; kill compilation process before starting another
compilation-ask-about-save nil ; save all buffers on `compile' compilation-ask-about-save nil ; save all buffers on `compile'
compilation-scroll-output 'first-error) 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)) (add-hook 'compilation-filter-hook #'doom-apply-ansi-color-to-compilation-buffer-h))
(use-package! ediff (after! ediff
:defer t
:config
(setq ediff-diff-options "-w" ; turn off whitespace checking (setq ediff-diff-options "-w" ; turn off whitespace checking
ediff-split-window-function #'split-window-horizontally ediff-split-window-function #'split-window-horizontally
ediff-window-setup-function #'ediff-setup-windows-plain) 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-enable-line-numbers-h () (display-line-numbers-mode +1))
(defun doom-disable-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 ;;; Theme & font

View file

@ -1,13 +1,12 @@
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
(when (version< emacs-version "25.3") (when (version< emacs-version "26.1")
(error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher" (error "Detected Emacs %s. Doom only supports Emacs 26.1 and higher"
emacs-version)) emacs-version))
(defconst doom-version "2.0.9" (defconst doom-version "2.0.9"
"Current version of Doom Emacs.") "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-MAC (eq system-type 'darwin))
(defconst IS-LINUX (eq system-type 'gnu/linux)) (defconst IS-LINUX (eq system-type 'gnu/linux))
@ -29,6 +28,8 @@
;; Load the bare necessities ;; Load the bare necessities
(require 'core-lib) (require 'core-lib)
(autoload 'doom-initialize-packages "core-packages")
;; ;;
;;; Global variables ;;; Global variables
@ -209,6 +210,7 @@ users).")
tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/") tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/")
tramp-backup-directory-alist backup-directory-alist tramp-backup-directory-alist backup-directory-alist
tramp-persistency-file-name (concat doom-cache-dir "tramp-persistency.el") 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-cache-directory (concat doom-cache-dir "url/")
url-configuration-directory (concat doom-etc-dir "url/") url-configuration-directory (concat doom-etc-dir "url/")
gamegrid-user-score-file-directory (concat doom-etc-dir "games/")) 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) (let (command-switch-alist)
(load (substring file 0 -3) 'noerror 'nomessage)) (load (substring file 0 -3) 'noerror 'nomessage))
((debug error) ((debug error)
(if doom-interactive-mode (message "Autoload file error: %s -> %s" (file-name-nondirectory file) e)
(message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) nil)))
(signal 'doom-autoload-error (list (file-name-nondirectory file) e))))))
(defun doom-load-envvars-file (file &optional noerror) (defun doom-load-envvars-file (file &optional noerror)
"Read and set envvars from FILE." "Read and set envvars from FILE."
(if (not (file-readable-p file)) (if (not (file-readable-p file))
(unless noerror (unless noerror
(signal 'file-error (list "Couldn't read envvar file" file))) (signal 'file-error (list "Couldn't read envvar file" file)))
(let (vars) (let (environment)
(with-temp-buffer (with-temp-buffer
(insert-file-contents file)
(while (re-search-forward "\n *\\([^#][^= \n]+\\)=" nil t)
(save-excursion (save-excursion
(let ((var (string-trim-left (match-string 1))) (insert "\n")
(value (buffer-substring-no-properties (insert-file-contents file))
(point) (while (re-search-forward "\n *\\([^#= \n]*\\)=" nil t)
(1- (or (when (re-search-forward "^\\([^= ]+\\)=" nil t) (push (buffer-substring
(line-beginning-position)) (match-beginning 1)
(point-max)))))) (1- (or (save-excursion
(push (cons var value) vars) (when (re-search-forward "^\\([^= ]+\\)=" nil t)
(setenv var value))))) (line-beginning-position)))
(when vars (point-max))))
environment)))
(when environment
(setq-default (setq-default
process-environment (nreverse environment)
exec-path (append (parse-colon-path (getenv "PATH")) exec-path (append (parse-colon-path (getenv "PATH"))
(list exec-directory)) (list exec-directory))
shell-file-name (or (getenv "SHELL") shell-file-name (or (getenv "SHELL")
shell-file-name)) shell-file-name))
(nreverse vars))))) process-environment))))
(defun doom-initialize (&optional force-p) (defun doom-initialize (&optional force-p)
"Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). "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 (let (;; `doom-autoload-file' tells Emacs where to load all its functions
;; from. This includes everything in core/autoload/*.el and autoload ;; from. This includes everything in core/autoload/*.el and autoload
;; files in enabled modules. ;; files in enabled modules.
(core-autoloads-p (core-autoloads-p (doom-load-autoloads-file doom-autoload-file))
(with-demoted-errors "Core autoload error: %s"
(doom-load-autoloads-file doom-autoload-file)))
;; Loads `doom-package-autoload-file', which loads a concatenated ;; Loads `doom-package-autoload-file', which loads a concatenated
;; package autoloads file which caches `load-path', `auto-mode-alist', ;; package autoloads file which caches `load-path', `auto-mode-alist',
;; `Info-directory-list', and `doom-disabled-packages'. A big ;; `Info-directory-list', and `doom-disabled-packages'. A big
;; reduction in startup time. ;; reduction in startup time.
(pkg-autoloads-p (pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file)))
(with-demoted-errors "Package autoload error: %s"
(doom-load-autoloads-file doom-package-autoload-file))))
(if (and core-autoloads-p pkg-autoloads-p (not force-p)) (if (and core-autoloads-p pkg-autoloads-p (not force-p))
;; In case we want to use package.el or straight via M-x ;; In case we want to use package.el or straight via M-x
@ -524,12 +522,6 @@ to least)."
(require 'core-packages) (require 'core-packages)
(doom-initialize-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 ;; Create all our core directories to quell file errors
(dolist (dir (list doom-local-dir (dolist (dir (list doom-local-dir
doom-etc-dir doom-etc-dir

View file

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

View file

@ -9,12 +9,6 @@
(package! all-the-icons) (package! all-the-icons)
(package! hide-mode-line) (package! hide-mode-line)
(package! highlight-numbers) (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! rainbow-delimiters)
(package! restart-emacs) (package! restart-emacs)

View file

@ -13,6 +13,8 @@
;; ;;
;;; Buttercup extensions ;;; Buttercup extensions
(buttercup-define-matcher-for-binary-function :to-equal-file file-equal-p)
(buttercup-define-matcher :to-expand-into (form expected) (buttercup-define-matcher :to-expand-into (form expected)
(cl-destructuring-bind (form expected) (cl-destructuring-bind (form expected)
(mapcar #'funcall (list form expected)) (mapcar #'funcall (list form expected))

View file

@ -19,11 +19,23 @@
(kill-buffer c) (kill-buffer c)
(kill-buffer d)) (kill-buffer d))
(describe "buffer-list" (describe "buffer lists"
(describe "doom-buffer-list"
(it "should only see four buffers" (it "should only see four buffers"
(expect (doom-buffer-list) :to-contain-items (list a b c d)))) (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) :var (projectile-projects-cache-time projectile-projects-cache)
(before-all (require 'projectile)) (before-all (require 'projectile))
(after-all (unload-feature 'projectile t)) (after-all (unload-feature 'projectile t))
@ -47,7 +59,7 @@
(expect (doom-project-buffer-list) (expect (doom-project-buffer-list)
:to-have-same-items-as (buffer-list))))) :to-have-same-items-as (buffer-list)))))
(describe "fallback-buffer" (describe "doom-fallback-buffer"
(it "returns a live buffer" (it "returns a live buffer"
(expect (buffer-live-p (doom-fallback-buffer)))) (expect (buffer-live-p (doom-fallback-buffer))))
@ -56,12 +68,22 @@
(describe "real buffers" (describe "real buffers"
(before-each (before-each
(doom-set-buffer-real a t)
(with-current-buffer b (setq buffer-file-name "x")) (with-current-buffer b (setq buffer-file-name "x"))
(with-current-buffer c (rename-buffer "*C*"))) (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" (it "returns t for buffers manually marked real"
(doom-set-buffer-real a t)
(expect (doom-real-buffer-p a))) (expect (doom-real-buffer-p a)))
(it "returns t for file-visiting buffers" (it "returns t for file-visiting buffers"
(expect (doom-real-buffer-p b))) (expect (doom-real-buffer-p b)))
@ -69,7 +91,16 @@
(expect (doom-real-buffer-p c) :to-be nil) (expect (doom-real-buffer-p c) :to-be nil)
(expect (doom-real-buffer-p d) :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" (it "returns only real buffers"
(expect (doom-real-buffer-list) :to-contain-items (list a b))))) (expect (doom-real-buffer-list) :to-contain-items (list a b)))))
@ -82,25 +113,31 @@
(split-window) (split-window)
(switch-to-buffer b)) (switch-to-buffer b))
(describe "doom-matching-buffers"
(it "can match buffers by regexp" (it "can match buffers by regexp"
(expect (doom-matching-buffers "^[ac]$") :to-have-same-items-as (list a c))) (expect (doom-matching-buffers "^[ac]$") :to-have-same-items-as (list a c))))
(describe "doom-buffers-in-mode"
(it "can match buffers by major-mode" (it "can match buffers by major-mode"
(expect (doom-buffers-in-mode 'text-mode) :to-have-same-items-as (list b c))) (expect (doom-buffers-in-mode 'text-mode) :to-have-same-items-as (list b c))))
(describe "doom-buried-buffers"
(it "can find all buried buffers" (it "can find all buried buffers"
(expect (doom-buried-buffers) :to-contain-items (list c d))) (expect (doom-buried-buffers) :to-contain-items (list c d))))
(describe "doom-visible-buffers"
(it "can find all visible buffers" (it "can find all visible buffers"
(expect (doom-visible-buffers) (expect (doom-visible-buffers)
:to-have-same-items-as (list a b))) :to-have-same-items-as (list a b))))
(describe "doom-visible-windows"
(it "can find all visible windows" (it "can find all visible windows"
(expect (doom-visible-windows) (expect (doom-visible-windows)
:to-have-same-items-as :to-have-same-items-as
(mapcar #'get-buffer-window (list a b))))) (mapcar #'get-buffer-window (list a b))))))
(describe "kill-buffer-and-windows" (describe "killing buffers/windows"
(describe "doom-kill-buffer-and-windows"
(before-each (before-each
(split-window) (switch-to-buffer b) (split-window) (switch-to-buffer b)
(split-window) (switch-to-buffer a)) (split-window) (switch-to-buffer a))
@ -111,7 +148,13 @@
(expect (length (doom-visible-windows)) :to-be 1))) (expect (length (doom-visible-windows)) :to-be 1)))
;; TODO ;; TODO
(xdescribe "kill-all-buffers") (xdescribe "doom-fixup-windows")
(xdescribe "kill-other-buffers") (xdescribe "doom-kill-buffer-fixup-windows")
(xdescribe "kill-matching-buffers") (xdescribe "doom-kill-buffers-fixup-windows"))
(xdescribe "cleanup-session")))
(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"))))

View file

@ -5,11 +5,6 @@
(load! "autoload/files" doom-core-dir) (load! "autoload/files" doom-core-dir)
(xdescribe "doom-glob")
(xdescribe "doom-path")
(xdescribe "doom-dir")
(xdescribe "doom-files-in")
(describe "library" (describe "library"
(describe "file-exists-p!" (describe "file-exists-p!"
(it "is a (quasi) drop-in replacement for `file-exists-p'" (it "is a (quasi) drop-in replacement for `file-exists-p'"
@ -96,7 +91,16 @@
(getfilename)) (getfilename))
"LICENSE") "LICENSE")
doom-emacs-dir) 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" (describe "interactive file operations"
:var (src dest projectile-projects-cache-time projectile-projects-cache) :var (src dest projectile-projects-cache-time projectile-projects-cache)
@ -149,4 +153,12 @@
(expect (file-exists-p existing) :to-be nil)) (expect (file-exists-p existing) :to-be nil))
(it "prompts to delete any existing file" (it "prompts to delete any existing file"
(quiet! (doom/delete-this-file existing)) (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")))

View file

@ -4,6 +4,7 @@
(describe "core/keybinds" (describe "core/keybinds"
(require 'core-keybinds) (require 'core-keybinds)
;; FIXME test against their side effects rather than their implementation
(describe "map!" (describe "map!"
:var (doom--map-evil-p states-alist) :var (doom--map-evil-p states-alist)
(before-each (before-each

View file

@ -69,13 +69,15 @@
(describe "file!" (describe "file!"
(it "returns the executing file" (it "returns the executing file"
(expect (eval-and-compile (file!)) (expect (eval-and-compile (file!))
:to-equal (expand-file-name "test/test-core-lib.el" :to-equal
doom-core-dir)))) (eval-and-compile load-file-name))))
(describe "dir!" (describe "dir!"
(it "returns the executing directory" (it "returns the executing directory"
(expect (eval-and-compile (dir!)) (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!" (describe "pushnew!"
(it "pushes values onto a list symbol, in order" (it "pushes values onto a list symbol, in order"
@ -234,13 +236,19 @@
(it "loads a file relative to the current directory" (it "loads a file relative to the current directory"
(load! "path") (load! "path")
(expect 'load :to-have-been-called) (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" (it "loads a file relative to a specified directory"
(load! "path" doom-etc-dir) (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!" (describe "quiet!"
:var (doom-debug-mode)
(before-each
(setq doom-debug-mode nil))
(it "suppresses output from message" (it "suppresses output from message"
(expect (message "hello world") :to-output "hello world\n") (expect (message "hello world") :to-output "hello world\n")
(expect (message "hello world") :to-output) (expect (message "hello world") :to-output)

View file

@ -1,7 +1,23 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-modules.el ;;; 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!"))

View file

@ -21,14 +21,14 @@
(describe "project-root" (describe "project-root"
(it "should resolve to the project's 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" (it "should return nil if not in a project"
(expect (doom-project-root (expand-file-name "~")) :to-be nil))) (expect (doom-project-root (expand-file-name "~")) :to-be nil)))
(describe "project-expand" (describe "project-expand"
(it "expands to a path relative to the project root" (it "expands to a path relative to the project root"
(expect (doom-project-expand "init.el" doom-core-dir) (expect (doom-project-expand "init.el" doom-core-dir) :to-equal-file
:to-equal (expand-file-name "init.el" (doom-project-root doom-core-dir))))) (expand-file-name "init.el" (doom-project-root doom-core-dir)))))
(describe "project-file-exists-p!" (describe "project-file-exists-p!"
(let ((default-directory doom-core-dir)) (let ((default-directory doom-core-dir))

View file

@ -48,21 +48,13 @@
(spy-on 'doom-load-autoloads-file) (spy-on 'doom-load-autoloads-file)
(spy-on 'warn :and-return-value t)) (spy-on 'warn :and-return-value t))
(it "loads autoloads file" (it "loads autoloads files"
(let ((doom-interactive-mode t)) (ignore-errors (doom-initialize))
(ignore-errors (doom-initialize)))
(expect 'doom-load-autoloads-file (expect 'doom-load-autoloads-file
:to-have-been-called-with doom-autoload-file) :to-have-been-called-with doom-autoload-file)
(expect 'doom-load-autoloads-file (expect 'doom-load-autoloads-file
:to-have-been-called-with doom-package-autoload-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" (it "throws doom-autoload-error in interactive session where autoload files don't exist"
(let ((doom-interactive-mode t) (let ((doom-interactive-mode t)
(doom-autoload-file "doesnotexist") (doom-autoload-file "doesnotexist")
@ -81,20 +73,37 @@
(expect 'require :to-have-been-called-with 'core-editor)))) (expect 'require :to-have-been-called-with 'core-editor))))
(describe "doom-load-autoloads-file" (describe "doom-load-autoloads-file"
:var (doom-autoload-file doom-alt-autoload-file result)
(before-each (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) (doom-load-autoloads-file doom-autoload-file)
(expect 'load :to-have-been-called-with (file-name-sans-extension doom-autoload-file) (expect (caar load-history) :to-equal-file
'noerror 'nomessage))) (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" (describe "doom-load-envvars-file"
:var (envvarfile process-environment) :var (doom-env-file process-environment)
(before-each (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 (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 (after-each
(delete-file doom-env-file)) (delete-file doom-env-file))
@ -106,12 +115,14 @@
(expect (doom-load-envvars-file "/tmp/envvardoesnotexist" 'noerror) (expect (doom-load-envvars-file "/tmp/envvardoesnotexist" 'noerror)
:not :to-throw)) :not :to-throw))
(it "loads a well-formed envvar file" (it "returns the new value for `process-environment'"
(expect (getenv "A") :not :to-be-truthy)
(expect (doom-load-envvars-file doom-env-file) (expect (doom-load-envvars-file doom-env-file)
:to-equal '(("A" . "1") ("B" . "2") ("C" . "3"))) :to-equal '("A=1" "B=2" "C=3")))
(expect (getenv "A") :to-equal "1"))
(it "fails on an invalid envvar file" (it "alters environment variables"
(with-temp-file doom-env-file (insert "A=1\nB=2\nC=3\n")) (dolist (key '("A" "B" "C"))
(expect (doom-load-envvars-file doom-env-file) :to-throw)))) (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"))))

View file

@ -252,6 +252,76 @@ It is integrated into Helpful, in Doom.
:desc "Eval expression" ";" #'eval-expression) :desc "Eval expression" ";" #'eval-expression)
#+END_SRC #+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! *** package!
#+BEGIN_SRC elisp :eval no #+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources ;; To install a package that can be found on ELPA or any of the sources

View file

@ -59,8 +59,8 @@ things you should try first:
+ Make sure your configuration (or Doom Emacs) is *not* byte-compiled. Run ~doom + Make sure your configuration (or Doom Emacs) is *not* byte-compiled. Run ~doom
clean~ to ensure it isn't. *Byte-compilation interferes with debugging!* clean~ to ensure it isn't. *Byte-compilation interferes with debugging!*
+ Run ~bin/doom refresh -f~ to ensure all plugins are installed and autoload + Run ~bin/doom refresh~ to ensure all plugins are installed and autoload files
files generated. generated.
+ Run ~bin/doom doctor~ to diagnose common issues with your system. + 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. + 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 + If you happen to know what module(s) are relevant to your issue, check their

View file

@ -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. second program just to make the first run comfortably.
** How do I use Doom alongside other Emacs configs? ** 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]]. 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 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. Also, Doom's fast yo.
** What is the meaning behind Doom's naming conventions? ** What is the meaning behind Doom's naming conventions?
Doom has a number of naming conventions that it uses in addition to the standard You'll find [[file:contributing.org::*Conventions][an overview of Doom's code conventions]] in the [[file:contributing.org][contributing guide]].
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!~
** How can I contribute to/support Doom? ** How can I contribute to/support Doom?
Take a look at the [[file:contributing.org][Contributing guide]]. 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 Doom will prioritize =~/.config/doom=, if it exists. This directory is referred
to as your ~$DOOMDIR~. 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. 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 Remember to run ~bin/doom refresh~ afterwards, on the command line, to sync your
module list with Doom. 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? ** How do I install a package from ELPA?
Add a ~package!~ declaration to =~/.doom.d/packages.el= for each package you 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. Started]] guide.
** How do I install a package from github/another source? ** How do I install a package from github/another source?
The ~package!~ macro can be passed a MELPA style ~:recipe~, allowing you to The ~package!~ macro can be passed a MELPA style recipe, allowing you to install
install packages from just about anywhere: packages from just about anywhere:
#+BEGIN_SRC elisp #+BEGIN_SRC elisp
(package! evil :recipe (:host github :repo "hlissner/my-evil-fork")) (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. section of the [[file:getting_started.org][Getting Started]] guide.
** How do I change where an existing package is installed from? ** How do I change where an existing package is installed from?
~package!~ declarations in your private config have precedence over modules ~package!~ declarations in your private =packages.el= file have precedence over
(even your own). Simply add a new one for that package with the new recipe. 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]]" 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. section of the [[file:getting_started.org][Getting Started]] guide.
** How do I disable a package completely? ** How do I disable a package completely?
The ~package!~ macro has a ~:disable~ property: With the ~package!~ macro's ~:disable~ property:
#+BEGIN_SRC elisp #+BEGIN_SRC elisp
;;; in DOOMDIR/packages.el ;;; 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 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 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 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 can easily change how Doom uses ~doom-theme~, but I can't (easily) control how
the ~load-theme~ function. you use the ~load-theme~ function.
*** Installing a third party theme *** 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 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 #+END_SRC
** How do I bind my own keys (or change existing ones)? ** 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 The ~map!~ macro is recommended; it is a convenience macro that wraps around
wrapper around ~define-key~, ~global-set-key~, ~local-set-key~ and Emacs' (and evil's) keybinding API, i.e. ~define-key~, ~global-set-key~,
~evil-define-key~. ~local-set-key~ and ~evil-define-key~.
#+BEGIN_SRC emacs-lisp You'll find comprehensive examples of ~map!~'s usage in its documentation (via
;; bind a global key =SPC h f map!= or =C-h f map!= -- also found [[file:api.org][in docs/api]]).
(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 a more comprehensive example of ~map!~'s usage in You'll find a more comprehensive example of ~map!~'s usage in
[[file:../modules/config/default/+evil-bindings.el][config/default/+evil-bindings.el]]. [[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 Doom provides ~M-x doom/reload~ for your convenience, which will run ~doom
refresh~, restart the Doom initialization process, and re-evaluate your 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 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. 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. idempotent.
- Many ~bin/doom~ commands are available as elisp commands with the ~doom//*~ - 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 prefix. e.g. ~doom//refresh~, ~doom//update~, etc. Feel free to use them, but
@ -868,20 +787,20 @@ deprecated. It forwards to ~bin/doom~ anyway.
a shell script incapable of sentience and thus incapable of retaining, much less a shell script incapable of sentience and thus incapable of retaining, much less
divulging, your secrets to others). divulging, your secrets to others).
You can run ~bin/doom help~ to see what it's capable of, but here are the You can run ~bin/doom help~ to see what it's capable of, but here are some
highlights: commands that you may find particularly useful:
+ ~doom doctor~ :: Diagnose common issues in your environment and list missing + ~doom doctor~ :: Diagnose common issues in your environment and list missing
external dependencies for your enabled modules. 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 + ~doom refresh~ :: Ensures that all missing packages are installed, orphaned
packages are removed, and metadata properly generated. 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 + ~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 shell environment for Doom Emacs to load on startup. You need to run this for
changes to your shell environment to take effect. changes to your shell environment to take effect.
+ ~doom purge~ :: Purge orphaned packages (i.e. ones that aren't needed anymore) + ~doom purge -g~ :: Purge orphaned packages (i.e. ones that aren't needed
and regraft your repos. anymore) and regraft your repos.
+ ~doom upgrade~ :: Upgrade Doom to the latest version (then update your + ~doom upgrade~ :: Upgrade Doom to the latest version (then update your
packages). This is equivalent to: packages). This is equivalent to:
@ -894,10 +813,12 @@ highlights:
** When to run ~doom refresh~ ** When to run ~doom refresh~
As a rule of thumb you should run ~doom refresh~ whenever you: 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 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=). + 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 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 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 ** ~void-variable~ and ~void-function~ errors on startup
The most common culprit for these types of errors are: 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 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!~ block in =~/.doom.d/init.el=, or add ~package!~ declarations to
=~/.doom.d/packages.el=. If you modify =~/.emacs.d/.local= by hand, for =~/.doom.d/packages.el=. Or if you modify =~/.emacs.d/.local= by hand, for
whatever reason, run ~doom refresh -f~ to bypass caches and modify-checks. whatever reason.
See ~doom help refresh~ for details on what this command does and when you See ~doom help refresh~ for details on what this command does and when you
should use it. should use it.

View file

@ -59,8 +59,8 @@
- [[#variables-functions-faces-etc][Variables, functions, faces, etc.]] - [[#variables-functions-faces-etc][Variables, functions, faces, etc.]]
- [[#for-doom-modules-packages-autodefs-etc][For Doom Modules, packages, autodefs, 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]] - [[#how-to-extract-a-backtrace-from-an-error][How to extract a backtrace from an error]]
- [[#enabling-debug-on-error][Enabling `debug-on-error`]] - [[#enabling-debug-on-error][Enabling ~debug-on-error~]]
- [[#a-backtrace-from-bindoom][A backtrace from `bin/doom`]] - [[#a-backtrace-from-bindoom][A backtrace from ~bin/doom~]]
- [[#evaluating-elisp-on-the-fly][Evaluating Elisp on-the-fly]] - [[#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]] - [[#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]] - [[#testing-in-dooms-sandbox][Testing in Doom's sandbox]]
@ -187,8 +187,8 @@ cause you issues later on. Do not use them:
+ XEmacs + XEmacs
*** On Windows *** 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 *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. success with installing Doom via WSL, chocolatey on git-bash or cygwin.
#+BEGIN_QUOTE #+BEGIN_QUOTE
If you manage to get Doom on Windows and found this wasn't enough, or could be 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 ** How to extract a backtrace from an error
If you encounter an error while using Doom Emacs, you're probably about to head 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, off and file a bug report (or request help on [[https://discord.gg/bcZ6P3y][our Discord server]]). Before you
please generate a backtrace to include with it. do, please generate a backtrace to include with it.
To do so you must enable ~debug-on-error~ then recreate the error. To do so you must enable ~debug-on-error~ then recreate the error.
*** Enabling `debug-on-error` *** Enabling ~debug-on-error~
There are three ways to enable `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. startup.
2. Evil users can press <kbd>SPC h d d</kbd> and non-evil users can press 2. Evil users can press =SPC h d d= and non-evil users can press =C-h d d=.
=C-h d d=. 3. If the above don't work, there's always: ~M-x toggle-debug-on-error~
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.
*** 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 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 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. when an error occurs. The ~DEBUG~ environment variable will work to.

View file

@ -7,9 +7,10 @@ for your own Emacs configuration or a resource for enthusiasts to learn more
about our favorite OS. about our favorite OS.
#+begin_quote #+begin_quote
Doom's documentation can be viewed from within Emacs by pressing =<help> d h= Github fails to render org links to sub-sections, so it is recommended that you
(=<help>= is =SPC h= for evil users and =C-h= for vanilla users), or searched view the documentation from within Doom Emacs by pressing =<help> d h= (=<help>=
with =<help> d /=. is =SPC h= for evil users and =C-h= for vanilla users) or searching it with
=<help> d /=.
#+end_quote #+end_quote
* Table of Contents :TOC: * Table of Contents :TOC:

View file

@ -48,7 +48,8 @@
;; And let 'er rip! ;; And let 'er rip!
(doom-initialize) (doom-initialize)
(unless noninteractive (if noninteractive
(doom-initialize-packages)
(doom-initialize-core) (doom-initialize-core)
(doom-initialize-modules) (doom-initialize-modules)
(add-hook 'window-setup-hook #'doom-display-benchmark-h) (add-hook 'window-setup-hook #'doom-display-benchmark-h)

View file

@ -143,6 +143,7 @@
;;qt ; the 'cutest' gui framework ever ;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs ;;racket ; a DSL for DSLs
;;rest ; Emacs as a REST client ;;rest ; Emacs as a REST client
;;rst ; ReST in peace
;;ruby ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} ;;ruby ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() ;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good ;;scala ; java, but good
@ -154,28 +155,17 @@
;;web ; the tubes ;;web ; the tubes
:email :email
;;(mu4e +gmail) ; WIP ;;(mu4e +gmail)
;;notmuch ; WIP ;;notmuch
;;(wanderlust +gmail) ; WIP ;;(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 :app
;;calendar ;;calendar
;;irc ; how neckbeards socialize ;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader ;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought ;;twitter ; twitter client https://twitter.com/vnought
;;(write ; emacs for writers (fiction, notes, papers, etc.) ;;write ; emacs for writers (fiction, notes, papers, etc.)
;; +wordnut ; wordnet (wn) search
;; +langtool) ; a proofreader (grammar/style check) for Emacs
:config :config
;; For literate config users. This will tangle+compile a config.org
;; literate config in your `doom-private-dir' whenever it changes.
;;literate ;;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)) (default +bindings +smartparens))

View file

@ -62,7 +62,7 @@
(use-package! company-box (use-package! company-box
:when (and EMACS26+ (featurep! +childframe)) :when (featurep! +childframe)
:hook (company-mode . company-box-mode) :hook (company-mode . company-box-mode)
:config :config
(setq company-box-show-single-candidate t (setq company-box-show-single-candidate t

View file

@ -4,5 +4,5 @@
(package! company) (package! company)
(package! company-dict) (package! company-dict)
(package! company-prescient) (package! company-prescient)
(when (and EMACS26+ (featurep! +childframe)) (when (featurep! +childframe)
(package! company-box)) (package! company-box))

View file

@ -1,46 +1,17 @@
;;; completion/helm/autoload/evil.el -*- lexical-binding: t; -*- ;;; completion/helm/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil) ;;;###if (featurep! :editor evil)
;; ;;;###autoload (autoload '+helm:project-search "completion/helm/autoload/evil" nil t)
;; Project searching (evil-define-command +helm:project-search (all-files-p query)
;;;###autoload (autoload '+helm:grep "completion/helm/autoload/evil" nil t)
(evil-define-command +helm:grep (all-files-p query)
"Ex interface for `+helm/grep'" "Ex interface for `+helm/grep'"
(interactive "<!><a>") (interactive "<!><a>")
(+helm/grep all-files-p query)) (+helm/project-search all-files-p query))
;;;###autoload (autoload '+helm:ag "completion/helm/autoload/evil" nil t) ;;;###autoload (autoload '+helm:project-search-from-cwd "completion/helm/autoload/evil" nil t)
(evil-define-command +helm:ag (all-files-p query) (evil-define-command +helm:project-search-from-cwd (query &optional recurse-p)
"Ex interface for `+helm/ag'"
(interactive "<!><a>")
(+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 "<!><a>")
(+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)
"Ex interface for `+helm/grep-from-cwd'." "Ex interface for `+helm/grep-from-cwd'."
(interactive "<a><!>") (interactive "<a><!>")
(+helm/grep-from-cwd (not recurse-p) query)) (+helm/project-search-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 "<a><!>")
(+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 "<a><!>")
(+helm/rg-from-cwd (not recurse-p) query))
;;;###autoload ;;;###autoload
(defun +helm--set-prompt-display (pos) (defun +helm--set-prompt-display (pos)

View file

@ -1,11 +1,5 @@
;;; completion/helm/autoload/helm.el -*- lexical-binding: t; -*- ;;; 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 ;;;###autoload
(defun +helm/projectile-find-file () (defun +helm/projectile-find-file ()
"Call `helm-find-files' if called from HOME, otherwise "Call `helm-find-files' if called from HOME, otherwise
@ -41,70 +35,11 @@ workspace."
;; ;;
;; Project search ;;; 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)))
;;;###autoload ;;;###autoload
(cl-defun +helm-file-search (engine &key query in all-files (recursive t)) (cl-defun +helm-file-search (&key query in all-files (recursive t))
"Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and "Conduct a file search using ripgrep.
grep. If omitted, ENGINE will default to the first one it detects, in that
order.
:query STRING :query STRING
Determines the initial input to search for. Determines the initial input to search for.
@ -114,6 +49,8 @@ order.
:recursive BOOL :recursive BOOL
Whether or not to search files recursively from the base directory." Whether or not to search files recursively from the base directory."
(declare (indent defun)) (declare (indent defun))
(unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH"))
(require 'helm-ag) (require 'helm-ag)
(helm-ag--init-state) (helm-ag--init-state)
(let* ((project-root (or (doom-project-root) default-directory)) (let* ((project-root (or (doom-project-root) default-directory))
@ -121,13 +58,6 @@ order.
(default-directory directory) (default-directory directory)
(helm-ag--default-directory directory) (helm-ag--default-directory directory)
(helm-ag--default-target (list 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 (query (or query
(when (use-region-p) (when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
@ -135,23 +65,20 @@ order.
(when (> (abs (- end beg)) 1) (when (> (abs (- end beg)) 1)
(rxt-quote-pcre (buffer-substring-no-properties beg end))))) (rxt-quote-pcre (buffer-substring-no-properties beg end)))))
"")) ""))
(prompt (format "[%s %s] " (prompt (format "[rg %s] "
(symbol-name engine)
(cond ((file-equal-p directory project-root) (cond ((file-equal-p directory project-root)
(projectile-project-name)) (projectile-project-name))
((file-equal-p directory default-directory) ((file-equal-p directory default-directory)
"./") "./")
((file-relative-name directory project-root))))) ((file-relative-name directory project-root)))))
(command (command
(pcase engine (list "rg --no-heading --line-number --color never"
(`ag (+helm-ag-search-args all-files recursive)) "-S"
(`rg (+helm-rg-search-args all-files recursive)) (when all-files "-z -uu")
('grep (+helm--grep-search directory query prompt all-files recursive) (unless recursive "--maxdepth 1")))
(cl-return t)))) (helm-ag-base-command (string-join (delq nil command) " ")))
(helm-ag-base-command (string-join command " ")))
;; TODO Define our own sources instead ;; TODO Define our own sources instead
(helm-attrset 'name (format "[%s %s] Searching %s" (helm-attrset 'name (format "[rg %s] Searching %s"
engine
(string-join (delq nil (cdr command)) " ") (string-join (delq nil (cdr command)) " ")
(abbreviate-file-name directory)) (abbreviate-file-name directory))
helm-source-do-ag) helm-source-do-ag)
@ -159,82 +86,39 @@ order.
(cl-letf (((symbol-function 'helm-do-ag--helm) (cl-letf (((symbol-function 'helm-do-ag--helm)
(lambda () (helm :sources '(helm-source-do-ag) (lambda () (helm :sources '(helm-source-do-ag)
:prompt prompt :prompt prompt
:buffer "*helm-ag*" :buffer "*helm-rg*"
:keymap helm-do-ag-map :keymap helm-do-ag-map
:input query :input query
:history 'helm-ag--helm-history)))) :history 'helm-ag--helm-history))))
(helm-do-ag directory)))) (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 ;;;###autoload
(defun +helm/project-search (&optional arg initial-query directory) (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 ARG (universal argument), include all files, even hidden or compressed ones, in
the search." the search."
(interactive "P") (interactive "P")
(funcall (or (+helm--get-command "+helm/%s") (+helm-file-search
#'+helm/grep) :query initial-query
arg :in directory
initial-query :all-files (and (not (null arg))
directory)) (listp arg))))
;;;###autoload ;;;###autoload
(defun +helm/project-search-from-cwd (&optional arg initial-query) (defun +helm/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory. "Performs a project search recursively from the current directory.
Uses the first available search backend from `+helm-project-search-engines'. If If ARG (universal argument), include all files, even hidden or compressed ones."
ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P") (interactive "P")
(funcall (or (+helm--get-command "+helm/%s-from-cwd") (+helm-file-search
#'+helm/grep-from-cwd) :query initial-query
arg
initial-query))
;;;###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 :in default-directory
:all-files (and (not (null arg)) :all-files (and (not (null arg))
(listp 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 ;;;###autoload
active, the last known search is used. (defun +helm/jump-list ()
"TODO"
If ALL-FILES-P, search compressed and hidden files as well." (interactive)
engine))) (error "not implemented yet"))

View file

@ -1,15 +1,5 @@
;;; completion/helm/config.el -*- lexical-binding: t; -*- ;;; 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) ;; Posframe (requires +childframe)
(defvar +helm-posframe-handler #'+helm-poshandler-frame-center-near-bottom-fn (defvar +helm-posframe-handler #'+helm-poshandler-frame-center-near-bottom-fn
"The function that determines the location of the childframe. It should return "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)) (setq helm-default-prompt-display-function #'+helm--set-prompt-display))
:init :init
(when (and EMACS26+ (featurep! +childframe)) (when (featurep! +childframe)
(setq helm-display-function #'+helm-posframe-display-fn)) (setq helm-display-function #'+helm-posframe-display-fn))
(let ((fuzzy (featurep! +fuzzy))) (let ((fuzzy (featurep! +fuzzy)))
@ -135,7 +125,6 @@ be negative.")
:config (helm-flx-mode +1)) :config (helm-flx-mode +1))
;;;###package helm-ag
(after! helm-ag (after! helm-ag
(map! :map helm-ag-edit-map :n "RET" #'compile-goto-error) (map! :map helm-ag-edit-map :n "RET" #'compile-goto-error)
(define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort) (define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort)
@ -150,14 +139,12 @@ be negative.")
(setq helm-bookmark-show-location t) (setq helm-bookmark-show-location t)
;;;###package helm-files
(after! helm-files (after! helm-files
(setq helm-boring-file-regexp-list (setq helm-boring-file-regexp-list
(append (list "\\.projects$" "\\.DS_Store$") (append (list "\\.projects$" "\\.DS_Store$")
helm-boring-file-regexp-list))) helm-boring-file-regexp-list)))
;;;###package helm-locate
(defvar helm-generic-files-map (make-sparse-keymap)) (defvar helm-generic-files-map (make-sparse-keymap))
(after! helm-locate (after! helm-locate
(when (and IS-MAC (when (and IS-MAC
@ -167,7 +154,6 @@ be negative.")
(set-keymap-parent helm-generic-files-map helm-map)) (set-keymap-parent helm-generic-files-map helm-map))
;;;###package helm-org
(use-package! helm-org (use-package! helm-org
:when (featurep! :lang org) :when (featurep! :lang org)
:defer t :defer t
@ -178,7 +164,6 @@ be negative.")
'(org-set-tags . helm-org-completing-read-tags)))) '(org-set-tags . helm-org-completing-read-tags))))
;;;###package helm-projectile
(use-package! helm-projectile (use-package! helm-projectile
:commands (helm-projectile-find-file :commands (helm-projectile-find-file
helm-projectile-recentf helm-projectile-recentf
@ -191,7 +176,6 @@ be negative.")
(set-keymap-parent helm-projectile-find-file-map helm-map)) (set-keymap-parent helm-projectile-find-file-map helm-map))
;;;###package swiper-helm
(after! swiper-helm (after! swiper-helm
(setq swiper-helm-display-function (setq swiper-helm-display-function
(lambda (buf &optional _resume) (pop-to-buffer buf))) (lambda (buf &optional _resume) (pop-to-buffer buf)))

View file

@ -10,7 +10,7 @@
(package! swiper-helm) (package! swiper-helm)
(when (featurep! +fuzzy) (when (featurep! +fuzzy)
(package! helm-flx)) (package! helm-flx))
(when (and EMACS26+ (featurep! +childframe)) (when (featurep! +childframe)
(package! posframe)) (package! posframe))
(when (featurep! :lang org) (when (featurep! :lang org)
(package! helm-org)) (package! helm-org))

View file

@ -17,7 +17,6 @@
- [[#jump-to-file-project-navigation][Jump-to-file project navigation]] - [[#jump-to-file-project-navigation][Jump-to-file project navigation]]
- [[#project-search--replace][Project search & replace]] - [[#project-search--replace][Project search & replace]]
- [[#in-buffer-searching][In-buffer searching]] - [[#in-buffer-searching][In-buffer searching]]
- [[#task-lookup][Task lookup]]
- [[#ivy-integration-for-various-completing-commands][Ivy integration for various completing commands]] - [[#ivy-integration-for-various-completing-commands][Ivy integration for various completing commands]]
- [[#general][General]] - [[#general][General]]
- [[#jump-to-files-buffers-or-projects][Jump to files, buffers or projects)]] - [[#jump-to-files-buffers-or-projects][Jump to files, buffers or projects)]]
@ -29,8 +28,7 @@
* Description * Description
This module provides Ivy integration for a variety of Emacs commands, as well as 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, a unified interface for project search and replace, powered by ripgrep.
git-grep & grep (whichever is available).
#+begin_quote #+begin_quote
I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's 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. + =+fuzzy= Enables fuzzy completion for Ivy searches.
+ =+prescient= Enables prescient filtering and sorting for Ivy searches. + =+prescient= Enables prescient filtering and sorting for Ivy searches.
+ =+childframe= Causes Ivy to display in a floating child frame, above Emacs. + =+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 + =+icons= Enables file icons for switch-{buffer,project}/find-file counsel
commands. commands.
@ -67,32 +64,24 @@ lighter, simpler and faster in many cases.
command) command)
* Prerequisites * Prerequisites
This module optionally depends on one of: This module depends on:
+ [[https://github.com/BurntSushi/ripgrep][ripgrep]] (rg) + [[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 ** Install
*** MacOS *** MacOS
#+BEGIN_SRC sh #+BEGIN_SRC sh
brew install ripgrep the_silver_searcher brew install ripgrep
#+END_SRC #+END_SRC
*** Arch Linux *** Arch Linux
#+BEGIN_SRC sh :dir /sudo:: #+BEGIN_SRC sh :dir /sudo::
sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher sudo pacman --needed --noconfirm -S ripgrep
#+END_SRC #+END_SRC
*** openSUSE *** openSUSE
#+BEGIN_SRC sh :dir /sudo:: #+BEGIN_SRC sh :dir /sudo::
sudo zypper install ripgrep the_silver_searcher sudo zypper install ripgrep
#+END_SRC #+END_SRC
* Features * Features
@ -108,35 +97,21 @@ https://assets.doomemacs.org/completion/ivy/projectile.png
| Keybind | Description | | Keybind | Description |
|----------------------+-------------------------------------| |----------------------+-------------------------------------|
| =SPC f /=, =SPC SPC= | Jump to file in project | | =SPC p f=, =SPC SPC= | Jump to file in project |
| =SPC f .=, =SPC .= | Jump to file from current directory | | =SPC f f=, =SPC .= | Jump to file from current directory |
** Project search & replace ** Project search & replace
This module provides interactive text search and replace using the first search This module provides interactive text search and replace using ripgrep.
program available on your system (rg, ag, git-grep or grep).
| Keybind | Description | | Keybind | Description |
|-----------+---------------------------------| |-----------+---------------------------------|
| =SPC / b= | Search the current buffer | | =SPC s b= | Search the current buffer |
| =SPC / p= | Search project | | =SPC s p= | Search project |
| =SPC / d= | Search this directory | | =SPC s d= | Search this directory |
| =SPC p t= | List all TODO/FIXMEs in project | | =SPC p t= | List all TODO/FIXMEs in project |
https://assets.doomemacs.org/completion/ivy/search.png 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 The universal argument (=SPC u= for evil users; =C-u= otherwise) changes the
behavior of these commands, instructing the underlying search engine to include behavior of these commands, instructing the underlying search engine to include
ignored files. ignored files.
@ -144,21 +119,16 @@ ignored files.
This module also provides Ex Commands for evil users: This module also provides Ex Commands for evil users:
| Ex command | Description | | Ex command | Description |
|-----------------------+------------------------------------------------| |------------------------+------------------------------------------------------------------|
| ~:ag[!] [QUERY]~ | Search project w/ ag[fn:1] | | ~:pg[rep][!] [QUERY]~ | Search project (if ~!~, include hidden files) |
| ~:rg[!] [QUERY]~ | Search project w/ rg[fn:1] | | ~:pg[rep]d[!] [QUERY]~ | Search from current directory (if ~!~, don't search recursively) |
| ~: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 |
The optional BANG functions is equivalent to the universal argument for the The optional BANG functions is equivalent to the universal argument for the
previous commands. previous commands.
----- -----
While in a search (e.g. invoked from ~+ivy:ag~ or ~:rg~), these extra While in a search these extra keybindings are available to you:
keybindings are available to you:
| Keybind | Description | | 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 The =swiper= package provides an interactive buffer search powered by ivy. It
can be invoked with: can be invoked with:
+ =SPC / b= + =SPC s s=
+ =SPC s S= (uses thing at point as initial input)
+ ~:sw[iper] [QUERY]~ + ~:sw[iper] [QUERY]~
https://assets.doomemacs.org/completion/ivy/swiper.png https://assets.doomemacs.org/completion/ivy/swiper.png
A wgrep buffer can be opened from swiper with =C-c C-e=. 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 ** Ivy integration for various completing commands
*** General *** General
| Keybind | Description | | Keybind | Description |
@ -201,10 +163,10 @@ https://assets.doomemacs.org/completion/ivy/todo.png
*** Jump to files, buffers or projects) *** Jump to files, buffers or projects)
| Keybind | Description | | Keybind | Description |
|---------------------------------+---------------------------------------| |----------------------+---------------------------------------|
| =SPC RET= | Find bookmark | | =SPC RET= | Find bookmark |
| =SPC f .=, =SPC .= | Browse from current directory | | =SPC f f=, =SPC .= | Browse from current directory |
| =SPC f /=, =SPC p /=, =SPC SPC= | Find file in project | | =SPC p f=, =SPC SPC= | Find file in project |
| =SPC f r= | Find recently opened file | | =SPC f r= | Find recently opened file |
| =SPC p p= | Open another project | | =SPC p p= | Open another project |
| =SPC b b=, =SPC ,= | Switch to buffer in current workspace | | =SPC b b=, =SPC ,= | Switch to buffer in current workspace |
@ -212,13 +174,15 @@ https://assets.doomemacs.org/completion/ivy/todo.png
*** Search *** Search
| Keybind | Description | | 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 | | =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 * Configuration
** TODO Enable fuzzy/non-fuzzy search for specific commands ** TODO Enable fuzzy/non-fuzzy search for specific commands

View file

@ -1,55 +1,14 @@
;; completion/ivy/autoload/evil.el -*- lexical-binding: t; -*- ;; completion/ivy/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil) ;;;###if (featurep! :editor evil)
;;;###autoload (autoload '+ivy:swiper "completion/ivy/autoload/evil" nil t) ;;;###autoload (autoload '+ivy:project-search "completion/ivy/autoload/evil" nil t)
(evil-define-command +ivy:swiper (&optional search) (evil-define-command +ivy:project-search (query &optional all-files-p)
"Invoke `swiper' with SEARCH, otherwise with the symbol at point." "Ex interface for `+ivy/project-search'."
(interactive "<a>")
(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 "<!><a>")
(+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 "<!><a>")
(+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 "<!><a>")
(+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'."
(interactive "<a><!>") (interactive "<a><!>")
(+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) ;;;###autoload (autoload '+ivy:project-search-from-cwd "completion/ivy/autoload/evil" nil t)
(evil-define-command +ivy:ag-from-cwd (query &optional recurse-p) (evil-define-command +ivy:project-search-from-cwd (query &optional recurse-p)
"Ex interface for `+ivy/ag-from-cwd'." "Ex interface for `+ivy/project-search-from-cwd'."
(interactive "<a><!>") (interactive "<a><!>")
(+ivy/ag-from-cwd (not recurse-p) query)) (+ivy/project-search-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 "<a><!>")
(+ivy/rg-from-cwd (not recurse-p) query))

View file

@ -157,88 +157,6 @@ If ARG (universal argument), open selection in other-window."
(interactive) (interactive)
(+ivy--switch-buffer nil t)) (+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 ;;;###autoload
(defun +ivy/woccur () (defun +ivy/woccur ()
"Invoke a wgrep buffer on the current ivy results, if supported." "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 ;;;###autoload
(defun +ivy/projectile-find-file () (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)))) (#'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 ;;;###autoload
(cl-defun +ivy-file-search (engine &key query in all-files (recursive t)) (cl-defun +ivy-file-search (&key query in all-files (recursive t))
"Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and "Conduct a file search using ripgrep.
grep. If omitted, ENGINE will default to the first one it detects, in that
order.
:query STRING :query STRING
Determines the initial input to search for. Determines the initial input to search for.
@ -346,18 +248,16 @@ order.
:recursive BOOL :recursive BOOL
Whether or not to search files recursively from the base directory." Whether or not to search files recursively from the base directory."
(declare (indent defun)) (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)) (directory (or in project-root))
(default-directory directory) (default-directory directory)
(engine (or engine (args (concat (if all-files " -uu")
(cl-loop for tool in +ivy-project-search-engines (unless recursive " --maxdepth 1"))))
if (executable-find (symbol-name tool)) (counsel-rg
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) (or (if query query)
(when (use-region-p) (when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
@ -369,108 +269,35 @@ order.
(cond ((and (string= substr " ") (cond ((and (string= substr " ")
(not (featurep! +fuzzy))) (not (featurep! +fuzzy)))
" ") " ")
((and (string= substr "|") ((string= substr "|")
(eq engine 'rg))
"\\\\\\\\|") "\\\\\\\\|")
((concat "\\\\" substr)))) ((concat "\\\\" substr))))
(rxt-quote-pcre query)))))))) (rxt-quote-pcre query)))))))
(prompt directory args
(format "%s%%s %s" (format "rg%s %s"
(symbol-name engine) args
(cond ((equal directory default-directory) (cond ((equal directory default-directory)
"./") "./")
((equal directory project-root) ((equal directory project-root)
(projectile-project-name)) (projectile-project-name))
((file-relative-name directory project-root)))))) ((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))))
;;;###autoload ;;;###autoload
(defun +ivy/project-search (&optional arg initial-query directory) (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 If ARG (universal argument), include all files, even hidden or compressed ones,
ARG (universal argument), include all files, even hidden or compressed ones, in in the search."
the search."
(interactive "P") (interactive "P")
(funcall (or (+ivy--get-command "+ivy/%s") (+ivy-file-search :query initial-query :in directory :all-files arg))
#'+ivy/grep)
arg
initial-query
directory))
;;;###autoload ;;;###autoload
(defun +ivy/project-search-from-cwd (&optional arg initial-query) (defun +ivy/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory. "Performs a project search recursively from the current directory.
Uses the first available search backend from `+ivy-project-search-engines'. If If ARG (universal argument), include all files, even hidden or compressed ones."
ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P") (interactive "P")
(funcall (or (+ivy--get-command "+ivy/%s-from-cwd") (+ivy/project-search arg initial-query default-directory))
#'+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)))
;; ;;
@ -513,7 +340,7 @@ If ALL-FILES-P, search compressed and hidden files as well."
(cons (format "%s:%d: %s" (cons (format "%s:%d: %s"
(buffer-name) (buffer-name)
(line-number-at-pos) (line-number-at-pos)
(string-trim-right (thing-at-point 'line))) (string-trim-right (or (thing-at-point 'line) "")))
(point-marker))))))) (point-marker)))))))
(cddr (better-jumper-jump-list-struct-ring (cddr (better-jumper-jump-list-struct-ring
(better-jumper-get-jumps (better-jumper--get-current-context)))))))) (better-jumper-get-jumps (better-jumper--get-current-context))))))))

View file

@ -7,16 +7,6 @@ When nil, don't preview anything.
When non-nil, preview non-virtual buffers. When non-nil, preview non-virtual buffers.
When 'everything, also preview 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) (defvar +ivy-project-search-engines '(rg ag)
"What search tools for `+ivy/project-search' (and `+ivy-file-search' when no "What search tools for `+ivy/project-search' (and `+ivy-file-search' when no
ENGINE is specified) to try, and in what order. 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 persp-switch-to-buffer] #'+ivy/switch-workspace-buffer
[remap evil-show-jumps] #'+ivy/jump-list) [remap evil-show-jumps] #'+ivy/jump-list)
:config :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 (setq ivy-height 17
ivy-wrap t ivy-wrap t
ivy-fixed-height-minibuffer 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 (use-package! counsel
:commands counsel-describe-face :defer t
:init :init
(define-key! (define-key!
[remap apropos] #'counsel-apropos [remap apropos] #'counsel-apropos
@ -324,7 +319,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! ivy-posframe (use-package! ivy-posframe
:when (and EMACS26+ (featurep! +childframe)) :when (featurep! +childframe)
:hook (ivy-mode . ivy-posframe-mode) :hook (ivy-mode . ivy-posframe-mode)
:config :config
(setq ivy-fixed-height-minibuffer nil (setq ivy-fixed-height-minibuffer nil

View file

@ -1,5 +1,3 @@
;; -*- lexical-binding: t; no-byte-compile: t; -*- ;; -*- lexical-binding: t; no-byte-compile: t; -*-
;;; completion/ivy/doctor.el ;;; completion/ivy/doctor.el
(when (and (not EMACS26+) (featurep! +childframe))
(error! "The +childframe feature requires Emacs 26+"))

View file

@ -15,7 +15,7 @@
(when (featurep! +fuzzy) (when (featurep! +fuzzy)
(package! flx))) (package! flx)))
(when (and EMACS26+ (featurep! +childframe)) (when (featurep! +childframe)
(package! ivy-posframe)) (package! ivy-posframe))
(when (featurep! +icons) (when (featurep! +icons)

View file

@ -82,7 +82,7 @@
(:prefix ("p" . "project") (:prefix ("p" . "project")
:desc "Find file in other project" "F" #'doom/find-file-in-other-project :desc "Find file in other project" "F" #'doom/find-file-in-other-project
:desc "Search project" "s" #'+default/search-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 "Open project scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer :desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
;; later expanded by projectile ;; later expanded by projectile

View file

@ -294,24 +294,6 @@
:desc "Find file in project" "SPC" #'projectile-find-file :desc "Find file in project" "SPC" #'projectile-find-file
:desc "Jump to bookmark" "RET" #'bookmark-jump :desc "Jump to bookmark" "RET" #'bookmark-jump
;;; <leader> / --- 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)
;;; <leader> TAB --- workspace ;;; <leader> TAB --- workspace
(:when (featurep! :ui workspaces) (:when (featurep! :ui workspaces)
(:prefix-map ("TAB" . "workspace") (:prefix-map ("TAB" . "workspace")
@ -381,25 +363,20 @@
:desc "Send to repl" "s" #'+eval/send-region-to-repl :desc "Send to repl" "s" #'+eval/send-region-to-repl
:desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace
:desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines
:desc "List errors" "x" #'flymake-show-diagnostics-buffer
(:when (featurep! :tools flycheck) (:when (featurep! :tools flycheck)
:desc "List errors" "x" #'flycheck-list-errors) :desc "List errors" "x" #'flycheck-list-errors))
(:unless (featurep! :tools flycheck)
:desc "List errors" "x" #'flymake-show-diagnostics-buffer))
;;; <leader> f --- file ;;; <leader> f --- file
(:prefix-map ("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 "Open project editorconfig" "c" #'editorconfig-find-current-editorconfig
:desc "Copy this file" "C" #'doom/copy-this-file :desc "Copy this file" "C" #'doom/copy-this-file
:desc "Find directory" "d" #'dired :desc "Find directory" "d" #'dired
:desc "Delete this file" "D" #'doom/delete-this-file :desc "Delete this file" "D" #'doom/delete-this-file
:desc "Find file in emacs.d" "e" #'+default/find-in-emacsd :desc "Find file in emacs.d" "e" #'+default/find-in-emacsd
:desc "Browse emacs.d" "E" #'+default/browse-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 "Locate file" "l" #'locate
:desc "Move/rename file" "m" #'doom/move-this-file :desc "Move/rename file" "m" #'doom/move-this-file
:desc "Find file in private config" "p" #'doom/find-file-in-private-config :desc "Find file in private config" "p" #'doom/find-file-in-private-config
@ -468,15 +445,16 @@
;;; <leader> i --- insert ;;; <leader> i --- insert
(:prefix-map ("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 "From evil register" "r" #'evil-ex-registers
:desc "Snippet" "s" #'yas-insert-snippet :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)
;;; <leader> n --- notes ;;; <leader> n --- notes
(:prefix-map ("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 "Search notes for symbol" "*" #'+default/search-notes-for-symbol-at-point
:desc "Org agenda" "a" #'org-agenda :desc "Org agenda" "a" #'org-agenda
:desc "Org capture" "c" #'org-capture :desc "Org capture" "c" #'org-capture
@ -487,6 +465,7 @@
:desc "Find file in notes" "n" #'+default/find-in-notes :desc "Find file in notes" "n" #'+default/find-in-notes
:desc "Browse notes" "N" #'+default/browse-notes :desc "Browse notes" "N" #'+default/browse-notes
:desc "Todo list" "t" #'org-todo-list :desc "Todo list" "t" #'org-todo-list
:desc "Search notes" "s" #'+default/org-notes-search
:desc "View search" "v" #'org-search-view :desc "View search" "v" #'org-search-view
:desc "Org export to clipboard" "y" #'+org/export-to-clipboard :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 :desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text
@ -542,8 +521,6 @@
(:prefix-map ("p" . "project") (:prefix-map ("p" . "project")
:desc "Browse project" "." #'+default/browse-project :desc "Browse project" "." #'+default/browse-project
:desc "Browse other project" ">" #'doom/browse-in-other-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 "Run cmd in project root" "!" #'projectile-run-shell-command-in-root
:desc "Add new project" "a" #'projectile-add-known-project :desc "Add new project" "a" #'projectile-add-known-project
:desc "Switch to project buffer" "b" #'projectile-switch-to-buffer :desc "Switch to project buffer" "b" #'projectile-switch-to-buffer
@ -552,7 +529,7 @@
:desc "Remove known project" "d" #'projectile-remove-known-project :desc "Remove known project" "d" #'projectile-remove-known-project
:desc "Edit project .dir-locals" "e" #'projectile-edit-dir-locals :desc "Edit project .dir-locals" "e" #'projectile-edit-dir-locals
:desc "Find file in project" "f" #'projectile-find-file :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 "Configure project" "g" #'projectile-configure-project
:desc "Invalidate project cache" "i" #'projectile-invalidate-cache :desc "Invalidate project cache" "i" #'projectile-invalidate-cache
:desc "Kill project buffers" "k" #'projectile-kill-buffers :desc "Kill project buffers" "k" #'projectile-kill-buffers
@ -563,7 +540,7 @@
:desc "Save project files" "s" #'projectile-save-project-buffers :desc "Save project files" "s" #'projectile-save-project-buffers
:desc "Pop up scratch buffer" "x" #'doom/open-project-scratch-buffer :desc "Pop up scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to scratch buffer" "X" #'doom/switch-to-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) :desc "Test project" "T" #'projectile-test-project)
;;; <leader> q --- quit/session ;;; <leader> q --- quit/session
@ -591,27 +568,31 @@
:desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler :desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler
:desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler)) :desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler))
;;; <leader> s --- snippets ;;; <leader> s --- search
(:when (featurep! :editor snippets) (:prefix-map ("s" . "search")
(:prefix-map ("s" . "snippets") :desc "Search buffer" "b" #'swiper
:desc "View snippet for mode" "/" #'+snippets/find-for-current-mode :desc "Search current directory" "d" #'+default/search-cwd
:desc "View snippet (global)" "?" #'+snippets/find :desc "Search other directory" "D" #'+default/search-other-cwd
:desc "Edit snippet" "c" #'+snippets/edit :desc "Locate file" "f" #'locate
:desc "View private snippet" "f" #'+snippets/find-private :desc "Jump to symbol" "i" #'imenu
:desc "Insert snippet" "i" #'yas-insert-snippet :desc "Jump to link" "l" #'ace-link
:desc "New snippet" "n" #'+snippets/new :desc "Jump list" "j" #'evil-show-jumps
:desc "New snippet alias" "N" #'+snippets/new-alias :desc "Jump to mark" "m" #'evil-show-marks
:desc "Reload snippets" "r" #'yas-reload-all :desc "Look up online" "o" #'+lookup/online
:desc "Create temporary snippet" "s" #'aya-create :desc "Look up online (w/ prompt)" "O" #'+lookup/online-select
:desc "Expand temporary snippet" "e" #'aya-expand)) :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)
;;; <leader> t --- toggle ;;; <leader> t --- toggle
(:prefix-map ("t" . "toggle") (:prefix-map ("t" . "toggle")
:desc "Big mode" "b" #'doom-big-font-mode :desc "Big mode" "b" #'doom-big-font-mode
:desc "Flymake" "f" #'flymake-mode
(:when (featurep! :tools flycheck) (:when (featurep! :tools flycheck)
:desc "Flycheck" "f" #'flycheck-mode) :desc "Flycheck" "f" #'flycheck-mode)
(:unless (featurep! :tools flycheck)
:desc "Flymake" "f" #'flymake-mode)
:desc "Frame fullscreen" "F" #'toggle-frame-fullscreen :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
:desc "Evil goggles" "g" #'evil-goggles-mode :desc "Evil goggles" "g" #'evil-goggles-mode
(:when (featurep! :ui indent-guides) (:when (featurep! :ui indent-guides)

View file

@ -73,15 +73,6 @@ If ARG (universal argument), runs `compile' from the current directory."
(with-current-buffer buffer (with-current-buffer buffer
(funcall (default-value 'major-mode)))))) (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 ;;;###autoload
(defun +default/newline-above () (defun +default/newline-above ()
"Insert an indented new line before the current one." "Insert an indented new line before the current one."
@ -120,7 +111,7 @@ languages)."
(interactive) (interactive)
(if (and (sp-point-in-comment) (if (and (sp-point-in-comment)
comment-line-break-function) comment-line-break-function)
(funcall comment-line-break-function) (funcall comment-line-break-function nil)
(delete-horizontal-space t) (delete-horizontal-space t)
(newline nil t) (newline nil t)
(indent-according-to-mode))) (indent-according-to-mode)))
@ -333,3 +324,22 @@ ARG is set, prompt for a known project to search from."
(while (server-running-p) (while (server-running-p)
(sit-for 1)) (sit-for 1))
(server-start)) (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)))))

View file

@ -18,7 +18,7 @@ byte-compiled from.")
+literate-config-cache-file) +literate-config-cache-file)
force-p) force-p)
(message "Compiling your literate config...") (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")) (dest (concat (file-name-sans-extension +literate-config-file) ".el"))
(output (get-buffer-create "*org-tangle*"))) (output (get-buffer-create "*org-tangle*")))
(unwind-protect (unwind-protect

View file

@ -4,6 +4,7 @@
;;; Custom commands ;;; Custom commands
;; Editing ;; Editing
(evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me (evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me
(evil-ex-define-cmd "R[ead]" #'+evil:read)
(evil-ex-define-cmd "al[ign]" #'+evil:align) (evil-ex-define-cmd "al[ign]" #'+evil:align)
(evil-ex-define-cmd "ral[ign]" #'+evil:align-right) (evil-ex-define-cmd "ral[ign]" #'+evil:align-right)
(evil-ex-define-cmd "enhtml" #'+web:encode-html-entities) (evil-ex-define-cmd "enhtml" #'+web:encode-html-entities)
@ -56,25 +57,19 @@
(evil-ex-define-cmd "cd" #'+evil:cd) (evil-ex-define-cmd "cd" #'+evil:cd)
(evil-ex-define-cmd "pwd" #'+evil:pwd) (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 "<a>")
(swiper-isearch search))
(evil-ex-define-cmd "sw[iper]" #'+evil:swiper)
(cond ((featurep! :completion ivy) (cond ((featurep! :completion ivy)
(evil-ex-define-cmd "ag" #'+ivy:ag) (evil-ex-define-cmd "pg[rep]" #'+ivy:project-search)
(evil-ex-define-cmd "agc[wd]" #'+ivy:ag-from-cwd) (evil-ex-define-cmd "pg[grep]d" #'+ivy:project-search-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))
((featurep! :completion helm) ((featurep! :completion helm)
(evil-ex-define-cmd "ag" #'+helm:ag) (evil-ex-define-cmd "pg[rep]" #'+helm:project-search)
(evil-ex-define-cmd "agc[wd]" #'+helm:ag-from-cwd) (evil-ex-define-cmd "pg[grep]d" #'+helm:project-search-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'
))
;;; Project tools ;;; Project tools
(evil-ex-define-cmd "compile" #'+evil:compile) (evil-ex-define-cmd "compile" #'+evil:compile)

View file

@ -220,7 +220,7 @@ and complains if a module is loaded too early (during startup)."
(add-transient-hook! 'emacs-lisp-mode (add-transient-hook! 'emacs-lisp-mode
(+evil-collection-init 'elisp-mode)) (+evil-collection-init 'elisp-mode))
(add-transient-hook! 'occur-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 (evil-define-key* 'normal process-menu-mode-map
"q" #'kill-current-buffer "q" #'kill-current-buffer

View file

@ -80,7 +80,8 @@ more information on modifiers."
(when (and (not (string= path "")) (equal (substring path -1) "/")) (when (and (not (string= path "")) (equal (substring path -1) "/"))
(setq path (substring path 0 -1)))) (setq path (substring path 0 -1))))
(setq file-name (setq file-name
(replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" (replace-regexp-in-string
(format "\\(?:^\\|[^\\\\]\\)\\(%s\\)"
(regexp-quote (string-trim-left (car match)))) (regexp-quote (string-trim-left (car match))))
path file-name t t 1)))) path file-name t t 1))))
(replace-regexp-in-string regexp "\\1" file-name t))) (replace-regexp-in-string regexp "\\1" file-name t)))
@ -99,7 +100,7 @@ more information on modifiers."
;; FIXME oh god why ;; FIXME oh god why
(save-excursion (save-excursion
(if comment-line-break-function (if comment-line-break-function
(funcall comment-line-break-function) (funcall comment-line-break-function nil)
(comment-indent-new-line)) (comment-indent-new-line))
(when (and (derived-mode-p 'c-mode 'c++-mode 'objc-mode 'java-mode 'js2-mode) (when (and (derived-mode-p 'c-mode 'c++-mode 'objc-mode 'java-mode 'js2-mode)
(eq (char-after) ?/)) (eq (char-after) ?/))

View file

@ -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)))) (evil-ex-completed-binding (match-string 1 query))))
((message "Searching for %S, this may take a while..." query) ((message "Searching for %S, this may take a while..." query)
(apropos query t)))))) (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<fsh>")
(evil-read count (evil-ex-replace-special-filenames file)))

View file

@ -479,7 +479,20 @@ To change these keys see `+evil-repeat-keys'."
(:when (featurep! :tools eval) (:when (featurep! :tools eval)
:nv "gr" #'+eval:region :nv "gr" #'+eval:region
:n "gR" #'+eval/buffer :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 :nv "z=" #'flyspell-correct-word-generic
;; custom evil keybinds ;; custom evil keybinds

View file

@ -6,7 +6,7 @@
(setq ibuffer-show-empty-filter-groups nil (setq ibuffer-show-empty-filter-groups nil
ibuffer-filter-group-name-face '(:inherit (success bold)) ibuffer-filter-group-name-face '(:inherit (success bold))
ibuffer-formats ibuffer-formats
`((mark modified read-only ,(if EMACS26+ 'locked "") `((mark modified read-only locked
,@(if (featurep! +icons) ,@(if (featurep! +icons)
`(;; Here you may adjust by replacing :right with :center `(;; Here you may adjust by replacing :right with :center
;; or :left According to taste, if you want the icon ;; or :left According to taste, if you want the icon

View file

@ -66,14 +66,3 @@ otherwise in default state."
(when (and (bound-and-true-p evil-mode) (when (and (bound-and-true-p evil-mode)
(bobp) (eolp)) (bobp) (eolp))
(evil-insert-state))))) (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))))

View file

@ -100,7 +100,7 @@
(interactive) (interactive)
(let* ((msg-path (car (plist-get (notmuch-tree-get-message-properties) :filename))) (let* ((msg-path (car (plist-get (notmuch-tree-get-message-properties) :filename)))
(temp (make-temp-file "notmuch-message-" nil ".eml"))) (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)))) (start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
;;;###autoload ;;;###autoload
@ -108,7 +108,7 @@
(interactive) (interactive)
(let* ((msg-path (car (plist-get (notmuch-show-get-message-properties) :filename))) (let* ((msg-path (car (plist-get (notmuch-show-get-message-properties) :filename)))
(temp (make-temp-file "notmuch-message-" nil ".eml"))) (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)))) (start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))

View file

@ -160,6 +160,10 @@ This is ignored by ccls.")
;; ;;
;; Major modes ;; Major modes
(use-package! cmake-mode
:defer t
:config (set-docsets! 'cmake-mode "CMake"))
(use-package! company-cmake ; for `cmake-mode' (use-package! company-cmake ; for `cmake-mode'
:when (featurep! :completion company) :when (featurep! :completion company)
:after cmake-mode :after cmake-mode

View file

@ -9,7 +9,8 @@
;; tries to load `proof-site'. We prevent this by defining these two variables ;; tries to load `proof-site'. We prevent this by defining these two variables
;; early, in our own autoloads file. ;; early, in our own autoloads file.
(setq pg-init--script-full-path (locate-library "proof-general") (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 ;;;###package coq
@ -73,14 +74,15 @@
:references #'company-coq-grep-symbol :references #'company-coq-grep-symbol
:documentation #'company-coq-doc) :documentation #'company-coq-doc)
(if (not (featurep! :completion company)) (setq company-coq-disabled-features '(hello company-defaults))
(setq company-coq-disabled-features '(company 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 ;; `company-coq''s company defaults impose idle-completion on folks, so
;; we'll set up company ourselves. ;; we'll set up company ourselves.
(add-to-list 'company-coq-disabled-features 'company-defaults)
;; See https://github.com/cpitclaudel/company-coq/issues/42 ;; See https://github.com/cpitclaudel/company-coq/issues/42
(map! :map coq-mode-map [remap company-complete-common] (add-to-list 'company-coq-disabled-features 'company))
#'company-indent-or-complete-common))
(map! :map coq-mode-map (map! :map coq-mode-map
:localleader :localleader

View file

@ -21,7 +21,7 @@
:commands omnisharp-install-server :commands omnisharp-install-server
:preface :preface
(setq omnisharp-auto-complete-want-documentation nil (setq omnisharp-auto-complete-want-documentation nil
omnisharp-cache-directory (concat doom-cache-dir "omnisharp")) omnisharp-cache-directory (concat doom-etc-dir "omnisharp"))
:config :config
(defun +csharp-cleanup-omnisharp-server-h () (defun +csharp-cleanup-omnisharp-server-h ()
"Clean up the omnisharp server once you kill the last csharp-mode buffer." "Clean up the omnisharp server once you kill the last csharp-mode buffer."

View file

@ -7,8 +7,5 @@
(package! yaml-mode) (package! yaml-mode)
(package! csv-mode) (package! csv-mode)
(package! dhall-mode) (package! dhall-mode)
(package! protobuf-mode :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*"))) (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))

View file

@ -36,7 +36,7 @@ This module adds [[https://golang.org][Go]] support.
+ [[https://github.com/syohex/emacs-go-eldoc][go-eldoc]] + [[https://github.com/syohex/emacs-go-eldoc][go-eldoc]]
+ [[https://github.com/dominikh/go-mode.el][go-guru]] + [[https://github.com/dominikh/go-mode.el][go-guru]]
+ [[https://github.com/manute/gorepl-mode][gorepl-mode]] + [[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/mdempsky/gocode][company-go]]*
+ [[https://github.com/s-kostyaev/go-gen-test][go-gen-test]] + [[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) + ~guru~ (for code navigation & refactoring commands)
+ ~goimports~ (optional: for auto-formatting code on save & fixing imports) + ~goimports~ (optional: for auto-formatting code on save & fixing imports)
+ ~gotests~ (for generate test code) + ~gotests~ (for generate test code)
+ ~gomodifytags~ (for manipulating tags)
#+BEGIN_SRC sh #+BEGIN_SRC sh
export GOPATH=~/work/go 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/gorename
go get -u golang.org/x/tools/cmd/guru go get -u golang.org/x/tools/cmd/guru
go get -u github.com/cweill/gotests/... go get -u github.com/cweill/gotests/...
go get -u github.com/fatih/gomodifytags
#+END_SRC #+END_SRC
* TODO Features * TODO Features

View file

@ -25,7 +25,8 @@
(map! :map go-mode-map (map! :map go-mode-map
:localleader :localleader
"a" #'go-add-tags "a" #'go-tag-add
"d" #'go-tag-remove
"e" #'+go/play-buffer-or-region "e" #'+go/play-buffer-or-region
"i" #'go-goto-imports ; Go to imports "i" #'go-goto-imports ; Go to imports
(:prefix ("h" . "help") (:prefix ("h" . "help")

View file

@ -14,6 +14,9 @@
(unless (executable-find "gotests") (unless (executable-find "gotests")
(warn! "Couldn't find gotests. Generating tests will not work")) (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) (when (featurep! :completion company)
(require 'company-go) (require 'company-go)
(unless (executable-find company-go-gocode-command) (unless (executable-find company-go-gocode-command)

View file

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

View file

@ -188,7 +188,7 @@ to tide."
:map tide-mode-map :map tide-mode-map
"R" #'tide-restart-server "R" #'tide-restart-server
"f" #'tide-format "f" #'tide-format
"rs" #'tide-rename-symbol "rrs" #'tide-rename-symbol
"roi" #'tide-organize-imports)) "roi" #'tide-organize-imports))
@ -205,7 +205,25 @@ to tide."
:config :config
(when (featurep! :editor evil +everywhere) (when (featurep! :editor evil +everywhere)
(let ((js2-refactor-mode-map (evil-get-auxiliary-keymap js2-refactor-mode-map 'normal t t))) (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 (use-package! eslintd-fix
@ -234,6 +252,9 @@ to tide."
(:after skewer-html (:after skewer-html
:map skewer-html-mode-map :map skewer-html-mode-map
"e" #'skewer-html-eval-tag)) "e" #'skewer-html-eval-tag))
(map! :map js2-mode-map
:localleader
(:prefix ("s" . "skewer")))
;;;###package npm-mode ;;;###package npm-mode
@ -242,7 +263,10 @@ to tide."
:config :config
(map! :localleader (map! :localleader
:map npm-mode-keymap :map npm-mode-keymap
"n" npm-mode-command-keymap)) "n" npm-mode-command-keymap)
(map! :map js2-mode-map
:localleader
(:prefix ("n" . "npm"))))
;; ;;

View file

@ -1,7 +1,7 @@
;;; lang/ocaml/autoload.el -*- lexical-binding: t; -*- ;;; lang/ocaml/autoload.el -*- lexical-binding: t; -*-
;;;###autoload ;;;###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." "Break line at point and indent, continuing comment if within one."
(interactive) (interactive)
(comment-indent-new-line) (comment-indent-new-line)

View file

@ -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) (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." "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 (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")) (runtime-file (concat runtime-dir "/" "kernel-" session ".json"))
(tramp-path (concat "/ssh:" host ":" runtime-file)) (tramp-path (concat "/ssh:" host ":" runtime-file))
(tramp-copy (concat (or +ob-ipython-local-runtime-dir (tramp-copy (concat (or +ob-ipython-local-runtime-dir
(substring (shell-command-to-string "jupyter --runtime-dir") (cdr (doom-call-process "jupyter" "--runtime-dir")))
0 -1))
"/remote-" host "-kernel-" session ".json")) "/remote-" host "-kernel-" session ".json"))
(local-path (local-path
(concat (concat

View file

@ -1,23 +1,5 @@
;;; lang/org/autoload/org.el -*- lexical-binding: t; -*- ;;; lang/org/autoload/org.el -*- lexical-binding: t; -*-
;; HACK A necessary hack because org requires a compilation step after being
;; cloned, and during that compilation a org-version.el is generated with these
;; two functions, which return the output of a 'git describe ...' call in the
;; repo's root. Of course, this command won't work in a sparse clone, and more
;; than that, initiating these compilation step is a hassle, so...
;;;###autoload (defun +org--release-a () "9.3")
;;;###autoload (fset 'org-release #'+org--release-a)
;;;###autoload (fset 'org-git-version #'ignore)
;; Org itself may override the above if it's loaded too early by packages that
;; depend on it, so we have to advise it once again:
;;;###autoload (advice-add #'org-release :override #'+org--release-a)
;;;###autoload (advice-add #'org-git-version :override #'ignore)
;;;###autoload (add-to-list 'load-path (dir!))
;;;###autoload (provide 'org-version)
;; ;;
;;; Helpers ;;; Helpers
@ -90,24 +72,19 @@
org-insert-heading-respect-content) org-insert-heading-respect-content)
(goto-char (line-end-position)) (goto-char (line-end-position))
(org-end-of-subtree) (org-end-of-subtree)
(insert (concat "\n" (insert "\n" (make-string level ?*) " ")))
(when (= level 1)
(if at-eol
(ignore (cl-incf level))
"\n"))
(make-string level ?*)
" "))))
(`above (`above
(org-back-to-heading) (org-back-to-heading)
(insert (make-string level ?*) " ") (insert (make-string level ?*) " ")
(save-excursion (save-excursion (insert "\n"))))
(insert "\n") (when-let* ((todo-keyword (org-element-property :todo-keyword context))
(if (= level 1) (insert "\n"))))) (todo-type (org-element-property :todo-type context)))
(when-let (todo-keyword (org-element-property :todo-keyword context)) (org-todo (cond ((eq todo-type 'done)
(org-todo (or (car (+org-get-todo-keywords-for todo-keyword)) (car (+org-get-todo-keywords-for todo-keyword)))
'todo))))) (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) (when (org-invisible-p)
(org-show-hidden-entry)) (org-show-hidden-entry))

View file

@ -207,19 +207,7 @@ background (and foreground) match the current theme."
;; Fix 'require(...).print is not a function' error from `ob-js' when ;; Fix 'require(...).print is not a function' error from `ob-js' when
;; executing JS src blocks ;; executing JS src blocks
(setq org-babel-js-function-wrapper "console.log(require('util').inspect(function(){\n%s\n}()));") (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))))
(defun +org-init-babel-lazy-loader-h () (defun +org-init-babel-lazy-loader-h ()
@ -633,6 +621,7 @@ between the two."
(:when (featurep! :completion helm) (:when (featurep! :completion helm)
"." #'helm-org-in-buffer-headings "." #'helm-org-in-buffer-headings
"/" #'helm-org-agenda-files-headings) "/" #'helm-org-agenda-files-headings)
"A" #'org-archive-subtree
"d" #'org-deadline "d" #'org-deadline
"e" #'org-export-dispatch "e" #'org-export-dispatch
"f" #'org-footnote-new "f" #'org-footnote-new
@ -648,26 +637,29 @@ between the two."
"s" #'org-schedule "s" #'org-schedule
"t" #'org-todo "t" #'org-todo
"T" #'org-todo-list "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") (:prefix ("a" . "attachments")
"a" #'+org-attach/file "a" #'+org-attach/file
"u" #'+org-attach/uri "u" #'+org-attach/uri
"f" #'+org-attach/find-file "f" #'+org-attach/find-file
"s" #'+org-attach/sync) "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") (:prefix ("c" . "clock")
"c" #'org-clock-in "c" #'org-clock-in
"C" #'org-clock-out "C" #'org-clock-out
"d" #'org-clock-mark-default-task "d" #'org-clock-mark-default-task
"e" #'org-clock-modify-effort-estimate "e" #'org-clock-modify-effort-estimate
"E" #'org-set-effort
"l" #'org-clock-in-last "l" #'org-clock-in-last
"g" #'org-clock-goto "g" #'org-clock-goto
"G" (λ! (org-clock-goto 'select)) "G" (λ! (org-clock-goto 'select))
"r" #'org-clock-report
"x" #'org-clock-cancel "x" #'org-clock-cancel
"=" #'org-clock-timestamps-up "=" #'org-clock-timestamps-up
"-" #'org-clock-timestamps-down) "-" #'org-clock-timestamps-down)
@ -676,21 +668,18 @@ between the two."
(:when (featurep! :completion ivy) (:when (featurep! :completion ivy)
"g" #'counsel-org-goto "g" #'counsel-org-goto
"G" #'counsel-org-goto-all) "G" #'counsel-org-goto-all)
"a" #'org-agenda-goto
"A" #'org-agenda-clock-goto
"c" #'org-clock-goto "c" #'org-clock-goto
"C" (λ! (org-clock-goto 'select)) "C" (λ! (org-clock-goto 'select))
"i" #'org-id-goto "i" #'org-id-goto
"r" #'org-refile-goto-last-stored "r" #'org-refile-goto-last-stored
"x" #'org-capture-goto-last-stored) "x" #'org-capture-goto-last-stored)
(:prefix ("b" . "tables") (:prefix ("r" . "refile")
"-" #'org-table-insert-hline "." #'+org/refile-to-current-file
"a" #'org-table-align "c" #'+org/refile-to-running-clock
"c" #'org-table-create-or-convert-from-region "l" #'+org/refile-to-last-location
"e" #'org-table-edit-field "o" #'+org/refile-to-other-window
"h" #'org-table-field-info "O" #'+org/refile-to-other-buffers
(:when (featurep! +gnuplot) "r" #'org-refile)) ; to all `org-refile-targets'
"p" #'org-plot/gnuplot)))
(map! :after org-agenda (map! :after org-agenda
:map org-agenda-mode-map :map org-agenda-mode-map
@ -699,6 +688,13 @@ between the two."
[remap org-agenda-Quit] #'org-agenda-exit [remap org-agenda-Quit] #'org-agenda-exit
:localleader :localleader
"d" #'org-agenda-deadline "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 "q" #'org-agenda-set-tags
"r" #'org-agenda-refile "r" #'org-agenda-refile
"s" #'org-agenda-schedule "s" #'org-agenda-schedule

View file

@ -6,6 +6,22 @@
(when-let (orglib (locate-library "org" nil doom--initial-load-path)) (when-let (orglib (locate-library "org" nil doom--initial-load-path))
(setq load-path (delete (substring (file-name-directory orglib) 0 -1) (setq load-path (delete (substring (file-name-directory orglib) 0 -1)
load-path))) load-path)))
;; 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! org-plus-contrib) ; install cutting-edge version of org-mode
(package! htmlize) (package! htmlize)
@ -14,12 +30,29 @@
(package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt")) (package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt"))
(package! ox-clip) (package! ox-clip)
(package! toc-org) (package! toc-org)
(when (featurep! :editor evil +everywhere) (when (featurep! :editor evil +everywhere)
(package! evil-org :recipe (:host github :repo "hlissner/evil-org-mode"))) (package! evil-org :recipe (:host github :repo "hlissner/evil-org-mode")))
(when (featurep! :tools pdf) (when (featurep! :tools pdf)
(package! org-pdfview)) (package! org-pdfview))
(when (featurep! :tools magit) (when (featurep! :tools magit)
(package! orgit)) (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 ;;; Babel
(package! ob-async) (package! ob-async)
@ -36,25 +69,11 @@
(when (featurep! :lang rust) (when (featurep! :lang rust)
(package! ob-rust)) (package! ob-rust))
;;; Modules ;;; Export
(when (featurep! +dragndrop)
(package! org-download))
(when (featurep! +gnuplot)
(package! gnuplot)
(package! gnuplot-mode))
(when (featurep! +ipython)
(package! ob-ipython))
(when (featurep! +pandoc) (when (featurep! +pandoc)
(package! ox-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) (when (featurep! +hugo)
(package! ox-hugo (package! ox-hugo
:recipe (:host github :repo "kaushalmodi/ox-hugo" :nonrecursive t))) :recipe (:host github :repo "kaushalmodi/ox-hugo" :nonrecursive t)))
(when (featurep! :lang rst)
(package! ox-rst))

View file

@ -18,31 +18,19 @@
(kill-buffer (get-buffer "org"))) (kill-buffer (get-buffer "org")))
(describe "headlines" (describe "headlines"
(it "appends first-level headlines with an extra newline" (it "opens new headline below"
(insert!! "* {0}Header") (insert!! "* {0}Header")
(+org/insert-item-below 1) (+org/insert-item-below 1)
(expect (eobp)) (expect (eobp))
(expect (buffer-substring-no-properties (point-min) (point-max)) (expect (buffer-substring-no-properties (point-min) (point-max))
:to-equal "* Header\n\n* ")) :to-equal "* Header\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"))
(it "appends second-level headlines with an no extra newline" (it "opens new headline above"
(insert!! "** {0}Header") (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")
(+org/insert-item-above 1) (+org/insert-item-above 1)
(expect (eolp)) (expect (eolp))
(expect (buffer-substring-no-properties (point-min) (point-max)) (expect (buffer-substring-no-properties (point-min) (point-max))
:to-equal "** \n** Header")) :to-equal "* \n* Header"))
(it "appends headlines, skipping subtrees" (it "appends headlines, skipping subtrees"
(insert!! "** {0}First\n" (insert!! "** {0}First\n"

View file

@ -20,6 +20,9 @@ called.")
:init :init
(setq python-environment-directory doom-cache-dir (setq python-environment-directory doom-cache-dir
python-indent-guess-indent-offset-verbose nil) python-indent-guess-indent-offset-verbose nil)
(when (featurep! +lsp)
(add-hook 'python-mode-local-vars-hook #'lsp!))
:config :config
(set-repl-handler! 'python-mode #'+python/open-repl :persist t) (set-repl-handler! 'python-mode #'+python/open-repl :persist t)
(set-docsets! 'python-mode "Python 3" "NumPy" "SciPy") (set-docsets! 'python-mode "Python 3" "NumPy" "SciPy")
@ -45,9 +48,6 @@ called.")
;; Stop the spam! ;; Stop the spam!
(setq python-indent-guess-indent-offset-verbose nil) (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 ;; Default to Python 3. Prefer the versioned Python binaries since some
;; systems stupidly make the unversioned one point at Python 2. ;; systems stupidly make the unversioned one point at Python 2.
(when (and (executable-find "python3") (when (and (executable-find "python3")
@ -88,10 +88,17 @@ called.")
(use-package! anaconda-mode (use-package! anaconda-mode
:after python :defer t
:init :init
(setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/") (setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/")
anaconda-mode-eldoc-as-single-line t) 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 :config
(add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode) (add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode)
(set-company-backend! 'anaconda-mode '(company-anaconda)) (set-company-backend! 'anaconda-mode '(company-anaconda))
@ -101,13 +108,6 @@ called.")
:documentation #'anaconda-mode-show-doc) :documentation #'anaconda-mode-show-doc)
(set-popup-rule! "^\\*anaconda-mode" :select nil) (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 () (defun +python-auto-kill-anaconda-processes-h ()
"Kill anaconda processes if this buffer is the last python buffer." "Kill anaconda processes if this buffer is the last python buffer."
(when (and (eq major-mode 'python-mode) (when (and (eq major-mode 'python-mode)
@ -115,7 +115,8 @@ called.")
(doom-buffers-in-mode 'python-mode (buffer-list))))) (doom-buffers-in-mode 'python-mode (buffer-list)))))
(anaconda-mode-stop))) (anaconda-mode-stop)))
(add-hook! 'python-mode-hook (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) (when (featurep 'evil)
(add-hook 'anaconda-mode-hook #'evil-normalize-keymaps)) (add-hook 'anaconda-mode-hook #'evil-normalize-keymaps))
@ -130,9 +131,10 @@ called.")
(use-package! pyimport (use-package! pyimport
:after python :defer t
:config :init
(map! :map python-mode-map (map! :after python
:map python-mode-map
:localleader :localleader
(:prefix ("i" . "imports") (:prefix ("i" . "imports")
:desc "Insert missing imports" "i" #'pyimport-insert-missing :desc "Insert missing imports" "i" #'pyimport-insert-missing
@ -193,7 +195,18 @@ called.")
(_ (pipenv-project-p))) (_ (pipenv-project-p)))
(format "PIPENV_MAX_DEPTH=9999 %s run %%c %%o %%s %%a" bin) (format "PIPENV_MAX_DEPTH=9999 %s run %%c %%o %%s %%a" bin)
"%c %o %s %a"))) "%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 (use-package! pyvenv
@ -206,18 +219,7 @@ called.")
(add-hook 'python-mode-local-vars-hook #'pyvenv-track-virtualenv) (add-hook 'python-mode-local-vars-hook #'pyvenv-track-virtualenv)
(add-to-list 'global-mode-string (add-to-list 'global-mode-string
'(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name " ")) '(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name " "))
'append) '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))
@ -243,7 +245,7 @@ called.")
;; If none of these work for you, `conda-anaconda-home' must be set ;; If none of these work for you, `conda-anaconda-home' must be set
;; explicitly. Afterwards, run M-x `conda-env-activate' to switch between ;; explicitly. Afterwards, run M-x `conda-env-activate' to switch between
;; environments ;; environments
(unless (cl-loop for dir in (list conda-anaconda-home (or (cl-loop for dir in (list conda-anaconda-home
"~/.anaconda" "~/.anaconda"
"~/.miniconda" "~/.miniconda"
"~/.miniconda3" "~/.miniconda3"
@ -297,5 +299,6 @@ called.")
(use-package! flycheck-cython (use-package! flycheck-cython
:when (featurep! +cython)
:when (featurep! :tools flycheck) :when (featurep! :tools flycheck)
:after cython-mode) :after cython-mode)

View file

@ -24,10 +24,8 @@
(add-hook! 'racket-mode-hook (add-hook! 'racket-mode-hook
#'rainbow-delimiters-mode #'rainbow-delimiters-mode
#'highlight-quoted-mode) #'highlight-quoted-mode
#'racket-smart-open-bracket-mode)
(map! :map (racket-mode-map racket-repl-mode-map)
:i "[" #'racket-smart-open-bracket)
(map! :localleader (map! :localleader
:map racket-mode-map :map racket-mode-map

View file

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

View file

@ -0,0 +1,4 @@
;; -*- no-byte-compile: t; -*-
;;; lang/rst/packages.el
(package! sphinx-mode)

View file

@ -7,43 +7,13 @@
;; ;;
;;; Packages ;;; 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 (use-package! rustic
:when EMACS26+ :mode ("\\.rs$" . rustic-mode)
:after rust-mode
:preface :preface
(setq rustic-rls-pkg (if (featurep! +lsp) 'lsp-mode)) (setq rustic-rls-pkg (if (featurep! +lsp) 'lsp-mode))
:config :config
(set-docsets! 'rustic-mode "Rust")
(setq rustic-indent-method-chain t (setq rustic-indent-method-chain t
rustic-flycheck-setup-mode-line-p nil rustic-flycheck-setup-mode-line-p nil
;; use :editor format instead ;; use :editor format instead
@ -52,38 +22,35 @@
;; buffers, so we disable it, but only for evil users, because it ;; buffers, so we disable it, but only for evil users, because it
;; affects `forward-sexp' and its ilk. See ;; affects `forward-sexp' and its ilk. See
;; https://github.com/rust-lang/rust-mode/issues/288. ;; 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) (add-hook 'rustic-mode-hook #'rainbow-delimiters-mode)
(defadvice! +rust--dont-install-packages-p (orig-fn &rest args) (when (featurep! +lsp)
:around #'rustic-setup-rls (add-hook 'rustic-mode-local-vars-hook #'lsp!)))
(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). (use-package! racer
((symbol-function #'package-installed-p) :unless (featurep! +lsp)
(lambda (pkg) :hook (rustic-mode . racer-mode)
(require pkg nil t))) :config
;; If lsp/elgot isn't available, it attempts to install lsp-mode (set-lookup-handlers! 'rustic-mode
;; via package.el. Doom manages its own dependencies so we disable :definition '(racer-find-definition :async t)
;; that behavior. :documentation '+rust-racer-lookup-documentation))
((symbol-function #'rustic-install-rls-client-p)
(lambda (&rest _)
(message "No RLS server running"))))
(apply orig-fn args))))
;; ;;
;;; Tools ;;; Tools
(use-package! cargo (use-package! cargo
:after rust-mode :after rustic-mode
:config :config
(defvar +rust-keymap (map! :map rustic-mode-map
(if (boundp 'rustic-mode-map)
rustic-mode-map
rust-mode-map))
(map! :map +rust-keymap
:localleader :localleader
(:prefix ("b" . "build") (:prefix ("b" . "build")
:desc "cargo add" "a" #'cargo-process-add :desc "cargo add" "a" #'cargo-process-add

View file

@ -1,9 +1,7 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; lang/rust/packages.el ;;; lang/rust/packages.el
(when EMACS26+ (package! rustic)
(package! rustic))
(package! rust-mode)
(unless (featurep! +lsp) (unless (featurep! +lsp)
(package! racer)) (package! racer))

View file

@ -1,7 +1,7 @@
;;; lang/scala/autoload.el -*- lexical-binding: t; -*- ;;; lang/scala/autoload.el -*- lexical-binding: t; -*-
;;;###autoload ;;;###autoload
(defun +scala-comment-indent-new-line (&rest _) (defun +scala-comment-indent-new-line (&optional _)
"Continue the commnt on the current line. "Continue the commnt on the current line.
Meant to be used for `scala-mode's `comment-line-break-function'." Meant to be used for `scala-mode's `comment-line-break-function'."

View file

@ -22,7 +22,7 @@
(use-package! lsp-sourcekit (use-package! lsp-sourcekit
:when (featurep! +lsp) :when (featurep! +lsp)
:after swift-mode :after swift-mode
:init (add-hook 'swift-mode-hook #'lsp!) :init (add-hook 'swift-mode-local-vars-hook #'lsp!)
:config :config
(unless (getenv "SOURCEKIT_TOOLCHAIN_PATH") (unless (getenv "SOURCEKIT_TOOLCHAIN_PATH")
(setenv "SOURCEKIT_TOOLCHAIN_PATH" "/Library/Developer/Toolchains/swift-latest.xctoolchain")) (setenv "SOURCEKIT_TOOLCHAIN_PATH" "/Library/Developer/Toolchains/swift-latest.xctoolchain"))

View file

@ -28,12 +28,6 @@
(after! css-mode (after! css-mode
;; css-mode hooks apply to scss and less-css modes ;; css-mode hooks apply to scss and less-css modes
(add-hook 'css-mode-hook #'rainbow-delimiters-mode) (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! :localleader
:map scss-mode-map :map scss-mode-map
"b" #'+css/scss-build "b" #'+css/scss-build

View file

@ -42,11 +42,8 @@
(cl-loop for pair in (cdr alist) (cl-loop for pair in (cdr alist)
unless (string-match-p "^[a-z-]" (cdr pair)) unless (string-match-p "^[a-z-]" (cdr pair))
collect (cons (car pair) collect (cons (car pair)
;; TODO Replace with `string-trim-right' (Emacs 26+) (string-trim-right (cdr pair)
(let ((string (cdr pair))) "\\(?:>\\|]\\|}\\)+\\'")))))
(if (string-match "\\(?:>\\|]\\|}\\)+\\'" string)
(replace-match "" t t string)
string))))))
(delq! nil web-mode-engines-auto-pairs)) (delq! nil web-mode-engines-auto-pairs))
(map! :map web-mode-map (map! :map web-mode-map

View file

@ -44,7 +44,7 @@
(+css--toggle-inline-or-block beg end)))) (+css--toggle-inline-or-block beg end))))
;;;###autoload ;;;###autoload
(defun +css/comment-indent-new-line () (defun +css/comment-indent-new-line (&optional _)
"Continues the comment in an indented new line. "Continues the comment in an indented new line.
Meant for `comment-line-break-function' in `css-mode' and `scss-mode'." Meant for `comment-line-break-function' in `css-mode' and `scss-mode'."

View file

@ -12,7 +12,7 @@
;; +css.el ;; +css.el
(package! css-mode :built-in t) (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! sass-mode)
(package! stylus-mode) (package! stylus-mode)

View file

@ -22,9 +22,9 @@ buffer.")
(defvar +eshell-aliases (defvar +eshell-aliases
'(("q" "exit") ; built-in '(("q" "exit") ; built-in
("f" "find-file $1") ("f" "find-file $1")
("d" "dired $1")
("bd" "eshell-up $1") ("bd" "eshell-up $1")
("rg" "rg --color=always $*") ("rg" "rg --color=always $*")
("ag" "ag --color=always $*")
("l" "ls -lh") ("l" "ls -lh")
("ll" "ls -lah") ("ll" "ls -lah")
("clear" "clear-scrollback")) ; more sensible than default ("clear" "clear-scrollback")) ; more sensible than default

View file

@ -1,7 +1,7 @@
;;; tools/ein/autoload/hydra.el -*- lexical-binding: t; -*- ;;; tools/ein/autoload/hydra.el -*- lexical-binding: t; -*-
;;;###if (featurep! :ui hydra) ;;;###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) (defhydra +ein/hydra (:hint t :color red)
" "
Operations on Cells^^^^^^ Other Operations on Cells^^^^^^ Other

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