2020-10-20 02:49:02 +02:00
|
|
|
from typing import Any, List, Mapping, Optional, Tuple
|
2020-06-03 06:37:07 +02:00
|
|
|
from xml.etree.ElementTree import Element, SubElement
|
2018-07-07 22:14:30 +02:00
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
import markdown
|
|
|
|
from markdown.extensions import Extension
|
2023-10-12 19:43:45 +02:00
|
|
|
from typing_extensions import override
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2020-06-25 15:00:33 +02:00
|
|
|
from zerver.lib.markdown import ResultWithFamily, walk_tree_with_family
|
2022-06-26 02:35:20 +02:00
|
|
|
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2018-07-07 22:14:30 +02:00
|
|
|
|
|
|
|
class NestedCodeBlocksRenderer(Extension):
|
2023-10-12 19:43:45 +02:00
|
|
|
@override
|
2020-10-20 01:28:13 +02:00
|
|
|
def extendMarkdown(self, md: markdown.Markdown) -> None:
|
|
|
|
md.treeprocessors.register(
|
2018-07-07 22:14:30 +02:00
|
|
|
NestedCodeBlocksRendererTreeProcessor(md, self.getConfigs()),
|
2021-02-12 08:20:45 +01:00
|
|
|
"nested_code_blocks",
|
2021-09-17 19:01:36 +02:00
|
|
|
PREPROCESSOR_PRIORITES["nested_code_blocks"],
|
2018-07-07 22:14:30 +02:00
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-07-07 22:14:30 +02:00
|
|
|
class NestedCodeBlocksRendererTreeProcessor(markdown.treeprocessors.Treeprocessor):
|
2020-10-20 02:49:02 +02:00
|
|
|
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
|
2020-04-09 21:51:58 +02:00
|
|
|
super().__init__(md)
|
2018-07-07 22:14:30 +02:00
|
|
|
|
2023-10-12 19:43:45 +02:00
|
|
|
@override
|
2018-07-07 22:14:30 +02:00
|
|
|
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)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.replace_element(block.family.grandparent, codehilite_block, block.family.parent)
|
2018-07-07 22:14:30 +02:00
|
|
|
|
|
|
|
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(
|
2021-02-12 08:19:30 +01:00
|
|
|
self,
|
|
|
|
code_tags: List[ResultWithFamily[Tuple[str, Optional[str]]]],
|
2020-05-09 02:59:28 +02:00
|
|
|
) -> List[ResultWithFamily[Tuple[str, Optional[str]]]]:
|
2018-07-07 22:14:30 +02:00
|
|
|
nested_code_blocks = []
|
|
|
|
for code_tag in code_tags:
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
parent: Any = code_tag.family.parent
|
|
|
|
grandparent: Any = code_tag.family.grandparent
|
2023-01-18 02:59:37 +01:00
|
|
|
if (
|
|
|
|
parent.tag == "p"
|
|
|
|
and grandparent.tag == "li"
|
|
|
|
and parent.text is None
|
2023-09-12 23:19:57 +02:00
|
|
|
and len(parent) == 1
|
|
|
|
and sum(1 for text in parent.itertext()) == 1
|
2023-01-18 02:59:37 +01:00
|
|
|
):
|
2018-07-24 21:33:34 +02:00
|
|
|
# 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
|
2023-01-18 02:59:37 +01:00
|
|
|
nested_code_blocks.append(code_tag)
|
2018-07-07 22:14:30 +02:00
|
|
|
|
|
|
|
return nested_code_blocks
|
|
|
|
|
2020-05-09 02:59:28 +02:00
|
|
|
def get_codehilite_block(self, code_block_text: Optional[str]) -> Element:
|
2020-06-03 06:37:07 +02:00
|
|
|
div = Element("div")
|
2018-07-07 22:14:30 +02:00
|
|
|
div.set("class", "codehilite")
|
2020-06-03 06:37:07 +02:00
|
|
|
pre = SubElement(div, "pre")
|
2018-07-07 22:14:30 +02:00
|
|
|
pre.text = code_block_text
|
|
|
|
return div
|
|
|
|
|
|
|
|
def replace_element(
|
2021-02-12 08:19:30 +01:00
|
|
|
self,
|
|
|
|
parent: Optional[Element],
|
|
|
|
replacement: Element,
|
|
|
|
element_to_replace: Element,
|
2018-07-07 22:14:30 +02:00
|
|
|
) -> None:
|
|
|
|
if parent is None:
|
|
|
|
return
|
|
|
|
|
2020-06-04 02:15:21 +02:00
|
|
|
for index, child in enumerate(parent):
|
2018-07-07 22:14:30 +02:00
|
|
|
if child is element_to_replace:
|
|
|
|
parent.insert(index, replacement)
|
|
|
|
parent.remove(element_to_replace)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-07-07 22:14:30 +02:00
|
|
|
def makeExtension(*args: Any, **kwargs: str) -> NestedCodeBlocksRenderer:
|
2018-12-20 08:28:40 +01:00
|
|
|
return NestedCodeBlocksRenderer(**kwargs)
|