Skip to content

zamlz/mdutils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Markdown Utils

Version: 0.1.0

Why?

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.

Features

  • 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 let statements
  • 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 md itself!

Disclaimer

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!

Usage

md supports the following commands: (Please click on the links to see more detailed documentation)

  • new: Create simple elements like new tables
  • table: Format and apply formulas to existing tables
  • code: Evaluation of code blocks
  • toc: 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>

TOC Example

cat EXAMPLE.md | md toc

Input:

# Title Heading
<!-- md-toc: -->
## Heading 1
### Heading 2

Output:

# Title Heading
<!-- md-toc: -->
- [Heading 1](#heading-1)
  - [Heading 2](#heading-2)
<!-- md-toc: end -->
## Heading 1
### Heading 2

Table Example

cat EXAMPLE.md | md table

Input:

| 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 -->

Code Example

cat EXAMPLE.md | md code

Input:

```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" -->

New Example

md new table:7:7

Output:

|     |     |     |     |     |     |     |
| --- | --- | --- | --- | --- | --- | --- |
|     |     |     |     |     |     |     |
|     |     |     |     |     |     |     |
|     |     |     |     |     |     |     |
|     |     |     |     |     |     |     |
|     |     |     |     |     |     |     |
|     |     |     |     |     |     |     |
|     |     |     |     |     |     |     |

Idempotency

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

Meta-Programming

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.

Examples

The examples/ directory contains real-world usage examples:

Each example demonstrates different features and can be processed with md commands.

Development

This project uses Nix flakes for reproducible builds and development environments.

Build

Build the project using Nix:

nix build

The binary will be available at md.

Run

Run the built binary:

md table < input.md

Or run directly through the development shell:

nix develop --command cargo run -- table < input.md

Test

Run all tests (389 total tests including unit, integration, and doc tests):

nix develop --command cargo test

Run specific test:

nix develop --command cargo test test_name

Run tests for a specific module:

nix develop --command cargo test table::
nix develop --command cargo test code::
nix develop --command cargo test toc::

Debug

Enter the development shell for interactive development:

nix develop

Inside 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 code

For debugging with GDB or LLDB:

nix develop --command cargo build
nix develop --command gdb ./target/debug/md

Troubleshooting

Binary not found after nix build

After 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/

Command-specific issues

For troubleshooting specific to each command, see:

Getting help

  • Check the detailed documentation in docs/
  • View examples in examples/
  • Report issues on GitHub (if repository is public)

About

Useful markdown utilities

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published