mdformat-space-control 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ # Test-specific editorconfig - isolates from system/home settings
2
+ root = true
3
+
4
+ [*.md]
5
+ indent_style = space
6
+ indent_size = 2
@@ -0,0 +1,61 @@
1
+ *PLAN.md
2
+
3
+ # Python
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ *.so
8
+ .Python
9
+ build/
10
+ develop-eggs/
11
+ dist/
12
+ downloads/
13
+ eggs/
14
+ .eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+ MANIFEST
25
+
26
+ # Testing
27
+ htmlcov/
28
+ .tox/
29
+ .coverage
30
+ .coverage.*
31
+ .cache
32
+ coverage.xml
33
+ *.cover
34
+ .hypothesis/
35
+ .pytest_cache/
36
+
37
+ # Virtual environments
38
+ .env
39
+ .venv
40
+ env/
41
+ venv/
42
+ ENV/
43
+ env.bak/
44
+ venv.bak/
45
+
46
+ # IDEs
47
+ .vscode/
48
+ .idea/
49
+ *.swp
50
+ *.swo
51
+ *~
52
+
53
+ # OS
54
+ .DS_Store
55
+ Thumbs.db
56
+
57
+ # Package managers
58
+ uv.lock
59
+ poetry.lock
60
+ Pipfile.lock
61
+ pip-wheel-metadata/
@@ -0,0 +1,138 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ mdformat-space-control is an mdformat plugin that provides unified control over Markdown spacing:
8
+ - **EditorConfig support**: Configure list indentation via `.editorconfig` files
9
+ - **Tight list formatting**: Automatically removes unnecessary blank lines between list items
10
+ - **Frontmatter spacing**: Normalizes spacing after YAML frontmatter (works with mdformat-frontmatter)
11
+ - **Consecutive blank line normalization**: Limits runs of 3+ empty lines to a maximum of 2
12
+ - **Trailing whitespace removal**: Strips trailing whitespace outside code blocks
13
+ - **Escaped link repair**: Fixes malformed multi-line links from web-clipped content
14
+ - **Wikilink preservation**: Handles Obsidian-style `[[links]]`, `[[links|aliases]]`, `[[page#heading]]`, `[[page#^blockid]]`, and `![[embeds]]`
15
+ - **Explicit line breaks**: Converts soft breaks to hard breaks (`\` + newline) in paragraphs, list items, and blockquotes
16
+
17
+ This plugin merges functionality from mdformat-editorconfig and mdformat-tight-lists into a single plugin, solving the issue where mdformat only applies one set of list renderers when multiple plugins are installed.
18
+
19
+ ## Build and Test Commands
20
+
21
+ ```bash
22
+ uv sync --extra test # Install dependencies including test deps
23
+ uv run python -m pytest # Run all tests
24
+ uv run python -m pytest -v # Run tests verbosely
25
+ uv run python -m pytest --cov=mdformat_space_control # Run with coverage
26
+ uv run python -m pytest tests/test_editorconfig.py # Run specific test file
27
+ ```
28
+
29
+ ## Architecture
30
+
31
+ ```
32
+ mdformat_space_control/
33
+ ├── __init__.py # Public API exports, version
34
+ ├── config.py # EditorConfig lookup, file context tracking
35
+ └── plugin.py # List renderers (RENDERERS dict)
36
+ ```
37
+
38
+ **Key components:**
39
+
40
+ - **`config.py`**: Uses `contextvars` for thread-safe file path tracking. Falls back to `Path.cwd() / "_.md"` for CLI usage when no explicit file context is set.
41
+ - **`plugin.py`**: Provides renderers, postprocessors, and parser extensions:
42
+ - `_wikilink_rule`: Inline parser for Obsidian-style wikilinks
43
+ - `_render_wikilink`: Preserves wikilinks unchanged
44
+ - `_render_softbreak`: Converts soft breaks to hard breaks (backslash + newline)
45
+ - `_render_list_item`: Per-item tight/loose formatting based on paragraph count
46
+ - `_render_bullet_list`: Configurable indent + content-based tight/loose
47
+ - `_render_ordered_list`: Configurable indent + content-based tight/loose
48
+ - `_postprocess_root`: Combined postprocessor applying frontmatter spacing, escaped link repair, consecutive blank line normalization, and trailing whitespace removal
49
+
50
+ ## Plugin Extension Points
51
+
52
+ mdformat plugins expose:
53
+ 1. **`RENDERERS`**: Dict mapping node types to render functions
54
+ 2. **`POSTPROCESSORS`**: Dict mapping node types to postprocess functions
55
+ 3. **`update_mdit(mdit)`**: Hook to modify the markdown-it parser (used for wikilink parsing)
56
+
57
+ Entry point in `pyproject.toml`:
58
+ ```toml
59
+ [project.entry-points."mdformat.parser_extension"]
60
+ space_control = "mdformat_space_control"
61
+ ```
62
+
63
+ ## Test Structure
64
+
65
+ - **`tests/fixtures.md`**: Markdown-it fixture format for tight-list tests
66
+ - **`tests/test_fixtures.py`**: Parametrized fixture tests
67
+ - **`tests/test_editorconfig.py`**: EditorConfig-specific tests using temp directories
68
+ - **`tests/test_frontmatter.py`**: Frontmatter spacing tests (requires mdformat-frontmatter)
69
+ - **`tests/test_spacing_features.py`**: Trailing whitespace, hard breaks, escaped link repair, consecutive blank line tests
70
+ - **`tests/test_integration.py`**: Full-stack integration tests combining multiple features
71
+ - **`tests/test_plugin_interactions.py`**: Tests for compatibility with other mdformat plugins
72
+
73
+ ## Key Dependencies
74
+
75
+ - **mdformat** (>=0.7.0): The Markdown formatter being extended
76
+ - **editorconfig** (>=0.12.0): EditorConfig file parsing
77
+
78
+ ## Compatible Plugins
79
+
80
+ Tested to work alongside:
81
+ - `mdformat-frontmatter` - YAML frontmatter parsing
82
+ - `mdformat-simple-breaks` - Normalizes thematic breaks to `---`
83
+ - `mdformat-gfm` - GFM table parsing (prevents hard breaks in pipe table rows)
84
+
85
+ Note: Wikilink support is built-in; `mdformat-wikilink` is not needed.
86
+
87
+ ## Release Process
88
+
89
+ 1. Update version in `__init__.py`
90
+ 2. Commit changes
91
+ 3. Tag with `git tag vX.Y.Z`
92
+ 4. Push tag to trigger PyPI publish: `git push origin vX.Y.Z`
93
+
94
+ ## Claude Code Configuration
95
+
96
+ This project uses the global `~/.claude/settings.json` for all permissions and settings.
97
+
98
+ ### Tools Manager: tlmgr
99
+
100
+ The `tlmgr` command manages all tool repositories from the umbrella ~/tools directory:
101
+
102
+ ```bash
103
+ tlmgr --json summary # Overall status of all 14 repos
104
+ tlmgr --json list # Detailed status with branches
105
+ tlmgr changes # Show uncommitted changes
106
+ tlmgr unpushed # Show unpushed commits
107
+ ```
108
+
109
+ Always use `tlmgr` (not relative paths like `./bin/tools-manager.sh`).
110
+
111
+ ### Development Workflow
112
+
113
+ **Auto-allowed git operations:**
114
+ - Read: status, diff, log, show, branch, grep, blame
115
+ - Write: add, commit, push, pull, checkout, switch, restore, stash
116
+
117
+ **Require confirmation:**
118
+ - Destructive: merge, rebase, reset, cherry-pick, revert
119
+ - Force operations: push --force
120
+ - Repository changes: clone, init, submodule
121
+
122
+ ### Available Development Tools
123
+
124
+ **Python:** pytest, pip, poetry, uv (install, run, sync)
125
+ **Node:** npm (test, run, install), node
126
+ **Build:** make, bash scripts in ./scripts/
127
+ **Utilities:** find, grep, rg, cat, ls, tree, jq, yq, head, tail, wc
128
+ **Documents:** pandoc, md2docx, mdformat
129
+
130
+ ### Configuration
131
+
132
+ All permissions are centralized in `~/.claude/settings.json`:
133
+ - Sandbox is disabled globally
134
+ - Full read/write access to ~/tools/** and ~/agents/**
135
+ - Standard security protections (no ~/.ssh, .env files, etc.)
136
+ - Consistent behavior across all projects
137
+
138
+ No project-specific `.claude/` folders are needed.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Executable Books
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.4
2
+ Name: mdformat_space_control
3
+ Version: 0.3.2
4
+ Summary: An mdformat plugin for space control: EditorConfig indentation, tight lists, frontmatter spacing, and wikilinks.
5
+ Project-URL: Homepage, https://github.com/jdmonaco/mdformat-space-control
6
+ Author-email: Joseph Monaco <joe@selfmotion.net>
7
+ License-File: LICENSE
8
+ Keywords: editorconfig,formatter,frontmatter,indentation,lists,markdown,mdformat,obsidian,wikilink
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: editorconfig>=0.12.0
21
+ Requires-Dist: mdformat>=0.7.0
22
+ Provides-Extra: test
23
+ Requires-Dist: mdformat-frontmatter>=2.0.0; extra == 'test'
24
+ Requires-Dist: mdformat-simple-breaks>=0.1.0; extra == 'test'
25
+ Requires-Dist: pytest-cov>=4.0; extra == 'test'
26
+ Requires-Dist: pytest>=7.0; extra == 'test'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # mdformat-space-control
30
+
31
+ [![Build Status][ci-badge]][ci-link]
32
+ [![PyPI version][pypi-badge]][pypi-link]
33
+
34
+ An [mdformat](https://github.com/executablebooks/mdformat) plugin that provides unified control over Markdown spacing:
35
+
36
+ - **EditorConfig support**: Configure list indentation via `.editorconfig` files
37
+ - **Tight list formatting**: Automatically removes unnecessary blank lines between list items
38
+ - **Frontmatter spacing**: Normalizes spacing after YAML frontmatter (works with [mdformat-frontmatter](https://github.com/butler54/mdformat-frontmatter))
39
+ - **Consecutive blank line normalization**: Limits runs of 3+ empty lines to a maximum of 2
40
+ - **Trailing whitespace removal**: Strips trailing whitespace outside code blocks
41
+ - **Escaped link repair**: Fixes malformed multi-line links from web-clipped content
42
+ - **Wikilink preservation**: Handles Obsidian-style `[[links]]`, `[[links|aliases]]`, `[[page#heading]]`, `[[page#^blockid]]`, and `![[embeds]]`
43
+ - **Explicit line breaks**: Converts soft breaks to hard breaks (`\` + newline) so source line breaks render visually in all Markdown renderers
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install mdformat-space-control
49
+ ```
50
+
51
+ Or with [pipx](https://pipx.pypa.io/) for command-line usage:
52
+
53
+ ```bash
54
+ pipx install mdformat
55
+ pipx inject mdformat mdformat-space-control
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ After installation, mdformat will automatically use this plugin:
61
+
62
+ ```bash
63
+ mdformat your-file.md
64
+ ```
65
+
66
+ ### EditorConfig Support
67
+
68
+ Create an `.editorconfig` file in your project:
69
+
70
+ ```ini
71
+ # .editorconfig
72
+ root = true
73
+
74
+ [*.md]
75
+ indent_style = space
76
+ indent_size = 4
77
+ ```
78
+
79
+ Nested lists will use the configured indentation:
80
+
81
+ **Before:**
82
+ ```markdown
83
+ - Item 1
84
+ - Nested item
85
+ - Item 2
86
+ ```
87
+
88
+ **After (with 4-space indent):**
89
+ ```markdown
90
+ - Item 1
91
+ - Nested item
92
+ - Item 2
93
+ ```
94
+
95
+ ### Tight List Formatting
96
+
97
+ Lists with single-paragraph items are automatically formatted as tight lists:
98
+
99
+ **Before:**
100
+ ```markdown
101
+ - Item 1
102
+
103
+ - Item 2
104
+
105
+ - Item 3
106
+ ```
107
+
108
+ **After:**
109
+ ```markdown
110
+ - Item 1
111
+ - Item 2
112
+ - Item 3
113
+ ```
114
+
115
+ Multi-paragraph items preserve loose formatting:
116
+
117
+ ```markdown
118
+ - First item with multiple paragraphs
119
+
120
+ Second paragraph of first item
121
+
122
+ - Second item
123
+ ```
124
+
125
+ ### Frontmatter Spacing
126
+
127
+ When used with [mdformat-frontmatter](https://github.com/butler54/mdformat-frontmatter), this plugin removes blank lines between the frontmatter closing delimiter and the first content block:
128
+
129
+ **Before:**
130
+ ```markdown
131
+ ---
132
+ title: My Document
133
+ ---
134
+
135
+
136
+ # Introduction
137
+ ```
138
+
139
+ **After:**
140
+ ```markdown
141
+ ---
142
+ title: My Document
143
+ ---
144
+ # Introduction
145
+ ```
146
+
147
+ Install both plugins for this feature:
148
+
149
+ ```bash
150
+ pip install mdformat-space-control mdformat-frontmatter
151
+ ```
152
+
153
+ ### EditorConfig Properties
154
+
155
+ | Property | Status | Notes |
156
+ |----------|--------|-------|
157
+ | `indent_style` | Supported | `space` or `tab` for list indentation |
158
+ | `indent_size` | Supported | Number of spaces per indent level |
159
+ | `tab_width` | Supported | Used when `indent_size = tab` |
160
+
161
+ ### Python API
162
+
163
+ When using the Python API, you can set the file context for EditorConfig lookup:
164
+
165
+ ```python
166
+ import mdformat
167
+ from mdformat_space_control import set_current_file
168
+
169
+ set_current_file("/path/to/your/file.md")
170
+ try:
171
+ result = mdformat.text(markdown_text, extensions={"space_control"})
172
+ finally:
173
+ set_current_file(None)
174
+ ```
175
+
176
+ ### Wikilink Preservation
177
+
178
+ Obsidian-style wikilinks are preserved during formatting:
179
+
180
+ ```markdown
181
+ Link to [[another note]] or [[note|with alias]].
182
+ Embed an image: ![[photo.jpg]]
183
+ Link to heading: [[note#section]]
184
+ Block reference: [[note#^blockid]]
185
+ ```
186
+
187
+ Wikilinks inside markdown link text are correctly handled without duplication:
188
+
189
+ ```markdown
190
+ [![[image.jpg]]](http://example.com)
191
+ ```
192
+
193
+ ### Explicit Line Breaks
194
+
195
+ Soft breaks (plain newlines within paragraphs) are converted to hard breaks (backslash + newline). This makes source line breaks explicit so they render visually regardless of the Markdown renderer's line-break strictness setting. The conversion applies to paragraphs, list items, and blockquotes.
196
+
197
+ **Before:**
198
+ ```markdown
199
+ This is a paragraph with
200
+ a soft line break in the source.
201
+ ```
202
+
203
+ **After:**
204
+ ```markdown
205
+ This is a paragraph with\
206
+ a hard line break in the output.
207
+ ```
208
+
209
+ > **Note:** Without a GFM table parser, markdown-it treats pipe tables as paragraphs
210
+ > containing soft breaks, which causes backslashes to appear at the end of table rows.
211
+ > Install `mdformat-gfm` to enable proper table parsing and avoid this issue
212
+ > (see [Compatible Plugins](#compatible-plugins)).
213
+
214
+ ## Compatible Plugins
215
+
216
+ This plugin is tested to work alongside:
217
+
218
+ - [mdformat-frontmatter](https://github.com/butler54/mdformat-frontmatter) - YAML frontmatter parsing
219
+ - [mdformat-simple-breaks](https://github.com/csala/mdformat-simple-breaks) - Normalizes thematic breaks to `---`
220
+ - [mdformat-gfm](https://github.com/hukkin/mdformat-gfm) - GFM table parsing (prevents hard breaks from being inserted into pipe table rows)
221
+
222
+ For formatting files in an **Obsidian vault**, the recommended install is:
223
+
224
+ ```bash
225
+ pip install mdformat-space-control mdformat-frontmatter mdformat-gfm
226
+ ```
227
+
228
+ Note: Wikilink support is built-in; `mdformat-wikilink` is not needed.
229
+
230
+ ## Development
231
+
232
+ ```bash
233
+ # Install dependencies
234
+ uv sync
235
+
236
+ # Run tests
237
+ uv run python -m pytest
238
+
239
+ # Run with coverage
240
+ uv run python -m pytest --cov=mdformat_space_control
241
+ ```
242
+
243
+ ## License
244
+
245
+ MIT - see LICENSE file for details.
246
+
247
+ [ci-badge]: https://github.com/jdmonaco/mdformat-space-control/actions/workflows/tests.yml/badge.svg?branch=main
248
+ [ci-link]: https://github.com/jdmonaco/mdformat-space-control/actions/workflows/tests.yml
249
+ [pypi-badge]: https://img.shields.io/pypi/v/mdformat-space-control.svg
250
+ [pypi-link]: https://pypi.org/project/mdformat-space-control
@@ -0,0 +1,222 @@
1
+ # mdformat-space-control
2
+
3
+ [![Build Status][ci-badge]][ci-link]
4
+ [![PyPI version][pypi-badge]][pypi-link]
5
+
6
+ An [mdformat](https://github.com/executablebooks/mdformat) plugin that provides unified control over Markdown spacing:
7
+
8
+ - **EditorConfig support**: Configure list indentation via `.editorconfig` files
9
+ - **Tight list formatting**: Automatically removes unnecessary blank lines between list items
10
+ - **Frontmatter spacing**: Normalizes spacing after YAML frontmatter (works with [mdformat-frontmatter](https://github.com/butler54/mdformat-frontmatter))
11
+ - **Consecutive blank line normalization**: Limits runs of 3+ empty lines to a maximum of 2
12
+ - **Trailing whitespace removal**: Strips trailing whitespace outside code blocks
13
+ - **Escaped link repair**: Fixes malformed multi-line links from web-clipped content
14
+ - **Wikilink preservation**: Handles Obsidian-style `[[links]]`, `[[links|aliases]]`, `[[page#heading]]`, `[[page#^blockid]]`, and `![[embeds]]`
15
+ - **Explicit line breaks**: Converts soft breaks to hard breaks (`\` + newline) so source line breaks render visually in all Markdown renderers
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install mdformat-space-control
21
+ ```
22
+
23
+ Or with [pipx](https://pipx.pypa.io/) for command-line usage:
24
+
25
+ ```bash
26
+ pipx install mdformat
27
+ pipx inject mdformat mdformat-space-control
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ After installation, mdformat will automatically use this plugin:
33
+
34
+ ```bash
35
+ mdformat your-file.md
36
+ ```
37
+
38
+ ### EditorConfig Support
39
+
40
+ Create an `.editorconfig` file in your project:
41
+
42
+ ```ini
43
+ # .editorconfig
44
+ root = true
45
+
46
+ [*.md]
47
+ indent_style = space
48
+ indent_size = 4
49
+ ```
50
+
51
+ Nested lists will use the configured indentation:
52
+
53
+ **Before:**
54
+ ```markdown
55
+ - Item 1
56
+ - Nested item
57
+ - Item 2
58
+ ```
59
+
60
+ **After (with 4-space indent):**
61
+ ```markdown
62
+ - Item 1
63
+ - Nested item
64
+ - Item 2
65
+ ```
66
+
67
+ ### Tight List Formatting
68
+
69
+ Lists with single-paragraph items are automatically formatted as tight lists:
70
+
71
+ **Before:**
72
+ ```markdown
73
+ - Item 1
74
+
75
+ - Item 2
76
+
77
+ - Item 3
78
+ ```
79
+
80
+ **After:**
81
+ ```markdown
82
+ - Item 1
83
+ - Item 2
84
+ - Item 3
85
+ ```
86
+
87
+ Multi-paragraph items preserve loose formatting:
88
+
89
+ ```markdown
90
+ - First item with multiple paragraphs
91
+
92
+ Second paragraph of first item
93
+
94
+ - Second item
95
+ ```
96
+
97
+ ### Frontmatter Spacing
98
+
99
+ When used with [mdformat-frontmatter](https://github.com/butler54/mdformat-frontmatter), this plugin removes blank lines between the frontmatter closing delimiter and the first content block:
100
+
101
+ **Before:**
102
+ ```markdown
103
+ ---
104
+ title: My Document
105
+ ---
106
+
107
+
108
+ # Introduction
109
+ ```
110
+
111
+ **After:**
112
+ ```markdown
113
+ ---
114
+ title: My Document
115
+ ---
116
+ # Introduction
117
+ ```
118
+
119
+ Install both plugins for this feature:
120
+
121
+ ```bash
122
+ pip install mdformat-space-control mdformat-frontmatter
123
+ ```
124
+
125
+ ### EditorConfig Properties
126
+
127
+ | Property | Status | Notes |
128
+ |----------|--------|-------|
129
+ | `indent_style` | Supported | `space` or `tab` for list indentation |
130
+ | `indent_size` | Supported | Number of spaces per indent level |
131
+ | `tab_width` | Supported | Used when `indent_size = tab` |
132
+
133
+ ### Python API
134
+
135
+ When using the Python API, you can set the file context for EditorConfig lookup:
136
+
137
+ ```python
138
+ import mdformat
139
+ from mdformat_space_control import set_current_file
140
+
141
+ set_current_file("/path/to/your/file.md")
142
+ try:
143
+ result = mdformat.text(markdown_text, extensions={"space_control"})
144
+ finally:
145
+ set_current_file(None)
146
+ ```
147
+
148
+ ### Wikilink Preservation
149
+
150
+ Obsidian-style wikilinks are preserved during formatting:
151
+
152
+ ```markdown
153
+ Link to [[another note]] or [[note|with alias]].
154
+ Embed an image: ![[photo.jpg]]
155
+ Link to heading: [[note#section]]
156
+ Block reference: [[note#^blockid]]
157
+ ```
158
+
159
+ Wikilinks inside markdown link text are correctly handled without duplication:
160
+
161
+ ```markdown
162
+ [![[image.jpg]]](http://example.com)
163
+ ```
164
+
165
+ ### Explicit Line Breaks
166
+
167
+ Soft breaks (plain newlines within paragraphs) are converted to hard breaks (backslash + newline). This makes source line breaks explicit so they render visually regardless of the Markdown renderer's line-break strictness setting. The conversion applies to paragraphs, list items, and blockquotes.
168
+
169
+ **Before:**
170
+ ```markdown
171
+ This is a paragraph with
172
+ a soft line break in the source.
173
+ ```
174
+
175
+ **After:**
176
+ ```markdown
177
+ This is a paragraph with\
178
+ a hard line break in the output.
179
+ ```
180
+
181
+ > **Note:** Without a GFM table parser, markdown-it treats pipe tables as paragraphs
182
+ > containing soft breaks, which causes backslashes to appear at the end of table rows.
183
+ > Install `mdformat-gfm` to enable proper table parsing and avoid this issue
184
+ > (see [Compatible Plugins](#compatible-plugins)).
185
+
186
+ ## Compatible Plugins
187
+
188
+ This plugin is tested to work alongside:
189
+
190
+ - [mdformat-frontmatter](https://github.com/butler54/mdformat-frontmatter) - YAML frontmatter parsing
191
+ - [mdformat-simple-breaks](https://github.com/csala/mdformat-simple-breaks) - Normalizes thematic breaks to `---`
192
+ - [mdformat-gfm](https://github.com/hukkin/mdformat-gfm) - GFM table parsing (prevents hard breaks from being inserted into pipe table rows)
193
+
194
+ For formatting files in an **Obsidian vault**, the recommended install is:
195
+
196
+ ```bash
197
+ pip install mdformat-space-control mdformat-frontmatter mdformat-gfm
198
+ ```
199
+
200
+ Note: Wikilink support is built-in; `mdformat-wikilink` is not needed.
201
+
202
+ ## Development
203
+
204
+ ```bash
205
+ # Install dependencies
206
+ uv sync
207
+
208
+ # Run tests
209
+ uv run python -m pytest
210
+
211
+ # Run with coverage
212
+ uv run python -m pytest --cov=mdformat_space_control
213
+ ```
214
+
215
+ ## License
216
+
217
+ MIT - see LICENSE file for details.
218
+
219
+ [ci-badge]: https://github.com/jdmonaco/mdformat-space-control/actions/workflows/tests.yml/badge.svg?branch=main
220
+ [ci-link]: https://github.com/jdmonaco/mdformat-space-control/actions/workflows/tests.yml
221
+ [pypi-badge]: https://img.shields.io/pypi/v/mdformat-space-control.svg
222
+ [pypi-link]: https://pypi.org/project/mdformat-space-control