diff options
Diffstat (limited to 'src/mistune/plugins/spoiler.py')
-rw-r--r-- | src/mistune/plugins/spoiler.py | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/src/mistune/plugins/spoiler.py b/src/mistune/plugins/spoiler.py new file mode 100644 index 0000000..2931d2b --- /dev/null +++ b/src/mistune/plugins/spoiler.py @@ -0,0 +1,80 @@ +import re + +__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, m, state): + 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, m, state): + 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, text): + return '<div class="spoiler">\n' + text + '</div>\n' + + +def render_inline_spoiler(renderer, text): + return '<span class="spoiler">' + text + '</span>' + + +def spoiler(md): + """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) |