pax_global_header 0000666 0000000 0000000 00000000064 14437433217 0014522 g ustar 00root root 0000000 0000000 52 comment=44c8e1c2d1e12f721f2c5c195b9fed3bab2cd1c3
mdit-py-plugins-0.4.0/ 0000775 0000000 0000000 00000000000 14437433217 0014565 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/.github/ 0000775 0000000 0000000 00000000000 14437433217 0016125 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/.github/dependabot.yml 0000664 0000000 0000000 00000001126 14437433217 0020755 0 ustar 00root root 0000000 0000000 # To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: github-actions
directory: /
commit-message:
prefix: ⬆️
schedule:
interval: weekly
- package-ecosystem: pip
directory: /
commit-message:
prefix: ⬆️
schedule:
interval: weekly
mdit-py-plugins-0.4.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14437433217 0020162 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/.github/workflows/tests.yml 0000664 0000000 0000000 00000004020 14437433217 0022043 0 ustar 00root root 0000000 0000000 # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: continuous-integration
on:
push:
branches: [master]
tags:
- 'v*'
pull_request:
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:
python-version: 3.8
- uses: pre-commit/action@v3.0.0
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['pypy-3.8', '3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[testing]
- name: Run pytest
run: |
pytest --cov=mdit_py_plugins --cov-report=xml --cov-report=term-missing
- name: Upload to Codecov
uses: codecov/codecov-action@v3
with:
name: mdit-py-plugins-pytests
flags: pytests
file: ./coverage.xml
fail_ci_if_error: true
allgood:
runs-on: ubuntu-latest
needs:
- pre-commit
- tests
steps:
- run: echo "Great success!"
publish:
name: Publish to PyPi
needs: [pre-commit, tests]
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.8"
- name: install flit
run: |
pip install flit~=3.4
- name: Build and publish
run: |
flit publish
env:
FLIT_USERNAME: __token__
FLIT_PASSWORD: ${{ secrets.PYPI_KEY }}
mdit-py-plugins-0.4.0/.gitignore 0000664 0000000 0000000 00000003612 14437433217 0016557 0 ustar 00root root 0000000 0000000 # Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
benchmark/extra/
node_modules/
coverage/
demo/
apidoc/
*.log
__pycache__/
.ropeproject/
*.egg-info/
.vscode/
.DS_Store
docs/api/
mdit-py-plugins-0.4.0/.pre-commit-config.yaml 0000664 0000000 0000000 00000001453 14437433217 0021051 0 ustar 00root root 0000000 0000000 # Install pre-commit hooks via
# pre-commit install
exclude: >
(?x)^(
\.vscode/settings\.json|
test.*\.md|
test.*\.txt|
test.*\.html|
)$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-json
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.270
hooks:
- id: ruff
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.3.0
hooks:
- id: mypy
additional_dependencies: [markdown-it-py~=3.0]
exclude: ^tests/
mdit-py-plugins-0.4.0/.readthedocs.yml 0000664 0000000 0000000 00000000233 14437433217 0017651 0 ustar 00root root 0000000 0000000 version: 2
python:
version: "3.8"
install:
- method: pip
path: .
extra_requirements: [rtd]
sphinx:
builder: html
fail_on_warning: true
mdit-py-plugins-0.4.0/CHANGELOG.md 0000664 0000000 0000000 00000011522 14437433217 0016377 0 ustar 00root root 0000000 0000000 # Change Log
## 0.4.0 - 2023-06-05
* ⬆️ UPGRADE: Drop python 3.7 and support 3.11 ([#77](https://github.com/executablebooks/mdit-py-plugins/pull/77))
* ⬆️ UPGRADE: Allow markdown-it-py v3 ([#85](https://github.com/executablebooks/mdit-py-plugins/pull/85))
* 👌 Make field_list compatible with latest upstream ([#75](https://github.com/executablebooks/mdit-py-plugins/pull/75))
* 🔧 Convert `state.srcCharCode` -> `state.src` ([#84](https://github.com/executablebooks/mdit-py-plugins/pull/84))
* 🔧 Remove unnecessary method arg by @chrisjsewell in( [#76](https://github.com/executablebooks/mdit-py-plugins/pull/76))
* 👌 Centralise code block test ([#83](https://github.com/executablebooks/mdit-py-plugins/pull/83) and [#87](https://github.com/executablebooks/mdit-py-plugins/pull/87))
* This means that disabling the `code` block rule in markdown-it-py v3+ will now allow all syntax blocks to be indented by any amount of whitespace.
* 👌 Improve `dollarmath` plugin: Add `allow_blank_lines` option, thanks to [@eric-wieser](https://github.com/eric-wieser) ([#46](https://github.com/executablebooks/mdit-py-plugins/pull/46))
* 👌 Improve `admon` plugin: Add `???` support, thanks to [@KyleKing](https://github.com/KyleKing) ([#58](https://github.com/executablebooks/mdit-py-plugins/pull/58))
* 🔧 MAINTAIN: Make type checking strict ([#86](https://github.com/executablebooks/mdit-py-plugins/pull/86))
**Full Changelog**:
## 0.3.5 - 2023-03-02
- 🐛 FIX: Regression in dollarmath by @chrisjsewell in [#69](https://github.com/executablebooks/mdit-py-plugins/pull/69)
- 🐛 Fix regression in amsmath by @chrisjsewell in [#70](https://github.com/executablebooks/mdit-py-plugins/pull/70)
- 🔧 Correct project documentation link by @andersk in [#73](https://github.com/executablebooks/mdit-py-plugins/pull/73)
## 0.3.4 - 2023-02-18
- ✨ NEW: Add attrs_block_plugin by @chrisjsewell in [#66](https://github.com/executablebooks/mdit-py-plugins/pull/66)
- 👌 Improve field lists by @chrisjsewell in [#65](https://github.com/executablebooks/mdit-py-plugins/pull/65)
- 🔧 Update pre-commit by @chrisjsewell in [#64](https://github.com/executablebooks/mdit-py-plugins/pull/64) (moving from flake8 to ruff)
**Full Changelog**: [v0.3.3...v0.3.](https://github.com/executablebooks/mdit-py-plugins/compare/v0.3.3...v0.3.4)
## 0.3.3 - 2022-12-06
🐛 FIX: span with end of inline before attrs
## 0.3.2 - 2022-12-05
- ✨ NEW: Port `admon` plugin by @KyleKing ([#53](https://github.com/executablebooks/mdit-py-plugins/pull/53))
- ✨ NEW: Add span parsing to inline attributes plugin by @chrisjsewell ([#55](https://github.com/executablebooks/mdit-py-plugins/pull/55))
- 🐛 FIX: Task list item marker can be followed by any GFM whitespace by @hukkin in ([#42](https://github.com/executablebooks/mdit-py-plugins/pull/42))
**Full Changelog**: [v0.3.1...v0.4.0](https://github.com/executablebooks/mdit-py-plugins/compare/v0.3.1...v0.4.0)
## 0.3.1 - 2022-09-27
- ⬆️ UPGRADE: Drop Python 3.6, support Python 3.10
- 🐛 FIX: Parsing when newline is between footnote ID and first paragraph
- 🐛 FIX: Anchor ids in separate renders no longer affect each other.
- ✨ NEW: Add `attrs_plugin`
- 🔧 MAINTAIN: Use flit PEP 621 package build
## 0.3.0 - 2021-12-03
- ⬆️ UPGRADE: Compatible with markdown-it-py `v2`.
- ✨ NEW: Add field list plugin, Based on the [restructuredtext syntax](https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#field-lists)
- ♻️ REFACTOR: dollarmath plugin, `math_block_eqno` -> `math_block_label` token
- ♻️ REFACTOR: Remove AttrDict usage from texmath
- 👌 IMPROVE: Default HTML rendering for dollarmath and amsmath plugins
- 👌 IMPROVE: Add render options for dollarmath and amsmath plugins
- 👌 IMPROVE: MyST parsing of target blocks (allow whitespace) and roles (allow for new lines)
## 0.2.8 - 2021-05-03
🐛 FIX: `wordcount` update of minutes
## 0.2.7 - 2021-05-03
- ⬆️ UPDATE: markdown-it-py~=1.0
- ✨ NEW: Add `wordcount_plugin`
- 👌 IMPROVE: `dollarmath`: Allow inline double-dollar
- 👌 IMPROVE: `myst_blocks`: Parse multiline comments
- 👌 IMPROVE: Replace use of `env` as an `AttrDict`
- 🐛 FIX: `front_matter`: don't duplicate content storage in `Token.meta`
## 0.2.6 - 2021-03-17
👌 IMPROVE: Remove direct use of `Token.attrs`
## 0.2.5 - 2021-02-06
🐛 FIX: front-matter: `IndexError` if first line is single dash
## 0.2.2 - 2020-12-16
✨ NEW: Add substitution_plugin
(improvements in 0.2.3 and 0.2.4)
## 0.2.0 - 2020-12-14
Add mypy type-checking, code taken from: https://github.com/executablebooks/markdown-it-py/commit/2eb1fe6b47cc0ad4ebe954cabd91fb8e52a2f03d
## 0.1.0 - 2020-12-14
First release, code taken from: https://github.com/executablebooks/markdown-it-py/commit/3a5bdcc98e67de9df26ebb8bc7cd0221a0d6b51b
mdit-py-plugins-0.4.0/LICENSE 0000664 0000000 0000000 00000002066 14437433217 0015576 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2020 ExecutableBookProject
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
mdit-py-plugins-0.4.0/README.md 0000664 0000000 0000000 00000002262 14437433217 0016046 0 ustar 00root root 0000000 0000000 # mdit-py-plugins
[![Github-CI][github-ci]][github-link]
[![Coverage Status][codecov-badge]][codecov-link]
[![PyPI][pypi-badge]][pypi-link]
[![Conda][conda-badge]][conda-link]
[![Code style: black][black-badge]][black-link]
Collection of core plugins for [markdown-it-py](https://github.com/executablebooks/markdown-it-py).
[github-ci]: https://github.com/executablebooks/mdit-py-plugins/workflows/continuous-integration/badge.svg
[github-link]: https://github.com/executablebooks/mdit-py-plugins
[pypi-badge]: https://img.shields.io/pypi/v/mdit-py-plugins.svg
[pypi-link]: https://pypi.org/project/mdit-py-plugins
[conda-badge]: https://anaconda.org/conda-forge/mdit-py-plugins/badges/version.svg
[conda-link]: https://anaconda.org/conda-forge/mdit-py-plugins
[codecov-badge]: https://codecov.io/gh/executablebooks/mdit-py-plugins/branch/master/graph/badge.svg
[codecov-link]: https://codecov.io/gh/executablebooks/mdit-py-plugins
[black-badge]: https://img.shields.io/badge/code%20style-black-000000.svg
[black-link]: https://github.com/ambv/black
[install-badge]: https://img.shields.io/pypi/dw/mdit-py-plugins?label=pypi%20installs
[install-link]: https://pypistats.org/packages/mdit-py-plugins
mdit-py-plugins-0.4.0/codecov.yml 0000664 0000000 0000000 00000000242 14437433217 0016730 0 ustar 00root root 0000000 0000000 coverage:
status:
project:
default:
target: 92%
threshold: 0.2%
patch:
default:
target: 80%
threshold: 0.2%
mdit-py-plugins-0.4.0/docs/ 0000775 0000000 0000000 00000000000 14437433217 0015515 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/docs/conf.py 0000664 0000000 0000000 00000001327 14437433217 0017017 0 ustar 00root root 0000000 0000000 project = "mdit-py-plugins"
copyright = "2020, Executable Book Project"
author = "Executable Book Project"
master_doc = "index"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
"sphinx.ext.intersphinx",
"myst_parser",
]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
intersphinx_mapping = {
"python": ("https://docs.python.org/3.8", None),
"markdown_it": ("https://markdown-it-py.readthedocs.io/en/latest", None),
}
html_title = "mdit-py-plugins"
html_theme = "sphinx_book_theme"
html_theme_options = {
"use_edit_page_button": True,
"repository_url": "https://github.com/executablebooks/mdit-py-plugins",
"repository_branch": "master",
"path_to_docs": "docs",
}
mdit-py-plugins-0.4.0/docs/index.md 0000664 0000000 0000000 00000006274 14437433217 0017157 0 ustar 00root root 0000000 0000000 # Markdown-It-Py Plugin Extensions
## Built-in
The following plugins are embedded within the core package:
- [tables](https://help.github.com/articles/organizing-information-with-tables/) (GFM)
- [strikethrough](https://help.github.com/articles/basic-writing-and-formatting-syntax/#styling-text) (GFM)
These can be enabled individually:
```python
from markdown_it import MarkdownIt
md = MarkdownIt("commonmark").enable('table')
```
or as part of a configuration:
```python
from markdown_it import MarkdownIt
md = MarkdownIt("gfm-like")
```
```{seealso}
See
```
## mdit-py-plugins package
The [`mdit_py_plugins`](https://github.com/executablebooks/mdit-py-plugins), contains a number of common plugins.
They can be chained and loaded *via*:
```python
from markdown_it import MarkdownIt
from mdit_py_plugins import plugin1, plugin2
md = MarkdownIt().use(plugin1, keyword=value).use(plugin2, keyword=value)
html_string = md.render("some *Markdown*")
```
## Front-Matter
```{eval-rst}
.. autofunction:: mdit_py_plugins.front_matter.front_matter_plugin
```
## Footnotes
```{eval-rst}
.. autofunction:: mdit_py_plugins.footnote.footnote_plugin
```
## Definition Lists
```{eval-rst}
.. autofunction:: mdit_py_plugins.deflist.deflist_plugin
```
## Task lists
```{eval-rst}
.. autofunction:: mdit_py_plugins.tasklists.tasklists_plugin
```
## Field Lists
```{eval-rst}
.. autofunction:: mdit_py_plugins.field_list.fieldlist_plugin
```
## Heading Anchors
```{eval-rst}
.. autofunction:: mdit_py_plugins.anchors.anchors_plugin
```
## Word Count
```{eval-rst}
.. autofunction:: mdit_py_plugins.wordcount.wordcount_plugin
```
## Containers
```{eval-rst}
.. autofunction:: mdit_py_plugins.container.container_plugin
```
```{eval-rst}
.. autofunction:: mdit_py_plugins.admon.admon_plugin
```
## Attributes
```{eval-rst}
.. autofunction:: mdit_py_plugins.attrs.attrs_plugin
```
```{eval-rst}
.. autofunction:: mdit_py_plugins.attrs.attrs_block_plugin
```
## Math
```{eval-rst}
.. autofunction:: mdit_py_plugins.texmath.texmath_plugin
```
```{eval-rst}
.. autofunction:: mdit_py_plugins.dollarmath.dollarmath_plugin
```
```{eval-rst}
.. autofunction:: mdit_py_plugins.amsmath.amsmath_plugin
```
## MyST plugins
`myst_blocks` and `myst_role` plugins are also available, for utilisation by the [MyST renderer](https://myst-parser.readthedocs.io/en/latest/using/syntax.html)
```{eval-rst}
.. autofunction:: mdit_py_plugins.myst_role.myst_role_plugin
.. autofunction:: mdit_py_plugins.myst_blocks.myst_block_plugin
```
## Write your own
Use the `mdit_py_plugins` as a guide to write your own, following the [markdown-it design principles](inv:markdown_it#architecture).
There are many other plugins which could easily be ported from the JS versions (and hopefully will):
- [subscript](https://github.com/markdown-it/markdown-it-sub)
- [superscript](https://github.com/markdown-it/markdown-it-sup)
- [abbreviation](https://github.com/markdown-it/markdown-it-abbr)
- [emoji](https://github.com/markdown-it/markdown-it-emoji)
- [insert](https://github.com/markdown-it/markdown-it-ins)
- [mark](https://github.com/markdown-it/markdown-it-mark)
- ... and [others](https://www.npmjs.org/browse/keyword/markdown-it-plugin)
mdit-py-plugins-0.4.0/mdit_py_plugins/ 0000775 0000000 0000000 00000000000 14437433217 0017773 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/__init__.py 0000664 0000000 0000000 00000000026 14437433217 0022102 0 ustar 00root root 0000000 0000000 __version__ = "0.4.0"
mdit-py-plugins-0.4.0/mdit_py_plugins/admon/ 0000775 0000000 0000000 00000000000 14437433217 0021071 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/admon/LICENSE 0000664 0000000 0000000 00000002151 14437433217 0022075 0 ustar 00root root 0000000 0000000 Copyright (c) 2015 Vitaly Puzrin, Alex Kocharin.
Copyright (c) 2018 jebbs
Copyright (c) 2021- commenthol
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mdit-py-plugins-0.4.0/mdit_py_plugins/admon/__init__.py 0000664 0000000 0000000 00000000056 14437433217 0023203 0 ustar 00root root 0000000 0000000 from .index import admon_plugin # noqa: F401
mdit-py-plugins-0.4.0/mdit_py_plugins/admon/index.py 0000664 0000000 0000000 00000014064 14437433217 0022557 0 ustar 00root root 0000000 0000000 # Process admonitions and pass to cb.
from __future__ import annotations
from typing import TYPE_CHECKING, Callable, Sequence
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
def _get_tag(params: str) -> tuple[str, str]:
"""Separate the tag name from the admonition title."""
if not params.strip():
return "", ""
tag, *_title = params.strip().split(" ")
joined = " ".join(_title)
title = ""
if not joined:
title = tag.title()
elif joined != '""':
title = joined
return tag.lower(), title
def _validate(params: str) -> bool:
"""Validate the presence of the tag name after the marker."""
tag = params.strip().split(" ", 1)[-1] or ""
return bool(tag)
MARKER_LEN = 3 # Regardless of extra characters, block indent stays the same
MARKERS = ("!!!", "???", "???+")
MARKER_CHARS = {_m[0] for _m in MARKERS}
MAX_MARKER_LEN = max(len(_m) for _m in MARKERS)
def _extra_classes(markup: str) -> list[str]:
"""Return the list of additional classes based on the markup."""
if markup.startswith("?"):
if markup.endswith("+"):
return ["is-collapsible collapsible-open"]
return ["is-collapsible collapsible-closed"]
return []
def admonition(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
if is_code_block(state, startLine):
return False
start = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
# Check out the first character quickly, which should filter out most of non-containers
if state.src[start] not in MARKER_CHARS:
return False
# Check out the rest of the marker string
marker = ""
marker_len = MAX_MARKER_LEN
while marker_len > 0:
marker_pos = start + marker_len
markup = state.src[start:marker_pos]
if markup in MARKERS:
marker = markup
break
marker_len -= 1
else:
return False
params = state.src[marker_pos:maximum]
if not _validate(params):
return False
# Since start is found, we can report success here in validation mode
if silent:
return True
old_parent = state.parentType
old_line_max = state.lineMax
old_indent = state.blkIndent
blk_start = marker_pos
while blk_start < maximum and state.src[blk_start] == " ":
blk_start += 1
state.parentType = "admonition"
# Correct block indentation when extra marker characters are present
marker_alignment_correction = MARKER_LEN - len(marker)
state.blkIndent += blk_start - start + marker_alignment_correction
was_empty = False
# Search for the end of the block
next_line = startLine
while True:
next_line += 1
if next_line >= endLine:
# unclosed block should be autoclosed by end of document.
# also block seems to be autoclosed by end of parent
break
pos = state.bMarks[next_line] + state.tShift[next_line]
maximum = state.eMarks[next_line]
is_empty = state.sCount[next_line] < state.blkIndent
# two consecutive empty lines autoclose the block
if is_empty and was_empty:
break
was_empty = is_empty
if pos < maximum and state.sCount[next_line] < state.blkIndent:
# non-empty line with negative indent should stop the block:
# - !!!
# test
break
# this will prevent lazy continuations from ever going past our end marker
state.lineMax = next_line
tag, title = _get_tag(params)
token = state.push("admonition_open", "div", 1)
token.markup = markup
token.block = True
token.attrs = {"class": " ".join(["admonition", tag, *_extra_classes(markup)])}
token.meta = {"tag": tag}
token.content = title
token.info = params
token.map = [startLine, next_line]
if title:
title_markup = f"{markup} {tag}"
token = state.push("admonition_title_open", "p", 1)
token.markup = title_markup
token.attrs = {"class": "admonition-title"}
token.map = [startLine, startLine + 1]
token = state.push("inline", "", 0)
token.content = title
token.map = [startLine, startLine + 1]
token.children = []
token = state.push("admonition_title_close", "p", -1)
state.md.block.tokenize(state, startLine + 1, next_line)
token = state.push("admonition_close", "div", -1)
token.markup = markup
token.block = True
state.parentType = old_parent
state.lineMax = old_line_max
state.blkIndent = old_indent
state.line = next_line
return True
def admon_plugin(md: MarkdownIt, render: None | Callable[..., str] = None) -> None:
"""Plugin to use
`python-markdown style admonitions
`_.
.. code-block:: md
!!! note
*content*
`And mkdocs-style collapsible blocks
`_.
.. code-block:: md
???+ note
*content*
Note, this is ported from
`markdown-it-admon
`_.
"""
def renderDefault(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
_options: OptionsDict,
env: EnvType,
) -> str:
return self.renderToken(tokens, idx, _options, env) # type: ignore
render = render or renderDefault
md.add_render_rule("admonition_open", render)
md.add_render_rule("admonition_close", render)
md.add_render_rule("admonition_title_open", render)
md.add_render_rule("admonition_title_close", render)
md.block.ruler.before(
"fence",
"admonition",
admonition,
{"alt": ["paragraph", "reference", "blockquote", "list"]},
)
mdit-py-plugins-0.4.0/mdit_py_plugins/admon/port.yaml 0000664 0000000 0000000 00000000165 14437433217 0022743 0 ustar 00root root 0000000 0000000 - package: markdown-it-admon
commit: 9820ba89415c464a3cc18a780f222a0ceb3e18bd
date: Jul 3, 2021
version: 1.0.0
mdit-py-plugins-0.4.0/mdit_py_plugins/amsmath/ 0000775 0000000 0000000 00000000000 14437433217 0021425 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/amsmath/__init__.py 0000664 0000000 0000000 00000010754 14437433217 0023545 0 ustar 00root root 0000000 0000000 """An extension to capture amsmath latex environments."""
from __future__ import annotations
import re
from typing import TYPE_CHECKING, Callable, Optional, Sequence
from markdown_it import MarkdownIt
from markdown_it.common.utils import escapeHtml
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
# Taken from amsmath version 2.1
# http://anorien.csc.warwick.ac.uk/mirrors/CTAN/macros/latex/required/amsmath/amsldoc.pdf
ENVIRONMENTS = [
# 3.2 single equation with an automatically gen-erated number
"equation",
# 3.3 variation equation, used for equations that dont fit on a single line
"multline",
# 3.5 a group of consecutive equations when there is no alignment desired among them
"gather",
# 3.6 Used for two or more equations when vertical alignment is desired
"align",
# allows the horizontal space between equationsto be explicitly specified.
"alignat",
# stretches the space betweenthe equation columns to the maximum possible width
"flalign",
# 4.1 The pmatrix, bmatrix, Bmatrix, vmatrix and Vmatrix have (respectively)
# (),[],{},||,and ‖‖ delimiters built in.
"matrix",
"pmatrix",
"bmatrix",
"Bmatrix",
"vmatrix",
"Vmatrix",
# eqnarray is another math environment, it is not part of amsmath,
# and note that it is better to use align or equation+split instead
"eqnarray",
]
# other "non-top-level" environments:
# 3.4 the split environment is for single equations that are too long to fit on one line
# and hence must be split into multiple lines,
# it is intended for use only inside some other displayed equation structure,
# usually an equation, align, or gather environment
# 3.7 variants gathered, aligned,and alignedat are provided
# whose total width is the actual width of the contents;
# thus they can be used as a component in a containing expression
RE_OPEN = re.compile(r"\\begin\{(" + "|".join(ENVIRONMENTS) + r")([\*]?)\}")
def amsmath_plugin(
md: MarkdownIt, *, renderer: Optional[Callable[[str], str]] = None
) -> None:
"""Parses TeX math equations, without any surrounding delimiters,
only for top-level `amsmath `__ environments:
.. code-block:: latex
\\begin{gather*}
a_1=b_1+c_1\\\\
a_2=b_2+c_2-d_2+e_2
\\end{gather*}
:param renderer: Function to render content, by default escapes HTML
"""
md.block.ruler.before(
"blockquote",
"amsmath",
amsmath_block,
{"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]},
)
_renderer = (lambda content: escapeHtml(content)) if renderer is None else renderer
def render_amsmath_block(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
content = _renderer(str(tokens[idx].content))
return f'
\n{content}\n
\n'
md.add_render_rule("amsmath", render_amsmath_block)
def match_environment(string: str) -> None | tuple[str, str, int]:
match_open = RE_OPEN.match(string)
if not match_open:
return None
environment = match_open.group(1)
numbered = match_open.group(2)
match_close = re.search(
r"\\end\{" + environment + numbered.replace("*", r"\*") + "\\}", string
)
if not match_close:
return None
return (environment, numbered, match_close.end())
def amsmath_block(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
if is_code_block(state, startLine):
return False
begin = state.bMarks[startLine] + state.tShift[startLine]
outcome = match_environment(state.src[begin:])
if not outcome:
return False
environment, numbered, endpos = outcome
endpos += begin
line = startLine
while line < endLine:
if endpos >= state.bMarks[line] and endpos <= state.eMarks[line]:
# line for end of block math found ...
state.line = line + 1
break
line += 1
if not silent:
token = state.push("amsmath", "math", 0)
token.block = True
token.content = state.src[begin:endpos]
token.meta = {"environment": environment, "numbered": numbered}
token.map = [startLine, line]
return True
mdit-py-plugins-0.4.0/mdit_py_plugins/anchors/ 0000775 0000000 0000000 00000000000 14437433217 0021430 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/anchors/__init__.py 0000664 0000000 0000000 00000000057 14437433217 0023543 0 ustar 00root root 0000000 0000000 from .index import anchors_plugin # noqa F401
mdit-py-plugins-0.4.0/mdit_py_plugins/anchors/index.py 0000664 0000000 0000000 00000010022 14437433217 0023104 0 ustar 00root root 0000000 0000000 import re
from typing import Callable, List, Optional, Set
from markdown_it import MarkdownIt
from markdown_it.rules_core import StateCore
from markdown_it.token import Token
def anchors_plugin(
md: MarkdownIt,
min_level: int = 1,
max_level: int = 2,
slug_func: Optional[Callable[[str], str]] = None,
permalink: bool = False,
permalinkSymbol: str = "¶",
permalinkBefore: bool = False,
permalinkSpace: bool = True,
) -> None:
"""Plugin for adding header anchors, based on
`markdown-it-anchor `__
.. code-block:: md
# Title String
renders as:
.. code-block:: html
:param min_level: minimum header level to apply anchors
:param max_level: maximum header level to apply anchors
:param slug_func: function to convert title text to id slug.
:param permalink: Add a permalink next to the title
:param permalinkSymbol: the symbol to show
:param permalinkBefore: Add the permalink before the title, otherwise after
:param permalinkSpace: Add a space between the permalink and the title
Note, the default slug function aims to mimic the GitHub Markdown format, see:
- https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/toc_filter.rb
- https://gist.github.com/asabaylus/3071099
"""
selected_levels = list(range(min_level, max_level + 1))
md.core.ruler.push(
"anchor",
_make_anchors_func(
selected_levels,
slug_func or slugify,
permalink,
permalinkSymbol,
permalinkBefore,
permalinkSpace,
),
)
def _make_anchors_func(
selected_levels: List[int],
slug_func: Callable[[str], str],
permalink: bool,
permalinkSymbol: str,
permalinkBefore: bool,
permalinkSpace: bool,
) -> Callable[[StateCore], None]:
def _anchor_func(state: StateCore) -> None:
slugs: Set[str] = set()
for idx, token in enumerate(state.tokens):
if token.type != "heading_open":
continue
level = int(token.tag[1])
if level not in selected_levels:
continue
inline_token = state.tokens[idx + 1]
assert inline_token.children is not None
title = "".join(
child.content
for child in inline_token.children
if child.type in ["text", "code_inline"]
)
slug = unique_slug(slug_func(title), slugs)
token.attrSet("id", slug)
if permalink:
link_open = Token(
"link_open",
"a",
1,
)
link_open.attrSet("class", "header-anchor")
link_open.attrSet("href", f"#{slug}")
link_tokens = [
link_open,
Token("html_block", "", 0, content=permalinkSymbol),
Token("link_close", "a", -1),
]
if permalinkBefore:
inline_token.children = (
link_tokens
+ (
[Token("text", "", 0, content=" ")]
if permalinkSpace
else []
)
+ inline_token.children
)
else:
inline_token.children.extend(
([Token("text", "", 0, content=" ")] if permalinkSpace else [])
+ link_tokens
)
return _anchor_func
def slugify(title: str) -> str:
return re.sub(r"[^\w\u4e00-\u9fff\- ]", "", title.strip().lower().replace(" ", "-"))
def unique_slug(slug: str, slugs: Set[str]) -> str:
uniq = slug
i = 1
while uniq in slugs:
uniq = f"{slug}-{i}"
i += 1
slugs.add(uniq)
return uniq
mdit-py-plugins-0.4.0/mdit_py_plugins/attrs/ 0000775 0000000 0000000 00000000000 14437433217 0021130 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/attrs/__init__.py 0000664 0000000 0000000 00000000102 14437433217 0023232 0 ustar 00root root 0000000 0000000 from .index import attrs_block_plugin, attrs_plugin # noqa: F401
mdit-py-plugins-0.4.0/mdit_py_plugins/attrs/index.py 0000664 0000000 0000000 00000016711 14437433217 0022617 0 ustar 00root root 0000000 0000000 from typing import List, Optional, Sequence
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from markdown_it.rules_core import StateCore
from markdown_it.rules_inline import StateInline
from markdown_it.token import Token
from mdit_py_plugins.utils import is_code_block
from .parse import ParseError, parse
def attrs_plugin(
md: MarkdownIt,
*,
after: Sequence[str] = ("image", "code_inline", "link_close", "span_close"),
spans: bool = False,
span_after: str = "link",
) -> None:
"""Parse inline attributes that immediately follow certain inline elements::
{#id .a b=c}
This syntax is inspired by
`Djot spans
`_.
Inside the curly braces, the following syntax is possible:
- `.foo` specifies foo as a class.
Multiple classes may be given in this way; they will be combined.
- `#foo` specifies foo as an identifier.
An element may have only one identifier;
if multiple identifiers are given, the last one is used.
- `key="value"` or `key=value` specifies a key-value attribute.
Quotes are not needed when the value consists entirely of
ASCII alphanumeric characters or `_` or `:` or `-`.
Backslash escapes may be used inside quoted values.
- `%` begins a comment, which ends with the next `%` or the end of the attribute (`}`).
Multiple attribute blocks are merged.
:param md: The MarkdownIt instance to modify.
:param after: The names of inline elements after which attributes may be specified.
This plugin does not support attributes after emphasis, strikethrough or text elements,
which all require post-parse processing.
:param spans: If True, also parse attributes after spans of text, encapsulated by `[]`.
Note Markdown link references take precedence over this syntax.
:param span_after: The name of an inline rule after which spans may be specified.
"""
def _attr_inline_rule(state: StateInline, silent: bool) -> bool:
if state.pending or not state.tokens:
return False
token = state.tokens[-1]
if token.type not in after:
return False
try:
new_pos, attrs = parse(state.src[state.pos :])
except ParseError:
return False
token_index = _find_opening(state.tokens, len(state.tokens) - 1)
if token_index is None:
return False
state.pos += new_pos + 1
if not silent:
attr_token = state.tokens[token_index]
if "class" in attrs and "class" in token.attrs:
attrs["class"] = f"{attr_token.attrs['class']} {attrs['class']}"
attr_token.attrs.update(attrs)
return True
if spans:
md.inline.ruler.after(span_after, "span", _span_rule)
if after:
md.inline.ruler.push("attr", _attr_inline_rule)
def attrs_block_plugin(md: MarkdownIt) -> None:
"""Parse block attributes.
Block attributes are attributes on a single line, with no other content.
They attach the specified attributes to the block below them::
{.a #b c=1}
A paragraph, that will be assigned the class ``a`` and the identifier ``b``.
Attributes can be stacked, with classes accumulating and lower attributes overriding higher::
{#a .a c=1}
{#b .b c=2}
A paragraph, that will be assigned the class ``a b c``, and the identifier ``b``.
This syntax is inspired by Djot block attributes.
"""
md.block.ruler.before("fence", "attr", _attr_block_rule)
md.core.ruler.after("block", "attr", _attr_resolve_block_rule)
def _find_opening(tokens: List[Token], index: int) -> Optional[int]:
"""Find the opening token index, if the token is closing."""
if tokens[index].nesting != -1:
return index
level = 0
while index >= 0:
level += tokens[index].nesting
if level == 0:
return index
index -= 1
return None
def _span_rule(state: StateInline, silent: bool) -> bool:
if state.src[state.pos] != "[":
return False
maximum = state.posMax
labelStart = state.pos + 1
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, False)
# parser failed to find ']', so it's not a valid span
if labelEnd < 0:
return False
pos = labelEnd + 1
# check not at end of inline
if pos >= maximum:
return False
try:
new_pos, attrs = parse(state.src[pos:])
except ParseError:
return False
pos += new_pos + 1
if not silent:
state.pos = labelStart
state.posMax = labelEnd
token = state.push("span_open", "span", 1)
token.attrs = attrs # type: ignore
state.md.inline.tokenize(state)
token = state.push("span_close", "span", -1)
state.pos = pos
state.posMax = maximum
return True
def _attr_block_rule(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
"""Find a block of attributes.
The block must be a single line that begins with a `{`, after three or less spaces,
and end with a `}` followed by any number if spaces.
"""
if is_code_block(state, startLine):
return False
pos = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
# if it doesn't start with a {, it's not an attribute block
if state.src[pos] != "{":
return False
# find first non-space character from the right
while maximum > pos and state.src[maximum - 1] in (" ", "\t"):
maximum -= 1
# if it doesn't end with a }, it's not an attribute block
if maximum <= pos:
return False
if state.src[maximum - 1] != "}":
return False
try:
new_pos, attrs = parse(state.src[pos:maximum])
except ParseError:
return False
# if the block was resolved earlier than expected, it's not an attribute block
# TODO this was not working in some instances, so I disabled it
# if (maximum - 1) != new_pos:
# return False
if silent:
return True
token = state.push("attrs_block", "", 0)
token.attrs = attrs # type: ignore
token.map = [startLine, startLine + 1]
state.line = startLine + 1
return True
def _attr_resolve_block_rule(state: StateCore) -> None:
"""Find attribute block then move its attributes to the next block."""
i = 0
len_tokens = len(state.tokens)
while i < len_tokens:
if state.tokens[i].type != "attrs_block":
i += 1
continue
if i + 1 < len_tokens:
next_token = state.tokens[i + 1]
# classes are appended
if "class" in state.tokens[i].attrs and "class" in next_token.attrs:
state.tokens[i].attrs[
"class"
] = f"{state.tokens[i].attrs['class']} {next_token.attrs['class']}"
if next_token.type == "attrs_block":
# subsequent attribute blocks take precedence, when merging
for key, value in state.tokens[i].attrs.items():
if key == "class" or key not in next_token.attrs:
next_token.attrs[key] = value
else:
# attribute block takes precedence over attributes in other blocks
next_token.attrs.update(state.tokens[i].attrs)
state.tokens.pop(i)
len_tokens -= 1
mdit-py-plugins-0.4.0/mdit_py_plugins/attrs/parse.py 0000664 0000000 0000000 00000017112 14437433217 0022616 0 ustar 00root root 0000000 0000000 """Parser for attributes::
attributes { id = "foo", class = "bar baz",
key1 = "val1", key2 = "val2" }
Adapted from:
https://github.com/jgm/djot/blob/fae7364b86bfce69bc6d5b5eede1f5196d845fd6/djot/attributes.lua#L1
syntax:
attributes <- '{' whitespace* attribute (whitespace attribute)* whitespace* '}'
attribute <- identifier | class | keyval
identifier <- '#' name
class <- '.' name
name <- (nonspace, nonpunctuation other than ':', '_', '-')+
keyval <- key '=' val
key <- (ASCII_ALPHANUM | ':' | '_' | '-')+
val <- bareval | quotedval
bareval <- (ASCII_ALPHANUM | ':' | '_' | '-')+
quotedval <- '"' ([^"] | '\"') '"'
"""
from __future__ import annotations
from enum import Enum
import re
from typing import Callable
class State(Enum):
START = 0
SCANNING = 1
SCANNING_ID = 2
SCANNING_CLASS = 3
SCANNING_KEY = 4
SCANNING_VALUE = 5
SCANNING_BARE_VALUE = 6
SCANNING_QUOTED_VALUE = 7
SCANNING_COMMENT = 8
SCANNING_ESCAPED = 9
DONE = 10
REGEX_SPACE = re.compile(r"\s")
REGEX_SPACE_PUNCTUATION = re.compile(r"[\s!\"#$%&'()*+,./;<=>?@[\]^`{|}~]")
REGEX_KEY_CHARACTERS = re.compile(r"[a-zA-Z\d_:-]")
class TokenState:
def __init__(self) -> None:
self._tokens: list[tuple[int, int, str]] = []
self.start: int = 0
def set_start(self, start: int) -> None:
self.start = start
def append(self, start: int, end: int, ttype: str) -> None:
self._tokens.append((start, end, ttype))
def compile(self, string: str) -> dict[str, str]:
"""compile the tokens into a dictionary"""
attributes = {}
classes = []
idx = 0
while idx < len(self._tokens):
start, end, ttype = self._tokens[idx]
if ttype == "id":
attributes["id"] = string[start:end]
elif ttype == "class":
classes.append(string[start:end])
elif ttype == "key":
key = string[start:end]
if idx + 1 < len(self._tokens):
start, end, ttype = self._tokens[idx + 1]
if ttype == "value":
if key == "class":
classes.append(string[start:end])
else:
attributes[key] = string[start:end]
idx += 1
idx += 1
if classes:
attributes["class"] = " ".join(classes)
return attributes
def __str__(self) -> str:
return str(self._tokens)
def __repr__(self) -> str:
return repr(self._tokens)
class ParseError(Exception):
def __init__(self, msg: str, pos: int) -> None:
self.pos = pos
super().__init__(msg + f" at position {pos}")
def parse(string: str) -> tuple[int, dict[str, str]]:
"""Parse attributes from start of string.
:returns: (length of parsed string, dict of attributes)
"""
pos = 0
state: State = State.START
tokens = TokenState()
while pos < len(string):
state = HANDLERS[state](string[pos], pos, tokens)
if state == State.DONE:
return pos, tokens.compile(string)
pos = pos + 1
return pos, tokens.compile(string)
def handle_start(char: str, pos: int, tokens: TokenState) -> State:
if char == "{":
return State.SCANNING
raise ParseError("Attributes must start with '{'", pos)
def handle_scanning(char: str, pos: int, tokens: TokenState) -> State:
if char == " " or char == "\t" or char == "\n" or char == "\r":
return State.SCANNING
if char == "}":
return State.DONE
if char == "#":
tokens.set_start(pos)
return State.SCANNING_ID
if char == "%":
tokens.set_start(pos)
return State.SCANNING_COMMENT
if char == ".":
tokens.set_start(pos)
return State.SCANNING_CLASS
if REGEX_KEY_CHARACTERS.fullmatch(char):
tokens.set_start(pos)
return State.SCANNING_KEY
raise ParseError(f"Unexpected character whilst scanning: {char}", pos)
def handle_scanning_comment(char: str, pos: int, tokens: TokenState) -> State:
if char == "%":
return State.SCANNING
return State.SCANNING_COMMENT
def handle_scanning_id(char: str, pos: int, tokens: TokenState) -> State:
if not REGEX_SPACE_PUNCTUATION.fullmatch(char):
return State.SCANNING_ID
if char == "}":
if (pos - 1) > tokens.start:
tokens.append(tokens.start + 1, pos, "id")
return State.DONE
if REGEX_SPACE.fullmatch(char):
if (pos - 1) > tokens.start:
tokens.append(tokens.start + 1, pos, "id")
return State.SCANNING
raise ParseError(f"Unexpected character whilst scanning id: {char}", pos)
def handle_scanning_class(char: str, pos: int, tokens: TokenState) -> State:
if not REGEX_SPACE_PUNCTUATION.fullmatch(char):
return State.SCANNING_CLASS
if char == "}":
if (pos - 1) > tokens.start:
tokens.append(tokens.start + 1, pos, "class")
return State.DONE
if REGEX_SPACE.fullmatch(char):
if (pos - 1) > tokens.start:
tokens.append(tokens.start + 1, pos, "class")
return State.SCANNING
raise ParseError(f"Unexpected character whilst scanning class: {char}", pos)
def handle_scanning_key(char: str, pos: int, tokens: TokenState) -> State:
if char == "=":
tokens.append(tokens.start, pos, "key")
return State.SCANNING_VALUE
if REGEX_KEY_CHARACTERS.fullmatch(char):
return State.SCANNING_KEY
raise ParseError(f"Unexpected character whilst scanning key: {char}", pos)
def handle_scanning_value(char: str, pos: int, tokens: TokenState) -> State:
if char == '"':
tokens.set_start(pos)
return State.SCANNING_QUOTED_VALUE
if REGEX_KEY_CHARACTERS.fullmatch(char):
tokens.set_start(pos)
return State.SCANNING_BARE_VALUE
raise ParseError(f"Unexpected character whilst scanning value: {char}", pos)
def handle_scanning_bare_value(char: str, pos: int, tokens: TokenState) -> State:
if REGEX_KEY_CHARACTERS.fullmatch(char):
return State.SCANNING_BARE_VALUE
if char == "}":
tokens.append(tokens.start, pos, "value")
return State.DONE
if REGEX_SPACE.fullmatch(char):
tokens.append(tokens.start, pos, "value")
return State.SCANNING
raise ParseError(f"Unexpected character whilst scanning bare value: {char}", pos)
def handle_scanning_escaped(char: str, pos: int, tokens: TokenState) -> State:
return State.SCANNING_QUOTED_VALUE
def handle_scanning_quoted_value(char: str, pos: int, tokens: TokenState) -> State:
if char == '"':
tokens.append(tokens.start + 1, pos, "value")
return State.SCANNING
if char == "\\":
return State.SCANNING_ESCAPED
if char == "{" or char == "}":
raise ParseError(
f"Unexpected character whilst scanning quoted value: {char}", pos
)
if char == "\n":
tokens.append(tokens.start + 1, pos, "value")
return State.SCANNING_QUOTED_VALUE
return State.SCANNING_QUOTED_VALUE
HANDLERS: dict[State, Callable[[str, int, TokenState], State]] = {
State.START: handle_start,
State.SCANNING: handle_scanning,
State.SCANNING_COMMENT: handle_scanning_comment,
State.SCANNING_ID: handle_scanning_id,
State.SCANNING_CLASS: handle_scanning_class,
State.SCANNING_KEY: handle_scanning_key,
State.SCANNING_VALUE: handle_scanning_value,
State.SCANNING_BARE_VALUE: handle_scanning_bare_value,
State.SCANNING_QUOTED_VALUE: handle_scanning_quoted_value,
State.SCANNING_ESCAPED: handle_scanning_escaped,
}
mdit-py-plugins-0.4.0/mdit_py_plugins/colon_fence.py 0000664 0000000 0000000 00000007563 14437433217 0022632 0 ustar 00root root 0000000 0000000 from __future__ import annotations
from typing import TYPE_CHECKING, Sequence
from markdown_it import MarkdownIt
from markdown_it.common.utils import escapeHtml, unescapeAll
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
def colon_fence_plugin(md: MarkdownIt) -> None:
"""This plugin directly mimics regular fences, but with `:` colons.
Example::
:::name
contained text
:::
"""
md.block.ruler.before(
"fence",
"colon_fence",
_rule,
{"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]},
)
md.add_render_rule("colon_fence", _render)
def _rule(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
if is_code_block(state, startLine):
return False
haveEndMarker = False
pos = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
if pos + 3 > maximum:
return False
marker = state.src[pos]
if marker != ":":
return False
# scan marker length
mem = pos
pos = _skipCharsStr(state, pos, marker)
length = pos - mem
if length < 3:
return False
markup = state.src[mem:pos]
params = state.src[pos:maximum]
# Since start is found, we can report success here in validation mode
if silent:
return True
# search end of block
nextLine = startLine
while True:
nextLine += 1
if nextLine >= endLine:
# unclosed block should be autoclosed by end of document.
# also block seems to be autoclosed by end of parent
break
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
maximum = state.eMarks[nextLine]
if pos < maximum and state.sCount[nextLine] < state.blkIndent:
# non-empty line with negative indent should stop the list:
# - ```
# test
break
if state.src[pos] != marker:
continue
if is_code_block(state, nextLine):
continue
pos = _skipCharsStr(state, pos, marker)
# closing code fence must be at least as long as the opening one
if pos - mem < length:
continue
# make sure tail has spaces only
pos = state.skipSpaces(pos)
if pos < maximum:
continue
haveEndMarker = True
# found!
break
# If a fence has heading spaces, they should be removed from its inner block
length = state.sCount[startLine]
state.line = nextLine + (1 if haveEndMarker else 0)
token = state.push("colon_fence", "code", 0)
token.info = params
token.content = state.getLines(startLine + 1, nextLine, length, True)
token.markup = markup
token.map = [startLine, state.line]
return True
def _skipCharsStr(state: StateBlock, pos: int, ch: str) -> int:
"""Skip character string from given position."""
# TODO this can be replaced with StateBlock.skipCharsStr in markdown-it-py 3.0.0
while True:
try:
current = state.src[pos]
except IndexError:
break
if current != ch:
break
pos += 1
return pos
def _render(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
token = tokens[idx]
info = unescapeAll(token.info).strip() if token.info else ""
content = escapeHtml(token.content)
block_name = ""
if info:
block_name = info.split()[0]
return (
"
"
+ content
+ "
\n"
)
mdit-py-plugins-0.4.0/mdit_py_plugins/container/ 0000775 0000000 0000000 00000000000 14437433217 0021755 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/container/LICENSE 0000664 0000000 0000000 00000002061 14437433217 0022761 0 ustar 00root root 0000000 0000000 Copyright (c) 2015 Vitaly Puzrin, Alex Kocharin.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mdit-py-plugins-0.4.0/mdit_py_plugins/container/README.md 0000664 0000000 0000000 00000004745 14437433217 0023246 0 ustar 00root root 0000000 0000000 # markdown-it-container
[](https://travis-ci.org/markdown-it/markdown-it-container)
[](https://www.npmjs.org/package/markdown-it-container)
[](https://coveralls.io/r/markdown-it/markdown-it-container?branch=master)
> Plugin for creating block-level custom containers for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser.
__v2.+ requires `markdown-it` v5.+, see changelog.__
With this plugin you can create block containers like:
```
::: warning
*here be dragons*
:::
```
.... and specify how they should be rendered. If no renderer defined, `
` with
container name class will be created:
```html
here be dragons
```
Markup is the same as for [fenced code blocks](http://spec.commonmark.org/0.18/#fenced-code-blocks).
Difference is, that marker use another character and content is rendered as markdown markup.
## Installation
node.js, browser:
```bash
$ npm install markdown-it-container --save
$ bower install markdown-it-container --save
```
## API
```js
var md = require('markdown-it')()
.use(require('markdown-it-container'), name [, options]);
```
Params:
- __name__ - container name (mandatory)
- __options:__
- __validate__ - optional, function to validate tail after opening marker, should
return `true` on success.
- __render__ - optional, renderer function for opening/closing tokens.
- __marker__ - optional (`:`), character to use in delimiter.
## Example
```js
var md = require('markdown-it')();
md.use(require('markdown-it-container'), 'spoiler', {
validate: function(params) {
return params.trim().match(/^spoiler\s+(.*)$/);
},
render: function (tokens, idx) {
var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);
if (tokens[idx].nesting === 1) {
// opening tag
return '' + md.utils.escapeHtml(m[1]) + '\n';
} else {
// closing tag
return '\n';
}
}
});
console.log(md.render('::: spoiler click me\n*content*\n:::\n'));
// Output:
//
// click me
//
content
//
```
## License
[MIT](https://github.com/markdown-it/markdown-it-container/blob/master/LICENSE)
mdit-py-plugins-0.4.0/mdit_py_plugins/container/__init__.py 0000664 0000000 0000000 00000000061 14437433217 0024063 0 ustar 00root root 0000000 0000000 from .index import container_plugin # noqa F401
mdit-py-plugins-0.4.0/mdit_py_plugins/container/index.py 0000664 0000000 0000000 00000013155 14437433217 0023443 0 ustar 00root root 0000000 0000000 """Process block-level custom containers."""
from __future__ import annotations
from math import floor
from typing import TYPE_CHECKING, Any, Callable, Sequence
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
def container_plugin(
md: MarkdownIt,
name: str,
marker: str = ":",
validate: None | Callable[[str, str], bool] = None,
render: None | Callable[..., str] = None,
) -> None:
"""Plugin ported from
`markdown-it-container `__.
It is a plugin for creating block-level custom containers:
.. code-block:: md
:::: name
::: name
*markdown*
:::
::::
:param name: the name of the container to parse
:param marker: the marker character to use
:param validate: func(marker, param) -> bool, default matches against the name
:param render: render func
"""
def validateDefault(params: str, *args: Any) -> bool:
return params.strip().split(" ", 2)[0] == name
def renderDefault(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
_options: OptionsDict,
env: EnvType,
) -> str:
# add a class to the opening tag
if tokens[idx].nesting == 1:
tokens[idx].attrJoin("class", name)
return self.renderToken(tokens, idx, _options, env) # type: ignore
min_markers = 3
marker_str = marker
marker_char = marker_str[0]
marker_len = len(marker_str)
validate = validate or validateDefault
render = render or renderDefault
def container_func(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
if is_code_block(state, startLine):
return False
auto_closed = False
start = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
# Check out the first character quickly,
# this should filter out most of non-containers
if marker_char != state.src[start]:
return False
# Check out the rest of the marker string
pos = start + 1
while pos <= maximum:
try:
character = state.src[pos]
except IndexError:
break
if marker_str[(pos - start) % marker_len] != character:
break
pos += 1
marker_count = floor((pos - start) / marker_len)
if marker_count < min_markers:
return False
pos -= (pos - start) % marker_len
markup = state.src[start:pos]
params = state.src[pos:maximum]
assert validate is not None
if not validate(params, markup):
return False
# Since start is found, we can report success here in validation mode
if silent:
return True
# Search for the end of the block
nextLine = startLine
while True:
nextLine += 1
if nextLine >= endLine:
# unclosed block should be autoclosed by end of document.
# also block seems to be autoclosed by end of parent
break
start = state.bMarks[nextLine] + state.tShift[nextLine]
maximum = state.eMarks[nextLine]
if start < maximum and state.sCount[nextLine] < state.blkIndent:
# non-empty line with negative indent should stop the list:
# - ```
# test
break
if marker_char != state.src[start]:
continue
if is_code_block(state, nextLine):
continue
pos = start + 1
while pos <= maximum:
try:
character = state.src[pos]
except IndexError:
break
if marker_str[(pos - start) % marker_len] != character:
break
pos += 1
# closing code fence must be at least as long as the opening one
if floor((pos - start) / marker_len) < marker_count:
continue
# make sure tail has spaces only
pos -= (pos - start) % marker_len
pos = state.skipSpaces(pos)
if pos < maximum:
continue
# found!
auto_closed = True
break
old_parent = state.parentType
old_line_max = state.lineMax
state.parentType = "container"
# this will prevent lazy continuations from ever going past our end marker
state.lineMax = nextLine
token = state.push(f"container_{name}_open", "div", 1)
token.markup = markup
token.block = True
token.info = params
token.map = [startLine, nextLine]
state.md.block.tokenize(state, startLine + 1, nextLine)
token = state.push(f"container_{name}_close", "div", -1)
token.markup = state.src[start:pos]
token.block = True
state.parentType = old_parent
state.lineMax = old_line_max
state.line = nextLine + (1 if auto_closed else 0)
return True
md.block.ruler.before(
"fence",
"container_" + name,
container_func,
{"alt": ["paragraph", "reference", "blockquote", "list"]},
)
md.add_render_rule(f"container_{name}_open", render)
md.add_render_rule(f"container_{name}_close", render)
mdit-py-plugins-0.4.0/mdit_py_plugins/container/port.yaml 0000664 0000000 0000000 00000000204 14437433217 0023621 0 ustar 00root root 0000000 0000000 - package: markdown-it-container
commit: adb3defde3a1c56015895b47ce4c6591b8b1e3a2
date: Jun 2, 2020
version: 3.0.0
changes:
mdit-py-plugins-0.4.0/mdit_py_plugins/deflist/ 0000775 0000000 0000000 00000000000 14437433217 0021425 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/deflist/LICENSE 0000664 0000000 0000000 00000002066 14437433217 0022436 0 ustar 00root root 0000000 0000000 Copyright (c) 2014-2015 Vitaly Puzrin, Alex Kocharin.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mdit-py-plugins-0.4.0/mdit_py_plugins/deflist/README.md 0000664 0000000 0000000 00000002371 14437433217 0022707 0 ustar 00root root 0000000 0000000 # markdown-it-deflist
[](https://travis-ci.org/markdown-it/markdown-it-deflist)
[](https://www.npmjs.org/package/markdown-it-deflist)
[](https://coveralls.io/r/markdown-it/markdown-it-deflist?branch=master)
> Definition list (`
`) tag plugin for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser.
__v2.+ requires `markdown-it` v5.+, see changelog.__
Syntax is based on [pandoc definition lists](http://johnmacfarlane.net/pandoc/README.html#definition-lists).
## Install
node.js, browser:
```bash
npm install markdown-it-deflist --save
bower install markdown-it-deflist --save
```
## Use
```js
var md = require('markdown-it')()
.use(require('markdown-it-deflist'));
md.render(/*...*/);
```
_Differences in browser._ If you load script directly into the page, without
package system, module will add itself globally as `window.markdownitDeflist`.
## License
[MIT](https://github.com/markdown-it/markdown-it-deflist/blob/master/LICENSE)
mdit-py-plugins-0.4.0/mdit_py_plugins/deflist/__init__.py 0000664 0000000 0000000 00000000057 14437433217 0023540 0 ustar 00root root 0000000 0000000 from .index import deflist_plugin # noqa F401
mdit-py-plugins-0.4.0/mdit_py_plugins/deflist/index.py 0000664 0000000 0000000 00000016416 14437433217 0023116 0 ustar 00root root 0000000 0000000 """Process definition lists."""
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
def deflist_plugin(md: MarkdownIt) -> None:
"""Plugin ported from
`markdown-it-deflist `__.
The syntax is based on
`pandoc definition lists `__:
.. code-block:: md
Term 1
: Definition 1 long form
second paragraph
Term 2 with *inline markup*
~ Definition 2a compact style
~ Definition 2b
"""
def skipMarker(state: StateBlock, line: int) -> int:
"""Search `[:~][\n ]`, returns next pos after marker on success or -1 on fail."""
start = state.bMarks[line] + state.tShift[line]
maximum = state.eMarks[line]
if start >= maximum:
return -1
# Check bullet
marker = state.src[start]
start += 1
if marker != "~" and marker != ":":
return -1
pos = state.skipSpaces(start)
# require space after ":"
if start == pos:
return -1
# no empty definitions, e.g. " : "
if pos >= maximum:
return -1
return start
def markTightParagraphs(state: StateBlock, idx: int) -> None:
level = state.level + 2
i = idx + 2
l2 = len(state.tokens) - 2
while i < l2:
if (
state.tokens[i].level == level
and state.tokens[i].type == "paragraph_open"
):
state.tokens[i + 2].hidden = True
state.tokens[i].hidden = True
i += 2
i += 1
def deflist(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
if is_code_block(state, startLine):
return False
if silent:
# quirk: validation mode validates a dd block only, not a whole deflist
if state.ddIndent < 0:
return False
return skipMarker(state, startLine) >= 0
nextLine = startLine + 1
if nextLine >= endLine:
return False
if state.isEmpty(nextLine):
nextLine += 1
if nextLine >= endLine:
return False
if state.sCount[nextLine] < state.blkIndent:
return False
contentStart = skipMarker(state, nextLine)
if contentStart < 0:
return False
# Start list
listTokIdx = len(state.tokens)
tight = True
token = state.push("dl_open", "dl", 1)
token.map = listLines = [startLine, 0]
# Iterate list items
dtLine = startLine
ddLine = nextLine
# One definition list can contain multiple DTs,
# and one DT can be followed by multiple DDs.
#
# Thus, there is two loops here, and label is
# needed to break out of the second one
#
break_outer = False
while True:
prevEmptyEnd = False
token = state.push("dt_open", "dt", 1)
token.map = [dtLine, dtLine]
token = state.push("inline", "", 0)
token.map = [dtLine, dtLine]
token.content = state.getLines(
dtLine, dtLine + 1, state.blkIndent, False
).strip()
token.children = []
token = state.push("dt_close", "dt", -1)
while True:
token = state.push("dd_open", "dd", 1)
token.map = itemLines = [nextLine, 0]
pos = contentStart
maximum = state.eMarks[ddLine]
offset = (
state.sCount[ddLine]
+ contentStart
- (state.bMarks[ddLine] + state.tShift[ddLine])
)
while pos < maximum:
if state.src[pos] == "\t":
offset += 4 - offset % 4
elif state.src[pos] == " ":
offset += 1
else:
break
pos += 1
contentStart = pos
oldTight = state.tight
oldDDIndent = state.ddIndent
oldIndent = state.blkIndent
oldTShift = state.tShift[ddLine]
oldSCount = state.sCount[ddLine]
oldParentType = state.parentType
state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2
state.tShift[ddLine] = contentStart - state.bMarks[ddLine]
state.sCount[ddLine] = offset
state.tight = True
state.parentType = "deflist"
state.md.block.tokenize(state, ddLine, endLine)
# If any of list item is tight, mark list as tight
if not state.tight or prevEmptyEnd:
tight = False
# Item become loose if finish with empty line,
# but we should filter last element, because it means list finish
prevEmptyEnd = (state.line - ddLine) > 1 and state.isEmpty(
state.line - 1
)
state.tShift[ddLine] = oldTShift
state.sCount[ddLine] = oldSCount
state.tight = oldTight
state.parentType = oldParentType
state.blkIndent = oldIndent
state.ddIndent = oldDDIndent
token = state.push("dd_close", "dd", -1)
itemLines[1] = nextLine = state.line
if nextLine >= endLine:
break_outer = True
break
if state.sCount[nextLine] < state.blkIndent:
break_outer = True
break
contentStart = skipMarker(state, nextLine)
if contentStart < 0:
break
ddLine = nextLine
# go to the next loop iteration:
# insert DD tag and repeat checking
if break_outer:
break_outer = False
break
if nextLine >= endLine:
break
dtLine = nextLine
if state.isEmpty(dtLine):
break
if state.sCount[dtLine] < state.blkIndent:
break
ddLine = dtLine + 1
if ddLine >= endLine:
break
if state.isEmpty(ddLine):
ddLine += 1
if ddLine >= endLine:
break
if state.sCount[ddLine] < state.blkIndent:
break
contentStart = skipMarker(state, ddLine)
if contentStart < 0:
break
# go to the next loop iteration:
# insert DT and DD tags and repeat checking
# Finalise list
token = state.push("dl_close", "dl", -1)
listLines[1] = nextLine
state.line = nextLine
# mark paragraphs tight if needed
if tight:
markTightParagraphs(state, listTokIdx)
return True
md.block.ruler.before(
"paragraph",
"deflist",
deflist,
{"alt": ["paragraph", "reference", "blockquote"]},
)
mdit-py-plugins-0.4.0/mdit_py_plugins/deflist/port.yaml 0000664 0000000 0000000 00000000204 14437433217 0023271 0 ustar 00root root 0000000 0000000 - package: markdown-it-deflist
commit: 20db400948520308291da029a23b0751cb30f3a0
date: July 12, 2017
version: 2.0.3
changes:
mdit-py-plugins-0.4.0/mdit_py_plugins/dollarmath/ 0000775 0000000 0000000 00000000000 14437433217 0022122 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/dollarmath/__init__.py 0000664 0000000 0000000 00000000062 14437433217 0024231 0 ustar 00root root 0000000 0000000 from .index import dollarmath_plugin # noqa F401
mdit-py-plugins-0.4.0/mdit_py_plugins/dollarmath/index.py 0000664 0000000 0000000 00000030321 14437433217 0023602 0 ustar 00root root 0000000 0000000 from __future__ import annotations
import re
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Sequence
from markdown_it import MarkdownIt
from markdown_it.common.utils import escapeHtml, isWhiteSpace
from markdown_it.rules_block import StateBlock
from markdown_it.rules_inline import StateInline
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
def dollarmath_plugin(
md: MarkdownIt,
*,
allow_labels: bool = True,
allow_space: bool = True,
allow_digits: bool = True,
allow_blank_lines: bool = True,
double_inline: bool = False,
label_normalizer: Optional[Callable[[str], str]] = None,
renderer: Optional[Callable[[str, Dict[str, Any]], str]] = None,
label_renderer: Optional[Callable[[str], str]] = None,
) -> None:
"""Plugin for parsing dollar enclosed math,
e.g. inline: ``$a=1$``, block: ``$$b=2$$``
This is an improved version of ``texmath``; it is more performant,
and handles ``\\`` escaping properly and allows for more configuration.
:param allow_labels: Capture math blocks with label suffix, e.g. ``$$a=1$$ (eq1)``
:param allow_space: Parse inline math when there is space
after/before the opening/closing ``$``, e.g. ``$ a $``
:param allow_digits: Parse inline math when there is a digit
before/after the opening/closing ``$``, e.g. ``1$`` or ``$2``.
This is useful when also using currency.
:param allow_blank_lines: Allow blank lines inside ``$$``. Note that blank lines are
not allowed in LaTeX, executablebooks/markdown-it-dollarmath, or the Github or
StackExchange markdown dialects. Hoever, they have special semantics if used
within Sphinx `..math` admonitions, so are allowed for backwards-compatibility.
:param double_inline: Search for double-dollar math within inline contexts
:param label_normalizer: Function to normalize the label,
by default replaces whitespace with `-`
:param renderer: Function to render content: `(str, {"display_mode": bool}) -> str`,
by default escapes HTML
:param label_renderer: Function to render labels, by default creates anchor
"""
if label_normalizer is None:
label_normalizer = lambda label: re.sub(r"\s+", "-", label)
md.inline.ruler.before(
"escape",
"math_inline",
math_inline_dollar(allow_space, allow_digits, double_inline),
)
md.block.ruler.before(
"fence",
"math_block",
math_block_dollar(allow_labels, label_normalizer, allow_blank_lines),
)
# TODO the current render rules are really just for testing
# would be good to allow "proper" math rendering,
# e.g. https://github.com/roniemartinez/latex2mathml
_renderer = (
(lambda content, _: escapeHtml(content)) if renderer is None else renderer
)
_label_renderer: Callable[[str], str]
if label_renderer is None:
_label_renderer = (
lambda label: f'¶' # noqa: E501
)
else:
_label_renderer = label_renderer
def render_math_inline(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
content = _renderer(str(tokens[idx].content).strip(), {"display_mode": False})
return f'{content}'
def render_math_inline_double(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
content = _renderer(str(tokens[idx].content).strip(), {"display_mode": True})
return f'
\n'
md.add_render_rule("math_inline", render_math_inline)
md.add_render_rule("math_inline_double", render_math_inline_double)
md.add_render_rule("math_block", render_math_block)
md.add_render_rule("math_block_label", render_math_block_label)
def is_escaped(state: StateInline, back_pos: int, mod: int = 0) -> bool:
"""Test if dollar is escaped."""
# count how many \ are before the current position
backslashes = 0
while back_pos >= 0:
back_pos = back_pos - 1
if state.src[back_pos] == "\\":
backslashes += 1
else:
break
if not backslashes:
return False
# if an odd number of \ then ignore
if (backslashes % 2) != mod:
return True
return False
def math_inline_dollar(
allow_space: bool = True, allow_digits: bool = True, allow_double: bool = False
) -> Callable[[StateInline, bool], bool]:
"""Generate inline dollar rule.
:param allow_space: Parse inline math when there is space
after/before the opening/closing ``$``, e.g. ``$ a $``
:param allow_digits: Parse inline math when there is a digit
before/after the opening/closing ``$``, e.g. ``1$`` or ``$2``.
This is useful when also using currency.
:param allow_double: Search for double-dollar math within inline contexts
"""
def _math_inline_dollar(state: StateInline, silent: bool) -> bool:
"""Inline dollar rule.
- Initial check:
- check if first character is a $
- check if the first character is escaped
- check if the next character is a space (if not allow_space)
- check if the next character is a digit (if not allow_digits)
- Advance one, if allow_double
- Find closing (advance one, if allow_double)
- Check closing:
- check if the previous character is a space (if not allow_space)
- check if the next character is a digit (if not allow_digits)
- Check empty content
"""
# TODO options:
# even/odd backslash escaping
if state.src[state.pos] != "$":
return False
if not allow_space:
# whitespace not allowed straight after opening $
try:
if isWhiteSpace(ord(state.src[state.pos + 1])):
return False
except IndexError:
return False
if not allow_digits:
# digit not allowed straight before opening $
try:
if state.src[state.pos - 1].isdigit():
return False
except IndexError:
pass
if is_escaped(state, state.pos):
return False
try:
is_double = allow_double and state.src[state.pos + 1] == "$"
except IndexError:
return False
# find closing $
pos = state.pos + 1 + (1 if is_double else 0)
found_closing = False
while not found_closing:
try:
end = state.src.index("$", pos)
except ValueError:
return False
if is_escaped(state, end):
pos = end + 1
continue
try:
if is_double and state.src[end + 1] != "$":
pos = end + 1
continue
except IndexError:
return False
if is_double:
end += 1
found_closing = True
if not found_closing:
return False
if not allow_space:
# whitespace not allowed straight before closing $
try:
if isWhiteSpace(ord(state.src[end - 1])):
return False
except IndexError:
return False
if not allow_digits:
# digit not allowed straight after closing $
try:
if state.src[end + 1].isdigit():
return False
except IndexError:
pass
text = (
state.src[state.pos + 2 : end - 1]
if is_double
else state.src[state.pos + 1 : end]
)
# ignore empty
if not text:
return False
if not silent:
token = state.push(
"math_inline_double" if is_double else "math_inline", "math", 0
)
token.content = text
token.markup = "$$" if is_double else "$"
state.pos = end + 1
return True
return _math_inline_dollar
# reversed end of block dollar equation, with equation label
DOLLAR_EQNO_REV = re.compile(r"^\s*\)([^)$\r\n]+?)\(\s*\${2}")
def math_block_dollar(
allow_labels: bool = True,
label_normalizer: Optional[Callable[[str], str]] = None,
allow_blank_lines: bool = False,
) -> Callable[[StateBlock, int, int, bool], bool]:
"""Generate block dollar rule."""
def _math_block_dollar(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
# TODO internal backslash escaping
if is_code_block(state, startLine):
return False
haveEndMarker = False
startPos = state.bMarks[startLine] + state.tShift[startLine]
end = state.eMarks[startLine]
if startPos + 2 > end:
return False
if state.src[startPos] != "$" or state.src[startPos + 1] != "$":
return False
# search for end of block
nextLine = startLine
label = None
# search for end of block on same line
lineText = state.src[startPos:end]
if len(lineText.strip()) > 3:
if lineText.strip().endswith("$$"):
haveEndMarker = True
end = end - 2 - (len(lineText) - len(lineText.strip()))
elif allow_labels:
# reverse the line and match
eqnoMatch = DOLLAR_EQNO_REV.match(lineText[::-1])
if eqnoMatch:
haveEndMarker = True
label = eqnoMatch.group(1)[::-1]
end = end - eqnoMatch.end()
# search for end of block on subsequent line
if not haveEndMarker:
while True:
nextLine += 1
if nextLine >= endLine:
break
start = state.bMarks[nextLine] + state.tShift[nextLine]
end = state.eMarks[nextLine]
lineText = state.src[start:end]
if lineText.strip().endswith("$$"):
haveEndMarker = True
end = end - 2 - (len(lineText) - len(lineText.strip()))
break
if lineText.strip() == "" and not allow_blank_lines:
break # blank lines are not allowed within $$
# reverse the line and match
if allow_labels:
eqnoMatch = DOLLAR_EQNO_REV.match(lineText[::-1])
if eqnoMatch:
haveEndMarker = True
label = eqnoMatch.group(1)[::-1]
end = end - eqnoMatch.end()
break
if not haveEndMarker:
return False
state.line = nextLine + (1 if haveEndMarker else 0)
token = state.push("math_block_label" if label else "math_block", "math", 0)
token.block = True
token.content = state.src[startPos + 2 : end]
token.markup = "$$"
token.map = [startLine, state.line]
if label:
token.info = label if label_normalizer is None else label_normalizer(label)
return True
return _math_block_dollar
mdit-py-plugins-0.4.0/mdit_py_plugins/field_list/ 0000775 0000000 0000000 00000000000 14437433217 0022111 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/field_list/__init__.py 0000664 0000000 0000000 00000020131 14437433217 0024217 0 ustar 00root root 0000000 0000000 """Field list plugin"""
from contextlib import contextmanager
from typing import Iterator, Optional, Tuple
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
def fieldlist_plugin(md: MarkdownIt) -> None:
"""Field lists are mappings from field names to field bodies, based on the
`reStructureText syntax
`_.
.. code-block:: md
:name *markup*:
:name1: body content
:name2: paragraph 1
paragraph 2
:name3:
paragraph 1
paragraph 2
A field name may consist of any characters except colons (":").
Inline markup is parsed in field names.
The field name is followed by whitespace and the field body.
The field body may be empty or contain multiple body elements.
Since the field marker may be quite long,
the second and subsequent lines of the field body do not have to
line up with the first line, but they must be indented relative to the
field name marker, and they must line up with each other.
"""
md.block.ruler.before(
"paragraph",
"fieldlist",
_fieldlist_rule,
{"alt": ["paragraph", "reference", "blockquote"]},
)
def parseNameMarker(state: StateBlock, startLine: int) -> Tuple[int, str]:
"""Parse field name: `:name:`
:returns: position after name marker, name text
"""
start = state.bMarks[startLine] + state.tShift[startLine]
pos = start
maximum = state.eMarks[startLine]
# marker should have at least 3 chars (colon + character + colon)
if pos + 2 >= maximum:
return -1, ""
# first character should be ':'
if state.src[pos] != ":":
return -1, ""
# scan name length
name_length = 1
found_close = False
for ch in state.src[pos + 1 :]:
if ch == "\n":
break
if ch == ":":
# TODO backslash escapes
found_close = True
break
name_length += 1
if not found_close:
return -1, ""
# get name
name_text = state.src[pos + 1 : pos + name_length]
# name should contain at least one character
if not name_text.strip():
return -1, ""
return pos + name_length + 1, name_text
@contextmanager
def set_parent_type(state: StateBlock, name: str) -> Iterator[None]:
"""Temporarily set parent type to `name`"""
oldParentType = state.parentType
state.parentType = name
yield
state.parentType = oldParentType
def _fieldlist_rule(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
# adapted from markdown_it/rules_block/list.py::list_block
if is_code_block(state, startLine):
return False
posAfterName, name_text = parseNameMarker(state, startLine)
if posAfterName < 0:
return False
# For validation mode we can terminate immediately
if silent:
return True
# start field list
token = state.push("field_list_open", "dl", 1)
token.attrSet("class", "field-list")
token.map = listLines = [startLine, 0]
# iterate list items
nextLine = startLine
with set_parent_type(state, "fieldlist"):
while nextLine < endLine:
# create name tokens
token = state.push("fieldlist_name_open", "dt", 1)
token.map = [startLine, startLine]
token = state.push("inline", "", 0)
token.map = [startLine, startLine]
token.content = name_text
token.children = []
token = state.push("fieldlist_name_close", "dt", -1)
# set indent positions
pos = posAfterName
maximum: int = state.eMarks[nextLine]
first_line_body_indent = (
state.sCount[nextLine]
+ posAfterName
- (state.bMarks[startLine] + state.tShift[startLine])
)
# find indent to start of body on first line
while pos < maximum:
ch = state.src[pos]
if ch == "\t":
first_line_body_indent += (
4 - (first_line_body_indent + state.bsCount[nextLine]) % 4
)
elif ch == " ":
first_line_body_indent += 1
else:
break
pos += 1
contentStart = pos
# to figure out the indent of the body,
# we look at all non-empty, indented lines and find the minimum indent
block_indent: Optional[int] = None
_line = startLine + 1
while _line < endLine:
# if start_of_content < end_of_content, then non-empty line
if (state.bMarks[_line] + state.tShift[_line]) < state.eMarks[_line]:
if state.tShift[_line] <= 0:
# the line has no indent, so it's the end of the field
break
block_indent = (
state.tShift[_line]
if block_indent is None
else min(block_indent, state.tShift[_line])
)
_line += 1
has_first_line = contentStart < maximum
if block_indent is None: # no body content
if not has_first_line: # noqa SIM108
# no body or first line, so just use default
block_indent = 2
else:
# only a first line, so use it's indent
block_indent = first_line_body_indent
else:
block_indent = min(block_indent, first_line_body_indent)
# Run subparser on the field body
token = state.push("fieldlist_body_open", "dd", 1)
token.map = [startLine, startLine]
with temp_state_changes(state, startLine):
diff = 0
if has_first_line and block_indent < first_line_body_indent:
# this is a hack to get the first line to render correctly
# we temporarily "shift" it to the left by the difference
# between the first line indent and the block indent
# and replace the "hole" left with space,
# so that src indexes still match
diff = first_line_body_indent - block_indent
state.src = (
state.src[: contentStart - diff]
+ " " * diff
+ state.src[contentStart:]
)
state.tShift[startLine] = contentStart - diff - state.bMarks[startLine]
state.sCount[startLine] = first_line_body_indent - diff
state.blkIndent = block_indent
state.md.block.tokenize(state, startLine, endLine)
state.push("fieldlist_body_close", "dd", -1)
nextLine = startLine = state.line
token.map[1] = nextLine
if nextLine >= endLine:
break
contentStart = state.bMarks[startLine]
# Try to check if list is terminated or continued.
if state.sCount[nextLine] < state.blkIndent:
break
if is_code_block(state, startLine):
break
# get next field item
posAfterName, name_text = parseNameMarker(state, startLine)
if posAfterName < 0:
break
# Finalize list
token = state.push("field_list_close", "dl", -1)
listLines[1] = nextLine
state.line = nextLine
return True
@contextmanager
def temp_state_changes(state: StateBlock, startLine: int) -> Iterator[None]:
"""Allow temporarily changing certain state attributes."""
oldTShift = state.tShift[startLine]
oldSCount = state.sCount[startLine]
oldBlkIndent = state.blkIndent
oldSrc = state.src
yield
state.blkIndent = oldBlkIndent
state.tShift[startLine] = oldTShift
state.sCount[startLine] = oldSCount
state.src = oldSrc
mdit-py-plugins-0.4.0/mdit_py_plugins/footnote/ 0000775 0000000 0000000 00000000000 14437433217 0021630 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/footnote/LICENSE 0000664 0000000 0000000 00000002066 14437433217 0022641 0 ustar 00root root 0000000 0000000 Copyright (c) 2014-2015 Vitaly Puzrin, Alex Kocharin.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mdit-py-plugins-0.4.0/mdit_py_plugins/footnote/__init__.py 0000664 0000000 0000000 00000000061 14437433217 0023736 0 ustar 00root root 0000000 0000000 from .index import footnote_plugin # noqa: F401
mdit-py-plugins-0.4.0/mdit_py_plugins/footnote/index.py 0000664 0000000 0000000 00000032347 14437433217 0023322 0 ustar 00root root 0000000 0000000 """Process footnotes"""
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Sequence
from markdown_it import MarkdownIt
from markdown_it.helpers import parseLinkLabel
from markdown_it.rules_block import StateBlock
from markdown_it.rules_core import StateCore
from markdown_it.rules_inline import StateInline
from markdown_it.token import Token
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.utils import EnvType, OptionsDict
def footnote_plugin(md: MarkdownIt) -> None:
"""Plugin ported from
`markdown-it-footnote `__.
It is based on the
`pandoc definition `__:
.. code-block:: md
Normal footnote:
Here is a footnote reference,[^1] and another.[^longnote]
[^1]: Here is the footnote.
[^longnote]: Here's one with multiple blocks.
Subsequent paragraphs are indented to show that they
belong to the previous footnote.
"""
md.block.ruler.before(
"reference", "footnote_def", footnote_def, {"alt": ["paragraph", "reference"]}
)
md.inline.ruler.after("image", "footnote_inline", footnote_inline)
md.inline.ruler.after("footnote_inline", "footnote_ref", footnote_ref)
md.core.ruler.after("inline", "footnote_tail", footnote_tail)
md.add_render_rule("footnote_ref", render_footnote_ref)
md.add_render_rule("footnote_block_open", render_footnote_block_open)
md.add_render_rule("footnote_block_close", render_footnote_block_close)
md.add_render_rule("footnote_open", render_footnote_open)
md.add_render_rule("footnote_close", render_footnote_close)
md.add_render_rule("footnote_anchor", render_footnote_anchor)
# helpers (only used in other rules, no tokens are attached to those)
md.add_render_rule("footnote_caption", render_footnote_caption)
md.add_render_rule("footnote_anchor_name", render_footnote_anchor_name)
# ## RULES ##
def footnote_def(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
"""Process footnote block definition"""
if is_code_block(state, startLine):
return False
start = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
# line should be at least 5 chars - "[^x]:"
if start + 4 > maximum:
return False
if state.src[start] != "[":
return False
if state.src[start + 1] != "^":
return False
pos = start + 2
while pos < maximum:
if state.src[pos] == " ":
return False
if state.src[pos] == "]":
break
pos += 1
if pos == start + 2: # no empty footnote labels
return False
pos += 1
if pos >= maximum or state.src[pos] != ":":
return False
if silent:
return True
pos += 1
label = state.src[start + 2 : pos - 2]
state.env.setdefault("footnotes", {}).setdefault("refs", {})[":" + label] = -1
open_token = Token("footnote_reference_open", "", 1)
open_token.meta = {"label": label}
open_token.level = state.level
state.level += 1
state.tokens.append(open_token)
oldBMark = state.bMarks[startLine]
oldTShift = state.tShift[startLine]
oldSCount = state.sCount[startLine]
oldParentType = state.parentType
posAfterColon = pos
initial = offset = (
state.sCount[startLine]
+ pos
- (state.bMarks[startLine] + state.tShift[startLine])
)
while pos < maximum:
ch = state.src[pos]
if ch == "\t":
offset += 4 - offset % 4
elif ch == " ":
offset += 1
else:
break
pos += 1
state.tShift[startLine] = pos - posAfterColon
state.sCount[startLine] = offset - initial
state.bMarks[startLine] = posAfterColon
state.blkIndent += 4
state.parentType = "footnote"
if state.sCount[startLine] < state.blkIndent:
state.sCount[startLine] += state.blkIndent
state.md.block.tokenize(state, startLine, endLine)
state.parentType = oldParentType
state.blkIndent -= 4
state.tShift[startLine] = oldTShift
state.sCount[startLine] = oldSCount
state.bMarks[startLine] = oldBMark
open_token.map = [startLine, state.line]
token = Token("footnote_reference_close", "", -1)
state.level -= 1
token.level = state.level
state.tokens.append(token)
return True
def footnote_inline(state: StateInline, silent: bool) -> bool:
"""Process inline footnotes (^[...])"""
maximum = state.posMax
start = state.pos
if start + 2 >= maximum:
return False
if state.src[start] != "^":
return False
if state.src[start + 1] != "[":
return False
labelStart = start + 2
labelEnd = parseLinkLabel(state, start + 1)
# parser failed to find ']', so it's not a valid note
if labelEnd < 0:
return False
# We found the end of the link, and know for a fact it's a valid link
# so all that's left to do is to call tokenizer.
#
if not silent:
refs = state.env.setdefault("footnotes", {}).setdefault("list", {})
footnoteId = len(refs)
tokens: List[Token] = []
state.md.inline.parse(
state.src[labelStart:labelEnd], state.md, state.env, tokens
)
token = state.push("footnote_ref", "", 0)
token.meta = {"id": footnoteId}
refs[footnoteId] = {"content": state.src[labelStart:labelEnd], "tokens": tokens}
state.pos = labelEnd + 1
state.posMax = maximum
return True
def footnote_ref(state: StateInline, silent: bool) -> bool:
"""Process footnote references ([^...])"""
maximum = state.posMax
start = state.pos
# should be at least 4 chars - "[^x]"
if start + 3 > maximum:
return False
if "footnotes" not in state.env or "refs" not in state.env["footnotes"]:
return False
if state.src[start] != "[":
return False
if state.src[start + 1] != "^":
return False
pos = start + 2
while pos < maximum:
if state.src[pos] == " ":
return False
if state.src[pos] == "\n":
return False
if state.src[pos] == "]":
break
pos += 1
if pos == start + 2: # no empty footnote labels
return False
if pos >= maximum:
return False
pos += 1
label = state.src[start + 2 : pos - 1]
if (":" + label) not in state.env["footnotes"]["refs"]:
return False
if not silent:
if "list" not in state.env["footnotes"]:
state.env["footnotes"]["list"] = {}
if state.env["footnotes"]["refs"][":" + label] < 0:
footnoteId = len(state.env["footnotes"]["list"])
state.env["footnotes"]["list"][footnoteId] = {"label": label, "count": 0}
state.env["footnotes"]["refs"][":" + label] = footnoteId
else:
footnoteId = state.env["footnotes"]["refs"][":" + label]
footnoteSubId = state.env["footnotes"]["list"][footnoteId]["count"]
state.env["footnotes"]["list"][footnoteId]["count"] += 1
token = state.push("footnote_ref", "", 0)
token.meta = {"id": footnoteId, "subId": footnoteSubId, "label": label}
state.pos = pos
state.posMax = maximum
return True
def footnote_tail(state: StateCore) -> None:
"""Post-processing step, to move footnote tokens to end of the token stream.
Also removes un-referenced tokens.
"""
insideRef = False
refTokens = {}
if "footnotes" not in state.env:
return
current: List[Token] = []
tok_filter = []
for tok in state.tokens:
if tok.type == "footnote_reference_open":
insideRef = True
current = []
currentLabel = tok.meta["label"]
tok_filter.append(False)
continue
if tok.type == "footnote_reference_close":
insideRef = False
# prepend ':' to avoid conflict with Object.prototype members
refTokens[":" + currentLabel] = current
tok_filter.append(False)
continue
if insideRef:
current.append(tok)
tok_filter.append((not insideRef))
state.tokens = [t for t, f in zip(state.tokens, tok_filter) if f]
if "list" not in state.env.get("footnotes", {}):
return
foot_list = state.env["footnotes"]["list"]
token = Token("footnote_block_open", "", 1)
state.tokens.append(token)
for i, foot_note in foot_list.items():
token = Token("footnote_open", "", 1)
token.meta = {"id": i, "label": foot_note.get("label", None)}
# TODO propagate line positions of original foot note
# (but don't store in token.map, because this is used for scroll syncing)
state.tokens.append(token)
if "tokens" in foot_note:
tokens = []
token = Token("paragraph_open", "p", 1)
token.block = True
tokens.append(token)
token = Token("inline", "", 0)
token.children = foot_note["tokens"]
token.content = foot_note["content"]
tokens.append(token)
token = Token("paragraph_close", "p", -1)
token.block = True
tokens.append(token)
elif "label" in foot_note:
tokens = refTokens[":" + foot_note["label"]]
state.tokens.extend(tokens)
if state.tokens[len(state.tokens) - 1].type == "paragraph_close":
lastParagraph: Optional[Token] = state.tokens.pop()
else:
lastParagraph = None
t = (
foot_note["count"]
if (("count" in foot_note) and (foot_note["count"] > 0))
else 1
)
j = 0
while j < t:
token = Token("footnote_anchor", "", 0)
token.meta = {"id": i, "subId": j, "label": foot_note.get("label", None)}
state.tokens.append(token)
j += 1
if lastParagraph:
state.tokens.append(lastParagraph)
token = Token("footnote_close", "", -1)
state.tokens.append(token)
token = Token("footnote_block_close", "", -1)
state.tokens.append(token)
########################################
# Renderer partials
def render_footnote_anchor_name(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
n = str(tokens[idx].meta["id"] + 1)
prefix = ""
doc_id = env.get("docId", None)
if isinstance(doc_id, str):
prefix = f"-{doc_id}-"
return prefix + n
def render_footnote_caption(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
n = str(tokens[idx].meta["id"] + 1)
if tokens[idx].meta.get("subId", -1) > 0:
n += ":" + str(tokens[idx].meta["subId"])
return "[" + n + "]"
def render_footnote_ref(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
ident: str = self.rules["footnote_anchor_name"](tokens, idx, options, env) # type: ignore[attr-defined]
caption: str = self.rules["footnote_caption"](tokens, idx, options, env) # type: ignore[attr-defined]
refid = ident
if tokens[idx].meta.get("subId", -1) > 0:
refid += ":" + str(tokens[idx].meta["subId"])
return (
''
+ caption
+ ""
)
def render_footnote_block_open(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
return (
(
'\n'
if options.xhtmlOut
else '\n'
)
+ '\n'
+ '\n'
)
def render_footnote_block_close(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
return "\n\n"
def render_footnote_open(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
ident: str = self.rules["footnote_anchor_name"](tokens, idx, options, env) # type: ignore[attr-defined]
if tokens[idx].meta.get("subId", -1) > 0:
ident += ":" + tokens[idx].meta["subId"]
return '
\n"
def render_footnote_anchor(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
ident: str = self.rules["footnote_anchor_name"](tokens, idx, options, env) # type: ignore[attr-defined]
if tokens[idx].meta["subId"] > 0:
ident += ":" + str(tokens[idx].meta["subId"])
# ↩ with escape code to prevent display as Apple Emoji on iOS
return ' \u21a9\uFE0E'
mdit-py-plugins-0.4.0/mdit_py_plugins/footnote/port.yaml 0000664 0000000 0000000 00000000170 14437433217 0023476 0 ustar 00root root 0000000 0000000 - package: markdown-it-footnote
commit: cab6665ba39c6eb517cbbae3baeb549004bf740c
date: Jul 9, 2019
version: 3.0.2
mdit-py-plugins-0.4.0/mdit_py_plugins/front_matter/ 0000775 0000000 0000000 00000000000 14437433217 0022477 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/front_matter/LICENSE 0000664 0000000 0000000 00000002040 14437433217 0023500 0 ustar 00root root 0000000 0000000 Copyright (c) 2016-2020 ParkSB.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mdit-py-plugins-0.4.0/mdit_py_plugins/front_matter/__init__.py 0000664 0000000 0000000 00000000065 14437433217 0024611 0 ustar 00root root 0000000 0000000 from .index import front_matter_plugin # noqa: F401
mdit-py-plugins-0.4.0/mdit_py_plugins/front_matter/index.py 0000664 0000000 0000000 00000006437 14437433217 0024172 0 ustar 00root root 0000000 0000000 """Process front matter."""
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
def front_matter_plugin(md: MarkdownIt) -> None:
"""Plugin ported from
`markdown-it-front-matter `__.
It parses initial metadata, stored between opening/closing dashes:
.. code-block:: md
---
valid-front-matter: true
---
"""
md.block.ruler.before(
"table",
"front_matter",
_front_matter_rule,
{"alt": ["paragraph", "reference", "blockquote", "list"]},
)
def _front_matter_rule(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
marker_chr = "-"
min_markers = 3
auto_closed = False
start = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
src_len = len(state.src)
# Check out the first character of the first line quickly,
# this should filter out non-front matter
if startLine != 0 or state.src[0] != marker_chr:
return False
# Check out the rest of the marker string
# while pos <= 3
pos = start + 1
while pos <= maximum and pos < src_len:
if state.src[pos] != marker_chr:
break
pos += 1
marker_count = pos - start
if marker_count < min_markers:
return False
# Since start is found, we can report success here in validation mode
if silent:
return True
# Search for the end of the block
nextLine = startLine
while True:
nextLine += 1
if nextLine >= endLine:
# unclosed block should be autoclosed by end of document.
return False
if state.src[start:maximum] == "...":
break
start = state.bMarks[nextLine] + state.tShift[nextLine]
maximum = state.eMarks[nextLine]
if start < maximum and state.sCount[nextLine] < state.blkIndent:
# non-empty line with negative indent should stop the list:
# - ```
# test
break
if state.src[start] != marker_chr:
continue
if is_code_block(state, nextLine):
continue
pos = start + 1
while pos < maximum:
if state.src[pos] != marker_chr:
break
pos += 1
# closing code fence must be at least as long as the opening one
if (pos - start) < marker_count:
continue
# make sure tail has spaces only
pos = state.skipSpaces(pos)
if pos < maximum:
continue
# found!
auto_closed = True
break
old_parent = state.parentType
old_line_max = state.lineMax
state.parentType = "container"
# this will prevent lazy continuations from ever going past our end marker
state.lineMax = nextLine
token = state.push("front_matter", "", 0)
token.hidden = True
token.markup = marker_chr * min_markers
token.content = state.src[state.bMarks[startLine + 1] : state.eMarks[nextLine - 1]]
token.block = True
state.parentType = old_parent
state.lineMax = old_line_max
state.line = nextLine + (1 if auto_closed else 0)
token.map = [startLine, state.line]
return True
mdit-py-plugins-0.4.0/mdit_py_plugins/front_matter/port.yaml 0000664 0000000 0000000 00000000174 14437433217 0024351 0 ustar 00root root 0000000 0000000 - package: markdown-it-front-matter
commit: b404f5d8fd536e7e9ddb276267ae0b6f76e9cf9d
date: Feb 7, 2020
version: 0.2.1
mdit-py-plugins-0.4.0/mdit_py_plugins/myst_blocks/ 0000775 0000000 0000000 00000000000 14437433217 0022324 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/myst_blocks/__init__.py 0000664 0000000 0000000 00000000063 14437433217 0024434 0 ustar 00root root 0000000 0000000 from .index import myst_block_plugin # noqa: F401
mdit-py-plugins-0.4.0/mdit_py_plugins/myst_blocks/index.py 0000664 0000000 0000000 00000010730 14437433217 0024006 0 ustar 00root root 0000000 0000000 from __future__ import annotations
import itertools
from typing import TYPE_CHECKING, Sequence
from markdown_it import MarkdownIt
from markdown_it.common.utils import escapeHtml
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
def myst_block_plugin(md: MarkdownIt) -> None:
"""Parse MyST targets (``(name)=``), blockquotes (``% comment``) and block breaks (``+++``)."""
md.block.ruler.before(
"blockquote",
"myst_line_comment",
line_comment,
{"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]},
)
md.block.ruler.before(
"hr",
"myst_block_break",
block_break,
{"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]},
)
md.block.ruler.before(
"hr",
"myst_target",
target,
{"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]},
)
md.add_render_rule("myst_target", render_myst_target)
md.add_render_rule("myst_line_comment", render_myst_line_comment)
def line_comment(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
if is_code_block(state, startLine):
return False
pos = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
if state.src[pos] != "%":
return False
if silent:
return True
token = state.push("myst_line_comment", "", 0)
token.attrSet("class", "myst-line-comment")
token.content = state.src[pos + 1 : maximum].rstrip()
token.markup = "%"
# search end of block while appending lines to `token.content`
for nextLine in itertools.count(startLine + 1):
if nextLine >= endLine:
break
pos = state.bMarks[nextLine] + state.tShift[nextLine]
maximum = state.eMarks[nextLine]
if state.src[pos] != "%":
break
token.content += "\n" + state.src[pos + 1 : maximum].rstrip()
state.line = nextLine
token.map = [startLine, nextLine]
return True
def block_break(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
if is_code_block(state, startLine):
return False
pos = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
marker = state.src[pos]
pos += 1
# Check block marker
if marker != "+":
return False
# markers can be mixed with spaces, but there should be at least 3 of them
cnt = 1
while pos < maximum:
ch = state.src[pos]
if ch != marker and ch not in ("\t", " "):
break
if ch == marker:
cnt += 1
pos += 1
if cnt < 3:
return False
if silent:
return True
state.line = startLine + 1
token = state.push("myst_block_break", "hr", 0)
token.attrSet("class", "myst-block")
token.content = state.src[pos:maximum].strip()
token.map = [startLine, state.line]
token.markup = marker * cnt
return True
def target(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
if is_code_block(state, startLine):
return False
pos = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
text = state.src[pos:maximum].strip()
if not text.startswith("("):
return False
if not text.endswith(")="):
return False
if not text[1:-2]:
return False
if silent:
return True
state.line = startLine + 1
token = state.push("myst_target", "", 0)
token.attrSet("class", "myst-target")
token.content = text[1:-2]
token.map = [startLine, state.line]
return True
def render_myst_target(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
label = tokens[idx].content
class_name = "myst-target"
target = f'({label})='
return f'
{target}
'
def render_myst_line_comment(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
# Strip leading whitespace from all lines
content = "\n".join(line.lstrip() for line in tokens[idx].content.split("\n"))
return f""
mdit-py-plugins-0.4.0/mdit_py_plugins/myst_role/ 0000775 0000000 0000000 00000000000 14437433217 0022010 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/myst_role/__init__.py 0000664 0000000 0000000 00000000062 14437433217 0024117 0 ustar 00root root 0000000 0000000 from .index import myst_role_plugin # noqa: F401
mdit-py-plugins-0.4.0/mdit_py_plugins/myst_role/index.py 0000664 0000000 0000000 00000003763 14437433217 0023502 0 ustar 00root root 0000000 0000000 import re
from typing import TYPE_CHECKING, Sequence
from markdown_it import MarkdownIt
from markdown_it.common.utils import escapeHtml
from markdown_it.rules_inline import StateInline
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
VALID_NAME_PATTERN = re.compile(r"^\{([a-zA-Z0-9\_\-\+\:]+)\}")
def myst_role_plugin(md: MarkdownIt) -> None:
"""Parse ``{role-name}`content```"""
md.inline.ruler.before("backticks", "myst_role", myst_role)
md.add_render_rule("myst_role", render_myst_role)
def myst_role(state: StateInline, silent: bool) -> bool:
# check name
match = VALID_NAME_PATTERN.match(state.src[state.pos :])
if not match:
return False
name = match.group(1)
# check for starting backslash escape
try:
if state.src[state.pos - 1] == "\\":
# escaped (this could be improved in the case of edge case '\\{')
return False
except IndexError:
pass
# scan opening tick length
start = pos = state.pos + match.end()
try:
while state.src[pos] == "`":
pos += 1
except IndexError:
return False
tick_length = pos - start
if not tick_length:
return False
# search for closing ticks
match = re.search("`" * tick_length, state.src[pos + 1 :])
if not match:
return False
content = state.src[pos : pos + match.start() + 1].replace("\n", " ")
if not silent:
token = state.push("myst_role", "", 0)
token.meta = {"name": name}
token.content = content
state.pos = pos + match.end() + 1
return True
def render_myst_role(
self: "RendererProtocol",
tokens: Sequence["Token"],
idx: int,
options: "OptionsDict",
env: "EnvType",
) -> str:
token = tokens[idx]
name = token.meta.get("name", "unknown")
return f'{{{name}}}[{escapeHtml(token.content)}]'
mdit-py-plugins-0.4.0/mdit_py_plugins/py.typed 0000664 0000000 0000000 00000000032 14437433217 0021465 0 ustar 00root root 0000000 0000000 # Marker file for PEP 561
mdit-py-plugins-0.4.0/mdit_py_plugins/substitution.py 0000664 0000000 0000000 00000006062 14437433217 0023125 0 ustar 00root root 0000000 0000000 from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from markdown_it.rules_inline import StateInline
from mdit_py_plugins.utils import is_code_block
def substitution_plugin(
md: MarkdownIt, start_delimiter: str = "{", end_delimiter: str = "}"
) -> None:
"""A plugin to create substitution tokens.
These, token should be handled by the renderer.
Example::
{{ block }}
a {{ inline }} b
"""
def _substitution_inline(state: StateInline, silent: bool) -> bool:
try:
if (
state.src[state.pos] != start_delimiter
or state.src[state.pos + 1] != start_delimiter
):
return False
except IndexError:
return False
pos = state.pos + 2
found_closing = False
while True:
try:
end = state.src.index(end_delimiter, pos)
except ValueError:
return False
try:
if state.src[end + 1] == end_delimiter:
found_closing = True
break
except IndexError:
return False
pos = end + 2
if not found_closing:
return False
text = state.src[state.pos + 2 : end].strip()
state.pos = end + 2
if silent:
return True
token = state.push("substitution_inline", "span", 0)
token.block = False
token.content = text
token.attrSet("class", "substitution")
token.attrSet("text", text)
token.markup = f"{start_delimiter}{end_delimiter}"
return True
def _substitution_block(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
if is_code_block(state, startLine):
return False
startPos = state.bMarks[startLine] + state.tShift[startLine]
end = state.eMarks[startLine]
lineText = state.src[startPos:end].strip()
try:
if (
lineText[0] != start_delimiter
or lineText[1] != start_delimiter
or lineText[-1] != end_delimiter
or lineText[-2] != end_delimiter
or len(lineText) < 5
):
return False
except IndexError:
return False
text = lineText[2:-2].strip()
# special case if multiple on same line, e.g. {{a}}{{b}}
if (end_delimiter * 2) in text:
return False
state.line = startLine + 1
if silent:
return True
token = state.push("substitution_block", "div", 0)
token.block = True
token.content = text
token.attrSet("class", "substitution")
token.attrSet("text", text)
token.markup = f"{start_delimiter}{end_delimiter}"
token.map = [startLine, state.line]
return True
md.block.ruler.before("fence", "substitution_block", _substitution_block)
md.inline.ruler.before("escape", "substitution_inline", _substitution_inline)
mdit-py-plugins-0.4.0/mdit_py_plugins/tasklists/ 0000775 0000000 0000000 00000000000 14437433217 0022014 5 ustar 00root root 0000000 0000000 mdit-py-plugins-0.4.0/mdit_py_plugins/tasklists/__init__.py 0000664 0000000 0000000 00000013206 14437433217 0024127 0 ustar 00root root 0000000 0000000 """Builds task/todo lists out of markdown lists with items starting with [ ] or [x]"""
# Ported by Wolmar Nyberg Åkerström from https://github.com/revin/markdown-it-task-lists
# ISC License
# Copyright (c) 2016, Revin Guillen
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from __future__ import annotations
import re
from uuid import uuid4
from markdown_it import MarkdownIt
from markdown_it.rules_core import StateCore
from markdown_it.token import Token
# Regex string to match a whitespace character, as specified in
# https://github.github.com/gfm/#whitespace-character
# (spec version 0.29-gfm (2019-04-06))
_GFM_WHITESPACE_RE = r"[ \t\n\v\f\r]"
def tasklists_plugin(
md: MarkdownIt,
enabled: bool = False,
label: bool = False,
label_after: bool = False,
) -> None:
"""Plugin for building task/todo lists out of markdown lists with items starting with [ ] or [x]
.. Nothing else
For example::
- [ ] An item that needs doing
- [x] An item that is complete
The rendered HTML checkboxes are disabled; to change this, pass a truthy value into the enabled
property of the plugin options.
:param enabled: True enables the rendered checkboxes
:param label: True wraps the rendered list items in a
.
Indented by 4 spaces
.
\begin{equation}
a = 1
\end{equation}
.
\begin{equation}
a = 1
\end{equation}
.
Indented by 4 spaces, DISABLE-CODEBLOCKS
.
\begin{equation}
a = 1
\end{equation}
.
.
mdit-py-plugins-0.4.0/tests/fixtures/attrs.md 0000664 0000000 0000000 00000005530 14437433217 0021262 0 ustar 00root root 0000000 0000000 block with preceding text is not a block
.
{#a .a b=c} a
.
{#a .a b=c} a
.
block no preceding
.
{#a .a c=1}
.
.
block basic
.
{#a .a c=1}
a
.
a
.
multiple blocks
.
{#a .a c=1}
{#b .b c=2}
a
.
a
.
block list
.
{#a .a c=1}
- a
.
a
.
block quote
.
{#a .a c=1}
> a
.
a
.
block fence
.
{#a .b c=1}
```python
a = 1
```
.
a = 1
.
block after paragraph
.
a
{#a .a c=1}
.
a
{#a .a c=1}
.
simple reference link
.
[text *emphasis*](a){#id .a}
.
.
Indented by 4 spaces, DISABLE-CODEBLOCKS
.
{#a .a b=c}
# head
.
head
.
mdit-py-plugins-0.4.0/tests/fixtures/colon_fence.md 0000664 0000000 0000000 00000010244 14437433217 0022375 0 ustar 00root root 0000000 0000000 # The initial tests are adapted from the test for normal code fences in tests/test_port/fixtures/commonmark_spec.md
src line: 1638
.
:::
<
>
:::
.
.
math-escaping: double-escaped end $:
.
$p_2 = \\$a
.
p_2 = \\a
.
Inline double-dollar start:
.
$$a=1$$ b
.
a=1
b
.
Inline double-dollar end:
.
a $$a=1$$
.
a
a=1
.
Inline double-dollar enclosed:
.
a $$a=1$$ (1) b
.
a
a=1
(1) b
.
Inline double-dollar, escaped:
.
a \$$a=1$$ b
.
a $a=1$ b
.
Inline mixed single/double dollars:
.
Hence, for $\alpha \in (0, 1)$,
$$
\mathbb P (\alpha \bar{X} \ge \mu) \le \alpha;
$$
i.e., $[\alpha \bar{X}, \infty)$ is a lower 1-sided $1-\alpha$ confidence bound for $\mu$.
.
Hence, for \alpha \in (0, 1),
\mathbb P (\alpha \bar{X} \ge \mu) \le \alpha;
i.e., [\alpha \bar{X}, \infty) is a lower 1-sided 1-\alpha confidence bound for \mu.
.
display equation with label containing whitespace. (valid=True)
.
$$1+1=2$$ (a b)
.
.
Indented by 4 spaces, DISABLE-CODEBLOCKS
.
$$a$$
.
a
.
mdit-py-plugins-0.4.0/tests/fixtures/field_list.md 0000664 0000000 0000000 00000010541 14437433217 0022241 0 ustar 00root root 0000000 0000000 Docutils example
.
:Date: 2001-08-16
:Version: 1
:Authors: - Me
- Myself
- I
:Indentation: Since the field marker may be quite long, the second
and subsequent lines of the field body do not have to line up
with the first line, but they must be indented relative to the
field name marker, and they must line up with each other.
:Parameter i: integer
.
Date
2001-08-16
Version
1
Authors
Me
Myself
I
Indentation
Since the field marker may be quite long, the second
and subsequent lines of the field body do not have to line up
with the first line, but they must be indented relative to the
field name marker, and they must line up with each other.
Parameter i
integer
.
Body alignment:
.
:no body:
:single line: content
:paragraph: content
running onto new line
:body inline: paragraph 1
paragraph 2
paragraph 3
:body less: paragraph 1
paragraph 2
paragraph 3
:body on 2nd line:
paragraph 1
paragraph 2
:body on 3rd line:
paragraph 1
paragraph 2
.
no body
single line
content
paragraph
content
running onto new line
body inline
paragraph 1
paragraph 2
paragraph 3
body less
paragraph 1
paragraph 2
paragraph 3
body on 2nd line
paragraph 1
paragraph 2
body on 3rd line
paragraph 1
paragraph 2
.
choose smallest indent
.
:name: a
b
c
.
name
a
b
c
.
Empty name:
.
::
.
::
.
Whitespace only name:
.
: :
.
: :
.
Name markup:
.
:inline *markup*:
.
inline markup
.
Content paragraph markup:
.
:name: body *markup*
.
.
Following blocks:
.
:name: body
- item 1
:name: body
> block quote
:name: body
```python
code
```
:name: body
more
more
trailing
other
.
name
body
item 1
name
body
block quote
name
body
code
name
body
more
more
trailing
other
.
In list:
.
- :name: body
- item 2
.
name
body
item 2
.
In blockquote:
.
> :name: body
> :name: body
> other
> :name: body
>
> other
> :name: body
>
> other
.
name
body
name
body
other
name
body
other
name
body
other
.
Indented by 4 spaces
.
:name: text
indented
.
:name: text
indented
.
Indented by 4 spaces, DISABLE-CODEBLOCKS
.
:name: text
indented
.
name
text
indented
.
mdit-py-plugins-0.4.0/tests/fixtures/footnote.md 0000664 0000000 0000000 00000020227 14437433217 0021762 0 ustar 00root root 0000000 0000000
Pandoc example:
.
Here is a footnote reference,[^1] and another.[^longnote]
[^1]: Here is the footnote.
[^longnote]: Here's one with multiple blocks.
Subsequent paragraphs are indented to show that they
belong to the previous footnote.
{ some.code }
The whole paragraph can be indented, or just the first
line. In this way, multi-paragraph footnotes work like
multi-paragraph list items.
This paragraph won't be part of the note, because it
isn't indented.
.
.
Their labels could not contain spaces or newlines:
.
[^ foo]: bar baz
[^foo
]: bar baz
.
[^ foo]: bar baz
[^foo
]: bar baz
.
We support inline notes too (pandoc example):
.
Here is an inline note.^[Inlines notes are easier to write, since
you don't have to pick an identifier and move down to type the
note.]
.
.
mdit-py-plugins-0.4.0/tests/fixtures/front_matter.md 0000664 0000000 0000000 00000001551 14437433217 0022630 0 ustar 00root root 0000000 0000000
should parse empty front matter:
.
---
---
# Head
.
Head
.
should parse basic front matter:
.
---
x: 1
---
# Head
.
Head
.
should parse until triple dots:
.
---
x: 1
...
# Head
.
Head
.
should parse front matter with indentation:
.
---
title: Associative arrays
people:
name: John Smith
age: 33
morePeople: { name: Grace Jones, age: 21 }
---
# Head
.
Head
.
should ignore spaces after front matter delimiters:
.
---
title: Associative arrays
people:
name: John Smith
age: 33
morePeople: { name: Grace Jones, age: 21 }
---
# Head
.
Head
.
should ignore front matter with less than 3 opening dashes:
.
--
x: 1
--
# Head
.
--
x: 1
Head
.
should require front matter have matching number of opening and closing dashes:
.
----
x: 1
---
# Head
.
.
dirty.md:
.
- [ ] unchecked todo item 1
- [ ]
- [ ] not a todo item 2
- [ x] not a todo item 3
- [x ] not a todo item 4
- [ x ] not a todo item 5
- [x] todo item 6
.
unchecked todo item 1
[ ]
[ ] not a todo item 2
[ x] not a todo item 3
[x ] not a todo item 4
[ x ] not a todo item 5
todo item 6
.
mixed-nested.md:
.
# Test 1
1. foo
* [ ] nested unchecked item 1
* not a todo item 2
* not a todo item 3
* [x] nested checked item 4
2. bar
3. spam
# Test 2
- foo
- [ ] nested unchecked item 1
- [ ] nested unchecked item 2
- [x] nested checked item 3
- [X] nested checked item 4
.
.
conjugate complex (valid=True)
.
\(a^\*b\) with \(a^\*\)
.
a^\*b with a^\*
.
Inline multi-line (valid=True)
.
a \(a
\not=1\) b
.
a a
\not=1 b
.
Inline multi-line with newline (valid=False)
.
a \(a
\not=1\) b
.
a (a
\not=1) b
.
single block equation, greek index (valid=True)
.
\[e_\\alpha\]
.
e_\\alpha
.
display equation on its own single line. (valid=True)
.
\[1+1=2\]
.
1+1=2
.
inline equation followed by block equation. (valid=True)
.
\({e}_x\)
\[e_\\alpha\]
.
{e}_x
e_\\alpha
.
underline tests (valid=True)
.
\[c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha\]
.
c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha
.
non-numeric character before opening $ or
after closing $ or both is allowed. (valid=True)
.
a\(1+1=2\)
\(1+1=2\)b
c\(x\)d
.
a1+1=21+1=2b
cxd
.
following dollar character '$' is allowed. (valid=True)
.
\(x\) $
.
.
conjugate complex (valid=True)
.
$a^\*b$ with $a^\*$
.
a^\*b with a^\*
.
Inline multi-line (valid=True)
.
a $a
\not=1$ b
.
a a
\not=1 b
.
Inline multi-line with newline (valid=False)
.
a $a
\not=1$ b
.
a $a
\not=1$ b
.
single block equation, greek index (valid=True)
.
$$e_\\alpha$$
.
e_\\alpha
.
display equation on its own single line. (valid=True)
.
$$1+1=2$$
.
1+1=2
.
inline equation followed by block equation. (valid=True)
.
${e}_x$
$$e_\\alpha$$
.
{e}_x
e_\\alpha
.
underline tests (valid=True)
.
$$c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha$$
.
c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha
.
non-numeric character before opening $ or
after closing $ or both is allowed. (valid=True)
.
a$1+1=2$
$1+1=2$b
c$x$d
.
a1+1=21+1=2b
cxd
.
following dollar character '$' is allowed. (valid=True)
.
$x$ $
.