3.7 KiB
table.pretty
and changes to printing tables
Summary
We will add a function to display a pretty version of a table for debugging purposes. We will use this function as the backbone to changing the default implementation of print(table)
in CLI contexts.
Motivation
In Roblox Studio, printing out a table with "Log mode" off will display an interactive table. In roblox-cli, Lune, and luau.exe, it will print the memory address, which is faintly useful, and makes debugging tables a massive chore. Making tables pretty print is a clear win in this regard.
Adding a function to create these pretty forms and using that as the underlying implementation behind print
is useful for custom loggers that do not use print and go to file, or for something like debug commands.
Design
table.pretty
The following function will be added to the standard library:
table.pretty(input: { [any]: any }, indentation: number?): string
indentation
defaults to 2.
For a non-empty table, this will perform the following behavior:
- The initial opening and ending brace will be on their own line
- Form the string
[$key]: $value,
for every key and value, based on the same behavior as generalized iteration, even using__iter
if it exists.$key
and$value
will be pretty printed if it is both a table and if it would be acceptable to do so (no__metatable
). This will be indentedindentation
number of spaces.- If
$value
is a string, it will be wrapped in quotes. - Otherwise,
$key
and$value
will be the result oftostring(value)
. - The comma is always included.
$key
is not included if it is a number part of a sequence of keys starting with1
.
- In the event of a cyclic table with no tostring implementation, the literal string
"<cyclic: memory address>"
will be put in its place.
If indentation is 0, then it will all be put onto a single line, with spaces being used in place of new lines. In this case, trailing commas will not be present.
Examples
print(table.pretty({
width = 12,
toppings = {
"pepperoni",
"sausage",
},
}))
-->
{
width = 12,
toppings = {
"pepperoni",
"sausage",
}
}
print(table.pretty({
"my cool",
"mixed table",
key = "value",
[150] = "awesome",
}))
-->
{
"my cool",
"mixed table",
key = "value",
[150] = "awesome",
}
print(table.pretty({
"my",
"table",
{
x = 1,
y = 2,
}
}, 0))
-->
{ "my", "table", { x = 1, y = 2 } }
local t = { x = 1 }
t.y = t
print(table.pretty(t))
-->
{
x = 1,
y = <cyclic: 0xDEADBEEF>,
}
Caveats
An empty table will always produce "{}".
Calling table.pretty
on an input table with a custom __metatable
will throw an error.
Changes to printing tables
The default behavior of printing will change from calling __tostring
to instead using __tostring
if it exists, and table.pretty(t)
if it does not. The existing behavior of printing a memory address will still be available through print(tostring(t))
, which is already useful in a context like Roblox when working with Log mode.
Roblox
While Luau is not Roblox specific, it is nevertheless useful to clarify the expected behavior in that environment. Roblox Studio is expected to work the same way as it does now when "Log mode" is disabled--an interactive table expansion. When "Log mode" is enabled, the new printing behavior will take effect.
Drawbacks
TODO:
- LogService breaking change
Alternatives
TODO:
- implement one and not the other
- use tabs
- more configuration like spaces/tabs/column width (Python pprint)
- implement nothing, only change in roblox-cli
- custom metamethod for printing/pretty
- no trailing comma