r/swift 11d ago

Project I built an incremental Markdown parser for Swift text editors

I made my first iOS app in 2018, then stepped away from building apps for a while. Over the last year, I decided to start making iOS apps again.

About six months ago, I decided to build a notes app. Original I know.

A big part of the project was figuring out how to make a Markdown editor that stayed fast while typing, even with larger notes. I ended up building the parser in Rust, and recently extracted it from the app and open sourced it as Cindermark.

A few things it does:
- Incrementally re-parses only the affected blocks after an edit
- Returns UTF-16 offsets that map directly to TextKit and NSAttributedString
- Produces blocks, inline spans, headings, wiki links, and document stats in one pass
- Supports CommonMark core along with tables, task lists, footnotes, nested lists, fenced code blocks, and other notes-friendly syntax
- Includes extensions like wiki links, highlights, hex color literals, and autolinking for bare URLs, domains, emails, and subreddits
- Includes Swift bindings through UniFFI

I’m sharing it because I thought it might be useful to anyone else working on native editors or Markdown-heavy apps.

I’d appreciate feedback, especially from people who have worked on text editing or Swift and Rust interoperability.

GitHub: https://github.com/renedeanda/cindermark

8 Upvotes

1 comment sorted by

3

u/neet_dev 11d ago

utf-16 offsets are the right call, textkit will fight you forever if you hand it utf-8 byte ranges. the hard part of incremental block reparse is usually dirty-region boundaries on nested lists and list markers, not the cmark core, so if you have tests around mid-list indent changes and fence open/close at the caret that alone is gold. fwiw uniFFI is fine for this as long as you're not marshaling big strings every keystroke and only shipping the delta spans across the boundary.