Skip to content

Commit 6e36833

Browse files
committed
Improve markdown rendering (fix #6)
1 parent f31dcf8 commit 6e36833

File tree

9 files changed

+85
-6
lines changed

9 files changed

+85
-6
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
{ "name": "pydantic", "depth": 2 },
1818
{ "name": "discord", "depth": 3 },
1919
{ "name": "sqlalchemy", "depth": 2 },
20+
{ "name": "marko", "depth": 2 },
2021
],
2122
"sqltools.useNodeRuntime": true,
2223
"sqltools.connections": [

bot/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies = [
2222
"pylette>=4.0.0",
2323
"humanize>=4.13.0",
2424
"babel>=2.17.0",
25+
"marko>=2.2.0",
2526
]
2627

2728
[tool.rye]

bot/src/ghutils/ui/embeds/commits.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from githubkit.exception import GitHubException
1010
from githubkit.rest import Commit, SimpleUser
1111

12-
from ghutils.utils.discord.embeds import set_embed_author
12+
from ghutils.utils.discord.embeds import set_embed_author, truncate_markdown_description
1313
from ghutils.utils.github import (
1414
CommitCheckState,
1515
RepositoryName,
@@ -35,7 +35,7 @@ async def create_commit_embed(
3535
description = None
3636
if "\n" in message:
3737
message, description = message.split("\n", maxsplit=1)
38-
description = truncate_str(description.strip(), 200)
38+
description = truncate_markdown_description(description)
3939

4040
embed = Embed(
4141
title=truncate_str(f"[{short_sha}] {message}", 256),

bot/src/ghutils/ui/embeds/issues.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from githubkit.rest import Issue, IssuePropPullRequest, PullRequest
1010

1111
from ghutils.ui.components.visibility import MessageContents
12-
from ghutils.utils.discord.embeds import set_embed_author
12+
from ghutils.utils.discord.embeds import set_embed_author, truncate_markdown_description
1313
from ghutils.utils.discord.references import IssueReference, IssueReferenceTransformer
1414
from ghutils.utils.github import IssueState, PullRequestState, RepositoryName
1515
from ghutils.utils.strings import truncate_str
@@ -42,7 +42,7 @@ def create_issue_embed(
4242
)
4343

4444
if issue.body and add_body:
45-
embed.description = truncate_str(issue.body, 200)
45+
embed.description = truncate_markdown_description(issue.body)
4646

4747
if issue.user:
4848
set_embed_author(embed, issue.user)

bot/src/ghutils/ui/embeds/releases.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from discord.ui import Button, Item
88
from githubkit.rest import Release
99

10-
from ghutils.utils.discord.embeds import set_embed_author
10+
from ghutils.utils.discord.embeds import set_embed_author, truncate_markdown_description
1111
from ghutils.utils.github import ReleaseState, RepositoryName, get_reactions_by_emoji
1212
from ghutils.utils.strings import truncate_str
1313

@@ -57,7 +57,7 @@ def create_release_embed(
5757
)
5858

5959
if release.body and add_body:
60-
embed.description = truncate_str(release.body, 200)
60+
embed.description = truncate_markdown_description(release.body)
6161

6262
if release.author:
6363
set_embed_author(embed, release.author)

bot/src/ghutils/utils/discord/embeds.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
from __future__ import annotations
22

3+
import re
4+
from typing import overload
5+
36
from discord import Embed
47
from githubkit.rest import SimpleUser
58

9+
from ghutils.utils.markdown import reflow_markdown
10+
from ghutils.utils.strings import truncate_str
11+
612

713
def set_embed_author(embed: Embed, user: SimpleUser):
814
embed.set_author(
@@ -11,3 +17,35 @@ def set_embed_author(embed: Embed, user: SimpleUser):
1117
icon_url=user.avatar_url,
1218
)
1319
return embed
20+
21+
22+
_NEWLINE_HEADING_PATTERN = re.compile(r"(\n|^)\n+(#+ +\S)")
23+
24+
25+
@overload
26+
def truncate_markdown_description(text: str, limit: int = ...) -> str: ...
27+
28+
29+
@overload
30+
def truncate_markdown_description(text: str | None, limit: int = ...) -> str | None: ...
31+
32+
33+
def truncate_markdown_description(text: str | None, limit: int = 256) -> str | None:
34+
if text is None:
35+
return None
36+
37+
text = reflow_markdown(text)
38+
text = _NEWLINE_HEADING_PATTERN.sub(r"\1\2", text)
39+
text = text.strip()
40+
41+
if len(text) <= limit:
42+
return text
43+
44+
# truncate the string at the first whitespace character after overflowing
45+
# TODO: would it be better to truncate before instead?
46+
i = 0
47+
for c in text:
48+
if i >= limit and c.isspace():
49+
break
50+
i += 1
51+
return text[:i] + "..."

bot/src/ghutils/utils/markdown.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import dataclasses
2+
import re
3+
from typing import override
4+
5+
from marko import Markdown
6+
from marko.block import HTMLBlock
7+
from marko.ext.gfm import GFM
8+
from marko.inline import InlineHTML
9+
from marko.md_renderer import MarkdownRenderer
10+
11+
12+
class DiscordMarkdownRenderer(MarkdownRenderer):
13+
@override
14+
def render_html_block(self, element: HTMLBlock) -> str:
15+
return _strip_html_comments(super().render_html_block(element))
16+
17+
@override
18+
def render_inline_html(self, element: InlineHTML) -> str:
19+
return _strip_html_comments(super().render_inline_html(element))
20+
21+
22+
_HTML_COMMENT_PATTERN = re.compile(r"<!--.*?-->")
23+
24+
25+
def _strip_html_comments(text: str) -> str:
26+
return _HTML_COMMENT_PATTERN.sub("", text)
27+
28+
29+
def reflow_markdown(text: str) -> str:
30+
md = Markdown(
31+
renderer=DiscordMarkdownRenderer,
32+
# don't render as GFM
33+
extensions=[dataclasses.replace(GFM, renderer_mixins=[])],
34+
)
35+
return md.convert(text)

requirements-dev.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ jsii==1.103.1
136136
# via constructs
137137
markdown-it-py==3.0.0
138138
# via rich
139+
marko==2.2.0
140+
# via ghutils-bot
139141
markupsafe==2.1.5
140142
# via jinja2
141143
mdurl==0.1.2

requirements.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ jsii==1.103.1
128128
# via constructs
129129
markdown-it-py==3.0.0
130130
# via rich
131+
marko==2.2.0
132+
# via ghutils-bot
131133
markupsafe==2.1.5
132134
# via jinja2
133135
mdurl==0.1.2

0 commit comments

Comments
 (0)