mdit-py-plugins 0.5.0__tar.gz → 0.6.1__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.
Files changed (72) hide show
  1. mdit_py_plugins-0.6.1/.github/workflows/copilot-setup-steps.yml +43 -0
  2. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.pre-commit-config.yaml +2 -2
  3. mdit_py_plugins-0.6.1/AGENTS.md +373 -0
  4. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/CHANGELOG.md +33 -0
  5. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/PKG-INFO +2 -1
  6. mdit_py_plugins-0.6.1/mdit_py_plugins/__init__.py +1 -0
  7. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/attrs/index.py +1 -1
  8. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/dollarmath/index.py +3 -1
  9. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/field_list/__init__.py +3 -2
  10. mdit_py_plugins-0.6.1/mdit_py_plugins/gfm/__init__.py +102 -0
  11. mdit_py_plugins-0.6.1/mdit_py_plugins/gfm_autolink/__init__.py +15 -0
  12. mdit_py_plugins-0.6.1/mdit_py_plugins/gfm_autolink/_match.py +250 -0
  13. mdit_py_plugins-0.6.1/mdit_py_plugins/gfm_autolink/index.py +295 -0
  14. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/subscript/__init__.py +2 -4
  15. mdit_py_plugins-0.6.1/mdit_py_plugins/superscript/__init__.py +5 -0
  16. mdit_py_plugins-0.6.1/mdit_py_plugins/superscript/index.py +110 -0
  17. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/utils.py +7 -0
  18. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/pyproject.toml +4 -0
  19. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/tox.ini +9 -0
  20. mdit_py_plugins-0.5.0/mdit_py_plugins/__init__.py +0 -1
  21. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.coveragerc +0 -0
  22. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.github/dependabot.yml +0 -0
  23. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.github/workflows/tests.yml +0 -0
  24. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.gitignore +0 -0
  25. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.readthedocs.yml +0 -0
  26. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/LICENSE +0 -0
  27. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/README.md +0 -0
  28. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/codecov.yml +0 -0
  29. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/LICENSE +0 -0
  30. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/__init__.py +0 -0
  31. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/index.py +0 -0
  32. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/port.yaml +0 -0
  33. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/amsmath/__init__.py +0 -0
  34. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/anchors/__init__.py +0 -0
  35. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/anchors/index.py +0 -0
  36. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/attrs/__init__.py +0 -0
  37. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/attrs/parse.py +0 -0
  38. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/colon_fence.py +0 -0
  39. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/LICENSE +0 -0
  40. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/README.md +0 -0
  41. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/__init__.py +0 -0
  42. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/index.py +0 -0
  43. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/port.yaml +0 -0
  44. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/LICENSE +0 -0
  45. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/README.md +0 -0
  46. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/__init__.py +0 -0
  47. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/index.py +0 -0
  48. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/port.yaml +0 -0
  49. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/dollarmath/__init__.py +0 -0
  50. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/LICENSE +0 -0
  51. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/__init__.py +0 -0
  52. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/index.py +0 -0
  53. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/port.yaml +0 -0
  54. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/LICENSE +0 -0
  55. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/__init__.py +0 -0
  56. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/index.py +0 -0
  57. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/port.yaml +0 -0
  58. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_blocks/__init__.py +0 -0
  59. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_blocks/index.py +0 -0
  60. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_role/__init__.py +0 -0
  61. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_role/index.py +0 -0
  62. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/py.typed +0 -0
  63. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/subscript/port.yaml +0 -0
  64. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/substitution.py +0 -0
  65. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/tasklists/__init__.py +0 -0
  66. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/tasklists/port.yaml +0 -0
  67. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/LICENSE +0 -0
  68. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/README.md +0 -0
  69. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/__init__.py +0 -0
  70. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/index.py +0 -0
  71. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/port.yaml +0 -0
  72. {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/wordcount/__init__.py +0 -0
@@ -0,0 +1,43 @@
1
+ name: Copilot Setup Steps
2
+
3
+ # Automatically run the setup steps when they are changed to allow for easy validation, and
4
+ # allow manual testing through the repository's "Actions" tab
5
+ on:
6
+ workflow_dispatch:
7
+ push:
8
+ paths:
9
+ - .github/workflows/copilot-setup-steps.yml
10
+ pull_request:
11
+ paths:
12
+ - .github/workflows/copilot-setup-steps.yml
13
+
14
+ jobs:
15
+ # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
16
+ copilot-setup-steps:
17
+ runs-on: ubuntu-latest
18
+
19
+ # Set the permissions to the lowest permissions possible needed for your steps.
20
+ # Copilot will be given its own token for its operations.
21
+ permissions:
22
+ contents: read
23
+
24
+ # You can define any steps you want, and they will run before the agent starts.
25
+ # If you do not check out your code, Copilot will do this for you.
26
+ steps:
27
+ - name: Checkout code
28
+ uses: actions/checkout@v4
29
+
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v5
32
+ with:
33
+ python-version: "3.11"
34
+ cache: pip
35
+
36
+ - name: Install uv
37
+ run: pip install uv
38
+
39
+ - name: Install pre-commit and pre-commit-uv
40
+ run: pip install pre-commit pre-commit-uv
41
+
42
+ - name: Install tox and tox-uv
43
+ run: pip install tox tox-uv
@@ -20,14 +20,14 @@ repos:
20
20
  - id: trailing-whitespace
21
21
 
22
22
  - repo: https://github.com/astral-sh/ruff-pre-commit
23
- rev: v0.12.8
23
+ rev: v0.15.12
24
24
  hooks:
25
25
  - id: ruff
26
26
  args: [--fix]
27
27
  - id: ruff-format
28
28
 
29
29
  - repo: https://github.com/pre-commit/mirrors-mypy
30
- rev: v1.17.1
30
+ rev: v1.20.2
31
31
  hooks:
32
32
  - id: mypy
33
33
  additional_dependencies: [markdown-it-py~=3.0]
@@ -0,0 +1,373 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance for AI coding agents working on the **mdit-py-plugins** repository.
4
+
5
+ ## Project Overview
6
+
7
+ mdit-py-plugins is a collection of plugins for [markdown-it-py](https://github.com/executablebooks/markdown-it-py), the Python Markdown parser. It provides:
8
+
9
+ - Syntax extensions for markdown-it-py (footnotes, front matter, definition lists, task lists, etc.)
10
+ - Both block-level and inline-level parsing rules
11
+ - Each plugin follows the markdown-it-py plugin architecture: a function that receives a `MarkdownIt` instance and registers rules
12
+ - Used extensively by [MyST-Parser](https://github.com/executablebooks/MyST-Parser) for Sphinx documentation
13
+
14
+ ## Repository Structure
15
+
16
+ ```
17
+ pyproject.toml # Project configuration and dependencies (flit)
18
+ tox.ini # Tox test environment configuration
19
+ .pre-commit-config.yaml # Pre-commit hooks configuration
20
+
21
+ mdit_py_plugins/ # Main source code
22
+ ├── __init__.py # Package init (version only)
23
+ ├── utils.py # Shared utility functions
24
+ ├── py.typed # PEP 561 marker
25
+ ├── admon/ # Admonition plugin (note, warning, etc.)
26
+ ├── amsmath/ # AMS math environment plugin
27
+ ├── anchors/ # Heading anchor plugin
28
+ ├── attrs/ # Inline/block attribute plugin ({.class #id})
29
+ ├── colon_fence.py # Colon fence plugin (:::)
30
+ ├── container/ # Container plugin (custom divs)
31
+ ├── deflist/ # Definition list plugin
32
+ ├── dollarmath/ # Dollar math plugin ($...$, $$...$$)
33
+ ├── field_list/ # Field list plugin
34
+ ├── footnote/ # Footnote plugin
35
+ ├── front_matter/ # YAML front matter plugin
36
+ ├── myst_blocks/ # MyST block syntax plugin
37
+ ├── myst_role/ # MyST role syntax plugin ({role}`text`)
38
+ ├── subscript/ # Subscript plugin (~sub~)
39
+ ├── substitution.py # Substitution plugin
40
+ ├── tasklists/ # Task list plugin (- [x] done)
41
+ ├── texmath/ # TeX math plugin
42
+ └── wordcount/ # Word count plugin
43
+
44
+ tests/ # Test suite
45
+ ├── fixtures/ # Shared test fixture files
46
+ ├── test_admon/ # Admonition tests with fixture files
47
+ ├── test_amsmath/ # AMS math tests
48
+ ├── test_anchors.py # Anchors tests
49
+ ├── test_attrs/ # Attributes tests
50
+ ├── test_colon_fence/ # Colon fence tests
51
+ ├── test_container/ # Container tests
52
+ ├── test_deflist/ # Definition list tests
53
+ ├── test_dollarmath/ # Dollar math tests
54
+ ├── test_field_list/ # Field list tests
55
+ ├── test_footnote.py # Footnote tests
56
+ ├── test_front_matter.py # Front matter tests
57
+ ├── test_myst_block.py # MyST block tests
58
+ ├── test_myst_role.py # MyST role tests
59
+ ├── test_subscript.py # Subscript tests
60
+ ├── test_substitution/ # Substitution tests
61
+ ├── test_tasklists/ # Task list tests
62
+ ├── test_texmath/ # TeX math tests
63
+ └── test_wordcount.py # Word count tests
64
+
65
+ docs/ # Documentation source
66
+ ├── conf.py # Sphinx configuration
67
+ └── index.md # Documentation index
68
+ ```
69
+
70
+ ## Development Commands
71
+
72
+ All commands should be run via [`tox`](https://tox.wiki) for consistency. The project uses `tox-uv` for faster environment creation.
73
+
74
+ ### Testing
75
+
76
+ ```bash
77
+ # Run all tests
78
+ tox
79
+
80
+ # Run tests with specific Python version
81
+ tox -e py311
82
+
83
+ # Run a specific test file
84
+ tox -e py310 -- tests/test_footnote.py
85
+
86
+ # Run a specific test function
87
+ tox -e py310 -- tests/test_footnote.py::test_function_name
88
+
89
+ # Run tests with coverage
90
+ tox -e py310 -- --cov=mdit_py_plugins --cov-report=html
91
+
92
+ # Update regression test fixtures (produces error if files change)
93
+ tox -e py310 -- --force-regen
94
+ ```
95
+
96
+ ### Documentation
97
+
98
+ ```bash
99
+ # Build docs (clean)
100
+ tox -e docs-clean
101
+
102
+ # Build docs (incremental)
103
+ tox -e docs-update
104
+
105
+ # Build with a specific builder (e.g., linkcheck to validate external links)
106
+ tox -e docs-update -- linkcheck
107
+ ```
108
+
109
+ ### Code Quality
110
+
111
+ ```bash
112
+ # Run all pre-commit hooks (ruff, mypy, trailing whitespace, etc.)
113
+ tox -e pre-commit
114
+
115
+ # Run pre-commit with specific hook
116
+ tox -e pre-commit -- --all-files ruff
117
+
118
+ # Run mypy type checking
119
+ tox -e mypy
120
+
121
+ # Run pre-commit hooks directly (if pre-commit is installed)
122
+ pre-commit run --all-files
123
+ ```
124
+
125
+ ## Code Style Guidelines
126
+
127
+ - **Formatter/Linter**: Ruff (configured in `pyproject.toml`)
128
+ - **Type Checking**: Mypy with strict settings (configured in `pyproject.toml`)
129
+ - **Pre-commit**: Use pre-commit hooks for consistent code style
130
+
131
+ ### Best Practices
132
+
133
+ - **Type annotations**: Use complete type annotations for all function signatures.
134
+ - **Docstrings**: Include docstrings for public functions and classes.
135
+ - **Pure functions**: Where possible, write pure functions without side effects.
136
+ - Follow existing naming conventions (`N802`, `N803`, `N806` are excluded to match markdown-it naming).
137
+
138
+ ## Testing Guidelines
139
+
140
+ - **Framework**: pytest with `pytest-regressions` for fixture-based tests
141
+ - **Fixture pattern**: Most plugins use `.md` fixture files containing input and expected HTML output, separated by `.` markers
142
+ - **Test location**: Each plugin has a corresponding test file or directory in `tests/`
143
+ - **Regression tests**: Use `pytest-regressions` for comparing rendered output against stored fixtures
144
+
145
+ ### Fixture File Format
146
+
147
+ Fixture files (`.md` files in test directories) use a specific format:
148
+
149
+ ```markdown
150
+ Plugin Name
151
+ .
152
+ markdown input
153
+ .
154
+ expected html output
155
+ .
156
+
157
+ Another Test Case
158
+ .
159
+ more markdown input
160
+ .
161
+ more expected html output
162
+ .
163
+ ```
164
+
165
+ ### Running Specific Tests
166
+
167
+ ```bash
168
+ # Test a single plugin
169
+ tox -e py310 -- tests/test_dollarmath.py
170
+
171
+ # Verbose output with full tracebacks
172
+ tox -e py310 -- -v --tb=long tests/test_footnote.py
173
+ ```
174
+
175
+ ## Architecture
176
+
177
+ ### Plugin Structure
178
+
179
+ Each plugin follows the markdown-it-py plugin pattern:
180
+
181
+ ```python
182
+ from markdown_it import MarkdownIt
183
+
184
+
185
+ def my_plugin(md: MarkdownIt, **options: ...) -> None:
186
+ """My custom plugin description."""
187
+ # Register block rules
188
+ md.block.ruler.before("fence", "my_block_rule", my_block_rule)
189
+
190
+ # Register inline rules
191
+ md.inline.ruler.after("emphasis", "my_inline_rule", my_inline_rule)
192
+
193
+ # Add render rules
194
+ md.add_render_rule("my_token_type", render_my_token)
195
+ ```
196
+
197
+ ### Plugin Types
198
+
199
+ 1. **Block plugins** (e.g., `amsmath`, `container`, `deflist`, `front_matter`): Parse block-level syntax using `StateBlock`
200
+ 2. **Inline plugins** (e.g., `dollarmath`, `myst_role`, `subscript`): Parse inline syntax using `StateInline`
201
+ 3. **Core plugins** (e.g., `footnote`, `wordcount`): Operate on the full token stream in a post-processing step
202
+ 4. **Single-file plugins** (e.g., `colon_fence.py`, `substitution.py`): Simple plugins in a single module
203
+ 5. **Multi-file plugins** (e.g., `footnote/`, `dollarmath/`): More complex plugins with `__init__.py` and `index.py`
204
+
205
+ ### Multi-file Plugin Layout
206
+
207
+ ```
208
+ plugin_name/
209
+ ├── __init__.py # Exports the plugin function (e.g., dollarmath_plugin)
210
+ └── index.py # Contains the implementation (rule functions, renderers)
211
+ ```
212
+
213
+ ### Token Flow
214
+
215
+ Plugins create and manipulate markdown-it tokens:
216
+
217
+ ```python
218
+ token = state.push("my_type_open", "div", 1) # Opening tag
219
+ token.attrSet("class", "my-class")
220
+ token.markup = "..."
221
+ token.map = [startLine, nextLine]
222
+
223
+ # Content tokens...
224
+
225
+ token = state.push("my_type_close", "div", -1) # Closing tag
226
+ ```
227
+
228
+ ## Commit Message Format
229
+
230
+ Use this format:
231
+
232
+ ```
233
+ <EMOJI> <KEYWORD>: Summarize in 72 chars or less (#<PR>)
234
+
235
+ Optional detailed explanation.
236
+ ```
237
+
238
+ Keywords:
239
+
240
+ - `✨ NEW:` – New feature
241
+ - `🐛 FIX:` – Bug fix
242
+ - `👌 IMPROVE:` – Improvement (no breaking changes)
243
+ - `‼️ BREAKING:` – Breaking change
244
+ - `📚 DOCS:` – Documentation
245
+ - `🔧 MAINTAIN:` – Maintenance changes only (typos, etc.)
246
+ - `🧪 TEST:` – Tests or CI changes only
247
+ - `♻️ REFACTOR:` – Refactoring
248
+
249
+ ## Pull Request Requirements
250
+
251
+ When submitting changes:
252
+
253
+ 1. **Description**: Include a meaningful description or link explaining the change
254
+ 2. **Tests**: Include test cases for new functionality or bug fixes
255
+ 3. **Documentation**: Update docs if behavior changes or new features are added
256
+ 4. **Changelog**: Update `CHANGELOG.md` under the appropriate section
257
+ 5. **Code Quality**: Ensure `pre-commit run --all-files` passes
258
+ 6. **Type Checking**: Ensure mypy passes with strict settings
259
+
260
+ ## Key Files
261
+
262
+ - `pyproject.toml` - Project configuration, dependencies, and tool settings (Ruff, Mypy)
263
+ - `tox.ini` - Test environment configuration
264
+ - `mdit_py_plugins/__init__.py` - Package version
265
+ - `mdit_py_plugins/utils.py` - Shared utilities (e.g., `is_code_block`)
266
+ - `CHANGELOG.md` - Change log for releases
267
+
268
+ ## Debugging
269
+
270
+ - Use `md.parse(text)` to inspect the token stream produced by plugins
271
+ - Use `md.render(text)` to see final HTML output
272
+ - Enable/disable specific plugins using `md.use()` / `md.disable()`
273
+ - Use `tox -- -v --tb=long` for verbose test output with full tracebacks
274
+
275
+ ### Debugging Tips
276
+
277
+ ```python
278
+ from markdown_it import MarkdownIt
279
+
280
+ from mdit_py_plugins.dollarmath import dollarmath_plugin
281
+
282
+ md = MarkdownIt().use(dollarmath_plugin)
283
+
284
+ # See the token stream
285
+ tokens = md.parse("Some $inline$ math and $$block$$ math")
286
+ for token in tokens:
287
+ print(f"{token.type} | {token.tag} | {token.content}")
288
+ if token.children:
289
+ for child in token.children:
290
+ print(f" {child.type} | {child.tag} | {child.content}")
291
+
292
+ # See rendered HTML
293
+ print(md.render("Some $inline$ math"))
294
+ ```
295
+
296
+ ## Common Patterns
297
+
298
+ ### Adding a New Plugin
299
+
300
+ 1. Create a new directory under `mdit_py_plugins/` (or a single `.py` file for simple plugins)
301
+ 2. Implement the plugin function following the standard pattern:
302
+ ```python
303
+ from markdown_it import MarkdownIt
304
+
305
+ def my_plugin(md: MarkdownIt) -> None:
306
+ md.block.ruler.before("fence", "my_rule", _my_rule)
307
+ md.add_render_rule("my_type", _render_my_type)
308
+ ```
309
+ 3. Export the plugin function from `__init__.py`
310
+ 4. Add test fixtures in `tests/test_my_plugin/`
311
+ 5. Add a test file `tests/test_my_plugin.py`
312
+ 6. Update `CHANGELOG.md`
313
+
314
+ ### Adding a Block Rule
315
+
316
+ Block rules receive `(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool`:
317
+
318
+ ```python
319
+ from markdown_it.rules_block import StateBlock
320
+
321
+ def my_block_rule(
322
+ state: StateBlock, startLine: int, endLine: int, silent: bool
323
+ ) -> bool:
324
+ # Check if this line matches your syntax
325
+ pos = state.bMarks[startLine] + state.tShift[startLine]
326
+ maximum = state.eMarks[startLine]
327
+
328
+ if state.src[pos] != "$":
329
+ return False
330
+
331
+ # In silent mode, just return True if matched (used for validation)
332
+ if silent:
333
+ return True
334
+
335
+ # Parse content and create tokens
336
+ token = state.push("my_type", "", 0)
337
+ token.content = ...
338
+ token.map = [startLine, nextLine]
339
+
340
+ state.line = nextLine
341
+ return True
342
+ ```
343
+
344
+ ### Adding an Inline Rule
345
+
346
+ Inline rules receive `(state: StateInline, silent: bool) -> bool`:
347
+
348
+ ```python
349
+ from markdown_it.rules_inline import StateInline
350
+
351
+ def my_inline_rule(state: StateInline, silent: bool) -> bool:
352
+ if state.src[state.pos] != "$":
353
+ return False
354
+
355
+ # Parse and validate
356
+ ...
357
+
358
+ if not silent:
359
+ token = state.push("my_type", "", 0)
360
+ token.content = content
361
+
362
+ state.pos = end
363
+ return True
364
+ ```
365
+
366
+ ## Reference Documentation
367
+
368
+ - [markdown-it-py Documentation](https://markdown-it-py.readthedocs.io/)
369
+ - [markdown-it-py Repository](https://github.com/executablebooks/markdown-it-py)
370
+ - [mdit-py-plugins Documentation](https://mdit-py-plugins.readthedocs.io/)
371
+ - [Original markdown-it (JavaScript)](https://github.com/markdown-it/markdown-it)
372
+ - [CommonMark Spec](https://spec.commonmark.org/)
373
+ - [MyST-Parser Repository](https://github.com/executablebooks/MyST-Parser)
@@ -1,5 +1,38 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.6.1 - 2026-05-13
4
+
5
+ - 🐛 FIX: Nested field lists incorrectly nesting inside parent containers (#139)
6
+
7
+ Field lists inside list items (or other indented containers) would recursively nest each field inside the previous one, instead of being siblings.
8
+
9
+ ## 0.6.0 - 2026-05-07
10
+
11
+ - ✨ NEW: Add GFM autolink and composite GFM plugins (#135)
12
+
13
+ The `gfm_autolink` plugin implements [GFM autolink literals](https://github.github.com/gfm/#autolinks-extension-),
14
+ auto-linking URLs and email addresses without requiring angle brackets:
15
+
16
+ ```markdown
17
+ Visit www.example.com or contact user@example.com
18
+ ```
19
+
20
+ The `gfm` composite plugin enables a full Github Flavored Markdown (GFM)-like configuration in a single call
21
+ (tables, strikethrough, autolinks, task lists, alerts, and footnotes).
22
+
23
+ See [the GitHub docs](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for more information.
24
+
25
+ **Requires markdown-it-py >= 4.1.0.**
26
+
27
+ - ✨ NEW: Add superscript plugin and tests, thanks to @elijahgreenstein (#128)
28
+
29
+ Ported from [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup).
30
+ Renders superscript text using `^..^` syntax:
31
+
32
+ ```markdown
33
+ 29^th^ of May
34
+ ```
35
+
3
36
  ## 0.5.0 - 2025-08-11
4
37
 
5
38
  - ⬆️ Drop Python 3.9, which is EoL next month <https://devguide.python.org/versions> and allow for the, soon to be released, markdown-it-py v4.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdit-py-plugins
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: Collection of plugins for markdown-it-py
5
5
  Keywords: markdown,markdown-it,lexer,parser,development
6
6
  Author-email: Chris Sewell <chrisj_sewell@hotmail.com>
@@ -28,6 +28,7 @@ Requires-Dist: coverage ; extra == "testing"
28
28
  Requires-Dist: pytest ; extra == "testing"
29
29
  Requires-Dist: pytest-cov ; extra == "testing"
30
30
  Requires-Dist: pytest-regressions ; extra == "testing"
31
+ Requires-Dist: pytest-timeout ; extra == "testing"
31
32
  Project-URL: Documentation, https://mdit-py-plugins.readthedocs.io
32
33
  Project-URL: Homepage, https://github.com/executablebooks/mdit-py-plugins
33
34
  Provides-Extra: code-style
@@ -0,0 +1 @@
1
+ __version__ = "0.6.1"
@@ -209,7 +209,7 @@ def _attr_block_rule(
209
209
  return False
210
210
 
211
211
  try:
212
- new_pos, attrs = parse(state.src[pos:maximum])
212
+ _new_pos, attrs = parse(state.src[pos:maximum])
213
213
  except ParseError:
214
214
  return False
215
215
 
@@ -78,7 +78,9 @@ def dollarmath_plugin(
78
78
  _label_renderer: Callable[[str], str]
79
79
  if label_renderer is None:
80
80
  _label_renderer = ( # noqa: E731
81
- lambda label: f'<a href="#{label}" class="mathlabel" title="Permalink to this equation">¶</a>'
81
+ lambda label: (
82
+ f'<a href="#{label}" class="mathlabel" title="Permalink to this equation">¶</a>'
83
+ )
82
84
  )
83
85
  else:
84
86
  _label_renderer = label_renderer
@@ -164,8 +164,9 @@ def _fieldlist_rule(
164
164
  while _line < endLine:
165
165
  # if start_of_content < end_of_content, then non-empty line
166
166
  if (state.bMarks[_line] + state.tShift[_line]) < state.eMarks[_line]:
167
- if state.tShift[_line] <= 0:
168
- # the line has no indent, so it's the end of the field
167
+ if state.tShift[_line] <= state.blkIndent:
168
+ # the line is not indented relative to the field marker,
169
+ # so it's the end of the field body
169
170
  break
170
171
  block_indent = (
171
172
  state.tShift[_line]
@@ -0,0 +1,102 @@
1
+ """Composite GFM (GitHub Flavored Markdown) plugin.
2
+
3
+ Enables a set of plugins that together approximate GitHub's Markdown rendering:
4
+
5
+ - Tables (built-in)
6
+ - Strikethrough with single and double tildes (built-in)
7
+ - Autolinks (gfm_autolink plugin)
8
+ - Task lists (built-in, markdown-it-py >= 4.1.0)
9
+ - Alerts (built-in, markdown-it-py >= 4.1.0)
10
+ - Footnotes (``[^label]`` references and definitions)
11
+
12
+ Optional extras:
13
+
14
+ - Dollar math (``$...$`` / ``$$...$$``)
15
+ - Front matter (YAML)
16
+
17
+ .. note::
18
+ Tag filtering (disallowed raw HTML tags) is not yet implemented.
19
+
20
+ .. seealso::
21
+ - `GitHub Flavored Markdown Spec <https://github.github.com/gfm/>`__
22
+ - `GitHub basic formatting syntax
23
+ <https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax>`__
24
+
25
+ .. versionadded:: 0.5.0
26
+
27
+ Requires markdown-it-py >= 4.1.0.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from functools import lru_cache
33
+
34
+ from markdown_it import MarkdownIt
35
+ from markdown_it import __version__ as _mdit_version
36
+
37
+ from mdit_py_plugins.dollarmath import dollarmath_plugin
38
+ from mdit_py_plugins.footnote import footnote_plugin
39
+ from mdit_py_plugins.front_matter import front_matter_plugin
40
+ from mdit_py_plugins.gfm_autolink import gfm_autolink_plugin
41
+
42
+ __all__ = ("gfm_plugin",)
43
+
44
+ _MIN_VERSION = (4, 1, 0)
45
+
46
+
47
+ @lru_cache(maxsize=8)
48
+ def _parse_version(v: str) -> tuple[int, ...]:
49
+ """Parse a version string like '4.1.0' into a tuple of ints."""
50
+ return tuple(int(x) for x in v.split(".")[:3])
51
+
52
+
53
+ def gfm_plugin(
54
+ md: MarkdownIt,
55
+ *,
56
+ dollarmath: bool = False,
57
+ front_matter: bool = False,
58
+ tasklists_editable: bool = False,
59
+ ) -> None:
60
+ """Enable GFM-like rendering.
61
+
62
+ Starts from the current parser configuration and enables the GFM
63
+ components on top.
64
+
65
+ :param dollarmath: Enable dollar-delimited math (``$...$``, ``$$...$$``).
66
+ :param front_matter: Enable YAML front matter (``---``).
67
+ :param tasklists_editable: If True, rendered task list checkboxes are not
68
+ disabled (i.e. they are interactive).
69
+ """
70
+ if _parse_version(_mdit_version) < _MIN_VERSION:
71
+ raise RuntimeError(
72
+ f"gfm_plugin requires markdown-it-py >= {'.'.join(str(x) for x in _MIN_VERSION)} "
73
+ f"(installed: {_mdit_version})"
74
+ )
75
+
76
+ # Enable table and strikethrough rules (built into markdown-it-py)
77
+ md.enable("table")
78
+ md.enable("strikethrough")
79
+
80
+ # GFM options available in markdown-it-py >= 4.1.0
81
+ md.options["tasklists"] = True
82
+ md.options["tasklists_editable"] = tasklists_editable
83
+ md.options["alerts"] = True
84
+ md.options["strikethrough_single_tilde"] = True
85
+ # GFM autolinks
86
+ md.use(gfm_autolink_plugin)
87
+
88
+ # Footnotes (inline footnotes ^[...] are not part of GFM)
89
+ md.use(footnote_plugin, inline=False)
90
+
91
+ # Dollar math (inline $...$ and block $$...$$)
92
+ if dollarmath:
93
+ md.use(dollarmath_plugin, allow_blank_lines=False)
94
+
95
+ # TODO: Tag filter — replace leading `<` with `&lt;` for disallowed raw
96
+ # HTML tags: <title>, <textarea>, <style>, <xmp>, <iframe>, <noembed>,
97
+ # <noframes>, <script>, <plaintext>.
98
+ # See https://github.github.com/gfm/#disallowed-raw-html-extension-
99
+
100
+ # Optional plugins
101
+ if front_matter:
102
+ md.use(front_matter_plugin)
@@ -0,0 +1,15 @@
1
+ """GFM autolink extension plugin for markdown-it-py.
2
+
3
+ Implements the `GFM autolink extension
4
+ <https://github.github.com/gfm/#autolinks-extension->`_,
5
+ which recognises bare URLs (``http://``, ``https://``, ``www.``),
6
+ protocol links (``mailto:``, ``xmpp:``),
7
+ and bare email addresses without requiring angle brackets.
8
+
9
+ Ported from the Rust crate
10
+ `markdown_it_autolink <https://github.com/markdown-it-rust/markdown-it-plugins.rs>`_.
11
+ """
12
+
13
+ from .index import gfm_autolink_plugin
14
+
15
+ __all__ = ("gfm_autolink_plugin",)