Find footnote definitions and store for later use.
| 244 | |
| 245 | |
| 246 | class FootnoteBlockProcessor(BlockProcessor): |
| 247 | """ Find footnote definitions and store for later use. """ |
| 248 | |
| 249 | RE = re.compile(r'^[ ]{0,3}\[\^([^\]]*)\]:[ ]*(.*)$', re.MULTILINE) |
| 250 | |
| 251 | def __init__(self, footnotes: FootnoteExtension): |
| 252 | super().__init__(footnotes.parser) |
| 253 | self.footnotes = footnotes |
| 254 | |
| 255 | def test(self, parent: etree.Element, block: str) -> bool: |
| 256 | return True |
| 257 | |
| 258 | def run(self, parent: etree.Element, blocks: list[str]) -> bool: |
| 259 | """ Find, set, and remove footnote definitions. """ |
| 260 | block = blocks.pop(0) |
| 261 | |
| 262 | m = self.RE.search(block) |
| 263 | if m: |
| 264 | id = m.group(1) |
| 265 | fn_blocks = [m.group(2)] |
| 266 | |
| 267 | # Handle rest of block |
| 268 | therest = block[m.end():].lstrip('\n') |
| 269 | m2 = self.RE.search(therest) |
| 270 | if m2: |
| 271 | # Another footnote exists in the rest of this block. |
| 272 | # Any content before match is continuation of this footnote, which may be lazily indented. |
| 273 | before = therest[:m2.start()].rstrip('\n') |
| 274 | fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(before)]).lstrip('\n') |
| 275 | # Add back to blocks everything from beginning of match forward for next iteration. |
| 276 | blocks.insert(0, therest[m2.start():]) |
| 277 | else: |
| 278 | # All remaining lines of block are continuation of this footnote, which may be lazily indented. |
| 279 | fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(therest)]).strip('\n') |
| 280 | |
| 281 | # Check for child elements in remaining blocks. |
| 282 | fn_blocks.extend(self.detectTabbed(blocks)) |
| 283 | |
| 284 | footnote = "\n\n".join(fn_blocks) |
| 285 | self.footnotes.setFootnote(id, footnote.rstrip()) |
| 286 | |
| 287 | if block[:m.start()].strip(): |
| 288 | # Add any content before match back to blocks as separate block |
| 289 | blocks.insert(0, block[:m.start()].rstrip('\n')) |
| 290 | return True |
| 291 | # No match. Restore block. |
| 292 | blocks.insert(0, block) |
| 293 | return False |
| 294 | |
| 295 | def detectTabbed(self, blocks: list[str]) -> list[str]: |
| 296 | """ Find indented text and remove indent before further processing. |
| 297 | |
| 298 | Returns: |
| 299 | A list of blocks with indentation removed. |
| 300 | """ |
| 301 | fn_blocks = [] |
| 302 | while blocks: |
| 303 | if blocks[0].startswith(' '*4): |