aboutsummaryrefslogtreecommitdiff
path: root/doom.d/modules/literate.org
blob: 562fb71327b5d6f2d8cfeb7d0be5495b06b41485 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#+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.
#+BEGIN_SRC elisp
(setq oreodave/literate/bin-dir (expand-file-name (concat doom-private-dir "bin/")))
(setq oreodave/literate/preloaded-files (list "README.org" "modules/packages.org"
                                              "modules/config.org" "modules/literate.org"))
#+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 oreodave/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
#+BEGIN_SRC elisp
(defun oreodave/literate/destination(SRC)
  "Parse a src.org file to a bin/src.el file"
  (replace-regexp-in-string ".*/\\(\\w+\\).org"
                            (expand-file-name (concat oreodave/literate/bin-dir "\\1.el")) SRC))
#+END_SRC
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.
* Parser
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 oreodave/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
            (or (and (zerop (call-process
                             "emacs" nil output nil
                             "-q" "--batch"
                             "-l" "ob-tangle"
                             "--eval" (format "(org-babel-tangle-file %S %S)"
                                              SRC DEST)))
                     (with-current-buffer output
                       (message "%s" (buffer-string))
                       t))
                (warn (format "Problem with tanging %S to %S" SRC DEST)))
          (kill-buffer output))))))
#+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 oreodave/literate/compile-hook ()
  (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 "modules")))
             (not (string= buffer-file-name (expand-file-name (concat doom-private-dir "README.org")))))
    (oreodave/literate/tangle buffer-file-name (oreodave/literate/destination buffer-file-name))))

(after! org
  (add-hook 'after-save-hook #'oreodave/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.
#+BEGIN_SRC elisp
(defun oreodave/literate/tangle-all (&optional location)
  "Tangle all org files in `location' to el files in the `destination'"
  (or location (setq location doom-private-dir))
  (interactive)
  (message "Starting compilation process")
  (let ((files (directory-files-recursively location ".org")))
    (dolist (file files)
      (message "Compiling and parsing %s" file)
      (oreodave/literate/tangle file (oreodave/literate/destination file)))))
#+END_SRC
The location is not set because this function could be easily programmed to use
multiple /differing/ sources to produce the config.
* Load configuration
Final step of the literate cycle: load the config for the first time.
#+BEGIN_SRC elisp
(let ((files (directory-files-recursively "~/.doom.d/" ".org"))) ; Load
    (dolist (file (oreodave/literate/remove-mult oreodave/literate/preloaded-files files))
      (load (oreodave/literate/destination file))))
#+END_SRC
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