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.
- mdit_py_plugins-0.6.1/.github/workflows/copilot-setup-steps.yml +43 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.pre-commit-config.yaml +2 -2
- mdit_py_plugins-0.6.1/AGENTS.md +373 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/CHANGELOG.md +33 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/PKG-INFO +2 -1
- mdit_py_plugins-0.6.1/mdit_py_plugins/__init__.py +1 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/attrs/index.py +1 -1
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/dollarmath/index.py +3 -1
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/field_list/__init__.py +3 -2
- mdit_py_plugins-0.6.1/mdit_py_plugins/gfm/__init__.py +102 -0
- mdit_py_plugins-0.6.1/mdit_py_plugins/gfm_autolink/__init__.py +15 -0
- mdit_py_plugins-0.6.1/mdit_py_plugins/gfm_autolink/_match.py +250 -0
- mdit_py_plugins-0.6.1/mdit_py_plugins/gfm_autolink/index.py +295 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/subscript/__init__.py +2 -4
- mdit_py_plugins-0.6.1/mdit_py_plugins/superscript/__init__.py +5 -0
- mdit_py_plugins-0.6.1/mdit_py_plugins/superscript/index.py +110 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/utils.py +7 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/pyproject.toml +4 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/tox.ini +9 -0
- mdit_py_plugins-0.5.0/mdit_py_plugins/__init__.py +0 -1
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.coveragerc +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.github/dependabot.yml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.github/workflows/tests.yml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.gitignore +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/.readthedocs.yml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/README.md +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/codecov.yml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/admon/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/amsmath/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/anchors/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/anchors/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/attrs/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/attrs/parse.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/colon_fence.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/README.md +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/container/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/README.md +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/deflist/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/dollarmath/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/footnote/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/front_matter/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_blocks/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_blocks/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_role/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/myst_role/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/py.typed +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/subscript/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/substitution.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/tasklists/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/tasklists/port.yaml +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/LICENSE +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/README.md +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/__init__.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/index.py +0 -0
- {mdit_py_plugins-0.5.0 → mdit_py_plugins-0.6.1}/mdit_py_plugins/texmath/port.yaml +0 -0
- {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
|
|
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.
|
|
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.
|
|
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"
|
|
@@ -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:
|
|
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] <=
|
|
168
|
-
# the line
|
|
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 `<` 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",)
|