2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00
2026-01-16 00:23:20 +01:00

loom

why

i wanted to write docs that contains working code where the code is extracted and can actually be compiled and run. knuth called this literate programming and there are other tools for this but they're either retarded, over-done, or don't work with markdown. loom fixes that

code blocks with a file= attribute get extracted and written to disk whereas fragments with fragment= can be reused across blocks using <<name>> transclusion. cycles are detected and that's basically it

how

you write markdown like this:

```python file=gay.py
print("im gay")
```

then run loom on it:

./loom gay.md

and it generates gay.py with the contents of that code block

fragments

sometimes you want to define a piece of code once and include it in multiple places. fragments let you do that, for example:

```rust fragment=imports
use std::io;
use std::fs;
```

```rust file=main.rs
<<imports>>

fn main() {
    // whatever
}
```

```rust file=lib.rs
<<imports>>

pub fn autism() {}
```

when loom processes this both main.rs and lib.rs will have the imports inlined at the top

fragments can reference other fragments too since loom builds a dependency graph and does a topological sort to resolve everything in the right order. if you create a cycle it will tell you

append mode

if you want multiple code blocks to contribute to the same file, use append=true:

```python file=script.py
def foo():
    pass
```

```python file=script.py append=true
def bar():
    pass
```

both blocks end up in script.py in order

building

loom is written in OCaml so you'll need OCaml installed with the str and unix libraries (they're usually included)

make build
sudo make install

for the js version (used by the obsidian plugin and available as an npm package):

make js

this uses dune and js_of_ocaml to compile the core to js

output goes to js/dist/loom.js

to clean everything:

make clean

running

you can execute code blocks directly from your markdown. add run=<mode> to make a block runnable:

```python run=isolated
def square(x):
    return x * x
```

run modes

  • isolated which runs the block standalone. input is appended as code
  • this runs the block as-is and the input goes to stdin
  • file will run the full assembled file. input goes to stdin also

input blocks

provide test input with a separate block:

```python fragment=math run=isolated
def factorial(n):
    return 1 if n <= 1 else n * factorial(n - 1)
```

```input for=math
print(factorial(5))
print(factorial(10))
```

when you run the math fragment the input block gets appended. output:

120
3628800

supported languages

interpreted: python, js, ts, ruby, bash, lua, go, OCaml

compiled: rust, C, C++

ill add more when i can be bothered to (not likely)

obsidian

there's a plugin that integrates loom into obsidian bc my friend is autistic and likes things to look pretty

make obsidian

then copy obsidian-plugin/main.js, manifest.json and styles.css to your vault at and enable the plugin in settings

commands

Ctrl+P and type loom. use your brain

there's also a play button in the ribbon for quick execution :)

embed

process current file extracts all code blocks with file= attributes which writes the files and replaces the code blocks with embeds like ![[utils.py]]

live sync

enable live sync keeps the code blocks in your markdown and sets up bidirectional sync so that as you type in the code block the generated file updates in real time

if you edit the generated file directly the changes sync back to the markdown

blocks with transclusion or append mode only sync forward

js api

the js build is published to npm as loom-md

you can use it in node or electron apps:

const { loom } = require('loom-md');

const markdown = `
\`\`\`python file=test.py
print("hello")
\`\`\`
`;

const result = loom.process(markdown);

if (result.success) {
    for (const file of result.files) {
        console.log(file.path, file.content);
    }
} else {
    console.error(result.error);
}

the result object has success, files (array of {path, content}) and error (string or null)

ts types are included

examples

these are more involved examples showing consistent hashing implemented in both OCaml and Haskell (which was initially my only intention until my friend thought i could do better) from the same markdown file

it demonstrates fragment composition and multi language extraction

run loom on example.md to generate the source files and then use the makefile in examples/ to compile them, like so:

./loom example.md
cd examples && make
./examples/hashring-ml
./examples/hashring-hs
Description
markdown compiler with fragment composition + topological dependency resolution + polyglot code extraction
Readme 78 KiB
Languages
OCaml 92.9%
Makefile 6.1%
Dune 1%