Badness
Badness is a formatter, linter, and language server for LaTeX, built on a lossless concrete syntax tree.
It parses LaTeX once and serves three tools from that tree:
- Formatter (
badness format): deterministic, rule-based layout. - Linter (
badness lint): diagnostics with source snippets. - Language server (
badness lsp): both, live in your editor.
The architecture follows rust-analyzer: a generic, error-tolerant, hand-written parser produces a lossless tree, semantics are layered on top as a separate concern, and recomputation is incremental. badness never requires resolving macros or catcodes to succeed—anything it cannot statically recognize degrades to generic nodes rather than a crash. Two properties hold by construction and are enforced as tests: losslessness (the tree reconstructs the input byte-for-byte) and idempotence (formatting an already formatted file changes nothing).
Installation
Badness is written in Rust. Build from a checkout:
Usage
# Format a file in place (or stdin → stdout with no path)
# Verify formatting without writing — exits non-zero if anything would change
# Lint, reporting parse diagnostics
# Run the language server over stdio
Formatter style is set through flags: --line-width (default 80),
--indent-width (default 2), and --wrap (reflow by default; also
preserve, with sentence/semantic planned). See the documentation for the
full reference.
Documentation
Full documentation lives at https://jolars.github.io/badness/ (built with
mdBook from docs/).
Contributing
Architecture, tenets, and conventions are documented in
AGENTS.md, written for both human and AI contributors. In short:
keep the syntactic layer free of semantic knowledge, every parser feature needs
corpus and snapshot tests plus a losslessness assertion, and code stays
rustfmt-clean with clippy warnings treated as errors.