let # Should be extern # { inherit (builtins) any attrNames concatMap concatStringsSep deepSeq elemAt foldl' genList getFlake head isAttrs isList isString length map match removeAttrs replaceStrings split stringLength substring trace toString typeOf; foot = list: elemAt list (length list - 1); apply = transformations: value: foldl' ( value: transformation: transformation value ) value transformations; ltrim = list: if head list == "" && length list >= 2 then genList (x: elemAt list ( x + 2 )) ( length list - 2 ) else list; rtrim = list: if foot list == "" && length list >= 2 then genList (x: elemAt list x) ( length list - 2 ) else list; splitSpaces = split "([[:space:]]+)"; trim = apply [ splitSpaces ltrim rtrim ]; collectLines = apply [ ( foldl' ( { line, lines }: token: if token != null then { line = line ++ [ token ]; inherit lines; } else { line = [ ]; lines = lines ++ ( if line != [] then [ (concatStringsSep "" line) ] else [ ] ); } ) { line = [ ]; lines = [ ]; } ) ( { line, lines }: if line != [] then lines ++ [ (concatStringsSep "" line) ] else lines ) ]; traceDeep = x: y: trace (deepSeq x x) y; # } getOptions = branch: modules: removeAttrs ( (getFlake "github:sivizius/nixpkgs/${branch}").lib.nixosSystem { inherit modules; } ).options [ "_module" ]; getMeta = value: let internal = value.internal or false; in { declarations = value.declarations or null; default = if value ? __toString && toString value == "virtualisation.cri-o.package" then null else value.defaultText or value.default or null; description = value.description or null; example = value.example or null; hasDefault = value ? defaultText || value ? default; hasExample = value ? example; type = value.type.description; visible = value.visible or (!internal); }; parseTree = root: options: foldl' ( result: optionName: let name = if root != null then "${root}.${optionName}" else optionName; value = options.${optionName}; getOptions = foldl' ( options: submodules: if isAttrs submodules then let imports = getOptions (submodules.imports or [ ] ); in options // (submodules.options or { } ) // imports else options ) { }; submodules = getOptions value.type.getSubModules or []; in if isAttrs value then if value ? _type then result // { ${optionName} = let meta = getMeta value; in { tree = if meta.type == "submodule" then parseTree name submodules else null; inherit name meta value; }; } else result // { ${optionName} = { inherit name; meta = null; tree = parseTree name value; }; } else result ) {} ( attrNames options ); getDocumentation = branches: modules: foldl' ( result: branch: result // { ${branch} = parseTree null (getOptions branch modules); } ) {} branches; renderDescription = branch: description: let description' = if isString description then renderDocBook branch description else if isAttrs description && description ? _type && description ? text then { "mdDoc" = [ (renderMarkDown description.text) ]; }.${description._type} or (throw "Unknown type ${description._type}") else [ "—" ]; length' = length description'; in if length' == 0 then [ "
" ] else if length' == 1 then [ "${code}";
escapeHTML = replaceStrings [ "<" ">" "&" "\"" "'" ] [ "<" ">" "&" """ "'" ];
renderDocBook
= branch:
apply
[
#(text: traceDeep { _ = "DocBook"; inherit text; } text)
trim
(
text:
concatStringsSep ""
(
map
(
token:
if isList token
then
let
token' = head token;
matchBreak = match ".*\n.*\n.*" token';
in
if matchBreak != null
then
"\n"
else
" "
else
token)
text
)
)
(split "<(/?[A-Za-z]+)( [^>]+)?/?>")
(
concatMap
(
token:
if isList token
then
let
token' = elemAt token 0;
arguments = elemAt token 1;
in
{
"citerefentry" = [ "" ];
"code" = [ "" ];
"/code" = [ "" ];
"command" = [ "" ];
"/command" = [ "" ];
"emphasis" = [ "" ];
"/emphasis" = [ " " ];
"envar" = [ "$" ];
"/envar" = [ "" ];
"filename" = [ "" ];
"/filename" = [ "" ];
"function" = [ "" ];
"/function" = [ "" ];
"important" = [ "" ];
"/important" = [ "" ];
"link" = [ "" ];
"/link" = [ "" ];
"itemizedlist" = [ "" null ];
"/itemizedlist" = [ "
" ];
"listitem" = [ " " ];
"/listitem" = [ " " null ];
"literal" = [ "" ];
"/literal" = [ "" ];
"literallayout" = [ "" ];
"/literallayout" = [ "" ];
"manvolnum" = [ "(" ];
"/manvolnum" = [ ")" ];
"member" = [ " " ];
"/member" = [ " " null ];
"note" = [ "" ];
"/note" = [ "" ];
"option" = [ "" ];
"/option" = [ "" ];
"package" = [ "" ];
"/package" = [ "" ];
"para" = [ "" ];
"/para" = [ "
" ];
"productname" = [ "" ];
"/productname" = [ "™ " ];
"programlisting" = [ null "" ];
"/programlisting" = [ "
" null ];
"prompt" = [ null ];
"/prompt" = [ ];
"quote" = [ "" ];
"/quote" = [ "
" ];
"refentrytitle" = [ " class=\"manpage\">" ];
"/refentrytitle" = [ ];
"replaceable" = [ ];
"/replaceable" = [ ];
"screen" = [ null "" ];
"/screen" = [ "" null ];
"simplelist" = [ "" null ];
"/simplelist" = [ "
" ];
"term" = [ " " ];
"/term" = [ " " null ];
"title" = [ "" ];
"/title" = [ "" ];
"variablelist" = [ "" null ];
"/variablelist" = [ "
" ];
"varlistentry" = [ " "];
"/varlistentry" = [ " " null ];
"varname" = [ "" ];
"/varname" = [ "" ];
"warning" = [ "" ];
"/warning" = [ "" ];
"xref" = [ "${arguments}" ];
"/xref" = [ "" ];
}.${token'} or ( throw { inherit token' arguments; } )
else
[ (escapeHTML token) ]
)
)
collectLines
];
renderMarkDown
= text:
#traceDeep { _ = "MarkDown"; inherit text; }
text;
renderExpression
= branch:
optionName:
expression:
if isAttrs expression
&& expression ? _type
&& expression ? text
then
let
splitLines
= apply
[
(split "(\n)")
ltrim
rtrim
(foldl' (result: token: if isList token then result else result ++ [token]) [])
];
lines = splitLines expression.text;
expression'
= if length lines == 1
then
head lines
else
concatStringsSep "\n" lines;
in
{
"literalDocBook" = concatStringsSep "\n" (renderDocBook branch expression');
"literalExpression" = renderCode expression';
"literalMD" = renderMarkDown expression';
}.${expression._type} or (throw "Unknown type ${expression._type}")
else
renderCode (renderExpression' "" optionName expression);
renderExpression'
= indentation:
optionName:
expression:
{
"bool" = if expression then "true" else "false";
"float" = toString expression;
"int" = toString expression;
"lambda" = "_: …";
"list"
= let
indentation' = "${indentation} ";
content = map (renderExpression' indentation' optionName) expression;
content' = concatStringsSep " " content;
isLarge = ( length content > 16 ) || any (item: isAttrs item && item != {}) expression;
in
if expression == []
then
"[]"
else if isLarge
then
"[\n${indentation'}${concatStringsSep "\n${indentation'}" content}\n${indentation}]"
else
"[ ${concatStringsSep " " content} ]";
"null" = "null";
"path" = toString expression;
"set"
= let
indentation' = "${indentation} ";
escapeName
= name:
let
validMatch = match "[0-9A-Za-z'_-]+" name;
in
if validMatch != null
then
name
else
"\"${replaceStrings [ "\n" "\r" "\t" "\\" "\"" "\${" ] [ "\\n" "\\r" "\\t" "\\\\" "\\\"" "\\\${" ] name}\"";
content
= map
(
name:
let
value = renderExpression' indentation' optionName expression.${name};
in
"${escapeName name} = ${value};"
)
( attrNames expression );
content' = concatStringsSep "\n${indentation'}" content;
in
if expression == {}
then
"{}"
else if expression.type or null == "derivation"
&& expression ? name
then
"«derivation ${expression.name}»"
else
"{\n${indentation'}${content'}\n${indentation}}";
"string"
= let
indentation' = "${indentation} ";
splitLines
= apply
[
(split "(\n)")
ltrim
rtrim
(foldl' (result: token: if isList token then result else result ++ [token]) [])
];
lines = splitLines expression;
in
if length lines == 1
then
"\"${head lines}\""
else
"''\n${indentation'}${concatStringsSep "\n${indentation'}" lines}\n${indentation}''";
}.${typeOf expression};
renderExample
= branch:
hasExample:
optionName:
example:
if hasExample
then
renderExpression branch optionName example
else
null;
renderMeta
= branch:
value:
if value.meta != null
then
[
"${value.name}${value.meta.type}