Skip to content

v0.10.0 - 2026-08-10

Latest

Choose a tag to compare

@github-actions github-actions released this 10 Aug 19:21
v0.10.0
f2602b3

Release Notes

Features

  • (latex) [latex].verbatim_envs / structure_envs / verbatim_commands in .snapperrc.toml add names to the built-in lists (minted/lstlisting/verbatim, NON_PROSE_ENVS, \\verb/\\lstinline); missing keys keep today's defaults. Extra command names are tokenized like \\verb before split. No regex other: key
  • (reflow) --clause-breaks / clause_breaks with max_width = 0 inserts a newline after every independent-clause mark (,, ;, :, em dash, --) that is already followed by whitespace; a one-clause sentence stays one line; max_width > 0 keeps wrap-prefer-clause. Tokens such as 1,000, URLs, --flags, and unspaced dashes never split. --check uses the same mode
  • (cli / check) --check --output-format json|sarif emits 1-indexed fused / wrap / long diagnostics with excerpts; long is advisory unless --strict-long; stdin --check honors the same JSON/SARIF and exit codes; SARIF URIs are repo-relative (or file: plus invocations[0].workingDirectory)
  • (mcp) mcp is a default feature, so published snapper / snapper-fmt binaries include the stdio server; agents should call it (or the CLI) instead of applying sembr/skills by hand
  • (mcp) format_text accepts clause_breaks, range (start/end, 1-indexed inclusive), and max_width (default 0)
  • (mcp) check_formatting accepts clause_breaks (default false) and returns would_reformat identical to CLI --check, plus the same line diagnostics
  • (safety) native parsers record source byte ranges and splice reflowed prose into the original document; Structure/Code/Blank are input slices
  • (safety) format_text runs to a byte fixpoint (cap 4, including A/B cycles) or returns the original; a format-local oracle (region-kind + slice tree; Markdown HTML plus a code-byte check) mismatch also returns the original
  • (safety) format_bytes refuses invalid UTF-8 with InvalidUtf8Error; --use-pandoc refuses (PandocCannotSplice) because the AST has no source offsets
  • (safety) property tests and a cargo fuzz target (fixture corpus) drive format_text per format with the backstops off
  • (code-block) comment reflow copies non-comment lines as original slices; only comment spans are rewritten

Bug Fixes

  • (latex) render oracle and --check payloads honor [latex] extras, so a configured \\Verb!%! is not a comment and production backstops no longer revert the file
  • (latex) tokenize \\verb / \\lstinline (optional [...]) before split so inner % is not a comment and inner .!? do not split; unmatched \\verb runs to EOL; unlisted \\begin / \\end are region bounds (optional [...] stays on the begin token); mid-line \\begin{equation} leaves leading words as prose; nested same-name envs (including verbatim/lstlisting) close on matching depth, so \\end{python} inside lstlisting does not steal the closer; listing body scans raw \\begin/\\end (no % stop, no \\verb skip) so print(1) % \\end{lstlisting} and print(\"%\") still close
  • (check) fused and long run on the parser prose payload, so 1. Hello world. and See Fig. 1. % TODO cite are not false fused
  • (reflow) splice keeps the trailing space before a mid-line TeX % comment so that line stays one source line
  • (reflow) --max-width keeps links, images, inline code, autolinks, $math$, and Org [[...]] on one line (overlong token may sit alone; no hyphen splits); wrap-created lines that the format grammar would read as a new block (fences, thematic breaks, [ref]:, HTML, >foo, LaTeX %/\begin/\section, RST .., Org |, lists) are backslash-escaped in Markdown or skip-cut (loop) elsewhere; <https://...> / <user@host> are autolinks, not HTML openers, so wrap does not inject \\<; skip-cut wins when a \\ would corrupt an inline token; wrap measures hanging indent and tests interrupt after it; no NBSP; first line of a list item stays a list; second pass does not accumulate backslashes; the render oracle treats wrap-created Markdown escapes (\-, 1\\.) as the source words so splice does not return the original
  • (reflow) list continuation sentences hang at the marker width so Org rejoins the item on reparse; Markdown quotes repeat the > prefix (> One. / > Two., nested > >)
  • (latex) a trailing % eats the newline (TeX nospace join), so foo%\\nbar is no longer emitted as foo% bar (which comments out bar); mid-line % comments leave prose
  • (sentence) w.r.t. is a multi-word abbreviation; \\(...\\) and $$...$$ stay atomic like $...$
  • (cli) unknown source extensions (.rs, .py, no extension) are refused unless --format is explicit; .txt remains plaintext
  • (org / sentence) verbatim and inline-code spans pair to the first closer that satisfies Org's border and post rules, so an = or ~ inside the span no longer orphans the real closer onto the next line
  • (sentence) CommonMark */** and GFM ~~ pair by flanking rules, so **the end. Still bold** stays one sentence while **complex**. Equity may split after the closer
  • (markdown) > is kept on each reflowed content line (including max_width wraps; prefix counts toward width); two-space and backslash hard breaks are not joined with a space; a quote hard break does not leave a stray > on the next non-quote line; multiline <!-- ... --> is structure (<!-- snapper:off --> still works)

Documentation

  • README distinguishes snapper from admk/sembr and sembr/skills; crate keywords include sembr and markdown
  • (markdown / sentence) inline code delimited by two or more backticks can contain a shorter backtick run
  • (code-block) a block-comment closer inside a string no longer ends the comment; quote-sequence closers (""") still match naively so Python docstrings reflow
  • (sentence) abbreviation merge does not invent a space before LaTeX ~ (so Eq.~\ref{} stays attached once org ~code~ pairing no longer swallows that tilde)
  • (sentence) restore wrapped placeholders from the outside in, so a markdown-link regex match that contains a paired-span token cannot leak \x00PHn\x00 into output

Notes

  • Native format_text refuses --use-pandoc (PandocCannotSplice); splice needs source offsets the AST does not carry
  • --clause-breaks with the default max_width = 0 is SemBr rule 5 (break at existing whitespace after clause punct); max_width > 0 still wrap-prefers
  • Published binaries include the MCP server (mcp is a default feature)

Install snapper-fmt 0.10.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/TurtleTech-ehf/snapper/releases/download/v0.10.0/snapper-fmt-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://github.com/TurtleTech-ehf/snapper/releases/download/v0.10.0/snapper-fmt-installer.ps1 | iex"

Install prebuilt binaries via Homebrew

brew install TurtleTech-ehf/tap/snapper-fmt

Download snapper-fmt 0.10.0

File Platform Checksum
snapper-fmt-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
snapper-fmt-x86_64-apple-darwin.tar.xz Intel macOS checksum
snapper-fmt-x86_64-pc-windows-msvc.zip x64 Windows checksum
snapper-fmt-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
snapper-fmt-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum