Open source
Cindermark
The incremental Markdown parser behind Ember Notes' live editor. A Rust core with first-class Swift bindings for iOS and macOS — UTF-16 offsets for TextKit, re-parsing only what changed, in microseconds.
What's new in 0.2.0
An incremental release — same parser, more of the syntax a notes app leans on, and a leaner way to depend on it.
Nested lists
Bullets, ordered lists, and checkboxes indented for nesting — up to 32 columns — now parse as nested items instead of degrading into indented code. Column-based and editor-safe, so the nesting is reconstructable in editable mode.
WebAssembly-powered playground
The live editor below runs the new WasmParser surface — parseJson, keystroke, resetState, interactive checkbox toggling, and colored highlight spans — compiled straight from the Rust source.
Pure-Rust by default
cargo add cindermark now pulls only three crates (memchr, rustc-hash, unicode-segmentation). The UniFFI/Swift bindings moved behind an opt-in ffi feature. Published on crates.io and Swift Package Manager.
Type. Watch it parse.
The real parser, compiled to WebAssembly and running in a Web Worker — nothing you type leaves this page. The strip beside the editor is the whole document; each keystroke lights up exactly what got re-parsed.
UTF-16 offsets, natively
Every block and inline span carries UTF-16 ranges — the coordinate system of NSTextStorage and TextKit. No conversion layer, no off-by-one emoji bugs. This page's preview is styled purely from those offsets.
Incremental re-parsing
After an edit, only the dirty blocks are re-parsed and the rest shift. The minimap above shows it happening in real time.
Single-pass architecture
One pass produces the block + inline AST, document stats, wiki links, and headings. It replaced 8–9 regex passes in the app it came from.
First-class Swift bindings
Generated with UniFFI — a real Swift API, not a C header. Rust panics surface as catchable Swift errors, never app crashes.
Drop it into an iOS or macOS app
Cindermark's reason for being is native Apple text editors. Add it with Swift Package Manager and you get a real Swift API — no Rust toolchain, nothing to compile. It ships as a prebuilt XCFramework, targets iOS 16+ and macOS 13+, and every block and inline span it returns is addressed in UTF-16 — the same coordinate system as NSTextStorage and TextKit, so styling maps on with no conversion layer.
import Cindermark
let parser = CindermarkParser()
let result = parser.parseEditable(text: textView.string)
for block in result.blocks {
let range = NSRange(
location: Int(block.utf16Start),
length: Int(block.utf16End - block.utf16Start))
// apply attributes to `range` in your NSTextStorage
}// On each edit, re-parse just the dirty window
let update = parser.parseEditableIncrementalStyleOnly(
text: newText,
editUtf16Start: editStart,
editOldUtf16Len: oldLen,
editNewUtf16Len: newLen)
// Restyle blocks update.dirtyStart ..< update.dirtyEndMeasured, not promised
criterion benchmarks, release profile with fat LTO. Native numbers — the WASM build above is typically 1.5–3× slower than native and still feels instant.
| Benchmark | Apple Silicon | x86_64 Linux |
|---|---|---|
| Incremental keystroke, 500-line note | ~117 µs | ~255 µs |
| Incremental keystroke, 2,500-line note | ~562 µs | ~1.3 ms |
| Incremental keystroke, 10,000-line note | ~2.3 ms | ~8.7 ms |
| Full parse, 2,500-line note | ~3.2 ms | ~7.3 ms |
Use it
.package(
url: "https://github.com/renedeanda/cindermark",
from: "0.2.0"
)
import Cindermark
let parser = CindermarkParser()
let result = parser.parseEditable(text: "# Hi")[dependencies]
cindermark = "0.2"
use cindermark::CindermarkParser;
// Pure-Rust by default — pass None for CommonMark-clean output.
let parser = CindermarkParser::new(None);
let result = parser.parse("# Hello".to_string());MIT licensed. 457 tests, including checks that every incremental parse must equal the full parse. crates.io · docs.rs · Read the source.
Building something with it? A star on GitHub helps other developers find it — and issues and pull requests are welcome.
Frequently asked
Can I use Cindermark in a Swift or iOS app?
Yes. Add it with Swift Package Manager (from: "0.2.0"). It ships as a prebuilt XCFramework binary target, so there is no Rust toolchain to install and nothing to compile in your Xcode build. It supports iOS 16+ and macOS 13+.
How is it different from a normal Markdown parser?
Most Markdown parsers render a document once. Cindermark is built for editing: after each edit it re-parses only the blocks that changed, and every block and inline span carries UTF-16 offsets that map directly onto NSTextStorage and TextKit — the coordinate system Swift text editors already use.
Does it work from Rust too?
Yes. cargo add cindermark (cindermark = "0.2") pulls a pure-Rust build with three dependencies — memchr, rustc-hash, and unicode-segmentation. The Swift/UniFFI bindings live behind an opt-in "ffi" feature, so a Rust-only project stays lean. It is published on crates.io with docs on docs.rs.
Which Markdown syntax does it support?
CommonMark core plus the extensions a notes app needs: headings, nested bullet, ordered, and task lists, tables with alignment, fenced code blocks, blockquotes, footnotes, callouts, and wiki links — plus inline bold, italic, strikethrough, inline code, and highlights.
Is it open source and production-ready?
It is MIT-licensed and ships in Ember Notes' live editor on iPhone, iPad, and Mac. The parser is covered by 457 tests, including checks that every incremental parse result equals a full re-parse.
See it in its natural habitat
Cindermark was built for Ember Notes — a fast, private markdown notes app for iPhone, iPad, and Mac.
Meet Ember Notes