Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mistune/plugins/footnotes.py: 35%
82 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 06:10 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 06:10 +0000
1import re
2from ..inline_parser import LINK_LABEL
3from ..util import unikey
5__all__ = ['plugin_footnotes']
7#: inline footnote syntax looks like::
8#:
9#: [^key]
10INLINE_FOOTNOTE_PATTERN = r'\[\^(' + LINK_LABEL + r')\]'
12#: define a footnote item like::
13#:
14#: [^key]: paragraph text to describe the note
15DEF_FOOTNOTE = re.compile(
16 r'( {0,3})\[\^(' + LINK_LABEL + r')\]:[ \t]*('
17 r'[^\n]*\n+'
18 r'(?:\1 {1,3}(?! )[^\n]*\n+)*'
19 r')'
20)
23def parse_inline_footnote(inline, m, state):
24 key = unikey(m.group(1))
25 def_footnotes = state.get('def_footnotes')
26 if not def_footnotes or key not in def_footnotes:
27 return 'text', m.group(0)
29 index = state.get('footnote_index', 0)
30 index += 1
31 state['footnote_index'] = index
32 state['footnotes'].append(key)
33 return 'footnote_ref', key, index
36def parse_def_footnote(block, m, state):
37 key = unikey(m.group(2))
38 if key not in state['def_footnotes']:
39 state['def_footnotes'][key] = m.group(3)
42def parse_footnote_item(block, k, i, state):
43 def_footnotes = state['def_footnotes']
44 text = def_footnotes[k]
46 stripped_text = text.strip()
47 if '\n' not in stripped_text:
48 children = [{'type': 'paragraph', 'text': stripped_text}]
49 else:
50 lines = text.splitlines()
51 for second_line in lines[1:]:
52 if second_line:
53 break
55 spaces = len(second_line) - len(second_line.lstrip())
56 pattern = re.compile(r'^ {' + str(spaces) + r',}', flags=re.M)
57 text = pattern.sub('', text)
58 children = block.parse_text(text, state)
59 if not isinstance(children, list):
60 children = [children]
62 return {
63 'type': 'footnote_item',
64 'children': children,
65 'params': (k, i)
66 }
69def md_footnotes_hook(md, result, state):
70 footnotes = state.get('footnotes')
71 if not footnotes:
72 return result
74 children = [
75 parse_footnote_item(md.block, k, i + 1, state)
76 for i, k in enumerate(footnotes)
77 ]
78 tokens = [{'type': 'footnotes', 'children': children}]
79 output = md.block.render(tokens, md.inline, state)
80 return result + output
83def render_ast_footnote_ref(key, index):
84 return {'type': 'footnote_ref', 'key': key, 'index': index}
87def render_ast_footnote_item(children, key, index):
88 return {
89 'type': 'footnote_item',
90 'children': children,
91 'key': key,
92 'index': index,
93 }
96def render_html_footnote_ref(key, index):
97 i = str(index)
98 html = '<sup class="footnote-ref" id="fnref-' + i + '">'
99 return html + '<a href="#fn-' + i + '">' + i + '</a></sup>'
102def render_html_footnotes(text):
103 return (
104 '<section class="footnotes">\n<ol>\n'
105 + text +
106 '</ol>\n</section>\n'
107 )
110def render_html_footnote_item(text, key, index):
111 i = str(index)
112 back = '<a href="#fnref-' + i + '" class="footnote">↩</a>'
114 text = text.rstrip()
115 if text.endswith('</p>'):
116 text = text[:-4] + back + '</p>'
117 else:
118 text = text + back
119 return '<li id="fn-' + i + '">' + text + '</li>\n'
122def plugin_footnotes(md):
123 md.inline.register_rule(
124 'footnote',
125 INLINE_FOOTNOTE_PATTERN,
126 parse_inline_footnote
127 )
128 index = md.inline.rules.index('std_link')
129 if index != -1:
130 md.inline.rules.insert(index, 'footnote')
131 else:
132 md.inline.rules.append('footnote')
134 md.block.register_rule('def_footnote', DEF_FOOTNOTE, parse_def_footnote)
135 index = md.block.rules.index('def_link')
136 if index != -1:
137 md.block.rules.insert(index, 'def_footnote')
138 else:
139 md.block.rules.append('def_footnote')
141 if md.renderer.NAME == 'html':
142 md.renderer.register('footnote_ref', render_html_footnote_ref)
143 md.renderer.register('footnote_item', render_html_footnote_item)
144 md.renderer.register('footnotes', render_html_footnotes)
145 elif md.renderer.NAME == 'ast':
146 md.renderer.register('footnote_ref', render_ast_footnote_ref)
147 md.renderer.register('footnote_item', render_ast_footnote_item)
149 md.after_render_hooks.append(md_footnotes_hook)