Version: 0.1.0
A Rust CLI tool for markdown processing with multiple subcommands. This is heavily based on some of the useful features I used when I used to use GNU/Emacs and Org-mode. Spreadsheet and literate programming were really cool features but were limited to GNU/Emacs. Emacs is really awesome, but if I needed to edit markdown files, I wanted to be able to do some of the same things. And even more so, I wanted to do so in any text editor! Editors like VIM, NeoVIM, Kakoune, Helix, etc. allow you to take a selection of text and pipe it to some program in your path and then replaces the highlighted text with it's output. This tool does just that. It is not editor specific and utilizes HTML comments in markdown (which is valid syntax) to tag and extend markdown with some of these capabilities.
- Table Formatting - Auto-align and format markdown tables
- Spreadsheet Formulas - Excel-like formulas with vectors, matrices, and functions (
sum,avg,min,max,count,prod) - Code Execution - Execute code blocks and capture output directly in markdown
- Table of Contents - Auto-generate TOCs with GitHub-style anchors
- Cross-Table References - Reference data between tables using table IDs
- Variables - Store intermediate results in formulas with
letstatements - Matrix Operations - Transpose (
.T), matrix multiplication (@), ranges (A1:C3) - Idempotent - Running commands multiple times produces the same result
- Editor Agnostic - Works with any editor that can pipe text (Vim, Neovim, Kakoune, Helix, etc.)
- Meta-Programmable - This README and all docs are generated using
mditself!
This tool is entirely vibe-coded. It started off as an experiment in vibe-coding and I figured it was simple enough to define and structurely build up in an interative manner. I had the features and design in my head but I just didn't have the time to work on a project like this. It's simple enough where I can define everything exactly to what I need, but also complex enough where it would take me a while.
In any case, you have been warned!
md supports the following commands:
(Please click on the links to see more detailed documentation)
new: Create simple elements like new tablestable: Format and apply formulas to existing tablescode: Evaluation of code blockstoc: Generation of table of contents
All commands (with the exception of new) operate with the idea that
it reads from STDIN and then tranforms the input to produce some output
to STDOUT. The primary use-case for this functionality is with an editor
that can take the current selection and pipe it to this tool. This makes
the tool editor agnostic (for the most part).
cat EXAMPLE.md | md <command>cat EXAMPLE.md | md toc
Input:
# Title Heading
<!-- md-toc: -->
## Heading 1
### Heading 2Output:
# Title Heading
<!-- md-toc: -->
- [Heading 1](#heading-1)
- [Heading 2](#heading-2)
<!-- md-toc: end -->
## Heading 1
### Heading 2
cat EXAMPLE.md | md tableInput:
| x | y | z |
| --- | --- | --- |
| 2 | 3 | 4 |
| | | |
<!-- md-table: _2 = _1 ^ 3 -->Output:
| x | y | z |
| --- | --- | --- |
| 2 | 3 | 4 |
| 8 | 27 | 64 |
<!-- md-table: _2 = _1 ^ 3 -->cat EXAMPLE.md | md codeInput:
```python
def collatz_sequence(n):
while n != 1:
yield n
n = n // 2 if n % 2 == 0 else 3 * n + 1
yield 1
start_num = 300
result_sequence = list(collatz_sequence(start_num))
print(f"Collatz sequence for {start_num}:\n{result_sequence}")
```
<!-- md-code: id="code-test"; bin="python3" -->Output:
```python
def collatz_sequence(n):
while n != 1:
yield n
n = n // 2 if n % 2 == 0 else 3 * n + 1
yield 1
start_num = 300
result_sequence = list(collatz_sequence(start_num))
print(f"Collatz sequence for {start_num}:\n{result_sequence}")
```
<!-- md-code: id="code-test"; bin="python3" -->
Output:
```
Collatz sequence for 300:
[300, 150, 75, 226, 113, 340, 170, 85, 256, 128, 64, 32, 16, 8, 4, 2, 1]
```
<!-- md-code-output: id="code-test" -->md new table:7:7Output:
| | | | | | | |
| --- | --- | --- | --- | --- | --- | --- |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |Idempotency means that the following is true,
command(document) == command(command(document))
This tool is ONLY idempotent if the following conditions are met!
- code blocks are running deterministic code
- table formulas are deterministic and not self-referencial
The tool is designed to allow meta-programming of itself! This means that
this very README.md file is built and modified using the same md
tool. This includes all files in the docs/ sub-folder.
The examples/ directory contains real-world usage examples:
data-analysis.md- Data analysis workflowsgrades.md- Grade calculation spreadsheetmeta-programming.md- Usingmdto document itselfproject-docs.md- Project documentation examplescientific-computation.md- Scientific computing examples
Each example demonstrates different features and can be processed with md commands.
This project uses Nix flakes for reproducible builds and development environments.
Build the project using Nix:
nix buildThe binary will be available at md.
Run the built binary:
md table < input.mdOr run directly through the development shell:
nix develop --command cargo run -- table < input.mdRun all tests (389 total tests including unit, integration, and doc tests):
nix develop --command cargo testRun specific test:
nix develop --command cargo test test_nameRun tests for a specific module:
nix develop --command cargo test table::
nix develop --command cargo test code::
nix develop --command cargo test toc::Enter the development shell for interactive development:
nix developInside the shell, you can use standard Cargo commands:
cargo build # Build the project
cargo run -- table # Run the project with table subcommand
cargo test # Run tests
cargo check # Check for errors without building
cargo clippy # Run linter
cargo fmt # Format codeFor debugging with GDB or LLDB:
nix develop --command cargo build
nix develop --command gdb ./target/debug/mdAfter running nix build, the binary is available at ./result/bin/md. You can either:
- Use the full path:
./result/bin/md table < input.md - Add to PATH:
export PATH="$PWD/result/bin:$PATH" - Copy to a location in your PATH:
cp result/bin/md ~/.local/bin/
For troubleshooting specific to each command, see:
- Table formulas - See docs/table.md
- Code execution - See docs/code.md
- TOC generation - See docs/toc.md