Skip to content

Commit 5f8cb1d

Browse files
rsashankkingjunoyuktasarode
committed
messages/views: Add markup to code blocks.
Updated existing tests. New test added. Co-authored-by: kingjuno <[email protected]> Co-authored-by: yuktasarode <[email protected]>
1 parent bbe8250 commit 5f8cb1d

File tree

3 files changed

+122
-15
lines changed

3 files changed

+122
-15
lines changed

tests/ui_tools/test_messages.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ def test_soup2markup(self, content, expected_markup, mocker):
680680
server_url=SERVER_URL,
681681
message_links=OrderedDict(),
682682
time_mentions=list(),
683+
code_blocks=list(),
683684
bq_len=0,
684685
)
685686

@@ -1585,14 +1586,84 @@ def test_keypress_EDIT_MESSAGE(
15851586
# fmt: on
15861587
],
15871588
)
1588-
def test_transform_content(self, mocker, raw_html, expected_content):
1589+
def test_transform_content(self, raw_html, expected_content):
15891590
expected_content = expected_content.replace("{}", QUOTED_TEXT_MARKER)
15901591

15911592
content, *_ = MessageBox.transform_content(raw_html, SERVER_URL)
15921593

15931594
rendered_text = Text(content)
15941595
assert rendered_text.text == expected_content
15951596

