From 66fa71a8f11b6ce5e8471b533f67cc3a1fdb85a8 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Sun, 29 Jan 2023 10:30:54 +0000 Subject: Update to new mistune, removed old mistune, rewrite to python3 --- src/mistune/plugins/footnotes.py | 153 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/mistune/plugins/footnotes.py (limited to 'src/mistune/plugins/footnotes.py') diff --git a/src/mistune/plugins/footnotes.py b/src/mistune/plugins/footnotes.py new file mode 100644 index 0000000..2e10704 --- /dev/null +++ b/src/mistune/plugins/footnotes.py @@ -0,0 +1,153 @@ +import re +from ..core import BlockState +from ..util import unikey +from ..helpers import LINK_LABEL + +__all__ = ['footnotes'] + +_PARAGRAPH_SPLIT = re.compile(r'\n{2,}') +# https://michelf.ca/projects/php-markdown/extra/#footnotes +REF_FOOTNOTE = ( + r'^(?P {0,3})' + r'\[\^(?P' + LINK_LABEL + r')]:[ \t]' + r'(?P[^\n]*(?:\n+|$)' + r'(?:(?P=footnote_lead) {1,3}(?! )[^\n]*\n+)*' + r')' +) + +INLINE_FOOTNOTE = r'\[\^(?P' + LINK_LABEL + r')\]' + + +def parse_inline_footnote(inline, m: re.Match, state): + 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, m: re.Match, state: BlockState): + 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, key: str, index: int, state: BlockState): + ref = state.env.get('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, result: str, state: BlockState): + 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 + + +def render_footnote_ref(renderer, key: str, index: int): + i = str(index) + html = '' + return html + '' + i + '' + + +def render_footnotes(renderer, text: str): + return '
\n
    \n' + text + '
\n
\n' + + +def render_footnote_item(renderer, text: str, key: str, index: int): + i = str(index) + back = '' + text = text.rstrip()[:-4] + back + '

' + return '
  • ' + text + '
  • \n' + + +def footnotes(md): + """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 + +

    That's some text with a footnote.1

    +
    +
      +
    1. And that's the footnote.

    2. +
    +
    + + :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) -- cgit v1.2.3