Add code block

This commit is contained in:
Jake Howard 2022-06-27 23:29:55 +01:00
parent 8f6dc4860b
commit a1d72c122a
Signed by: jake
GPG key ID: 57AFB45680EDD477
17 changed files with 2977 additions and 0 deletions

View file

@ -2,3 +2,4 @@ Django==4.0.5
wagtail==3.0 wagtail==3.0
django-environ==0.8.1 django-environ==0.8.1
whitenoise[brotli]==6.2.0 whitenoise[brotli]==6.2.0
pygments==2.12.0

View file

@ -1,3 +1,4 @@
@use "bulma/sass/utilities/initial-variables" as *; @use "bulma/sass/utilities/initial-variables" as *;
$container-max-width: $widescreen; $container-max-width: $widescreen;
$pre-background: unset;

View file

@ -16,6 +16,7 @@
<link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'contrib/fontawesome/css/all.min.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'contrib/fontawesome/css/all.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% url 'static-pygments:styles' 'default' %}">
{% block extra_css %}{% endblock %} {% block extra_css %}{% endblock %}
</head> </head>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,8 @@ from wagtail import blocks
from wagtail.embeds.blocks import EmbedBlock from wagtail.embeds.blocks import EmbedBlock
from wagtail.images.blocks import ImageChooserBlock from wagtail.images.blocks import ImageChooserBlock
from website.contrib.code_block.blocks import CodeBlock
IGNORE_PLAINTEXT_BLOCKS = (blocks.RawHTMLBlock, EmbedBlock) IGNORE_PLAINTEXT_BLOCKS = (blocks.RawHTMLBlock, EmbedBlock)
RICH_TEXT_FEATURES = [ RICH_TEXT_FEATURES = [
@ -68,6 +70,7 @@ def get_blocks() -> list[tuple[str, blocks.BaseBlock]]:
("lorem", LoremBlock()), ("lorem", LoremBlock()),
("html", blocks.RawHTMLBlock()), ("html", blocks.RawHTMLBlock()),
("image", ImageCaptionBlock()), ("image", ImageCaptionBlock()),
("code", CodeBlock()),
] ]

View file

View file

View file

@ -0,0 +1,3 @@
# `code_block`
Heavily based on [`wagtail-pygments`](https://github.com/truongvan/wagtail-pygments), with several changes to the block, as well as a view for exposing the CSS styles.

View file

View file

@ -0,0 +1,33 @@
from typing import Iterator
from django.utils.safestring import mark_safe
from pygments import highlight
from pygments.formatters.html import HtmlFormatter
from pygments.lexers import get_all_lexers, get_lexer_by_name
from wagtail.blocks import ChoiceBlock, StructBlock, StructValue, TextBlock
def get_language_choices() -> Iterator[tuple[str, str]]:
for name, _, _, _ in sorted(get_all_lexers()):
yield (name, name)
class CodeStructValue(StructValue):
def code(self) -> str:
lexer = get_lexer_by_name(self.get("language"))
formatter = HtmlFormatter(
linenos=None,
)
return mark_safe(highlight(self.get("source"), lexer, formatter))
class CodeBlock(StructBlock):
language = ChoiceBlock(
choices=get_language_choices(),
)
source = TextBlock()
class Meta:
icon = "code"
value_class = CodeStructValue
template = "contrib/blocks/code.html"

View file

@ -0,0 +1 @@
{{ value.code }}

View file

@ -0,0 +1,20 @@
from django.test import TestCase
from django.urls import reverse
from pygments.styles import get_all_styles
class PygmentsStylesTestCase(TestCase):
def test_accessible(self) -> None:
for style in get_all_styles():
with self.subTest(style=style):
response = self.client.get(
reverse("static-pygments:styles", args=[style])
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Cache-Control"], "max-age=3600, public")
def test_unknown_style(self) -> None:
response = self.client.get(
reverse("static-pygments:styles", args=["not-a-style"])
)
self.assertEqual(response.status_code, 404)

View file

@ -0,0 +1,7 @@
from django.urls import path
from .views import pygments_styles
app_name = "code_block"
urlpatterns = [path("<slug:name>.css", pygments_styles, name="styles")]

View file

@ -0,0 +1,16 @@
from django.http import Http404, HttpRequest, HttpResponse
from django.views.decorators.cache import cache_control
from pygments.formatters.html import HtmlFormatter
from pygments.util import ClassNotFound
@cache_control(max_age=3600, public=True)
def pygments_styles(request: HttpRequest, name: str) -> HttpResponse:
try:
formatter = HtmlFormatter(style=name)
except ClassNotFound:
# Raising an exception here bypasses the cache header
raise Http404
return HttpResponse(
formatter.get_style_defs("." + formatter.cssclass), content_type="text/css"
)

View file

@ -23,6 +23,7 @@ INSTALLED_APPS = [
"website.home", "website.home",
"website.search", "website.search",
"website.blog", "website.blog",
"website.contrib.code_block",
"wagtail.contrib.forms", "wagtail.contrib.forms",
"wagtail.contrib.redirects", "wagtail.contrib.redirects",
"wagtail.contrib.modeladmin", "wagtail.contrib.modeladmin",

View file

@ -13,6 +13,10 @@ urlpatterns = [
path("admin/", include(wagtailadmin_urls)), path("admin/", include(wagtailadmin_urls)),
path("documents/", include(wagtaildocs_urls)), path("documents/", include(wagtaildocs_urls)),
path("search/", search_views.search, name="search"), path("search/", search_views.search, name="search"),
path(
"static-pygments/",
include("website.contrib.code_block.urls", namespace="static-pygments"),
),
re_path( re_path(
r"^images/([^/]*)/(\d*)/([^/]*)/[^/]*$", r"^images/([^/]*)/(\d*)/([^/]*)/[^/]*$",
ServeView.as_view(action="redirect"), ServeView.as_view(action="redirect"),