feat(cli): add 'doom make completions' for zsh

'doom make completions' will generate a rudimentary ZSH completion
script for bin/doom. It can be used from your shell dotfiles, but I
recommend caching the output with a function like:
https://github.com/hlissner/dotfiles/blob/master/config/zsh/.zshenv#L1-L14.

Then add this to your .zshrc:

  _cache doom make completions --zsh && compdef _doom doom

Ref: https://github.com/hlissner/dotfiles/blob/master/config/zsh/.zshenv#L1-L14
This commit is contained in:
Henrik Lissner 2022-06-18 23:24:23 +02:00
parent e5b7edcd8d
commit daad6bc21d
No known key found for this signature in database
GPG key ID: B60957CA074D39A3
3 changed files with 208 additions and 1 deletions

View file

@ -277,7 +277,7 @@ SEE ALSO:
(defgroup! "Development"
:docs "Commands for developing or launching Doom."
(defautoload! (ci))
;; TODO (defautoload! (make))
(defautoload! (make))
(defautoload! (run))
;; FIXME Test framework

64
core/cli/make.el Normal file
View file

@ -0,0 +1,64 @@
;;; core/cli/make.el --- file generation commands -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(load! "make/completions")
;; (load! "make/docs")
;; (load! "make/manpage")
;;
;;; Variables
;; (defvar doom-make-codeowners ()
;; "TODO")
;;
;;; Commands
(defcli! make ()
"(Re)Generate project files and boilerplate."
:partial t)
;; TODO Finish and generalize me
(defcli! (make codeowners) ()
"TODO"
:stub t
(print! (start "Generating CODEOWNERS file"))
(let ((codeowners (doom-path doom-emacs-dir ".github/CODEOWNERS")))
(with-temp-file codeowners
(insert-file-contents codeowners)
(when (re-search-forward "^# Don't edit this by hand!" nil t)
(goto-char (line-end-position))
(delete-region (point) (point-max))
(insert "\n")
(dolist (path (cdr (doom-module-load-path (list doom-modules-dir))))
(when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)/$" path)
(insert (format "%-35s @doomemacs/maintainers @doomemacs/%s-%s\n"
(concat (substring (match-string-no-properties 0 path) 1) "*")
(match-string-no-properties 1 path)
(match-string-no-properties 2 path)))))))))
;; TODO Finish me
(defcli! (make changelog) () :stub t)
;;
;;; Helpers
(defmacro doom-make--with-file (file &rest body)
(declare (indent 1))
`(let ((inhibit-read-only t))
(with-current-buffer
(or (get-file-buffer ,file)
(find-file-noselect ,file))
(save-excursion
(goto-char (point-min))
,@body
(when (buffer-modified-p)
(save-buffer))))))
(provide 'core-cli-make)
;;; make.el ends here

View file

@ -0,0 +1,143 @@
;;; core/cli/make/completions.el --- generate shell completion scripts -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
;;
;;; Variables
;; (defvar doom-make-completions-zsh-spec
;; '(("FILE" . "_files"))
;; "TODO")
;;
;;; Commands
(defcli! (make completions)
((shell ("--zsh" "--bash") "Generate a particular flavor of completion files (defaults to $SHELL)")
;; TODO (outfile ("-o" "--outfile" file))
&context context &args args)
"Generate completion scripts for a Doom-CLI script."
;; :stub t
;; (unless outfile
;; (user-error "No destination file specified"))
(let ((shell (or shell (file-name-base (getenv "SHELL"))))
;; TODO Allow this command to read other Doom binscripts, which will
;; dump their `doom-cli--table' if __DOOMDUMP is set.
;; (table (read (letenv! (("__DOOMDUMP" "1")) (apply #'sh! script-file args))))
)
(print!
"%s" (pcase (string-remove-prefix "--" shell)
("zsh" (doom-make-completions-zsh context nil))
("bash" (doom-make-completions-bash context nil))
(_ (user-error "No support for %S shell at this time" shell))))))
;;
;;; ZSH Helpers
;; TODO Write to OUTFILE when specified
(defun doom-make-completions-zsh (context _outfile)
(let* ((cli (doom-cli-get context))
(prefix (doom-cli-context-prefix context))
(options (doom-cli-help--options cli t))
(commands (doom-cli-subcommands (list prefix))))
(with-temp-buffer
(insert "#compdef " (doom-cli-context-prefix context) "\n\n"
"_globalargs=(\n ")
(doom-make-completions--zsh-insert-options
(append '(((("--help") ("-?")) . "Show help documentation")
((("--version")) . "Show version information"))
(alist-get 'global options))
"\n ")
(insert "\n)\n\n")
(doom-make-completions--zsh-insert-command '("doom"))
(mapc #'doom-make-completions--zsh-insert-command commands)
;; (insert "\n\n_doom")
(buffer-string))
;; (set-file-modes outfile #o755)
;; outfile
))
(defun doom-make-completions--zsh-insert-options (options &optional cr)
;; FIXME Refactor, generalize, and parameterize this mess
(dolist (option options)
(let* ((switches (cl-loop for (sw . args) in (car option)
if (string-prefix-p "--[no-]" sw)
collect (cons (concat "--" (string-remove-prefix "--[no-]" sw)) args)
and collect (cons (concat "--no-" (string-remove-prefix "--[no-]" sw)) args)
else collect (cons sw args)))
(args (remove "..." (cdr (car switches))))
(argspec (cl-loop for arg in args
concat
(format ":%s:%s"
(replace-regexp-in-string
":" ";" (shell-quote-argument arg))
(or (plist-get (cdr (assoc (intern-soft (downcase (car args)))
doom-cli-option-arg-types))
:zshcomp)
""))))
(multiple? (member "..." (cdr (car switches)))))
(insert (format "%s%s%s"
(if multiple?
"\\*"
(format "'(%s)'" (mapconcat #'car switches " ")))
(if (cdr switches)
(format
"{%s}" (combine-and-quote-strings
(cl-loop for (sw . _) in switches
if (and args (string-prefix-p "--" sw))
collect (concat (shell-quote-argument sw) "=")
else collect (shell-quote-argument sw))
","))
(format "%s%s" (caar switches)
(if (and args (string-prefix-p "--" (caar switches)))
"=" "")))
(format "'[%s]%s'"
(replace-regexp-in-string "'" "''" (cdr option))
(or argspec "")))
(or cr "\n")))))
(defun doom-make-completions--zsh-insert-command (command)
(let* ((commandstr (doom-cli-command-string command))
(options (alist-get 'local (doom-cli-help--options (doom-cli-get command) t)))
(subcommands (doom-cli-subcommands command 1)))
(insert "_" (replace-regexp-in-string "[- ]" "_" commandstr) "() {\n"
" local line state\n"
" _arguments -s -S -C \"${_globalargs[@]}\" \\\n ")
(doom-make-completions--zsh-insert-options options " \\\n ")
(insert "\"1: :->cmds\" \"*::arg:->args\"\n"
" case $state in\n"
" cmds)\n"
" _values \"" commandstr "\" \\\n "
(string-join
(cl-loop for command in subcommands
unless (string-prefix-p ":" (car command))
collect (format "'%s[%s]' "
(car (last command))
(or (doom-cli-short-docs (doom-cli-get command))
"TODO")))
" \\\n ")
"\n ;;\n"
" args)\n"
" case $line[1] in\n "
(string-join
(cl-loop for command in subcommands
unless (string-prefix-p ":" (car command))
collect (format "%s) _%s ;; "
(car (last command))
(replace-regexp-in-string "[- ]" "_" (doom-cli-command-string command))))
"\n ")
"\n esac\n"
" ;;\n"
" esac\n"
"}\n")))
;;
;;; Bash helpers
(defun doom-make-completions-bash (context _outfile)
(user-error "Bash completion exporter hasn't been implemented yet!"))
;;; completions.el ends here