docs: Create a markdown-it plugin

This commit is contained in:
Quentin Boyer 2024-09-11 19:12:19 +02:00 committed by Matt Sturgeon
parent 6665521525
commit cab2a30ae1
No known key found for this signature in database
GPG key ID: 4F91844CED1A8299
12 changed files with 751 additions and 0 deletions

View file

@ -55,6 +55,8 @@ in
{
inherit options-json;
gfm-alerts-to-admonitions = pkgs.python3.pkgs.callPackage ./gfm-alerts-to-admonitions { };
man-docs = pkgs.callPackage ./man { inherit options-json; };
}
// lib.optionalAttrs (!pkgs.stdenv.isDarwin) (

View file

@ -0,0 +1,30 @@
{
buildPythonPackage,
pytestCheckHook,
markdown-it-py,
lib,
setuptools,
pytest-regressions,
}:
buildPythonPackage {
pname = "gfm-alerts-to-admonitions";
version = "0.0";
format = "pyproject";
src = ./.;
build-system = [ setuptools ];
dependencies = [ markdown-it-py ];
nativeCheckInputs = [
pytestCheckHook
pytest-regressions
];
pythonImportsCheck = [ "gfm_alerts_to_admonitions" ];
meta = {
description = "Transform GFM alerts to nixos-render-docs admonitions";
license = lib.licenses.mit;
maintainers = [ lib.maintainers.traxys ];
};
}

View file

@ -0,0 +1 @@
from .main import gfm_alert_to_admonition as gfm_alert_to_admonition

View file

@ -0,0 +1,55 @@
import re
from re import Match
from markdown_it import MarkdownIt
from markdown_it.rules_core import StateCore
from markdown_it.token import Token
_ALERT_PATTERN = re.compile(
r"^\[\!(TIP|NOTE|IMPORTANT|WARNING|CAUTION)\]\s*", re.IGNORECASE
)
def gfm_alert_to_admonition(md: MarkdownIt):
def gfm_alert_to_adm(state: StateCore):
# Scan all tokens, looking for blockquotes,
# convert any alert-style blockquotes to admonition tokens
tokens: list[Token] = state.tokens
for i, token in enumerate(tokens):
if token.type == "blockquote_open":
# Store the blockquote's opening token
open = tokens[i]
start_index = i
# Find the blockquote's closing token
while tokens[i].type != "blockquote_close" and i < len(tokens):
i += 1
close = tokens[i]
end_index = i
# Find the first `inline` token in the blockquote
first_content = next(
(
t
for t in tokens[start_index : end_index + 1]
if t.type == "inline"
),
None,
)
if first_content is None:
continue
# Check if the blockquote is actually an alert
m: Match = _ALERT_PATTERN.match(first_content.content)
if m is None:
continue
# Remove ' [!TIP]' from the token's content
first_content.content = first_content.content[m.end(0) :]
# Convert the opening & closing tokens from "blockquote" to "admonition"
open.type = "admonition_open"
open.meta["kind"] = m.group(1).lower()
close.type = "admonition_close"
md.core.ruler.after("block", "github-alerts", gfm_alert_to_adm)

View file

@ -0,0 +1,15 @@
[project]
name = "gfm-alerts-to-admonitions"
version = "0.0"
description = "MarkdownIt plugin to convert gfm alerts to nixos-render-docs admonitions"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
[build-system]
requires = ["setuptools"]
[tool.pytest.ini_options]
pythonpath = ["."]

View file

@ -0,0 +1,25 @@
import pytest
from gfm_alerts_to_admonitions import gfm_alert_to_admonition
from markdown_it import MarkdownIt
@pytest.mark.parametrize("kind", ["tip", "note", "important", "warning", "caution"])
def test_parse(data_regression, kind):
input = f"> [!{kind.upper()}]\n> This is an *alert*"
md = MarkdownIt("commonmark").use(gfm_alert_to_admonition)
tokens = md.parse(input)
data_regression.check([t.as_dict() for t in tokens])
def test_case():
def make_input(kind):
return f"> [!{kind}]\n> tip"
md = MarkdownIt("commonmark").use(gfm_alert_to_admonition)
reference = [t.as_dict() for t in md.parse(make_input("TIP"))]
lower = [t.as_dict() for t in md.parse(make_input("tip"))]
mixed = [t.as_dict() for t in md.parse(make_input("tIp"))]
assert lower == reference
assert mixed == reference

View file

