Skip to main content

mdformat-slw

Build Status PyPI version

An mdformat plugin for semantic line wrapping (slw), which breaks lines after sentence-ending punctuation so that diffs stay small and readable.

Wrapping is on by default. A break is inserted after each end-of-sentence marker (.!?) except when:

  • The line is shorter than the minimum length (40 characters by default), which keeps short sentences together. Set --slw-min-line=0 to always wrap
  • The text can't be wrapped: inline code, links, definition lists, code blocks, tables, and HTML blocks
  • The marker ends an abbreviation, either because multiple markers occur (p.m., e.g.) or because the word is in the language list or in --slw-abbreviations. Matching is case-insensitive

The algorithm collapses consecutive whitespace to a single space (preserving non-breaking spaces and the linebreaks that follow them), finds the protected regions, inserts the sentence breaks, replaces spaces in link text with non-breaking spaces, and finally wraps anything still longer than --slw-wrap without splitting words or indents.

For examples, see ./tests/pre-commit-test.md and ./tests/format/fixtures

mdformat Usage

Add this package wherever you use mdformat and the plugin will be auto-recognized. For additional information on plugins, see the official mdformat documentation here

mdformat document.md

# Recommended when using --slw-wrap, because --wrap=keep turns off mdformat's own wrapping
mdformat document.md --slw-wrap=88 --wrap=keep

pre-commit/prek

repos:
  - repo: https://github.com/executablebooks/mdformat
    rev: 1.0.0
    hooks:
      - id: mdformat
        additional_dependencies:
          - mdformat-slw

uvx

uvx --with=mdformat-slw mdformat

Or with pipx:

pipx install mdformat
pipx inject mdformat mdformat-slw

Configuration

mdformat-slw adds the CLI arguments:

  • --no-wrap-sentences to turn off sentence wrapping
  • --slw-markers for the characters that end a sentence (default: .!?)
  • --slw-wrap for the maximum line width (default: 88, set to 0 to disable)
  • --slw-min-line for the shortest line that may be wrapped (default: 40, set to 0 to always wrap)
  • --slw-lang for the built-in abbreviation list (default: ac, one of ac, en, de, es, fr, or it)
  • --slw-abbreviations for a comma-separated list of extra abbreviations, such as "NASA,FBI,CustomCorp"
  • --slw-abbreviations-only to use only those abbreviations and skip the language list

You can also use the toml configuration (https://mdformat.readthedocs.io/en/stable/users/configuration_file.html):

# .mdformat.toml

[plugin.slw]
no_wrap_sentences = false
slw_markers = ".!?"
slw_wrap = 88
slw_min_line = 40
lang = "en"
abbreviations = "Corp,Inc,NASA"
abbreviations_only = false

[mdformat]
wrap = "keep"

Or the Python API:

import mdformat

mdformat.text("This is a test. It has multiple sentences!", extensions={"slw"})
# 'This is a test. It has multiple sentences!\n'

mdformat.text("This is a test. It has multiple sentences!", extensions={"slw"}, options={"slw_min_line": 0})
# 'This is a test.\nIt has multiple sentences!\n'

Example

Dr. Smith met with Prof. Johnson at 3 p.m. to review the draft. They discussed the wrapping rules etc. and agreed on the defaults.

Becomes:

Dr. Smith met with Prof. Johnson at 3 p.m. to review the draft.
They discussed the wrapping rules etc. and agreed on the defaults.

p.m. and etc. are abbreviations, so no break follows them.

Language Support

Abbreviation lists are built in for ac (Author's Choice, the 77-abbreviation default covering titles, time, Latin, academic, business, and geography terms), en (17), de (54), es (36), fr (42), and it (40).

mdformat-slw targets non-symbolic, left-to-right languages. Sentence detection relies on whitespace after a marker and wrapping relies on space-delimited words, but neither are applicable CJK or RTL scripts. Doing those properly means Unicode line breaking (UAX #14), kinsoku shori, word segmentation, and bidi handling, which is a different project, so they aren't planned:

  • CJK text has no spaces between sentences, so no boundaries are found. The text passes through unchanged (wcwidth accounts for double-width characters when measuring line length), and wrapping only applies if you space the sentences yourself, e.g. これは最初の文です。 これは2番目の文です。, and set --slw-markers=".!?。!?". See tests/format/fixtures/lang_ja.md and lang_ko.md
  • Arabic and other RTL text gets no bidi handling, and the native punctuation (؟ U+061F, ، U+060C, ؛ U+061B) isn't in the default marker set. ASCII markers still work and --slw-markers / --slw-abbreviations can add the rest for partial support

Acknowledgments

This plugin is inspired by and named after razziel89/mdslw, which is an excellent standalone tool for semantic line wrapping. mdslw can't be used as an mdformat plugin because mdformat formats from its own AST, so this package reimplements the idea natively, which lets it run alongside other mdformat plugins and in the same pre-commit hook. Overtime, the implementations have diverged, but I'm always open to submissions and feature requests to continually improve the developer experience.

Contributing

See CONTRIBUTING.md

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mdformat_slw-0.4.0.tar.gz (17.2 kB view details)

Uploaded Source

Built Distribution

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

mdformat_slw-0.4.0-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file mdformat_slw-0.4.0.tar.gz.

File metadata

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

File hashes

Hashes for mdformat_slw-0.4.0.tar.gz
Algorithm Hash digest
SHA256 93a8e40f47ab2ded82831c18264c3eb0356820602c63f3ab79bbc013c3dfb6a6
MD5 8d1ea32e1186fe97569673b08878f82c
BLAKE2b-256 dfe4875b5de5ab59c6f9c3d21e49d45245c7da0ebcae0f64b6ced9497e4bfd4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for mdformat_slw-0.4.0.tar.gz:

Publisher: tests.yml on KyleKing/mdformat-slw

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

File details

Details for the file mdformat_slw-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: mdformat_slw-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 16.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mdformat_slw-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8bbd18e8ec5facd383668bcc5a5cc14d53a58f16ffd47134631cef2ae66f8334
MD5 a8dadf9079101c0277205db96ceb83d9
BLAKE2b-256 04a02b29ed72e9afb60a93ec1d72ae2d079e46582919a5b794b7a22e268ece17

See more details on using hashes here.

Provenance

The following attestation bundles were made for mdformat_slw-0.4.0-py3-none-any.whl:

Publisher: tests.yml on KyleKing/mdformat-slw

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