name: debug-formatter description: Debug a SQL formatter bug. Use when the user reports incorrect formatting output — wrong whitespace, misplaced comments, blank lines, etc. user_invocable: true
debug-formatter
Debug a SQL formatter bug using red-green testing and pipeline tracing.
Instructions
1. Reproduce (RED)
Add a failing test to syntaqlite/tests/fmt_comments.rs (comment bugs) or the
relevant tests/fmt_diff_tests/*.py file. Always eprintln! the actual output:
#[test]
fn descriptive_name() {
let input = "...";
let out = fmt(input);
eprintln!("=== actual ===\n{out}=== end ===");
assert_eq!(out, "...\n");
}
Run it and confirm RED:
cargo test -p syntaqlite --features fmt,sqlite --test fmt_comments TEST_NAME -- --nocapture
2. Trace the pipeline
Work through these layers to find the root cause:
| Layer | File | What to check |
|---|---|---|
| Grammar | syntaqlite-buildtools/parser-nodes/*.synq |
fmt { } block; clause("KW", field) expands to IF_SET → LINE → KEYWORD → NEST(LINE, CHILD) |
| Bytecode compiler | syntaqlite-buildtools/src/dialect_codegen/fmt_compiler.rs |
How Fmt::Clause, Fmt::Keyword compile to opcodes |
| Interpreter | syntaqlite/src/fmt/interpret.rs |
Main loop in interpret_node; state = running (committed doc) + pending (deferred line breaks) |
| Comment drain | syntaqlite/src/fmt/comment.rs |
drain_before(offset) respects has_non_comment_text guard; drain_keyword_interior() skips it for multi-word keywords |
| flush_drain | interpret.rs |
Integrates DrainResult { trailing, leading } into running/pending; when leading != NIL, pending is dropped |
| Doc renderer | syntaqlite/src/fmt/doc.rs |
Wadler-style; hardline always breaks, line breaks if group broken |
3. Common bug patterns
- Blank line between comments: Two consecutive drains both produce
hardline + comment + hardline; back-to-back hardlines = blank line. Fix viacontinuationflag ondrain_keyword_interior. - Comment on wrong side of keyword:
flush_drainfor interior comments runs before keyword text is emitted. Check flush vs keyword append ordering in theKeywordmatch arm. - Comment lost:
has_non_comment_textguard stopsdrain_beforewhen a keyword sits between the comment and target offset. Multi-word interiors needskip_text_check: true. - Pending dropped:
flush_drainwithleading != NILzeroes pending. If pending held a LINE from the clause template, that break is lost.
4. Fix and verify (GREEN)
cargo test -p syntaqlite --features fmt,sqlite --test fmt_comments TEST_NAME -- --nocapture
cargo test -p syntaqlite --features fmt,sqlite
cargo build -p syntaqlite-cli && tools/run-integration-tests
All 7 integration suites must pass (including ~650 idempotency checks).