A beautiful diagnostics renderer for compiler errors and warnings
Key Features ⢠Installation ⢠Quick Start ⢠API Reference ⢠Testing
š¦ For Rust Users: This README covers the complete Musubi project (Lua/C/Rust implementations).
Looking for Rust API documentation? ā See the comprehensive Rust API docs with examples and usage guides.
Rust crate:musubi-rs
Overview
Musubi (ēµć³, "connection" in Japanese) is a high-performance diagnostics renderer inspired by Rust's Ariadne library. It produces beautiful, color-coded diagnostic messages with precise source location highlighting, multi-line spans, and intelligent label clustering.
Originally ported from Rust's Ariadne library, Musubi has evolved into a production-ready multi-language implementation:
- C Library: High-performance core with Lua bindings (
musubi.h,musubi.c) - Rust Crate: Safe FFI wrapper with ergonomic builder API (
musubi-rs)
Both implementations produce identical output and are thoroughly tested (26 Rust unit tests + 30 doc tests, 100 Lua tests).
Key Features
⨠Beautiful Output
- Multi-line diagnostics with color-coded labels
- Intelligent label clustering and virtual row rendering
- Unicode and CJK character support
- ASCII/Unicode glyph sets for terminal compatibility
š Performance Optimized
- O(n) rendering complexity (vs original O(n²))
- Pre-computed width caching for UTF-8 strings
- Binary search for line windowing calculations
- Zero-copy source file handling with streaming support
šÆ Improved Implementation
- Cleaner Implement with seprated small functions
- Bugfixes towards original Ariadne implement
- New feature: Line limited support
- New feature: No message label rendered
š”ļø Production Ready
- 100% test coverage (all reachable code covered)
- Memory-safe C implementation
- Comprehensive error handling
- Tested on Lua 5.1, 5.4, and LuaJIT
Example
local mu = require
local cg = mu.
print
Output:
Installation
Requirements
Rust Crate:
- Rust 1.56+ (edition 2024)
- No external dependencies (self-contained C implementation)
C Library with Lua Bindings:
- C89-compatible compiler (GCC, Clang, MSVC)
- Lua 5.1+ headers for Lua bindings
- Optional:
lcovfor coverage reports
Building
Rust:
C Library with Lua Bindings:
# Compile shared library
# Or with coverage instrumentation
macOS:
Quick Start
Basic Usage (C Bindings)
local mu = require
-- Create a color generator for automatic color cycling
local cg = mu.
-- Build a report
local report = mu. -- Primary error position
:
:
:::
:
:
:
print
Configuration
local mu = require
local cfg = mu.
: -- Enable compact mode
: -- Draw arrows across line gaps
: -- Tab expansion width
: -- Truncate long lines to 80 columns
: -- Use Unicode box-drawing characters
: -- Use character offsets (vs "byte")
: -- Ambiguous character width (1 or 2)
: -- Use natural label ordering (default)
: -- Align label messages (default)
mu.
:
-- ... rest of report
Multi-Source Files
local mu = require
mu.
:: -- src_id=1, first source
:: -- src_id=2, second source
:
:
:
File Sources (C Bindings Only)
local mu = require
local io = require
local fp = io.
mu.
: -- Streams file on-demand
::
:
Notice that if you use file handle on Windows, the musubi.so must not be built as static linking (/MT).
API Reference
Report Builder
| Method | Description |
|---|---|
mu.report(pos, src_id?) |
Create a new report at position pos |
:title(level, message) |
Set report level ("Error", "Warning") and title |
:code(code) |
Set optional error code (e.g., "E0308") |
:label(start, end?, src_id?) |
Add a label span (half-open interval [start, end)) |
:message(text, width?) |
Attach message to the last added label |
:color(color) |
Set color for the last added label |
:order(n) |
Set display order for the last label |
:priority(n) |
Set priority for clustering |
:note(text) |
Add a note to the footer |
:help(text) |
Add a help message to the footer |
:source(content, name?, offset?) |
Register a source (string or FILE*) with line offset (0 default) |
:render(writer?) |
Render the report (returns string or calls writer function) |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
compact |
boolean | false |
Compact mode (works with underlines) |
cross_gap |
boolean | true |
Draw arrows across skipped lines |
underlines |
boolean | true |
Draw underlines for single-line labels |
column_order |
boolean | false |
Simple column order (true) vs natural ordering (false) |
align_messages |
boolean | true |
Align label messages to same column |
multiline_arrows |
boolean | true |
Use arrows for multi-line spans |
tab_width |
integer | 4 |
Number of spaces per tab |
limit_width |
integer | 0 |
Max line width (0 = unlimited) |
ambi_width |
integer | 1 |
Width of ambiguous Unicode characters |
label_attach |
string | "middle" |
Label attachment point ("start", "middle", "end") |
index_type |
string | "char" |
Position indexing ("char" or "byte") |
char_set |
string | "unicode" |
Glyph set ("unicode" or "ascii") |
color |
boolean | true |
Enable ANSI color codes |
Color Generator
local cg = mu. -- min_brightness ā [0, 1], default 0.5
local color_func = cg: -- Get next color in cycle
Architecture
Rendering Pipeline
Report:render()
āā Context Creation (group labels by source, calculate widths)
āā Header Rendering (error level, code, message)
āā For each source group:
ā āā Reference Header (file:line:col)
ā āā Line Rendering:
ā ā āā Label Clustering (group overlapping labels)
ā ā āā Window Calculation (when limit_width > 0)
ā ā āā Virtual Row Splitting (multi-line labels)
ā ā āā For each cluster:
ā ā āā Line Content (with label highlighting)
ā ā āā Arrow Drawing (underlines, connectors, messages)
ā āā Empty Line
āā Footer Rendering (notes, help messages)
Key Design Decisions
Intervals:
- All position named
start/enduse half-open intervals[start, end) - All position named
first/lastuse close intervals[fist, last]
Width Caching:
- Pre-compute cumulative display widths for each line
- Binary search (
muC_widthindex) for O(log n) position lookups - Handles UTF-8 multi-byte characters, Emoji, RI, CJK double-width, tabs
Label Clustering:
- Group overlapping/nearby labels into virtual rows
- Separate inline labels (single line) from multiline labels
- Dynamic column range calculation for windowing
Memory Management (C):
- Caller provides allocator function (defaults to
malloc/free) - Dynamic arrays with geometric growth (muA_* macros)
- External pointers (messages, source names) must outlive render call
Testing
Running Tests
# Compile with coverage
# Run tests (uses C bindings by default)
# Generate coverage report
Test Coverage
Both implementations maintain 100% test coverage:
- 100 test cases covering all rendering paths
- Edge cases: zero-width spans, CJK characters, tab expansion, window truncation
- Regression tests for all fixed bugs
- Pixel-perfect output verification (2400+ lines of expected output)
Test Categories:
- Basic rendering (labels, messages, colors)
- Multi-line spans and clustering
- Line width limiting and windowing
- Unicode and CJK character handling
- Configuration options (compact, cross_gap, etc.)
- Multi-source file support
- File streaming (C only)
Implementation Notes
Differences from Rust Ariadne
Improvements:
- Cleaner margin render handling
- Explicit virtual row rendering for multi-line labels
- Width-based windowing with binary search optimization
- Label without message supports
Limitations:
- Only supports
\nnewlines (not Unicode line separators) - Not full UAX#29 grapheme cluster breaking (only support ZWJ & RI now)
C Implementation Details
See .github/c_port.md for detailed implementation notes:
- API constraints and call ordering requirements
- Memory management and lifetime rules
- UTF-8 handling and Unicode width calculations
- Source lifecycle and file streaming
- Known limitations and edge cases
Project Structure
See .github/project-structure.md for:
- Detailed architecture documentation
- Data structure definitions
- Rendering algorithm explanations
- Bug fix history and rationale
Contributing
Contributions are welcome! Please:
- Run tests before submitting:
lua test.lua - Maintain 100% coverage: Add tests for new features
- Follow existing style: Lua uses tabs, C uses 4 spaces
- Update documentation: Keep README and .github/*.md in sync
Development Workflow
# Run tests with coverage
# Find uncovered lines
# Run specific test
License
MIT License - See LICENSE for details.
Credits
- Original Ariadne library: zesterer/ariadne
- UTF-8 support: starwing/luautf8
- Test framework: LuaUnit
Related Projects
- Ariadne (Rust) - Original implementation
- Annotate Snippets (Rust) - Similar project
- Miette (Rust) - Fancy diagnostics library
- Codespan (Rust) - Alternative approach
Made with ā¤ļø for better compiler diagnostics