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.
- mdformat_space_control-0.3.2/.editorconfig +6 -0
- mdformat_space_control-0.3.2/.gitignore +61 -0
- mdformat_space_control-0.3.2/CLAUDE.md +138 -0
- mdformat_space_control-0.3.2/LICENSE +21 -0
- mdformat_space_control-0.3.2/PKG-INFO +250 -0
- mdformat_space_control-0.3.2/README.md +222 -0
- mdformat_space_control-0.3.2/dev/CLAUDE.md +102 -0
- mdformat_space_control-0.3.2/dev/contributing.md +113 -0
- mdformat_space_control-0.3.2/dev/default-formatting-style.md +188 -0
- mdformat_space_control-0.3.2/dev/installation-and-usage.md +159 -0
- mdformat_space_control-0.3.2/dev/overview.md +62 -0
- mdformat_space_control-0.3.2/dev/plugins.md +83 -0
- mdformat_space_control-0.3.2/mdformat_space_control/__init__.py +19 -0
- mdformat_space_control-0.3.2/mdformat_space_control/config.py +122 -0
- mdformat_space_control-0.3.2/mdformat_space_control/plugin.py +400 -0
- mdformat_space_control-0.3.2/pyproject.toml +54 -0
|
@@ -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
|