Trivia-preserving parsing and formatting for logos + lalrpop grammars.
marginalia plugs into the standard Rust parsing stack and keeps comments and blank lines around
the AST, so you can write a formatter for your language without losing the user's notes.
The crate has three layers, all in one place:
TriviaLexeradapts anyIterator<Item = Result<(usize, Tok, usize), E>>and records line, block, and blank-line trivia in aTriviaTablewhile the parser sees only semantic tokens.marginalia::attachplaces those trivia events on AST node spans as leading, trailing, or dangling comments.marginalia::prettyis a smallDocIR with explicit trivia slots that the renderer resolves against aCommentMap.
The shape of an integration:
use marginalia::TriviaLexer;
use marginalia::attach::{attach, AttachOptions};
use marginalia::pretty::{render, RenderOpts};
let raw = my_logos_lexer(source);
let mut lex = TriviaLexer::new(raw, source);
let program = MyParser::new().parse(&mut lex)?;
let table = lex.into_table();
let map = attach(source, &table, my_spans(&program), AttachOptions::default());
let doc = program.doc();
let formatted = render(&doc, &map, RenderOpts::default());The lexer must yield Result<(usize, Tok, usize), E> and Tok must implement
marginalia::Classify so marginalia knows which tokens are comments.
Classify, Trivia, TriviaTable, and CommentMap are all generic over a kind enum K (default
BuiltinKind = Line | Block). A language that needs richer categories supplies its own enum and an
impl Classify<MyKind> for MyTok. The renderer requires K: TriviaClass so it knows which trailing
comments terminate a line.
examples/calc is a 200-line calculator language with full lex + parse + format
roundtrip and comment preservation. Read it end-to-end as the canonical integration template:
cargo run -p marginalia-calc -- examples/calc/tests/input.calcThis project is licensed under the MIT License. See the LICENSE file for details.