Table of Contents
About
A fast, format-aware semantic line break formatter. Reformats prose so each sentence occupies its own line, producing minimal and meaningful git diffs when collaborating on documents.
Why?
When multiple authors collaborate on a paper using Git, traditional line wrapping at a fixed column width causes problems. A single word change can trigger a diff that spans an entire paragraph. By breaking at sentence boundaries instead, each edit affects only the sentence that changed.
This convention, often called semantic line breaks, enjoys longstanding support from technical writers.
Existing tools fall short: latexindent.pl only handles LaTeX, SemBr requires Python and neural networks, and most lack multi-format awareness.
snapper solves this as a standalone Rust binary with no runtime dependencies, handling Org-mode, LaTeX, Markdown, and plaintext.
Design
snapper runs a three-stage pipeline:
- Parse: Classify input into prose regions and structure regions
- Split: Detect sentence boundaries in prose regions
- Emit: Output each sentence on its own line
Math environments, tables, front matter, drawers, and non-source comments pass through unchanged.
Fenced and delimited source blocks are Region::Code: fence/open/close lines stay structure, non-comment code stays verbatim, and comment lines reflow at sentence boundaries when the language has a [code.<lang>] entry in .snapperrc.toml (snapper init seeds common languages).
Pass --format-code to also pipe each block body through an optional per-language formatter argv (missing binary, non-zero exit, and timeouts degrade to the reflowed body).
Sentence detection relies on Unicode UAX #29 segmentation with abbreviation-aware post-processing that avoids false breaks at titles (Dr., Prof.), references (Fig., Eq.), and Latin terms (e.g., i.e., et al.).
Org emphasis (*bold*, /italic/, _underline_, +strike+) is kept atomic so splits cannot open a pseudo-headline mid-span.
Installation
Pre-built binary (fastest):
cargo binstall snapper-fmt
Shell one-liner (Linux/macOS):
curl -LsSf https://github.com/TurtleTech-ehf/snapper/releases/latest/download/snapper-fmt-installer.sh | sh
Homebrew:
brew install TurtleTech-ehf/tap/snapper-fmt
pip:
pip install snapper-fmt
Compile from source:
cargo install snapper-fmt
Nix:
nix build github:TurtleTech-ehf/snapper
The crate is snapper-fmt on all registries; the binary it installs is snapper.
Pre-built installers (cargo-dist) ship the plain binary: native Org/LaTeX/Markdown/RST parsers, no GHC, no embedded pandoc. Multi-format pandoc is opt-in at runtime or via an optional build feature (below).
Usage
Format a file (output to stdout):
snapper paper.org
Format in place:
snapper --in-place paper.org
Pipe through stdin (for editor integration):
cat draft.org | snapper --format org
Check formatting without modifying (for CI):
snapper --check paper.org paper.tex notes.md
Limit line width (wrap long sentences at word boundaries):
snapper --max-width 80 paper.org
Preview changes as a unified diff before committing:
snapper --diff paper.org
Compare two versions at the sentence level (whitespace reflow produces zero diff):
snapper sdiff paper_v1.org paper_v2.org
Watch files and auto-reformat on save:
snapper watch '*.org' 'sections/*.tex'
Initialize a project (generates config, pre-commit, gitattributes):
snapper init
Pandoc backend (optional multi-format)
Parse with pandoc, then reflow only prose from the AST (headers, code, tables, math stay structure):
# Uses system `pandoc` on PATH (JSON CLI), or in-process FFI if available
snapper --use-pandoc paper.typ
snapper --use-pandoc --pandoc-backend cli guide.adoc
snapper --use-pandoc --pandoc-backend ffi paper.md # needs libsnapper_pandoc or colink build
- CLI backend (default when FFI is missing): install the normal
pandocbinary. - FFI backend: build
native/snapper-pandocand setSNAPPER_PANDOC_LIB, or build with featurepandoc-colinkafter./native/snapper-pandoc/build-static.shso one binary absorbs the static archive (no env discovery; larger binary; not the default release artifact). - UPX (optional third-party packer) can shrink on-disk size of a finished
static-colink binary via
./native/snapper-pandoc/pack-upx.sh; it is not a tree-shaker and is not part of default CI or cargo-dist. Seenative/snapper-pandoc/README.md.
MCP server (AI assistants)
Expose snapper's tools to Claude Desktop, Claude Code, and other MCP clients:
npx @turtletech/snapper-mcp
Or directly:
snapper mcp
Tools: format_text, detect_format, check_formatting, split_sentences.
Configuration guide (org source in-tree): docs/orgmode/howto/mcp-integration.org; HTML docs: https://snapper.turtletech.us/docs/howto/mcp-integration/ .
Supported formats
| Format | Extensions | Structure / code handling |
|---|---|---|
| Org-mode | .org |
Drawers, tables, keywords; #+BEGIN_SRC comment reflow |
| LaTeX | .tex, .latex |
Preamble, math; minted and lstlisting comment reflow |
| Markdown | .md, .markdown |
Front matter, HTML; fenced blocks comment reflow when configured |
| RST | .rst |
Directives, literals; .. code-block:: comment reflow |
| Plaintext | everything else | (none; all text treated as prose) |
Pre-commit hook
- repo: https://github.com/TurtleTech-ehf/snapper
rev: v0.8.0
hooks:
- id: snapper
Emacs (Apheleia)
(with-eval-after-load 'apheleia
(push '(snapper . ("snapper" "--format" "org")) apheleia-formatters)
(push '(org-mode . snapper) apheleia-mode-alist))
VS Code
Install TurtleTech.snapper from the VS Code Marketplace. The extension uses the built-in LSP server for format-on-save, range formatting, diagnostics, and code actions.
Neovim
With lazy.nvim (rocks support):
{
"TurtleTech-ehf/snapper",
ft = { "org", "tex", "markdown", "rst" },
config = function()
vim.opt.runtimepath:append(
vim.fn.stdpath("data") .. "/lazy/snapper/editors/nvim"
)
require("snapper").setup()
end,
}
Or with rocks.nvim:
:Rocks install snapper.nvim
Vim
Plug 'TurtleTech-ehf/snapper', { 'rtp': 'editors/vim' }
This provides formatprg support for automatic formatting with the gq operator.
Obsidian
Install the snapper plugin from Community Plugins (search "Snapper"). Uses WebAssembly -- no binary installation required.
Microsoft Word
Install the snapper add-in from AppSource (Insert > Get Add-ins > search "Snapper"). Uses WebAssembly -- no binary installation required.
Git smudge/clean filter
Auto-format on commit, transparent to collaborators:
git config filter.snapper.clean "snapper --format org"
git config filter.snapper.smudge cat
Then add to .gitattributes:
*.org filter=snapper
Vale integration
snapper ships a vale style package for editor hints.
Add to your .vale.ini:
StylesPath = /path/to/snapper/vale
[*.org]
BasedOnStyles = snapper
For precise CI checks, use snapper --check directly.
Project config
Drop a .snapperrc.toml in your project root:
extra_abbreviations = ["GROMACS", "LAMMPS", "DFT"]
ignore = ["*.bib", "*.cls"]
format = "org"
max_width = 0
snapper walks up from the current directory to find it.
Documentation
Build the docs site with:
pixi run docbld
Development
Key dependencies
- Clap 4 (derive): CLI argument parsing
- unicode-segmentation: UAX #29 sentence boundaries
- regex: Abbreviation and format pattern matching
- textwrap: Optional line width limiting
- thiserror: Typed error handling
Conventions
We use cocogitto via cog to handle commit conventions.
Readme
Construct the readme via:
./scripts/org_to_md.sh readme_src.org README.md
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file snapper_fmt-0.8.0.tar.gz.
File metadata
- Download URL: snapper_fmt-0.8.0.tar.gz
- Upload date:
- Size: 1.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa66a90a0d175ec8c6b407ebb0506e04d11243f99e096143f0f7a1629f45921e
|
|
| MD5 |
a907b1a821a9b9cd66e2435c2ef68ac8
|
|
| BLAKE2b-256 |
9263d73d0eaacc2ae5c2035e587c91c51dde2afb7581d46b28c030c44d4a43f0
|
Provenance
The following attestation bundles were made for snapper_fmt-0.8.0.tar.gz:
Publisher:
pypi.yml on TurtleTech-ehf/snapper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snapper_fmt-0.8.0.tar.gz -
Subject digest:
aa66a90a0d175ec8c6b407ebb0506e04d11243f99e096143f0f7a1629f45921e - Sigstore transparency entry: 2205373367
- Sigstore integration time:
-
Permalink:
TurtleTech-ehf/snapper@466b189777b95994393be56b9efb70a5649f1340 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TurtleTech-ehf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@466b189777b95994393be56b9efb70a5649f1340 -
Trigger Event:
push
-
Statement type:
File details
Details for the file snapper_fmt-0.8.0-py3-none-win_amd64.whl.
File metadata
- Download URL: snapper_fmt-0.8.0-py3-none-win_amd64.whl
- Upload date:
- Size: 7.3 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
957846b8f0cd962f183a4a436800bd425e1e30e7dfc8531d1347559b995ce18e
|
|
| MD5 |
6a0062418bae7b4a16430cd2fed0926c
|
|
| BLAKE2b-256 |
96b204df1b6844770be06d959102dcb8d4d80cbaad4e9229fbd28e55b9ffb7dc
|
Provenance
The following attestation bundles were made for snapper_fmt-0.8.0-py3-none-win_amd64.whl:
Publisher:
pypi.yml on TurtleTech-ehf/snapper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snapper_fmt-0.8.0-py3-none-win_amd64.whl -
Subject digest:
957846b8f0cd962f183a4a436800bd425e1e30e7dfc8531d1347559b995ce18e - Sigstore transparency entry: 2205373476
- Sigstore integration time:
-
Permalink:
TurtleTech-ehf/snapper@466b189777b95994393be56b9efb70a5649f1340 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TurtleTech-ehf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@466b189777b95994393be56b9efb70a5649f1340 -
Trigger Event:
push
-
Statement type:
File details
Details for the file snapper_fmt-0.8.0-py3-none-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: snapper_fmt-0.8.0-py3-none-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 6.3 MB
- Tags: Python 3, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a10dd930c8de24ca5888f7fcd86ba04fb5226d76e14a7d534552d7915ca3337
|
|
| MD5 |
56aca3e36cc9a920b348fbbb126ee4c3
|
|
| BLAKE2b-256 |
7d9135b7233253d3a7452141c0c875579a8386d60be1a959034d131d6d585cdd
|
Provenance
The following attestation bundles were made for snapper_fmt-0.8.0-py3-none-manylinux_2_28_aarch64.whl:
Publisher:
pypi.yml on TurtleTech-ehf/snapper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snapper_fmt-0.8.0-py3-none-manylinux_2_28_aarch64.whl -
Subject digest:
5a10dd930c8de24ca5888f7fcd86ba04fb5226d76e14a7d534552d7915ca3337 - Sigstore transparency entry: 2205373419
- Sigstore integration time:
-
Permalink:
TurtleTech-ehf/snapper@466b189777b95994393be56b9efb70a5649f1340 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TurtleTech-ehf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@466b189777b95994393be56b9efb70a5649f1340 -
Trigger Event:
push
-
Statement type:
File details
Details for the file snapper_fmt-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: snapper_fmt-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 7.0 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28b6807695a0a504df3da2b3958f806409d5a197601c858626355712d0700b35
|
|
| MD5 |
02a95934a59fcf6c543cf006ae5b953e
|
|
| BLAKE2b-256 |
4a41d66a5d65262327a0924697f93cc6d268be8db43d1887427a4113323d1816
|
Provenance
The following attestation bundles were made for snapper_fmt-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
pypi.yml on TurtleTech-ehf/snapper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snapper_fmt-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
28b6807695a0a504df3da2b3958f806409d5a197601c858626355712d0700b35 - Sigstore transparency entry: 2205373459
- Sigstore integration time:
-
Permalink:
TurtleTech-ehf/snapper@466b189777b95994393be56b9efb70a5649f1340 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TurtleTech-ehf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@466b189777b95994393be56b9efb70a5649f1340 -
Trigger Event:
push
-
Statement type:
File details
Details for the file snapper_fmt-0.8.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: snapper_fmt-0.8.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 6.2 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f551c0050bfaf0485f6b0471a00590bc44c83725d9c2e8a4361a1ea5bfbe1023
|
|
| MD5 |
7b8dbbf4e5f4c43c02145cc59e6be6b1
|
|
| BLAKE2b-256 |
3d862613d0b7bc9c0dfa2886123836ac066d6fe72b061d5c8f3b651fc3aa5178
|
Provenance
The following attestation bundles were made for snapper_fmt-0.8.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
pypi.yml on TurtleTech-ehf/snapper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snapper_fmt-0.8.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
f551c0050bfaf0485f6b0471a00590bc44c83725d9c2e8a4361a1ea5bfbe1023 - Sigstore transparency entry: 2205373393
- Sigstore integration time:
-
Permalink:
TurtleTech-ehf/snapper@466b189777b95994393be56b9efb70a5649f1340 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TurtleTech-ehf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@466b189777b95994393be56b9efb70a5649f1340 -
Trigger Event:
push
-
Statement type:
File details
Details for the file snapper_fmt-0.8.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: snapper_fmt-0.8.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 6.7 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
523982238fc37361d1b24aacc05692b394e09ba903debd980963a9c4a504d9eb
|
|
| MD5 |
267b363730f45eb15ce16482c24bca14
|
|
| BLAKE2b-256 |
8c275f7097ef06f3f0d16a6de4276ac4833fea2a9518da25802662702ed706ac
|
Provenance
The following attestation bundles were made for snapper_fmt-0.8.0-py3-none-macosx_10_12_x86_64.whl:
Publisher:
pypi.yml on TurtleTech-ehf/snapper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snapper_fmt-0.8.0-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
523982238fc37361d1b24aacc05692b394e09ba903debd980963a9c4a504d9eb - Sigstore transparency entry: 2205373442
- Sigstore integration time:
-
Permalink:
TurtleTech-ehf/snapper@466b189777b95994393be56b9efb70a5649f1340 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TurtleTech-ehf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@466b189777b95994393be56b9efb70a5649f1340 -
Trigger Event:
push
-
Statement type: