Skip to main content

img

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. Each install ships two CLI names for the same program: snapper and snapper-fmt.

Name collision: openSUSE snapper is a different project (Btrfs/LVM snapshots) that also installs a snapper binary. On systems where that tool already owns /usr/bin/snapper, call this formatter as snapper-fmt, or put the TurtleTech install path ahead of the system path.

Pre-built installers (cargo-dist) ship the plain binaries: 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 pandoc binary.
  • FFI backend: build native/snapper-pandoc and set SNAPPER_PANDOC_LIB, or build with feature pandoc-colink after ./native/snapper-pandoc/build-static.sh so 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. See native/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.1
  hooks:
    - id: snapper

Runs snapper --in-place on *.org, *.tex / *.latex, *.md / *.markdown, *.rst, and *.txt (matched by extension; pre-commit's identify database has no org type). Requires a Rust toolchain so pre-commit can build the hook from this repo.

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

snapper_fmt-0.8.1.tar.gz (1.9 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

snapper_fmt-0.8.1-py3-none-win_amd64.whl (14.4 MB view details)

Uploaded Python 3Windows x86-64

snapper_fmt-0.8.1-py3-none-manylinux_2_28_aarch64.whl (12.4 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

snapper_fmt-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (13.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

snapper_fmt-0.8.1-py3-none-macosx_11_0_arm64.whl (12.1 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

snapper_fmt-0.8.1-py3-none-macosx_10_12_x86_64.whl (13.1 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file snapper_fmt-0.8.1.tar.gz.

File metadata

  • Download URL: snapper_fmt-0.8.1.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

Hashes for snapper_fmt-0.8.1.tar.gz
Algorithm Hash digest
SHA256 f13e7de4088728a92d76dd7b1b49651df5787ae3ca5144f8b6eacff6cda907bb
MD5 735e0d96e893f192a4815fb37392f317
BLAKE2b-256 8a6a91a2f33b975ab5606fa8ced8b216534c6317a0e29ed6f62efe43e03e4458

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.8.1.tar.gz:

Publisher: pypi.yml on TurtleTech-ehf/snapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file snapper_fmt-0.8.1-py3-none-win_amd64.whl.

File metadata

  • Download URL: snapper_fmt-0.8.1-py3-none-win_amd64.whl
  • Upload date:
  • Size: 14.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for snapper_fmt-0.8.1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 3ebbf71917359bc12ade630d9e9a68b7de355c43ebbc25acd6ca13ec0d88335c
MD5 f159bd15800a4a0e4bf05a4954fd148a
BLAKE2b-256 db6afe7b60825b4ac2966d05256e56ddd9ec80c96948f1f6122606430ae681a6

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.8.1-py3-none-win_amd64.whl:

Publisher: pypi.yml on TurtleTech-ehf/snapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file snapper_fmt-0.8.1-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.8.1-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 d10884e41ef36746426b0684b1e400228bbcc043c212736780f45760213e599f
MD5 3ec80bce70788590a31cdcfdfe9e6c2e
BLAKE2b-256 c060958ac636bc70a85cb8f17d4b718b28528528f2dc9f2da398c8beef8dab12

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.8.1-py3-none-manylinux_2_28_aarch64.whl:

Publisher: pypi.yml on TurtleTech-ehf/snapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file snapper_fmt-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8ba795b15bc9ad7e11a337898f9567a9654f6a2655e73518876776b38b572dc1
MD5 3cb20dd472e94443de3e3d08a7dd0109
BLAKE2b-256 5d27a7bc8ecff2c5791c6d202a71bdb93b0c48ac97ff71d08350405b50e96fe0

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: pypi.yml on TurtleTech-ehf/snapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file snapper_fmt-0.8.1-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.8.1-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d59fb92eab4188a6d1336ab379de24a898c78042adc0b239fcf46ca22112b7a1
MD5 b812191f37da1a280013088d04a9c595
BLAKE2b-256 1affb8c3ab819c4b90abeb197f24abb373391daa472c066605247d032941aefe

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.8.1-py3-none-macosx_11_0_arm64.whl:

Publisher: pypi.yml on TurtleTech-ehf/snapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file snapper_fmt-0.8.1-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.8.1-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 0b7e88e2cc254837365769bfc7b2fd03153b5e546a22246e3246462721bcd5cd
MD5 b0a5b6dc62e0f7fda459e71532660251
BLAKE2b-256 ab3e9a192182abfc9ab94686fd437c313c8001d79a597cf1aaba44338d1416fd

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.8.1-py3-none-macosx_10_12_x86_64.whl:

Publisher: pypi.yml on TurtleTech-ehf/snapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page