Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions overview/content-types/option-list.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
lib,
name,
config,
pkgs,
...
}:
let
inherit (lib)
types
mkOption
optionalString
join
concatLines
;
in
{
options = {
prefix = mkOption {
type = with types; listOf str;
};
project-options = mkOption {
type =
with types;
listOf (submodule {
imports = [ ./option.nix ];
_module.args.pkgs = pkgs;
});
default = [ ];
};
__toString = mkOption {
type = with types; functionTo str;
readOnly = true;
default =
self:
optionalString (self.project-options != [ ]) ''
<details><summary><code>${join "." self.prefix}</code></summary><dl>
${concatLines (map toString self.project-options)}
</dl></details>
'';
};
};
}
114 changes: 114 additions & 0 deletions overview/content-types/option.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
{
lib,
pkgs,
config,
...
}:
let
inherit (lib)
types
mkOption
join
drop
optionalString
take
;
in
{
options = {
# TODO: simplify this / make it dynamic
prefix-length = mkOption {
type = types.int;
Comment thread
eljamm marked this conversation as resolved.
description = ''
Length to visually separate the fixed and moving parts of an option, making
less specific elements less dominant to reduce cognitive load.
'';
default = 2;
};
attrpath = mkOption {
type = with types; listOf str;
description = ''
Attribute path in the NixOS options tree.
'';
};
type = mkOption {
type = types.str;
};
default = mkOption {
type = types.attrs;
default = { };
};
description = mkOption {
type = types.str;
};
readOnly = mkOption {
type = types.bool;
};
__toString = mkOption {
type = with types; functionTo str;
readOnly = true;
default =
self:
let
option-prefix =
let
prefix-head = take self.prefix-length self.attrpath;
prefix-tail = drop self.prefix-length self.attrpath;
in
''
<span class="option-prefix">${join "." prefix-head}.</span><span>${join "." prefix-tail}</span>
'';
option-type = ''
<dt>Type:</dt>
<dd class="option-type"><code>${self.type}</code></dd>
'';
option-default = optionalString (self.default ? text) ''
<dt>Default:</dt>
<dd class="option-default"><code>${self.default.text}</code></dd>
'';
option-description =
let
# This doesn't actually produce a HTML string but a Jinja2 template string
# literal, that is then replaced by it's HTML translation at the last build
# step.
# Also, this avoids IFD (which would make things very slow with a
# growing number of such strings in the website rendering) since
# this way we can do markdown processing in a single step per output file at the end
markdownToHtml = markdown: "{{ markdown_to_html(${builtins.toJSON markdown}) }}";
in
''
<div class="option-description">
${markdownToHtml self.description}
</div>
'';
alert-update-script =
let
isDrv = self.type == "package";
optionName = lib.removePrefix "pkgs." self.default.text;
in
# TODO: plug a function that recurses into all dependencies,
# so we'd know how much of the build graph is kept up to date automatically
optionalString (isDrv && !pkgs ? ${optionName}.passthru.updateScript) ''
<dt>Notes:</dt>
<dd><span class="option-alert">Missing update script</span> An update script is required for automatically tracking the latest release.</dd>
'';
in
''
<dt class="option-name">
${option-prefix}
${optionalString self.readOnly ''
<span class="option-alert" title="This option can't be set by users">Read-only</span>
''}
</dt>
<dd class="option-body">
${option-description}
<dl>
${option-type}
${option-default}
${alert-update-script}
</dl>
</dd>
'';
};
};
}
89 changes: 28 additions & 61 deletions overview/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ let
isList
isInt
substring
toJSON
toString
;

Expand All @@ -27,8 +26,6 @@ let
filterAttrs
mapAttrs'
nameValuePair
drop
join
;

empty =
Expand Down Expand Up @@ -81,57 +78,24 @@ let
);
};

# This doesn't actually produce a HTML string but a Jinja2 template string
# literal, that is then replaced by it's HTML translation at the last build
# step.
markdownToHtml = markdown: "{{ markdown_to_html(${toJSON markdown}) }}";

render = {
options = rec {
one =
prefix: option:
let
maybeDefault = optionalString (option ? default.text) ''
<dt>Default:</dt>
<dd class="option-default"><code>${option.default.text}</code></dd>
'';
maybeReadonly = optionalString option.readOnly ''
<span class="option-alert" title="This option can't be set by users">Read-only</span>
'';
updateScriptStatus =
let
optionName = lib.removePrefix "pkgs." option.default.text;
in
optionalString (option.type == "package" && !pkgs ? ${optionName}.passthru.updateScript) ''
<dt>Notes:</dt>
<dd><span class="option-alert">Missing update script</span> An update script is required for automatically tracking the latest release.</dd>
'';
in
''
<dt class="option-name">
<span class="option-prefix">${join "." prefix}.</span><span>${join "." (drop (lib.length prefix) option.loc)}</span>
${maybeReadonly}
</dt>
<dd class="option-body">
<div class="option-description">
${markdownToHtml option.description}
</div>
<dl>
<dt>Type:</dt>
<dd class="option-type"><code>${option.type}</code></dd>
${maybeDefault}
${updateScriptStatus}
</dl>
</dd>
'';
many =
prefix: projectOptions:
optionalString (!empty projectOptions) ''
<details><summary><code>${join "." prefix}</code></summary><dl>
${concatLines (map (one prefix) projectOptions)}
</dl></details>
'';
};
options =
prefix: projectOptions:
eval {
imports = [ ./content-types/option-list.nix ];
_module.args.pkgs = pkgs;

inherit prefix;
project-options = map (option: {
inherit (option)
type
description
readOnly
;
attrpath = option.loc;
default = option.default or { };
}) projectOptions;
};

examples = rec {
one = example: ''
Expand Down Expand Up @@ -193,14 +157,17 @@ let
type:
lib.concatMapAttrsStringSep "\n" (
name: val:
optionalString (val.module != null) (
render.options.many [ type name ] (
pick.options [
type
name
]
)
)
let
project-options = pick.options [
type
name
];
attrpath-prefix = [
type
name
];
in
render.options attrpath-prefix project-options
) project.nixos.modules.${type}
)
[
Expand Down