Skip to content
bitemyappPublic

About

A Rust rewrite of Downright, the native macOS Markdown reader/editor — pixel-exact with the Swift original

Resources

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

Upleft

Upleft is a Rust rewrite of Downright, the native macOS Markdown reader and editor. It uses AppKit and TextKit 2 through objc2.

The goal is strict: the same output as Downright, pixel for pixel, at the same speed or faster. Every layer is checked against the Swift original, which is built from source out of vendor/downright.

Status

The layers below are ported in this order. A layer counts as done only when the harness agrees with the original on it.

Layer Swift source Rust crate Conformance gate Status
cmark-gfm (C) → pulldown-cmark swiftlang/swift-cmark @ 7898f1b Upleft's fork of pulldown-cmark 0.13.4 (vendor/pulldown-cmark) plus the adapter in upleft-markup; upleft-cmark-gfm-sys (the C sources) is now only a test oracle syntax-tree dump identical replaced: no shipped binary links C Markdown code. The fork's ENABLE_CMARK_GFM_COMPAT option parses as cmark-gfm does wherever CommonMark 0.31 and cmark-gfm disagree (the fork's UPLEFT.md lists each case), and the adapter rebuilds cmark-gfm's tree and source ranges, quirks included. Against the C parser it is identical on 251/251 corpus documents, 744/744 spec examples, 7960/7960 corpus documents after the incremental suite's edits, and every seeded mutated, random and line-built document tried, several million in all. No difference is known; see docs/KNOWN-DIFFERENCES.md.
swift-markdown converter apple/swift-markdown @ 27b7fc1 upleft-markup syntax-tree dump identical done: markup 915/915 corpus documents identical; the 5,000-line document parses in 0.9 ms vs 10.6 ms in Swift (1.5 ms with the old cmark-gfm converter)
MarkdownCore Sources/MarkdownCore upleft-core parse dump identical done: parse 915/915 corpus documents identical (plus 14/14 UTF-8 edge cases); the text-level functions (core-text, core-io) are identical on 915/915 documents and 33/33 byte-level edge cases. The 5,000-line document parses in 3.9 ms vs 24.1 ms in Swift, and every other benchmarked stage is at least as fast.
MarkdownRender Sources/MarkdownRender upleft-render decorate dump and render pixels identical done. The text view, every object fragment (lists, code, callouts, tables, images, rules, front matter, math, Mermaid), the density gutter and the decoration engine: render 3660/3660 (every corpus document in 4 variants), render-state 100/100 (all themes, Read mode, below the fold, highlights, change marks, folding, zoom, streamed appends, edits), render-dark-themes 100/100, render-images 28/28, decorate 5490/5490, incremental 2745/2745, displaymap 2745/2745. All captures are headless. Wholesale decoration is 0.44× Swift's time and document open to first frame 0.79×. See docs/VALIDATION.md.
SwiftMath Vendor/SwiftMath upleft-math math pixels identical done: math-image and math-tree each 15420/15420 identical (1285 inputs × 6 themes × light and dark); parse, typeset and render together take 137–175 ms vs 263–278 ms in Swift
beautiful-mermaid + ELK lukilabs/* upleft-mermaid, upleft-elk diagram pixels identical done. ELK: elk 449/449 layouts identical (Mermaid-generated, elk-swift test, random and polyline graphs; where elk-swift itself varies between runs, Rust matches one of its outcomes), 6.6–14× faster than elk-swift. Mermaid, on 308 corpus diagrams of every type: mermaid-parse 308/308, mermaid-layout 1232/1232 and mermaid (bridge pixels) 1232/1232 across light, dark, Nord and High Contrast; mermaid-replay 211/211 (ELK inputs byte-identical to Swift's). The uncached bridge path is about 2.7× faster than Swift.
DownrightApp and the command-line tools Sources/DownrightApp, drdownright, down, DownrightSpotlightMetadata, DownrightSpotlightImporter, DownrightQL, DownrightThumb upleft-app, upleft (the app binary), upleft-cli, upleft-spotlight-metadata, upleft-spotlight-importer, upleft-quicklook, upleft-thumb, upleft-foundation app-layer suites identical; window captures identical in progress. Done: the non-UI layer, the command-line tools, the windows, menus and panels, the app binary and just upleft-app (Upleft.app with down, Sparkle 2.9.6, the Spotlight importer and both Quick Look extensions). html-export 14042/14042 (6 themes × light and dark, plus print), spotlight 928/928, down-cli 244/244, workspace 6/6, find 4/4, palette 23/23, formats 30/30, updater 10/10, local-ai 4/4; app-window 42/42 (document window, start, setup and Settings windows, captured off-screen by the window server) and app-menu 4/4; panel 228/228 and panel-model 39/39 (589 states: every Panels/ view built from a scenario off-screen, window-server pixels and view-tree dumps); quicklook-preview 16/16, quicklook-thumbnail 2745/2745. All just app-bench and just panel-bench stages (task panel on the 5,000-line document 0.76×, palette ranking 0.74–0.77× Swift) and just app-window-bench (document open, mode switch: 0.61–0.89× Swift) are as fast or faster. Unverified: the app launched on screen, a real Sparkle update, the system loading the importer and extensions. See crates/app/PORTING.md and crates/app/src/panels/PORTING.md.

Performance

just bench runs Downright's own drbench and Upleft's upleft-bench three times each, interleaved, and compares the median p50 of every stage. It fails if any stage is slower than Swift by more than 5% (a run-to-run noise allowance). Latest run on an Apple Silicon MacBook Pro, milliseconds:

Stage Swift p50 Upleft p50 Upleft / Swift
cmark alone (5k lines; Upleft: pulldown-cmark and the adapter) 10.6 0.89 0.08
MarkdownParser.parse, all passes 24.1 3.9 0.16
parse 100 KB (cold open) 19.8 3.1 0.16
incremental decorate, one dirty block 0.086 0.045 0.52
wholesale decorate (mode switch) 93.2 41.1 0.44
edit + paragraph map (typing response) 0.139 0.076 0.55
worker pipeline (end-to-end convergence) 24.1 3.9 0.16
Metrics.metrics 22.9 4.8 0.21
syntax highlight 10 KB 0.082 0.046 0.56

All 21 drbench stages are as fast or faster. The math, Mermaid and ELK layers have their own benchmarks; see each crate's PORTING.md and the status table above.

Layout

vendor/downright            the Swift original (git submodule, pinned)
vendor/swift-cmark          cmark-gfm at the revision Downright resolves (Upleft's test oracle)
vendor/pulldown-cmark       Upleft's fork of pulldown-cmark, the parser (see its UPLEFT.md)
vendor/swift-markdown       swift-markdown at the revision Downright resolves
vendor/beautiful-mermaid-swift, vendor/elk-swift   Mermaid dependencies
oracle/                     downright-oracle: the Swift reference for conformance
crates/                     the Rust port
crates/swift-text           Swift String/Character/CharacterSet/NSString semantics shared by
                            every crate, with Unicode tables generated from the Swift runtime
                            and checked for every scalar by the `unicode` suite
corpus/                     documents the conformance runner checks

Other applications can embed the renderer: docs/EMBEDDING.md covers hosted mode, where each MarkdownTextView is one message in the host's own scroll view (a chat transcript, for example).

Conformance

oracle/ is a small Swift package that links Downright's own MarkdownCore and MarkdownRender. It writes three outputs for a document:

  • parse dumps the parsed document tree: every block, inline span, range, hash, and derived structure.
  • decorate dumps every attribute run on the decorated NSTextStorage. Fonts, colors, and paragraph styles are compared bit for bit.
  • render captures a PNG of the real MarkdownContainerView, along with a dump of the layout fragments. It runs headless by default: the app is never activated, its window sits off-screen, and cacheDisplay records it. An on-screen ScreenCaptureKit capture (--capture screen) is opt-in, for a handful of spot checks.

upleft-oracle takes the same arguments and writes the same formats. The runner compares the two outputs structurally and decodes the PNGs to compare pixels exactly. A render request can also be sent to the full app. Downright's DOWNRIGHT_DEBUG_LAYOUT and DOWNRIGHT_DEBUG_CAPTURE hooks capture the whole window, and Upleft implements the same hooks.

git submodule update --init
just oracle          # build downright-oracle
just conform         # run the corpus through both and compare

Licensing

Upleft is MIT-licensed and keeps Downright's copyright notice. Ports of third-party code keep their upstream license:

  • upleft-markup (from swift-markdown) is Apache-2.0.
  • upleft-elk (from elk-swift) is EPL-2.0.
  • upleft-math (from SwiftMath) and upleft-mermaid (from beautiful-mermaid-swift) are MIT.

About

A Rust rewrite of Downright, the native macOS Markdown reader/editor — pixel-exact with the Swift original

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages