Add code block
This commit is contained in:
parent
8f6dc4860b
commit
a1d72c122a
17 changed files with 2977 additions and 0 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
@ -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()),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
0
website/contrib/README.md
Normal file
0
website/contrib/README.md
Normal file
0
website/contrib/__init__.py
Normal file
0
website/contrib/__init__.py
Normal file
3
website/contrib/code_block/README.md
Normal file
3
website/contrib/code_block/README.md
Normal 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.
|
0
website/contrib/code_block/__init__.py
Normal file
0
website/contrib/code_block/__init__.py
Normal file
33
website/contrib/code_block/blocks.py
Normal file
33
website/contrib/code_block/blocks.py
Normal 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"
|
|
@ -0,0 +1 @@
|
||||||
|
{{ value.code }}
|
20
website/contrib/code_block/tests.py
Normal file
20
website/contrib/code_block/tests.py
Normal 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)
|
7
website/contrib/code_block/urls.py
Normal file
7
website/contrib/code_block/urls.py
Normal 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")]
|
16
website/contrib/code_block/views.py
Normal file
16
website/contrib/code_block/views.py
Normal 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"
|
||||||
|
)
|
|
@ -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",
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in a new issue