@ -0,0 +1,124 @@
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map:
- 0
- 2
markup: '>'
meta:
kind: caution
nesting: 1
tag: blockquote
type: admonition_open
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map:
- 0
- 2
markup: ''
meta: {}
nesting: 1
tag: p
type: paragraph_open
- attrs: null
block: true
children:
- attrs: null
block: false
children: null
content: 'This is an '
hidden: false
info: ''
level: 0
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: 1
tag: em
type: em_open
- attrs: null
block: false
children: null
content: alert
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: -1
tag: em
type: em_close
content: This is an *alert*
hidden: false
info: ''
level: 2
map:
- 0
- 2
markup: ''
meta: {}
nesting: 0
tag: ''
type: inline
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: -1
tag: p
type: paragraph_close
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '>'
meta: {}
nesting: -1
tag: blockquote
type: admonition_close

View file

@ -0,0 +1,124 @@
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map:
- 0
- 2
markup: '>'
meta:
kind: important
nesting: 1
tag: blockquote
type: admonition_open
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map:
- 0
- 2
markup: ''
meta: {}
nesting: 1
tag: p
type: paragraph_open
- attrs: null
block: true
children:
- attrs: null
block: false
children: null
content: 'This is an '
hidden: false
info: ''
level: 0
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: 1
tag: em
type: em_open
- attrs: null
block: false
children: null
content: alert
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: -1
tag: em
type: em_close
content: This is an *alert*
hidden: false
info: ''
level: 2
map:
- 0
- 2
markup: ''
meta: {}
nesting: 0
tag: ''
type: inline
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: -1
tag: p
type: paragraph_close
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '>'
meta: {}
nesting: -1
tag: blockquote
type: admonition_close

View file

@ -0,0 +1,124 @@
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map:
- 0
- 2
markup: '>'
meta:
kind: note
nesting: 1
tag: blockquote
type: admonition_open
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map:
- 0
- 2
markup: ''
meta: {}
nesting: 1
tag: p
type: paragraph_open
- attrs: null
block: true
children:
- attrs: null
block: false
children: null
content: 'This is an '
hidden: false
info: ''
level: 0
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: 1
tag: em
type: em_open
- attrs: null
block: false
children: null
content: alert
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: -1
tag: em
type: em_close
content: This is an *alert*
hidden: false
info: ''
level: 2
map:
- 0
- 2
markup: ''
meta: {}
nesting: 0
tag: ''
type: inline
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: -1
tag: p
type: paragraph_close
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '>'
meta: {}
nesting: -1
tag: blockquote
type: admonition_close

View file

@ -0,0 +1,124 @@
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map:
- 0
- 2
markup: '>'
meta:
kind: tip
nesting: 1
tag: blockquote
type: admonition_open
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map:
- 0
- 2
markup: ''
meta: {}
nesting: 1
tag: p
type: paragraph_open
- attrs: null
block: true
children:
- attrs: null
block: false
children: null
content: 'This is an '
hidden: false
info: ''
level: 0
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: 1
tag: em
type: em_open
- attrs: null
block: false
children: null
content: alert
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: -1
tag: em
type: em_close
content: This is an *alert*
hidden: false
info: ''
level: 2
map:
- 0
- 2
markup: ''
meta: {}
nesting: 0
tag: ''
type: inline
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: -1
tag: p
type: paragraph_close
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '>'
meta: {}
nesting: -1
tag: blockquote
type: admonition_close

View file

@ -0,0 +1,124 @@
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map:
- 0
- 2
markup: '>'
meta:
kind: warning
nesting: 1
tag: blockquote
type: admonition_open
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map:
- 0
- 2
markup: ''
meta: {}
nesting: 1
tag: p
type: paragraph_open
- attrs: null
block: true
children:
- attrs: null
block: false
children: null
content: 'This is an '
hidden: false
info: ''
level: 0
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: 1
tag: em
type: em_open
- attrs: null
block: false
children: null
content: alert
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: 0
tag: ''
type: text
- attrs: null
block: false
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '*'
meta: {}
nesting: -1
tag: em
type: em_close
content: This is an *alert*
hidden: false
info: ''
level: 2
map:
- 0
- 2
markup: ''
meta: {}
nesting: 0
tag: ''
type: inline
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 1
map: null
markup: ''
meta: {}
nesting: -1
tag: p
type: paragraph_close
- attrs: null
block: true
children: null
content: ''
hidden: false
info: ''
level: 0
map: null
markup: '>'
meta: {}
nesting: -1
tag: blockquote
type: admonition_close

View file

@ -52,6 +52,9 @@
"**.scm"
"**.svg"
"**/man/*.5"
# Those files are generated by pytest-regression, which then `diff`s them.
# Formatting them will make the tests fail.
"docs/gfm-alerts-to-admonitions/tests/**/*.yml"
];
formatter.ruff-format.options = [ "--isolated" ];
};