about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorNaïm Favier <n@monade.li>2022-11-03 15:56:27 +0100
committerpennae <82953136+pennae@users.noreply.github.com>2022-12-08 17:52:52 +0100
commit6a117e2759b84b9508f1d69cb5be54ca331bff98 (patch)
tree5679f8c723a9c84da0fc40ba03bdfef6bb60e27c /lib
parent0b661ce32af9baa07da56b3f48fcd3ef5611b66c (diff)
nixos/doc: render option values using `lib.generators.toPretty`
Render un`_type`d defaults and examples as `literalExpression`s using
`lib.generators.toPretty` so that consumers don't have to reinvent Nix
pretty-printing. `renderOptionValue` is kept internal for now intentionally.

Make `toPretty` print floats as valid Nix values (without a tilde).

Get rid of the now-obsolete `substSpecial` function.

Move towards disallowing evaluation of packages in the manual by
raising a warning on `pkgs.foo.{outPath,drvPath}`; later, this should
throw an error. Instead, module authors should use `literalExpression`
and `mkPackageOption`.
Diffstat (limited to 'lib')
-rw-r--r--lib/generators.nix14
-rw-r--r--lib/options.nix21
-rw-r--r--lib/tests/misc.nix2
3 files changed, 28 insertions, 9 deletions
diff --git a/lib/generators.nix b/lib/generators.nix
index 06823d215e360..c0fe69389e00d 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -278,8 +278,11 @@ rec {
         mapAny 0;
 
   /* Pretty print a value, akin to `builtins.trace`.
-    * Should probably be a builtin as well.
-    */
+   * Should probably be a builtin as well.
+   * The pretty-printed string should be suitable for rendering default values
+   * in the NixOS manual. In particular, it should be as close to a valid Nix expression
+   * as possible.
+   */
   toPretty = {
     /* If this option is true, attrsets like { __pretty = fn; val = …; }
        will use fn to convert val to a pretty printed representation.
@@ -294,7 +297,10 @@ rec {
             introSpace = if multiline then "\n${indent}  " else " ";
             outroSpace = if multiline then "\n${indent}" else " ";
     in if   isInt      v then toString v
-    else if isFloat    v then "~${toString v}"
+    # toString loses precision on floats, so we use toJSON instead. This isn't perfect
+    # as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
+    # pretty-printing purposes this is acceptable.
+    else if isFloat    v then builtins.toJSON v
     else if isString   v then
       let
         lines = filter (v: ! isList v) (builtins.split "\n" v);
@@ -328,7 +334,7 @@ rec {
                          else "<function, args: {${showFnas}}>"
     else if isAttrs    v then
       # apply pretty values if allowed
-      if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
+      if allowPrettyValues && v ? __pretty && v ? val
          then v.__pretty v.val
       else if v == {} then "{ }"
       else if v ? type && v.type == "derivation" then
diff --git a/lib/options.nix b/lib/options.nix
index c80256c0e653b..b13687576e810 100644
--- a/lib/options.nix
+++ b/lib/options.nix
@@ -218,7 +218,7 @@ rec {
   # the set generated with filterOptionSets.
   optionAttrSetToDocList = optionAttrSetToDocList' [];
 
-  optionAttrSetToDocList' = prefix: options:
+  optionAttrSetToDocList' = _: options:
     concatMap (opt:
       let
         docOption = rec {
@@ -234,9 +234,8 @@ rec {
           readOnly = opt.readOnly or false;
           type = opt.type.description or "unspecified";
         }
-        // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
-        // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
-        // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }
+        // optionalAttrs (opt ? example) { example = renderOptionValue opt.example; }
+        // optionalAttrs (opt ? default) { default = renderOptionValue (opt.defaultText or opt.default); }
         // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
 
         subOptions =
@@ -256,6 +255,9 @@ rec {
      efficient: the XML representation of derivations is very large
      (on the order of megabytes) and is not actually used by the
      manual generator.
+
+     This function was made obsolete by renderOptionValue and is kept for
+     compatibility with out-of-tree code.
   */
   scrubOptionValue = x:
     if isDerivation x then
@@ -265,6 +267,17 @@ rec {
     else x;
 
 
+  /* Ensures that the given option value (default or example) is a `_type`d string
+     by rendering Nix values to `literalExpression`s.
+  */
+  renderOptionValue = v:
+    if v ? _type && v ? text then v
+    else literalExpression (lib.generators.toPretty {
+      multiline = true;
+      allowPrettyValues = true;
+    } v);
+
+
   /* For use in the `defaultText` and `example` option attributes. Causes the
      given string to be rendered verbatim in the documentation as Nix code. This
      is necessary for complex values, e.g. functions, or values that depend on
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index cfad5a3cd92a0..648c05ab35720 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -741,7 +741,7 @@ runTests {
     };
     expected = rec {
       int = "42";
-      float = "~0.133700";
+      float = "0.1337";
       bool = "true";
       emptystring = ''""'';
       string = ''"fn\''${o}\"r\\d"'';