Damn that's a mouthful. I don't know if anyone else is interested in this, but I've been hacking away at making emacs play nicely with my ideal literate programming workflow with `org-mode`. Here's a sample of the file I'm working on. The Macro stuff is about creating an HTML page for static hosting that will hold all documentation and code in a single file:
#+AUTHOR: granitrocky
#+TITLE: Literate Programming Example -- Snake
#+OPTIONS: H:3 toc:2
#+STARTUP: overview
#+MACRO: setup-file (eval (if (eq org-export-current-backend 'html) "#+SETUPFILE: https://fniessen.github.io/org-html-themes/org/theme-bigblow.setup"))
{{{setup-file}}}
# Local Variables:
# eval: (add-hook 'after-save-hook (lambda () (setq-local filename (org-html-export-to-html)) (rename-file filename "docs/index.html" t)) 0 t)
# End:
* Purpose
This document is an attempt at putting a complete Bevy game and Design Document in a single file.
We will be creating a document based on [[https://mbuffett.com/posts/bevy-snake-tutorial/][this tutorial]]
** Why?
The reason for this workflow is to keep all our documentation and code in a single place. It's practice for creating a living org doc that will be my standard for creating projects in the future.
That is, the code for this project is pulled directly from this document using ~org-babel~
* Specifications
** Use Bevy
In our [[*Cargo File \[2022-07-19 Tue\]][Cargo File]], we define Bevy 0.7 as a dependency. Bevy allows us to create a game using ECS and built in 2D and 3D renderers and compile it to wasm32 for web games. In our case, we will be making a 2D snake game and embedding it in this file.
** Show a snake
** Move the snake
** Accept User input
** Grow the snake when it collects pellets
** End the game if the snake hits a wall or its tail
** OPTIONAL Keep a score
* Code
** Cargo File [2022-07-19 Tue] :Code:
#+begin_src conf-toml :tangle "Cargo.toml"
[package]
name = "literate-snake"
version = "0.1.0"
edition = "2021"
[dependencies]
bevy = "0.7"
rand = "0.7.3"
#+end_src
** Start The Game [2022-07-19 Tue] :Code:
#+begin_src rust :tangle "src/main.rs"
use bevy::prelude::*;
fn main() {
App::new()
.run();
}
#+end_src
** Systems
** Add Camera System [2022-07-20 Wed] :Code:
#+begin_src rust :tangle "src/main.rs"
fn add_camera(mut commands: Commands){ commands. }
#+end_src
**
The real key thing here is that snippets of `main.rs` can be spread throughout the document as they need additional documentation. To do this, I had to make some wrappers around the `org-src-edit` functions.
I am proud of this bit. On opening the `org-src` edit buffer, it reads the tangled file and deletes the part that is in the block we're editing.
This gives the LSP engine, `eglot` in our case, the necessary data for completions.
The real beauty is that the file block is hidden with narrowing and not saved when the code block we're editing is saved or closed
;; Make sure rustic gets activated in the org-src block and add the original file's source code.
(defun org-babel-edit-prep:rust (babel-info)
;; This gets the second item in the "babel-info" list, which holds the code in the original src block
(setq-local src-code (nth 1 babel-info))
(setq-local buffer-file-name (expand-file-name (->> babel-info caddr (alist-get :tangle))))
(setq-local buffer-src-code (replace-regexp-in-string src-code "" (my-read-file-to-string (buffer-file-name))))
(goto-char (point-max))
(insert buffer-src-code)
(narrow-to-region (point-min) (+ (point-min) (length src-code)))
(rustic-mode)
(org-src-mode))
(defun my-delete-hidden-text ()
"Remove all text that would be revealed by a call to `widen'"
(-let [p-start (point-max)]
(widen)
(delete-region p-start (point-max))))
(define-advice org-edit-src-exit
(:before (&rest _args) remove-src-block)
(when (eq major-mode 'rustic-mode)
(my-delete-hidden-text)))
(define-advice org-edit-src-save
(:before (&rest _args) remove-src-block)
(when (eq major-mode 'rustic-mode)
(my-delete-hidden-text)))
Anyway, if anyone else finds this useful, or has any way to improve this, I'd love to hear it.