blob: 7d42c13be7befa34e090970906bb378da381f89d [file] [log] [blame]
Avi Drissmandfd880852022-09-15 20:11:091# Copyright 2015 The Chromium Authors
yutakcf19a4362015-10-22 05:00:492# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Implements Gitiles' notification, aside and promotion blocks.
6
7This extention makes the Markdown parser recognize the Gitiles' extended
8blocks notation. The syntax is explained at:
9
10https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md#Notification_aside_promotion-blocks
11"""
12
13from markdown.blockprocessors import BlockProcessor
14from markdown.extensions import Extension
yutakcf19a4362015-10-22 05:00:4915import re
Peter Kasting3f58d92bd2022-12-16 21:07:1116import xml.etree.ElementTree as etree
yutakcf19a4362015-10-22 05:00:4917
18
19class _GitilesExtBlockProcessor(BlockProcessor):
20 """Process Gitiles' notification, aside and promotion blocks."""
21
22 RE_START = re.compile(r'^\*\*\* (note|aside|promo) *\n')
23 RE_END = re.compile(r'\n\*\*\* *\n?$')
24
25 def __init__(self, *args, **kwargs):
26 self._last_parent = None
27 BlockProcessor.__init__(self, *args, **kwargs)
28
29 def test(self, parent, block):
30 return self.RE_START.search(block) or self.RE_END.search(block)
31
32 def run(self, parent, blocks):
33 raw_block = blocks.pop(0)
34 match_start = self.RE_START.search(raw_block)
35 if match_start:
36 # Opening a new block.
37 rest = raw_block[match_start.end():]
38
39 if self._last_parent:
40 # Inconsistent state (nested starting markers). Ignore the marker
41 # and keep going.
42 blocks.insert(0, rest)
43 return
44
45 div = etree.SubElement(parent, 'div')
46 # Setting the class name is sufficient, because doc.css already has
47 # styles for these classes.
48 div.set('class', match_start.group(1))
49 self._last_parent = parent
50 blocks.insert(0, rest)
51 self.parser.parseBlocks(div, blocks)
52 return
53
54 match_end = self.RE_END.search(raw_block)
55 if match_end:
56 # Ending an existing block.
57
58 # Process the text preceding the ending marker in the current context
59 # (i.e. within the div block).
60 rest = raw_block[:match_end.start()]
61 self.parser.parseBlocks(parent, [rest])
62
63 if not self._last_parent:
64 # Inconsistent state (the ending marker is found but there is no
65 # matching starting marker).
66 # Let's continue as if we did not see the ending marker.
67 return
68
69 last_parent = self._last_parent
70 self._last_parent = None
71 self.parser.parseBlocks(last_parent, blocks)
72 return
73
74
75class _GitilesExtBlockExtension(Extension):
Peter Kasting3f58d92bd2022-12-16 21:07:1176 """Add Gitiles' extended blocks to Markdown, with a priority higher than the
77 highest builtin."""
Yu-Ping Wu4f924c902021-12-01 04:33:2178
79 def extendMarkdown(self, md):
Peter Kasting3f58d92bd2022-12-16 21:07:1180 md.parser.blockprocessors.register(_GitilesExtBlockProcessor(md.parser),
81 'gitilesextblocks', 101)
yutakcf19a4362015-10-22 05:00:4982
83
84def makeExtension(*args, **kwargs):
85 return _GitilesExtBlockExtension(*args, **kwargs)