Canonical home: codeberg.org/adel/mdformat-sembr — issues and contributions tracked there. The GitHub mirror exists solely to enable PyPI Trusted Publishing and provenance attestation.
⚠️ Disclaimer: This project was built using vibe-/agentic-coding
An mdformat parser-extension plugin that inserts Semantic Line Breaks (SemBr) as CommonMark soft breaks.
SemBr is a convention for adding line breaks in Markdown source at sentence and
clause boundaries. Because the breaks are CommonMark soft breaks (a bare \n
inside a paragraph), they render to a single space — the rendered HTML output is
unchanged, only the source becomes more diff-friendly.
The plugin is fully deterministic: no ML, no network, no LLM calls. The same input always produces the same output.
Moving SemBr logic out of an LLM/agent loop into a token-free, reproducible formatter pass makes authored Markdown consistent and cheap to maintain.
# uv (recommended) — --with is repeatable (or comma-separate the plugins)
uv tool install mdformat --with mdformat-sembr --with mdformat-frontmatter
# pipx — install the app, then inject the plugins into its environment
pipx install mdformat
pipx inject mdformat mdformat-sembr mdformat-frontmatter
# local development
pip install -e .mdformat-frontmatter is optional: install it only if your Markdown uses
YAML/TOML frontmatter and you want mdformat to preserve/format it. It composes
with mdformat-sembr (frontmatter is a separate node type and is never broken).
mdformat --version # should list "mdformat_sembr"
echo "First sentence. Second sentence." | mdformat -From Python:
import mdformat
mdformat.text("First sentence. Second sentence.\n", extensions={"sembr"})
# 'First sentence.\nSecond sentence.\n'The plugin registers a postprocessor on the paragraph node type. At that point
inline formatting (emphasis, links, inline code) is already resolved into the string,
so it operates on the final rendered text and only protects a few inline constructs by
regex. Block-level elements (headings, code blocks, tables, frontmatter, HTML blocks)
are separate node types and are never touched.
CHANGES_AST = False: soft breaks are AST-safe by design, so mdformat's built-in
is_md_equal validator gates correctness. If validation ever fails, the break logic is
wrong — it is never worked around with --no-validate or hard breaks.
Configure via [plugin.sembr] in .mdformat.toml, or via CLI flags. CLI values merge
over TOML.
| Option | Type | Default | Meaning |
|---|---|---|---|
min_chars |
int | 15 |
Minimum length of the segment before a break is allowed. |
abbreviations |
list[str] | see below | Tokens after which no sentence break is inserted. |
break_clauses |
bool | false |
Enable clause-level breaks (SemBr "SHOULD"). Off by default. |
clause_chars |
str | ",;:—" |
Clause punctuation set (only used when break_clauses true). |
CLI flags: --sembr-min-chars, --sembr-abbreviations, --sembr-break-clauses,
--sembr-clause-chars.
.mdformat.toml example:
[plugin.sembr]
min_chars = 20
break_clauses = trueMIT