diff --git a/doc/manual/rl-next/print-derivations.md b/doc/manual/rl-next/print-derivations.md new file mode 100644 index 000000000..ea8c6c867 --- /dev/null +++ b/doc/manual/rl-next/print-derivations.md @@ -0,0 +1,20 @@ +--- +synopsis: Derivations can now be printed in detail in `nix repl` +cls: [3842] +category: Improvements +credits: [Lunaphied] +--- + +Traditionally derivations printed in the REPL would only print a formatted object +representing the path of the derivation file it refers to. This makes inspecting +the enhanced derivation attribute sets encountered from `mkDerivation` or similar +wrappers more difficult. Even the `:p`/`:print` command would not elaborate attribute sets +tagged as a derivation. + +With this change you can now use `:p`/`:print` to directly inspect a derivation +by providing one as the top-level object. Derivation attribute sets will only be +printed two levels deep and internal derivation attrsets will remain in unexpanded +path form as before. `drvAttrs` will also be elided as these attributes are already +present in the top-level attribute set of the derivation. These heuristics provide +a balance between readability and functionality. When the `:p`/`:print` is omitted, +a bare derivation is printed in the path format as before. diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index b737e5521..e0b0658cd 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -209,12 +209,14 @@ struct NixRepl void printValue(std::ostream & str, Value & v, - unsigned int maxDepth = std::numeric_limits::max()) + unsigned int maxDepth = std::numeric_limits::max(), + bool replDerivation = false) { ::nix::printValue(state, str, v, PrintOptions { .ansiColors = true, .force = true, - .derivationPaths = true, + .derivationPaths = !replDerivation, + .replDerivation = replDerivation, .maxDepth = maxDepth, .prettyIndent = 2, .errors = ErrorPrintBehavior::ThrowTopLevel, @@ -793,8 +795,10 @@ ProcessLineResult NixRepl::processLine(std::string line) evalString(arg, v); if (v.type() == nString) { std::cout << v.str(); + } else if (v.type() == nAttrs && state.isDerivation(v)) { + printValue(std::cout, v, 2, true); } else { - printValue(std::cout, v); + printValue(std::cout, v, std::numeric_limits::max()); } std::cout << std::endl; } diff --git a/lix/libexpr/print-options.hh b/lix/libexpr/print-options.hh index 6941126c5..b374507d3 100644 --- a/lix/libexpr/print-options.hh +++ b/lix/libexpr/print-options.hh @@ -53,6 +53,12 @@ struct PrintOptions */ bool derivationPaths = false; + /** + * If true, we are interactively printing a full derivation and should + * switch back to printing paths as desired normally after the first entry. + */ + bool replDerivation = false; + /** * If true, track which values have been printed and skip them on * subsequent encounters. Useful for self-referential values. diff --git a/lix/libexpr/print.cc b/lix/libexpr/print.cc index 1c7990cd8..48a2ff8f5 100644 --- a/lix/libexpr/print.cc +++ b/lix/libexpr/print.cc @@ -286,6 +286,11 @@ private: } else if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second) { printRepeated(); } else if (depth < options.maxDepth || v.attrs()->empty()) { + bool isPrintingReplDerivation = depth == 0 && options.replDerivation && state.isDerivation(v); + if (isPrintingReplDerivation) { + // Switch back to eliding paths if it was initially off. + options.derivationPaths = true; + } increaseIndent(); output << "{"; @@ -304,7 +309,7 @@ private: for (auto & i : sorted) { printSpace(prettyPrint); - if (attrsPrinted >= options.maxAttrs) { + if (attrsPrinted >= options.maxAttrs ) { printElided(sorted.size() - printedHere, "attribute", "attributes"); break; } @@ -324,6 +329,21 @@ private: } output << " = "; + + // Elide repeated drvAttrs attribute. + if (isPrintingReplDerivation && i.first == "drvAttrs") { + state.forceValue(i.second->value, noPos); + if (i.second->value.type() == ValueType::nAttrs) { + printElided(i.second->value.attrs()->size(), "attribute", "attributes"); + } else { + print(i.second->value, depth + 1); + } + output << ";"; + attrsPrinted++; + printedHere++; + continue; + } + print(i.second->value, depth + 1); output << ";"; attrsPrinted++; @@ -582,7 +602,6 @@ public: seen.reset(); } - ValuesSeen seen; print(v, 0); } }; diff --git a/tests/functional/repl_characterization/data/repl_printing.test b/tests/functional/repl_characterization/data/repl_printing.test index c9836efbb..72b87e9c4 100644 --- a/tests/functional/repl_characterization/data/repl_printing.test +++ b/tests/functional/repl_characterization/data/repl_printing.test @@ -100,3 +100,15 @@ Printing an environment with :env after adding a variable to the scope Env level 1 abort baseNameOf break builtins derivation derivationStrict dirOf false fetchGit fetchMercurial fetchTarball fetchTree fromTOML import isNull map null placeholder removeAttrs scopedImport throw toString true + +Printing a derivation or something + + nix-repl> fakeDrv = let drvAttrs = { builder = "meow"; system = "meower"; name = "mrowmrow"; }; in { inherit (drvAttrs) builder system name; inherit drvAttrs; type = "derivation"; } + nix-repl> :p fakeDrv + { + builder = "meow"; + drvAttrs = «3 attributes elided»; + name = "mrowmrow"; + system = "meower"; + type = "derivation"; + }