Finalize core-dispatcher API

+ Add doom-auto-accept to replace YES envvar.
+ Add docstrings & documentation to all commands.
+ Make dispatcher support more than one alias.
+ Document quickstart command
+ Update autoloads/install/autoremove/update/refresh to fit new initialization workflows
+ Rename doom//info and doom//version
+ Recompile alias is now rc instead of cc
+ Improve reliability of doom//quickstart
This commit is contained in:
Henrik Lissner 2018-05-24 19:03:36 +02:00
parent 8746c12fae
commit 3261f1fd71
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395

View file

@ -1,5 +1,8 @@
;;; -*- lexical-binding: t; no-byte-compile: t; -*- ;;; -*- lexical-binding: t; no-byte-compile: t; -*-
;; Eagerly load these libraries because this module may be loaded in a session
;; that hasn't been fully initialized (where autoloads files haven't been
;; generated or `load-path' populated).
(load! autoload/packages) (load! autoload/packages)
(load! autoload/modules) (load! autoload/modules)
(load! autoload/debug) (load! autoload/debug)
@ -7,14 +10,16 @@
;; ;;
;; Dispatcher ;; Dispatcher API
;; ;;
(defvar doom-dispatch-command-alist () (defvar doom-auto-accept (getenv "YES")
"TODO") "If non-nil, Doom will auto-accept any confirmation prompts during batch
commands like `doom//packages-install', `doom//packages-update' and
`doom//packages-autoremove'.")
(defvar doom-dispatch-alias-alist () (defconst doom--dispatch-command-alist ())
"TODO") (defconst doom--dispatch-alias-alist ())
(defun doom--dispatch-format (desc &optional short) (defun doom--dispatch-format (desc &optional short)
(if (equal desc "TODO") (if (equal desc "TODO")
@ -33,26 +38,29 @@
(line-end-position)))))) (line-end-position))))))
(defun doom--dispatch-help (&optional command desc &rest args) (defun doom--dispatch-help (&optional command desc &rest args)
"TODO" "Display help documentation for a dispatcher command. If COMMAND and DESC are
omitted, show all available commands, their aliases and brief descriptions."
(if command (if command
(princ (doom--dispatch-format desc)) (princ (doom--dispatch-format desc))
(print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description")) (print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description"))
(dolist (spec (sort doom-dispatch-command-alist (dolist (spec (sort doom--dispatch-command-alist
(lambda (x y) (string-lessp (car x) (car y))))) (lambda (x y) (string-lessp (car x) (car y)))))
(cl-destructuring-bind (command &key desc _body) spec (cl-destructuring-bind (command &key desc _body) spec
(let ((alias (car (rassq command doom-dispatch-alias-alist)))) (let ((aliases (cl-loop for (alias . cmd) in doom--dispatch-alias-alist
if (eq cmd command)
collect (symbol-name alias))))
(print! " %-10s\t%s\t%s" (print! " %-10s\t%s\t%s"
command (or alias "") command (if aliases (string-join aliases ",") "")
(doom--dispatch-format desc t))))))) (doom--dispatch-format desc t)))))))
(defun doom-dispatch (args) (defun doom-dispatch (args)
"TODO" "Invoke a dispatcher command and pass ARGS to it."
(let ((help (equal (car args) "help"))) (let ((help (equal (car args) "help")))
(if help (pop args)) (if help (pop args))
(cl-destructuring-bind (command &key desc body) (cl-destructuring-bind (command &key desc body)
(let ((sym (intern (car args)))) (let ((sym (intern (car args))))
(or (assq sym doom-dispatch-command-alist) (or (assq sym doom--dispatch-command-alist)
(assq (cdr (assq sym doom-dispatch-alias-alist)) doom-dispatch-command-alist) (assq (cdr (assq sym doom--dispatch-alias-alist)) doom--dispatch-command-alist)
(error "Invalid command: %s" (car args)))) (error "Invalid command: %s" (car args))))
(if help (if help
(apply #'doom--dispatch-help command desc (cdr args)) (apply #'doom--dispatch-help command desc (cdr args))
@ -60,18 +68,31 @@
;; FIXME Clumsy way of registering commands, refactor! ;; FIXME Clumsy way of registering commands, refactor!
(defmacro def-dispatcher! (command desc &rest body) (defmacro def-dispatcher! (command desc &rest body)
"TODO" "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 (doc-string 2)) (declare (doc-string 2))
(let* ((command (doom-enlist command)) (let* ((command (doom-enlist command))
(cmd (car command)) (cmd (car command))
(alias (car (cdr command)))) (aliases (cdr command)))
`(progn `(progn
,(when alias ,(when aliases
`(map-put doom-dispatch-alias-alist ',alias ',cmd)) `(dolist (alias ',aliases)
(map-put doom-dispatch-command-alist (map-put doom--dispatch-alias-alist alias ',cmd)))
',cmd (list :desc ,desc :body (lambda (args) ,@body)))))) (map-put doom--dispatch-command-alist
',cmd (list :desc ,desc
;; FIXME Implicit args var; ew
:body (lambda (args) ,@body))))))
;; ;;
;; Dispatch commands
;;
;; Dummy dispatchers (no-op because they're handled especially)
(def-dispatcher! run (def-dispatcher! run
"Run Doom Emacs from bin/doom's parent directory. "Run Doom Emacs from bin/doom's parent directory.
@ -80,8 +101,9 @@ All arguments are passed on to Emacs (except for -p and -e).
doom run doom run
doom run -nw init.el doom run -nw init.el
Warning, this is for convenience and testing purposes, Doom will not run its WARNING: this command exists for convenience and testing. Doom will suffer
best or fastest when started in this manner.") additional overhead for be started this way. For the best performance, it
is best to run Doom out of ~/.emacs.d and ~/.doom.d.")
(def-dispatcher! (doctor doc) (def-dispatcher! (doctor doc)
"Checks for issues with your current Doom config.") "Checks for issues with your current Doom config.")
@ -89,36 +111,43 @@ best or fastest when started in this manner.")
(def-dispatcher! (help h) (def-dispatcher! (help h)
"Look up additional information about a command.") "Look up additional information about a command.")
;; ;; Real dispatchers
(def-dispatcher! quickstart (def-dispatcher! (quickstart qs)
"TODO" "Quickly deploy a private module and Doom.
(doom//quickstart))
This deploys a barebones config to ~/.doom.d. The destination can be changed
with the -p option, e.g.
doom -p ~/.config/doom quickstart
This command will refuse to overwrite the private directory if it already
exists."
(doom//quickstart args))
(def-dispatcher! (install i) (def-dispatcher! (install i)
"Installs requested plugins that aren't installed." "Installs requested plugins that aren't installed."
(doom-initialize) (doom//reload-doom-autoloads)
(when (doom//packages-install) (when (doom//packages-install doom-auto-accept)
(doom//reload-autoloads))) (doom//reload)))
(def-dispatcher! (update u) (def-dispatcher! (update u)
"Checks for and updates outdated plugins." "Checks for and updates outdated plugins."
(doom-initialize) (doom//reload-doom-autoloads)
(when (doom//packages-update) (when (doom//packages-update doom-auto-accept)
(doom//reload-autoloads))) (doom//reload)))
(def-dispatcher! (autoremove r) (def-dispatcher! (autoremove r)
"Removes orphaned plugins." "Removes orphaned plugins."
(doom-initialize) (doom//reload-doom-autoloads)
(when (doom//packages-autoremove) (when (doom//packages-autoremove doom-auto-accept)
(doom//reload-autoloads))) (doom//reload)))
(def-dispatcher! (autoloads a) (def-dispatcher! (autoloads a)
"Regenerates Doom's autoloads file. "Regenerates Doom's autoloads file.
This file tells Emacs where to find your module's autoloaded functions and This file tells Emacs where to find your module's autoloaded functions and
plugins." plugins."
(doom-initialize) (doom//reload-autoloads nil 'force))
(doom//reload-autoloads))
(def-dispatcher! (upgrade up) (def-dispatcher! (upgrade up)
"Checks out the latest Doom on this branch." "Checks out the latest Doom on this branch."
@ -136,7 +165,7 @@ to byte-compile Doom's core files, your private config or your ELPA plugins,
respectively." respectively."
(doom//byte-compile args)) (doom//byte-compile args))
(def-dispatcher! (recompile cc) (def-dispatcher! (recompile rc)
"Re-byte-compiles outdated *.elc files." "Re-byte-compiles outdated *.elc files."
(doom//byte-compile args 'recompile)) (doom//byte-compile args 'recompile))
@ -151,14 +180,14 @@ respectively."
(def-dispatcher! info (def-dispatcher! info
"Output system info in markdown for bug reports." "Output system info in markdown for bug reports."
(doom//info)) (doom/info))
(def-dispatcher! (version v) (def-dispatcher! (version v)
"Reports the version of Doom and Emacs." "Reports the version of Doom and Emacs."
(doom//version)) (doom/version))
(def-dispatcher! (refresh re) (def-dispatcher! (refresh re)
"Refresh Doom. "Refresh Doom. Same as autoremove+install+autoloads.
This is the equivalent of running autoremove, install, autoloads, then This is the equivalent of running autoremove, install, autoloads, then
recompile. Run this whenever you: recompile. Run this whenever you:
@ -167,26 +196,25 @@ recompile. Run this whenever you:
2. Add or remove `package!' blocks to your config, 2. Add or remove `package!' blocks to your config,
3. Add or remove autoloaded functions in module autoloaded files. 3. Add or remove autoloaded functions in module autoloaded files.
4. Update Doom outside of Doom (e.g. with git)" 4. Update Doom outside of Doom (e.g. with git)"
(doom-initialize) (if (let* ((doom--inhibit-reload t)
(let (reload-p) (autoremove-p (with-demoted-errors "%s" (doom//packages-autoremove)))
(when (let* ((doom--inhibit-reload t) (install-p (with-demoted-errors "%s" (doom//packages-install))))
(autoremove-p (doom//packages-autoremove)) (or autoremove-p install-p))
(install-p (doom//packages-install))) (doom//reload)
(or autoremove-p install-p)) (doom//reload-autoloads))
(doom//reload)) (doom//byte-compile nil 'recompile))
(doom//byte-compile nil 'recompile)))
;; ;;
;; Quality of Life Commands ;; Quality of Life Commands
;; ;;
;; FIXME Detect & enforce remote
(defvar doom-remote "origin" (defvar doom-remote "origin"
"TODO") "TODO")
(defun doom//upgrade () (defun doom//upgrade ()
"TODO" "Upgrade Doom to the latest version."
(declare (interactive-only t))
(interactive) (interactive)
(let ((core-file (expand-file-name "init.el" doom-core-dir)) (let ((core-file (expand-file-name "init.el" doom-core-dir))
(branch (vc-git--symbolic-ref core-file)) (branch (vc-git--symbolic-ref core-file))
@ -210,8 +238,7 @@ recompile. Run this whenever you:
(abbreviate-file-name doom-emacs-dir))) (abbreviate-file-name doom-emacs-dir)))
(if (equal current-rev rev) (if (equal current-rev rev)
(message "Doom is up to date!") (message "Doom is up to date!")
(when (or doom-auto-accept
(when (or (getenv "YES")
(y-or-n-p "Doom is out of date, update?")) (y-or-n-p "Doom is out of date, update?"))
(unless (zerop (process-file "git" nil buf nil (unless (zerop (process-file "git" nil buf nil
"checkout" (format "%s/%s" doom-remote branch))) "checkout" (format "%s/%s" doom-remote branch)))
@ -223,33 +250,31 @@ recompile. Run this whenever you:
(message "Done! Please restart Emacs for changes to take effect"))))))))) (message "Done! Please restart Emacs for changes to take effect")))))))))
(defun doom//quickstart () (defun doom//quickstart ()
"TODO" "Quickly deploy a private module and Doom.
This deploys a barebones config to `doom-private-dir', installs all missing
packages and regenerates the autoloads file."
(declare (interactive-only t)) (declare (interactive-only t))
(interactive) (interactive)
(let ((short-private-dir (abbreviate-file-name doom-private-dir))) (let ((short-private-dir (abbreviate-file-name doom-private-dir)))
(when (file-directory-p doom-private-dir) (unless (file-directory-p doom-private-dir)
(error "%s already exists! Aborting." short-private-dir)) (print! "Creating %s" short-private-dir)
(message "Creating %s directory" short-private-dir) (make-directory doom-private-dir t))
(make-directory doom-private-dir)
(let ((init-file (expand-file-name "init.el" doom-private-dir))) (let ((init-file (expand-file-name "init.el" doom-private-dir)))
(if (not (file-exists-p init-file)) (if (file-exists-p init-file)
(message "%sinit.el already exists. Skipping.") (print! "%sinit.el already exists. Skipping." short-private-dir)
(message "Copying init.example.el to %s" short-private-dir) (print! "Copying init.example.el to %s" short-private-dir)
(copy-file (expand-file-name "init.example.el" doom-emacs-dir) (copy-file (expand-file-name "init.example.el" doom-emacs-dir)
init-file))) init-file)))
(let ((config-file (expand-file-name "config.el" doom-private-dir))) (let ((config-file (expand-file-name "config.el" doom-private-dir)))
(if (file-exists-p config-file) (if (file-exists-p config-file)
(with-temp-file config-file (print! "%sconfig.el already exists. Skipping." short-private-dir)
(insert "")) (with-temp-file config-file (insert "")))))
(message "%sconfig.el already exists. Skipping.")))) (print! "Installing plugins & generating autoloads file, if necessary")
(doom-initialize) (doom//packages-install)
(let* ((doom--inhibit-reload t)
(autoremove-p (doom//packages-autoremove))
(install-p (doom//packages-install)))
(or autoremove-p install-p))
(doom//reload-autoloads) (doom//reload-autoloads)
(message "\n\nDone! Doom Emacs is ready.\n") (print! "\n\nDone! Doom Emacs is ready.\n")
(message "Remember to run M-x all-the-icons-install-fonts after starting Emacs for the first time.")) (print! "Remember to run M-x all-the-icons-install-fonts after starting Emacs for the first time."))
(provide 'core-dispatcher) (provide 'core-dispatcher)
;;; core-dispatcher.el ends here ;;; core-dispatcher.el ends here