badness 0.3.0

An LSP, formatter, and linter for LaTeX
Documentation
# Parse concordance vs texlab (soft signal)

_Generated by `task parse-compat` (`tests/parse_compat.rs`). Do not edit by hand._

This is a **soft gauge, not a quality gate.** It projects badness's generic CST and texlab's semantic CST onto one coarse skeleton (commands, environments, groups, math, verbatim — names but not roles) and measures how structurally close they are. A divergence is never a bug by itself: it is either a deliberate, recorded deviation or an open question (badness models TeX surface syntax; texlab resolves semantics). AGENTS.md: measure against, never match.

- **Corpus:** corpus (`tests/corpus/*.tex`)
- **Skeleton similarity:** 79.5%  _(Dice coefficient over skeleton lines)_
- **File concordance:** 27.3%  (6/22 files identical after projection)
- **Intentional deviations:** 16  ·  **Unexplained divergences:** 0
- **Skipped:** 1 (badness could not parse cleanly)

## Recorded intentional deviations

Listed in `tests/parse_compat_allowlist.toml`. These diverge from texlab on purpose.

| File | Skeleton similarity | Reason |
|---|---|---|
| `accents.tex` | 70.0% | Control-symbol accents (`\\\"{o}`, `\\'{e}`) keep their `{…}` group a sibling because greedy attachment fires on control *words*, not control symbols; texlab nests the group as the accent's argument. Same deviation as edge.tex. |
| `citations.tex` | 77.8% | Citation/ref commands and their opt+required args are concordant; the only divergence is texlab nesting the trailing `\\label`/`\\ref` inside the preceding `\\section` scope, which badness keeps flat (section scoping, same as sectioning.tex). |
| `comments_trivia.tex` | 33.3% | Comment/trivia attachment produces no skeleton divergence (trivia is projected away); the only divergence is texlab nesting post-`\\section` content (itemize, math) inside the section scope, which badness keeps flat (section scoping). |
| `conditionals.tex` | 94.1% | badness does not evaluate `\\iffalse…\\fi` (no TeX execution, a non-goal), so it reads the body as generic commands and greedily attaches `{Hidden}` to `\\section`; texlab's conditional handling drops that group. badness is the faithful surface reading. |
| `display_math.tex` | 76.5% | Bundles three recorded deviations: subscript gluing (`\\int_0`), `\\`-line-break optional-arg artifacts (texlab manufactures empty `(opt)` nodes badness does not), and `\\(…\\)` which badness models as INLINE_MATH while texlab leaves the delimiters as bare commands (badness is the more faithful surface reading). |
| `edge.tex` | 81.8% | Greedy `{…}` args attach to control *words*, not bare control-symbol tokens (`\\^{}`, `\\~{}`), so accent groups stay siblings; `\\def`'s signature is not special-cased, so its argument attachment differs from texlab. |
| `environments.tex` | 74.1% | texlab opens a semantic scope for `\\item` (nesting following content) and glues subscripts into the command name (`\\varepsilon_0`); badness keeps `\\item` a generic command and `_0` a separate SUBSCRIPT. `\\` is a LINE_BREAK node here vs texlab's generic command + greedy optional arg. |
| `math.tex` | 84.6% | texlab glues the subscript into the command name (`\\int_0`); badness keeps `\\int` a command and `_0` a separate SUBSCRIPT (badness is the more faithful surface reading). `\\` is a LINE_BREAK node vs texlab's generic command + optional arg. |
| `math_operators.tex` | 78.6% | `\\left…\\right` is a `LEFT_RIGHT` node isolating the delimiter token (`\\left[` lexes `[` as the delimiter, not an optional-arg open); texlab instead parses delimiter content into a `(opt)`/bracket group. badness is the more faithful surface reading (AGENTS.md decision #1). |
| `nested_envs.tex` | 60.0% | texlab nests the inner `itemize` and its math inside the enclosing `\\item` scope; badness keeps the nested environment a flat sibling after `\\item` (item scoping, same as environments.tex). |
| `newcommand.tex` | 94.9% | `\\newcommand`/`\\renewcommand`/`\\DeclareMathOperator`/`\\newenvironment` are concordant; the only divergence is `\\def\\foo{bar}`, whose signature badness does not special-case, so `\\foo` and `{bar}` stay flat siblings vs texlab's signature-aware nesting. Same as edge.tex. |
| `optional_args.tex` | 66.7% | Nested optional args, key-value options, and bracketed optionals (`\\sqrt[\\frac12]{x}`, `\\item[{[b] label}]`) are concordant; the only divergence is texlab nesting a trailing `\\newcommand` inside the preceding `\\item` scope (item scoping). |
| `sectioning.tex` | 30.0% | texlab opens a semantic scope for sectioning commands (`\\section`, `\\subsection`, `\\paragraph`), nesting following content inside; badness keeps them flat generic COMMANDs (meaning never leaks into the parser, AGENTS.md tenet 2). Same scoping family as `\\item`. |
| `tables.tex` | 92.3% | Not a parser divergence: badness's CST keeps the `\\begin{tabular}{lcr}` column-spec as a GROUP child of BEGIN, but the skeleton projector drops BEGIN wholesale, so the group is hidden while texlab surfaces it. A gauge-projection artifact — see docs/parse-compat-triage.md. |
| `verbatim_cmd.tex` | 42.1% | The verbatim argument now attaches as a *child* of the command (matching texlab's structure); the residual divergence is deliberate: badness keeps `\\url`/`\\lstinline` bodies opaque (VERB→`(verbatim)`) where texlab parses them as a `(group)`, and `\\verb`/`\\verb*` is one opaque VERB token vs texlab's command + verbatim split. |
| `verbatim_env.tex` | 40.0% | badness protects `verbatim`/`lstlisting` bodies as a single opaque VERBATIM_BODY (built-in signature DB); texlab under its default config parses *into* the body (`{braces}`→group, `\\commands`→command). badness is the more faithful reading; texlab is the one diverging. |