mirror of
https://github.com/zulip/zulip.git
synced 2025-11-20 22:48:16 +00:00
Fixes #2665. Regenerated by tabbott with `lint --fix` after a rebase and change in parameters. Note from tabbott: In a few cases, this converts technical debt in the form of unsorted imports into different technical debt in the form of our largest files having very long, ugly import sequences at the start. I expect this change will increase pressure for us to split those files, which isn't a bad thing. Signed-off-by: Anders Kaseorg <anders@zulip.com>
76 lines
2.9 KiB
Python
76 lines
2.9 KiB
Python
from typing import Any, Dict, List, Optional, Tuple
|
|
from xml.etree.ElementTree import Element, SubElement
|
|
|
|
import markdown
|
|
from markdown.extensions import Extension
|
|
|
|
from zerver.lib.bugdown import ResultWithFamily, walk_tree_with_family
|
|
|
|
|
|
class NestedCodeBlocksRenderer(Extension):
|
|
def extendMarkdown(self, md: markdown.Markdown, md_globals: Dict[str, Any]) -> None:
|
|
md.treeprocessors.add(
|
|
'nested_code_blocks',
|
|
NestedCodeBlocksRendererTreeProcessor(md, self.getConfigs()),
|
|
'_end',
|
|
)
|
|
|
|
class NestedCodeBlocksRendererTreeProcessor(markdown.treeprocessors.Treeprocessor):
|
|
def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None:
|
|
super().__init__(md)
|
|
|
|
def run(self, root: Element) -> None:
|
|
code_tags = walk_tree_with_family(root, self.get_code_tags)
|
|
nested_code_blocks = self.get_nested_code_blocks(code_tags)
|
|
for block in nested_code_blocks:
|
|
tag, text = block.result
|
|
codehilite_block = self.get_codehilite_block(text)
|
|
self.replace_element(block.family.grandparent,
|
|
codehilite_block,
|
|
block.family.parent)
|
|
|
|
def get_code_tags(self, e: Element) -> Optional[Tuple[str, Optional[str]]]:
|
|
if e.tag == "code":
|
|
return (e.tag, e.text)
|
|
return None
|
|
|
|
def get_nested_code_blocks(
|
|
self, code_tags: List[ResultWithFamily[Tuple[str, Optional[str]]]],
|
|
) -> List[ResultWithFamily[Tuple[str, Optional[str]]]]:
|
|
nested_code_blocks = []
|
|
for code_tag in code_tags:
|
|
parent: Any = code_tag.family.parent
|
|
grandparent: Any = code_tag.family.grandparent
|
|
if parent.tag == "p" and grandparent.tag == "li":
|
|
# if the parent (<p>) has no text, and no children,
|
|
# that means that the <code> element inside is its
|
|
# only thing inside the bullet, we can confidently say
|
|
# that this is a nested code block
|
|
if parent.text is None and len(list(parent)) == 1 and len(list(parent.itertext())) == 1:
|
|
nested_code_blocks.append(code_tag)
|
|
|
|
return nested_code_blocks
|
|
|
|
def get_codehilite_block(self, code_block_text: Optional[str]) -> Element:
|
|
div = Element("div")
|
|
div.set("class", "codehilite")
|
|
pre = SubElement(div, "pre")
|
|
pre.text = code_block_text
|
|
return div
|
|
|
|
def replace_element(
|
|
self, parent: Optional[Element],
|
|
replacement: Element,
|
|
element_to_replace: Element,
|
|
) -> None:
|
|
if parent is None:
|
|
return
|
|
|
|
for index, child in enumerate(parent):
|
|
if child is element_to_replace:
|
|
parent.insert(index, replacement)
|
|
parent.remove(element_to_replace)
|
|
|
|
def makeExtension(*args: Any, **kwargs: str) -> NestedCodeBlocksRenderer:
|
|
return NestedCodeBlocksRenderer(**kwargs)
|