Skip to content

Commit 673d974

Browse files
author
Subhasish-Behera
committed
messages: Added a method transform_content_alert_words.
1 parent 13f3eb0 commit 673d974

File tree

1 file changed

+58
-3
lines changed

1 file changed

+58
-3
lines changed

zulipterminal/ui_tools/messages.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
UI to render a Zulip message for display, and respond contextually to actions
33
"""
44

5+
import re
56
import typing
67
from collections import defaultdict
78
from datetime import date, datetime
89
from time import time
9-
from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union
10+
from typing import Any, Dict, List, Match, NamedTuple, Optional, Tuple, Union
1011
from urllib.parse import urljoin, urlparse
1112

1213
import dateutil.parser
@@ -390,6 +391,9 @@ def soup2markup(
390391
metadata["bq_len"] -= 1
391392
continue
392393
markup.append(element)
394+
elif tag == "span" and "alert-word" in element.get("class", []):
395+
if tag_text:
396+
markup.append(("msg_code", tag_text))
393397
elif tag == "div" and (set(tag_classes) & set(unrendered_div_classes)):
394398
# UNRENDERED DIV CLASSES
395399
# NOTE: Though `matches` is generalized for multiple
@@ -629,6 +633,8 @@ def main_view(self) -> List[Any]:
629633
else:
630634
recipient_header = None
631635

636+
self.alerted_words: Optional[Any] = self.model.get_alert_words()
637+
632638
# Content Header
633639
message = {
634640
key: {
@@ -717,7 +723,11 @@ def main_view(self) -> List[Any]:
717723

718724
# Transform raw message content into markup (As needed by urwid.Text)
719725
content, self.message_links, self.time_mentions = self.transform_content(
720-
self.message["content"], self.model.server_url
726+
self.message["content"],
727+
self.model.server_url,
728+
self.alerted_words,
729+
"has_alert_word" in self.message["flags"]
730+
# self.message["flags"],
721731
)
722732
self.content.set_text(content)
723733

@@ -798,14 +808,59 @@ def update_message_author_status(self) -> bool:
798808

799809
return author_is_present
800810

811+
@staticmethod
812+
def transform_content_alert_words(content: str, alerted_list: List[str]) -> Any:
813+
alert_regex_replacements = {
814+
"&": "&",
815+
"<": "&lt;",
816+
">": "&gt;",
817+
# Accept quotes with or without HTML escaping
818+
'"': r"(?:\"|&quot;)",
819+
"'": r"(?:\'|&#39;)",
820+
}
821+
822+
before_punctuation = r"\s|^|>|[\\(\".,';\\[]"
823+
824+
after_punctuation = r"(?=\s|$|<|[\\)\"?!:.,';\]!])"
825+
826+
clean: str
827+
828+
def replace_callback(match: Match[Union[str, str, str]]) -> str:
829+
before = match.group(1)
830+
word = match.group(2)
831+
after = match.group(3)
832+
offset = match.start()
833+
matched_content = match.string
834+
pre_match = matched_content[:offset]
835+
check_string = pre_match + match.group()
836+
in_tag = check_string.rfind("<") > check_string.rfind(">")
837+
if in_tag:
838+
return f"{before}{word}{after}"
839+
return f"{before}<span class='alert-word'>{word}</span>{after}"
840+
841+
for word in alerted_list:
842+
clean = "".join(alert_regex_replacements.get(c, c) for c in word)
843+
regex = f"({before_punctuation})({clean})({after_punctuation})"
844+
regex1 = re.compile(regex, re.IGNORECASE)
845+
content = re.sub(regex1, replace_callback, content)
846+
847+
return content
848+
801849
@classmethod
802850
def transform_content(
803-
cls, content: Any, server_url: str
851+
cls,
852+
content: Any,
853+
server_url: str,
854+
alerted_list: List[str] = list(),
855+
alert_word_present: bool = False,
804856
) -> Tuple[
805857
Tuple[None, Any],
806858
Dict[str, Tuple[str, int, bool]],
807859
List[Tuple[str, str]],
808860
]:
861+
if alert_word_present:
862+
content = cls.transform_content_alert_words(content, alerted_list)
863+
809864
soup = BeautifulSoup(content, "lxml")
810865
body = soup.find(name="body")
811866

0 commit comments

Comments
 (0)