Async shell command gives me a buffer for my output, is faster as it's async rather than blocking my Emacs, and all around better to use.
109 lines
5.1 KiB
Org Mode
109 lines
5.1 KiB
Org Mode
#+TITLE: Literate configuration
|
|
|
|
* Preclude
|
|
My setup to produce a literate configuration. Allows me to write org files all
|
|
around the /doom-private-dir/ and access them. Also shaved like 0.2s off my
|
|
loading time.
|
|
* Constants
|
|
Initialise some basic constants for where stuff is.
|
|
- literate/bin-dir: Where to compile to
|
|
- literate/preloaded-files: Relative to ~$DOOM~, which files are already
|
|
preloaded/don't need to be compiled
|
|
#+BEGIN_SRC elisp
|
|
(defvar dx:literate/bin-dir (expand-file-name (concat doom-private-dir "bin/")) "Directory to store elisp compiled files")
|
|
(defvar dx:literate/load-files '("~/.doom.d/org/personal.org") "Files to load after compilation")
|
|
#+END_SRC
|
|
* Remove function
|
|
When loading the lisp, we need to load everything excluding "config.el"
|
|
(preloaded by doom) and "literate.el" (loaded by "config.el"). We'll make a very
|
|
specific remove function that will remove entries from a given list and return
|
|
the new list, given the fact that the files variable will be a list of fully
|
|
expanded file names.
|
|
#+BEGIN_SRC elisp
|
|
(defun dx:literate/remove-mult (remove-files files)
|
|
"Remove any occurrences of `remove-files' from `files'"
|
|
(let ((parsed-remove-files (map 'list
|
|
#'(lambda (i) (expand-file-name (concat doom-private-dir i)))
|
|
remove-files))) ; Generate a list of all fully expanded files to remove
|
|
(remove-if #'(lambda (l) (member l parsed-remove-files)) files))) ; remove any files that are in the remove-files
|
|
#+END_SRC
|
|
* Destination for parser
|
|
Generate the destination for a literate config org file to parse to, in this
|
|
case the bin folder in the private directory
|
|
This is not fitted onto the parser because the parser could be fitted to
|
|
multiple /differing/ outputs easily if it isn't specified a destination.
|
|
#+BEGIN_SRC elisp
|
|
(defun dx:literate/destination(SRC)
|
|
"Parse a src.org file to a bin/src.el file"
|
|
(replace-regexp-in-string ".*/\\(\\w+\\).org"
|
|
(expand-file-name (concat dx:literate/bin-dir "\\1.el")) SRC))
|
|
#+END_SRC
|
|
* Parser
|
|
** Tangle function
|
|
First we need to get some sort of parser which can, given a source org file and
|
|
a destination, parse and produce an Emacs lisp file. We'll copy this from the
|
|
literate module of doom.
|
|
#+BEGIN_SRC elisp
|
|
(defun dx:literate/tangle (SRC DEST)
|
|
"Tangle a source org file into a destination el file using a new emacs instance"
|
|
(let ((default-directory doom-private-dir))
|
|
(when (file-newer-than-file-p SRC DEST)
|
|
(let ((output (get-buffer-create "*org-tangle*")))
|
|
(unwind-protect
|
|
(async-shell-command
|
|
(concat "emacs "
|
|
"-q " "--batch "
|
|
"-l " "ob-tangle "
|
|
"--eval "
|
|
(format "'(org-babel-tangle-file %S %S)'" SRC DEST))
|
|
"*org-tangle-messages*"
|
|
"*org-tangle-errors*"))))))
|
|
#+END_SRC
|
|
* Hook on save
|
|
Now we need to make a hook function that, when the current buffer is an org file
|
|
in the doom directory, will run the literate config procedure from above. Use
|
|
this hook function and add it to the after-save-hook once org mode has been
|
|
loaded. README.org has been added as an exception because it doesn't contain
|
|
literate contents.
|
|
#+BEGIN_SRC elisp
|
|
(defun dx:literate/compile-hook ()
|
|
"Any org file within $DOOM/org will be compiled on save"
|
|
(when (and (eq major-mode 'org-mode)
|
|
(or (file-in-directory-p buffer-file-name doom-private-dir)
|
|
(file-in-directory-p buffer-file-name (concat doom-private-dir "org")))
|
|
(not (string= buffer-file-name (expand-file-name (concat doom-private-dir "README.org")))))
|
|
(dx:literate/tangle buffer-file-name (dx:literate/destination buffer-file-name))))
|
|
|
|
(after! org
|
|
(add-hook 'after-save-hook #'dx:literate/compile-hook))
|
|
#+END_SRC
|
|
* Procedure for all files
|
|
A procedure that parses all the org files in a given directory into Emacs lisp
|
|
files, using the parser function made. Assume all org files in the "location"
|
|
directory contribute to the config.
|
|
The location is not set because this function could be easily programmed to use
|
|
multiple /differing/ sources to produce the config. The tangle function is set
|
|
because this is the function we'll be using for tangling all org files to ELisp files.
|
|
#+BEGIN_SRC elisp
|
|
(defun dx:literate/tangle-all (&optional location)
|
|
"Tangle all org files in `location' to el files in the `destination'"
|
|
(interactive)
|
|
(or location (setq location doom-private-dir))
|
|
(message "Starting compilation process")
|
|
(let ((files (directory-files-recursively location ".org")))
|
|
(dolist (file files)
|
|
(message "Compiling and parsing %s" file)
|
|
(dx:literate/tangle file (dx:literate/destination file)))))
|
|
#+END_SRC
|
|
* Load configuration
|
|
Final step of the literate cycle: load the config for the first time.
|
|
Remove the config.el and literate.el files from the load list because:
|
|
1) config.org is preloaded by doom
|
|
2) literate.org is loaded by config.org, thus no need to reload it
|
|
|
|
#+BEGIN_SRC elisp
|
|
(let ((files (directory-files-recursively "~/.doom.d/org/" ".org"))) ; Load
|
|
(dolist (file (dx:literate/remove-mult dx:literate/preloaded-files files))
|
|
(load (dx:literate/destination file))))
|
|
#+END_SRC
|