# Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Implements Gitiles' notification, aside and promotion blocks. This extention makes the Markdown parser recognize the Gitiles' extended blocks notation. The syntax is explained at: https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md#Notification_aside_promotion-blocks """ from markdown.blockprocessors import BlockProcessor from markdown.extensions import Extension from markdown.util import etree import re class _GitilesExtBlockProcessor(BlockProcessor): """Process Gitiles' notification, aside and promotion blocks.""" RE_START = re.compile(r'^\*\*\* (note|aside|promo) *\n') RE_END = re.compile(r'\n\*\*\* *\n?$') def __init__(self, *args, **kwargs): self._last_parent = None BlockProcessor.__init__(self, *args, **kwargs) def test(self, parent, block): return self.RE_START.search(block) or self.RE_END.search(block) def run(self, parent, blocks): raw_block = blocks.pop(0) match_start = self.RE_START.search(raw_block) if match_start: # Opening a new block. rest = raw_block[match_start.end():] if self._last_parent: # Inconsistent state (nested starting markers). Ignore the marker # and keep going. blocks.insert(0, rest) return div = etree.SubElement(parent, 'div') # Setting the class name is sufficient, because doc.css already has # styles for these classes. div.set('class', match_start.group(1)) self._last_parent = parent blocks.insert(0, rest) self.parser.parseBlocks(div, blocks) return match_end = self.RE_END.search(raw_block) if match_end: # Ending an existing block. # Process the text preceding the ending marker in the current context # (i.e. within the div block). rest = raw_block[:match_end.start()] self.parser.parseBlocks(parent, [rest]) if not self._last_parent: # Inconsistent state (the ending marker is found but there is no # matching starting marker). # Let's continue as if we did not see the ending marker. return last_parent = self._last_parent self._last_parent = None self.parser.parseBlocks(last_parent, blocks) return class _GitilesExtBlockExtension(Extension): """Add Gitiles' extended blocks to Markdown.""" def extendMarkdown(self, md, md_globals): md.parser.blockprocessors.add('gitilesextblocks', _GitilesExtBlockProcessor(md.parser), '_begin') def makeExtension(*args, **kwargs): return _GitilesExtBlockExtension(*args, **kwargs)