Building Project Files
هذا الالتزام موجود في:
@@ -0,0 +1,50 @@
|
||||
from importlib import import_module
|
||||
from typing import TYPE_CHECKING, Dict, Protocol, Union, cast
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..markdown import Markdown
|
||||
|
||||
_plugins = {
|
||||
"speedup": "mistune.plugins.speedup.speedup",
|
||||
"strikethrough": "mistune.plugins.formatting.strikethrough",
|
||||
"mark": "mistune.plugins.formatting.mark",
|
||||
"insert": "mistune.plugins.formatting.insert",
|
||||
"superscript": "mistune.plugins.formatting.superscript",
|
||||
"subscript": "mistune.plugins.formatting.subscript",
|
||||
"footnotes": "mistune.plugins.footnotes.footnotes",
|
||||
"table": "mistune.plugins.table.table",
|
||||
"url": "mistune.plugins.url.url",
|
||||
"abbr": "mistune.plugins.abbr.abbr",
|
||||
"def_list": "mistune.plugins.def_list.def_list",
|
||||
"math": "mistune.plugins.math.math",
|
||||
"ruby": "mistune.plugins.ruby.ruby",
|
||||
"task_lists": "mistune.plugins.task_lists.task_lists",
|
||||
"spoiler": "mistune.plugins.spoiler.spoiler",
|
||||
}
|
||||
|
||||
|
||||
class Plugin(Protocol):
|
||||
def __call__(self, md: "Markdown") -> None: ...
|
||||
|
||||
|
||||
_cached_modules: Dict[str, Plugin] = {}
|
||||
|
||||
PluginRef = Union[str, Plugin] # reference to register a plugin
|
||||
|
||||
|
||||
def import_plugin(name: PluginRef) -> Plugin:
|
||||
if callable(name):
|
||||
return name
|
||||
|
||||
if name in _cached_modules:
|
||||
return _cached_modules[name]
|
||||
|
||||
if name in _plugins:
|
||||
module_path, func_name = _plugins[name].rsplit(".", 1)
|
||||
else:
|
||||
module_path, func_name = name.rsplit(".", 1)
|
||||
|
||||
module = import_module(module_path)
|
||||
plugin = cast(Plugin, getattr(module, func_name))
|
||||
_cached_modules[name] = plugin
|
||||
return plugin
|
||||
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
ملف ثنائي غير معروض.
109
venv/lib/python3.12/site-packages/mistune/plugins/abbr.py
Normal file
109
venv/lib/python3.12/site-packages/mistune/plugins/abbr.py
Normal file
@@ -0,0 +1,109 @@
|
||||
import re
|
||||
import types
|
||||
from typing import TYPE_CHECKING, Match
|
||||
|
||||
from ..helpers import PREVENT_BACKSLASH
|
||||
from ..util import escape
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BaseRenderer, BlockState, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["abbr"]
|
||||
|
||||
# https://michelf.ca/projects/php-markdown/extra/#abbr
|
||||
REF_ABBR = (
|
||||
r"^ {0,3}\*\[(?P<abbr_key>[^\]]+)" + PREVENT_BACKSLASH + r"\]:"
|
||||
r"(?P<abbr_text>(?:[ \t]*\n(?: {3,}|\t)[^\n]+)|(?:[^\n]*))$"
|
||||
)
|
||||
|
||||
|
||||
def parse_ref_abbr(block: "BlockParser", m: Match[str], state: "BlockState") -> int:
|
||||
ref = state.env.get("ref_abbrs")
|
||||
if not ref:
|
||||
ref = {}
|
||||
key = m.group("abbr_key")
|
||||
text = m.group("abbr_text")
|
||||
ref[key] = text.strip()
|
||||
state.env["ref_abbrs"] = ref
|
||||
# abbr definition can split paragraph
|
||||
state.append_token({"type": "blank_line"})
|
||||
return m.end() + 1
|
||||
|
||||
|
||||
def process_text(inline: "InlineParser", text: str, state: "InlineState") -> None:
|
||||
ref = state.env.get("ref_abbrs")
|
||||
if not ref:
|
||||
return state.append_token({"type": "text", "raw": text})
|
||||
|
||||
if state.tokens:
|
||||
last = state.tokens[-1]
|
||||
if last["type"] == "text":
|
||||
state.tokens.pop()
|
||||
text = last["raw"] + text
|
||||
|
||||
abbrs_re = state.env.get("abbrs_re")
|
||||
if not abbrs_re:
|
||||
abbrs_re = re.compile(r"|".join(re.escape(k) for k in ref.keys()))
|
||||
state.env["abbrs_re"] = abbrs_re
|
||||
|
||||
pos = 0
|
||||
while pos < len(text):
|
||||
m = abbrs_re.search(text, pos)
|
||||
if not m:
|
||||
break
|
||||
|
||||
end_pos = m.start()
|
||||
if end_pos > pos:
|
||||
hole = text[pos:end_pos]
|
||||
state.append_token({"type": "text", "raw": hole})
|
||||
|
||||
label = m.group(0)
|
||||
state.append_token(
|
||||
{"type": "abbr", "children": [{"type": "text", "raw": label}], "attrs": {"title": ref[label]}}
|
||||
)
|
||||
pos = m.end()
|
||||
|
||||
if pos == 0:
|
||||
# special case, just pure text
|
||||
state.append_token({"type": "text", "raw": text})
|
||||
elif pos < len(text):
|
||||
state.append_token({"type": "text", "raw": text[pos:]})
|
||||
|
||||
|
||||
def render_abbr(renderer: "BaseRenderer", text: str, title: str) -> str:
|
||||
if not title:
|
||||
return "<abbr>" + text + "</abbr>"
|
||||
return '<abbr title="' + escape(title) + '">' + text + "</abbr>"
|
||||
|
||||
|
||||
def abbr(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support abbreviations, spec defined at
|
||||
https://michelf.ca/projects/php-markdown/extra/#abbr
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
The HTML specification
|
||||
is maintained by the W3C.
|
||||
|
||||
*[HTML]: Hyper Text Markup Language
|
||||
*[W3C]: World Wide Web Consortium
|
||||
|
||||
It will be converted into HTML:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
The <abbr title="Hyper Text Markup Language">HTML</abbr> specification
|
||||
is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.block.register("ref_abbr", REF_ABBR, parse_ref_abbr, before="paragraph")
|
||||
# replace process_text
|
||||
md.inline.process_text = types.MethodType(process_text, md.inline) # type: ignore[method-assign]
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("abbr", render_abbr)
|
||||
145
venv/lib/python3.12/site-packages/mistune/plugins/def_list.py
Normal file
145
venv/lib/python3.12/site-packages/mistune/plugins/def_list.py
Normal file
@@ -0,0 +1,145 @@
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Match
|
||||
|
||||
from ..util import strip_end
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BaseRenderer, BlockState
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["def_list"]
|
||||
|
||||
# https://michelf.ca/projects/php-markdown/extra/#def-list
|
||||
|
||||
DEF_PATTERN = (
|
||||
r"^(?P<def_list_head>(?:[^\n]+\n)+?)"
|
||||
r"\n?(?:"
|
||||
r"\:[ \t]+.*\n"
|
||||
r"(?:[^\n]+\n)*" # lazy continue line
|
||||
r"(?:(?:[ \t]*\n)*[ \t]+[^\n]+\n)*"
|
||||
r"(?:[ \t]*\n)*"
|
||||
r")+"
|
||||
)
|
||||
DEF_RE = re.compile(DEF_PATTERN, re.M)
|
||||
DD_START_RE = re.compile(r"^:[ \t]+", re.M)
|
||||
TRIM_RE = re.compile(r"^ {0,4}", re.M)
|
||||
HAS_BLANK_LINE_RE = re.compile(r"\n[ \t]*\n$")
|
||||
|
||||
|
||||
def parse_def_list(block: "BlockParser", m: Match[str], state: "BlockState") -> int:
|
||||
pos = m.end()
|
||||
children = list(_parse_def_item(block, m))
|
||||
|
||||
m2 = DEF_RE.match(state.src, pos)
|
||||
while m2:
|
||||
children.extend(list(_parse_def_item(block, m2)))
|
||||
pos = m2.end()
|
||||
m2 = DEF_RE.match(state.src, pos)
|
||||
|
||||
state.append_token(
|
||||
{
|
||||
"type": "def_list",
|
||||
"children": children,
|
||||
}
|
||||
)
|
||||
return pos
|
||||
|
||||
|
||||
def _parse_def_item(block: "BlockParser", m: Match[str]) -> Iterable[Dict[str, Any]]:
|
||||
head = m.group("def_list_head")
|
||||
for line in head.splitlines():
|
||||
yield {
|
||||
"type": "def_list_head",
|
||||
"text": line,
|
||||
}
|
||||
|
||||
src = m.group(0)
|
||||
end = len(head)
|
||||
|
||||
m2 = DD_START_RE.search(src, end)
|
||||
assert m2 is not None
|
||||
start = m2.start()
|
||||
prev_blank_line = src[end:start] == "\n"
|
||||
while m2:
|
||||
m2 = DD_START_RE.search(src, start + 1)
|
||||
if not m2:
|
||||
break
|
||||
|
||||
end = m2.start()
|
||||
text = src[start:end].replace(":", " ", 1)
|
||||
children = _process_text(block, text, prev_blank_line)
|
||||
prev_blank_line = bool(HAS_BLANK_LINE_RE.search(text))
|
||||
yield {
|
||||
"type": "def_list_item",
|
||||
"children": children,
|
||||
}
|
||||
start = end
|
||||
|
||||
text = src[start:].replace(":", " ", 1)
|
||||
children = _process_text(block, text, prev_blank_line)
|
||||
yield {
|
||||
"type": "def_list_item",
|
||||
"children": children,
|
||||
}
|
||||
|
||||
|
||||
def _process_text(block: "BlockParser", text: str, loose: bool) -> List[Any]:
|
||||
text = TRIM_RE.sub("", text)
|
||||
state = block.state_cls()
|
||||
state.process(strip_end(text))
|
||||
# use default list rules
|
||||
block.parse(state, block.list_rules)
|
||||
tokens = state.tokens
|
||||
if not loose and len(tokens) == 1 and tokens[0]["type"] == "paragraph":
|
||||
tokens[0]["type"] = "block_text"
|
||||
return tokens
|
||||
|
||||
|
||||
def render_def_list(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<dl>\n" + text + "</dl>\n"
|
||||
|
||||
|
||||
def render_def_list_head(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<dt>" + text + "</dt>\n"
|
||||
|
||||
|
||||
def render_def_list_item(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<dd>" + text + "</dd>\n"
|
||||
|
||||
|
||||
def def_list(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support def list, spec defined at
|
||||
https://michelf.ca/projects/php-markdown/extra/#def-list
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Apple
|
||||
: Pomaceous fruit of plants of the genus Malus in
|
||||
the family Rosaceae.
|
||||
|
||||
Orange
|
||||
: The fruit of an evergreen tree of the genus Citrus.
|
||||
|
||||
It will be converted into HTML:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<dl>
|
||||
<dt>Apple</dt>
|
||||
<dd>Pomaceous fruit of plants of the genus Malus in
|
||||
the family Rosaceae.</dd>
|
||||
|
||||
<dt>Orange</dt>
|
||||
<dd>The fruit of an evergreen tree of the genus Citrus.</dd>
|
||||
</dl>
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.block.register("def_list", DEF_PATTERN, parse_def_list, before="paragraph")
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("def_list", render_def_list)
|
||||
md.renderer.register("def_list_head", render_def_list_head)
|
||||
md.renderer.register("def_list_item", render_def_list_item)
|
||||
154
venv/lib/python3.12/site-packages/mistune/plugins/footnotes.py
Normal file
154
venv/lib/python3.12/site-packages/mistune/plugins/footnotes.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Match, Union
|
||||
|
||||
from ..core import BlockState
|
||||
from ..helpers import LINK_LABEL
|
||||
from ..util import unikey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BaseRenderer, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["footnotes"]
|
||||
|
||||
_PARAGRAPH_SPLIT = re.compile(r"\n{2,}")
|
||||
# https://michelf.ca/projects/php-markdown/extra/#footnotes
|
||||
REF_FOOTNOTE = (
|
||||
r"^(?P<footnote_lead> {0,3})"
|
||||
r"\[\^(?P<footnote_key>" + LINK_LABEL + r")]:[ \t]"
|
||||
r"(?P<footnote_text>[^\n]*(?:\n+|$)"
|
||||
r"(?:(?P=footnote_lead) {1,3}(?! )[^\n]*\n+)*"
|
||||
r")"
|
||||
)
|
||||
|
||||
INLINE_FOOTNOTE = r"\[\^(?P<footnote_key>" + LINK_LABEL + r")\]"
|
||||
|
||||
|
||||
def parse_inline_footnote(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
key = unikey(m.group("footnote_key"))
|
||||
ref = state.env.get("ref_footnotes")
|
||||
if ref and key in ref:
|
||||
notes = state.env.get("footnotes")
|
||||
if not notes:
|
||||
notes = []
|
||||
if key not in notes:
|
||||
notes.append(key)
|
||||
state.env["footnotes"] = notes
|
||||
state.append_token({"type": "footnote_ref", "raw": key, "attrs": {"index": notes.index(key) + 1}})
|
||||
else:
|
||||
state.append_token({"type": "text", "raw": m.group(0)})
|
||||
return m.end()
|
||||
|
||||
|
||||
def parse_ref_footnote(block: "BlockParser", m: Match[str], state: BlockState) -> int:
|
||||
ref = state.env.get("ref_footnotes")
|
||||
if not ref:
|
||||
ref = {}
|
||||
|
||||
key = unikey(m.group("footnote_key"))
|
||||
if key not in ref:
|
||||
ref[key] = m.group("footnote_text")
|
||||
state.env["ref_footnotes"] = ref
|
||||
return m.end()
|
||||
|
||||
|
||||
def parse_footnote_item(block: "BlockParser", key: str, index: int, state: BlockState) -> Dict[str, Any]:
|
||||
ref = state.env.get("ref_footnotes")
|
||||
if not ref:
|
||||
raise ValueError("Missing 'ref_footnotes'.")
|
||||
text = ref[key]
|
||||
|
||||
lines = text.splitlines()
|
||||
second_line = None
|
||||
for second_line in lines[1:]:
|
||||
if second_line:
|
||||
break
|
||||
|
||||
if second_line:
|
||||
spaces = len(second_line) - len(second_line.lstrip())
|
||||
pattern = re.compile(r"^ {" + str(spaces) + r",}", flags=re.M)
|
||||
text = pattern.sub("", text).strip()
|
||||
items = _PARAGRAPH_SPLIT.split(text)
|
||||
children = [{"type": "paragraph", "text": s} for s in items]
|
||||
else:
|
||||
text = text.strip()
|
||||
children = [{"type": "paragraph", "text": text}]
|
||||
return {"type": "footnote_item", "children": children, "attrs": {"key": key, "index": index}}
|
||||
|
||||
|
||||
def md_footnotes_hook(
|
||||
md: "Markdown", result: Union[str, List[Dict[str, Any]]], state: BlockState
|
||||
) -> Union[str, List[Dict[str, Any]]]:
|
||||
notes = state.env.get("footnotes")
|
||||
if not notes:
|
||||
return result
|
||||
|
||||
children = [parse_footnote_item(md.block, k, i + 1, state) for i, k in enumerate(notes)]
|
||||
state = BlockState()
|
||||
state.tokens = [{"type": "footnotes", "children": children}]
|
||||
output = md.render_state(state)
|
||||
return result + output # type: ignore[operator]
|
||||
|
||||
|
||||
def render_footnote_ref(renderer: "BaseRenderer", key: str, index: int) -> str:
|
||||
i = str(index)
|
||||
html = '<sup class="footnote-ref" id="fnref-' + i + '">'
|
||||
return html + '<a href="#fn-' + i + '">' + i + "</a></sup>"
|
||||
|
||||
|
||||
def render_footnotes(renderer: "BaseRenderer", text: str) -> str:
|
||||
return '<section class="footnotes">\n<ol>\n' + text + "</ol>\n</section>\n"
|
||||
|
||||
|
||||
def render_footnote_item(renderer: "BaseRenderer", text: str, key: str, index: int) -> str:
|
||||
i = str(index)
|
||||
back = '<a href="#fnref-' + i + '" class="footnote">↩</a>'
|
||||
text = text.rstrip()[:-4] + back + "</p>"
|
||||
return '<li id="fn-' + i + '">' + text + "</li>\n"
|
||||
|
||||
|
||||
def footnotes(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support footnotes, spec defined at
|
||||
https://michelf.ca/projects/php-markdown/extra/#footnotes
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
That's some text with a footnote.[^1]
|
||||
|
||||
[^1]: And that's the footnote.
|
||||
|
||||
It will be converted into HTML:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<p>That's some text with a footnote.<sup class="footnote-ref" id="fnref-1"><a href="#fn-1">1</a></sup></p>
|
||||
<section class="footnotes">
|
||||
<ol>
|
||||
<li id="fn-1"><p>And that's the footnote.<a href="#fnref-1" class="footnote">↩</a></p></li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register(
|
||||
"footnote",
|
||||
INLINE_FOOTNOTE,
|
||||
parse_inline_footnote,
|
||||
before="link",
|
||||
)
|
||||
md.block.register(
|
||||
"ref_footnote",
|
||||
REF_FOOTNOTE,
|
||||
parse_ref_footnote,
|
||||
before="ref_link",
|
||||
)
|
||||
md.after_render_hooks.append(md_footnotes_hook)
|
||||
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("footnote_ref", render_footnote_ref)
|
||||
md.renderer.register("footnote_item", render_footnote_item)
|
||||
md.renderer.register("footnotes", render_footnotes)
|
||||
183
venv/lib/python3.12/site-packages/mistune/plugins/formatting.py
Normal file
183
venv/lib/python3.12/site-packages/mistune/plugins/formatting.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Match, Optional, Pattern
|
||||
|
||||
from ..helpers import PREVENT_BACKSLASH
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..core import BaseRenderer, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["strikethrough", "mark", "insert", "superscript", "subscript"]
|
||||
|
||||
_STRIKE_END = re.compile(r"(?:" + PREVENT_BACKSLASH + r"\\~|[^\s~])~~(?!~)")
|
||||
_MARK_END = re.compile(r"(?:" + PREVENT_BACKSLASH + r"\\=|[^\s=])==(?!=)")
|
||||
_INSERT_END = re.compile(r"(?:" + PREVENT_BACKSLASH + r"\\\^|[^\s^])\^\^(?!\^)")
|
||||
|
||||
SUPERSCRIPT_PATTERN = r"\^(?:" + PREVENT_BACKSLASH + r"\\\^|\S|\\ )+?\^"
|
||||
SUBSCRIPT_PATTERN = r"~(?:" + PREVENT_BACKSLASH + r"\\~|\S|\\ )+?~"
|
||||
|
||||
|
||||
def parse_strikethrough(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
|
||||
return _parse_to_end(inline, m, state, "strikethrough", _STRIKE_END)
|
||||
|
||||
|
||||
def render_strikethrough(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<del>" + text + "</del>"
|
||||
|
||||
|
||||
def parse_mark(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
|
||||
return _parse_to_end(inline, m, state, "mark", _MARK_END)
|
||||
|
||||
|
||||
def render_mark(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<mark>" + text + "</mark>"
|
||||
|
||||
|
||||
def parse_insert(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
|
||||
return _parse_to_end(inline, m, state, "insert", _INSERT_END)
|
||||
|
||||
|
||||
def render_insert(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<ins>" + text + "</ins>"
|
||||
|
||||
|
||||
def parse_superscript(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
return _parse_script(inline, m, state, "superscript")
|
||||
|
||||
|
||||
def render_superscript(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<sup>" + text + "</sup>"
|
||||
|
||||
|
||||
def parse_subscript(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
return _parse_script(inline, m, state, "subscript")
|
||||
|
||||
|
||||
def render_subscript(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<sub>" + text + "</sub>"
|
||||
|
||||
|
||||
def _parse_to_end(
|
||||
inline: "InlineParser",
|
||||
m: Match[str],
|
||||
state: "InlineState",
|
||||
tok_type: str,
|
||||
end_pattern: Pattern[str],
|
||||
) -> Optional[int]:
|
||||
pos = m.end()
|
||||
m1 = end_pattern.search(state.src, pos)
|
||||
if not m1:
|
||||
return None
|
||||
end_pos = m1.end()
|
||||
text = state.src[pos : end_pos - 2]
|
||||
new_state = state.copy()
|
||||
new_state.src = text
|
||||
children = inline.render(new_state)
|
||||
state.append_token({"type": tok_type, "children": children})
|
||||
return end_pos
|
||||
|
||||
|
||||
def _parse_script(inline: "InlineParser", m: Match[str], state: "InlineState", tok_type: str) -> int:
|
||||
text = m.group(0)
|
||||
new_state = state.copy()
|
||||
new_state.src = text[1:-1].replace("\\ ", " ")
|
||||
children = inline.render(new_state)
|
||||
state.append_token({"type": tok_type, "children": children})
|
||||
return m.end()
|
||||
|
||||
|
||||
def strikethrough(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support strikethrough. Spec defined by
|
||||
GitHub flavored Markdown and commonly used by many parsers:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
~~This was mistaken text~~
|
||||
|
||||
It will be converted into HTML:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<del>This was mistaken text</del>
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register(
|
||||
"strikethrough",
|
||||
r"~~(?=[^\s~])",
|
||||
parse_strikethrough,
|
||||
before="link",
|
||||
)
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("strikethrough", render_strikethrough)
|
||||
|
||||
|
||||
def mark(md: "Markdown") -> None:
|
||||
"""A mistune plugin to add ``<mark>`` tag. Spec defined at
|
||||
https://facelessuser.github.io/pymdown-extensions/extensions/mark/:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
==mark me== ==mark \\=\\= equal==
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register(
|
||||
"mark",
|
||||
r"==(?=[^\s=])",
|
||||
parse_mark,
|
||||
before="link",
|
||||
)
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("mark", render_mark)
|
||||
|
||||
|
||||
def insert(md: "Markdown") -> None:
|
||||
"""A mistune plugin to add ``<ins>`` tag. Spec defined at
|
||||
https://facelessuser.github.io/pymdown-extensions/extensions/caret/#insert:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
^^insert me^^
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register(
|
||||
"insert",
|
||||
r"\^\^(?=[^\s\^])",
|
||||
parse_insert,
|
||||
before="link",
|
||||
)
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("insert", render_insert)
|
||||
|
||||
|
||||
def superscript(md: "Markdown") -> None:
|
||||
"""A mistune plugin to add ``<sup>`` tag. Spec defined at
|
||||
https://pandoc.org/MANUAL.html#superscripts-and-subscripts:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
2^10^ is 1024.
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register("superscript", SUPERSCRIPT_PATTERN, parse_superscript, before="linebreak")
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("superscript", render_superscript)
|
||||
|
||||
|
||||
def subscript(md: "Markdown") -> None:
|
||||
"""A mistune plugin to add ``<sub>`` tag. Spec defined at
|
||||
https://pandoc.org/MANUAL.html#superscripts-and-subscripts:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
H~2~O is a liquid.
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register("subscript", SUBSCRIPT_PATTERN, parse_subscript, before="linebreak")
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("subscript", render_subscript)
|
||||
65
venv/lib/python3.12/site-packages/mistune/plugins/math.py
Normal file
65
venv/lib/python3.12/site-packages/mistune/plugins/math.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from typing import TYPE_CHECKING, Match
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BaseRenderer, BlockState, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["math", "math_in_quote", "math_in_list"]
|
||||
|
||||
BLOCK_MATH_PATTERN = r"^ {0,3}\$\$[ \t]*\n(?P<math_text>[\s\S]+?)\n\$\$[ \t]*$"
|
||||
INLINE_MATH_PATTERN = r"\$(?!\s)(?P<math_text>.+?)(?!\s)\$"
|
||||
|
||||
|
||||
def parse_block_math(block: "BlockParser", m: Match[str], state: "BlockState") -> int:
|
||||
text = m.group("math_text")
|
||||
state.append_token({"type": "block_math", "raw": text})
|
||||
return m.end() + 1
|
||||
|
||||
|
||||
def parse_inline_math(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
text = m.group("math_text")
|
||||
state.append_token({"type": "inline_math", "raw": text})
|
||||
return m.end()
|
||||
|
||||
|
||||
def render_block_math(renderer: "BaseRenderer", text: str) -> str:
|
||||
return '<div class="math">$$\n' + text + "\n$$</div>\n"
|
||||
|
||||
|
||||
def render_inline_math(renderer: "BaseRenderer", text: str) -> str:
|
||||
return r'<span class="math">\(' + text + r"\)</span>"
|
||||
|
||||
|
||||
def math(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support math. The syntax is used
|
||||
by many markdown extensions:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Block math is surrounded by $$:
|
||||
|
||||
$$
|
||||
f(a)=f(b)
|
||||
$$
|
||||
|
||||
Inline math is surrounded by `$`, such as $f(a)=f(b)$
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.block.register("block_math", BLOCK_MATH_PATTERN, parse_block_math, before="list")
|
||||
md.inline.register("inline_math", INLINE_MATH_PATTERN, parse_inline_math, before="link")
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("block_math", render_block_math)
|
||||
md.renderer.register("inline_math", render_inline_math)
|
||||
|
||||
|
||||
def math_in_quote(md: "Markdown") -> None:
|
||||
"""Enable block math plugin in block quote."""
|
||||
md.block.insert_rule(md.block.block_quote_rules, "block_math", before="list")
|
||||
|
||||
|
||||
def math_in_list(md: "Markdown") -> None:
|
||||
"""Enable block math plugin in list."""
|
||||
md.block.insert_rule(md.block.list_rules, "block_math", before="list")
|
||||
112
venv/lib/python3.12/site-packages/mistune/plugins/ruby.py
Normal file
112
venv/lib/python3.12/site-packages/mistune/plugins/ruby.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Match, Optional
|
||||
|
||||
from ..helpers import parse_link, parse_link_label
|
||||
from ..util import unikey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..core import BaseRenderer, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
|
||||
RUBY_PATTERN = r"\[(?:\w+\([\w ]+\))+\]"
|
||||
_ruby_re = re.compile(RUBY_PATTERN)
|
||||
|
||||
|
||||
def parse_ruby(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
text = m.group(0)[1:-2]
|
||||
items = text.split(")")
|
||||
tokens = []
|
||||
for item in items:
|
||||
rb, rt = item.split("(")
|
||||
tokens.append({"type": "ruby", "raw": rb, "attrs": {"rt": rt}})
|
||||
|
||||
end_pos = m.end()
|
||||
|
||||
next_match = _ruby_re.match(state.src, end_pos)
|
||||
if next_match:
|
||||
for tok in tokens:
|
||||
state.append_token(tok)
|
||||
return parse_ruby(inline, next_match, state)
|
||||
|
||||
# repeat link logic
|
||||
if end_pos < len(state.src):
|
||||
link_pos = _parse_ruby_link(inline, state, end_pos, tokens)
|
||||
if link_pos:
|
||||
return link_pos
|
||||
|
||||
for tok in tokens:
|
||||
state.append_token(tok)
|
||||
return end_pos
|
||||
|
||||
|
||||
def _parse_ruby_link(
|
||||
inline: "InlineParser", state: "InlineState", pos: int, tokens: List[Dict[str, Any]]
|
||||
) -> Optional[int]:
|
||||
c = state.src[pos]
|
||||
if c == "(":
|
||||
# standard link [text](<url> "title")
|
||||
attrs, link_pos = parse_link(state.src, pos + 1)
|
||||
if link_pos:
|
||||
state.append_token(
|
||||
{
|
||||
"type": "link",
|
||||
"children": tokens,
|
||||
"attrs": attrs,
|
||||
}
|
||||
)
|
||||
return link_pos
|
||||
|
||||
elif c == "[":
|
||||
# standard ref link [text][label]
|
||||
label, link_pos = parse_link_label(state.src, pos + 1)
|
||||
if label and link_pos:
|
||||
ref_links = state.env["ref_links"]
|
||||
key = unikey(label)
|
||||
env = ref_links.get(key)
|
||||
if env:
|
||||
attrs = {"url": env["url"], "title": env.get("title")}
|
||||
state.append_token(
|
||||
{
|
||||
"type": "link",
|
||||
"children": tokens,
|
||||
"attrs": attrs,
|
||||
}
|
||||
)
|
||||
else:
|
||||
for tok in tokens:
|
||||
state.append_token(tok)
|
||||
state.append_token(
|
||||
{
|
||||
"type": "text",
|
||||
"raw": "[" + label + "]",
|
||||
}
|
||||
)
|
||||
return link_pos
|
||||
return None
|
||||
|
||||
|
||||
def render_ruby(renderer: "BaseRenderer", text: str, rt: str) -> str:
|
||||
return "<ruby>" + text + "<rt>" + rt + "</rt></ruby>"
|
||||
|
||||
|
||||
def ruby(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support ``<ruby>`` tag. The syntax is defined
|
||||
at https://lepture.com/en/2022/markdown-ruby-markup:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
[漢字(ㄏㄢˋㄗˋ)]
|
||||
[漢(ㄏㄢˋ)字(ㄗˋ)]
|
||||
|
||||
[漢字(ㄏㄢˋㄗˋ)][link]
|
||||
[漢字(ㄏㄢˋㄗˋ)](/url "title")
|
||||
|
||||
[link]: /url "title"
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.inline.register("ruby", RUBY_PATTERN, parse_ruby, before="link")
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("ruby", render_ruby)
|
||||
50
venv/lib/python3.12/site-packages/mistune/plugins/speedup.py
Normal file
50
venv/lib/python3.12/site-packages/mistune/plugins/speedup.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import re
|
||||
import string
|
||||
from typing import TYPE_CHECKING, Match
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BlockState, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
# because mismatch is too slow, add parsers for paragraph and text
|
||||
|
||||
HARD_LINEBREAK_RE = re.compile(r" *\n\s*")
|
||||
PARAGRAPH = (
|
||||
# start with none punctuation, not number, not whitespace
|
||||
r"(?:^[^\s\d" + re.escape(string.punctuation) + r"][^\n]*\n)+"
|
||||
)
|
||||
|
||||
__all__ = ["speedup"]
|
||||
|
||||
|
||||
def parse_text(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
text = m.group(0)
|
||||
text = HARD_LINEBREAK_RE.sub("\n", text)
|
||||
inline.process_text(text, state)
|
||||
return m.end()
|
||||
|
||||
|
||||
def parse_paragraph(block: "BlockParser", m: Match[str], state: "BlockState") -> int:
|
||||
text = m.group(0)
|
||||
state.add_paragraph(text)
|
||||
return m.end()
|
||||
|
||||
|
||||
def speedup(md: "Markdown") -> None:
|
||||
"""Increase the speed of parsing paragraph and inline text."""
|
||||
md.block.register("paragraph", PARAGRAPH, parse_paragraph)
|
||||
|
||||
punc = r"\\><!\[_*`~\^\$="
|
||||
text_pattern = r"[\s\S]+?(?=[" + punc + r"]|"
|
||||
if "url_link" in md.inline.rules:
|
||||
text_pattern += "https?:|"
|
||||
|
||||
if md.inline.hard_wrap:
|
||||
text_pattern += r" *\n|"
|
||||
else:
|
||||
text_pattern += r" {2,}\n|"
|
||||
|
||||
text_pattern += r"$)"
|
||||
md.inline.register("text", text_pattern, parse_text)
|
||||
87
venv/lib/python3.12/site-packages/mistune/plugins/spoiler.py
Normal file
87
venv/lib/python3.12/site-packages/mistune/plugins/spoiler.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Match
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BaseRenderer, BlockState, InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["spoiler"]
|
||||
|
||||
_BLOCK_SPOILER_START = re.compile(r"^ {0,3}! ?", re.M)
|
||||
_BLOCK_SPOILER_MATCH = re.compile(r"^( {0,3}![^\n]*\n)+$")
|
||||
|
||||
INLINE_SPOILER_PATTERN = r">!\s*(?P<spoiler_text>.+?)\s*!<"
|
||||
|
||||
|
||||
def parse_block_spoiler(block: "BlockParser", m: Match[str], state: "BlockState") -> int:
|
||||
text, end_pos = block.extract_block_quote(m, state)
|
||||
if not text.endswith("\n"):
|
||||
# ensure it endswith \n to make sure
|
||||
# _BLOCK_SPOILER_MATCH.match works
|
||||
text += "\n"
|
||||
|
||||
depth = state.depth()
|
||||
if not depth and _BLOCK_SPOILER_MATCH.match(text):
|
||||
text = _BLOCK_SPOILER_START.sub("", text)
|
||||
tok_type = "block_spoiler"
|
||||
else:
|
||||
tok_type = "block_quote"
|
||||
|
||||
# scan children state
|
||||
child = state.child_state(text)
|
||||
if state.depth() >= block.max_nested_level - 1:
|
||||
rules = list(block.block_quote_rules)
|
||||
rules.remove("block_quote")
|
||||
else:
|
||||
rules = block.block_quote_rules
|
||||
|
||||
block.parse(child, rules)
|
||||
token = {"type": tok_type, "children": child.tokens}
|
||||
if end_pos:
|
||||
state.prepend_token(token)
|
||||
return end_pos
|
||||
state.append_token(token)
|
||||
return state.cursor
|
||||
|
||||
|
||||
def parse_inline_spoiler(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
text = m.group("spoiler_text")
|
||||
new_state = state.copy()
|
||||
new_state.src = text
|
||||
children = inline.render(new_state)
|
||||
state.append_token({"type": "inline_spoiler", "children": children})
|
||||
return m.end()
|
||||
|
||||
|
||||
def render_block_spoiler(renderer: "BaseRenderer", text: str) -> str:
|
||||
return '<div class="spoiler">\n' + text + "</div>\n"
|
||||
|
||||
|
||||
def render_inline_spoiler(renderer: "BaseRenderer", text: str) -> str:
|
||||
return '<span class="spoiler">' + text + "</span>"
|
||||
|
||||
|
||||
def spoiler(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support block and inline spoiler. The
|
||||
syntax is inspired by stackexchange:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Block level spoiler looks like block quote, but with `>!`:
|
||||
|
||||
>! this is spoiler
|
||||
>!
|
||||
>! the content will be hidden
|
||||
|
||||
Inline spoiler is surrounded by `>!` and `!<`, such as >! hide me !<.
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
# reset block quote parser with block spoiler parser
|
||||
md.block.register("block_quote", None, parse_block_spoiler)
|
||||
md.inline.register("inline_spoiler", INLINE_SPOILER_PATTERN, parse_inline_spoiler)
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("block_spoiler", render_block_spoiler)
|
||||
md.renderer.register("inline_spoiler", render_inline_spoiler)
|
||||
189
venv/lib/python3.12/site-packages/mistune/plugins/table.py
Normal file
189
venv/lib/python3.12/site-packages/mistune/plugins/table.py
Normal file
@@ -0,0 +1,189 @@
|
||||
import re
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
Match,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
from ..helpers import PREVENT_BACKSLASH
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..block_parser import BlockParser
|
||||
from ..core import BaseRenderer, BlockState
|
||||
from ..markdown import Markdown
|
||||
|
||||
# https://michelf.ca/projects/php-markdown/extra/#table
|
||||
|
||||
__all__ = ["table", "table_in_quote", "table_in_list"]
|
||||
|
||||
|
||||
TABLE_PATTERN = (
|
||||
r"^ {0,3}\|(?P<table_head>.+)\|[ \t]*\n"
|
||||
r" {0,3}\|(?P<table_align> *[-:]+[-| :]*)\|[ \t]*\n"
|
||||
r"(?P<table_body>(?: {0,3}\|.*\|[ \t]*(?:\n|$))*)\n*"
|
||||
)
|
||||
NP_TABLE_PATTERN = (
|
||||
r"^ {0,3}(?P<nptable_head>\S.*\|.*)\n"
|
||||
r" {0,3}(?P<nptable_align>[-:]+ *\|[-| :]*)\n"
|
||||
r"(?P<nptable_body>(?:.*\|.*(?:\n|$))*)\n*"
|
||||
)
|
||||
|
||||
TABLE_CELL = re.compile(r"^ {0,3}\|(.+)\|[ \t]*$")
|
||||
CELL_SPLIT = re.compile(r" *" + PREVENT_BACKSLASH + r"\| *")
|
||||
ALIGN_CENTER = re.compile(r"^ *:-+: *$")
|
||||
ALIGN_LEFT = re.compile(r"^ *:-+ *$")
|
||||
ALIGN_RIGHT = re.compile(r"^ *-+: *$")
|
||||
|
||||
|
||||
def parse_table(block: "BlockParser", m: Match[str], state: "BlockState") -> Optional[int]:
|
||||
pos = m.end()
|
||||
header = m.group("table_head")
|
||||
align = m.group("table_align")
|
||||
thead, aligns = _process_thead(header, align)
|
||||
if not thead:
|
||||
return None
|
||||
assert aligns is not None
|
||||
|
||||
rows = []
|
||||
body = m.group("table_body")
|
||||
for text in body.splitlines():
|
||||
m2 = TABLE_CELL.match(text)
|
||||
if not m2: # pragma: no cover
|
||||
return None
|
||||
row = _process_row(m2.group(1), aligns)
|
||||
if not row:
|
||||
return None
|
||||
rows.append(row)
|
||||
|
||||
children = [thead, {"type": "table_body", "children": rows}]
|
||||
state.append_token({"type": "table", "children": children})
|
||||
return pos
|
||||
|
||||
|
||||
def parse_nptable(block: "BlockParser", m: Match[str], state: "BlockState") -> Optional[int]:
|
||||
header = m.group("nptable_head")
|
||||
align = m.group("nptable_align")
|
||||
thead, aligns = _process_thead(header, align)
|
||||
if not thead:
|
||||
return None
|
||||
assert aligns is not None
|
||||
|
||||
rows = []
|
||||
body = m.group("nptable_body")
|
||||
for text in body.splitlines():
|
||||
row = _process_row(text, aligns)
|
||||
if not row:
|
||||
return None
|
||||
rows.append(row)
|
||||
|
||||
children = [thead, {"type": "table_body", "children": rows}]
|
||||
state.append_token({"type": "table", "children": children})
|
||||
return m.end()
|
||||
|
||||
|
||||
def _process_thead(header: str, align: str) -> Union[Tuple[None, None], Tuple[Dict[str, Any], List[str]]]:
|
||||
headers = CELL_SPLIT.split(header)
|
||||
aligns = CELL_SPLIT.split(align)
|
||||
if len(headers) != len(aligns):
|
||||
return None, None
|
||||
|
||||
for i, v in enumerate(aligns):
|
||||
if ALIGN_CENTER.match(v):
|
||||
aligns[i] = "center"
|
||||
elif ALIGN_LEFT.match(v):
|
||||
aligns[i] = "left"
|
||||
elif ALIGN_RIGHT.match(v):
|
||||
aligns[i] = "right"
|
||||
else:
|
||||
aligns[i] = None
|
||||
|
||||
children = [
|
||||
{"type": "table_cell", "text": text.strip(), "attrs": {"align": aligns[i], "head": True}}
|
||||
for i, text in enumerate(headers)
|
||||
]
|
||||
thead = {"type": "table_head", "children": children}
|
||||
return thead, aligns
|
||||
|
||||
|
||||
def _process_row(text: str, aligns: List[str]) -> Optional[Dict[str, Any]]:
|
||||
cells = CELL_SPLIT.split(text)
|
||||
if len(cells) != len(aligns):
|
||||
return None
|
||||
|
||||
children = [
|
||||
{"type": "table_cell", "text": text.strip(), "attrs": {"align": aligns[i], "head": False}}
|
||||
for i, text in enumerate(cells)
|
||||
]
|
||||
return {"type": "table_row", "children": children}
|
||||
|
||||
|
||||
def render_table(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<table>\n" + text + "</table>\n"
|
||||
|
||||
|
||||
def render_table_head(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<thead>\n<tr>\n" + text + "</tr>\n</thead>\n"
|
||||
|
||||
|
||||
def render_table_body(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<tbody>\n" + text + "</tbody>\n"
|
||||
|
||||
|
||||
def render_table_row(renderer: "BaseRenderer", text: str) -> str:
|
||||
return "<tr>\n" + text + "</tr>\n"
|
||||
|
||||
|
||||
def render_table_cell(renderer: "BaseRenderer", text: str, align: Optional[str] = None, head: bool = False) -> str:
|
||||
if head:
|
||||
tag = "th"
|
||||
else:
|
||||
tag = "td"
|
||||
|
||||
html = " <" + tag
|
||||
if align:
|
||||
html += ' style="text-align:' + align + '"'
|
||||
|
||||
return html + ">" + text + "</" + tag + ">\n"
|
||||
|
||||
|
||||
def table(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support table, spec defined at
|
||||
https://michelf.ca/projects/php-markdown/extra/#table
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
First Header | Second Header
|
||||
------------- | -------------
|
||||
Content Cell | Content Cell
|
||||
Content Cell | Content Cell
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.block.register("table", TABLE_PATTERN, parse_table, before="paragraph")
|
||||
md.block.register("nptable", NP_TABLE_PATTERN, parse_nptable, before="paragraph")
|
||||
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("table", render_table)
|
||||
md.renderer.register("table_head", render_table_head)
|
||||
md.renderer.register("table_body", render_table_body)
|
||||
md.renderer.register("table_row", render_table_row)
|
||||
md.renderer.register("table_cell", render_table_cell)
|
||||
|
||||
|
||||
def table_in_quote(md: "Markdown") -> None:
|
||||
"""Enable table plugin in block quotes."""
|
||||
md.block.insert_rule(md.block.block_quote_rules, "table", before="paragraph")
|
||||
md.block.insert_rule(md.block.block_quote_rules, "nptable", before="paragraph")
|
||||
|
||||
|
||||
def table_in_list(md: "Markdown") -> None:
|
||||
"""Enable table plugin in list."""
|
||||
md.block.insert_rule(md.block.list_rules, "table", before="paragraph")
|
||||
md.block.insert_rule(md.block.list_rules, "nptable", before="paragraph")
|
||||
@@ -0,0 +1,69 @@
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..core import BaseRenderer, BlockState
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["task_lists"]
|
||||
|
||||
|
||||
TASK_LIST_ITEM = re.compile(r"^(\[[ xX]\])\s+")
|
||||
|
||||
|
||||
def task_lists_hook(md: "Markdown", state: "BlockState") -> Iterable[Dict[str, Any]]:
|
||||
return _rewrite_all_list_items(state.tokens)
|
||||
|
||||
|
||||
def render_task_list_item(renderer: "BaseRenderer", text: str, checked: bool = False) -> str:
|
||||
checkbox = '<input class="task-list-item-checkbox" type="checkbox" disabled'
|
||||
if checked:
|
||||
checkbox += " checked/>"
|
||||
else:
|
||||
checkbox += "/>"
|
||||
|
||||
if text.startswith("<p>"):
|
||||
text = text.replace("<p>", "<p>" + checkbox, 1)
|
||||
else:
|
||||
text = checkbox + text
|
||||
|
||||
return '<li class="task-list-item">' + text + "</li>\n"
|
||||
|
||||
|
||||
def task_lists(md: "Markdown") -> None:
|
||||
"""A mistune plugin to support task lists. Spec defined by
|
||||
GitHub flavored Markdown and commonly used by many parsers:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
- [ ] unchecked task
|
||||
- [x] checked task
|
||||
|
||||
:param md: Markdown instance
|
||||
"""
|
||||
md.before_render_hooks.append(task_lists_hook)
|
||||
if md.renderer and md.renderer.NAME == "html":
|
||||
md.renderer.register("task_list_item", render_task_list_item)
|
||||
|
||||
|
||||
def _rewrite_all_list_items(tokens: Iterable[Dict[str, Any]]) -> Iterable[Dict[str, Any]]:
|
||||
for tok in tokens:
|
||||
if tok["type"] == "list_item":
|
||||
_rewrite_list_item(tok)
|
||||
if "children" in tok:
|
||||
_rewrite_all_list_items(tok["children"])
|
||||
return tokens
|
||||
|
||||
|
||||
def _rewrite_list_item(tok: Dict[str, Any]) -> None:
|
||||
children = tok["children"]
|
||||
if children:
|
||||
first_child = children[0]
|
||||
text = first_child.get("text", "")
|
||||
m = TASK_LIST_ITEM.match(text)
|
||||
if m:
|
||||
mark = m.group(1)
|
||||
first_child["text"] = text[m.end() :]
|
||||
|
||||
tok["type"] = "task_list_item"
|
||||
tok["attrs"] = {"checked": mark != "[ ]"}
|
||||
32
venv/lib/python3.12/site-packages/mistune/plugins/url.py
Normal file
32
venv/lib/python3.12/site-packages/mistune/plugins/url.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from typing import TYPE_CHECKING, Match
|
||||
|
||||
from ..util import escape_url
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..core import InlineState
|
||||
from ..inline_parser import InlineParser
|
||||
from ..markdown import Markdown
|
||||
|
||||
__all__ = ["url"]
|
||||
|
||||
URL_LINK_PATTERN = r"""https?:\/\/[^\s<]+[^<.,:;"')\]\s]"""
|
||||
|
||||
|
||||
def parse_url_link(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
|
||||
text = m.group(0)
|
||||
pos = m.end()
|
||||
if state.in_link:
|
||||
inline.process_text(text, state)
|
||||
return pos
|
||||
state.append_token(
|
||||
{
|
||||
"type": "link",
|
||||
"children": [{"type": "text", "raw": text}],
|
||||
"attrs": {"url": escape_url(text)},
|
||||
}
|
||||
)
|
||||
return pos
|
||||
|
||||
|
||||
def url(md: "Markdown") -> None:
|
||||
md.inline.register("url_link", URL_LINK_PATTERN, parse_url_link)
|
||||
المرجع في مشكلة جديدة
حظر مستخدم