summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSilvan Mosberger <contact@infinisil.com>2020-09-21 17:11:49 +0200
committerGitHub <noreply@github.com>2020-09-21 17:11:49 +0200
commit366a677dbbae7983df69c7ff8076d3e0bfb1d350 (patch)
tree464bff940e32a9b232e549ff4e8f593d95a90add /lib
parent8a5eb89b0f70999c08ce9ce6df89238671e186dc (diff)
parent15c5ba9d28dbdcebd3320864b62fdc7f2b37defd (diff)
Merge pull request #97133 from Infinisil/improved-toPretty
Improve `generators.toPretty`
Diffstat (limited to 'lib')
-rw-r--r--lib/generators.nix59
-rw-r--r--lib/tests/misc.nix72
2 files changed, 104 insertions, 27 deletions
diff --git a/lib/generators.nix b/lib/generators.nix
index abd237eb7d37..501a23599f45 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -203,40 +203,59 @@ rec {
/* If this option is true, attrsets like { __pretty = fn; val = …; }
will use fn to convert val to a pretty printed representation.
(This means fn is type Val -> String.) */
- allowPrettyValues ? false
- }@args: v: with builtins;
+ allowPrettyValues ? false,
+ /* If this option is true, the output is indented with newlines for attribute sets and lists */
+ multiline ? true
+ }@args: let
+ go = indent: v: with builtins;
let isPath = v: typeOf v == "path";
+ 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}"
- else if isString v then ''"${libStr.escape [''"''] v}"''
+ else if isString v then
+ let
+ # Separate a string into its lines
+ newlineSplits = filter (v: ! isList v) (builtins.split "\n" v);
+ # For a '' string terminated by a \n, which happens when the closing '' is on a new line
+ multilineResult = "''" + introSpace + concatStringsSep introSpace (lib.init newlineSplits) + outroSpace + "''";
+ # For a '' string not terminated by a \n, which happens when the closing '' is not on a new line
+ multilineResult' = "''" + introSpace + concatStringsSep introSpace newlineSplits + "''";
+ # For single lines, replace all newlines with their escaped representation
+ singlelineResult = "\"" + libStr.escape [ "\"" ] (concatStringsSep "\\n" newlineSplits) + "\"";
+ in if multiline && length newlineSplits > 1 then
+ if lib.last newlineSplits == "" then multilineResult else multilineResult'
+ else singlelineResult
else if true == v then "true"
else if false == v then "false"
else if null == v then "null"
else if isPath v then toString v
- else if isList v then "[ "
- + libStr.concatMapStringsSep " " (toPretty args) v
- + " ]"
+ else if isList v then
+ if v == [] then "[ ]"
+ else "[" + introSpace
+ + libStr.concatMapStringsSep introSpace (go (indent + " ")) v
+ + outroSpace + "]"
+ else if isFunction v then
+ let fna = lib.functionArgs v;
+ showFnas = concatStringsSep ", " (libAttr.mapAttrsToList
+ (name: hasDefVal: if hasDefVal then name + "?" else name)
+ fna);
+ in if fna == {} then "<function>"
+ else "<function, args: {${showFnas}}>"
else if isAttrs v then
# apply pretty values if allowed
if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
then v.__pretty v.val
- # TODO: there is probably a better representation?
+ else if v == {} then "{ }"
else if v ? type && v.type == "derivation" then
- "<δ:${v.name}>"
- # "<δ:${concatStringsSep "," (builtins.attrNames v)}>"
- else "{ "
- + libStr.concatStringsSep " " (libAttr.mapAttrsToList
+ "<derivation ${v.drvPath}>"
+ else "{" + introSpace
+ + libStr.concatStringsSep introSpace (libAttr.mapAttrsToList
(name: value:
- "${toPretty args name} = ${toPretty args value};") v)
- + " }"
- else if isFunction v then
- let fna = lib.functionArgs v;
- showFnas = concatStringsSep "," (libAttr.mapAttrsToList
- (name: hasDefVal: if hasDefVal then "(${name})" else name)
- fna);
- in if fna == {} then "<λ>"
- else "<λ:{${showFnas}}>"
+ "${libStr.escapeNixIdentifier name} = ${go (indent + " ") value};") v)
+ + outroSpace + "}"
else abort "generators.toPretty: should never happen (v = ${v})";
+ in go "";
# PLIST handling
toPlist = {}: v: let
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 03eff4ce48b7..3a6db53c276d 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -445,32 +445,90 @@ runTests {
expected = builtins.toJSON val;
};
- testToPretty = {
- expr = mapAttrs (const (generators.toPretty {})) rec {
+ testToPretty =
+ let
+ deriv = derivation { name = "test"; builder = "/bin/sh"; system = builtins.currentSystem; };
+ in {
+ expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
int = 42;
float = 0.1337;
bool = true;
+ emptystring = "";
string = ''fno"rd'';
+ newlinestring = "\n";
path = /. + "/foo";
null_ = null;
function = x: x;
functionArgs = { arg ? 4, foo }: arg;
list = [ 3 4 function [ false ] ];
+ emptylist = [];
attrs = { foo = null; "foo bar" = "baz"; };
- drv = derivation { name = "test"; system = builtins.currentSystem; };
+ emptyattrs = {};
+ drv = deriv;
};
expected = rec {
int = "42";
float = "~0.133700";
bool = "true";
+ emptystring = ''""'';
string = ''"fno\"rd"'';
+ newlinestring = "\"\\n\"";
path = "/foo";
null_ = "null";
- function = "<λ>";
- functionArgs = "<λ:{(arg),foo}>";
+ function = "<function>";
+ functionArgs = "<function, args: {arg?, foo}>";
list = "[ 3 4 ${function} [ false ] ]";
- attrs = "{ \"foo\" = null; \"foo bar\" = \"baz\"; }";
- drv = "<δ:test>";
+ emptylist = "[ ]";
+ attrs = "{ foo = null; \"foo bar\" = \"baz\"; }";
+ emptyattrs = "{ }";
+ drv = "<derivation ${deriv.drvPath}>";
+ };
+ };
+
+ testToPrettyMultiline = {
+ expr = mapAttrs (const (generators.toPretty { })) rec {
+ list = [ 3 4 [ false ] ];
+ attrs = { foo = null; bar.foo = "baz"; };
+ newlinestring = "\n";
+ multilinestring = ''
+ hello
+ there
+ test
+ '';
+ multilinestring' = ''
+ hello
+ there
+ test'';
+ };
+ expected = rec {
+ list = ''
+ [
+ 3
+ 4
+ [
+ false
+ ]
+ ]'';
+ attrs = ''
+ {
+ bar = {
+ foo = "baz";
+ };
+ foo = null;
+ }'';
+ newlinestring = "''\n \n''";
+ multilinestring = ''
+ '''
+ hello
+ there
+ test
+ ''''';
+ multilinestring' = ''
+ '''
+ hello
+ there
+ test''''';
+
};
};