| 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"): |
| |
| |
| 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" |
|
|
| |
| 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 |
| """ |
| |
| 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) |
|
|