Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ NEW: Port admon plugin #53

Merged
merged 14 commits into from
Dec 5, 2022
Merged
4 changes: 4 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ html_string = md.render("some *Markdown*")
.. autofunction:: mdit_py_plugins.container.container_plugin
```

```{eval-rst}
.. autofunction:: mdit_py_plugins.admon.admon_plugin
```

## Inline Attributes

```{eval-rst}
Expand Down
24 changes: 24 additions & 0 deletions mdit_py_plugins/admon/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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
KyleKing marked this conversation as resolved.
Show resolved Hide resolved
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.
1 change: 1 addition & 0 deletions mdit_py_plugins/admon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .index import admon_plugin # noqa: F401
172 changes: 172 additions & 0 deletions mdit_py_plugins/admon/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Process admonitions and pass to cb.

import math
from typing import Callable, Optional, Tuple

from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock


def get_tag(params: str) -> Tuple[str, str]:
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:
tag = params.strip().split(" ", 1)[-1] or ""
return bool(tag)


MIN_MARKERS = 3
MARKER_STR = "!"
MARKER_CHAR = ord(MARKER_STR)
MARKER_LEN = len(MARKER_STR)


def admonition(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
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 MARKER_CHAR != ord(state.src[start]):
return False

# Check out the rest of the marker string
pos = start + 1
while pos <= maximum and MARKER_STR[(pos - start) % MARKER_LEN] == state.src[pos]:
pos += 1

marker_count = math.floor((pos - start) / MARKER_LEN)
if marker_count < MIN_MARKERS:
return False
marker_pos = pos - ((pos - start) % MARKER_LEN)
params = state.src[marker_pos:maximum]
markup = state.src[start:marker_pos]

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 = pos
while blk_start < maximum and state.src[blk_start] == " ":
blk_start += 1

state.parentType = "admonition"
state.blkIndent += blk_start - start

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": f"admonition {tag}"}
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)
token.markup = title_markup

state.md.block.tokenize(state, startLine + 1, next_line)

token = state.push("admonition_close", "div", -1)
token.markup = state.src[start:pos]
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: Optional[Callable] = None) -> None:
"""Plugin to use
`python-markdown style admonitions
<https://python-markdown.github.io/extensions/admonition>`_.

.. code-block:: md

!!! note
*content*

Note, this is ported from
`markdown-it-admon
<https://github.com/commenthol/markdown-it-admon>`_.
"""

def renderDefault(self, tokens, idx, _options, env):
return self.renderToken(tokens, idx, _options, env)

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"]},
)
4 changes: 4 additions & 0 deletions mdit_py_plugins/admon/port.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- package: markdown-it-admon
commit: 9820ba89415c464a3cc18a780f222a0ceb3e18bd
date: Jul 3, 2021
version: 1.0.0
Loading