Colour language tag using GitHub's linguist colours
This commit is contained in:
parent
e504da0bcf
commit
d85065d914
6 changed files with 67 additions and 10 deletions
|
@ -17,3 +17,4 @@ flake8-mutable==1.2.0
|
||||||
flake8-print==5.0.0
|
flake8-print==5.0.0
|
||||||
flake8-tuple==0.4.1
|
flake8-tuple==0.4.1
|
||||||
djlint==1.19.2
|
djlint==1.19.2
|
||||||
|
types-pyyaml==6.0.12.9
|
||||||
|
|
|
@ -80,6 +80,14 @@ div.block-tangent {
|
||||||
div.block-code {
|
div.block-code {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
pre,
|
||||||
|
span,
|
||||||
|
& {
|
||||||
|
@include dark-mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.code-header {
|
.code-header {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-family: $family-code;
|
font-family: $family-code;
|
||||||
|
@ -103,19 +111,17 @@ div.block-code {
|
||||||
|
|
||||||
.language-tag {
|
.language-tag {
|
||||||
background-color: $primary-light;
|
background-color: $primary-light;
|
||||||
|
font-weight: $weight-medium;
|
||||||
|
|
||||||
@include dark-mode {
|
@include dark-mode {
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight {
|
&.linguist-color span {
|
||||||
pre,
|
// HACK: Naive inverse threshold of the colour for optimum contrast
|
||||||
span,
|
filter: contrast(1000) grayscale(100) invert(1);
|
||||||
& {
|
}
|
||||||
@include dark-mode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,4 @@ $code-padding: 0;
|
||||||
$code: inherit;
|
$code: inherit;
|
||||||
$pre: unset;
|
$pre: unset;
|
||||||
$content-heading-weight: $weight-medium;
|
$content-heading-weight: $weight-medium;
|
||||||
$content-pre-padding: 1.5em 1.25em 1.25em;
|
$content-pre-padding: 1.5em;
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from pygments import highlight
|
from pygments import highlight
|
||||||
from pygments.formatters.html import HtmlFormatter
|
from pygments.formatters.html import HtmlFormatter
|
||||||
from pygments.lexers import find_lexer_class, get_all_lexers
|
from pygments.lexers import find_lexer_class, get_all_lexers
|
||||||
from pygments.lexers.python import PythonConsoleLexer, PythonLexer
|
from pygments.lexers.python import PythonConsoleLexer, PythonLexer
|
||||||
|
from pygments.lexers.shell import BashLexer, BashSessionLexer
|
||||||
from pygments.lexers.special import TextLexer
|
from pygments.lexers.special import TextLexer
|
||||||
from wagtail.blocks import CharBlock, ChoiceBlock, StructBlock, StructValue, TextBlock
|
from wagtail.blocks import CharBlock, ChoiceBlock, StructBlock, StructValue, TextBlock
|
||||||
|
|
||||||
|
from .utils import get_linguist_colours
|
||||||
|
|
||||||
|
|
||||||
def get_language_choices() -> Iterator[tuple[str, str]]:
|
def get_language_choices() -> Iterator[tuple[str, str]]:
|
||||||
for name, _, _, _ in sorted(get_all_lexers()):
|
for name, _, _, _ in sorted(get_all_lexers()):
|
||||||
|
@ -17,6 +21,8 @@ def get_language_choices() -> Iterator[tuple[str, str]]:
|
||||||
class CodeStructValue(StructValue):
|
class CodeStructValue(StructValue):
|
||||||
LANGUAGE_DISPLAY_MAPPING = {PythonConsoleLexer.name: PythonLexer.name}
|
LANGUAGE_DISPLAY_MAPPING = {PythonConsoleLexer.name: PythonLexer.name}
|
||||||
|
|
||||||
|
LINGUIST_MAPPING = {BashLexer.name: "Shell", BashSessionLexer.name: "Shell"}
|
||||||
|
|
||||||
def code(self) -> str:
|
def code(self) -> str:
|
||||||
lexer = find_lexer_class(self["language"])()
|
lexer = find_lexer_class(self["language"])()
|
||||||
formatter = HtmlFormatter(
|
formatter = HtmlFormatter(
|
||||||
|
@ -39,6 +45,25 @@ class CodeStructValue(StructValue):
|
||||||
"""
|
"""
|
||||||
return self.LANGUAGE_DISPLAY_MAPPING.get(self["language"], self["language"])
|
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
|
||||||
|
|
||||||
|
|
||||||
class CodeBlock(StructBlock):
|
class CodeBlock(StructBlock):
|
||||||
filename = CharBlock(max_length=128, required=False)
|
filename = CharBlock(max_length=128, required=False)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{% if value.header_text %}
|
{% if value.header_text %}
|
||||||
<div class="code-header tags">
|
<div class="code-header">
|
||||||
{% if value.header_text %}<span class="tag ml-auto mr-5 language-tag" title="{{ value.language }}">{{ value.header_text }}</span>{% endif %}
|
{% if value.header_text %}
|
||||||
|
<span class="tag ml-auto mr-5 language-tag {% if value.colour %}linguist-color{% endif %}" title="{{ value.language }}" {% if value.colour %}style="background-color: {{ value.colour }}"{% endif %}>
|
||||||
|
<span {% if value.colour %}style="color: {{ value.colour }}"{% endif %}>{{ value.header_text }}</span>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
<span class="tag code-copy-tag">
|
<span class="tag code-copy-tag">
|
||||||
<i class="fa-regular fa-clipboard is-clickable code-copy" title="Copy to clipboard"></i>
|
<i class="fa-regular fa-clipboard is-clickable code-copy" title="Copy to clipboard"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,4 +1,25 @@
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import yaml
|
||||||
|
from django_cache_decorator import django_cache_decorator
|
||||||
|
|
||||||
PYGMENTS_VERSION = version("pygments")
|
PYGMENTS_VERSION = version("pygments")
|
||||||
PYGMENTS_VERSION_SLUG = PYGMENTS_VERSION.replace(".", "-")
|
PYGMENTS_VERSION_SLUG = PYGMENTS_VERSION.replace(".", "-")
|
||||||
|
|
||||||
|
LINGUIST_DATA_URL = "https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml"
|
||||||
|
|
||||||
|
|
||||||
|
@django_cache_decorator(time=21600)
|
||||||
|
def get_linguist_colours() -> dict[str, str]:
|
||||||
|
response = requests.get(LINGUIST_DATA_URL)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
linguist_data = yaml.safe_load(response.text)
|
||||||
|
|
||||||
|
return {
|
||||||
|
language.lower(): l["color"]
|
||||||
|
for language, l in linguist_data.items()
|
||||||
|
if l.get("color")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue