| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- 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)
|