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. snapper is a deterministic formatter (UAX #29 plus abbreviation tables and optional nnsplit). It is not admk/sembr (learned clause breaks) and not sembr/skills (agent rewrite instructions). latexindent.pl covers LaTeX only; snapper is a standalone Rust binary for Org-mode, LaTeX, Markdown, RST, 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. Markdown *em* / **strong** (CommonMark flanking) and GFM ~~strike~~ are kept atomic the same way.

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

conda-forge:

conda install -c conda-forge 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.

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

Break after independent-clause punctuation (comma, semicolon, colon, em dash). With the default unlimited width this inserts a newline after every such mark that already has whitespace after it:

snapper --clause-breaks paper.org

With --max-width set, overflowing sentences prefer those marks. A one-clause sentence stays one line.

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

MCP server (AI assistants)

Published snapper / snapper-fmt binaries include the MCP server (mcp is a default Cargo feature). Start the stdio server:

snapper mcp

Agents should call snapper MCP (format_text) or the snapper CLI instead of applying sembr.org / sembr/skills wrapping by hand. format_text accepts clause_breaks, range (start / end, 1-indexed inclusive), and max_width (default 0). From source with --no-default-features, rebuild with MCP:

cargo install snapper-fmt --features 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.10.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

Development preview; not listed in Community Plugins. The WebAssembly plugin source lives in editors/obsidian and is available for development builds only.

Microsoft Word

Development preview; not published in AppSource. The WebAssembly add-in source and sideloading instructions live in editors/word.

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
clause_breaks = false

[latex]
verbatim_envs = ["Verbatim"]
structure_envs = ["algorithm", "comment"]
verbatim_commands = ["Verb"]

snapper walks up from the current directory to find it. Missing [latex] keys keep the built-in minted/lstlisting/verbatim, equation/figure, and verb/lstinline lists.

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.10.0.tar.gz (208.5 kB 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.10.0-py3-none-win_amd64.whl (17.7 MB view details)

Uploaded Python 3Windows x86-64

snapper_fmt-0.10.0-py3-none-manylinux_2_28_aarch64.whl (15.5 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

snapper_fmt-0.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.9 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

snapper_fmt-0.10.0-py3-none-macosx_11_0_arm64.whl (15.4 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

snapper_fmt-0.10.0-py3-none-macosx_10_12_x86_64.whl (16.3 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

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

File metadata

  • Download URL: snapper_fmt-0.10.0.tar.gz
  • Upload date:
  • Size: 208.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for snapper_fmt-0.10.0.tar.gz
Algorithm Hash digest
SHA256 78b9ddfb44cae3b45c519a27154cc925fd2bb989a15a678d61c58f6cd5fba8e0
MD5 a947ed3a80fcbc4bc2b6111865e3f1d7
BLAKE2b-256 75d16242f0043a364a4a443e9b733169f681e90ca980ff75d63a69116e24baf2

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.10.0.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.10.0-py3-none-win_amd64.whl.

File metadata

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

File hashes

Hashes for snapper_fmt-0.10.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 6ca7f1474896fa73f6c9bf8a42feecaa877916b5c78e8dfcd75fab5fece0a19a
MD5 9ead82a582a1416eab679b990e2fee20
BLAKE2b-256 e0a368afa00b1fb8357cff972c86b59878f341b21ba4a52186289f793a0bb4eb

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.10.0-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.10.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.10.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 188fef42f47298da7d6f8b1739db9deb0e698ef4a78b1cd90f8ea33ed93fe5a5
MD5 6c88d12f8ed818cb6ea8420a70b49148
BLAKE2b-256 3dcaa7676d3987c7527a3e36f2cfe51ee273c351b092ed530e4987ee241b8a0d

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.10.0-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.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e90843acda9d17beca02fb88c3eda98ac24eea8193bfca6add34103c8188ac45
MD5 ac2c59538d384dc63c9dc00dbaab3ff3
BLAKE2b-256 a3c7fabc5e0b66cedc7fd8fb47c82c03669646da9d3b63f5e517d06ac6ab4e9e

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.10.0-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.10.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.10.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8eb12e7147130ed564076b94d9668d657e7bc3d32e1c1431e36f86cc1a8b0ab3
MD5 dbfefc086a947735992d01bad18572ec
BLAKE2b-256 7c4c75aa7bdecae280cd1d16f3b2f95ffd358eb38e5ab5188340bf57619e14f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.10.0-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.10.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for snapper_fmt-0.10.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 048d52c3fb98dba1e8e141e1e07e2e5abe6103e7fa23356dd67db130a8b4f377
MD5 e1bb39692c647a0b53bc2a4d88697da8
BLAKE2b-256 7b2fb62b55314db8086546015ff3a925430c887e4ee036b57176513ba466855d

See more details on using hashes here.

Provenance

The following attestation bundles were made for snapper_fmt-0.10.0-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