mdformat-space-control
An mdformat plugin that provides unified control over Markdown spacing:
- EditorConfig support: Configure list indentation via
.editorconfigfiles - Tight list formatting: Automatically removes unnecessary blank lines between list items
- Frontmatter spacing: Normalizes spacing after YAML frontmatter (works with mdformat-frontmatter)
- Consecutive blank line normalization: Limits runs of 3+ empty lines to a maximum of 2
- Trailing whitespace removal: Strips trailing whitespace outside code blocks
- Escaped link repair: Fixes malformed multi-line links from web-clipped content
- Smart dash conversion: Converts
--to en-dash (–) and---to em-dash (—), preserving code blocks, inline code, HTML comments, HTML tags, URIs, CLI options, wikilinks, and link destinations - Wikilink preservation: Handles Obsidian-style
[[links]],[[links|aliases]],[[page#heading]],[[page#^blockid]], and![[embeds]] - Soft break joining: Joins soft breaks (plain newlines within paragraphs) into single lines, normalizing to single-line paragraphs
Installation
pip install mdformat-space-control
Or with pipx for command-line usage:
pipx install mdformat
pipx inject mdformat mdformat-space-control
Usage
After installation, mdformat will automatically use this plugin:
mdformat your-file.md
EditorConfig Support
Create an .editorconfig file in your project:
# .editorconfig
root = true
[*.md]
indent_style = space
indent_size = 4
Nested lists will use the configured indentation:
Before:
- Item 1
- Nested item
- Item 2
After (with 4-space indent):
- Item 1
- Nested item
- Item 2
Tight List Formatting
Lists with single-paragraph items are automatically formatted as tight lists:
Before:
- Item 1
- Item 2
- Item 3
After:
- Item 1
- Item 2
- Item 3
Multi-paragraph items preserve loose formatting:
- First item with multiple paragraphs
Second paragraph of first item
- Second item
Frontmatter Spacing
When used with mdformat-frontmatter, this plugin removes blank lines between the frontmatter closing delimiter and the first content block:
Before:
---
title: My Document
---
# Introduction
After:
---
title: My Document
---
# Introduction
Install both plugins for this feature:
pip install mdformat-space-control mdformat-frontmatter
EditorConfig Properties
| Property | Status | Notes |
|---|---|---|
indent_style |
Supported | space or tab for list indentation |
indent_size |
Supported | Number of spaces per indent level |
tab_width |
Supported | Used when indent_size = tab |
Python API
When using the Python API, you can set the file context for EditorConfig lookup:
import mdformat
from mdformat_space_control import set_current_file
set_current_file("/path/to/your/file.md")
try:
result = mdformat.text(markdown_text, extensions={"space_control"})
finally:
set_current_file(None)
Smart Dash Conversion
Markdown dash sequences are automatically converted to their Unicode equivalents:
--→ en-dash (–, U+2013)---→ em-dash (—, U+2014)
Before:
The result---unexpected as it was---changed everything.
Pages 10--20 of the report.
After:
The result—unexpected as it was—changed everything.
Pages 10–20 of the report.
Dashes are preserved inside fenced code blocks, inline code spans, HTML comments, HTML tags, URIs (gs://bucket--name), CLI options (--output), wikilink targets (![[images/a--b/frame.jpg]]), and markdown link destinations ([text](docs/a--b.md)). Thematic breaks (---), GFM table separator rows (| -- | -- |), and frontmatter delimiters are not affected. Sequences of 4+ dashes are left unchanged.
These exclusions apply equally inside blockquotes, at any nesting depth: leading > markers are stripped before block-level patterns are matched, so tables and code fences in a blockquote or Obsidian callout are preserved just as they are at the top level.
Wikilink Preservation
Obsidian-style wikilinks are preserved during formatting:
Link to [[another note]] or [[note|with alias]].
Embed an image: ![[photo.jpg]]
Link to heading: [[note#section]]
Block reference: [[note#^blockid]]
Wikilinks inside markdown link text are correctly handled without duplication:
[![[image.jpg]]](http://example.com)
Literal square brackets inside a target are preserved (e.g. a note titled like an email subject), and are not escaped as stray link syntax:
[[[EXTERNAL] requesting feedback]]
Inside GFM table cells, an alias pipe is escaped to \| so the table parser does not split the wikilink across two columns (this requires mdformat-gfm). The escaped pipe renders correctly in Obsidian, and prose wikilinks keep a bare |:
| Note | Status |
| -- | -- |
| [[path/to/note|Short Alias]] | Active |
Soft Break Joining
Soft breaks (plain newlines within paragraphs) are joined into single lines with spaces. This normalizes editor-inserted line wraps to single-line paragraphs, matching CommonMark rendering behavior where soft breaks produce spaces in HTML output. The joining applies to paragraphs, list items, and blockquotes.
Before:
This is a paragraph with
a soft line break in the source.
After:
This is a paragraph with a soft line break in the source.
Explicit hard breaks (\ + newline) are preserved unchanged.
Compatible Plugins
This plugin is tested to work alongside:
- mdformat-frontmatter - YAML frontmatter parsing
- mdformat-simple-breaks - Normalizes thematic breaks to
--- - mdformat-gfm - GFM table parsing (prevents hard breaks from being inserted into pipe table rows)
For formatting files in an Obsidian vault, the recommended install is:
pip install mdformat-space-control mdformat-frontmatter mdformat-gfm
Note: Wikilink support is built-in; mdformat-wikilink is not needed.
Development
# Install dependencies
uv sync
# Run tests
uv run python -m pytest
# Run with coverage
uv run python -m pytest --cov=mdformat_space_control
License
MIT - see LICENSE file for details.
Metadata
Release files for mdformat-space-control 0.4.11
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mdformat_space_control-0.4.11.tar.gz | 24.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mdformat_space_control-0.4.11-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.4 kB
Release files / mdformat_space_control-0.4.11.tar.gz
| Download URL | mdformat_space_control-0.4.11.tar.gz |
|---|---|
| Size | 24.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f7c7cf94200c4944c4b926c8ab9c4885b9e598e858e94b48979939125fd8203e
|
|
BLAKE2b-256 checksum How to use checksums |
fca8bcb3a29379032eafa393aee6a9bdb25d98389aa63890e3bcac3e91070b84
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / mdformat_space_control-0.4.11-py3-none-any.whl
| Download URL | mdformat_space_control-0.4.11-py3-none-any.whl |
|---|---|
| Size | 14.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a607c4cd0d3429076dccaa6963e76926006eef591e6019a287e192d54bb09cab
|
|
BLAKE2b-256 checksum How to use checksums |
55c06fdf9153438f3e13f687d5720208e18fe4013f5aedd8130bb1b814678823
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|