Replacement BlockProcessor for Header IDs.
| 75 | |
| 76 | |
| 77 | class HeaderIdProcessor(markdown.blockprocessors.BlockProcessor): |
| 78 | """ Replacement BlockProcessor for Header IDs. """ |
| 79 | |
| 80 | # Detect a header at start of any line in block |
| 81 | RE = re.compile(r"""(^|\n) |
| 82 | (?P<level>\#{1,6}) # group('level') = string of hashes |
| 83 | (?P<header>.*?) # group('header') = Header text |
| 84 | \#* # optional closing hashes |
| 85 | (?:[ \t]*\{[ \t]*\#(?P<id>[-_:a-zA-Z0-9]+)[ \t]*\})? |
| 86 | (\n|$) # ^^ group('id') = id attribute |
| 87 | """, |
| 88 | re.VERBOSE) |
| 89 | |
| 90 | IDs = [] |
| 91 | |
| 92 | def test(self, parent, block): |
| 93 | return bool(self.RE.search(block)) |
| 94 | |
| 95 | def run(self, parent, blocks): |
| 96 | block = blocks.pop(0) |
| 97 | m = self.RE.search(block) |
| 98 | if m: |
| 99 | before = block[:m.start()] # All lines before header |
| 100 | after = block[m.end():] # All lines after header |
| 101 | if before: |
| 102 | # As the header was not the first line of the block and the |
| 103 | # lines before the header must be parsed first, |
| 104 | # recursively parse this lines as a block. |
| 105 | self.parser.parseBlocks(parent, [before]) |
| 106 | # Create header using named groups from RE |
| 107 | start_level, force_id = self._get_meta() |
| 108 | level = len(m.group('level')) + start_level |
| 109 | if level > 6: |
| 110 | level = 6 |
| 111 | h = markdown.etree.SubElement(parent, 'h%d' % level) |
| 112 | h.text = m.group('header').strip() |
| 113 | if m.group('id'): |
| 114 | h.set('id', self._unique_id(m.group('id'))) |
| 115 | elif force_id: |
| 116 | h.set('id', self._create_id(m.group('header').strip())) |
| 117 | if after: |
| 118 | # Insert remaining lines as first block for future parsing. |
| 119 | blocks.insert(0, after) |
| 120 | else: |
| 121 | # This should never happen, but just in case... |
| 122 | message(CRITICAL, "We've got a problem header!") |
| 123 | |
| 124 | def _get_meta(self): |
| 125 | """ Return meta data suported by this ext as a tuple """ |
| 126 | level = int(self.config['level'][0]) - 1 |
| 127 | force = self._str2bool(self.config['forceid'][0]) |
| 128 | if hasattr(self.md, 'Meta'): |
| 129 | if 'header_level' in self.md.Meta: |
| 130 | level = int(self.md.Meta['header_level'][0]) - 1 |
| 131 | if 'header_forceid' in self.md.Meta: |
| 132 | force = self._str2bool(self.md.Meta['header_forceid'][0]) |
| 133 | return level, force |
| 134 |