summary refs log tree commit diff
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
parent8a5eb89b0f70999c08ce9ce6df89238671e186dc (diff)
parent15c5ba9d28dbdcebd3320864b62fdc7f2b37defd (diff)
downloadnixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.tar
nixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.tar.gz
nixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.tar.bz2
nixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.tar.lz
nixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.tar.xz
nixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.tar.zst
nixpkgs-366a677dbbae7983df69c7ff8076d3e0bfb1d350.zip
Merge pull request #97133 from Infinisil/improved-toPretty
Improve `generators.toPretty`
-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 abd237eb7d3..501a23599f4 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 03eff4ce48b..3a6db53c276 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''''';
+
     };
   };