Skip to content

Type to search extensions

croma ABC

v1.0.0

ABC music notation support for Zed: tree-sitter highlighting, folds, and the croma-lsp language server.

Updated 1 month ago
17 downloads 3 stars Repository ↗

croma

CI gates audit crates.io docs.rs License: Apache-2.0 corpus: 10k proven

A Rust-first toolkit for ABC music notation — convert ABC to MusicXML and back, format it, and edit it with real language-server support. Everything is built on one library (croma-core); the CLI, formatter, reader, and language server are thin layers over the same parse/model pipeline, not separate parsers.

croma targets ABC 2.1 as the stable spec (standard), with ABC 2.2 treated as a draft compatibility mode. It is parser-strict: malformed input is rejected with a diagnostic rather than silently guessed at; loose source is repaired explicitly by croma fmt --auto-fix.

What it does

  • ABC → MusicXML (croma xml, croma-core): a library-first ABC 2.1 parser and MusicXML 4.0 writer. This is the foundation everything else builds on.
  • Formatter (croma fmt, croma fmt --auto-fix, croma-fmt): a canonical ABC pretty-printer. Formatting is idempotent and lossless; --auto-fix additionally sanitizes loose source (redundant/malformed barlines, whitespace) into spelling the strict parser reads cleanly.
  • MusicXML → ABC (croma read, croma musicxml2abc): the reverse reader — inverts croma's own writer and reads foreign MusicXML dialects (abc2xml, MuseScore, Finale, Sibelius).
  • Lossless round-trip + agent help (croma agent): the MusicXML → ABC → MusicXML round-trip preserves facts ABC 2.1 cannot natively express through private [I:cr <code> …] / %%croma-* carriers (ignorable by other ABC tools; the older [I:croma-<name> …] spelling is still read until 2.0); croma agent explains those notations to an AI agent / LLM, with the same topics available as a croma-core API. See docs/carriers.md and docs/agent.md.
  • Language server (croma-lsp): a stdio LSP — diagnostics, formatting, semantic tokens, document symbols, folding, hover, completion, and code actions — that is a thin adapter over the core (its output is byte-identical to the CLI's, by construction).
  • Editor support: a reusable tree-sitter-abc grammar (Zed, web/WASM, Markdown ```abc injection, Neovim/Helix) and a Zed extension wiring the grammar + croma-lsp together.

Out of scope: PDF rendering and engraving layout.

Install

From crates.io:

cargo install croma-cli      # the `croma` CLI
cargo install croma-lsp      # the language server
cargo add croma-core         # the library (ABC <-> MusicXML, zero-dependency)

Prebuilt binaries for macOS / Linux / Windows are attached to each GitHub Release — croma-* (CLI) and croma-lsp-* (server), one per platform.

From source (pinned Rust 1.96.0, via rust-toolchain.toml):

git clone https://github.com/ro-ag/croma
cd croma
cargo build --release        # target/release/croma (CLI) + croma-lsp

A reader-less, zero-dependency CLI is available with cargo build -p croma-cli --no-default-features.

Usage

# ABC -> MusicXML
croma xml tune.abc > tune.musicxml

# Lint / diagnose (text or JSON)
croma check tune.abc
croma check --diagnostics=json tune.abc

# Format (stdout), or rewrite in place, or repair loose source
croma fmt tune.abc
croma fmt --write tune.abc
croma fmt --auto-fix tune.abc

# MusicXML -> ABC
croma read score.musicxml --format abc
croma musicxml2abc score.musicxml

# Explain croma's non-standard carrier notations to an AI agent / LLM
croma agent
croma agent xvoice-slur

As a library:

use croma_core::abc_to_musicxml;

let xml = abc_to_musicxml("X:1\nT:Scale\nM:4/4\nL:1/8\nK:C\nC D E F G A B c|\n")?;

croma-core's default build is zero-dependency and crates.io-publishable. The MusicXML reader's only dependency (roxmltree) is opt-in via the musicxml-reader feature and ships on the CLI binary, never the library default.

API stability (1.x)

Within 1.x, the conversion entry points (abc_to_musicxml, export_musicxml, export_musicxml_with_options, parse_document, lower_score, write_musicxml, write_abc, read_musicxml) and the builder methods on the option types keep compiling. The public data types — the score model, syntax and parse trees, diagnostics, and the option structs' fields — are not covered: a minor release may add struct fields and enum variants, as 1.1–1.3 did. Build options from Default plus the builder methods rather than struct literals, and give matches on croma enums a wildcard arm. 2.0 will mark these types #[non_exhaustive] so the compiler enforces it.

Workspace

crates/croma-core   ABC 2.1 parser, model, and ABC<->MusicXML conversion (the library)
crates/croma-cli    the `croma` command-line tool (thin wrapper)
crates/croma-fmt    the formatter / auto-fixer, on the core model
crates/croma-lsp    the stdio language server, a thin adapter over core + fmt
tree-sitter-abc/    reusable ABC grammar (outside the cargo workspace)
editors/zed/        Zed extension (outside the cargo workspace)

Per-capability docs live in docs/: formatter, MusicXML reader, LSP, editors, and the benchmark baseline.

How it's proven

croma's correctness is validated against a real-world corpus of 10,000 ABC files (the Zenodo ABC dataset), not just hand-written unit tests (though there are about 1,100 of those too). Every shipped capability has a corpus-scale gate that must stay green:

Capability Gate Result (10k corpus)
ABC → MusicXML writer structural parity vs abc2xml (raw comparator) 9,228 / 10,000 match; the other 772 are 707 adjudicated divergences and 65 files with no music
Formatter idempotent and lossless re-formatting 10,000 / 10,000
MusicXML → ABC reader self-loop XML re-emission 9,933 / 9,935
MusicXML → ABC reader foreign-dialect parity vs music21 98.50%
croma-lsp diagnostics / formatting / token fidelity vs core 10,000 / 0 mismatches
croma-lsp totality (no panics, no hangs on malformed input) 0 panics / 10,000
tree-sitter-abc clean parse (no ERROR nodes) 99.46% (9,946 / 10,000)

Plus cargo test --workspace, cargo clippy --all-targets -- -D warnings, and cargo fmt --check on every change. Each gate's residual is adjudicated — a documented, spec-justified non-bug — never an unexplained failure. The figures were last measured on 2026-10-07; the corpus, the adjudications and the foreign-dialect (music21) measurement live in the private croma-test repository, so they cannot yet be reproduced from this repository alone.

The corpus, the abc2xml comparator, the spec knowledge base, and the Python provers live in a companion repository, croma-test, so a developer working on croma never has to download the heavy proving apparatus. croma builds and tests standalone (the corpus-scale gates are environment-gated and skip cleanly when the corpus is absent); the full matrix runs from croma-test.

How croma compares to abc2xml

abc2xml (by Willem Vree) is the long-standing reference ABC→MusicXML converter, and it is croma's correctness baseline: croma is validated against it over the full 10k corpus. Where abc2xml is spec-correct, croma matches it (9,228 of the 10,000 files match structurally); croma diverges only where abc2xml departs from the ABC 2.1 spec, and every such case (707 files) is adjudicated and documented. croma is an independent, library-first implementation written from the ABC 2.1 spec (it follows abc2xml's conventions in a few small tables, credited in THIRD_PARTY_NOTICES.md), and it improves on the reference in several ways:

abc2xml croma
Direction ABC → MusicXML (the reverse is a separate script, xml2abc) ABC ↔ MusicXML in one library
Form a Python script (needs a Python runtime) a Rust library + native binaries — zero-dependency, embeddable, crates.io-publishable, callable from any language
Speed interpreted Python compiled Rust — 7,007 ABC→MusicXML files/s and 50,391 parse files/s over the 10k corpus
Malformed input permissive — silent best-effort heuristics strict ABC 2.1 — structured diagnostics (codes + spans); recovers only when the intent is unambiguous, and always warns
Output artifacts inserts spurious elements as heuristic side effects — e.g. empty leading/section measures, phantom measures spec-faithful, minimal MusicXML — declines those artifacts; most croma↔abc2xml divergences are exactly such an artifact croma omits
Beyond conversion converter only also a formatter (idempotent + lossless), a language server (live editor diagnostics / formatting / completion), a reusable tree-sitter grammar, and a Zed extension
Validation — corpus-proven over 10k with a documented gate matrix + a reproducible benchmark baseline
Safety — memory-safe Rust (unsafe forbidden), pinned toolchain, clippy + fmt gates

In short: abc2xml is a mature one-way converter script; croma is a fast, embeddable, bidirectional ABC toolkit that holds stricter to the spec and emits cleaner, lower-artifact MusicXML, with editor-grade diagnostics — designed to be linked into applications and proven at scale. (It also stands on abc2xml's shoulders: using it as the parity baseline is how croma earns its correctness claims.)

Benchmarks

Headline throughput/latency, measured 2026-10-07 at commit 98ed4bd (Apple M4 Max, Rust 1.96.0, --release). Full methodology, per-call micro-benchmarks, and reproduction steps: docs/benchmarks.md.

Layer Headline
Parser (corpus, in-process) 50,391 files/s · 27.9 MB/s
Formatter (corpus, in-process) 32,139 files/s · 17.8 MB/s
ABC → MusicXML writer (corpus) 7,007 files/s · 3.9 MB/s
LSP diagnostics, real-size p99 ≤ 5.7 ms (release ceiling 50 ms)
LSP semantic tokens, real-size p99 ≤ 0.49 ms
tree-sitter-abc (steady state) ~9.0–9.2 MB/s

The corpus median file is 14 lines (max 244); at those sizes every operation is in the low-millisecond range. The ABC→MusicXML export path is super-linear on synthetic 1000-line inputs (recorded, no real-use impact — see benchmarks §6).

Contributing

Contributions are welcome — see CONTRIBUTING.md for setup, how to run the gates, and the conventions (strict-spec parser policy, branch and commit rules, and the "don't regress a proven gate" discipline).

License

Licensed under the Apache License, Version 2.0 — see LICENSE and NOTICE. Third-party files that ship in the repository (tree-sitter's MIT-licensed runtime files) and credits are listed in THIRD_PARTY_NOTICES.md.

You may use croma freely, including in commercial products. In return, any redistribution or derivative work must retain the attribution in NOTICE (per section 4 of the license) — i.e. commercial users must credit croma. The software is provided as-is, without warranty, and the author carries no liability for its use (sections 7–8).

ZedExt An unofficial community project. Not affiliated with Zed Industries.

Data from Zed’s extension registry and GitHub · updated