website/website/contrib/code_block/blocks.py

88 lines
2.6 KiB
Python
Raw Permalink Normal View History

2022-06-27 23:29:55 +01:00
from typing import Iterator
from django.utils.functional import cached_property
2022-06-27 23:29:55 +01:00
from django.utils.safestring import mark_safe
from pygments import highlight
from pygments.formatters.html import HtmlFormatter
from pygments.lexers import find_lexer_class, get_all_lexers
from pygments.lexers.python import PythonConsoleLexer, PythonLexer
from pygments.lexers.shell import BashLexer, BashSessionLexer
from pygments.lexers.special import TextLexer
from pygments.lexers.sql import PostgresConsoleLexer, PostgresLexer
from wagtail.blocks import CharBlock, ChoiceBlock, StructBlock, StructValue, TextBlock
2022-06-27 23:29:55 +01:00
from .utils import get_linguist_colours
2022-06-27 23:29:55 +01:00
def get_language_choices() -> Iterator[tuple[str, str]]:
for name, _, _, _ in sorted(get_all_lexers()):
yield (name, name.replace("+", " + "))
2022-06-27 23:29:55 +01:00
class CodeStructValue(StructValue):
LANGUAGE_DISPLAY_MAPPING = {
PythonConsoleLexer.name: PythonLexer.name,
PostgresLexer.name: "PostgreSQL",
}
LINGUIST_MAPPING = {
BashLexer.name: "Shell",
BashSessionLexer.name: "Shell",
PostgresLexer.name: "PLpgSQL",
PostgresConsoleLexer.name: "PLpgSQL",
}
2022-06-27 23:29:55 +01:00
def code(self) -> str:
lexer = find_lexer_class(self["language"])()
2022-06-27 23:29:55 +01:00
formatter = HtmlFormatter(
linenos=None,
)
return mark_safe(highlight(self["source"], lexer, formatter))
2022-06-27 23:29:55 +01:00
def header_text(self) -> str:
if filename := self["filename"]:
return filename
if self["language"] != TextLexer.name:
return self.language_display()
return ""
def language_display(self) -> str:
"""
Map ugly language names to something more useful
"""
return self.LANGUAGE_DISPLAY_MAPPING.get(self["language"], self["language"])
@cached_property
def colour(self) -> str | None:
"""
Expose the colour denoted by GitHub's linguist.
"""
linguist_colours = get_linguist_colours()
language = self.LINGUIST_MAPPING.get(self["language"], self["language"])
if exact_match := linguist_colours.get(language.lower()):
return exact_match
if language_display_match := linguist_colours.get(
self.LANGUAGE_DISPLAY_MAPPING.get(language, "").lower()
):
return language_display_match
return None
2022-06-27 23:29:55 +01:00
class CodeBlock(StructBlock):
filename = CharBlock(max_length=128, required=False)
2022-06-27 23:29:55 +01:00
language = ChoiceBlock(
choices=get_language_choices,
2022-06-27 23:29:55 +01:00
)
source = TextBlock()
class Meta:
icon = "code"
value_class = CodeStructValue
template = "contrib/blocks/code.html"