Skip to content

Commit e2325e5

Browse files
messages: Added a method transform_content_alert_words which adds <span class='alert-word'> around the transform_content_alert_words
1 parent db111e2 commit e2325e5

File tree

1 file changed

+57
-3
lines changed

1 file changed

+57
-3
lines changed

zulipterminal/ui_tools/messages.py

Lines changed: 57 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 OrderedDict, 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
@@ -392,6 +393,9 @@ def soup2markup(
392393
metadata["bq_len"] -= 1
393394
continue
394395
markup.append(element)
396+
elif tag == "span" and "alert-word" in element.get("class", []):
397+
if tag_text:
398+
markup.append(("msg_code", tag_text))
395399
elif tag == "div" and (set(tag_classes) & set(unrendered_div_classes)):
396400
# UNRENDERED DIV CLASSES
397401
# NOTE: Though `matches` is generalized for multiple
@@ -626,6 +630,8 @@ def main_view(self) -> List[Any]:
626630
else:
627631
recipient_header = None
628632

633+
self.alerted_words = self.model.alert_words
634+
629635
# Content Header
630636
message = {
631637
key: {
@@ -715,7 +721,11 @@ def main_view(self) -> List[Any]:
715721

716722
# Transform raw message content into markup (As needed by urwid.Text)
717723
content, self.message_links, self.time_mentions = self.transform_content(
718-
self.message["content"], self.model.server_url
724+
self.message["content"],
725+
self.model.server_url,
726+
self.alerted_words,
727+
"has_alert_word" in self.message["flags"]
728+
# self.message["flags"],
719729
)
720730
self.content.set_text(content)
721731

@@ -796,14 +806,58 @@ def update_message_author_status(self) -> bool:
796806

797807
return author_is_present
798808

809+
def transform_content_alert_words(content: str, alerted_list: List[str]) -> Any:
810+
alert_regex_replacements = {
811+
"&": "&amp;",
812+
"<": "&lt;",
813+
">": "&gt;",
814+
# Accept quotes with or without HTML escaping
815+
'"': r"(?:\"|&quot;)",
816+
"'": r"(?:\'|&#39;)",
817+
}
818+
819+
before_punctuation = r"\s|^|>|[\\(\".,';\\[]"
820+
821+
after_punctuation = r"(?=\s|$|<|[\\)\"?!:.,';\]!])"
822+
823+
clean: str
824+
825+
def replace_callback(match: Match[Union[str, str, str]]) -> str:
826+
before = match.group(1)
827+
word = match.group(2)
828+
after = match.group(3)
829+
offset = match.start()
830+
matched_content = match.string
831+
pre_match = matched_content[:offset]
832+
check_string = pre_match + match.group()
833+
in_tag = check_string.rfind("<") > check_string.rfind(">")
834+
if in_tag:
835+
return f"{before}{word}{after}"
836+
return f"{before}<span class='alert-word'>{word}</span>{after}"
837+
838+
for word in alerted_list:
839+
clean = "".join(alert_regex_replacements.get(c, c) for c in word)
840+
regex = f"({before_punctuation})({clean})({after_punctuation})"
841+
regex1 = re.compile(regex, re.IGNORECASE)
842+
content = re.sub(regex1, replace_callback, content)
843+
844+
return content
845+
799846
@classmethod
800847
def transform_content(
801-
cls, content: Any, server_url: str
848+
cls,
849+
content: Any,
850+
server_url: str,
851+
alerted_list: List[str] = list(),
852+
alert_word_present: bool = False,
802853
) -> Tuple[
803854
Tuple[None, Any],
804855
"OrderedDict[str, Tuple[str, int, bool]]",
805856
List[Tuple[str, str]],
806857
]:
858+
if alert_word_present:
859+
content = cls.transform_content_alert_words(content, alerted_list)
860+
807861
soup = BeautifulSoup(content, "lxml")
808862
body = soup.find(name="body")
809863

0 commit comments

Comments
 (0)