mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-10 00:08:07 +02:00
Merge pull request #9931 from 9999years/pretty-printer
Pretty-print values in the REPL
This commit is contained in:
commit
d857914e1a
6 changed files with 195 additions and 23 deletions
|
@ -101,7 +101,8 @@ struct NixRepl
|
|||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth
|
||||
.maxDepth = maxDepth,
|
||||
.prettyIndent = 2
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,24 +17,29 @@ struct PrintOptions
|
|||
* If true, output ANSI color sequences.
|
||||
*/
|
||||
bool ansiColors = false;
|
||||
|
||||
/**
|
||||
* If true, force values.
|
||||
*/
|
||||
bool force = false;
|
||||
|
||||
/**
|
||||
* If true and `force` is set, print derivations as
|
||||
* `«derivation /nix/store/...»` instead of as attribute sets.
|
||||
*/
|
||||
bool derivationPaths = false;
|
||||
|
||||
/**
|
||||
* If true, track which values have been printed and skip them on
|
||||
* subsequent encounters. Useful for self-referential values.
|
||||
*/
|
||||
bool trackRepeated = true;
|
||||
|
||||
/**
|
||||
* Maximum depth to evaluate to.
|
||||
*/
|
||||
size_t maxDepth = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Maximum number of attributes in attribute sets to print.
|
||||
*
|
||||
|
@ -42,6 +47,7 @@ struct PrintOptions
|
|||
* attribute set encountered.
|
||||
*/
|
||||
size_t maxAttrs = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Maximum number of list items to print.
|
||||
*
|
||||
|
@ -49,10 +55,26 @@ struct PrintOptions
|
|||
* list encountered.
|
||||
*/
|
||||
size_t maxListItems = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Maximum string length to print.
|
||||
*/
|
||||
size_t maxStringLength = std::numeric_limits<size_t>::max();
|
||||
|
||||
/**
|
||||
* Indentation width for pretty-printing.
|
||||
*
|
||||
* If set to 0 (the default), values are not pretty-printed.
|
||||
*/
|
||||
size_t prettyIndent = 0;
|
||||
|
||||
/**
|
||||
* True if pretty-printing is enabled.
|
||||
*/
|
||||
inline bool shouldPrettyPrint()
|
||||
{
|
||||
return prettyIndent > 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -153,6 +153,7 @@ struct ImportantFirstAttrNameCmp
|
|||
};
|
||||
|
||||
typedef std::set<const void *> ValuesSeen;
|
||||
typedef std::vector<std::pair<std::string, Value *>> AttrVec;
|
||||
|
||||
class Printer
|
||||
{
|
||||
|
@ -163,6 +164,37 @@ private:
|
|||
std::optional<ValuesSeen> seen;
|
||||
size_t attrsPrinted = 0;
|
||||
size_t listItemsPrinted = 0;
|
||||
std::string indent;
|
||||
|
||||
void increaseIndent()
|
||||
{
|
||||
if (options.shouldPrettyPrint()) {
|
||||
indent.append(options.prettyIndent, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
void decreaseIndent()
|
||||
{
|
||||
if (options.shouldPrettyPrint()) {
|
||||
assert(indent.size() >= options.prettyIndent);
|
||||
indent.resize(indent.size() - options.prettyIndent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a space (for separating items or attributes).
|
||||
*
|
||||
* If pretty-printing is enabled, a newline and the current `indent` is
|
||||
* printed instead.
|
||||
*/
|
||||
void printSpace(bool prettyPrint)
|
||||
{
|
||||
if (prettyPrint) {
|
||||
output << "\n" << indent;
|
||||
} else {
|
||||
output << " ";
|
||||
}
|
||||
}
|
||||
|
||||
void printRepeated()
|
||||
{
|
||||
|
@ -260,6 +292,28 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
bool shouldPrettyPrintAttrs(AttrVec & v)
|
||||
{
|
||||
if (!options.shouldPrettyPrint() || v.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pretty-print attrsets with more than one item.
|
||||
if (v.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto item = v[0].second;
|
||||
if (!item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pretty-print single-item attrsets only if they contain nested
|
||||
// structures.
|
||||
auto itemType = item->type();
|
||||
return itemType == nList || itemType == nAttrs || itemType == nThunk;
|
||||
}
|
||||
|
||||
void printAttrs(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && !seen->insert(v.attrs).second) {
|
||||
|
@ -270,9 +324,10 @@ private:
|
|||
if (options.force && options.derivationPaths && state.isDerivation(v)) {
|
||||
printDerivation(v);
|
||||
} else if (depth < options.maxDepth) {
|
||||
output << "{ ";
|
||||
increaseIndent();
|
||||
output << "{";
|
||||
|
||||
std::vector<std::pair<std::string, Value *>> sorted;
|
||||
AttrVec sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
|
||||
|
||||
|
@ -281,7 +336,11 @@ private:
|
|||
else
|
||||
std::sort(sorted.begin(), sorted.end(), ImportantFirstAttrNameCmp());
|
||||
|
||||
auto prettyPrint = shouldPrettyPrintAttrs(sorted);
|
||||
|
||||
for (auto & i : sorted) {
|
||||
printSpace(prettyPrint);
|
||||
|
||||
if (attrsPrinted >= options.maxAttrs) {
|
||||
printElided(sorted.size() - attrsPrinted, "attribute", "attributes");
|
||||
break;
|
||||
|
@ -290,13 +349,38 @@ private:
|
|||
printAttributeName(output, i.first);
|
||||
output << " = ";
|
||||
print(*i.second, depth + 1);
|
||||
output << "; ";
|
||||
output << ";";
|
||||
attrsPrinted++;
|
||||
}
|
||||
|
||||
decreaseIndent();
|
||||
printSpace(prettyPrint);
|
||||
output << "}";
|
||||
} else
|
||||
} else {
|
||||
output << "{ ... }";
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldPrettyPrintList(std::span<Value * const> list)
|
||||
{
|
||||
if (!options.shouldPrettyPrint() || list.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pretty-print lists with more than one item.
|
||||
if (list.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto item = list[0];
|
||||
if (!item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pretty-print single-item lists only if they contain nested
|
||||
// structures.
|
||||
auto itemType = item->type();
|
||||
return itemType == nList || itemType == nAttrs || itemType == nThunk;
|
||||
}
|
||||
|
||||
void printList(Value & v, size_t depth)
|
||||
|
@ -306,11 +390,16 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
output << "[ ";
|
||||
if (depth < options.maxDepth) {
|
||||
for (auto elem : v.listItems()) {
|
||||
increaseIndent();
|
||||
output << "[";
|
||||
auto listItems = v.listItems();
|
||||
auto prettyPrint = shouldPrettyPrintList(listItems);
|
||||
for (auto elem : listItems) {
|
||||
printSpace(prettyPrint);
|
||||
|
||||
if (listItemsPrinted >= options.maxListItems) {
|
||||
printElided(v.listSize() - listItemsPrinted, "item", "items");
|
||||
printElided(listItems.size() - listItemsPrinted, "item", "items");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -319,13 +408,15 @@ private:
|
|||
} else {
|
||||
printNullptr();
|
||||
}
|
||||
output << " ";
|
||||
listItemsPrinted++;
|
||||
}
|
||||
|
||||
decreaseIndent();
|
||||
printSpace(prettyPrint);
|
||||
output << "]";
|
||||
} else {
|
||||
output << "[ ... ]";
|
||||
}
|
||||
else
|
||||
output << "... ";
|
||||
output << "]";
|
||||
}
|
||||
|
||||
void printFunction(Value & v)
|
||||
|
@ -488,6 +579,7 @@ public:
|
|||
{
|
||||
attrsPrinted = 0;
|
||||
listItemsPrinted = 0;
|
||||
indent.clear();
|
||||
|
||||
if (options.trackRepeated) {
|
||||
seen.emplace();
|
||||
|
|
|
@ -6,4 +6,4 @@ error:
|
|||
| ^
|
||||
10|
|
||||
|
||||
error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «4294967295 attributes elided»}; «4294967294 attributes elided»}; «4294967293 attributes elided»}
|
||||
error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «4294967295 attributes elided» }; «4294967294 attributes elided» }; «4294967293 attributes elided» }
|
||||
|
|
|
@ -146,29 +146,86 @@ echo "$replResult" | grepQuiet -s afterChange
|
|||
# Normal output should print attributes in lexicographical order non-recursively
|
||||
testReplResponseNoRegex '
|
||||
{ a = { b = 2; }; l = [ 1 2 3 ]; s = "string"; n = 1234; x = rec { y = { z = { inherit y; }; }; }; }
|
||||
' '{ a = { ... }; l = [ ... ]; n = 1234; s = "string"; x = { ... }; }'
|
||||
' \
|
||||
'{
|
||||
a = { ... };
|
||||
l = [ ... ];
|
||||
n = 1234;
|
||||
s = "string";
|
||||
x = { ... };
|
||||
}
|
||||
'
|
||||
|
||||
# Same for lists, but order is preserved
|
||||
testReplResponseNoRegex '
|
||||
[ 42 1 "thingy" ({ a = 1; }) ([ 1 2 3 ]) ]
|
||||
' '[ 42 1 "thingy" { ... } [ ... ] ]'
|
||||
' \
|
||||
'[
|
||||
42
|
||||
1
|
||||
"thingy"
|
||||
{ ... }
|
||||
[ ... ]
|
||||
]
|
||||
'
|
||||
|
||||
# Same for let expressions
|
||||
testReplResponseNoRegex '
|
||||
let x = { y = { a = 1; }; inherit x; }; in x
|
||||
' '{ x = «repeated»; y = { ... }; }'
|
||||
' \
|
||||
'{
|
||||
x = { ... };
|
||||
y = { ... };
|
||||
}
|
||||
'
|
||||
|
||||
# The :p command should recursively print sets, but prevent infinite recursion
|
||||
testReplResponseNoRegex '
|
||||
:p { a = { b = 2; }; s = "string"; n = 1234; x = rec { y = { z = { inherit y; }; }; }; }
|
||||
' '{ a = { b = 2; }; n = 1234; s = "string"; x = { y = { z = { y = «repeated»; }; }; }; }'
|
||||
' \
|
||||
'{
|
||||
a = { b = 2; };
|
||||
n = 1234;
|
||||
s = "string";
|
||||
x = {
|
||||
y = {
|
||||
z = {
|
||||
y = «repeated»;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
'
|
||||
|
||||
# Same for lists
|
||||
testReplResponseNoRegex '
|
||||
:p [ 42 1 "thingy" (rec { a = 1; b = { inherit a; inherit b; }; }) ([ 1 2 3 ]) ]
|
||||
' '[ 42 1 "thingy" { a = 1; b = { a = 1; b = «repeated»; }; } [ 1 2 3 ] ]'
|
||||
' \
|
||||
'[
|
||||
42
|
||||
1
|
||||
"thingy"
|
||||
{
|
||||
a = 1;
|
||||
b = {
|
||||
a = 1;
|
||||
b = «repeated»;
|
||||
};
|
||||
}
|
||||
[
|
||||
1
|
||||
2
|
||||
3
|
||||
]
|
||||
]
|
||||
'
|
||||
|
||||
# Same for let expressions
|
||||
testReplResponseNoRegex '
|
||||
:p let x = { y = { a = 1; }; inherit x; }; in x
|
||||
' '{ x = «repeated»; y = { a = 1; }; }'
|
||||
' \
|
||||
'{
|
||||
x = «repeated»;
|
||||
y = { a = 1 };
|
||||
}
|
||||
'
|
||||
|
|
|
@ -720,7 +720,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
|||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«1 attribute elided»" ANSI_NORMAL "}",
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«1 attribute elided»" ANSI_NORMAL " }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxAttrs = 1
|
||||
|
@ -733,7 +733,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
|||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL "}",
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL " }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxAttrs = 1
|
||||
|
@ -757,7 +757,7 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
|
|||
vList.bigList.size = 2;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL "]",
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxListItems = 1
|
||||
|
@ -770,7 +770,7 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
|
|||
vList.bigList.size = 3;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL "]",
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxListItems = 1
|
||||
|
|
Loading…
Reference in a new issue