1597+
@pytest.mark.parametrize(
1598+
"raw_html, expected_code_blocks",
1599+
[
1600+
(
1601+
"""<div class="codehilite" data-code-language="Python"><pre><span></span><code><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
1602+
<span class="k">return</span><span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
1603+
</code></pre></div>""", # noqa: E501
1604+
[
1605+
(
1606+
"Python",
1607+
[
1608+
("pygments:k", "def"),
1609+
("pygments:w", " "),
1610+
("pygments:nf", "foo"),
1611+
("pygments:p", "("),
1612+
("pygments:n", "x"),
1613+
("pygments:p", "):"),
1614+
("pygments:w", "\n "),
1615+
("pygments:k", "return"),
1616+
("pygments:p", "("),
1617+
("pygments:n", "x"),
1618+
("pygments:o", "+"),
1619+
("pygments:mi", "1"),
1620+
("pygments:p", ")"),
1621+
("pygments:w", "\n"),
1622+
],
1623+
)
1624+
],
1625+
),
1626+
(
1627+
"""<div class="codehilite" data-code-language="JavaScript"><pre><span></span><code><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Hello, world!"</span><span class="p">);</span>
1628+
</code></pre></div>""", # noqa: E501
1629+
[
1630+
(
1631+
"JavaScript",
1632+
[
1633+
("pygments:nx", "console"),
1634+
("pygments:p", "."),
1635+
("pygments:nx", "log"),
1636+
("pygments:p", "("),
1637+
("pygments:s2", '"Hello, world!"'),
1638+
("pygments:p", ");"),
1639+
("pygments:w", "\n"),
1640+
],
1641+
)
1642+
],
1643+
),
1644+
(
1645+
"""<div class="codehilite" data-code-language="Python"><pre><span></span><code><span class="nb">print</span><span class="p">(</span><span class="s2">"Hello, world!"</span><span class="p">)</span>
1646+
</code></pre></div>""", # noqa: E501
1647+
[
1648+
(
1649+
"Python",
1650+
[
1651+
("pygments:nb", "print"),
1652+
("pygments:p", "("),
1653+
("pygments:s2", '"Hello, world!"'),
1654+
("pygments:p", ")"),
1655+
("pygments:w", "\n"),
1656+
],
1657+
)
1658+
],
1659+
),
1660+
],
1661+
)
1662+
def test_transform_content_code_blocks(self, raw_html, expected_code_blocks):
1663+
_, _, _, code_blocks = MessageBox.transform_content(raw_html, SERVER_URL)
1664+
1665+
assert code_blocks == expected_code_blocks
1666+
15961667
# FIXME This is the same parametrize as MsgInfoView:test_height_reactions
15971668
@pytest.mark.parametrize(
15981669
"to_vary_in_each_message, expected_text, expected_attributes",

zulipterminal/ui_tools/messages.py

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def __init__(self, message: Message, model: "Model", last_message: Any) -> None:
6363
self.message_links: Dict[str, Tuple[str, int, bool]] = dict()
6464
self.topic_links: Dict[str, Tuple[str, int, bool]] = dict()
6565
self.time_mentions: List[Tuple[str, str]] = list()
66+
self.code_blocks: List[Tuple[str, List[Tuple[str, str]]]] = list()
6667
self.last_message = last_message
6768
# if this is the first message
6869
if self.last_message is None:
@@ -371,12 +372,22 @@ def footlinks_view(
371372
@classmethod
372373
def soup2markup(
373374
cls, soup: Any, metadata: Dict[str, Any], **state: Any
374-
) -> Tuple[List[Any], Dict[str, Tuple[str, int, bool]], List[Tuple[str, str]]]:
375+
) -> Tuple[
376+
List[Any],
377+
Dict[str, Tuple[str, int, bool]],
378+
List[Tuple[str, str]],
379+
List[Tuple[str, List[Tuple[str, str]]]],
380+
]:
375381
# Ensure a string is provided, in case the soup finds none
376382
# This could occur if eg. an image is removed or not shown
377383
markup: List[Union[str, Tuple[Optional[str], Any]]] = [""]
378384
if soup is None: # This is not iterable, so return promptly
379-
return markup, metadata["message_links"], metadata["time_mentions"]
385+
return (
386+
markup,
387+
metadata["message_links"],
388+
metadata["time_mentions"],
389+
metadata["code_blocks"],
390+
)
380391
unrendered_tags = { # In pairs of 'tag_name': 'text'
381392
# TODO: Some of these could be implemented
382393
"br": "", # No indicator of absence
@@ -552,6 +563,8 @@ def soup2markup(
552563
if code_soup is None:
553564
code_soup = element.pre
554565

566+
code_block = []
567+
555568
for code_element in code_soup.contents:
556569
code_text = (
557570
code_element.text
@@ -562,10 +575,18 @@ def soup2markup(
562575
if code_element.name == "span":
563576
if len(code_text) == 0:
564577
continue
565-
css_style = code_element.attrs.get("class", ["w"])
566-
markup.append((f"pygments:{css_style[0]}", code_text))
578+
css_style = (
579+
f"pygments:{code_element.attrs.get('class', ['w'])[0]}"
580+
)
567581
else:
568-
markup.append(("pygments:w", code_text))
582+
css_style = "pygments:w"
583+
584+
markup.append((css_style, code_text))
585+
code_block.append((css_style, code_text))
586+
587+
code_language = element.attrs.get("data-code-language")
588+
589+
metadata["code_blocks"].append((code_language, code_block))
569590
elif tag in ("strong", "em"):
570591
# BOLD & ITALIC
571592
markup.append(("msg_bold", tag_text))
@@ -634,7 +655,12 @@ def soup2markup(
634655
metadata["time_mentions"].append((time_string, source_text))
635656
else:
636657
markup.extend(cls.soup2markup(element, metadata)[0])
637-
return markup, metadata["message_links"], metadata["time_mentions"]
658+
return (
659+
markup,
660+
metadata["message_links"],
661+
metadata["time_mentions"],
662+
metadata["code_blocks"],
663+
)
638664

639665
def main_view(self) -> List[Any]:
640666
# Recipient Header
@@ -730,11 +756,13 @@ def main_view(self) -> List[Any]:
730756
)
731757

732758
# Transform raw message content into markup (As needed by urwid.Text)
733-
content, self.message_links, self.time_mentions = self.transform_content(
734-
self.message["content"], self.model.server_url
735-
)
759+
(
760+
content,
761+
self.message_links,
762+
self.time_mentions,
763+
self.code_blocks,
764+
) = self.transform_content(self.message["content"], self.model.server_url)
736765
self.content.set_text(content)
737-
738766
if self.message["id"] in self.model.index["edited_messages"]:
739767
edited_label_size = 7
740768
left_padding = 1
@@ -819,6 +847,7 @@ def transform_content(
819847
Tuple[None, Any],
820848
Dict[str, Tuple[str, int, bool]],
821849
List[Tuple[str, str]],
850+
List[Tuple[str, List[Tuple[str, str]]]],
822851
]:
823852
soup = BeautifulSoup(content, "lxml")
824853
body = soup.find(name="body")
@@ -827,13 +856,16 @@ def transform_content(
827856
server_url=server_url,
828857
message_links=dict(),
829858
time_mentions=list(),
859+
code_blocks=list(),
830860
) # type: Dict[str, Any]
831861

832862
if isinstance(body, Tag) and body.find(name="blockquote"):
833863
metadata["bq_len"] = cls.indent_quoted_content(soup, QUOTED_TEXT_MARKER)
834864

835-
markup, message_links, time_mentions = cls.soup2markup(body, metadata)
836-
return (None, markup), message_links, time_mentions
865+
markup, message_links, time_mentions, code_blocks = cls.soup2markup(
866+
body, metadata
867+
)
868+
return (None, markup), message_links, time_mentions, code_blocks
837869

838870
@staticmethod
839871
def indent_quoted_content(soup: Any, padding_char: str) -> int:
@@ -1121,7 +1153,11 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
11211153
self.model.controller.view.middle_column.set_focus("footer")
11221154
elif is_command_key("MSG_INFO", key):
11231155
self.model.controller.show_msg_info(
1124-
self.message, self.topic_links, self.message_links, self.time_mentions
1156+
self.message,
1157+
self.topic_links,
1158+
self.message_links,
1159+
self.time_mentions,
1160+
self.code_blocks,
11251161
)
11261162
elif is_command_key("ADD_REACTION", key):
11271163
self.model.controller.show_emoji_picker(self.message)

zulipterminal/ui_tools/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1421,7 +1421,7 @@ def __init__(self, controller: Any, stream_id: int) -> None:
14211421

14221422
title = f"{stream_marker} {stream['name']}"
14231423
rendered_desc = stream["rendered_description"]
1424-
self.markup_desc, message_links, _ = MessageBox.transform_content(
1424+
self.markup_desc, message_links, _, _ = MessageBox.transform_content(
14251425
rendered_desc,
14261426
self.controller.model.server_url,
14271427
)

0 commit comments

Comments
 (0)