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:
matrix:
emacs_version:
- 25.3
- 26.1
- snapshot
- 26.2
- 26.3
include:
- emacs_version: 26.3
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">
</a>
<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 href="https://github.com/hlissner/doom-emacs/actions">
<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
":"; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*-
":"; command -v $EMACS >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; }
":"; VERSION=$($EMACS --version | head -n1)
":"; case "$VERSION" in *\ 2[0-2].[0-1].[0-9]) echo "You're running $VERSION"; echo "That version is too old to run Doom. Check your PATH"; echo; exit 2 ;; esac
":"; DOOMBASE=$(dirname "$0")/..
":"; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; }
":"; [ "$1" = doc ] || [ "$1" = doctor ] && { cd "$DOOMBASE"; shift; exec $EMACS --script bin/doom-doctor "$@"; exit 0; }
":"; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; }
":"; exec $EMACS --script "$0" -- "$@"
":"; exit 0
:; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*-
:; command -v $EMACS >/dev/null || { >&2 echo "Can't find emacs in your PATH"; exit 1; }
:; VERSION=$($EMACS --version | head -n1)
:; case "$VERSION" in *\ 2[0-5].[0-9]) echo "Detected Emacs $VERSION"; echo "Doom only supports Emacs 26.1 and newer"; echo; exit 2 ;; esac
:; DOOMBASE="$(dirname "$0")/.."
:; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; }
:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; }
:; exec $EMACS --script "$0" -- "$@"
:; exit 0
(defconst user-emacs-directory
(or (getenv "EMACSDIR")
(expand-file-name "../" (file-name-directory (file-truename load-file-name)))))
(let* ((loaddir (file-name-directory (file-truename load-file-name)))
(emacsdir (getenv "EMACSDIR"))
(user-emacs-directory (or emacsdir (expand-file-name "../" loaddir)))
(load-prefer-newer t))
(defun usage ()
(with-temp-buffer
(insert (format! "%s %s [COMMAND] [ARGS...]\n"
(bold "Usage:")
(file-name-nondirectory load-file-name))
"\n"
"A command line interface for managing Doom Emacs; including\n"
"package management, diagnostics, unit tests, and byte-compilation.\n"
"\n"
"This tool also makes it trivial to launch Emacs out of a different\n"
"folder or with a different private module.\n"
"\n"
(format! (bold "Example:\n"))
" doom install\n"
" doom help update\n"
" doom compile :core lang/php lang/python\n"
" doom run\n"
" doom run -nw file.txt file2.el\n"
" doom run -p ~/.other.doom.d -e ~/.other.emacs.d -nw file.txt\n"
"\n"
(format! (bold "Options:\n"))
" -h --help\t\tSame as help command\n"
" -d --debug\t\tTurns on doom-debug-mode (and debug-on-error)\n"
" -e --emacsd DIR\tUse the emacs config at DIR (e.g. ~/.emacs.d)\n"
" -i --insecure\t\tDisable TLS/SSL validation (not recommended)\n"
" -l --local DIR\tUse DIR as your local storage directory\n"
" -p --private DIR\tUse the private module at DIR (e.g. ~/.doom.d)\n"
" -y --yes\t\tAuto-accept all confirmation prompts\n\n")
(princ (buffer-string)))
(doom--dispatch-help))
(push (expand-file-name "core" user-emacs-directory) load-path)
(require 'core)
(require 'core-cli)
;;
(let ((args (cdr (cdr (cdr command-line-args)))))
;; Parse options
(while (ignore-errors (string-prefix-p "-" (car args)))
(pcase (pop args)
((or "-h" "--help")
(push "help" args))
((or "-d" "--debug")
(defcli! :main
((help-p ["-h" "--help"] "Same as help command")
(debug-p ["-d" "--debug"] "Turns on doom-debug-mode (and debug-on-error)")
(yes-p ["-y" "--yes"] "Auto-accept all confirmation prompts")
(emacsdir ["--emacsdir" dir] "Use the emacs config at DIR (e.g. ~/.emacs.d)")
(doomdir ["--doomdir" dir] "Use the private module at DIR (e.g. ~/.doom.d)")
(localdir ["--localdir" dir] "Use DIR as your local storage directory")
&optional command &rest args)
"A command line interface for managing Doom Emacs.
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")
(message "Debug mode on"))
((or "-i" "--insecure")
(setenv "INSECURE" "1")
(message "Insecure mode on"))
((or "-p" "--private")
(setq doom-private-dir (expand-file-name (concat (pop args) "/")))
(setenv "DOOMDIR" doom-private-dir)
(message "DOOMDIR changed to %s" doom-private-dir)
(or (file-directory-p doom-private-dir)
(message "Warning: %s does not exist"
(abbreviate-file-name doom-private-dir))))
((or "-l" "--local")
(setq doom-local-dir (expand-file-name (concat (pop args) "/")))
(setenv "DOOMLOCALDIR" doom-local-dir)
(message "DOOMLOCALDIR changed to %s" doom-local-dir))
((or "-e" "--emacsd")
(setq user-emacs-directory (expand-file-name (concat (pop args) "/")))
(message "Emacs directory changed to %s" user-emacs-directory))
((or "-y" "--yes")
(setq doom-debug-mode t)
(print! (info "Debug mode on")))
(when yes-p
(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)
(error "%s does not exist" user-emacs-directory))
;; Reload core in case any of the directories were changed.
(when (or emacsdir doomdir localdir)
(load! "core/core.el" user-emacs-directory))
;; Bootstrap Doom
(if (not noninteractive)
(let ((doom-interactive-mode t))
(load (expand-file-name "init.el" user-emacs-directory)
nil 'nomessage)
(cond ((not noninteractive)
(print! "Doom launched out of %s (test mode)" (path user-emacs-directory))
(load! "init.el" user-emacs-directory)
(doom-run-all-startup-hooks-h))
(load (expand-file-name "core/core.el" user-emacs-directory)
nil 'nomessage)
(doom-initialize 'force-p)
(doom-initialize-modules)
(cond ((or (not args)
(and (not (cdr args))
(member (car args) '("help" "h"))))
(unless args
(print! (error "No command detected.\n")))
(usage))
((require 'core-cli)
(setq argv nil)
(condition-case e
(doom-dispatch (car args) (cdr args))
((null command)
(doom-cli-execute "help"))
((condition-case e
(let ((start-time (current-time)))
(and (doom-cli-execute command args)
(terpri)
(print! (success "Finished! (%.4fs)")
(float-time
(time-subtract (current-time)
start-time)))))
(user-error
(print! (error "%s\n") (error-message-string e))
(print! (yellow "See 'doom help %s' for documentation on this command.") (car args)))
@ -116,5 +90,8 @@
"report, please include it!\n\n"
"Emacs outputs to standard error, so you'll need to redirect stderr to\n"
"stdout to pipe this to a file or clipboard!\n\n"
" e.g. doom -d install 2>&1 | clipboard-program"))
(signal 'doom-error e))))))))
" e.g. doom -d install 2>&1 | clipboard-program\n"))
(signal 'doom-error e)))))))
(doom-cli-execute :main (cdr (member "--" argv)))
(setq argv nil))

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)
(switch-to-buffer (doom-fallback-buffer)))
(mapc #'doom-kill-buffer-and-windows buffer-list)
(delete-other-windows)
(when interactive
(message "Killed %s buffers"
(- (length buffer-list)

View file

@ -124,89 +124,3 @@ Warning: freezes indefinitely on any stdin prompt."
(sit-for 0.1))
(process-exit-status process))
(string-trim (buffer-string)))))
(defun doom--cli-normalize (args specs)
(let* ((args (cl-remove-if-not #'stringp args))
(optspec (cl-remove-if-not #'listp specs))
(argspec (cl-remove-if #'listp specs))
(options (mapcar #'list (mapcar #'car-safe optspec)))
extra
arguments)
(dolist (spec optspec)
(setf (nth 1 spec) (doom-enlist (nth 1 spec))))
(while args
(let ((arg (pop args)))
(cl-check-type arg string)
(if (not (string-prefix-p "-" arg))
(push arg arguments)
(if-let (specs (cl-remove-if-not
(if (string-prefix-p "--" arg)
(doom-partial #'member arg)
(lambda (flags)
(cl-loop for switch in (split-string (string-remove-prefix "-" arg) "" t)
if (member (concat "-" switch) flags)
return t)))
optspec
:key #'cadr))
(pcase-dolist (`(,sym ,flags ,type) specs)
(setf (alist-get sym options)
(list
(let ((value (if type (pop args))))
(pcase type
(`&string value)
(`&int `(truncate (read ,value)))
(`&float `(float (read ,value)))
(`&path `(expand-file-name ,value))
(`&directory
`(let ((path (expand-file-name ,value)))
(unless (file-directory-p path)
(error "Directory does not exist: %s" path))
path))
(`&file
`(let ((path (expand-file-name ,value)))
(unless (file-exists-p path)
(error "File does not exist: %s" path))
path))
(`&sexp `(read ,value))
((or `nil `t) arg)
(_ (error "Not a valid type: %S" type)))))))
(push arg extra)))))
(list optspec (nreverse options)
argspec (nreverse arguments))))
;;;###autoload
(defun doom-cli-getopts (args specs)
"TODO"
(cl-destructuring-bind (optspec options argspec arguments)
(doom--cli-normalize args specs)
(let ((i 0)
optional-p
noerror-p)
(cl-dolist (spec argspec)
(cond ((eq spec '&rest)
(push (list (cadr (member '&rest specs))
`(quote
,(reverse
(butlast (reverse arguments) i))))
options)
(cl-return))
((eq spec '&all)
(push (list (cadr (member '&all specs))
`(quote ,args))
options))
((eq spec '&noerror) (setq noerror-p t))
((eq spec '&optional) (setq optional-p t))
((and (>= i (length arguments)) (not optional-p))
(signal 'wrong-number-of-arguments
(list argspec (length arguments))))
((push (list spec (nth i arguments)) options)
(cl-incf i)))))
(nreverse options)))
;;;###autoload
(defmacro let-cliopts! (args spec &rest body)
"Run BODY with command line ARGS parsed according to SPEC."
(declare (indent 2))
`(eval (append (list 'let (doom-cli-getopts ,args ',spec))
(quote ,body))
t))

View file

@ -8,10 +8,10 @@
emacs -Q -l init.el -f doom-run-all-startup-hooks-h"
(run-hook-wrapped 'after-init-hook #'doom-try-run-hook)
(setq after-init-time (current-time))
(dolist (hook (list 'delayed-warnings-hook
'emacs-startup-hook 'term-setup-hook
'window-setup-hook))
(run-hook-wrapped hook #'doom-try-run-hook)))
(mapc (doom-rpartial #'run-hook-wrapped #'doom-try-run-hook)
(list 'delayed-warnings-hook
'emacs-startup-hook 'tty-setup-hook
'window-setup-hook)))
;;
@ -32,9 +32,8 @@ ready to be pasted in a bug report on github."
(doom-modules (doom-modules)))
(cl-letf
(((symbol-function 'sh)
(lambda (format)
(string-trim
(shell-command-to-string format)))))
(lambda (&rest args)
(cdr (apply #'doom-call-process args)))))
`((emacs
(version . ,emacs-version)
(features ,@system-configuration-features)
@ -47,14 +46,14 @@ ready to be pasted in a bug report on github."
'server-running))))
(doom
(version . ,doom-version)
(build . ,(sh "git log -1 --format=\"%D %h %ci\"")))
(build . ,(sh "git" "log" "-1" "--format=%D %h %ci")))
(system
(type . ,system-type)
(config . ,system-configuration)
(shell . ,shell-file-name)
(uname . ,(if IS-WINDOWS
"n/a"
(sh "uname -msrv")))
(sh "uname" "-msrv")))
(path . ,(mapcar #'abbreviate-file-name exec-path)))
(config
(envfile
@ -117,7 +116,7 @@ branch and commit."
"n/a")
(or (vc-git-working-revision doom-core-dir)
"n/a")
(or (string-trim (shell-command-to-string "git log -1 --format=%ci"))
(or (cdr (doom-call-process "git" "log" "-1" "--format=%ci"))
"n/a"))))
;;;###autoload
@ -302,34 +301,6 @@ to reproduce bugs and determine if Doom is to blame."
;;
;;; Reporting bugs
(defun doom--report-bug ()
"TODO"
(interactive)
(let ((url "https://github.com/hlissner/doom-emacs/issues/new?body="))
;; TODO Refactor me
(save-restriction
(widen)
(goto-char (point-min))
(re-search-forward "^-------------------------------------------------------------------\n" nil t)
(skip-chars-forward " \n\t")
(condition-case e
(progn
(save-excursion
(when (and (re-search-backward "\\+ [ ] " nil t)
(not (y-or-n-p "You haven't checked all the boxes. Continue anyway?")))
(error "Aborted submit")))
(narrow-to-region (point) (point-max))
(let ((text (buffer-string)))
;; `url-encode-url' doesn't encode ampersands
(setq text (replace-regexp-in-string "&" "%26" text))
(setq url (url-encode-url (concat url text)))
;; HACK: encode some characters according to HTML URL Encoding Reference
;; http://www.w3schools.com/tags/ref_urlencode.asp
(setq url (replace-regexp-in-string "#" "%23" url))
(setq url (replace-regexp-in-string ";" "%3B" url))
(browse-url url)))
(error (signal (car e) (car e)))))))
;;;###autoload
(defun doom/report-bug ()
"Open a markdown buffer destinated to populate the New Issue page on Doom
@ -338,36 +309,7 @@ Emacs' issue tracker.
If called when a backtrace buffer is present, it and the output of `doom-info'
will be automatically appended to the result."
(interactive)
;; TODO Refactor me
(let ((backtrace
(when (get-buffer "*Backtrace*")
(with-current-buffer "*Backtrace*"
(string-trim
(buffer-substring-no-properties
(point-min)
(min (point-max) 1000))))))
(buf (get-buffer-create "*doom:sandbox*")))
(with-current-buffer buf
(erase-buffer)
(condition-case _ (gfm-mode)
(error (text-mode)))
(doom-template-insert "SUBMIT_BUG_REPORT")
(goto-char (point-max))
(let ((pos (point)))
(save-excursion
(insert
"\n" (doom-info) "\n"
(if (and backtrace (not (string-empty-p backtrace)))
(format "\n<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))))
(browse-url "https://github.com/hlissner/doom-emacs/issues/new/choose"))
;;

View file

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

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

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."
(when-let (plist (doom-package-get package))
(not (plist-get plist :ignore) t)))
(not (plist-get plist :ignore))))
;;;###autoload
(defun doom-package-private-p (package)

View file

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

View file

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

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

View file

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

View file

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

View file

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

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; -*-
(defcli! env (&rest args)
(defcli! env
((clear-p ["-c" "--clear"] "Clear and delete your envvar file")
(outputfile ["-o" PATH]
"Generate the envvar file at PATH. Note that envvar files that aren't in
`doom-env-file' won't be loaded automatically at startup. You will need to
load them manually from your private config with the `doom-load-envvars-file'
function."))
"Creates or regenerates your envvars file.
doom env [-c|--clear]
This is meant to be a faster and more comprehensive alternative to
exec-path-from-shell. See the FAQ in the documentation for an explanation why.
The envvars file is created by scraping your (interactive) shell environment
into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env'
(or '$SHELL -c set' on windows). Doom loads this file at startup (if it exists)
@ -21,14 +22,23 @@ app launchers on Linux).
This file is automatically regenerated when you run this command or 'doom
refresh'. However, 'doom refresh' will only regenerate this file if it exists.
Use the -c or --clear switch to delete your envvar file."
(when (member "clear" args) ; DEPRECATED
(message "'doom env clear' is deprecated. Use 'doom env -c' or 'doom env --clear' instead")
(push "-c" args))
(let ((env-file (or (cadr (member "-o" args))
doom-env-file)))
(cond ((or (member "-c" args)
(member "--clear" args))
Why this over exec-path-from-shell?
1. `exec-path-from-shell' spawns (at least) one process at startup to scrape
your shell environment. This can be arbitrarily slow depending on the
user's shell configuration. A single program (like pyenv or nvm) or config
framework (like oh-my-zsh) could undo all of Doom's startup optimizations
in one fell swoop.
2. `exec-path-from-shell' only scrapes some state from your shell. You have to
be proactive in order to get it to capture all the envvars relevant to your
development environment.
I'd rather it inherit your shell environment /correctly/ (and /completely/)
or not at all. It frontloads the debugging process rather than hiding it
until it you least want to deal with it."
(let ((env-file (expand-file-name (or outputfile doom-env-file))))
(cond (clear-p
(unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared"
(path env-file)))
@ -36,12 +46,11 @@ Use the -c or --clear switch to delete your envvar file."
(print! (success "Successfully deleted %S")
(path env-file)))
((or (null args)
(member "-o" args))
(doom-reload-env-file 'force env-file))
(args
(user-error "I don't understand 'doom env %s'"
(string-join args " ")))
((user-error "I don't understand 'doom env %s'"
(string-join args " "))))))
((doom-cli-reload-env-file 'force env-file)))))
;;
@ -66,22 +75,7 @@ Use the -c or --clear switch to delete your envvar file."
Each string is a regexp, matched against variable names to omit from
`doom-env-file'.")
(defvar doom-env-executable
(if IS-WINDOWS
"set"
(executable-find "env"))
"The program to use to scrape your shell environment with.
It is rare that you'll need to change this.")
(defvar doom-env-switches
(if IS-WINDOWS
"-c"
"-ic") ; Execute in an interactive shell
"The `shell-command-switch'es to use on `doom-env-executable'.
This is a list of strings. Each entry is run separately and in sequence with
`doom-env-executable' to scrape envvars from your shell environment.")
(defun doom-reload-env-file (&optional force-p env-file)
(defun doom-cli-reload-env-file (&optional force-p env-file)
"Generates `doom-env-file', if it doesn't exist (or if FORCE-P).
This scrapes the variables from your shell environment by running
@ -99,49 +93,37 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"Generating")
(path env-file))
(let ((process-environment doom--initial-process-environment))
(let ((shell-command-switch doom-env-switches)
(error-buffer (get-buffer-create "*env errors*")))
(print! (info "Scraping shell environment with '%s %s %s'")
(filename shell-file-name)
shell-command-switch
(filename doom-env-executable))
(save-excursion
(shell-command doom-env-executable (current-buffer) error-buffer))
(print! (info "Scraping shell environment"))
(print-group!
(let ((errors (with-current-buffer error-buffer (buffer-string))))
(unless (string-empty-p errors)
(print! (info "Warnings:\n\n%s") (indent 4 errors))))
;; Remove undesireable variables
(when doom-interactive-mode
(user-error "'doom env' must be run on the command line, not an interactive session"))
(goto-char (point-min))
(insert
(concat
"# -*- mode: dotenv -*-\n"
(format "# Generated with: %s %s %s\n"
shell-file-name
doom-env-switches
doom-env-executable)
(format "# Generated from a %s shell environent\n" shell-file-name)
"# ---------------------------------------------------------------------------\n"
"# This file was auto-generated by `doom env'. It contains a list of environment\n"
"# variables scraped from your default shell (excluding variables blacklisted\n"
"# in doom-env-ignored-vars).\n"
"#\n"
"# It is NOT safe to edit this file. Changes will be overwritten next time that\n"
"# `doom refresh` is executed. Alternatively, create your own env file with\n"
"# `doom env -o ~/.doom.d/myenv`, then load it with (doom-load-envvars-file FILE)\n"
"# in your private config.el.\n"
(if (file-equal-p env-file doom-env-file)
(concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n"
"# run 'doom refresh'. To create a safe-to-edit envvar file use:\n#\n"
"# doom env -o ~/.doom.d/myenv\n#\n"
"# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n")
(concat "# This file is safe to edit by hand, but needs to be loaded manually with:\n#\n"
"# (doom-load-envvars-file \"path/to/this/file\")\n#\n"
"# Use 'doom env -o path/to/this/file' to regenerate it."))
"# ---------------------------------------------------------------------------\n\n"))
(goto-char (point-min))
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
(save-excursion
(let* ((valend (or (save-match-data
(when (re-search-forward "^\\([^= ]+\\)=" nil t)
(line-beginning-position)))
(point-max)))
(var (match-string 1)))
(when (cl-loop for regexp in doom-env-ignored-vars
if (string-match-p regexp var)
return t)
(print! (info "Ignoring %s") var)
(delete-region (match-beginning 0) (1- valend)))))))
;; We assume that this noninteractive session was spawned from the
;; user's interactive shell, therefore we just dump
;; `process-environment' to a file.
(dolist (env process-environment)
(if (cl-find-if (doom-rpartial #'string-match-p env)
doom-env-ignored-vars)
(print! (info "Ignoring %s") env)
(insert env "\n")))
(print! (success "Successfully generated %S")
(path env-file))
t))))))

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; -*-
(defcli! quickstart (&rest args) ; DEPRECATED
"This is a deprecated alias for 'doom install'.
See 'doom help install' instead."
:hidden t
(apply #'doom-cli-install args))
(defcli! (install i) (&rest args)
(defcli! (install i)
((noconfig-p ["--no-config"] "Don't create DOOMDIR or dummy files therein")
(noenv-p ["--no-env"] "Don't generate an envvars file (see 'doom help env')")
(noinstall-p ["--no-install"] "Don't auto-install packages")
(nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts")
&rest args)
"Installs and sets up Doom Emacs for the first time.
This command does the following:
@ -25,23 +23,17 @@ The location of DOOMDIR can be changed with the -p option, or by setting the
DOOMDIR environment variable. e.g.
doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom install
The following switches are recognized:
--no-config Don't create DOOMDIR or dummy files therein
--no-install Don't auto-install packages
--no-env Don't generate an envvars file (see `doom help env`)
--no-fonts Don't install (or prompt to install) all-the-icons fonts
-y / --yes Auto-accept any confirmation prompts"
DOOMDIR=~/.config/doom doom install"
:bare t
(print! (green "Installing Doom Emacs!\n"))
(let ((default-directory (doom-path "~")))
;; Create `doom-private-dir'
(if (member "--no-config" args)
(if noconfig-p
(print! (warn "Not copying private config template, as requested"))
(print! "> Creating %s" (relpath doom-private-dir))
(print! (start "Creating %s") (relpath doom-private-dir))
(make-directory doom-private-dir 'parents)
(print! (success "Created %s") (relpath doom-private-dir))
(print-group!
(print! (success "Created %s") (relpath doom-private-dir)))
;; Create init.el, config.el & packages.el
(mapc (lambda (file)
@ -71,26 +63,29 @@ The following switches are recognized:
;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install)
(doom-initialize-packages 'force-p)
(doom-initialize 'force)
(doom-initialize-modules)
;; Ask if Emacs.app should be patched
(if (member "--no-env" args)
(print! (warn "- Not generating envvars file, as requested"))
;; Ask if user would like an envvar file generated
(if noenv-p
(print! (warn "Not generating envvars file, as requested"))
(if (file-exists-p doom-env-file)
(print! (info "Envvar file already exists, skipping"))
(when (or doom-auto-accept
(y-or-n-p "Generate an env file? (see `doom help env` for details)"))
(doom-reload-env-file 'force-p)))
(doom-cli-reload-env-file 'force-p))))
;; Install Doom packages
(if (member "--no-install" args)
(print! (warn "- Not installing plugins, as requested"))
(if noinstall-p
(print! (warn "Not installing plugins, as requested"))
(print! "Installing plugins")
(doom-packages-install doom-auto-accept))
(doom-cli-packages-install))
(print! "Regenerating autoloads files")
(doom-reload-autoloads nil 'force-p)
(doom-cli-reload-autoloads nil 'force-p)
(if (member "--no-fonts" args)
(print! (warn "- Not installing fonts, as requested"))
(if nofonts-p
(print! (warn "Not installing fonts, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons)
@ -98,6 +93,9 @@ The following switches are recognized:
(IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes))))
(when (file-exists-p "~/.emacs")
(print! (warn "A ~/.emacs file was detected. This conflicts with Doom and should be deleted!")))
(print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO")

View file

@ -1,73 +1,55 @@
;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el
(defmacro doom--ensure-autoloads-while (&rest body)
`(progn
(straight-check-all)
(doom-reload-core-autoloads)
(when (progn ,@body)
(doom-reload-package-autoloads 'force-p))
t))
;;
;;; Dispatchers
(defcli! (update u) (&rest args)
(defcli! (update u) ()
"Updates packages.
This works by fetching all installed package repos and checking the distance
between HEAD and FETCH_HEAD. This can take a while.
This excludes packages whose `package!' declaration contains a non-nil :freeze
or :ignore property.
or :ignore property."
(straight-check-all)
(doom-cli-reload-core-autoloads)
(when (doom-cli-packages-update)
(doom-cli-reload-package-autoloads 'force-p))
t)
Switches:
-t/--timeout TTL Seconds until a thread is timed out (default: 45)
--threads N How many threads to use (default: 8)"
(doom--ensure-autoloads-while
(doom-packages-update
doom-auto-accept
(when-let (threads (cadr (member "--threads" args)))
(string-to-number threads))
(when-let (timeout (cadr (or (member "--timeout" args)
(member "-t" args))))
(string-to-number timeout)))))
(defcli! (rebuild build b) (&rest args)
"Rebuilds all installed packages.
(defcli! (build b)
((rebuild-p ["-r"] "Only rebuild packages that need rebuilding"))
"Byte-compiles & symlinks installed packages.
This ensures that all needed files are symlinked from their package repo and
their elisp files are byte-compiled.
their elisp files are byte-compiled. This is especially necessary if you upgrade
Emacs (as byte-code is generally not forward-compatible)."
(when (doom-cli-packages-build (not rebuild-p))
(doom-cli-reload-package-autoloads 'force-p))
t)
Switches:
-f Forcibly rebuild autoloads files, even if they're up-to-date"
(doom--ensure-autoloads-while
(doom-packages-rebuild doom-auto-accept (member "-f" args))))
(defcli! (purge p)
((nobuilds-p ["-b" "--no-builds"] "Don't purge unneeded (built) packages")
(noelpa-p ["-p" "--no-elpa"] "Don't purge ELPA packages")
(norepos-p ["-r" "--no-repos"] "Don't purge unused straight repos")
(regraft-p ["-g" "--regraft"] "Regraft git repos (ie. compact them)"))
"Deletes orphaned packages & repos, and compacts them.
(defcli! (purge p) (&rest args)
"Deletes any unused ELPA packages, straight builds, and (optionally) repos.
Purges all installed ELPA packages (as they are considered temporary). Purges
all orphaned package repos and builds. If -g/--regraft is supplied, the git
repos among them will be regrafted and compacted to ensure they are as small as
possible.
By default, this does not purge ELPA packages or repos. It is a good idea to run
'doom purge --all' once in a while, to stymy build-up of repos and ELPA
packages that could be taking up precious space.
It is a good idea to occasionally run this doom purge -g to ensure your package
list remains lean."
(straight-check-all)
(when (doom-cli-packages-purge
(not noelpa-p)
(not norepos-p)
(not nobuilds-p)
regraft-p)
(doom-cli-reload-package-autoloads 'force-p))
t)
Switches:
--no-builds Don't purge unneeded (built) packages
-e / --elpa Don't purge ELPA packages
-r / --repos Purge unused repos
--all Purge builds, elpa packages and repos"
(doom--ensure-autoloads-while
(doom-packages-purge (or (member "-e" args)
(member "--elpa" args)
(member "--all" args))
(not (member "--no-builds" args))
(or (member "-r" args)
(member "--repos" args)
(member "--all" args))
doom-auto-accept)))
;; (defcli! rollback () ; TODO rollback
;; (defcli! rollback () ; TODO doom rollback
;; "<Not implemented yet>"
;; (user-error "Not implemented yet, sorry!"))
@ -75,15 +57,12 @@ Switches:
;;
;;; Library
(defun doom-packages-install (&optional auto-accept-p)
(defun doom-cli-packages-install ()
"Installs missing packages.
This function will install any primary package (i.e. a package with a `package!'
declaration) or dependency thereof that hasn't already been.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be installed."
(print! "> Installing & building packages...")
declaration) or dependency thereof that hasn't already been."
(print! (start "Installing & building packages..."))
(print-group!
(let ((n 0))
(dolist (package (hash-table-keys straight--recipe-cache))
@ -91,7 +70,7 @@ a list of packages that will be installed."
(local-repo)
(let ((existed-p (file-directory-p (straight--repos-dir package))))
(condition-case-unless-debug e
(and (straight-use-package (intern package) nil nil " ")
(and (straight-use-package (intern package) nil nil (make-string (1- (or doom-format-indent 1)) 32))
(not existed-p)
(file-directory-p (straight--repos-dir package))
(cl-incf n))
@ -104,17 +83,18 @@ a list of packages that will be installed."
t))))
(defun doom-packages-rebuild (&optional auto-accept-p all)
(defun doom-cli-packages-build (&optional force-p)
"(Re)build all packages."
(print! (start "(Re)building %spackages...") (if all "all " ""))
(print! (start "(Re)building %spackages...") (if force-p "all " ""))
(print-group!
(let ((n 0))
(if all
(if force-p
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(dolist (package (hash-table-keys straight--recipe-cache))
(straight-use-package
(intern package) nil (lambda (_) (cl-incf n) nil) " ")))
(intern package) nil (lambda (_) (cl-incf n) nil)
(make-string (1- (or doom-format-indent 1)) 32))))
(dolist (recipe (hash-table-values straight--recipe-cache))
(straight--with-plist recipe (package local-repo no-build)
(unless (or no-build (null local-repo))
@ -139,7 +119,9 @@ a list of packages that will be installed."
(lambda (&rest _) (cl-incf n)))
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(straight-use-package (intern package) nil nil " "))
(straight-use-package
(intern package) nil nil
(make-string (or doom-format-indent 0) 32)))
(straight--byte-compile-package recipe)
(dolist (dep (straight--get-dependencies package))
(when-let (recipe (gethash dep straight--recipe-cache))
@ -151,268 +133,107 @@ a list of packages that will be installed."
t))))
(defun doom--packages-remove-outdated-f (packages)
(async-start
`(lambda ()
(setq load-path ',load-path
doom-modules ',doom-modules
user-emacs-directory ',user-emacs-directory)
(condition-case e
(let (packages errors)
(load ,(concat doom-core-dir "core.el"))
(doom-initialize 'force)
(dolist (recipe ',group)
(when (straight--repository-is-available-p recipe)
(straight-vc-git--destructure recipe
(package local-repo nonrecursive upstream-remote upstream-repo upstream-host branch)
(condition-case e
(let ((default-directory (straight--repos-dir local-repo)))
;; HACK We normalize packages to avoid certain scenarios
;; where `straight-fetch-package' will create an
;; interactive popup prompting for action (which will
;; cause this async process to block indefinitely). We
;; can't use `straight-normalize-package' because could
;; create popup prompts too, so we do it manually:
(shell-command-to-string "git merge --abort")
(straight--get-call "git" "reset" "--hard" branch)
(straight--get-call "git" "clean" "-ffd")
(unless nonrecursive
(shell-command-to-string "git submodule update --init --recursive"))
(when upstream-repo
(let ((desired-url (straight-vc-git--encode-url upstream-repo upstream-host))
(actual-url (condition-case nil
(straight--get-call "git" "remote" "get-url" upstream-remote)
(error nil))))
(unless (straight-vc-git--urls-compatible-p actual-url desired-url)
(straight--get-call "git" "remote" "remove" upstream-remote)
(straight--get-call "git" "remote" "add" upstream-remote desired-url)
(straight--get-call "git" "fetch" upstream-remote))))
(straight-fetch-package package)
;; REVIEW Is there no better way to get this information?
(let ((n (length
(split-string
(straight--get-call "git" "rev-list" "--left-right" "HEAD..@{u}")
"\n" t)))
(pretime
(string-to-number
(shell-command-to-string "git log -1 --format=%at HEAD")))
(time
(string-to-number
;; HACK `straight--get-call' has a higher failure
;; rate when querying FETCH_HEAD; not sure why.
;; Doing this manually, with
;; `shell-command-to-string' works fine.
(shell-command-to-string "git log -1 --format=%at FETCH_HEAD"))))
(with-current-buffer (straight--process-get-buffer)
(with-silent-modifications
(print! (debug (autofill "%s") (indent 2 (buffer-string))))
(erase-buffer)))
(when (> n 0)
(push (list n pretime time recipe)
packages))))
(error
(push (list package e (string-trim (or (straight--process-get-output) "")))
errors))))))
(if errors
(cons 'error errors)
(cons 'ok (nreverse packages))))
(error
(cons 'error e))))))
(defun doom-packages-update (&optional auto-accept-p threads timeout)
"Updates packages.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be updated."
(print! (start "Scanning for outdated packages (this may take a while)..."))
(print-group!
(when timeout
(print! (info "Using %S as timeout value" timeout)))
(when threads
(print! (info "Limiting to %d thread(s)" threads)))
;; REVIEW Does this fail gracefully enough? Is it error tolerant?
;; TODO Add version-lock checks; don't want to spend all this effort on
;; packages that shouldn't be updated
(let* ((futures
;; REVIEW We can do better "thread" management here
(or (cl-loop for group
in (seq-partition (hash-table-values straight--repo-cache)
(/ (hash-table-count straight--repo-cache)
(or threads 8)))
for future = (doom--packages-remove-outdated-f group)
if (processp future)
collect (cons future group)
else
do (print! (warn "Failed to create thread for:\n\n%s\n\nReason: %s"
group future)))
(error! "Failed to create any threads")))
(total (length futures))
(timeout (or timeout 45)))
(condition-case-unless-debug e
(let (specs)
(while futures
(print! ". %.0f%%" (* (/ (- total (length futures))
(float total))
100))
(let ((time 0))
(catch 'timeout
(while (not (async-ready (caar futures)))
(when (> time timeout)
(print! (warn "A thread has timed out. The following packages were skipped: %s"
(mapconcat (lambda (p) (plist-get p :package))
(cdar futures)
", ")))
(throw 'timeout (pop futures)))
(sleep-for 1)
(when (cl-evenp time)
(print! "."))
(cl-incf time))
(cl-destructuring-bind (status . result)
(or (async-get (car (pop futures)))
(cons nil nil))
(cond ((null status)
(error "Thread returned an invalid result: %S" errors))
((eq status 'error)
(error "There were errors:\n\n%s"
(cond ((and (listp result)
(symbolp (car result)))
(prin1-to-string result))
((stringp result)
result)
((mapconcat (lambda (e)
(format! " - %s: %s" (yellow (car e)) (cdr e)))
result
"\n")))))
((eq status 'ok)
(print! (debug "Appended %S to package list") (or result "nothing"))
(appendq! specs result))
((error "Thread returned a non-standard status: %s\n\n%s"
status result)))))))
(print! ". 100%%")
(terpri)
(if-let (specs (delq nil specs))
(if (not
(or auto-accept-p
(y-or-n-p
(format!
"%s\n\nThere %s %d package%s available to update. Update them?"
(mapconcat
(lambda (spec)
(cl-destructuring-bind (n pretime time recipe) spec
(straight--with-plist recipe (package)
(format! "+ %-33s %s commit(s) behind %s -> %s"
(yellow package) (yellow n)
(format-time-string "%Y%m%d" pretime)
(format-time-string "%Y%m%d" time)))))
specs
"\n")
(if (cdr specs) "are" "is")
(length specs)
(if (cdr specs) "s" "")))))
(ignore (print! (info "Aborted update")))
(terpri)
(straight--make-package-modifications-available)
(defun doom-cli-packages-update ()
"Updates packages."
(print! (start "Updating packages (this may take a while)..."))
(let ((straight--packages-to-rebuild (make-hash-table :test #'equal))
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(dolist (spec specs)
(cl-destructuring-bind (n pretime time recipe) spec
(straight--with-plist recipe (local-repo package)
(let ((default-directory (straight--repos-dir local-repo)))
(print! (start "Updating %S") package)
(total (hash-table-count straight--repo-cache))
(i 1)
errors)
(print-group!
(dolist (recipe (hash-table-values straight--repo-cache))
(straight--with-plist recipe (package type local-repo)
(condition-case-unless-debug e
(let* ((default-directory (straight--repos-dir local-repo))
(commit (straight-vc-get-commit type local-repo)))
(if (not (straight-vc-fetch-from-remote recipe))
(print! (warn "(%d/%d) Failed to fetch %s" i total package))
(let ((output (straight--process-get-output)))
(straight-merge-package package)
;; HACK `straight-rebuild-package' doesn't pick up that
;; this package has changed, so we do it manually. Is
;; there a better way?
(let ((newcommit (straight-vc-get-commit type local-repo)))
(if (string= commit newcommit)
(print! (info "(%d/%d) %s is up-to-date") i total package)
(ignore-errors
(delete-directory (straight--build-dir package) 'recursive))
(puthash package t straight--packages-to-rebuild)
(cl-incf n))
(with-current-buffer (straight--process-get-buffer)
(with-silent-modifications
(print! (debug (autofill "%s") (indent 2 (buffer-string))))
(erase-buffer))))))
(doom--finalize-straight)
(doom-packages-rebuild auto-accept-p))
t)
(print! (success "No packages to update"))
nil))
(print! (success "(%d/%d) %s updated (%s -> %s)") i total package
(substring commit 0 7)
(substring newcommit 0 7))
(unless (string-empty-p output)
(print-group!
(print! (info "%s") output)
(when (eq type 'git)
(straight--call "git" "log" "--oneline" newcommit (concat "^" commit))
(print-group!
(print! "%s" (straight--process-get-output))))))))))
(cl-incf i))
(user-error
(signal 'user-error (error-message-string e)))
(error
(message "Output:\n%s" (straight--process-get-output))
(signal (car e) (error-message-string e)))))))
(print! (warn "(%d/%d) Encountered error with %s" i total package))
(print-group!
(print! (error "%s" e))
(print-group! (print! (info "%s" (straight--process-get-output)))))
(push package errors)))))
(when errors
(print! (error "There were %d errors, the offending packages are: %s")
(length errors) (string-join errors ", ")))
(if (hash-table-empty-p straight--packages-to-rebuild)
(ignore
(print! (success "All %d packages are up-to-date")
(hash-table-count straight--repo-cache)))
(let ((count (hash-table-count straight--packages-to-rebuild))
(packages (hash-table-keys straight--packages-to-rebuild)))
(sort packages #'string-lessp)
(doom--finalize-straight)
(doom-cli-packages-build)
(print! (success "Updated %d package(s)") count))
t))))
;;; PURGE (for the emperor)
(defun doom--prompt-p (list-fn list preamble postamble)
(or (y-or-n-p (format "%s%s\n\n%s"
(if preamble (concat preamble "\n\n") "")
(mapconcat list-fn list "\n")
(or postamble "")))
(user-error! "Aborted")))
(defun doom--prompt-columns-p (row-fn list preamble postamble)
(doom--prompt-p (lambda (row)
(mapconcat row-fn row ""))
(seq-partition (cl-sort (copy-sequence list) #'string-lessp)
3)
preamble
postamble))
(defun doom--packages-purge-build (build)
(defun doom--cli-packages-purge-build (build)
(let ((build-dir (straight--build-dir build)))
(print! (start "Purging build/%s..." build))
(delete-directory build-dir 'recursive)
(if (file-directory-p build-dir)
(ignore (print! (error "Failed to purg build/%s" build)))
(print! (success "Purged build/%s" build))
t)))
(defun doom--packages-purge-builds (builds &optional auto-accept-p)
(defun doom--cli-packages-purge-builds (builds)
(if (not builds)
(progn (print! (info "No builds to purge"))
0)
(or auto-accept-p
(doom--prompt-columns-p
(lambda (p) (format " + %-20.20s" p)) builds nil
(format! "Found %d orphaned package builds. Purge them?"
(length builds))))
(length
(delq nil (mapcar #'doom--packages-purge-build builds)))))
(delq nil (mapcar #'doom--cli-packages-purge-build builds)))))
(defun doom--packages-regraft-repo (repo)
(defun doom--cli-packages-regraft-repo (repo)
(let ((default-directory (straight--repos-dir repo)))
(if (not (file-directory-p ".git"))
(ignore (print! (warn "repos/%s is not a git repo, skipping" repo)))
(print! (debug "Regrafting repos/%s..." repo))
(let ((before-size (doom-directory-size default-directory)))
(straight--call "git" "reset" "--hard")
(straight--call "git" "clean" "--ffd")
(straight--call "git" "replace" "--graft" "HEAD")
(straight--call "git" "clean" "-ffd")
(if (not (car (straight--call "git" "replace" "--graft" "HEAD")))
(print! (info "repos/%s is already compact" repo))
(straight--call "git" "gc")
(print! (debug "%s" (straight--process-get-output)))
(print! (success "Regrafted repos/%s" repo))
(print! (success "Regrafted repos/%s (from %0.1fKB to %0.1fKB)")
repo before-size (doom-directory-size default-directory))
(print-group! (print! "%s" (straight--process-get-output)))))
t)))
(defun doom--packages-regraft-repos (repos &optional auto-accept-p)
(defun doom--cli-packages-regraft-repos (repos)
(if (not repos)
(progn (print! (info "No repos to regraft"))
0)
(or auto-accept-p
(y-or-n-p (format! "Preparing to regraft all %d repos. Continue?"
(length repos)))
(user-error! "Aborted!"))
(if (executable-find "du")
(cl-destructuring-bind (status . size)
(doom-sh "du" "-sh" (straight--repos-dir))
(prog1 (delq nil (mapcar #'doom--packages-regraft-repo repos))
(cl-destructuring-bind (status . newsize)
(doom-sh "du" "-sh" (straight--repos-dir))
(print! (success "Finshed regrafted. Size before: %s and after: %s"
(car (split-string size "\t"))
(car (split-string newsize "\t")))))))
(delq nil (mapcar #'doom--packages-regraft-repo repos)))))
(let ((before-size (doom-directory-size (straight--repos-dir))))
(prog1 (print-group! (delq nil (mapcar #'doom--cli-packages-regraft-repo repos)))
(let ((after-size (doom-directory-size (straight--repos-dir))))
(print! (success "Finished regrafting. Size before: %0.1fKB and after: %0.1fKB (%0.1fKB)")
before-size after-size
(- after-size before-size)))))))
(defun doom--packages-purge-repo (repo)
(print! (debug "Purging repos/%s..." repo))
(defun doom--cli-packages-purge-repo (repo)
(let ((repo-dir (straight--repos-dir repo)))
(delete-directory repo-dir 'recursive)
(ignore-errors
@ -422,19 +243,14 @@ a list of packages that will be updated."
(print! (success "Purged repos/%s" repo))
t)))
(defun doom--packages-purge-repos (repos &optional auto-accept-p)
(defun doom--cli-packages-purge-repos (repos)
(if (not repos)
(progn (print! (info "No repos to purge"))
0)
(or auto-accept-p
(doom--prompt-columns-p
(lambda (p) (format " + %-20.20s" p)) repos nil
(format! "Found %d orphaned repos. Purge them?"
(length repos))))
(length
(delq nil (mapcar #'doom--packages-purge-repo repos)))))
(delq nil (mapcar #'doom--cli-packages-purge-repo repos)))))
(defun doom--packages-purge-elpa (&optional auto-accept-p)
(defun doom--cli-packages-purge-elpa ()
(unless (bound-and-true-p package--initialized)
(package-initialize))
(let ((packages (cl-loop for (package desc) in package-alist
@ -444,16 +260,11 @@ a list of packages that will be updated."
(if (not package-alist)
(progn (print! (info "No ELPA packages to purge"))
0)
(doom--prompt-columns-p
(lambda (p) (format " + %-20.20s" p))
(mapcar #'car packages) nil
(format! "Found %d orphaned ELPA packages. Purge them?"
(length package-alist)))
(mapc (doom-rpartial #'delete-directory 'recursive)
(mapcar #'cdr packages))
(length packages))))
(defun doom-packages-purge (&optional elpa-p builds-p repos-p auto-accept-p)
(defun doom-cli-packages-purge (&optional elpa-p builds-p repos-p regraft-repos-p)
"Auto-removes orphaned packages and repos.
An orphaned package is a package that isn't a primary package (i.e. doesn't have
@ -461,10 +272,7 @@ a `package!' declaration) or isn't depended on by another primary package.
If BUILDS-P, include straight package builds.
If REPOS-P, include straight repos.
If ELPA-P, include packages installed with package.el (M-x package-install).
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be removed."
If ELPA-P, include packages installed with package.el (M-x package-install)."
(print! (start "Searching for orphaned packages to purge (for the emperor)..."))
(cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort))
@ -479,18 +287,20 @@ a list of packages that will be removed."
(print-group!
(if (not builds-p)
(print! (info "Skipping builds"))
(and (/= 0 (doom--packages-purge-builds builds-to-purge auto-accept-p))
(and (/= 0 (doom--cli-packages-purge-builds builds-to-purge))
(setq success t)
(straight-prune-build-cache)))
(if (not elpa-p)
(print! (info "Skipping elpa packages"))
(and (/= 0 (doom--packages-purge-elpa auto-accept-p))
(and (/= 0 (doom--cli-packages-purge-elpa))
(setq success t)))
(if (not repos-p)
(print! (info "Skipping repos"))
(and (/= 0 (doom--packages-purge-repos repos-to-purge auto-accept-p))
(setq success t))
(and (doom--packages-regraft-repos repos-to-regraft auto-accept-p)
(and (/= 0 (doom--cli-packages-purge-repos repos-to-purge))
(setq success t)))
(if (not regraft-repos-p)
(print! (info "Skipping regrafting"))
(and (doom--cli-packages-regraft-repos repos-to-regraft)
(setq success t)))
(when success
(doom--finalize-straight)

View file

@ -7,17 +7,21 @@
runemacs-binary-path
emacs-binary-path)))
(defcli! test (&rest targets)
"Run Doom unit tests."
(let (files error)
:bare t
(doom-initialize 'force)
(require 'ansi-color)
(let (files error read-files)
(unless targets
(setq targets
(cons doom-core-dir
(cl-remove-if-not
(lambda (path) (file-in-directory-p path doom-emacs-dir))
(doom-rpartial #'file-in-directory-p doom-emacs-dir)
;; Omit `doom-private-dir', which is always first
(let (doom-modules)
(load! "test/init" doom-core-dir)
(load (expand-file-name "test/init" doom-core-dir) nil t)
(cdr (doom-module-load-path)))))))
(while targets
(let ((target (pop targets)))
@ -29,47 +33,72 @@
(appendq! files (nreverse (doom-glob target "test/test-*.el"))))
((file-exists-p target)
(push target files)))))
(setenv "DOOMLOCALDIR" (concat doom-local-dir "test/"))
(setenv "DOOMDIR" (concat doom-core-dir "test/"))
(with-temp-buffer
(print! (start "Bootstrapping test environment, if necessary..."))
(if (zerop
(call-process
(cl-destructuring-bind (status . output)
(doom-exec-process
(doom--emacs-binary)
nil t nil "--batch"
"--eval" (prin1-to-string
"--batch"
"--eval"
(prin1-to-string
`(progn
(setq doom-emacs-dir ,doom-emacs-dir
doom-local-dir ,(concat doom-local-dir "test/")
doom-private-dir ,(concat doom-core-dir "test/"))
(setq user-emacs-directory ,doom-emacs-dir
doom-auto-accept t)
(require 'core ,(locate-library "core"))
(require 'core-cli)
(doom-initialize 'force)
(doom-initialize-modules)
(require 'core-cli)
(doom-reload-core-autoloads 'force)
(when (doom-packages-install 'auto-accept)
(doom-reload-package-autoloads 'force))))))
(message "%s" (buffer-string))
(message "%s" (buffer-string))
(error "Failed to bootstrap unit tests")))
(doom-cli-reload-core-autoloads 'force)
(when (doom-cli-packages-install)
(doom-cli-reload-package-autoloads 'force)))))
(unless (zerop status)
(error "Failed to bootstrap unit tests"))))
(with-temp-buffer
(dolist (file files)
(if (doom-file-cookie-p file "if" t)
(with-temp-buffer
(unless
(zerop
(apply #'call-process
(cl-destructuring-bind (_status . output)
(apply #'doom-exec-process
(doom--emacs-binary)
nil t nil "--batch"
(append (list
"-L" doom-core-dir
"-l" "core"
"-l" (concat doom-core-dir "test/helpers.el"))
(when (file-in-directory-p file doom-modules-dir)
"--batch"
"-l" (concat doom-core-dir "core.el")
"-l" (concat doom-core-dir "test/helpers.el")
(append (when (file-in-directory-p file doom-modules-dir)
(list "-f" "doom-initialize-core"))
(list
"-l" file
"-f" "buttercup-run"))))
(setq error t))
(message "%s" (buffer-string)))
(list "-l" file
"-f" "buttercup-run")))
(insert (replace-regexp-in-string ansi-color-control-seq-regexp "" output))
(push file read-files))
(print! (info "Ignoring %s" (relpath file)))))
(if error
(user-error "A test failed")
(let ((total 0)
(total-failed 0)
(i 0))
(print! "\n----------------------------------------\nTests finished")
(print-group!
(goto-char (point-min))
(while (re-search-forward "^Ran \\([0-9]+\\) specs, \\([0-9]+\\) failed," nil t)
(let ((ran (string-to-number (match-string 1)))
(failed (string-to-number (match-string 2))))
(when (> failed 0)
(terpri)
(print! (warn "(%s) Failed %d/%d tests")
(path (nth i read-files))
failed ran)
(save-excursion
(print-group!
(print!
"%s" (string-trim
(buffer-substring
(match-beginning 0)
(dotimes (_ failed (point))
(search-backward "========================================"))))))))
(cl-incf total ran)
(cl-incf total-failed failed)
(cl-incf i))))
(terpri)
(if (= total-failed 0)
(print! (success "Ran %d tests successfully." total total-failed))
(print! (error "Ran %d tests, %d failed") total total-failed)
(kill-emacs 1)))
t)))

View file

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

View file

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

View file

@ -3,41 +3,6 @@
(require 'cl-lib)
(require 'subr-x)
;; DEPRECATED Polyfills
(unless EMACS26+
(with-no-warnings
;; `kill-current-buffer' was introduced in Emacs 26
(defalias 'kill-current-buffer #'kill-this-buffer)
;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we alias
;; them for 25 users.
(defalias 'if-let* #'if-let)
(defalias 'when-let* #'when-let)
;; `mapcan' was introduced in 26.1. `cl-mapcan' isn't a perfect replacement,
;; but it's close enough.
(defalias 'mapcan #'cl-mapcan)
(defun alist-get (key alist &optional default remove testfn)
"Return the value associated with KEY in ALIST.
If KEY is not found in ALIST, return DEFAULT.
Use TESTFN to lookup in the alist if non-nil. Otherwise, use `assq'.
This is a generalized variable suitable for use with `setf'.
When using it to set a value, optional argument REMOVE non-nil
means to remove KEY from ALIST if the new value is `eql' to DEFAULT."
(ignore remove) ;;Silence byte-compiler.
(let ((x (if (not testfn)
(assq key alist)
;; In Emacs<26, `assoc' has no testfn arg, so we have to
;; implement it ourselves
(if testfn
(cl-loop for entry in alist
if (funcall testfn key entry)
return entry)
(assoc key alist)))))
(if x (cdr x) default)))))
;;
;;; Helpers

View file

@ -161,15 +161,20 @@ missing) and shouldn't be deleted.")
(push func options)
(print! "%2s) %s" (length options) desc)))))
(terpri)
(let ((answer
(read-number (format! "How to proceed? (%s) "
(let ((options (nreverse options))
answer fn)
(while
(not
(setq
fn (ignore-errors
(nth (1- (setq answer
(read-number
(format! "How to proceed? (%s) "
(mapconcat #'number-to-string
(number-sequence 1 (length options))
", "))))
fn)
(setq options (nreverse options))
(while (not (setq fn (nth (1- answer) options)))
(print! "%s is not a valid answer, try again." answer))
", ")))))
options))))
(print! (warn "%s is not a valid answer, try again.") answer))
(funcall fn))))))
@ -249,7 +254,7 @@ necessary package metadata is initialized and available for them."
;;; Module package macros
(cl-defmacro package!
(name &rest plist &key built-in _recipe disable ignore _freeze)
(name &rest plist &key built-in recipe ignore _disable _freeze)
"Declares a package and how to install it (if applicable).
This macro is declarative and does not load nor install packages. It is used to
@ -277,6 +282,13 @@ Accepts the following properties:
Returns t if package is successfully registered, and nil if it was disabled
elsewhere."
(declare (indent defun))
(when (and recipe (keywordp (car-safe recipe)))
(plist-put! plist :recipe `(quote ,recipe)))
(when built-in
(when (and (not ignore) (equal built-in '(quote prefer)))
(setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path)))
(plist-delete! plist :built-in)
(plist-put! plist :ignore built-in))
`(let* ((name ',name)
(plist (cdr (assq name doom-packages))))
(let ((module-list (plist-get plist :modules))
@ -287,33 +299,23 @@ elsewhere."
(list module)
nil))))
;; Handle :built-in
(let ((built-in ,built-in))
(unless ,ignore
(when built-in
(doom-log "Ignoring built-in package %S" name)
(when (eq built-in 'prefer)
(setq built-in (locate-library (symbol-name name) nil doom--initial-load-path))))
(plist-put! plist :ignore built-in)))
;; DEPRECATED Translate :fetcher to :host
(with-plist! plist (recipe)
(with-plist! recipe (fetcher)
(when fetcher
(message "%s\n%s"
(format "WARNING: The :fetcher property was used for the %S package."
name)
"This property is deprecated. Replace it with :host.")
(plist-put! recipe :host fetcher)
(plist-delete! recipe :fetcher))
(plist-put! plist :recipe recipe)))
(doplist! ((prop val) ',plist plist)
(doplist! ((prop val) (list ,@plist) plist)
(unless (null val)
(plist-put! plist prop val)))
;; Some basic key validation; error if you're not using a valid key
(condition-case e
(cl-destructuring-bind
(&key _local-repo _files _flavor _no-build
_type _repo _host _branch _remote _nonrecursive _fork _depth)
(plist-get plist :recipe))
(error
(signal 'doom-package-error
(cons ,(symbol-name name)
(error-message-string e)))))
(setf (alist-get name doom-packages) plist)
(if (not ,disable) t
(if (not (plist-get plist :disable)) t
(doom-log "Disabling package %S" name)
(cl-pushnew name doom-disabled-packages)
nil)))

View file

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

View file

@ -366,9 +366,7 @@ treat Emacs as a non-application window."
(setq ansi-color-for-comint-mode t)
(use-package! compile
:defer t
:config
(after! compile
(setq compilation-always-kill t ; kill compilation process before starting another
compilation-ask-about-save nil ; save all buffers on `compile'
compilation-scroll-output 'first-error)
@ -376,9 +374,7 @@ treat Emacs as a non-application window."
(add-hook 'compilation-filter-hook #'doom-apply-ansi-color-to-compilation-buffer-h))
(use-package! ediff
:defer t
:config
(after! ediff
(setq ediff-diff-options "-w" ; turn off whitespace checking
ediff-split-window-function #'split-window-horizontally
ediff-window-setup-function #'ediff-setup-windows-plain)
@ -512,79 +508,6 @@ treat Emacs as a non-application window."
(defun doom-enable-line-numbers-h () (display-line-numbers-mode +1))
(defun doom-disable-line-numbers-h () (display-line-numbers-mode -1))
;; DEPRECATED `nlinum' is used for Emacs 25 users; 26+ has native line numbers.
(use-package! nlinum
;; Line number column. A faster (or equivalent, in the worst case) line number
;; plugin than `linum-mode'.
:unless EMACS26+
:defer t
:init
(defvar doom-line-number-lpad 4
"How much padding to place before line numbers.")
(defvar doom-line-number-rpad 1
"How much padding to place after line numbers.")
(defvar doom-line-number-pad-char 32
"Character to use for padding line numbers.
By default, this is a space character. If you use `whitespace-mode' with
`space-mark', the whitespace in line numbers will be affected (this can look
ugly). In this case, you can change this to ?\u2002, which is a unicode
character that looks like a space that `whitespace-mode' won't affect.")
:config
(setq nlinum-highlight-current-line t)
;; Fix lingering hl-line overlays (caused by nlinum)
(add-hook! 'hl-line-mode-hook
(remove-overlays (point-min) (point-max) 'face 'hl-line))
(defun doom-nlinum-format-fn (line _width)
"A more customizable `nlinum-format-function'. See `doom-line-number-lpad',
`doom-line-number-rpad' and `doom-line-number-pad-char'. Allows a fix for
`whitespace-mode' space-marks appearing inside the line number."
(let ((str (number-to-string line)))
(setq str (concat (make-string (max 0 (- doom-line-number-lpad (length str)))
doom-line-number-pad-char)
str
(make-string doom-line-number-rpad doom-line-number-pad-char)))
(put-text-property 0 (length str) 'face
(if (and nlinum-highlight-current-line
(= line nlinum--current-line))
'nlinum-current-line
'linum)
str)
str))
(setq nlinum-format-function #'doom-nlinum-format-fn)
(add-hook! 'nlinum-mode-hook
(defun doom-init-nlinum-width-h ()
"Calculate line number column width beforehand (optimization)."
(setq nlinum--width
(length (save-excursion (goto-char (point-max))
(format-mode-line "%l")))))))
(use-package! nlinum-hl
;; Fixes disappearing line numbers in nlinum and other quirks
:unless EMACS26+
:after nlinum
:config
;; With `markdown-fontify-code-blocks-natively' enabled in `markdown-mode',
;; line numbers tend to vanish next to code blocks.
(advice-add #'markdown-fontify-code-block-natively
:after #'nlinum-hl-do-markdown-fontify-region)
;; When using `web-mode's code-folding an entire range of line numbers will
;; vanish in the affected area.
(advice-add #'web-mode-fold-or-unfold :after #'nlinum-hl-do-generic-flush)
;; Changing fonts can leave nlinum line numbers in their original size; this
;; forces them to resize.
(add-hook 'after-setting-font-hook #'nlinum-hl-flush-all-windows))
(use-package! nlinum-relative
:unless EMACS26+
:defer t
:config
(setq nlinum-format " %d ")
(add-hook 'evil-mode-hook #'nlinum-relative-setup-evil))
;;
;;; Theme & font

View file

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

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! hide-mode-line)
(package! highlight-numbers)
;; Some early 26.x builds of Emacs do not have `display-line-numbers' yet, so
;; check for it instead of Emacs' version.
(unless (locate-library "display-line-numbers")
(package! nlinum)
(package! nlinum-hl)
(package! nlinum-relative))
(package! rainbow-delimiters)
(package! restart-emacs)

View file

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

View file

@ -19,11 +19,23 @@
(kill-buffer c)
(kill-buffer d))
(describe "buffer-list"
(describe "buffer lists"
(describe "doom-buffer-list"
(it "should only see four buffers"
(expect (doom-buffer-list) :to-contain-items (list a b c d))))
(expect (doom-buffer-list) :to-contain-items (list a b c d)))))
(describe "project-buffer-list"
;; TODO predicate tests
(xdescribe "predicate functions"
(describe "doom-dired-buffer-p")
(describe "doom-special-buffer-p")
(describe "doom-temp-buffer-p")
(describe "doom-visible-buffer-p")
(describe "doom-buried-buffer-p")
(describe "doom-non-file-visiting-buffer-p")
(describe "doom-dired-buffer-p")
(describe "doom-buffer-frame-predicate"))
(describe "doom-project-buffer-list"
:var (projectile-projects-cache-time projectile-projects-cache)
(before-all (require 'projectile))
(after-all (unload-feature 'projectile t))
@ -47,7 +59,7 @@
(expect (doom-project-buffer-list)
:to-have-same-items-as (buffer-list)))))
(describe "fallback-buffer"
(describe "doom-fallback-buffer"
(it "returns a live buffer"
(expect (buffer-live-p (doom-fallback-buffer))))
@ -56,12 +68,22 @@
(describe "real buffers"
(before-each
(doom-set-buffer-real a t)
(with-current-buffer b (setq buffer-file-name "x"))
(with-current-buffer c (rename-buffer "*C*")))
(describe "real-buffer-p"
(describe "doom-mark-buffer-as-real-h"
(with-current-buffer a
(doom-mark-buffer-as-real-h)
(expect (buffer-local-value 'doom-real-buffer-p a))))
(describe "doom-set-buffer-real"
(it "sets `doom-real-buffer-p' buffer-locally"
(doom-set-buffer-real a t)
(expect (buffer-local-value 'doom-real-buffer-p a))))
(describe "doom-real-buffer-p"
(it "returns t for buffers manually marked real"
(doom-set-buffer-real a t)
(expect (doom-real-buffer-p a)))
(it "returns t for file-visiting buffers"
(expect (doom-real-buffer-p b)))
@ -69,7 +91,16 @@
(expect (doom-real-buffer-p c) :to-be nil)
(expect (doom-real-buffer-p d) :to-be nil)))
(describe "real-buffer-list"
(describe "doom-unreal-buffer-p"
(it "returns t for unreal buffers"
(expect (doom-unreal-buffer-p c))
(expect (doom-unreal-buffer-p d)))
(it "returns nil for real buffers"
(doom-set-buffer-real a t)
(expect (not (doom-unreal-buffer-p a)))
(expect (not (doom-unreal-buffer-p b)))))
(describe "doom-real-buffer-list"
(it "returns only real buffers"
(expect (doom-real-buffer-list) :to-contain-items (list a b)))))
@ -82,25 +113,31 @@
(split-window)
(switch-to-buffer b))
(describe "doom-matching-buffers"
(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"
(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"
(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"
(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"
(expect (doom-visible-windows)
: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
(split-window) (switch-to-buffer b)
(split-window) (switch-to-buffer a))
@ -111,7 +148,13 @@
(expect (length (doom-visible-windows)) :to-be 1)))
;; TODO
(xdescribe "kill-all-buffers")
(xdescribe "kill-other-buffers")
(xdescribe "kill-matching-buffers")
(xdescribe "cleanup-session")))
(xdescribe "doom-fixup-windows")
(xdescribe "doom-kill-buffer-fixup-windows")
(xdescribe "doom-kill-buffers-fixup-windows"))
(xdescribe "commands"
(describe "doom/kill-all-buffers")
(describe "doom/kill-other-buffers")
(describe "doom/kill-matching-buffers")
(describe "doom/kill-buried-buffers")
(describe "doom/kill-project-buffers"))))

View file

@ -5,11 +5,6 @@
(load! "autoload/files" doom-core-dir)
(xdescribe "doom-glob")
(xdescribe "doom-path")
(xdescribe "doom-dir")
(xdescribe "doom-files-in")
(describe "library"
(describe "file-exists-p!"
(it "is a (quasi) drop-in replacement for `file-exists-p'"
@ -96,7 +91,16 @@
(getfilename))
"LICENSE")
doom-emacs-dir)
:to-equal (expand-file-name "LICENSE" doom-emacs-dir))))))
:to-equal (expand-file-name "LICENSE" doom-emacs-dir)))))
;; TODO
(xdescribe "doom-glob")
(xdescribe "doom-path")
(xdescribe "doom-dir")
(xdescribe "doom-files-in")
(xdescribe "doom-file-size")
(xdescribe "doom-directory-size")
(xdescribe "doom-file-cookie-p"))
(describe "interactive file operations"
:var (src dest projectile-projects-cache-time projectile-projects-cache)
@ -149,4 +153,12 @@
(expect (file-exists-p existing) :to-be nil))
(it "prompts to delete any existing file"
(quiet! (doom/delete-this-file existing))
(expect 'y-or-n-p :to-have-been-called-times 1)))))
(expect 'y-or-n-p :to-have-been-called-times 1))))
(xdescribe "sudo {this,find} file"
(before-each
(spy-on 'find-file :and-return-value nil)
(spy-on 'find-alternate-file :and-return-value nil))
(describe "doom/sudo-find-file")
(describe "doom/sudo-this-file")))

View file

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

View file

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

View file

@ -1,7 +1,23 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/test-core-modules.el
;;;###if nil
;; (require 'core-modules)
(xdescribe "core-modules"
(require 'core-modules)
(xdescribe "core-modules")
(describe "doom!")
(describe "doom-modules")
(describe "doom-module-p")
(describe "doom-module-get")
(describe "doom-module-put")
(describe "doom-module-set")
(describe "doom-module-path")
(describe "doom-module-locate-path")
(describe "doom-module-from-path")
(describe "doom-module-load-path")
(describe "require!")
(describe "featurep!")
(describe "after!")
(describe "use-package!")
(describe "use-package-hook!"))

View file

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

View file

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

View file

@ -252,6 +252,76 @@ It is integrated into Helpful, in Doom.
:desc "Eval expression" ";" #'eval-expression)
#+END_SRC
These are side-by-side comparisons, showing how to bind keys with and without
~map!~:
#+BEGIN_SRC elisp :eval no
;; bind a global key
(global-set-key (kbd "C-x y") #'do-something)
(map! "C-x y" #'do-something)
;; bind a key on a keymap
(define-key emacs-lisp-mode (kbd "C-c p") #'do-something)
(map! :map emacs-lisp-mode "C-c p" #'do-something)
;; unbind a key defined elsewhere
(define-key lua-mode-map (kbd "SPC m b") nil)
(map! :map lua-mode-map "SPC m b" nil)
;; bind multiple keys
(global-set-key "C-x x" #'do-something)
(global-set-key "C-x y" #'do-something-else)
(global-set-key "C-x z" #'do-another-thing)
(map! "C-x x" #'do-something
"C-x y" #'do-something-else
"C-x z" #'do-another-thing)
;; bind global keys in normal mode
(evil-define-key* 'normal 'global
(kbd "C-x x") #'do-something
(kbd "C-x y") #'do-something-else
(kbd "C-x z") #'do-another-thing)
(map! :n "C-x x" #'do-something
:n "C-x y" #'do-something-else
:n "C-x z" #'do-another-thing)
;; or on a deferred keymap
(evil-define-key 'normal emacs-lisp-mode-map
(kbd "C-x x") #'do-something
(kbd "C-x y") #'do-something-else
(kbd "C-x z") #'do-another-thing)
(map! :map emacs-lisp-mode-map
:n "C-x x" #'do-something
:n "C-x y" #'do-something-else
:n "C-x z" #'do-another-thing)
;; or multiple maps
(dolist (map (list emacs-lisp-mode go-mode-map ivy-minibuffer-map))
(evil-define-key '(normal insert) map
"a" #'a
"b" #'b
"c" #'c))
(map! :map (emacs-lisp-mode go-mode-map ivy-minibuffer-map)
:ni "a" #'a
:ni "b" #'b
:ni "c" #'c)
;; or in multiple states (order of states doesn't matter)
(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
(evil-define-key* 'insert emacs-lisp-mode-map (kbd "C-x x") #'do-something-else)
(evil-define-key* '(visual normal insert emacs) emacs-lisp-mode-map (kbd "C-x z") #'do-another-thing)
(map! :map emacs-lisp-mode
:nv "C-x x" #'do-something ; normal+visual
:i "C-x y" #'do-something-else ; insert
:vnie "C-x z" #'do-another-thing) ; visual+normal+insert+emacs
;; You can nest map! calls:
(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
(evil-define-key* 'normal go-lisp-mode-map (kbd "C-x x") #'do-something-else)
(map! (:map emacs-lisp-mode :nv "C-x x" #'do-something)
(:map go-lisp-mode :n "C-x x" #'do-something-else))
#+END_SRC
*** package!
#+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources

View file

@ -59,8 +59,8 @@ things you should try first:
+ Make sure your configuration (or Doom Emacs) is *not* byte-compiled. Run ~doom
clean~ to ensure it isn't. *Byte-compilation interferes with debugging!*
+ Run ~bin/doom refresh -f~ to ensure all plugins are installed and autoload
files generated.
+ Run ~bin/doom refresh~ to ensure all plugins are installed and autoload files
generated.
+ Run ~bin/doom doctor~ to diagnose common issues with your system.
+ Check [[file:faq.org::*Common%20Issues][Common Issues]] in the FAQ to see if yours is a known issue.
+ If you happen to know what module(s) are relevant to your issue, check their

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.
** How do I use Doom alongside other Emacs configs?
I recommend [[https://github.com/plexus/chemacs][Chemacs]]. You can think of it as a bootloader for Emacs. You'll [[file:getting_started.org::Alongside%20other%20Emacs%20configs%20(with%20Chemacs)][find
I recommend [[https://github.com/plexus/chemacs][Chemacs]]. You can think of it as a bootloader for Emacs. You'll [[file:getting_started.org::*Alongside other Emacs configs (with Chemacs)][find
instructions on how to use it with Doom in the user manual]].
If you only want to try it out without affecting your current config, it is safe
@ -439,45 +439,7 @@ summons to fight for custody of your kids.
Also, Doom's fast yo.
** What is the meaning behind Doom's naming conventions?
Doom has a number of naming conventions that it uses in addition to the standard
lisp conventions. Third party packages may use their own conventions as well,
but this guide focuses only on what Doom Emacs uses:
*** Lisp Naming Conventions
The lisp conventions are simple. Symbols follow ~NAMESPACE-SYMBOLNAME~ for
public variables/functions (e.g. ~bookmark-default-file~ or
~electric-indent-mode~) and ~NAMESPACE--SYMBOLNAME~ for private ones (e.g.
~byte-compile--lexical-environment~ and ~yas--tables~).
~NAMESPACE~ is usually the name of the containing file or package. E.g. the
~company~ plugin prefixes all its variables/functions with ~company-~.
*** Doom Naming Conventions
+ ~doom/NAME~ or ~+MODULE/NAME~ :: Denotes a public command designed to be used
interactively, via =M-x= or a keybinding. e.g. ~doom/info~, ~+popup/other~,
~+ivy/rg~.
+ ~doom:NAME~ :: A public evil operator, motion or command. e.g. ~+evil:align~,
~+ivy:rg~.
+ ~doom-[-]NAME-h~ or ~+MODULE-[-]NAME-h~ :: A non-interactive function meant to
be used (exclusively) as a hook. e.g. ~+cc-fontify-constants-h~,
~+flycheck-buffer-h~.
+ ~doom-[-]NAME-a~ or ~+MODULE-[-]NAME-a~ :: Functions designed to be used as
advice for other functions. e.g. ~doom-set-jump-a~,
~doom--fix-broken-smie-modes-a~, ~+org--babel-lazy-load-library-a~
+ ~doom-[-]NAME-fn~ or ~+MODULE-[-]NAME-fn~ :: Indicates an [[https://en.wikipedia.org/wiki/Strategy_pattern][strategy]] function. A
good rule of thumb for what makes a strategy function is: is it
interchangeable? Can it be replaced with another function with a matching
signature? e.g. ~+lookup-dumb-jump-backend-fn~, ~+magit-display-buffer-fn~,
~+workspaces-set-project-action-fn~
+ ~abc!~ :: A public Doom "autodef" function or macro. An autodef should always
be defined, even if its containing module is disabled (i.e. they will not
throw a void-function error). The purpose of this is to avoid peppering module
configs with conditionals or `after!` blocks before using their APIs. They
should noop if their module is disabled, and should be zero-cost in the case
their module is disabled.
Autodefs usually serve to configure Doom or a module. e.g. ~after!~,
~set-company-backends!~, ~set-evil-initial-state!~
You'll find [[file:contributing.org::*Conventions][an overview of Doom's code conventions]] in the [[file:contributing.org][contributing guide]].
** How can I contribute to/support Doom?
Take a look at the [[file:contributing.org][Contributing guide]].
@ -503,7 +465,11 @@ Canonically, your private config is kept in =~/.doom.d/= or =~/.config/doom/=.
Doom will prioritize =~/.config/doom=, if it exists. This directory is referred
to as your ~$DOOMDIR~.
TL;DR. Put all your private configuration in =$DOOMDIR/config.el=.
Your private config is typically comprised of an =init.el=, =config.el= and
=packages.el= file. Put all your config in =config.el=, install packages by
adding ~package!~ declarations to =packagse.el=, and enable/disable modules in
you ~doom!~ block, which should have been created in your =init.el= when you
first ran ~doom install~.
Check out the [[file:getting_started.org::Customize][Customize section]] in the [[file:getting_started.org][Getting Started]] guide for details.
@ -523,7 +489,7 @@ semicolons:
Remember to run ~bin/doom refresh~ afterwards, on the command line, to sync your
module list with Doom.
You can find a comprehensive list of modules in the [[file:../modules/README.org][Module Index]].
You can find a comprehensive list of modules in the [[file:index.org::*Module list][Module Index]].
** How do I install a package from ELPA?
Add a ~package!~ declaration to =~/.doom.d/packages.el= for each package you
@ -539,8 +505,8 @@ You'll find more information in the "[[file:getting_started.org::*Installing%20p
Started]] guide.
** How do I install a package from github/another source?
The ~package!~ macro can be passed a MELPA style ~:recipe~, allowing you to
install packages from just about anywhere:
The ~package!~ macro can be passed a MELPA style recipe, allowing you to install
packages from just about anywhere:
#+BEGIN_SRC elisp
(package! evil :recipe (:host github :repo "hlissner/my-evil-fork"))
@ -562,14 +528,15 @@ You'll find more information in the "[[file:getting_started.org::*Installing%20p
section of the [[file:getting_started.org][Getting Started]] guide.
** How do I change where an existing package is installed from?
~package!~ declarations in your private config have precedence over modules
(even your own). Simply add a new one for that package with the new recipe.
~package!~ declarations in your private =packages.el= file have precedence over
modules (even your own). Simply add a new one for that package with the new
recipe.
You'll find more information in the "[[file:getting_started.org::*Changing%20a%20built-in%20recipe%20for%20a%20package][Changing a built-in recipe for a package]]"
section of the [[file:getting_started.org][Getting Started]] guide.
** How do I disable a package completely?
The ~package!~ macro has a ~:disable~ property:
With the ~package!~ macro's ~:disable~ property:
#+BEGIN_SRC elisp
;;; in DOOMDIR/packages.el
@ -630,8 +597,8 @@ At the moment, the only difference between the two is that ~doom-theme~ is
loaded when Emacs has finished initializing at startup and ~load-theme~ loads
the theme immediately. Which you choose depends on your needs, but I recommend
setting ~doom-theme~ because, if I later discover a better way to load themes, I
can easily change how Doom uses ~doom-theme~, but I can't control how you use
the ~load-theme~ function.
can easily change how Doom uses ~doom-theme~, but I can't (easily) control how
you use the ~load-theme~ function.
*** Installing a third party theme
To install a theme from a third party plugin, say, [[https://github.com/bbatsov/solarized-emacs][solarized]], you need only
@ -670,60 +637,12 @@ e.g.
#+END_SRC
** How do I bind my own keys (or change existing ones)?
The ~map!~ macro is recommended; it is a convenience macro that acts as a
wrapper around ~define-key~, ~global-set-key~, ~local-set-key~ and
~evil-define-key~.
The ~map!~ macro is recommended; it is a convenience macro that wraps around
Emacs' (and evil's) keybinding API, i.e. ~define-key~, ~global-set-key~,
~local-set-key~ and ~evil-define-key~.
#+BEGIN_SRC emacs-lisp
;; bind a global key
(global-set-key (kbd "C-x y") #'do-something)
(map! "C-x y" #'do-something)
;; bind a key on a keymap
(define-key emacs-lisp-mode (kbd "C-c p") #'do-something)
(map! :map emacs-lisp-mode "C-c p" #'do-something)
;; unbind a key defined elsewhere
(define-key lua-mode-map (kbd "SPC m b") nil)
(map! :map lua-mode-map "SPC m b" nil)
;; bind multiple keys
(map! "C-x x" #'do-something
"C-x y" #'do-something-else
"C-x z" #'do-another-thing)
#+END_SRC
Evil users can also define keys in particular evil states:
#+BEGIN_SRC emacs-lisp
;; bind global keys in normal mode
(map! :n "C-x x" #'do-something
:n "C-x y" #'do-something-else
:n "C-x z" #'do-another-thing)
;; or on a keymap
(map! :map emacs-lisp-mode
:n "C-x x" #'do-something
:n "C-x y" #'do-something-else
:n "C-x z" #'do-another-thing)
;; or multiple maps
(map! :map (emacs-lisp-mode go-mode-map ivy-minibuffer-map) ...)
;; or in multiple states (order of states doesn't matter)
(map! :map emacs-lisp-mode
:nv "C-x x" #'do-something ; normal+visual
:i "C-x y" #'do-something-else ; insert
:vnie "C-x z" #'do-another-thing) ; visual+normal+insert+emacs
;; You can nest map! calls:
(map! (:map emacs-lisp-mode :nv "C-x x" #'do-something)
(:map go-lisp-mode :n "C-x x" #'do-something-else))
#+END_SRC
~map!~ supports other properties, like ~:after~, ~:when~, ~:prefix~ and ~:desc~.
Look at ~map!~'s documentation for more information (=SPC h f map!= for evil
users, =C-h f map!= otherwise).
You'll find comprehensive examples of ~map!~'s usage in its documentation (via
=SPC h f map!= or =C-h f map!= -- also found [[file:api.org][in docs/api]]).
You'll find a more comprehensive example of ~map!~'s usage in
[[file:../modules/config/default/+evil-bindings.el][config/default/+evil-bindings.el]].
@ -845,10 +764,10 @@ tools for experienced Emacs users to skirt around it (most of the time):
Doom provides ~M-x doom/reload~ for your convenience, which will run ~doom
refresh~, restart the Doom initialization process, and re-evaluate your
personal config, but this won't clear pre-existing state. That may or may not
be a problem, this hasn't be thoroughly tested, and Doom cannot anticipate
be a problem, this hasn't be thoroughly tested and Doom cannot anticipate
complications arising from your private config.
If you intend to use ~doom/reload~, try to design your config to be
If you intend to use ~doom/reload~, you must design your config to be
idempotent.
- Many ~bin/doom~ commands are available as elisp commands with the ~doom//*~
prefix. e.g. ~doom//refresh~, ~doom//update~, etc. Feel free to use them, but
@ -868,20 +787,20 @@ deprecated. It forwards to ~bin/doom~ anyway.
a shell script incapable of sentience and thus incapable of retaining, much less
divulging, your secrets to others).
You can run ~bin/doom help~ to see what it's capable of, but here are the
highlights:
You can run ~bin/doom help~ to see what it's capable of, but here are some
commands that you may find particularly useful:
+ ~doom doctor~ :: Diagnose common issues in your environment and list missing
external dependencies for your enabled modules.
+ ~doom install~ :: Install any missing packages.
+ ~doom update~ :: Update all packages that Doom's (enabled) modules use.
+ ~doom refresh~ :: Ensures that all missing packages are installed, orphaned
packages are removed, and metadata properly generated.
+ ~doom install~ :: Install any missing packages.
+ ~doom update~ :: Update all packages that Doom's (enabled) modules use.
+ ~doom env~ :: Regenerates your envvar file, which contains a snapshot of your
shell environment for Doom Emacs to load on startup. You need to run this for
changes to your shell environment to take effect.
+ ~doom purge~ :: Purge orphaned packages (i.e. ones that aren't needed anymore)
and regraft your repos.
+ ~doom purge -g~ :: Purge orphaned packages (i.e. ones that aren't needed
anymore) and regraft your repos.
+ ~doom upgrade~ :: Upgrade Doom to the latest version (then update your
packages). This is equivalent to:
@ -894,10 +813,12 @@ highlights:
** When to run ~doom refresh~
As a rule of thumb you should run ~doom refresh~ whenever you:
+ Update Doom (with ~git pull~),
+ Update Doom with ~git pull~ instead of ~doom upgrade~,
+ Change your ~doom!~ block in =$DOOMDIR/init.el=,
+ Change the autoload files in any module (or =$DOOMDIR=),
+ Change autoload files in any module (or =$DOOMDIR=),
+ Or change the packages.el file in any module (or =$DOOMDIR=).
+ Install an Emacs package or dependency outside of Emacs (i.e. through your OS
package manager).
If anything is misbehaving, it's a good idea to run ~doom refresh~ first. ~doom
refresh~ is responsible for regenerating your autoloads file (which tells Doom
@ -1066,12 +987,12 @@ manually (e.g. by double-clicking each file in explorer).
** ~void-variable~ and ~void-function~ errors on startup
The most common culprit for these types of errors are:
1. An out-of-date autoloads file. To regenerate it, run ~doom refresh -f~.
1. An out-of-date autoloads file. To regenerate it, run ~doom refresh~.
To avoid this issue, remember to run ~doom refresh~ whenever you modify your
~doom!~ block in =~/.doom.d/init.el=, or add ~package!~ declarations to
=~/.doom.d/packages.el=. If you modify =~/.emacs.d/.local= by hand, for
whatever reason, run ~doom refresh -f~ to bypass caches and modify-checks.
=~/.doom.d/packages.el=. Or if you modify =~/.emacs.d/.local= by hand, for
whatever reason.
See ~doom help refresh~ for details on what this command does and when you
should use it.

View file

@ -59,8 +59,8 @@
- [[#variables-functions-faces-etc][Variables, functions, faces, etc.]]
- [[#for-doom-modules-packages-autodefs-etc][For Doom Modules, packages, autodefs, etc.]]
- [[#how-to-extract-a-backtrace-from-an-error][How to extract a backtrace from an error]]
- [[#enabling-debug-on-error][Enabling `debug-on-error`]]
- [[#a-backtrace-from-bindoom][A backtrace from `bin/doom`]]
- [[#enabling-debug-on-error][Enabling ~debug-on-error~]]
- [[#a-backtrace-from-bindoom][A backtrace from ~bin/doom~]]
- [[#evaluating-elisp-on-the-fly][Evaluating Elisp on-the-fly]]
- [[#how-to-determine-the-origin-of-a-bug][How to determine the origin of a bug]]
- [[#testing-in-dooms-sandbox][Testing in Doom's sandbox]]
@ -187,8 +187,8 @@ cause you issues later on. Do not use them:
+ XEmacs
*** On Windows
*Support for Windows is immature,* so your mileage will vary. [[https://www.reddit.com/r/emacs/comments/6bw35d/doom_an_emacsd_to_espouse_and_surpass_vim_in_any/dhtw32t/][Some have reported
success]] with installing Doom via WSL, chocolatey on git-bash or cygwin.
*Support for Windows is immature,* so your mileage will vary. Some have reported
success with installing Doom via WSL, chocolatey on git-bash or cygwin.
#+BEGIN_QUOTE
If you manage to get Doom on Windows and found this wasn't enough, or could be
@ -1080,24 +1080,23 @@ You can also evaluate code with ~eval-expression~ (=M-;= or =SPC ;=).
** How to extract a backtrace from an error
If you encounter an error while using Doom Emacs, you're probably about to head
off and file a bug report (or request help on [[[https://discord.gg/bcZ6P3y][our Discord server]]. Before you do,
please generate a backtrace to include with it.
off and file a bug report (or request help on [[https://discord.gg/bcZ6P3y][our Discord server]]). Before you
do, please generate a backtrace to include with it.
To do so you must enable ~debug-on-error~ then recreate the error.
*** Enabling `debug-on-error`
There are three ways to enable `debug-on-error`:
*** Enabling ~debug-on-error~
There are three ways to enable ~debug-on-error~:
1. Start Emacs with `emacs --debug-init`. Use this for errors that occur at
1. Start Emacs with ~emacs --debug-init~. Use this for errors that occur at
startup.
2. Evil users can press <kbd>SPC h d d</kbd> and non-evil users can press
=C-h d d=.
3. If the above don't work, there's always: `M-x toggle-debug-on-error`
2. Evil users can press =SPC h d d= and non-evil users can press =C-h d d=.
3. If the above don't work, there's always: ~M-x toggle-debug-on-error~
Now that `debug-on-error` is on, recreate the error. A window should pop up with
Now that ~debug-on-error~ is on, recreate the error. A window should pop up with
a backtrace.
*** A backtrace from `bin/doom`
*** A backtrace from ~bin/doom~
If the error you've encountered is emitted from ~bin/doom~, you can re-run the
same command with the ~-d~ or ~--debug~ switches to force it to emit a backtrace
when an error occurs. The ~DEBUG~ environment variable will work to.

View file

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

View file

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

View file

@ -143,6 +143,7 @@
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;rest ; Emacs as a REST client
;;rst ; ReST in peace
;;ruby ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good
@ -154,28 +155,17 @@
;;web ; the tubes
:email
;;(mu4e +gmail) ; WIP
;;notmuch ; WIP
;;(wanderlust +gmail) ; WIP
;;(mu4e +gmail)
;;notmuch
;;(wanderlust +gmail)
;; Applications are complex and opinionated modules that transform Emacs
;; toward a specific purpose. They may have additional dependencies and
;; should be loaded late.
:app
;;calendar
;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought
;;(write ; emacs for writers (fiction, notes, papers, etc.)
;; +wordnut ; wordnet (wn) search
;; +langtool) ; a proofreader (grammar/style check) for Emacs
;;write ; emacs for writers (fiction, notes, papers, etc.)
:config
;; For literate config users. This will tangle+compile a config.org
;; literate config in your `doom-private-dir' whenever it changes.
;;literate
;; The default module sets reasonable defaults for Emacs. It also
;; provides a Spacemacs-inspired keybinding scheme and a smartparens
;; config. Use it as a reference for your own modules.
(default +bindings +smartparens))

View file

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

View file

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

View file

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

View file

@ -1,11 +1,5 @@
;;; completion/helm/autoload/helm.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +helm/tasks (&optional _arg)
(interactive "P")
;; TODO Implement `+helm/tasks'
(error "Not implemented yet"))
;;;###autoload
(defun +helm/projectile-find-file ()
"Call `helm-find-files' if called from HOME, otherwise
@ -41,70 +35,11 @@ workspace."
;;
;; Project search
(defun +helm-ag-search-args (all-files-p recursive-p)
(list (concat "ag " (if IS-WINDOWS "--vimgrep" "--nocolor --nogroup"))
"-S"
(if all-files-p "-z -a")
(unless recursive-p "--depth 1")))
(defun +helm-rg-search-args (all-files-p recursive-p)
(list "rg --no-heading --line-number --color never"
"-S"
(when all-files-p "-z -uu")
(unless recursive-p "--maxdepth 1")))
;;
(defun +helm--grep-source ()
(require 'helm-projectile)
(helm-build-async-source (capitalize (helm-grep-command t))
:header-name (lambda (_name) "Helm Projectile Grep (C-c ? Help)")
:candidates-process #'helm-grep-collect-candidates
:filter-one-by-one #'helm-grep-filter-one-by-one
:candidate-number-limit 9999
:nohighlight t
:keymap helm-grep-map
:history 'helm-grep-history
:action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions)
:persistent-action 'helm-grep-persistent-action
:persistent-help "Jump to line (`C-u' Record in mark ring)"
:requires-pattern 2))
(defun +helm--grep-search (directory query prompt &optional all-files-p recursive-p)
(let* ((default-directory directory)
(helm-ff-default-directory directory)
(helm-grep-in-recurse recursive-p)
(helm-grep-ignored-files
(unless all-files-p
(cl-union (projectile-ignored-files-rel) grep-find-ignored-files)))
(helm-grep-ignored-directories
(unless all-files-p
(cl-union (mapcar 'directory-file-name (projectile-ignored-directories-rel))
grep-find-ignored-directories)))
(helm-grep-default-command
(if (and nil (eq (projectile-project-vcs) 'git))
(format "git --no-pager grep --no-color -n%%c -e %%p %s -- %%f"
(if recursive-p "" "--max-depth 1 "))
(format "grep -si -a%s %%e -n%%cH -e %%p %%f %s"
(if recursive-p " -R" "")
(if recursive-p "." "./*"))))
(helm-grep-default-recurse-command helm-grep-default-command))
(setq helm-source-grep (+helm--grep-source))
(helm :sources 'helm-source-grep
:input query
:prompt prompt
:buffer "*helm grep*"
:default-directory directory
:keymap helm-grep-map
:history 'helm-grep-history
:truncate-lines helm-grep-truncate-lines)))
;;; Project search
;;;###autoload
(cl-defun +helm-file-search (engine &key query in all-files (recursive t))
"Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and
grep. If omitted, ENGINE will default to the first one it detects, in that
order.
(cl-defun +helm-file-search (&key query in all-files (recursive t))
"Conduct a file search using ripgrep.
:query STRING
Determines the initial input to search for.
@ -114,6 +49,8 @@ order.
:recursive BOOL
Whether or not to search files recursively from the base directory."
(declare (indent defun))
(unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH"))
(require 'helm-ag)
(helm-ag--init-state)
(let* ((project-root (or (doom-project-root) default-directory))
@ -121,13 +58,6 @@ order.
(default-directory directory)
(helm-ag--default-directory directory)
(helm-ag--default-target (list directory))
(engine (or engine
(cl-find-if #'executable-find +helm-project-search-engines
:key #'symbol-name)
(and (or (executable-find "grep")
(executable-find "git"))
'grep)
(user-error "No search engine specified (is ag, rg, or git installed?)")))
(query (or query
(when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
@ -135,23 +65,20 @@ order.
(when (> (abs (- end beg)) 1)
(rxt-quote-pcre (buffer-substring-no-properties beg end)))))
""))
(prompt (format "[%s %s] "
(symbol-name engine)
(prompt (format "[rg %s] "
(cond ((file-equal-p directory project-root)
(projectile-project-name))
((file-equal-p directory default-directory)
"./")
((file-relative-name directory project-root)))))
(command
(pcase engine
(`ag (+helm-ag-search-args all-files recursive))
(`rg (+helm-rg-search-args all-files recursive))
('grep (+helm--grep-search directory query prompt all-files recursive)
(cl-return t))))
(helm-ag-base-command (string-join command " ")))
(list "rg --no-heading --line-number --color never"
"-S"
(when all-files "-z -uu")
(unless recursive "--maxdepth 1")))
(helm-ag-base-command (string-join (delq nil command) " ")))
;; TODO Define our own sources instead
(helm-attrset 'name (format "[%s %s] Searching %s"
engine
(helm-attrset 'name (format "[rg %s] Searching %s"
(string-join (delq nil (cdr command)) " ")
(abbreviate-file-name directory))
helm-source-do-ag)
@ -159,82 +86,39 @@ order.
(cl-letf (((symbol-function 'helm-do-ag--helm)
(lambda () (helm :sources '(helm-source-do-ag)
:prompt prompt
:buffer "*helm-ag*"
:buffer "*helm-rg*"
:keymap helm-do-ag-map
:input query
:history 'helm-ag--helm-history))))
(helm-do-ag directory))))
(defun +helm--get-command (format)
(cl-loop for tool in (cl-remove-duplicates +helm-project-search-engines :from-end t)
if (executable-find (symbol-name tool))
return (intern (format format tool))))
;;;###autoload
(defun +helm/project-search (&optional arg initial-query directory)
"Performs a project search from the project root.
"Performs a project search from the project root with ripgrep.
Uses the first available search backend from `+helm-project-search-engines'. If
ARG (universal argument), include all files, even hidden or compressed ones, in
the search."
(interactive "P")
(funcall (or (+helm--get-command "+helm/%s")
#'+helm/grep)
arg
initial-query
directory))
(+helm-file-search
:query initial-query
:in directory
:all-files (and (not (null arg))
(listp arg))))
;;;###autoload
(defun +helm/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory.
Uses the first available search backend from `+helm-project-search-engines'. If
ARG (universal argument), include all files, even hidden or compressed ones."
If ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P")
(funcall (or (+helm--get-command "+helm/%s-from-cwd")
#'+helm/grep-from-cwd)
arg
initial-query))
;;;###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
(+helm-file-search
:query initial-query
:in default-directory
:all-files (and (not (null arg))
(listp arg))))
(format "Perform a project file search from the current directory using %s.
QUERY is a regexp. If omitted, the current selection is used. If no selection is
active, the last known search is used.
If ALL-FILES-P, search compressed and hidden files as well."
engine)))
;;;###autoload
(defun +helm/jump-list ()
"TODO"
(interactive)
(error "not implemented yet"))

View file

@ -1,15 +1,5 @@
;;; completion/helm/config.el -*- lexical-binding: t; -*-
(defvar +helm-project-search-engines '(rg ag)
"What search tools for `+helm/project-search' (and `+helm-file-search' when no
ENGINE is specified) to try, and in what order.
To disable a particular tool, remove it from this list. To prioritize a tool
over others, move it to the front of the list. Later duplicates in this list are
silently ignored.
This falls back to git-grep (then grep) if none of these available.")
;; Posframe (requires +childframe)
(defvar +helm-posframe-handler #'+helm-poshandler-frame-center-near-bottom-fn
"The function that determines the location of the childframe. It should return
@ -82,7 +72,7 @@ be negative.")
(setq helm-default-prompt-display-function #'+helm--set-prompt-display))
:init
(when (and EMACS26+ (featurep! +childframe))
(when (featurep! +childframe)
(setq helm-display-function #'+helm-posframe-display-fn))
(let ((fuzzy (featurep! +fuzzy)))
@ -135,7 +125,6 @@ be negative.")
:config (helm-flx-mode +1))
;;;###package helm-ag
(after! helm-ag
(map! :map helm-ag-edit-map :n "RET" #'compile-goto-error)
(define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort)
@ -150,14 +139,12 @@ be negative.")
(setq helm-bookmark-show-location t)
;;;###package helm-files
(after! helm-files
(setq helm-boring-file-regexp-list
(append (list "\\.projects$" "\\.DS_Store$")
helm-boring-file-regexp-list)))
;;;###package helm-locate
(defvar helm-generic-files-map (make-sparse-keymap))
(after! helm-locate
(when (and IS-MAC
@ -167,7 +154,6 @@ be negative.")
(set-keymap-parent helm-generic-files-map helm-map))
;;;###package helm-org
(use-package! helm-org
:when (featurep! :lang org)
:defer t
@ -178,7 +164,6 @@ be negative.")
'(org-set-tags . helm-org-completing-read-tags))))
;;;###package helm-projectile
(use-package! helm-projectile
:commands (helm-projectile-find-file
helm-projectile-recentf
@ -191,7 +176,6 @@ be negative.")
(set-keymap-parent helm-projectile-find-file-map helm-map))
;;;###package swiper-helm
(after! swiper-helm
(setq swiper-helm-display-function
(lambda (buf &optional _resume) (pop-to-buffer buf)))

View file

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

View file

@ -17,7 +17,6 @@
- [[#jump-to-file-project-navigation][Jump-to-file project navigation]]
- [[#project-search--replace][Project search & replace]]
- [[#in-buffer-searching][In-buffer searching]]
- [[#task-lookup][Task lookup]]
- [[#ivy-integration-for-various-completing-commands][Ivy integration for various completing commands]]
- [[#general][General]]
- [[#jump-to-files-buffers-or-projects][Jump to files, buffers or projects)]]
@ -29,8 +28,7 @@
* Description
This module provides Ivy integration for a variety of Emacs commands, as well as
a unified interface for project search and replace, powered by ag, rg,
git-grep & grep (whichever is available).
a unified interface for project search and replace, powered by ripgrep.
#+begin_quote
I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's
@ -41,7 +39,6 @@ lighter, simpler and faster in many cases.
+ =+fuzzy= Enables fuzzy completion for Ivy searches.
+ =+prescient= Enables prescient filtering and sorting for Ivy searches.
+ =+childframe= Causes Ivy to display in a floating child frame, above Emacs.
*This requires GUI Emacs 26.1+*
+ =+icons= Enables file icons for switch-{buffer,project}/find-file counsel
commands.
@ -67,32 +64,24 @@ lighter, simpler and faster in many cases.
command)
* Prerequisites
This module optionally depends on one of:
This module depends on:
+ [[https://github.com/BurntSushi/ripgrep][ripgrep]] (rg)
+ [[https://github.com/ggreer/the_silver_searcher][the_silver_searcher]] (ag)
Ripgrep is recommended, but the order of its results aren't deterministic and it
doesn't support full PCRE (at the time of writing). The_silver_searcher is a
good alternative if either of these bother you.
If none of these are installed, file search commands will use git-grep (falling
back to grep, otherwise).
** Install
*** MacOS
#+BEGIN_SRC sh
brew install ripgrep the_silver_searcher
brew install ripgrep
#+END_SRC
*** Arch Linux
#+BEGIN_SRC sh :dir /sudo::
sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher
sudo pacman --needed --noconfirm -S ripgrep
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install ripgrep the_silver_searcher
sudo zypper install ripgrep
#+END_SRC
* Features
@ -108,35 +97,21 @@ https://assets.doomemacs.org/completion/ivy/projectile.png
| Keybind | Description |
|----------------------+-------------------------------------|
| =SPC f /=, =SPC SPC= | Jump to file in project |
| =SPC f .=, =SPC .= | Jump to file from current directory |
| =SPC p f=, =SPC SPC= | Jump to file in project |
| =SPC f f=, =SPC .= | Jump to file from current directory |
** Project search & replace
This module provides interactive text search and replace using the first search
program available on your system (rg, ag, git-grep or grep).
This module provides interactive text search and replace using ripgrep.
| Keybind | Description |
|-----------+---------------------------------|
| =SPC / b= | Search the current buffer |
| =SPC / p= | Search project |
| =SPC / d= | Search this directory |
| =SPC s b= | Search the current buffer |
| =SPC s p= | Search project |
| =SPC s d= | Search this directory |
| =SPC p t= | List all TODO/FIXMEs in project |
https://assets.doomemacs.org/completion/ivy/search.png
The ~+ivy-project-search-engines~ variable is consulted to determine which
underlying program to check for (and in what order). It's default value is ~'(rg
ag pt)~. If none of these are available, it will resort to =git-grep= (falling
back to =grep= after that).
To use a specific program, the following engine-specific commands are available
(but not bound to any key by default) for searching from the project root or the
current directory (recursively), respectively:
+ ~+ivy/ag~ / ~+ivy/ag-from-cwd~
+ ~+ivy/rg~ / ~+ivy/rg-from-cwd~
+ ~+ivy/grep~ / ~+ivy/grep-from-cwd~
The universal argument (=SPC u= for evil users; =C-u= otherwise) changes the
behavior of these commands, instructing the underlying search engine to include
ignored files.
@ -144,21 +119,16 @@ ignored files.
This module also provides Ex Commands for evil users:
| Ex command | Description |
|-----------------------+------------------------------------------------|
| ~:ag[!] [QUERY]~ | Search project w/ ag[fn:1] |
| ~:rg[!] [QUERY]~ | Search project w/ rg[fn:1] |
| ~:grep[!] [QUERY]~ | Search project w/ git-grep/grep[fn:1] |
| ~:agcwd[!] [QUERY]~ | Search this directory w/ the_silver_searcher |
| ~:rgcwd[!] [QUERY]~ | Search this directory w/ ripgrep |
| ~:grepcwd[!] [QUERY]~ | Search this directory w/ git-grep/grep |
|------------------------+------------------------------------------------------------------|
| ~:pg[rep][!] [QUERY]~ | Search project (if ~!~, include hidden files) |
| ~:pg[rep]d[!] [QUERY]~ | Search from current directory (if ~!~, don't search recursively) |
The optional BANG functions is equivalent to the universal argument for the
previous commands.
-----
While in a search (e.g. invoked from ~+ivy:ag~ or ~:rg~), these extra
keybindings are available to you:
While in a search these extra keybindings are available to you:
| Keybind | Description |
|-----------+-----------------------------------------------|
@ -176,22 +146,14 @@ https://assets.doomemacs.org/completion/ivy/search-replace.png
The =swiper= package provides an interactive buffer search powered by ivy. It
can be invoked with:
+ =SPC / b=
+ =SPC s s=
+ =SPC s S= (uses thing at point as initial input)
+ ~:sw[iper] [QUERY]~
https://assets.doomemacs.org/completion/ivy/swiper.png
A wgrep buffer can be opened from swiper with =C-c C-e=.
** Task lookup
Some projects have TODO's and FIXME's littered across them. The ~+ivy/tasks~
command allows you to search and jump to them. It can be invoked with:
+ =SPC p t= (C-u = restrict search to current file)
+ ~:todo[!]~ (BANG = restrict search to current file)
https://assets.doomemacs.org/completion/ivy/todo.png
** Ivy integration for various completing commands
*** General
| Keybind | Description |
@ -201,10 +163,10 @@ https://assets.doomemacs.org/completion/ivy/todo.png
*** Jump to files, buffers or projects)
| Keybind | Description |
|---------------------------------+---------------------------------------|
|----------------------+---------------------------------------|
| =SPC RET= | Find bookmark |
| =SPC f .=, =SPC .= | Browse from current directory |
| =SPC f /=, =SPC p /=, =SPC SPC= | Find file in project |
| =SPC f f=, =SPC .= | Browse from current directory |
| =SPC p f=, =SPC SPC= | Find file in project |
| =SPC f r= | Find recently opened file |
| =SPC p p= | Open another project |
| =SPC b b=, =SPC ,= | Switch to buffer in current workspace |
@ -212,13 +174,15 @@ https://assets.doomemacs.org/completion/ivy/todo.png
*** Search
| Keybind | Description |
|-----------+------------------------------------------|
| =SPC / i= | Search for symbol in current buffer |
| =SPC / I= | Search for symbol in all similar buffers |
| =SPC / b= | Search the current buffer |
| =SPC / p= | Search project |
| =SPC / d= | Search this directory |
|-----------+-------------------------------------------|
| =SPC p t= | List all TODO/FIXMEs in project |
| =SPC s b= | Search the current buffer |
| =SPC s d= | Search this directory |
| =SPC s D= | Search another directory |
| =SPC s i= | Search for symbol in current buffer |
| =SPC s p= | Search project |
| =SPC s P= | Search another project |
| =SPC s s= | Search the current buffer (incrementally) |
* Configuration
** TODO Enable fuzzy/non-fuzzy search for specific commands

View file

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

View file

@ -157,88 +157,6 @@ If ARG (universal argument), open selection in other-window."
(interactive)
(+ivy--switch-buffer nil t))
(defun +ivy--tasks-candidates (tasks)
"Generate a list of task tags (specified by `+ivy-task-tags') for
`+ivy/tasks'."
(let* ((max-type-width
(cl-loop for task in +ivy-task-tags maximize (length (car task))))
(max-desc-width
(cl-loop for task in tasks maximize (length (cl-cdadr task))))
(max-width (max (+ max-desc-width 3)
25)))
(cl-loop
with fmt = (format "%%-%ds %%-%ds%%s:%%s" max-type-width max-width)
for alist in tasks
collect
(let-alist alist
(list (format fmt
(propertize .type 'face (cdr (assoc .type +ivy-task-tags)))
(substring .desc 0 (min max-desc-width (length .desc)))
(propertize (abbreviate-file-name .file) 'face 'font-lock-keyword-face)
(propertize .line 'face 'font-lock-constant-face))
.type .file .line)))))
(defun +ivy--tasks (target)
(let* (case-fold-search
(task-tags (mapcar #'car +ivy-task-tags))
(cmd
(format "%s -H -S --no-heading -- %s %s"
(or (when-let (bin (executable-find "rg"))
(concat bin " --line-number"))
(when-let (bin (executable-find "ag"))
(concat bin " --numbers"))
(error "ripgrep & the_silver_searcher are unavailable"))
(shell-quote-argument
(concat "\\s("
(string-join task-tags "|")
")([\\s:]|\\([^)]+\\):?)"))
target)))
(save-match-data
(cl-loop with out = (shell-command-to-string cmd)
for x in (and out (split-string out "\n" t))
when (condition-case-unless-debug ex
(string-match
(concat "^\\([^:]+\\):\\([0-9]+\\):.+\\("
(string-join task-tags "\\|")
"\\):?\\s-*\\(.+\\)")
x)
(error
(print! (red "Error matching task in file: (%s) %s")
(error-message-string ex)
(car (split-string x ":")))
nil))
collect `((type . ,(match-string 3 x))
(desc . ,(match-string 4 x))
(file . ,(match-string 1 x))
(line . ,(match-string 2 x)))))))
(defun +ivy--tasks-open-action (x)
"Jump to the file and line of the current task."
(cl-destructuring-bind (_label type file line) x
(with-ivy-window
(find-file (expand-file-name file (doom-project-root)))
(goto-char (point-min))
(forward-line (1- (string-to-number line)))
(when (search-forward type (line-end-position) t)
(backward-char (length type)))
(recenter))))
;;;###autoload
(defun +ivy/tasks (&optional arg)
"Search through all TODO/FIXME tags in the current project. If ARG, only
search current file. See `+ivy-task-tags' to customize what this searches for."
(interactive "P")
(ivy-read (format "Tasks (%s): "
(if arg
(concat "in: " (file-relative-name buffer-file-name))
"project"))
(let ((tasks (+ivy--tasks (if arg buffer-file-name (doom-project-root)))))
(unless tasks
(user-error "No tasks in your project! Good job!"))
(+ivy--tasks-candidates tasks))
:action #'+ivy--tasks-open-action
:caller '+ivy/tasks))
;;;###autoload
(defun +ivy/woccur ()
"Invoke a wgrep buffer on the current ivy results, if supported."
@ -293,7 +211,7 @@ search current file. See `+ivy-task-tags' to customize what this searches for."
;;
;; File searching
;;; File searching
;;;###autoload
(defun +ivy/projectile-find-file ()
@ -318,25 +236,9 @@ The point of this is to avoid Emacs locking up indexing massive file trees."
(#'counsel-file-jump))))
(defvar +ivy-file-search-shell
(or (executable-find "dash")
(executable-find "sh")
shell-file-name)
"The SHELL to invoke ag/rg/pt/git-grep/grep searchs from.
This only affects `+ivy/*' search commands (e.g. `+ivy/rg' and
`+ivy/project-search').
By default, this the most basic, uncustomized shell, to prevent interference
caused by slow shell configs at the cost of isolating these programs from
envvars that may have been set in the user's shell config to change their
behavior. If this bothers you, change this to `shell-file-name'.")
;;;###autoload
(cl-defun +ivy-file-search (engine &key query in all-files (recursive t))
"Conduct a file search using ENGINE, which can be any of: rg, ag, pt, and
grep. If omitted, ENGINE will default to the first one it detects, in that
order.
(cl-defun +ivy-file-search (&key query in all-files (recursive t))
"Conduct a file search using ripgrep.
:query STRING
Determines the initial input to search for.
@ -346,18 +248,16 @@ order.
:recursive BOOL
Whether or not to search files recursively from the base directory."
(declare (indent defun))
(let* ((project-root (or (doom-project-root) default-directory))
(unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH"))
(require 'counsel)
(let* ((ivy-more-chars-alist '((t . 1)))
(project-root (or (doom-project-root) default-directory))
(directory (or in project-root))
(default-directory directory)
(engine (or engine
(cl-loop for tool in +ivy-project-search-engines
if (executable-find (symbol-name tool))
return tool)
(and (or (executable-find "grep")
(executable-find "git"))
'grep)
(error "No search engine specified (is ag, rg, pt or git installed?)")))
(query
(args (concat (if all-files " -uu")
(unless recursive " --maxdepth 1"))))
(counsel-rg
(or (if query query)
(when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
@ -369,108 +269,35 @@ order.
(cond ((and (string= substr " ")
(not (featurep! +fuzzy)))
" ")
((and (string= substr "|")
(eq engine 'rg))
((string= substr "|")
"\\\\\\\\|")
((concat "\\\\" substr))))
(rxt-quote-pcre query))))))))
(prompt
(format "%s%%s %s"
(symbol-name engine)
(rxt-quote-pcre query)))))))
directory args
(format "rg%s %s"
args
(cond ((equal directory default-directory)
"./")
((equal directory project-root)
(projectile-project-name))
((file-relative-name directory project-root))))))
(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))))
((file-relative-name directory project-root)))))))
;;;###autoload
(defun +ivy/project-search (&optional arg initial-query directory)
"Performs a project search from the project root.
"Performs a live project search from the project root using ripgrep.
Uses the first available search backend from `+ivy-project-search-engines'. If
ARG (universal argument), include all files, even hidden or compressed ones, in
the search."
If ARG (universal argument), include all files, even hidden or compressed ones,
in the search."
(interactive "P")
(funcall (or (+ivy--get-command "+ivy/%s")
#'+ivy/grep)
arg
initial-query
directory))
(+ivy-file-search :query initial-query :in directory :all-files arg))
;;;###autoload
(defun +ivy/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory.
Uses the first available search backend from `+ivy-project-search-engines'. If
ARG (universal argument), include all files, even hidden or compressed ones."
If ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P")
(funcall (or (+ivy--get-command "+ivy/%s-from-cwd")
#'+ivy/grep-from-cwd)
arg
initial-query))
;;;###autoload (autoload '+ivy/rg "completion/ivy/autoload/ivy" nil t)
;;;###autoload (autoload '+ivy/rg-from-cwd "completion/ivy/autoload/ivy" nil t)
;;;###autoload (autoload '+ivy/ag "completion/ivy/autoload/ivy" nil t)
;;;###autoload (autoload '+ivy/ag-from-cwd "completion/ivy/autoload/ivy" nil t)
;;;###autoload (autoload '+ivy/grep "completion/ivy/autoload/ivy" nil t)
;;;###autoload (autoload '+ivy/grep-from-cwd "completion/ivy/autoload/ivy" nil t)
(dolist (engine `(,@(cl-remove-duplicates +ivy-project-search-engines :from-end t) grep))
(defalias (intern (format "+ivy/%s" engine))
(lambda (all-files-p &optional query directory)
(interactive "P")
(+ivy-file-search engine :query query :in directory :all-files all-files-p))
(format "Perform a project file search using %s.
QUERY is a regexp. If omitted, the current selection is used. If no selection is
active, the last known search is used.
If ALL-FILES-P, search compressed and hidden files as well."
engine))
(defalias (intern (format "+ivy/%s-from-cwd" engine))
(lambda (all-files-p &optional query)
(interactive "P")
(+ivy-file-search engine :query query :in default-directory :all-files all-files-p))
(format "Perform a project file search from the current directory using %s.
QUERY is a regexp. If omitted, the current selection is used. If no selection is
active, the last known search is used.
If ALL-FILES-P, search compressed and hidden files as well."
engine)))
(+ivy/project-search arg initial-query default-directory))
;;
@ -513,7 +340,7 @@ If ALL-FILES-P, search compressed and hidden files as well."
(cons (format "%s:%d: %s"
(buffer-name)
(line-number-at-pos)
(string-trim-right (thing-at-point 'line)))
(string-trim-right (or (thing-at-point 'line) "")))
(point-marker)))))))
(cddr (better-jumper-jump-list-struct-ring
(better-jumper-get-jumps (better-jumper--get-current-context))))))))

View file

@ -7,16 +7,6 @@ When nil, don't preview anything.
When non-nil, preview non-virtual buffers.
When 'everything, also preview virtual buffers")
(defvar +ivy-task-tags
'(("TODO" . warning)
("FIXME" . error)
("HACK" . font-lock-constant-face)
("REVIEW" . font-lock-keyword-face)
("NOTE" . success)
("DEPRECATED" . font-lock-doc-face))
"An alist of tags for `+ivy/tasks' to include in its search, whose CDR is the
face to render it with.")
(defvar +ivy-project-search-engines '(rg ag)
"What search tools for `+ivy/project-search' (and `+ivy-file-search' when no
ENGINE is specified) to try, and in what order.
@ -69,6 +59,11 @@ results buffer.")
[remap persp-switch-to-buffer] #'+ivy/switch-workspace-buffer
[remap evil-show-jumps] #'+ivy/jump-list)
:config
;; Counsel changes a lot of ivy's state at startup; to control for that, we
;; need to load it as early as possible. Some packages (like `ivy-prescient')
;; require this.
(require 'counsel nil t)
(setq ivy-height 17
ivy-wrap t
ivy-fixed-height-minibuffer t
@ -182,7 +177,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! counsel
:commands counsel-describe-face
:defer t
:init
(define-key!
[remap apropos] #'counsel-apropos
@ -324,7 +319,7 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! ivy-posframe
:when (and EMACS26+ (featurep! +childframe))
:when (featurep! +childframe)
:hook (ivy-mode . ivy-posframe-mode)
:config
(setq ivy-fixed-height-minibuffer nil

View file

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

View file

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

View file

@ -82,7 +82,7 @@
(:prefix ("p" . "project")
:desc "Find file in other project" "F" #'doom/find-file-in-other-project
:desc "Search project" "s" #'+default/search-project
:desc "List project tasks" "t" #'+default/project-tasks
:desc "List project tasks" "t" #'magit-todos-list
:desc "Open project scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
;; later expanded by projectile

View file

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

View file

@ -73,15 +73,6 @@ If ARG (universal argument), runs `compile' from the current directory."
(with-current-buffer buffer
(funcall (default-value 'major-mode))))))
;;;###autoload
(defun +default/project-tasks ()
"Invokes `+ivy/tasks' or `+helm/tasks', depending on which is available."
(interactive)
(cond ((featurep! :tools magit)
(call-interactively #'magit-todos-list))
((featurep! :completion ivy) (+ivy/tasks))
((featurep! :completion helm) (+helm/tasks))))
;;;###autoload
(defun +default/newline-above ()
"Insert an indented new line before the current one."
@ -120,7 +111,7 @@ languages)."
(interactive)
(if (and (sp-point-in-comment)
comment-line-break-function)
(funcall comment-line-break-function)
(funcall comment-line-break-function nil)
(delete-horizontal-space t)
(newline nil t)
(indent-according-to-mode)))
@ -333,3 +324,22 @@ ARG is set, prompt for a known project to search from."
(while (server-running-p)
(sit-for 1))
(server-start))
;;;###autoload
(defun +default/find-file-under-here ()
"Perform a recursive file search from the current directory."
(interactive)
(if (featurep! :completion ivy)
(call-interactively #'counsel-file-jump)
(λ! (doom-project-find-file default-directory))))
;;;###autoload
(defun +default/insert-file-path (arg)
"Insert the file name (absolute path if prefix ARG).
If `buffer-file-name' isn't set, uses `default-directory'."
(interactive "P")
(let ((path (or buffer-file-name default-directory)))
(insert
(if arg
(abbreviate-file-name path)
(file-name-nondirectory path)))))

View file

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

View file

@ -4,6 +4,7 @@
;;; Custom commands
;; Editing
(evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me
(evil-ex-define-cmd "R[ead]" #'+evil:read)
(evil-ex-define-cmd "al[ign]" #'+evil:align)
(evil-ex-define-cmd "ral[ign]" #'+evil:align-right)
(evil-ex-define-cmd "enhtml" #'+web:encode-html-entities)
@ -56,25 +57,19 @@
(evil-ex-define-cmd "cd" #'+evil:cd)
(evil-ex-define-cmd "pwd" #'+evil:pwd)
(evil-define-command +evil:swiper (&optional search)
"Invoke `swiper' with SEARCH, otherwise with the symbol at point."
(interactive "<a>")
(swiper-isearch search))
(evil-ex-define-cmd "sw[iper]" #'+evil:swiper)
(cond ((featurep! :completion ivy)
(evil-ex-define-cmd "ag" #'+ivy:ag)
(evil-ex-define-cmd "agc[wd]" #'+ivy:ag-from-cwd)
(evil-ex-define-cmd "rg" #'+ivy:rg)
(evil-ex-define-cmd "rgc[wd]" #'+ivy:rg-from-cwd)
(evil-ex-define-cmd "grep" #'+ivy:grep)
(evil-ex-define-cmd "grepc[wd]" #'+ivy:grep-from-cwd)
(evil-ex-define-cmd "sw[iper]" #'+ivy:swiper)
(evil-ex-define-cmd "todo" #'+ivy:todo))
(evil-ex-define-cmd "pg[rep]" #'+ivy:project-search)
(evil-ex-define-cmd "pg[grep]d" #'+ivy:project-search-from-cwd))
((featurep! :completion helm)
(evil-ex-define-cmd "ag" #'+helm:ag)
(evil-ex-define-cmd "agc[wd]" #'+helm:ag-from-cwd)
(evil-ex-define-cmd "rg" #'+helm:rg)
(evil-ex-define-cmd "rgc[wd]" #'+helm:rg-from-cwd)
(evil-ex-define-cmd "grep" #'+helm:grep)
(evil-ex-define-cmd "grepc[wd]" #'+helm:grep-from-cwd)
;; (evil-ex-define-cmd "todo" #'+helm:todo) TODO implement `+helm:todo'
))
(evil-ex-define-cmd "pg[rep]" #'+helm:project-search)
(evil-ex-define-cmd "pg[grep]d" #'+helm:project-search-from-cwd)))
;;; Project tools
(evil-ex-define-cmd "compile" #'+evil:compile)

View file

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

View file

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

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))))
((message "Searching for %S, this may take a while..." query)
(apropos query t))))))
;;;###autoload (autoload '+evil:read "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:read (count file)
"Alternative version of `evil-read' that replaces filename modifiers in FILE."
(interactive "P<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)
:nv "gr" #'+eval:region
:n "gR" #'+eval/buffer
:v "gR" #'+eval:replace-region)
:v "gR" #'+eval:replace-region
;; Restore these keybinds, since the blacklisted/overwritten gr/gR will
;; undo them:
(:after dired
:map dired-mode-map
:n "gr" #'revert-buffer)
(:after notmuch
:map notmuch-common-keymap
:n "gr" #'notmuch-refresh-this-buffer
:n "gR" #'notmuch-poll-and-refresh-this-buffer)
(:after elfeed
:map elfeed-search-update--force
:n "gr" #'elfeed-search-update--force
:n "gR" #'elfeed-search-fetch))
:nv "z=" #'flyspell-correct-word-generic
;; custom evil keybinds

View file

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

View file

@ -66,14 +66,3 @@ otherwise in default state."
(when (and (bound-and-true-p evil-mode)
(bobp) (eolp))
(evil-insert-state)))))
(after! smerge-mode
(unless EMACS26+
(with-no-warnings
(defalias #'smerge-keep-upper #'smerge-keep-mine)
(defalias #'smerge-keep-lower #'smerge-keep-other)
(defalias #'smerge-diff-base-upper #'smerge-diff-base-mine)
(defalias #'smerge-diff-upper-lower #'smerge-diff-mine-other)
(defalias #'smerge-diff-base-lower #'smerge-diff-base-other))))

View file

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

View file

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

View file

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

View file

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

View file

@ -7,8 +7,5 @@
(package! yaml-mode)
(package! csv-mode)
(package! dhall-mode)
(package! protobuf-mode :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*")))
;; DEPRECATED `conf-toml-mode' exists in Emacs 26+
(unless (fboundp 'conf-toml-mode)
(package! toml-mode))
(package! protobuf-mode
:recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*")))

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

View file

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

View file

@ -14,6 +14,9 @@
(unless (executable-find "gotests")
(warn! "Couldn't find gotests. Generating tests will not work"))
(unless (executable-find "gomodifytags")
(warn! "Couldn't find gomodifytags. Manipulating struct tags will not work"))
(when (featurep! :completion company)
(require 'company-go)
(unless (executable-find company-go-gocode-command)

View file

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

View file

@ -188,7 +188,7 @@ to tide."
:map tide-mode-map
"R" #'tide-restart-server
"f" #'tide-format
"rs" #'tide-rename-symbol
"rrs" #'tide-rename-symbol
"roi" #'tide-organize-imports))
@ -205,7 +205,25 @@ to tide."
:config
(when (featurep! :editor evil +everywhere)
(let ((js2-refactor-mode-map (evil-get-auxiliary-keymap js2-refactor-mode-map 'normal t t)))
(js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key)))))
(js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key))))
(map! :map js2-mode-map
:localleader
(:prefix ("r" . "refactor")
(:prefix ("a" . "add/arguments"))
(:prefix ("b" . "barf"))
(:prefix ("c" . "contract"))
(:prefix ("d" . "debug"))
(:prefix ("e" . "expand/extract"))
(:prefix ("i" . "inject/inline/introduce"))
(:prefix ("l" . "localize/log"))
(:prefix ("o" . "organize"))
(:prefix ("r" . "rename"))
(:prefix ("s" . "slurp/split/string"))
(:prefix ("t" . "toggle"))
(:prefix ("u" . "unwrap"))
(:prefix ("v" . "var"))
(:prefix ("w" . "wrap"))
(:prefix ("3" . "ternary")))))
(use-package! eslintd-fix
@ -234,6 +252,9 @@ to tide."
(:after skewer-html
:map skewer-html-mode-map
"e" #'skewer-html-eval-tag))
(map! :map js2-mode-map
:localleader
(:prefix ("s" . "skewer")))
;;;###package npm-mode
@ -242,7 +263,10 @@ to tide."
:config
(map! :localleader
:map npm-mode-keymap
"n" npm-mode-command-keymap))
"n" npm-mode-command-keymap)
(map! :map js2-mode-map
:localleader
(:prefix ("n" . "npm"))))
;;

View file

@ -1,7 +1,7 @@
;;; lang/ocaml/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +ocaml/comment-indent-new-line ()
(defun +ocaml/comment-indent-new-line (&optional _)
"Break line at point and indent, continuing comment if within one."
(interactive)
(comment-indent-new-line)

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)
"Given a remote SESSION with PARAMS and corresponding HOST, copy remote config to local, start a jupyter console to generate a new one."
(let* ((runtime-dir
(substring (shell-command-to-string (concat "ssh " host " jupyter --runtime-dir")) 0 -1))
(cdr
(doom-call-process "ssh " host "jupyter" "--runtime-dir")))
(runtime-file (concat runtime-dir "/" "kernel-" session ".json"))
(tramp-path (concat "/ssh:" host ":" runtime-file))
(tramp-copy (concat (or +ob-ipython-local-runtime-dir
(substring (shell-command-to-string "jupyter --runtime-dir")
0 -1))
(cdr (doom-call-process "jupyter" "--runtime-dir")))
"/remote-" host "-kernel-" session ".json"))
(local-path
(concat

View file

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

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
;; executing JS src blocks
(setq org-babel-js-function-wrapper "console.log(require('util').inspect(function(){\n%s\n}()));")
;; Fix #2010: ob-async needs to initialize Doom Emacs at least minimally for
;; its async babel sessions to run correctly. This cannot be a named function
;; because it is interpolated directly into a closure to be evaluated on the
;; async session.
(defadvice! +org-init-doom-during-async-executation-a (orig-fn &rest args)
:around #'ob-async-org-babel-execute-src-block
(let ((ob-async-pre-execute-src-block-hook
;; Ensure our hook is always first
(cons `(lambda () (load ,(concat doom-emacs-dir "init.el")))
ob-async-pre-execute-src-block-hook)))
(apply orig-fn args))))
(setq org-babel-js-function-wrapper "console.log(require('util').inspect(function(){\n%s\n}()));"))
(defun +org-init-babel-lazy-loader-h ()
@ -633,6 +621,7 @@ between the two."
(:when (featurep! :completion helm)
"." #'helm-org-in-buffer-headings
"/" #'helm-org-agenda-files-headings)
"A" #'org-archive-subtree
"d" #'org-deadline
"e" #'org-export-dispatch
"f" #'org-footnote-new
@ -648,26 +637,29 @@ between the two."
"s" #'org-schedule
"t" #'org-todo
"T" #'org-todo-list
(:prefix ("r" . "refile")
"." #'+org/refile-to-current-file
"c" #'+org/refile-to-running-clock
"l" #'+org/refile-to-last-location
"o" #'+org/refile-to-other-window
"O" #'+org/refile-to-other-buffers
"r" #'org-refile) ; to all `org-refile-targets'
(:prefix ("a" . "attachments")
"a" #'+org-attach/file
"u" #'+org-attach/uri
"f" #'+org-attach/find-file
"s" #'+org-attach/sync)
(:prefix ("b" . "tables")
"-" #'org-table-insert-hline
"a" #'org-table-align
"c" #'org-table-create-or-convert-from-region
"e" #'org-table-edit-field
"h" #'org-table-field-info
(:when (featurep! +gnuplot)
"p" #'org-plot/gnuplot))
(:prefix ("c" . "clock")
"c" #'org-clock-in
"C" #'org-clock-out
"d" #'org-clock-mark-default-task
"e" #'org-clock-modify-effort-estimate
"E" #'org-set-effort
"l" #'org-clock-in-last
"g" #'org-clock-goto
"G" (λ! (org-clock-goto 'select))
"r" #'org-clock-report
"x" #'org-clock-cancel
"=" #'org-clock-timestamps-up
"-" #'org-clock-timestamps-down)
@ -676,21 +668,18 @@ between the two."
(:when (featurep! :completion ivy)
"g" #'counsel-org-goto
"G" #'counsel-org-goto-all)
"a" #'org-agenda-goto
"A" #'org-agenda-clock-goto
"c" #'org-clock-goto
"C" (λ! (org-clock-goto 'select))
"i" #'org-id-goto
"r" #'org-refile-goto-last-stored
"x" #'org-capture-goto-last-stored)
(:prefix ("b" . "tables")
"-" #'org-table-insert-hline
"a" #'org-table-align
"c" #'org-table-create-or-convert-from-region
"e" #'org-table-edit-field
"h" #'org-table-field-info
(:when (featurep! +gnuplot)
"p" #'org-plot/gnuplot)))
(:prefix ("r" . "refile")
"." #'+org/refile-to-current-file
"c" #'+org/refile-to-running-clock
"l" #'+org/refile-to-last-location
"o" #'+org/refile-to-other-window
"O" #'+org/refile-to-other-buffers
"r" #'org-refile)) ; to all `org-refile-targets'
(map! :after org-agenda
:map org-agenda-mode-map
@ -699,6 +688,13 @@ between the two."
[remap org-agenda-Quit] #'org-agenda-exit
:localleader
"d" #'org-agenda-deadline
(:prefix ("c" . "clock")
"c" #'org-agenda-clock-in
"C" #'org-agenda-clock-out
"g" #'org-agenda-clock-goto
"r" #'org-agenda-clockreport-mode
"s" #'org-agenda-show-clocking-issues
"x" #'org-agenda-clock-cancel)
"q" #'org-agenda-set-tags
"r" #'org-agenda-refile
"s" #'org-agenda-schedule

View file

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

View file

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

View file

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

View file

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

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
(use-package! rust-mode
:defer t
:config
(setq rust-indent-method-chain t)
(add-hook 'rust-mode-hook #'rainbow-delimiters-mode)
;; This is necessary because both plugins are fighting for supremacy in
;; `auto-mode-alist', so rustic-mode *must* load second. It only needs to
;; happen once.
;;
;; rust-mode is still required for `racer'.
(add-hook! 'rust-mode-hook
(defun +rust-init-h ()
"Switch to `rustic-mode', if it's available."
(when (require 'rustic nil t)
(rustic-mode))))
(set-docsets! '(rust-mode rustic-mode) "Rust")
(when (featurep! +lsp)
(add-hook 'rust-mode-local-vars-hook #'lsp!)))
(use-package! racer
:unless (featurep! +lsp)
:hook ((rust-mode rustic-mode) . racer-mode)
:config
(set-lookup-handlers! 'rust-mode
:definition '(racer-find-definition :async t)
:documentation '+rust-racer-lookup-documentation))
(use-package! rustic
:when EMACS26+
:after rust-mode
:mode ("\\.rs$" . rustic-mode)
:preface
(setq rustic-rls-pkg (if (featurep! +lsp) 'lsp-mode))
:config
(set-docsets! 'rustic-mode "Rust")
(setq rustic-indent-method-chain t
rustic-flycheck-setup-mode-line-p nil
;; use :editor format instead
@ -52,38 +22,35 @@
;; buffers, so we disable it, but only for evil users, because it
;; affects `forward-sexp' and its ilk. See
;; https://github.com/rust-lang/rust-mode/issues/288.
rustic-match-angle-brackets (not (featurep! :editor evil)))
rustic-match-angle-brackets (not (featurep! :editor evil))
;; `rustic-setup-rls' uses `package-installed-p' to determine if
;; lsp-mode/elgot are available. This breaks because Doom doesn't use
;; package.el to begin with (and lazy loads it). This is already handled
;; by the :tools lsp module, so...
rustic-lsp-setup-p nil)
(add-hook 'rustic-mode-hook #'rainbow-delimiters-mode)
(defadvice! +rust--dont-install-packages-p (orig-fn &rest args)
:around #'rustic-setup-rls
(cl-letf (;; `rustic-setup-rls' uses `package-installed-p' to determine if
;; lsp-mode/elgot are available. This breaks because Doom doesn't
;; use package.el to begin with (and lazy loads it).
((symbol-function #'package-installed-p)
(lambda (pkg)
(require pkg nil t)))
;; If lsp/elgot isn't available, it attempts to install lsp-mode
;; via package.el. Doom manages its own dependencies so we disable
;; that behavior.
((symbol-function #'rustic-install-rls-client-p)
(lambda (&rest _)
(message "No RLS server running"))))
(apply orig-fn args))))
(when (featurep! +lsp)
(add-hook 'rustic-mode-local-vars-hook #'lsp!)))
(use-package! racer
:unless (featurep! +lsp)
:hook (rustic-mode . racer-mode)
:config
(set-lookup-handlers! 'rustic-mode
:definition '(racer-find-definition :async t)
:documentation '+rust-racer-lookup-documentation))
;;
;;; Tools
(use-package! cargo
:after rust-mode
:after rustic-mode
:config
(defvar +rust-keymap
(if (boundp 'rustic-mode-map)
rustic-mode-map
rust-mode-map))
(map! :map +rust-keymap
(map! :map rustic-mode-map
:localleader
(:prefix ("b" . "build")
:desc "cargo add" "a" #'cargo-process-add

View file

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

View file

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

View file

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

View file

@ -28,12 +28,6 @@
(after! css-mode
;; css-mode hooks apply to scss and less-css modes
(add-hook 'css-mode-hook #'rainbow-delimiters-mode)
(set-company-backend! '(css-mode scss-mode)
(if EMACS26+
;; DEPRECATED css-mode's built in completion is superior in 26+
'company-capf
'company-css))
(map! :localleader
:map scss-mode-map
"b" #'+css/scss-build

View file

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

View file

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

View file

@ -12,7 +12,7 @@
;; +css.el
(package! css-mode :built-in t)
(package! less-css-mode :built-in (not (version< emacs-version "26.1")))
(package! less-css-mode :built-in t)
(package! sass-mode)
(package! stylus-mode)

View file

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

View file

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

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