From 6a117e2759b84b9508f1d69cb5be54ca331bff98 Mon Sep 17 00:00:00 2001 From: Naïm Favier Date: Thu, 3 Nov 2022 15:56:27 +0100 Subject: 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`. --- lib/generators.nix | 14 ++++++++++---- lib/options.nix | 21 +++++++++++++++++---- lib/tests/misc.nix | 2 +- 3 files changed, 28 insertions(+), 9 deletions(-) (limited to 'lib') 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 "" 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"''; -- cgit 1.4.1