Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mistune/plugins/footnotes.py: 31%
75 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1import re
2from ..core import BlockState
3from ..util import unikey
4from ..helpers import LINK_LABEL
6__all__ = ['footnotes']
8_PARAGRAPH_SPLIT = re.compile(r'\n{2,}')
9# https://michelf.ca/projects/php-markdown/extra/#footnotes
10REF_FOOTNOTE = (
11 r'^(?P<footnote_lead> {0,3})'
12 r'\[\^(?P<footnote_key>' + LINK_LABEL + r')]:[ \t]'
13 r'(?P<footnote_text>[^\n]*(?:\n+|$)'
14 r'(?:(?P=footnote_lead) {1,3}(?! )[^\n]*\n+)*'
15 r')'
16)
18INLINE_FOOTNOTE = r'\[\^(?P<footnote_key>' + LINK_LABEL + r')\]'
21def parse_inline_footnote(inline, m: re.Match, state):
22 key = unikey(m.group('footnote_key'))
23 ref = state.env.get('ref_footnotes')
24 if ref and key in ref:
25 notes = state.env.get('footnotes')
26 if not notes:
27 notes = []
28 if key not in notes:
29 notes.append(key)
30 state.env['footnotes'] = notes
31 state.append_token({
32 'type': 'footnote_ref',
33 'raw': key,
34 'attrs': {'index': notes.index(key) + 1}
35 })
36 else:
37 state.append_token({'type': 'text', 'raw': m.group(0)})
38 return m.end()
41def parse_ref_footnote(block, m: re.Match, state: BlockState):
42 ref = state.env.get('ref_footnotes')
43 if not ref:
44 ref = {}
46 key = unikey(m.group('footnote_key'))
47 if key not in ref:
48 ref[key] = m.group('footnote_text')
49 state.env['ref_footnotes'] = ref
50 return m.end()
53def parse_footnote_item(block, key: str, index: int, state: BlockState):
54 ref = state.env.get('ref_footnotes')
55 text = ref[key]
57 lines = text.splitlines()
58 second_line = None
59 for second_line in lines[1:]:
60 if second_line:
61 break
63 if second_line:
64 spaces = len(second_line) - len(second_line.lstrip())
65 pattern = re.compile(r'^ {' + str(spaces) + r',}', flags=re.M)
66 text = pattern.sub('', text).strip()
67 items = _PARAGRAPH_SPLIT.split(text)
68 children = [{'type': 'paragraph', 'text': s} for s in items]
69 else:
70 text = text.strip()
71 children = [{'type': 'paragraph', 'text': text}]
72 return {
73 'type': 'footnote_item',
74 'children': children,
75 'attrs': {'key': key, 'index': index}
76 }
79def md_footnotes_hook(md, result: str, state: BlockState):
80 notes = state.env.get('footnotes')
81 if not notes:
82 return result
84 children = [
85 parse_footnote_item(md.block, k, i + 1, state)
86 for i, k in enumerate(notes)
87 ]
88 state = BlockState()
89 state.tokens = [{'type': 'footnotes', 'children': children}]
90 output = md.render_state(state)
91 return result + output
94def render_footnote_ref(renderer, key: str, index: int):
95 i = str(index)
96 html = '<sup class="footnote-ref" id="fnref-' + i + '">'
97 return html + '<a href="#fn-' + i + '">' + i + '</a></sup>'
100def render_footnotes(renderer, text: str):
101 return '<section class="footnotes">\n<ol>\n' + text + '</ol>\n</section>\n'
104def render_footnote_item(renderer, text: str, key: str, index: int):
105 i = str(index)
106 back = '<a href="#fnref-' + i + '" class="footnote">↩</a>'
107 text = text.rstrip()[:-4] + back + '</p>'
108 return '<li id="fn-' + i + '">' + text + '</li>\n'
111def footnotes(md):
112 """A mistune plugin to support footnotes, spec defined at
113 https://michelf.ca/projects/php-markdown/extra/#footnotes
115 Here is an example:
117 .. code-block:: text
119 That's some text with a footnote.[^1]
121 [^1]: And that's the footnote.
123 It will be converted into HTML:
125 .. code-block:: html
127 <p>That's some text with a footnote.<sup class="footnote-ref" id="fnref-1"><a href="#fn-1">1</a></sup></p>
128 <section class="footnotes">
129 <ol>
130 <li id="fn-1"><p>And that's the footnote.<a href="#fnref-1" class="footnote">↩</a></p></li>
131 </ol>
132 </section>
134 :param md: Markdown instance
135 """
136 md.inline.register(
137 'footnote',
138 INLINE_FOOTNOTE,
139 parse_inline_footnote,
140 before='link',
141 )
142 md.block.register(
143 'ref_footnote',
144 REF_FOOTNOTE,
145 parse_ref_footnote,
146 before='ref_link',
147 )
148 md.after_render_hooks.append(md_footnotes_hook)
150 if md.renderer and md.renderer.NAME == 'html':
151 md.renderer.register('footnote_ref', render_footnote_ref)
152 md.renderer.register('footnote_item', render_footnote_item)
153 md.renderer.register('footnotes', render_footnotes)