diff --git a/app.py b/app.py
index bc16091..bf2f838 100644
--- a/app.py
+++ b/app.py
@@ -72,6 +72,7 @@ from core.shared import noindex
from core.shared import paginated_query
from utils.blacklist import is_blacklisted
from utils.emojis import EMOJIS
+from utils.highlight import HIGHLIGHT_CSS
from utils.key import get_secret_key
from utils.template_filters import filters
@@ -153,6 +154,7 @@ def inject_config():
else 0,
me=ME,
base_url=config.BASE_URL,
+ highlight_css=HIGHLIGHT_CSS,
)
diff --git a/requirements.txt b/requirements.txt
index 3e81189..5c0a865 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -24,3 +24,4 @@ pyyaml
pillow
emoji-unicode
html5lib
+Pygments
diff --git a/templates/layout.html b/templates/layout.html
index a71eab7..be29498 100644
--- a/templates/layout.html
+++ b/templates/layout.html
@@ -21,6 +21,7 @@
width: 25px;
height: 25px;
}
+{{ highlight_css }}
{% block headers %}{% endblock %}
diff --git a/templates/utils.html b/templates/utils.html
index caeace5..10a2303 100644
--- a/templates/utils.html
+++ b/templates/utils.html
@@ -77,7 +77,7 @@
{% if obj | has_type(['Article', 'Page']) %}
{{ obj.name }} {{ obj | url_or_id | get_url }}
{% elif obj | has_type('Question') %}
- {{ obj.content | clean | replace_custom_emojis(obj) | safe }}
+ {{ obj.content | clean | replace_custom_emojis(obj) | code_highlight | safe }}
@@ -146,7 +146,7 @@
{% else %}
- {{ obj.content | clean | replace_custom_emojis(obj) | safe }}
+ {{ obj.content | clean | replace_custom_emojis(obj) | code_highlight | safe }}
{% endif %}
diff --git a/utils/highlight.py b/utils/highlight.py
new file mode 100644
index 0000000..74fa16f
--- /dev/null
+++ b/utils/highlight.py
@@ -0,0 +1,30 @@
+from functools import lru_cache
+
+from bs4 import BeautifulSoup
+from pygments import highlight as phighlight
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import guess_lexer
+
+from config import THEME_STYLE
+from config import ThemeStyle
+
+_FORMATTER = HtmlFormatter(
+ style="default" if THEME_STYLE == ThemeStyle.LIGHT else "vim"
+)
+
+HIGHLIGHT_CSS = _FORMATTER.get_style_defs()
+
+
+@lru_cache(512)
+def highlight(html: str) -> str:
+ soup = BeautifulSoup(html, "html5lib")
+ for code in soup.find_all("code"):
+ if not code.parent.name == "pre":
+ continue
+ lexer = guess_lexer(code.text)
+ tag = BeautifulSoup(phighlight(code.text, lexer, _FORMATTER)).body.next
+ pre = code.parent
+ pre.replaceWith(tag)
+ out = soup.body
+ out.name = "div"
+ return str(out)
diff --git a/utils/template_filters.py b/utils/template_filters.py
index 11987d1..0e42231 100644
--- a/utils/template_filters.py
+++ b/utils/template_filters.py
@@ -21,6 +21,7 @@ from config import ID
from config import MEDIA_CACHE
from core.activitypub import _answer_key
from utils import parse_datetime
+from utils.highlight import highlight
from utils.media import Kind
from utils.media import _is_img
@@ -47,6 +48,11 @@ def visibility_is_public(v: str) -> bool:
return v in [ap.Visibility.PUBLIC.name, ap.Visibility.UNLISTED.name]
+@filters.app_template_filter()
+def code_highlight(content):
+ return highlight(content)
+
+
@filters.app_template_filter()
def emojify(text):
return emoji_unicode.replace(