158 lines
4.4 KiB
Nix
158 lines
4.4 KiB
Nix
let
|
|
elems = import ./elems.nix;
|
|
html = import ./html.nix;
|
|
in rec {
|
|
processMd = md:
|
|
if builtins.isPath md
|
|
then processStr (builtins.readFile md)
|
|
else processStr md;
|
|
|
|
recReadMd = root:
|
|
assert builtins.isPath root;
|
|
builtins.mapAttrs (path: type:
|
|
if type == "directory"
|
|
then recReadMd (root + (/. + path))
|
|
else if type == "regular"
|
|
then mdToPage (root + (/. + path))
|
|
else throw "Cannot read ${path}, file type ${type}") (builtins.readDir root);
|
|
|
|
recFixAppendix = site:
|
|
builtins.listToAttrs (builtins.attrValues (builtins.mapAttrs (name: value: {
|
|
name = fixAppendix name;
|
|
value =
|
|
if builtins.isAttrs value
|
|
then recFixAppendix value
|
|
else value;
|
|
})
|
|
site));
|
|
|
|
fixAppendix = builtins.replaceStrings [".md"] [".html"];
|
|
|
|
readDir = root: recFixAppendix (recReadMd root);
|
|
|
|
mdToPage = md:
|
|
html.document {
|
|
head = [];
|
|
body = elems.main (elems.article (processMd md));
|
|
};
|
|
|
|
splitList = block:
|
|
map listItem (builtins.filter (s: builtins.isString s && s != "")
|
|
(builtins.split "\n" block));
|
|
|
|
listItem = str: let
|
|
li = builtins.match "- (.*)" str;
|
|
checkbox = builtins.match "- \\[(.)] (.*)" str;
|
|
checked = builtins.elemAt checkbox 0;
|
|
content = builtins.elemAt checkbox 1;
|
|
in
|
|
if checkbox == null
|
|
then li
|
|
else [
|
|
(elems.input {
|
|
type = "checkbox";
|
|
checked = checked != " ";
|
|
disabled = true;
|
|
})
|
|
content
|
|
];
|
|
|
|
replace = regex: apply: block:
|
|
assert builtins.isString block; (let
|
|
m = builtins.match regex block;
|
|
before = builtins.elemAt m 0;
|
|
after = toString (builtins.elemAt m (matchCount - 1));
|
|
matchCount = builtins.length m;
|
|
in
|
|
if m == null
|
|
then block
|
|
else
|
|
(
|
|
if before == null
|
|
then ""
|
|
else replace regex apply before
|
|
)
|
|
+ (apply m)
|
|
+ after);
|
|
|
|
rule = matcher: apply: blocks:
|
|
map (block:
|
|
if builtins.isString block
|
|
then replace matcher apply block
|
|
else block)
|
|
blocks;
|
|
|
|
applyRules = i: rules: input: let
|
|
group =
|
|
if builtins.isString input
|
|
then [input]
|
|
else input;
|
|
len = builtins.length rules;
|
|
rule = builtins.elemAt rules i;
|
|
next = i + 1;
|
|
in
|
|
assert i < len;
|
|
if next < len
|
|
then rule (applyRules next rules group)
|
|
else rule group;
|
|
|
|
basicRule = matcher: elem: rule matcher (m: elem (builtins.elemAt m 1));
|
|
|
|
processStr = applyRules 0 [
|
|
(basicRule (wrap "\\^") elems.sup)
|
|
(basicRule (wrap "~") elems.sub)
|
|
(basicRule (wrap "\\*") elems.em)
|
|
(basicRule (wrapBreak "_") elems.em)
|
|
(basicRule (wrap "`") elems.code)
|
|
(basicRule (wrap "==") elems.mark)
|
|
(basicRule (wrap "~~") elems.del)
|
|
(basicRule (wrap "\\*\\*") elems.strong)
|
|
(basicRule (wrapBreak "__") elems.strong)
|
|
(rule (contains "\\[(.*)]\\((.*)\\)") (m: let
|
|
href = builtins.elemAt m 2;
|
|
text = builtins.elemAt m 1;
|
|
in (elems.a href text)))
|
|
list
|
|
(basicRule "(.*\n\n)?(.+)\n(.*)?" elems.p)
|
|
(basicRule "(.*\n\n)?```(.*)```(.*)?" (elems.textarea {readonly = true;}))
|
|
(basicRule (containsBreak "###### ([^\n]+)") (elems.h6))
|
|
(basicRule (containsBreak "##### ([^\n]+)") (elems.h5))
|
|
(basicRule (containsBreak "#### ([^\n]+)") (elems.h4))
|
|
(basicRule (containsBreak "### ([^\n]+)") (elems.h3))
|
|
(basicRule (containsBreak "## ([^\n]+)") (elems.h2))
|
|
(basicRule (containsBreak "# ([^\n]+)") (elems.h1))
|
|
(basicRule (containsBreak "<(${linkmatcher})>") (m: elems.a m m))
|
|
];
|
|
|
|
list = rule "((.*)(\n([^-\n][^\n]+)?\n))?((- [^\n]+\n)+)(.*)" (
|
|
l: (elems.ul (basicRule "(.*\n)?- ([^\n]+)\n(.*)" (m:
|
|
elems.li (basicRule "()\\[(.)] (.*)" (check:
|
|
elems.input {
|
|
type = "checkbox";
|
|
checked = check != " ";
|
|
disabled = true;
|
|
}) [m])) [(builtins.elemAt l 4)]))
|
|
);
|
|
|
|
linkmatcher = "[-[:alnum:].%?&#=:/]+";
|
|
contains = matcher: "(.*)?${matcher}(.*)";
|
|
wrap = matcher: contains "${matcher}([^${matcher}]+)${matcher}";
|
|
|
|
containsBreak = matcher: "(.*[[:space:]])?${matcher}(.*)";
|
|
wrapBreak = matcher: containsBreak "${matcher}([^${matcher}]+)${matcher}";
|
|
|
|
matchThen = matcher: block: func: let
|
|
m = builtins.match matcher block;
|
|
in
|
|
if m == null
|
|
then dontMatch block
|
|
else {
|
|
matched = true;
|
|
block = func m;
|
|
};
|
|
|
|
dontMatch = block: {
|
|
matched = false;
|
|
inherit block;
|
|
};
|
|
}
|