diff --git a/overview/content-types/option-list.nix b/overview/content-types/option-list.nix
new file mode 100644
index 000000000..6aeccf53e
--- /dev/null
+++ b/overview/content-types/option-list.nix
@@ -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 != [ ]) ''
+ ${join "." self.prefix}
+ ${concatLines (map toString self.project-options)}
+
+ '';
+ };
+ };
+}
diff --git a/overview/content-types/option.nix b/overview/content-types/option.nix
new file mode 100644
index 000000000..563f674eb
--- /dev/null
+++ b/overview/content-types/option.nix
@@ -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;
+ 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
+ ''
+ ${join "." prefix-head}.${join "." prefix-tail}
+ '';
+ option-type = ''
+
Type:
+ ${self.type}
+ '';
+ option-default = optionalString (self.default ? text) ''
+ Default:
+ ${self.default.text}
+ '';
+ 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
+ ''
+
+ ${markdownToHtml self.description}
+
+ '';
+ 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) ''
+ Notes:
+ Missing update script An update script is required for automatically tracking the latest release.
+ '';
+ in
+ ''
+
+ ${option-prefix}
+ ${optionalString self.readOnly ''
+ Read-only
+ ''}
+
+
+ ${option-description}
+
+ ${option-type}
+ ${option-default}
+ ${alert-update-script}
+
+
+ '';
+ };
+ };
+}
diff --git a/overview/default.nix b/overview/default.nix
index 5f4d7d92f..7139112fa 100644
--- a/overview/default.nix
+++ b/overview/default.nix
@@ -14,7 +14,6 @@ let
isList
isInt
substring
- toJSON
toString
;
@@ -27,8 +26,6 @@ let
filterAttrs
mapAttrs'
nameValuePair
- drop
- join
;
empty =
@@ -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) ''
- Default:
- ${option.default.text}
- '';
- maybeReadonly = optionalString option.readOnly ''
- Read-only
- '';
- updateScriptStatus =
- let
- optionName = lib.removePrefix "pkgs." option.default.text;
- in
- optionalString (option.type == "package" && !pkgs ? ${optionName}.passthru.updateScript) ''
- Notes:
- Missing update script An update script is required for automatically tracking the latest release.
- '';
- in
- ''
-
- ${join "." prefix}.${join "." (drop (lib.length prefix) option.loc)}
- ${maybeReadonly}
-
-
-
- ${markdownToHtml option.description}
-
-
- - Type:
- ${option.type}
- ${maybeDefault}
- ${updateScriptStatus}
-
-
- '';
- many =
- prefix: projectOptions:
- optionalString (!empty projectOptions) ''
- ${join "." prefix}
- ${concatLines (map (one prefix) projectOptions)}
-
- '';
- };
+ 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: ''
@@ -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}
)
[