query
On this page

toPretty

lib.generators.toPretty

Docs pulled from | This Revision | about 2 hours ago


Pretty print a value, akin to builtins.trace.

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.

Inputs

Structured function argument
allowPrettyValues
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.)
multiline
If this option is true, the output is indented with newlines for attribute sets and lists
indent
Initial indentation level
Value
The value to be pretty printed

Noogle detected

Implementation

The following is the current implementation of this function.

toPretty =
    {
      allowPrettyValues ? false,
      multiline ? true,
      indent ? "",
    }:
    let
      go =
        indent: v:
        let
          introSpace = if multiline then "\n${indent}  " else " ";
          outroSpace = if multiline then "\n${indent}" else " ";
        in
        if isInt 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) (split "\n" v);
            escapeSingleline = escape [
              "\\"
              "\""
              "\${"
            ];
            escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
            singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
            multilineResult =
              let
                escapedLines = map escapeMultiline lines;
                # The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
                # indentation level. Otherwise, '' is appended to the last line.
                lastLine = last escapedLines;
              in
              "''"
              + introSpace
              + concatStringsSep introSpace (init escapedLines)
              + (if lastLine == "" then outroSpace else introSpace + lastLine)
              + "''";
          in
          if multiline && length lines > 1 then 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
          if v == [ ] then
            "[ ]"
          else
            "[" + introSpace + concatMapStringsSep introSpace (go (indent + "  ")) v + outroSpace + "]"
        else if isFunction v then
          let
            fna = functionArgs v;
            showFnas = concatStringsSep ", " (
              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 allowPrettyValues && v ? __pretty && v ? val then
            v.__pretty v.val
          else if v == { } then
            "{ }"
          else if v ? type && v.type == "derivation" then
            "<derivation ${v.name or "???"}>"
          else
            "{"
            + introSpace
            + concatStringsSep introSpace (
              mapAttrsToList (
                name: value:
                "${escapeNixIdentifier name} = ${
                  addErrorContext "while evaluating an attribute `${name}`" (go (indent + "  ") value)
                };"
              ) v
            )
            + outroSpace
            + "}"
        else
          abort "generators.toPretty: should never happen (v = ${v})";
    in
    go indent;