Replace abbreviation text with ` ` elements.
| 79 | |
| 80 | |
| 81 | class AbbrTreeprocessor(Treeprocessor): |
| 82 | """ Replace abbreviation text with `<abbr>` elements. """ |
| 83 | |
| 84 | def __init__(self, md: Markdown | None = None, abbrs: dict | None = None): |
| 85 | self.abbrs: dict = abbrs if abbrs is not None else {} |
| 86 | self.RE: re.RegexObject | None = None |
| 87 | super().__init__(md) |
| 88 | |
| 89 | def create_element(self, title: str, text: str, tail: str) -> etree.Element: |
| 90 | ''' Create an `abbr` element. ''' |
| 91 | abbr = etree.Element('abbr', {'title': title}) |
| 92 | abbr.text = AtomicString(text) |
| 93 | abbr.tail = tail |
| 94 | return abbr |
| 95 | |
| 96 | def iter_element(self, el: etree.Element, parent: etree.Element | None = None) -> None: |
| 97 | ''' Recursively iterate over elements, run regex on text and wrap matches in `abbr` tags. ''' |
| 98 | for child in reversed(el): |
| 99 | self.iter_element(child, el) |
| 100 | if text := el.text: |
| 101 | if not isinstance(text, AtomicString): |
| 102 | for m in reversed(list(self.RE.finditer(text))): |
| 103 | if self.abbrs[m.group(0)]: |
| 104 | abbr = self.create_element(self.abbrs[m.group(0)], m.group(0), text[m.end():]) |
| 105 | el.insert(0, abbr) |
| 106 | text = text[:m.start()] |
| 107 | el.text = text |
| 108 | if parent is not None and el.tail: |
| 109 | tail = el.tail |
| 110 | index = list(parent).index(el) + 1 |
| 111 | if not isinstance(tail, AtomicString): |
| 112 | for m in reversed(list(self.RE.finditer(tail))): |
| 113 | abbr = self.create_element(self.abbrs[m.group(0)], m.group(0), tail[m.end():]) |
| 114 | parent.insert(index, abbr) |
| 115 | tail = tail[:m.start()] |
| 116 | el.tail = tail |
| 117 | |
| 118 | def run(self, root: etree.Element) -> etree.Element | None: |
| 119 | ''' Step through tree to find known abbreviations. ''' |
| 120 | if not self.abbrs: |
| 121 | # No abbreviations defined. Skip running processor. |
| 122 | return |
| 123 | # Build and compile regex |
| 124 | abbr_list = list(self.abbrs.keys()) |
| 125 | abbr_list.sort(key=len, reverse=True) |
| 126 | self.RE = re.compile(f"\\b(?:{ '|'.join(re.escape(key) for key in abbr_list) })\\b") |
| 127 | # Step through tree and modify on matches |
| 128 | self.iter_element(root) |
| 129 | |
| 130 | |
| 131 | class AbbrBlockprocessor(BlockProcessor): |