mirror of
https://github.com/kikito/inspect.lua.git
synced 2024-12-15 14:34:21 +00:00
Add options.override, refactor
This commit is contained in:
parent
78c67816ef
commit
dc5a7f48d2
108
inspect.lua
108
inspect.lua
@ -13,7 +13,6 @@ local inspect = {Options = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
inspect._VERSION = 'inspect.lua 3.1.0'
|
||||
inspect._URL = 'http://github.com/kikito/inspect.lua'
|
||||
inspect._DESCRIPTION = 'human-readable representations of tables'
|
||||
@ -41,8 +40,6 @@ inspect._LICENSE = [[
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
inspect.KEY = setmetatable({}, { __tostring = function() return 'inspect.KEY' end })
|
||||
inspect.METATABLE = setmetatable({}, { __tostring = function() return 'inspect.METATABLE' end })
|
||||
|
||||
local tostring = tostring
|
||||
local rep = string.rep
|
||||
@ -50,6 +47,7 @@ local match = string.match
|
||||
local char = string.char
|
||||
local gsub = string.gsub
|
||||
local fmt = string.format
|
||||
local concat = table.concat
|
||||
|
||||
local function rawpairs(t)
|
||||
return next, t, nil
|
||||
@ -150,33 +148,8 @@ local function countCycles(x, cycles)
|
||||
end
|
||||
end
|
||||
|
||||
local function puts(buf, str)
|
||||
buf.n = buf.n + 1
|
||||
buf[buf.n] = str
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Inspector = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local Inspector_mt = { __index = Inspector }
|
||||
|
||||
local function tabify(inspector)
|
||||
puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
|
||||
end
|
||||
|
||||
function Inspector:getId(v)
|
||||
local id = self.ids[v]
|
||||
local ids = self.ids
|
||||
local function getId(ids, v)
|
||||
local id = ids[v]
|
||||
if not id then
|
||||
local tv = type(v)
|
||||
id = (ids[tv] or 0) + 1
|
||||
@ -185,61 +158,77 @@ function Inspector:getId(v)
|
||||
return tostring(id)
|
||||
end
|
||||
|
||||
function Inspector:putValue(v)
|
||||
local buf = self.buf
|
||||
local function puts(buf, str)
|
||||
buf.n = buf.n + 1
|
||||
buf[buf.n] = str
|
||||
end
|
||||
|
||||
local function tabify(buf, level, options)
|
||||
local newline = options and options.newline or "\n"
|
||||
local indent = options and options.indent or " "
|
||||
puts(buf, newline .. rep(indent, level))
|
||||
end
|
||||
|
||||
local function putValue(v, buf, cycles, ids, level,
|
||||
options)
|
||||
|
||||
if options and options.override then
|
||||
local s = options.override(v)
|
||||
if s ~= nil then
|
||||
puts(buf, s)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local tv = type(v)
|
||||
if tv == 'string' then
|
||||
puts(buf, smartQuote(escape(v)))
|
||||
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
|
||||
tv == 'cdata' or tv == 'ctype' then
|
||||
puts(buf, tostring(v))
|
||||
elseif tv == 'table' and not self.ids[v] then
|
||||
elseif tv == 'table' and not ids[v] then
|
||||
local t = v
|
||||
|
||||
if t == inspect.KEY or t == inspect.METATABLE then
|
||||
puts(buf, tostring(t))
|
||||
elseif self.level >= self.depth then
|
||||
local depth = options and options.depth
|
||||
if depth and level >= depth then
|
||||
puts(buf, '{...}')
|
||||
else
|
||||
if self.cycles[t] > 1 then puts(buf, fmt('<%d>', self:getId(t))) end
|
||||
if cycles[t] > 1 then puts(buf, fmt('<%d>', getId(ids, t))) end
|
||||
|
||||
local keys, keysLen, seqLen = getKeys(t)
|
||||
|
||||
local nextLevel = level + 1
|
||||
puts(buf, '{')
|
||||
self.level = self.level + 1
|
||||
|
||||
for i = 1, seqLen + keysLen do
|
||||
if i > 1 then puts(buf, ',') end
|
||||
if i <= seqLen then
|
||||
puts(buf, ' ')
|
||||
self:putValue(t[i])
|
||||
putValue(t[i], buf, cycles, ids, nextLevel, options)
|
||||
else
|
||||
local k = keys[i - seqLen]
|
||||
tabify(self)
|
||||
tabify(buf, level + 1, options)
|
||||
if isIdentifier(k) then
|
||||
puts(buf, k)
|
||||
else
|
||||
puts(buf, "[")
|
||||
self:putValue(k)
|
||||
putValue(k, buf, cycles, ids, nextLevel, options)
|
||||
puts(buf, "]")
|
||||
end
|
||||
puts(buf, ' = ')
|
||||
self:putValue(t[k])
|
||||
putValue(t[k], buf, cycles, ids, nextLevel, options)
|
||||
end
|
||||
end
|
||||
|
||||
local mt = getmetatable(t)
|
||||
if type(mt) == 'table' then
|
||||
if seqLen + keysLen > 0 then puts(buf, ',') end
|
||||
tabify(self)
|
||||
tabify(buf, level + 1, options)
|
||||
puts(buf, '<metatable> = ')
|
||||
self:putValue(mt)
|
||||
putValue(mt, buf, cycles, ids, nextLevel, options)
|
||||
end
|
||||
|
||||
self.level = self.level - 1
|
||||
|
||||
if keysLen > 0 or type(mt) == 'table' then
|
||||
tabify(self)
|
||||
tabify(buf, level, options)
|
||||
elseif seqLen > 0 then
|
||||
puts(buf, ' ')
|
||||
end
|
||||
@ -248,7 +237,7 @@ function Inspector:putValue(v)
|
||||
end
|
||||
|
||||
else
|
||||
puts(buf, fmt('<%s %d>', tv, self:getId(v)))
|
||||
puts(buf, fmt('<%s %d>', tv, getId(ids, v)))
|
||||
end
|
||||
end
|
||||
|
||||
@ -256,28 +245,13 @@ end
|
||||
|
||||
|
||||
function inspect.inspect(root, options)
|
||||
options = options or {}
|
||||
|
||||
local depth = options.depth or (math.huge)
|
||||
local newline = options.newline or '\n'
|
||||
local indent = options.indent or ' '
|
||||
|
||||
local cycles = {}
|
||||
countCycles(root, cycles)
|
||||
|
||||
local inspector = setmetatable({
|
||||
buf = { n = 0 },
|
||||
ids = {},
|
||||
cycles = cycles,
|
||||
depth = depth,
|
||||
level = 0,
|
||||
newline = newline,
|
||||
indent = indent,
|
||||
}, Inspector_mt)
|
||||
local buf = { n = 0 }
|
||||
putValue(root, buf, cycles, {}, 0, options)
|
||||
|
||||
inspector:putValue(root)
|
||||
|
||||
return table.concat(inspector.buf)
|
||||
return concat(buf)
|
||||
end
|
||||
|
||||
setmetatable(inspect, {
|
||||
|
114
inspect.tl
114
inspect.tl
@ -4,13 +4,12 @@ local record inspect
|
||||
_URL: string
|
||||
_DESCRIPTION: string
|
||||
_LICENSE: string
|
||||
KEY: table
|
||||
METATABLE: table
|
||||
|
||||
record Options
|
||||
depth: integer
|
||||
newline: string
|
||||
indent: string
|
||||
override: function(any): string
|
||||
end
|
||||
end
|
||||
|
||||
@ -41,8 +40,6 @@ inspect._LICENSE = [[
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
inspect.KEY = setmetatable({}, {__tostring = function(): string return 'inspect.KEY' end})
|
||||
inspect.METATABLE = setmetatable({}, {__tostring = function(): string return 'inspect.METATABLE' end})
|
||||
|
||||
local tostring = tostring
|
||||
local rep = string.rep
|
||||
@ -50,6 +47,7 @@ local match = string.match
|
||||
local char = string.char
|
||||
local gsub = string.gsub
|
||||
local fmt = string.format
|
||||
local concat = table.concat
|
||||
|
||||
local function rawpairs(t: table): function, table, nil
|
||||
return next, t, nil
|
||||
@ -135,7 +133,7 @@ local function getKeys(t: table): {any}, integer, integer
|
||||
return keys, keysLen, seqLen
|
||||
end
|
||||
|
||||
local function countCycles(x: any, cycles: {any:integer}): nil
|
||||
local function countCycles(x: any, cycles: {table: integer}): nil
|
||||
if x is table then
|
||||
if cycles[x] then
|
||||
cycles[x] = cycles[x] + 1
|
||||
@ -150,33 +148,8 @@ local function countCycles(x: any, cycles: {any:integer}): nil
|
||||
end
|
||||
end
|
||||
|
||||
local function puts(buf: table, str:string): nil
|
||||
buf.n = buf.n as integer + 1
|
||||
buf[buf.n as integer] = str
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
local type Inspector = record
|
||||
buf: table
|
||||
depth: integer
|
||||
level: integer
|
||||
ids: {any:integer}
|
||||
newline: string
|
||||
indent: string
|
||||
cycles: {table: integer}
|
||||
puts: function(string)
|
||||
end
|
||||
|
||||
local Inspector_mt = {__index = Inspector}
|
||||
|
||||
local function tabify(inspector: Inspector)
|
||||
puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
|
||||
end
|
||||
|
||||
function Inspector:getId(v: any): string
|
||||
local id: integer = self.ids[v]
|
||||
local ids = self.ids
|
||||
local function getId(ids: {any:integer}, v:any): string
|
||||
local id: integer = ids[v]
|
||||
if not id then
|
||||
local tv: string = type(v)
|
||||
id = (ids[tv] or 0) + 1
|
||||
@ -185,61 +158,77 @@ function Inspector:getId(v: any): string
|
||||
return tostring(id)
|
||||
end
|
||||
|
||||
function Inspector:putValue(v: any)
|
||||
local buf = self.buf
|
||||
local function puts(buf: table, str:string): nil
|
||||
buf.n = buf.n as integer + 1
|
||||
buf[buf.n as integer] = str
|
||||
end
|
||||
|
||||
local function tabify(buf: table, level: integer, options: inspect.Options)
|
||||
local newline = options and options.newline or "\n"
|
||||
local indent = options and options.indent or " "
|
||||
puts(buf, newline .. rep(indent, level))
|
||||
end
|
||||
|
||||
local function putValue(v: any, buf: table, cycles: {table: integer}, ids: {any: integer}, level: integer,
|
||||
options: inspect.Options)
|
||||
|
||||
if options and options.override then
|
||||
local s = options.override(v)
|
||||
if s ~= nil then
|
||||
puts(buf, s)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local tv: string = type(v)
|
||||
if tv == 'string' then
|
||||
puts(buf, smartQuote(escape(v as string)))
|
||||
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
|
||||
tv == 'cdata' or tv == 'ctype' then
|
||||
puts(buf, tostring(v as number))
|
||||
elseif tv == 'table' and not self.ids[v] then
|
||||
elseif tv == 'table' and not ids[v] then
|
||||
local t = v as table
|
||||
|
||||
if t == inspect.KEY or t == inspect.METATABLE then
|
||||
puts(buf, tostring(t))
|
||||
elseif self.level >= self.depth then
|
||||
local depth = options and options.depth
|
||||
if depth and level >= depth then
|
||||
puts(buf, '{...}')
|
||||
else
|
||||
if self.cycles[t] > 1 then puts(buf, fmt('<%d>', self:getId(t))) end
|
||||
if cycles[t] > 1 then puts(buf, fmt('<%d>', getId(ids, t))) end
|
||||
|
||||
local keys, keysLen, seqLen = getKeys(t)
|
||||
|
||||
local nextLevel = level + 1
|
||||
puts(buf, '{')
|
||||
self.level = self.level + 1
|
||||
|
||||
for i = 1, seqLen + keysLen do
|
||||
if i > 1 then puts(buf, ',') end
|
||||
if i <= seqLen then
|
||||
puts(buf, ' ')
|
||||
self:putValue(t[i])
|
||||
putValue(t[i], buf, cycles, ids, nextLevel, options)
|
||||
else
|
||||
local k = keys[i - seqLen]
|
||||
tabify(self)
|
||||
tabify(buf, level + 1, options)
|
||||
if isIdentifier(k) then
|
||||
puts(buf, k as string)
|
||||
else
|
||||
puts(buf, "[")
|
||||
self:putValue(k)
|
||||
putValue(k, buf, cycles, ids, nextLevel, options)
|
||||
puts(buf, "]")
|
||||
end
|
||||
puts(buf, ' = ')
|
||||
self:putValue(t[k])
|
||||
putValue(t[k], buf, cycles, ids, nextLevel, options)
|
||||
end
|
||||
end
|
||||
|
||||
local mt = getmetatable(t)
|
||||
if type(mt) == 'table' then
|
||||
if seqLen + keysLen > 0 then puts(buf, ',') end
|
||||
tabify(self)
|
||||
tabify(buf, level + 1, options)
|
||||
puts(buf, '<metatable> = ')
|
||||
self:putValue(mt)
|
||||
putValue(mt, buf, cycles, ids, nextLevel, options)
|
||||
end
|
||||
|
||||
self.level = self.level - 1
|
||||
|
||||
if keysLen > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing }
|
||||
tabify(self)
|
||||
tabify(buf, level, options)
|
||||
elseif seqLen > 0 then -- array tables have one extra space before closing }
|
||||
puts(buf, ' ')
|
||||
end
|
||||
@ -248,7 +237,7 @@ function Inspector:putValue(v: any)
|
||||
end
|
||||
|
||||
else
|
||||
puts(buf, fmt('<%s %d>', tv, self:getId(v)))
|
||||
puts(buf, fmt('<%s %d>', tv, getId(ids, v)))
|
||||
end
|
||||
end
|
||||
|
||||
@ -256,28 +245,13 @@ end
|
||||
|
||||
|
||||
function inspect.inspect(root: any, options: inspect.Options): string
|
||||
options = options or {}
|
||||
|
||||
local depth: integer = options.depth or (math.huge as integer)
|
||||
local newline: string = options.newline or '\n'
|
||||
local indent: string = options.indent or ' '
|
||||
|
||||
local cycles = {}
|
||||
local cycles: {table: integer} = {}
|
||||
countCycles(root, cycles)
|
||||
|
||||
local inspector = setmetatable({
|
||||
buf = { n = 0 },
|
||||
ids = {},
|
||||
cycles = cycles,
|
||||
depth = depth,
|
||||
level = 0,
|
||||
newline = newline,
|
||||
indent = indent,
|
||||
} as Inspector, Inspector_mt)
|
||||
local buf: table = { n = 0 }
|
||||
putValue(root, buf, cycles, {}, 0, options)
|
||||
|
||||
inspector:putValue(root)
|
||||
|
||||
return table.concat(inspector.buf as {string})
|
||||
return concat(buf as {string})
|
||||
end
|
||||
|
||||
setmetatable(inspect, {
|
||||
|
@ -262,6 +262,55 @@ describe( 'inspect', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('the override option', function()
|
||||
it('changes how long strings are rendered, leaves values unaltered on nil', function()
|
||||
local t = {short = "short", long = "a long string that should be shortened"}
|
||||
|
||||
assert.equal(unindent([[
|
||||
{
|
||||
long = "shortened",
|
||||
short = "short"
|
||||
}
|
||||
]]), inspect(t, {override = function(x)
|
||||
if type(x) == "string" and #x > 5 then
|
||||
return '"shortened"'
|
||||
end
|
||||
end}))
|
||||
end)
|
||||
|
||||
it('overrides hash values', function()
|
||||
local t = { b = 2, c = "a" }
|
||||
|
||||
assert.equal(unindent([[
|
||||
{
|
||||
b = 2,
|
||||
c = "changed"
|
||||
}
|
||||
]]), inspect(t, {override = function(x)
|
||||
if x == "a" then
|
||||
return '"changed"'
|
||||
end
|
||||
end}))
|
||||
end)
|
||||
|
||||
it('can hide metatables', function()
|
||||
local t = setmetatable({a = 1}, {__index = "a"})
|
||||
|
||||
assert.equal(unindent([[
|
||||
{
|
||||
a = 1,
|
||||
<metatable> = {_}
|
||||
}
|
||||
]]), inspect(t, {override = function(x)
|
||||
if type(x) == "table" and x.__index then
|
||||
return "{_}"
|
||||
end
|
||||
end}))
|
||||
end)
|
||||
|
||||
|
||||
end)
|
||||
|
||||
describe('metatables', function()
|
||||
|
||||
it('includes the metatable as an extra hash attribute', function()
|
||||
|
Loading…
Reference in New Issue
Block a user