mirror of https://github.com/zulip/zulip.git
markdown: Add support for a pretty syntax for message links.
Links to zulip messages can now be written as `#**channel_name > topic_name @ message_id**.` The `message_id` is replaced with `💬` in the rendered message. Fixes part of #31920
This commit is contained in:
parent
65c9b249b7
commit
000cc7bcde
|
@ -194,6 +194,31 @@ def get_compiled_stream_topic_link_regex() -> Pattern[str]:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
STREAM_TOPIC_MESSAGE_LINK_REGEX = rf"""
|
||||||
|
{BEFORE_MENTION_ALLOWED_REGEX} # Start after whitespace or specified chars
|
||||||
|
\#\*\* # and after hash sign followed by double asterisks
|
||||||
|
(?P<stream_name>[^\*>]+) # stream name can contain anything except >
|
||||||
|
> # > acts as separator
|
||||||
|
(?P<topic_name>[^\*]+) # topic name can contain anything
|
||||||
|
@
|
||||||
|
(?P<message_id>\d+) # message id
|
||||||
|
\*\* # ends by double asterisks
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(None)
|
||||||
|
def get_compiled_stream_topic_message_link_regex() -> Pattern[str]:
|
||||||
|
# Not using verbose_compile as it adds ^(.*?) and
|
||||||
|
# (.*?)$ which cause extra overhead of matching
|
||||||
|
# pattern which is not required.
|
||||||
|
# With new InlineProcessor these extra patterns
|
||||||
|
# are not required.
|
||||||
|
return re.compile(
|
||||||
|
STREAM_TOPIC_MESSAGE_LINK_REGEX,
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(None)
|
@lru_cache(None)
|
||||||
def get_web_link_regex() -> Pattern[str]:
|
def get_web_link_regex() -> Pattern[str]:
|
||||||
# We create this one time, but not at startup. So the
|
# We create this one time, but not at startup. So the
|
||||||
|
@ -2041,6 +2066,29 @@ class StreamTopicPattern(StreamTopicMessageProcessor):
|
||||||
return el, m.start(), m.end()
|
return el, m.start(), m.end()
|
||||||
|
|
||||||
|
|
||||||
|
class StreamTopicMessagePattern(StreamTopicMessageProcessor):
|
||||||
|
@override
|
||||||
|
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
|
||||||
|
self, m: Match[str], data: str
|
||||||
|
) -> tuple[Element | str | None, int | None, int | None]:
|
||||||
|
stream_name = m.group("stream_name")
|
||||||
|
topic_name = m.group("topic_name")
|
||||||
|
message_id = m.group("message_id")
|
||||||
|
|
||||||
|
stream_id = self.find_stream_id(stream_name)
|
||||||
|
if stream_id is None or topic_name is None:
|
||||||
|
return None, None, None
|
||||||
|
el = Element("a")
|
||||||
|
el.set("class", "message-link")
|
||||||
|
stream_url = encode_stream(stream_id, stream_name)
|
||||||
|
topic_url = hash_util_encode(topic_name)
|
||||||
|
link = f"/#narrow/channel/{stream_url}/topic/{topic_url}/near/{message_id}"
|
||||||
|
el.set("href", link)
|
||||||
|
text = f"#{stream_name} > {topic_name} @ 💬"
|
||||||
|
el.text = markdown.util.AtomicString(text)
|
||||||
|
return el, m.start(), m.end()
|
||||||
|
|
||||||
|
|
||||||
def possible_linked_stream_names(content: str) -> set[str]:
|
def possible_linked_stream_names(content: str) -> set[str]:
|
||||||
return {
|
return {
|
||||||
*re.findall(STREAM_LINK_REGEX, content, re.VERBOSE),
|
*re.findall(STREAM_LINK_REGEX, content, re.VERBOSE),
|
||||||
|
@ -2305,6 +2353,11 @@ class ZulipMarkdown(markdown.Markdown):
|
||||||
)
|
)
|
||||||
reg.register(UserMentionPattern(mention.MENTIONS_RE, self), "usermention", 95)
|
reg.register(UserMentionPattern(mention.MENTIONS_RE, self), "usermention", 95)
|
||||||
reg.register(Tex(TEX_RE, self), "tex", 90)
|
reg.register(Tex(TEX_RE, self), "tex", 90)
|
||||||
|
reg.register(
|
||||||
|
StreamTopicMessagePattern(get_compiled_stream_topic_message_link_regex(), self),
|
||||||
|
"stream_topic_message",
|
||||||
|
89,
|
||||||
|
)
|
||||||
reg.register(StreamTopicPattern(get_compiled_stream_topic_link_regex(), self), "topic", 87)
|
reg.register(StreamTopicPattern(get_compiled_stream_topic_link_regex(), self), "topic", 87)
|
||||||
reg.register(StreamPattern(get_compiled_stream_link_regex(), self), "stream", 85)
|
reg.register(StreamPattern(get_compiled_stream_link_regex(), self), "stream", 85)
|
||||||
reg.register(Timestamp(TIMESTAMP_RE), "timestamp", 75)
|
reg.register(Timestamp(TIMESTAMP_RE), "timestamp", 75)
|
||||||
|
|
|
@ -3108,6 +3108,28 @@ class MarkdownStreamMentionTests(ZulipTestCase):
|
||||||
".</p>",
|
".</p>",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_message_id_multiple(self) -> None:
|
||||||
|
denmark = get_stream("Denmark", get_realm("zulip"))
|
||||||
|
sender_user_profile = self.example_user("othello")
|
||||||
|
msg = Message(
|
||||||
|
sender=sender_user_profile,
|
||||||
|
sending_client=get_client("test"),
|
||||||
|
realm=sender_user_profile.realm,
|
||||||
|
)
|
||||||
|
content = "As mentioned in #**Denmark>danish@123** and #**Denmark>danish@456**."
|
||||||
|
self.assertEqual(
|
||||||
|
render_message_markdown(msg, content).rendered_content,
|
||||||
|
"<p>As mentioned in "
|
||||||
|
f'<a class="message-link" '
|
||||||
|
f'href="/#narrow/channel/{denmark.id}-{denmark.name}/topic/danish/near/123">'
|
||||||
|
f"#Denmark > danish @ 💬</a>"
|
||||||
|
" and "
|
||||||
|
f'<a class="message-link" '
|
||||||
|
f'href="/#narrow/channel/{denmark.id}-{denmark.name}/topic/danish/near/456">'
|
||||||
|
f"#Denmark > danish @ 💬</a>"
|
||||||
|
".</p>",
|
||||||
|
)
|
||||||
|
|
||||||
def test_possible_stream_names(self) -> None:
|
def test_possible_stream_names(self) -> None:
|
||||||
content = """#**test here**
|
content = """#**test here**
|
||||||
This mentions #**Denmark** too.
|
This mentions #**Denmark** too.
|
||||||
|
|
Loading…
Reference in New Issue