Compare commits
2 Commits
fd03c384f6
...
779fa2884c
Author | SHA1 | Date | |
---|---|---|---|
779fa2884c | |||
748fe7835e |
272
README.md
272
README.md
|
@ -2,58 +2,48 @@
|
|||
<!--- THIS IS AN AUTO-GENERATED FILE --->
|
||||
<!--- MANUAL CHANGES CAN AND WILL BE OVERWRITTEN --->
|
||||
|
||||
# Pretty
|
||||
|
||||
pretty is an advanced pretty printer for Lua.
|
||||
# pretty is an advanced pretty printer for Lua.
|
||||
|
||||
It's primarily a
|
||||
debugging tool, aiming for human readability, by detecting pattern in the input
|
||||
data, and creating an output string utilizing and highlighting those patterns.
|
||||
debugging tool, aiming for human readability, by detecting pattern in the input
|
||||
data, and creating an output string utilizing and highlighting those patterns.
|
||||
|
||||
## Code Example
|
||||
|
||||
Setup is simple, use pretty = require 'pretty', and you're good to go.
|
||||
|
||||
```lua
|
||||
> print(pretty( { 1, 2, 3 } ))
|
||||
{ 1, 2, 3 }
|
||||
|
||||
> print(pretty( { hello = 'world', num = 42 } ))
|
||||
{
|
||||
|
||||
num = 42
|
||||
hello = 'world'
|
||||
$ print(pretty( { 1, 2, 3 } ))
|
||||
{ 1, 2, 3 }
|
||||
|
||||
$ print(pretty( { hello = 'world', num = 42 } ))
|
||||
{
|
||||
num = 42
|
||||
hello = 'world'
|
||||
}
|
||||
|
||||
> print(pretty( { abs = math.abs, max = math.max, some = function() end } ))
|
||||
{
|
||||
|
||||
abs = builtin function (x) ... end
|
||||
max = builtin function (x, ...) ... end
|
||||
some = function () ... end
|
||||
|
||||
$ print(pretty( { abs = math.abs, max = math.max, some = function() end } ))
|
||||
{
|
||||
abs = builtin function (x) ... end
|
||||
max = builtin function (x, ...) ... end
|
||||
some = function () ... end
|
||||
}
|
||||
|
||||
> print(pretty( math.abs ))
|
||||
builtin function (x)
|
||||
|
||||
-- math.abs
|
||||
-- Returns the absolute value of x
|
||||
|
||||
...
|
||||
$ print(pretty( math.abs ))
|
||||
builtin function (x)
|
||||
-- math.abs
|
||||
-- Returns the absolute value of x
|
||||
|
||||
...
|
||||
end
|
||||
```
|
||||
|
||||
## Motivation
|
||||
|
||||
This project is the outcome of my frustration with existing pretty printers, and
|
||||
a desire to expand upon the pretty printer I developed for
|
||||
Xenoterm. The original Xenoterm pretty
|
||||
printer was much simpler than pretty - and the current is even simpler - but
|
||||
the enhancements I make, when compared to other pretty printers, inspired me to
|
||||
create pretty.
|
||||
a desire to expand upon the pretty printer I developed for
|
||||
Xenoterm. The original Xenoterm pretty
|
||||
printer was much simpler than pretty - and the current is even simpler - but
|
||||
the enhancements I make, when compared to other pretty printers, inspired me to
|
||||
create pretty.
|
||||
|
||||
pretty sorts it's priorities like so:
|
||||
|
||||
|
@ -62,209 +52,47 @@ Lua-compatible output.
|
|||
Customization.
|
||||
|
||||
I'd rather have good defaults than provide a ton of customization options. If an
|
||||
structure avoids easy representation in Lua, I'd rather extend the syntax, than
|
||||
lose the info.
|
||||
structure avoids easy representation in Lua, I'd rather extend the syntax, than
|
||||
lose the info.
|
||||
|
||||
Another aspect where pretty shines is in exploratory programming, when
|
||||
attempting to avoid reliance on outside documentation. The amount of information
|
||||
pretty exposes varies by the data you are inspecting. If you're inspecting
|
||||
a list of functions, their function signatures are visible, but if you're
|
||||
inspecting a single function, documentation and source location may appear if
|
||||
available.
|
||||
attempting to avoid reliance on outside documentation. The amount of information
|
||||
pretty exposes varies by the data you are inspecting. If you're inspecting
|
||||
a list of functions, their function signatures are visible, but if you're
|
||||
inspecting a single function, documentation and source location may appear if
|
||||
available.
|
||||
|
||||
## Features
|
||||
|
||||
Written in good-old pureblood Lua, with support for PUC Lua 5.0+ and
|
||||
|
||||
|
||||
LuaJIT 2.0+.
|
||||
|
||||
|
||||
Written in good-old pure-blood Lua, with support for PUC Lua 5.0+ and LuaJIT 2.0+.
|
||||
Redefining what it means to be "human readable":
|
||||
Is multi-line centric, to aid readability.
|
||||
Indention and alignment of keys-value pairs.
|
||||
Keys-value pairs are
|
||||
alpha-numerically sorted by key
|
||||
type and thereafter alphabetically.
|
||||
The format and structure of output changes depending upon the input. Maps
|
||||
appear differently to deeply nested tables to long sequences with short
|
||||
strings to short lists.
|
||||
Uses the standard debug library to gain information about functions and
|
||||
other advanced structures.
|
||||
|
||||
|
||||
* Is multi-line centric, to aid readablitiy.
|
||||
* Indention and alignment of keys-value pairs.
|
||||
* Keys-value pairs are [properly](http://www.davekoelle.com/alphanum.html)
|
||||
sorted by key type and thereafter alphabetically.
|
||||
* The format and structure of output changes depending upon the input.
|
||||
Maps appear differently to deeply nested tables to long sequences
|
||||
with short strings to short lists.
|
||||
* Uses the standard debug library to gain information about functions
|
||||
and other advanced structures.
|
||||
|
||||
Installation
|
||||
## Installation
|
||||
|
||||
pretty is loadable directly with require. Either clone or download this
|
||||
repository. Where you place it, depends upon what you want to do:
|
||||
repository. Where you place it, depends upon what you want to do:
|
||||
|
||||
**You want pretty in a specific project**: Place the pretty folder
|
||||
|
||||
|
||||
somewhere in your project, and require it from one of your project files.
|
||||
|
||||
|
||||
somewhere in your project, and require it from one of your project files.
|
||||
**You want pretty on your system**: Place the pretty folder such that
|
||||
it's visible from your Lua-path. On my system this might be
|
||||
/usr/local/share/lua/5.1/. Now you can require it from anywhere.
|
||||
|
||||
|
||||
it's visible from your Lua-path. On my system this might be
|
||||
/usr/local/share/lua/5.1/. Now you can require it from anywhere.
|
||||
|
||||
API Documentation
|
||||
## API Documentation
|
||||
|
||||
pretty exposes a single function, the pretty function itself. It's function
|
||||
signature is pretty(value, options). value can be any Lua value. options
|
||||
must be a table.
|
||||
|
||||
### List of options
|
||||
|
||||
pretty is sure to complain if you give it an unknown option, or if you give an
|
||||
option a bad value.
|
||||
|
||||
indent: string: The string to indent with. Four spaces by default.
|
||||
|
||||
## TODO
|
||||
|
||||
Tasks to be done before pretty can be called version 1.0.0, in order of
|
||||
priority:
|
||||
|
||||
Add a dedicated unicode submodule, to handle some minor alignment and
|
||||
|
||||
|
||||
character escaping issues. pretty should escape all malformed unicode
|
||||
sequences.
|
||||
|
||||
|
||||
Align numbers towards right for tabular views.
|
||||
Add support for setmetatable, and exploring values in metatables.
|
||||
Provide nice formatting for cdata datatype in LuaJIT.
|
||||
Find a better name than pretty.
|
||||
Enhance internal structure some amount. See TODO markers in files.
|
||||
|
||||
It would be nice to have the following, but these are secondary:
|
||||
|
||||
Add option for colored output. Primarily syntax highlighting, but also
|
||||
|
||||
|
||||
[BlueJ-style](www.bluej.org/about.html) scope highlighting, with some faint
|
||||
background colors.
|
||||
|
||||
|
||||
Expand on the comment output in output, for __tostring methods, and global
|
||||
|
||||
|
||||
namespaces like io or math.
|
||||
|
||||
|
||||
Fit output within a predefined width limit. Default to 80.
|
||||
Look into tool for understanding complex structures with recursive
|
||||
|
||||
|
||||
definitions. Whatever modes are thought up, they should be automatic modes,
|
||||
not an options. Should at least include modes for self-referential tables
|
||||
and Directed-Acyclic-Graphs.
|
||||
|
||||
Alternative pretty printers
|
||||
|
||||
pretty is large, slow, and requires the debug library to work. It's not
|
||||
designed for serialization purposes, nor is it concerned with offering the same
|
||||
level of customization as other libraries do.
|
||||
|
||||
If you want a sleek, fast, customizable or embeddable library, there are
|
||||
thankfully other options.
|
||||
|
||||
inspect.lua: One of the classic debugging
|
||||
|
||||
|
||||
pretty printers.
|
||||
|
||||
|
||||
pprint.lua: Reimplementation of inspect.lua
|
||||
serpent: Advanced and fast pretty printer.
|
||||
pluto: Can serialize arbitrary parts of
|
||||
|
||||
|
||||
Lua, including functions, upvalues, and proper lexical scoping. Not written
|
||||
in native Lua.
|
||||
|
||||
|
||||
binser: Library for special purpose
|
||||
|
||||
|
||||
serialization.
|
||||
|
||||
|
||||
Even more are available at the lua-users wiki.
|
||||
|
||||
## Thoughts on displaying tables in an intuitive way.
|
||||
|
||||
Lua's table data-structure is likely to be the most concise data structure ever
|
||||
invented. (If not, please send me a link!) Lists, maps, objects, classes,
|
||||
proxies, etc. This obviously brings about it some difficulty when attempting to
|
||||
represent these tables. What do we want to highlight, and what do we choose to
|
||||
avoid?
|
||||
|
||||
One notable issue is whether to show every key that a table answers (to lift
|
||||
some Smalltalk terms) to, or to just display those it contains. That is, do we
|
||||
think about __index in the table's metatable and what it returns, or do we
|
||||
ignore __index? For cases where __index is a function, we cannot say
|
||||
anything about the keys that the table answers to. If __index is a table, we
|
||||
have a better idea, but it would be cluttered to display both types of keys side
|
||||
by side.
|
||||
|
||||
Native representation: Lua's native representation includes the type and
|
||||
|
||||
|
||||
address of the table. It allows for distinguishing between unique tables,
|
||||
but won't tell us anything about the contents.
|
||||
|
||||
|
||||
Omission: By representing tables as the pseudo-parsable {...}, it's
|
||||
|
||||
|
||||
clear we are talking about a table. We disregard the ability to
|
||||
distinguish between tables.
|
||||
|
||||
|
||||
2A. If the table is empty, we could represent it as {}. But what if the table
|
||||
|
||||
|
||||
has a metatable with __index defined? We could continue to represent it as
|
||||
{}, but {...} would be more "honest".
|
||||
|
||||
|
||||
Single-line: TODO
|
||||
Multi-line: TODO
|
||||
Columns: For some highly-regular structures, like lists of short strings,
|
||||
|
||||
|
||||
giving each string it's own line would be too long, but formatting them as a
|
||||
single-line list would be too cluttered. Thus we can take inspiration from
|
||||
the classic ls unix tool, and place the output into columns, to help guide
|
||||
the eyes.
|
||||
|
||||
|
||||
Tabular: Other structures are formatted like actual tables of data, e.g. a
|
||||
|
||||
|
||||
sequence of tuples, like one would see in an SQL database. For these
|
||||
structures it's an obvious choice to align them based on the keys.
|
||||
|
||||
|
||||
Pseudo-Tabular: Some structures are almost tabular, e.g. they are sequences
|
||||
|
||||
|
||||
of tuples, but some of the tuples differ in their structure. For these
|
||||
structures it's still useful to tabulate the keys that all tuples share. To
|
||||
do this we should sort the key order descending by the number of tuples with
|
||||
the key.
|
||||
But what do we do about the the outlier keys? We can either justify the
|
||||
entire table, and give specific spots for the outlier keys, thereby
|
||||
significantly increasing the size of the table, or we can leave the table
|
||||
unjustified, abandoning it's eye-guiding attributes.
|
||||
|
||||
|
||||
Special cases: (Array-tree, Table-Tree, Linked-List, Predictive Sequences) TODO
|
||||
signature is pretty(value, options). value can be any Lua value. options
|
||||
must be a table.
|
||||
|
||||
|
||||
|
||||
|
|
483
pretty.lua
483
pretty.lua
|
@ -1,205 +1,206 @@
|
|||
--- Pretty
|
||||
--
|
||||
-- `pretty` is an advanced pretty printer for [Lua](lua.org). It's primarily a
|
||||
-- debugging tool, aiming for human readability, by detecting pattern in the input
|
||||
-- data, and creating an output string utilizing and highlighting those patterns.
|
||||
--
|
||||
-- ## Code Example
|
||||
--
|
||||
-- Setup is simple, use `pretty = require 'pretty'`, and you're good to go.
|
||||
--
|
||||
-- ```lua
|
||||
-- > print(pretty( { 1, 2, 3 } ))
|
||||
-- { 1, 2, 3 }
|
||||
--
|
||||
-- > print(pretty( { hello = 'world', num = 42 } ))
|
||||
-- {
|
||||
-- num = 42
|
||||
-- hello = 'world'
|
||||
-- }
|
||||
--
|
||||
-- > print(pretty( { abs = math.abs, max = math.max, some = function() end } ))
|
||||
-- {
|
||||
-- abs = builtin function (x) ... end
|
||||
-- max = builtin function (x, ...) ... end
|
||||
-- some = function () ... end
|
||||
-- }
|
||||
--
|
||||
-- > print(pretty( math.abs ))
|
||||
-- builtin function (x)
|
||||
-- -- math.abs
|
||||
-- -- Returns the absolute value of x
|
||||
--
|
||||
-- ...
|
||||
-- end
|
||||
-- ```
|
||||
--
|
||||
-- ## Motivation
|
||||
--
|
||||
-- This project is the outcome of my frustration with existing pretty printers, and
|
||||
-- a desire to expand upon the pretty printer I developed for
|
||||
-- [Xenoterm](https://gitfub.space/takunomi/Xenoterm). The original Xenoterm pretty
|
||||
-- printer was much simpler than `pretty` - and the current is even simpler - but
|
||||
-- the enhancements I make, when compared to other pretty printers, inspired me to
|
||||
-- create `pretty`.
|
||||
--
|
||||
-- `pretty` sorts it's priorities like so:
|
||||
--
|
||||
-- 1. Human readability.
|
||||
-- 2. Lua-compatible output.
|
||||
-- 3. Customization.
|
||||
--
|
||||
-- I'd rather have good defaults than provide a ton of customization options. If an
|
||||
-- structure avoids easy representation in Lua, I'd rather extend the syntax, than
|
||||
-- lose the info.
|
||||
--
|
||||
-- Another aspect where `pretty` shines is in exploratory programming, when
|
||||
-- attempting to avoid reliance on outside documentation. The amount of information
|
||||
-- `pretty` exposes varies by the data you are inspecting. If you're inspecting
|
||||
-- a list of functions, their function signatures are visible, but if you're
|
||||
-- inspecting a single function, documentation and source location may appear if
|
||||
-- available.
|
||||
--
|
||||
-- ## Features
|
||||
--
|
||||
-- - Written in good-old pureblood Lua, with support for PUC Lua 5.0+ and
|
||||
-- LuaJIT 2.0+.
|
||||
-- - Redefining what it means to be "human readable":
|
||||
-- * Is multi-line centric, to aid readablitiy.
|
||||
-- * Indention and alignment of keys-value pairs.
|
||||
-- * Keys-value pairs are [properly](http://www.davekoelle.com/alphanum.html)
|
||||
-- sorted by key type and thereafter alphabetically.
|
||||
-- * The format and structure of output changes depending upon the input.
|
||||
-- Maps appear differently to deeply nested tables to long sequences
|
||||
-- with short strings to short lists.
|
||||
-- * Uses the standard `debug` library to gain information about functions
|
||||
-- and other advanced structures.
|
||||
--
|
||||
-- ## Installation
|
||||
--
|
||||
-- `pretty` is loadable directly with `require`. Either clone or download this
|
||||
-- repository. Where you place it, depends upon what you want to do:
|
||||
--
|
||||
-- 1. **You want `pretty` in a specific project**: Place the `pretty` folder
|
||||
-- somewhere in your project, and `require` it from one of your project files.
|
||||
-- 2. **You want `pretty` on your system**: Place the `pretty` folder such that
|
||||
-- it's visible from your Lua-path. On my system this might be
|
||||
-- `/usr/local/share/lua/5.1/`. Now you can `require` it from anywhere.
|
||||
--
|
||||
-- ## API Documentation
|
||||
--
|
||||
-- `pretty` exposes a single function, the `pretty` function itself. It's function
|
||||
-- signature is `pretty(value, options)`. `value` can be any Lua value. `options`
|
||||
-- must be a table.
|
||||
--
|
||||
-- ### List of options
|
||||
--
|
||||
-- `pretty` is sure to complain if you give it an unknown option, or if you give an
|
||||
-- option a bad value.
|
||||
--
|
||||
-- - `indent: string`: The string to indent with. Four spaces by default.
|
||||
--
|
||||
-- ## TODO
|
||||
--
|
||||
-- Tasks to be done before `pretty` can be called version 1.0.0, in order of
|
||||
-- priority:
|
||||
--
|
||||
-- - Add a dedicated unicode submodule, to handle some minor alignment and
|
||||
-- character escaping issues. `pretty` should escape all malformed unicode
|
||||
-- sequences.
|
||||
-- - Align numbers towards right for tabular views.
|
||||
-- - Add support for `setmetatable`, and exploring values in metatables.
|
||||
-- - Provide nice formatting for `cdata` datatype in LuaJIT.
|
||||
-- - Find a better name than `pretty`.
|
||||
-- - Enhance internal structure some amount. See `TODO` markers in files.
|
||||
--
|
||||
-- It would be nice to have the following, but these are secondary:
|
||||
--
|
||||
-- - Add option for colored output. Primarily syntax highlighting, but also
|
||||
-- [BlueJ-style](www.bluej.org/about.html) scope highlighting, with some faint
|
||||
-- background colors.
|
||||
-- - Expand on the comment output in output, for `__tostring` methods, and global
|
||||
-- namespaces like `io` or `math`.
|
||||
-- - Fit output within a predefined width limit. Default to 80.
|
||||
-- - Look into tool for understanding complex structures with recursive
|
||||
-- definitions. Whatever modes are thought up, they should be automatic modes,
|
||||
-- not an options. Should at least include modes for self-referential tables
|
||||
-- and Directed-Acyclic-Graphs.
|
||||
--
|
||||
-- ## Alternative pretty printers
|
||||
--
|
||||
-- `pretty` is large, slow, and requires the debug library to work. It's not
|
||||
-- designed for serialization purposes, nor is it concerned with offering the same
|
||||
-- level of customization as other libraries do.
|
||||
--
|
||||
-- If you want a sleek, fast, customizable or embeddable library, there are
|
||||
-- thankfully other options.
|
||||
--
|
||||
-- - [inspect.lua](github.com/kikito/inspect.lua): One of the classic debugging
|
||||
-- pretty printers.
|
||||
-- - [pprint.lua](github.com/jagt/pprint.lua): Reimplementation of `inspect.lua`
|
||||
-- - [serpent](github.com/pkulchenko/serpent): Advanced and fast pretty printer.
|
||||
-- - [pluto](lua-users.org/wiki/PlutoLibrary): Can serialize arbitrary parts of
|
||||
-- Lua, including functions, upvalues, and proper lexical scoping. Not written
|
||||
-- in native Lua.
|
||||
-- - [binser](github.com/bakpakin/binser): Library for special purpose
|
||||
-- serialization.
|
||||
--
|
||||
-- Even more are available at [the lua-users wiki](lua-users.org/wiki/TableSerialization).
|
||||
--
|
||||
-- ## Thoughts on displaying tables in an intuitive way.
|
||||
--
|
||||
-- Lua's table data-structure is likely to be the most concise data structure ever
|
||||
-- invented. (If not, please send me a link!) Lists, maps, objects, classes,
|
||||
-- proxies, etc. This obviously brings about it some difficulty when attempting to
|
||||
-- represent these tables. What do we want to highlight, and what do we choose to
|
||||
-- avoid?
|
||||
--
|
||||
-- One notable issue is whether to show every key that a table answers (to lift
|
||||
-- some Smalltalk terms) to, or to just display those it contains. That is, do we
|
||||
-- think about `__index` in the table's metatable and what it returns, or do we
|
||||
-- ignore `__index`? For cases where `__index` is a function, we cannot say
|
||||
-- anything about the keys that the table answers to. If `__index` is a table, we
|
||||
-- have a better idea, but it would be cluttered to display both types of keys side
|
||||
-- by side.
|
||||
--
|
||||
-- 1. Native representation: Lua's native representation includes the type and
|
||||
-- address of the table. It allows for distinguishing between unique tables,
|
||||
-- but won't tell us anything about the contents.
|
||||
-- 2. Omission: By representing tables as the pseudo-parsable `{...}`, it's
|
||||
-- clear we are talking about a table. We disregard the ability to
|
||||
-- distinguish between tables.
|
||||
-- 2A. If the table is empty, we could represent it as `{}`. But what if the table
|
||||
-- has a metatable with `__index` defined? We could continue to represent it as
|
||||
-- `{}`, but `{...}` would be more "honest".
|
||||
-- 3. Single-line: TODO
|
||||
-- 4. Multi-line: TODO
|
||||
-- 5. Columns: For some highly-regular structures, like lists of short strings,
|
||||
-- giving each string it's own line would be too long, but formatting them as a
|
||||
-- single-line list would be too cluttered. Thus we can take inspiration from
|
||||
-- the classic `ls` unix tool, and place the output into columns, to help guide
|
||||
-- the eyes.
|
||||
-- 6. Tabular: Other structures are formatted like actual tables of data, e.g. a
|
||||
-- sequence of tuples, like one would see in an SQL database. For these
|
||||
-- structures it's an obvious choice to align them based on the keys.
|
||||
-- 7. Pseudo-Tabular: Some structures are almost tabular, e.g. they are sequences
|
||||
-- of tuples, but some of the tuples differ in their structure. For these
|
||||
-- structures it's still useful to tabulate the keys that all tuples share. To
|
||||
-- do this we should sort the key order descending by the number of tuples with
|
||||
-- the key.
|
||||
-- But what do we do about the the outlier keys? We can either justify the
|
||||
-- entire table, and give specific spots for the outlier keys, thereby
|
||||
-- significantly increasing the size of the table, or we can leave the table
|
||||
-- unjustified, abandoning it's eye-guiding attributes.
|
||||
-- 8. Special cases: (Array-tree, Table-Tree, Linked-List, Predictive Sequences) TODO
|
||||
--[[--
|
||||
|
||||
# Pretty
|
||||
|
||||
`pretty` is an advanced pretty printer for [Lua](https://lua.org). It's primarily a
|
||||
debugging tool, aiming for human readability, by detecting pattern in the input
|
||||
data, and creating an output string utilizing and highlighting those patterns.
|
||||
|
||||
## Code Example
|
||||
|
||||
Setup is simple, use `pretty = require 'pretty'`, and you're good to go.
|
||||
|
||||
```lua
|
||||
$ print(pretty( { 1, 2, 3 } ))
|
||||
{ 1, 2, 3 }
|
||||
|
||||
$ print(pretty( { hello = 'world', num = 42 } ))
|
||||
{
|
||||
num = 42
|
||||
hello = 'world'
|
||||
}
|
||||
|
||||
$ print(pretty( { abs = math.abs, max = math.max, some = function() end } ))
|
||||
{
|
||||
abs = builtin function (x) ... end
|
||||
max = builtin function (x, ...) ... end
|
||||
some = function () ... end
|
||||
}
|
||||
|
||||
$ print(pretty( math.abs ))
|
||||
builtin function (x)
|
||||
-- math.abs
|
||||
-- Returns the absolute value of x
|
||||
|
||||
...
|
||||
end
|
||||
```
|
||||
|
||||
## Motivation
|
||||
|
||||
This project is the outcome of my frustration with existing pretty printers, and
|
||||
a desire to expand upon the pretty printer I developed for
|
||||
[Xenoterm](https://gitfub.space/takunomi/Xenoterm). The original Xenoterm pretty
|
||||
printer was much simpler than `pretty` - and the current is even simpler - but
|
||||
the enhancements I make, when compared to other pretty printers, inspired me to
|
||||
create `pretty`.
|
||||
|
||||
`pretty` sorts it's priorities like so:
|
||||
|
||||
1. Human readability.
|
||||
2. Lua-compatible output.
|
||||
3. Customization.
|
||||
|
||||
I'd rather have good defaults than provide a ton of customization options. If an
|
||||
structure avoids easy representation in Lua, I'd rather extend the syntax, than
|
||||
lose the info.
|
||||
|
||||
Another aspect where `pretty` shines is in exploratory programming, when
|
||||
attempting to avoid reliance on outside documentation. The amount of information
|
||||
`pretty` exposes varies by the data you are inspecting. If you're inspecting
|
||||
a list of functions, their function signatures are visible, but if you're
|
||||
inspecting a single function, documentation and source location may appear if
|
||||
available.
|
||||
|
||||
## Features
|
||||
|
||||
- Written in good-old pure-blood Lua, with support for PUC Lua 5.0+ and LuaJIT 2.0+.
|
||||
- Redefining what it means to be "human readable":
|
||||
* Is multi-line centric, to aid readability.
|
||||
* Indention and alignment of keys-value pairs.
|
||||
* Keys-value pairs are
|
||||
[alpha-numerically](http://www.davekoelle.com/alphanum.html) sorted by key
|
||||
type and thereafter alphabetically.
|
||||
* The format and structure of output changes depending upon the input. Maps
|
||||
appear differently to deeply nested tables to long sequences with short
|
||||
strings to short lists.
|
||||
* Uses the standard `debug` library to gain information about functions and
|
||||
other advanced structures.
|
||||
|
||||
## Installation
|
||||
|
||||
`pretty` is loadable directly with `require`. Either clone or download this
|
||||
repository. Where you place it, depends upon what you want to do:
|
||||
|
||||
1. **You want `pretty` in a specific project**: Place the `pretty` folder
|
||||
somewhere in your project, and `require` it from one of your project files.
|
||||
2. **You want `pretty` on your system**: Place the `pretty` folder such that
|
||||
it's visible from your Lua-path. On my system this might be
|
||||
`/usr/local/share/lua/5.1/`. Now you can `require` it from anywhere.
|
||||
|
||||
## API Documentation
|
||||
|
||||
`pretty` exposes a single function, the `pretty` function itself. It's function
|
||||
signature is `pretty(value, options)`. `value` can be any Lua value. `options`
|
||||
must be a table.
|
||||
|
||||
### List of options
|
||||
|
||||
`pretty` is sure to complain if you give it an unknown option, or if you give an
|
||||
option a bad value.
|
||||
|
||||
- `indent: string`: The string to indent with. Four spaces by default.
|
||||
|
||||
## TODO
|
||||
|
||||
Tasks to be done before `pretty` can be called version 1.0.0, in order of
|
||||
priority:
|
||||
|
||||
- [ ] Add dedicated unicode submodule, to handle some minor alignment and
|
||||
character escaping issues. `pretty` should escape all malformed unicode
|
||||
sequences.
|
||||
- [ ] Align numbers towards right for tabular views.
|
||||
- [ ] Add support for `setmetatable`, and exploring values in metatables.
|
||||
- [ ] Provide nice formatting for `cdata` datatype in LuaJIT.
|
||||
- [ ] Find a better name than `pretty`.
|
||||
- [ ] Enhance internal structure some amount. See `TODO` markers in files.
|
||||
|
||||
It would be nice to have the following, but these are secondary:
|
||||
|
||||
- [ ] Add option for colored output. Primarily syntax highlighting, but also
|
||||
[BlueJ-style](www.bluej.org/about.html) scope highlighting, with some faint
|
||||
background colors.
|
||||
- [ ] Expand on the comment output in output, for `__tostring` methods, and
|
||||
global namespaces like `io` or `math`.
|
||||
- [ ] Fit output within a predefined width limit. Default to 80.
|
||||
- [ ] Look into tool for understanding complex structures with recursive
|
||||
definitions. Whatever modes are thought up, they should be automatic modes, not
|
||||
an options. Should at least include modes for self-referential tables and
|
||||
Directed-Acyclic-Graphs.
|
||||
|
||||
## Alternative pretty printers
|
||||
|
||||
`pretty` is large, slow, and requires the debug library to work. It's not
|
||||
designed for serialization purposes, nor is it concerned with offering the same
|
||||
level of customization as other libraries do.
|
||||
|
||||
If you want a sleek, fast, customizable or embeddable library, there are
|
||||
thankfully other options.
|
||||
|
||||
- [inspect.lua](github.com/kikito/inspect.lua): One of the classic debugging pretty printers.
|
||||
- [pprint.lua](github.com/jagt/pprint.lua): Reimplementation of `inspect.lua`
|
||||
- [serpent](github.com/pkulchenko/serpent): Advanced and fast pretty printer.
|
||||
- [pluto](lua-users.org/wiki/PlutoLibrary): Can serialize arbitrary parts of
|
||||
Lua, including functions, upvalues, and proper lexical scoping. Not written in
|
||||
native Lua.
|
||||
- [binser](github.com/bakpakin/binser): Library for special purpose serialization.
|
||||
|
||||
Even more are available at [the lua-users wiki](lua-users.org/wiki/TableSerialization).
|
||||
|
||||
## Thoughts on displaying tables in an intuitive way.
|
||||
|
||||
Lua's table data-structure is likely to be the most concise data structure ever
|
||||
invented. (If not, please send me a link!) Lists, maps, objects, classes,
|
||||
proxies, etc. This obviously brings about it some difficulty when attempting to
|
||||
represent these tables. What do we want to highlight, and what do we choose to
|
||||
avoid?
|
||||
|
||||
One notable issue is whether to show every key that a table answers (to lift
|
||||
some Smalltalk terms) to, or to just display those it contains. That is, do we
|
||||
think about `__index` in the table's metatable and what it returns, or do we
|
||||
ignore `__index`? For cases where `__index` is a function, we cannot say
|
||||
anything about the keys that the table answers to. If `__index` is a table, we
|
||||
have a better idea, but it would be cluttered to display both types of keys side
|
||||
by side.
|
||||
|
||||
1. Native representation: Lua's native representation includes the type and
|
||||
address of the table. It allows for distinguishing between unique tables,
|
||||
but won't tell us anything about the contents.
|
||||
2. Omission: By representing tables as the pseudo-parsable `{...}`, it's
|
||||
clear we are talking about a table. We disregard the ability to
|
||||
distinguish between tables.
|
||||
2. If the table is empty, we could represent it as `{}`. But what if the table
|
||||
has a metatable with `__index` defined? We could continue to represent it as
|
||||
`{}`, but `{...}` would be more "honest".
|
||||
3. Single-line: TODO
|
||||
4. Multi-line: TODO
|
||||
5. Columns: For some highly-regular structures, like lists of short strings,
|
||||
giving each string it's own line would be too long, but formatting them as a
|
||||
single-line list would be too cluttered. Thus we can take inspiration from
|
||||
the classic `ls` unix tool, and place the output into columns, to help guide
|
||||
the eyes.
|
||||
6. Tabular: Other structures are formatted like actual tables of data, e.g. a
|
||||
sequence of tuples, like one would see in an SQL database. For these
|
||||
structures it's an obvious choice to align them based on the keys.
|
||||
7. Pseudo-Tabular: Some structures are almost tabular, e.g. they are sequences
|
||||
of tuples, but some of the tuples differ in their structure. For these
|
||||
structures it's still useful to tabulate the keys that all tuples share. To
|
||||
do this we should sort the key order descending by the number of tuples with
|
||||
the key.
|
||||
But what do we do about the the outlier keys? We can either justify the
|
||||
entire table, and give specific spots for the outlier keys, thereby
|
||||
significantly increasing the size of the table, or we can leave the table
|
||||
unjustified, abandoning it's eye-guiding attributes.
|
||||
8. Special cases: (Array-tree, Table-Tree, Linked-List, Predictive Sequences) TODO
|
||||
--]]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Import files
|
||||
|
||||
local import
|
||||
do
|
||||
local thispath = ... and select('1', ...):match('.+%.') or ''
|
||||
import = function (name, ignore_failure) return require(thispath..name) end
|
||||
local this_path = ... and select('1', ...):match('.+%.') or ''
|
||||
import = function (name, ignore_failure) return require(this_path..name) end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -421,10 +422,10 @@ local function attempt_to_align_into_columns (l, start_i, stop_i, nr_items_pr_ro
|
|||
assert(type(stop_i) == 'number')
|
||||
assert(type(nr_items_pr_row) == 'number')
|
||||
|
||||
local column = {}
|
||||
local column = {}
|
||||
---
|
||||
local start_of_item_i, item_nr = nil, 0
|
||||
for i = start_i, stop_i do
|
||||
for i = start_i, stop_i do
|
||||
if type(l[i]) == 'table' and (l[i][1] == 'indent' or l[i][1] == 'seperator' or l[i][1] == 'unindent') then
|
||||
if start_of_item_i then
|
||||
local width_of_item = width_of_strings_in_l(l, start_of_item_i, i-1)
|
||||
|
@ -433,12 +434,12 @@ local function attempt_to_align_into_columns (l, start_i, stop_i, nr_items_pr_ro
|
|||
end
|
||||
start_of_item_i, item_nr = i + 1, item_nr + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
---
|
||||
local width = nr_items_pr_row * 2 - 1 -- FIXME: Magic numbers: 2 = #', ', 1 = #' '
|
||||
for i = 1, #column do width = width + column[i] end
|
||||
--
|
||||
return width, column
|
||||
return width, column
|
||||
end
|
||||
|
||||
local function align_into_columns (l, start_i, stop_i)
|
||||
|
@ -448,8 +449,8 @@ local function align_into_columns (l, start_i, stop_i)
|
|||
|
||||
local start_i, stop_i = start_i or 1, stop_i or #l
|
||||
|
||||
assert(type(start_i) == 'number')
|
||||
assert(type(stop_i) == 'number')
|
||||
assert(type(start_i) == 'number')
|
||||
assert(type(stop_i) == 'number')
|
||||
|
||||
insert_alignment_estimations(l, start_i, stop_i)
|
||||
|
||||
|
@ -480,7 +481,7 @@ local function align_into_columns (l, start_i, stop_i)
|
|||
|
||||
-- Fit into columns.
|
||||
local start_of_item_i, item_nr = nil, 0
|
||||
for i = start_i, stop_i do
|
||||
for i = start_i, stop_i do
|
||||
if type(l[i]) ~= 'table' then
|
||||
-- Do nothing
|
||||
elseif (l[i][1] == 'indent' or l[i][1] == 'seperator' or l[i][1] == 'unindent') then
|
||||
|
@ -493,47 +494,47 @@ local function align_into_columns (l, start_i, stop_i)
|
|||
end
|
||||
start_of_item_i, item_nr = i + 1, item_nr + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function align_into_tabular_style (l, start_i, stop_i)
|
||||
-- Adds alignment after separators, to create nicely aligned tabular-format.
|
||||
-- Adds alignment after separators, to create nicely aligned tabular-format.
|
||||
|
||||
-- Argument fixing and Error Checking
|
||||
local start_i, stop_i = start_i or 1, stop_i or #l
|
||||
local start_i, stop_i = start_i or 1, stop_i or #l
|
||||
|
||||
assert(type(l) == 'table')
|
||||
assert(type(start_i) == 'number')
|
||||
assert(type(stop_i) == 'number')
|
||||
assert(type(l[start_i]) == 'table' and l[start_i][1] == 'indent')
|
||||
assert(type(l[stop_i]) == 'table' and l[stop_i][1] == 'unindent')
|
||||
assert(type(l) == 'table')
|
||||
assert(type(start_i) == 'number')
|
||||
assert(type(stop_i) == 'number')
|
||||
assert(type(l[start_i]) == 'table' and l[start_i][1] == 'indent')
|
||||
assert(type(l[stop_i]) == 'table' and l[stop_i][1] == 'unindent')
|
||||
|
||||
-- Calculate where to insert new alignment.
|
||||
local indent, key_nr, index_of_last_meta, insert_later = 0, 0, 1, {}
|
||||
for i = start_i + 1, stop_i - 1 do
|
||||
if type(l[i]) ~= 'table' then
|
||||
-- Do nothing
|
||||
elseif l[i][1] == 'indent' then
|
||||
indent = indent + 1
|
||||
if indent == 1 then key_nr = 1 end
|
||||
index_of_last_meta = i
|
||||
elseif l[i][1] == 'unindent' then
|
||||
insert_later[#insert_later+1] = {'align', 'end_subtable_'..key_nr, width_of_strings_in_l(l, index_of_last_meta+1, i), i}
|
||||
index_of_last_meta, key_nr = i, key_nr + 1
|
||||
indent = indent - 1
|
||||
elseif l[i][1] == 'seperator' and indent ~= 0 then
|
||||
insert_later[#insert_later+1] = {'align', 'key_'..key_nr, width_of_strings_in_l(l, index_of_last_meta+1, i), i+1}
|
||||
index_of_last_meta, key_nr = i, key_nr + 1
|
||||
end
|
||||
-- Calculate where to insert new alignment.
|
||||
local indent, key_nr, index_of_last_meta, insert_later = 0, 0, 1, {}
|
||||
for i = start_i + 1, stop_i - 1 do
|
||||
if type(l[i]) ~= 'table' then
|
||||
-- Do nothing
|
||||
elseif l[i][1] == 'indent' then
|
||||
indent = indent + 1
|
||||
if indent == 1 then key_nr = 1 end
|
||||
index_of_last_meta = i
|
||||
elseif l[i][1] == 'unindent' then
|
||||
insert_later[#insert_later+1] = {'align', 'end_subtable_'..key_nr, width_of_strings_in_l(l, index_of_last_meta+1, i), i}
|
||||
index_of_last_meta, key_nr = i, key_nr + 1
|
||||
indent = indent - 1
|
||||
elseif l[i][1] == 'seperator' and indent ~= 0 then
|
||||
insert_later[#insert_later+1] = {'align', 'key_'..key_nr, width_of_strings_in_l(l, index_of_last_meta+1, i), i+1}
|
||||
index_of_last_meta, key_nr = i, key_nr + 1
|
||||
end
|
||||
end
|
||||
-- Insert new alignment.
|
||||
for i = #insert_later, 1, -1 do
|
||||
local dat = insert_later[i]
|
||||
table.insert(l, dat[#dat], dat)
|
||||
dat[#dat] = nil
|
||||
end
|
||||
-- Fix that alignemnt
|
||||
return fix_alignment(l, start_i)
|
||||
-- Insert new alignment.
|
||||
for i = #insert_later, 1, -1 do
|
||||
local dat = insert_later[i]
|
||||
table.insert(l, dat[#dat], dat)
|
||||
dat[#dat] = nil
|
||||
end
|
||||
-- Fix that alignemnt
|
||||
return fix_alignment(l, start_i)
|
||||
end
|
||||
|
||||
local function fix_seperator_info (l, indent_char)
|
||||
|
@ -562,9 +563,9 @@ end
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local analyze_structure = import 'analyze_structure'
|
||||
local TABLE_TYPE = import 'common' . TABLE_TYPE
|
||||
local DISPLAY = import 'common' . DISPLAY
|
||||
local analyze_structure = import 'analyze_structure'
|
||||
local TABLE_TYPE = import 'common' . TABLE_TYPE
|
||||
local DISPLAY = import 'common' . DISPLAY
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Key-value pair formatting.
|
||||
|
@ -657,8 +658,8 @@ local function format_table (t, display, l, format_value)
|
|||
-- NOTE: Currently we only allow leaf-nodes to format into columns, due
|
||||
-- to issues with table alignment.
|
||||
align_into_columns(l, start_of_table_i)
|
||||
elseif table_info.is_tabular then
|
||||
align_into_tabular_style(l, start_of_table_i, #l)
|
||||
elseif table_info.is_tabular then
|
||||
align_into_tabular_style(l, start_of_table_i, #l)
|
||||
else
|
||||
-- Is long table: Fix whitespace alignment
|
||||
fix_alignment(l, start_of_table_i)
|
||||
|
|
Loading…
Reference in New Issue
Block a user