Skip to content

Instantly share code, notes, and snippets.

@hlissner
Last active July 16, 2022 21:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hlissner/ba8c3b4c6f37c24ff27b72194942b7aa to your computer and use it in GitHub Desktop.
Save hlissner/ba8c3b4c6f37c24ff27b72194942b7aa to your computer and use it in GitHub Desktop.
Examples of Doom CLI scripts
#!/usr/bin/env doomscript
;; An example script showing off how use Doom's CLI framework to write your own.
(defcli! (capture)
((key ("-k" "--key" key))
&input input &args args)
"Open an org-capture window."
(exit! "emacsclient" "-a" "" "-e"
(format "(+org-capture/open-frame %S %S)"
(concat input (string-join args " "))
key)))
;;
;;; Let 'er rip
(run! "capture" (cdr (member "--" argv)))
;; How to use this script:
;;
;; $ capture --key t "Do my homework"
#!/usr/bin/env doomscript
;; An example Doom CLI script for anyone aspiring to write their own.
;;
;; A few niceties that come with Doom's CLI framework:
;; - Implicit documentation. Try 'say --help', for example. Or 'say goodbye
;; --help'.
;; - Option/subcommand/argument parsing.
;; - (TODO)
(defcli! say (&input input &args args)
"Repeats whatever you say back at you.
Supports text piped in via stdin."
(if input
(print! "Found this in stdin: %s" input)
(print! "%s" (markup (join args)))))
(defcli! (say hello) (name &context context)
"Say hello to the user!
ARGUMENTS:
NAME
A required argument: the name of the user. Defaults to `user-login-name'."
(print! "Executed: %s" (doom-cli-context-command context))
(print! "Hello %s" (markup (or name user-login-name))))
(defcli! (say goodbye)
((lang ("--lang" "en|dk|fr") "Language to say goodbye in")
name)
"Say goodbye to the user!"
(print! "%s %s"
(pcase lang
("en" "Goodbye")
("dk" "Farvel")
("fr" "Au revoir"))
(markup (or name user-login-name))))
;;
;;; Let 'er rip
;; Now to finally execute your script. By this point, all commands should be
;; defined. The first argument should be the prefix for any commands you've
;; defined before this point (and by convention, should match the basename of
;; this file).
;;
;; The rest of `run!'s arguments are the arguments to this script. They may be
;; nested lists (they'll be flattened). The shebang line uses "--" to separate
;; arguments for Emacs from those intended for your script, so we exploit that
;; to grab the ones we want:
(run! "say" (cdr (member "--" argv)))
#!/usr/bin/env doomscript
;; An example script showing off how use Doom's CLI framework to write your own.
(defcli! (tangle)
((all? ("-a" "--all"))
(lang ("-l" "--lang" lang))
(print? ("-p" "--print"))
&multiple
(tags ("-t" "--tag" "--and" "--or" tag) "Which org tags to target when tangling")
&args paths)
"A command line interface for tangling org-mode files.
Useful for literate configs that rely on command line workflows to build it.
ARGUMENTS:
PATHS
A list of files to tangle.
OPTIONS:
--tags
Can be specified multiple times. This tidbit of documentation is appended to
the one-liner summary given above."
(dolist (path paths)
(let ((backup (make-temp-file (file-name-base file) nil ".backup.org")))
(unwind-protect
;; Prevent slow hooks from interfering
(let (org-mode-hook org-confirm-babel-evaluate)
;; We do the ol' switcheroo because `org-babel-tangle' writes
;; changes to the current file, which would be imposing on the user.
(copy-file file backup t)
(with-current-buffer (find-file-noselect file)
;; Tangling doesn't expand #+INCLUDE directives, so we do it
;; ourselves, since includes are so useful for literate configs!
(org-export-expand-include-keyword)
(org-babel-tangle nil nil lang)))
(ignore-errors (copy-file backup file t))
(ignore-errors (delete-file backup))))))
;;
;;; Let 'er rip
;; Now to finally execute your script. By this point, all commands should be
;; defined. The first argument should be the prefix for any commands you've
;; defined before this point (and by convention, should match the basename of
;; this file).
;;
;; The rest of `run!'s arguments are the arguments to this script. They may be
;; nested lists (they'll be flattened). The shebang line uses "--" to separate
;; arguments for Emacs from those intended for your script, so we exploit that
;; to grab the ones we want:
(run! "tangle" (cdr (member "--" argv)))
;; How to use this script:
;;
;; $ tangle --lang emacs-lisp config.org
;; $ tangle --lang bash --print dotfiles.org > script.sh
;; $ tangle --and work --and finance --or gamedev notes.org
;;
@benWindsorCode
Copy link

Just to link back: here hlissner explains how to set up a file from scratch to run with doomscript system doomemacs/doomemacs#6494 (comment)

@hlissner very cool system, thanks so much for making it! Really looking forward to this / batch being supported. Just used the above to setup org agenda html export and worked like a charm (side note: on wsl2 I had to run dos2unix on the doomscript file to get it to work else I got error '/usr/bin/env: ‘doomscript’: No such file or directory' , or trying to just run 'doomscript' I got '/usr/bin/env: ‘bash\r’: No such file or directory' which was the real heart of the problem)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment