Fork of Tangara with customizations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tangara-fw/lib/lua-repl/repl/plugins/pretty_print.lua

262 lines
6.5 KiB

-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-- the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- Pretty prints expression results (console only)
local format = string.format
local tconcat = table.concat
local tsort = table.sort
local tostring = tostring
local type = type
local floor = math.floor
local pairs = pairs
local ipairs = ipairs
local error = error
local stderr = io.stderr
pcall(require, 'luarocks.require')
local ok, term = pcall(require, 'term')
if not ok then
term = nil
end
local keywords = {
['and'] = true,
['break'] = true,
['do'] = true,
['else'] = true,
['elseif'] = true,
['end'] = true,
['false'] = true,
['for'] = true,
['function'] = true,
['if'] = true,
['in'] = true,
['local'] = true,
['nil'] = true,
['not'] = true,
['or'] = true,
['repeat'] = true,
['return'] = true,
['then'] = true,
['true'] = true,
['until'] = true,
['while'] = true,
}
local function compose(f, g)
return function(...)
return f(g(...))
end
end
local emptycolormap = setmetatable({}, { __index = function()
return function(s)
return s
end
end})
local colormap = emptycolormap
if term then
colormap = {
['nil'] = term.colors.blue,
string = term.colors.yellow,
punctuation = compose(term.colors.green, term.colors.bright),
ident = term.colors.red,
boolean = term.colors.green,
number = term.colors.cyan,
path = term.colors.white,
misc = term.colors.magenta,
}
end
local function isinteger(n)
return type(n) == 'number' and floor(n) == n
end
local function isident(s)
return type(s) == 'string' and not keywords[s] and s:match('^[a-zA-Z_][a-zA-Z0-9_]*$')
end
-- most of these are arbitrary, I *do* want numbers first, though
local type_order = {
number = 0,
string = 1,
userdata = 2,
table = 3,
thread = 4,
boolean = 5,
['function'] = 6,
cdata = 7,
}
local function cross_type_order(a, b)
local pos_a = type_order[ type(a) ]
local pos_b = type_order[ type(b) ]
if pos_a == pos_b then
return a < b
else
return pos_a < pos_b
end
end
local function sortedpairs(t)
local keys = {}
local seen_non_string
for k in pairs(t) do
keys[#keys + 1] = k
if not seen_non_string and type(k) ~= 'string' then
seen_non_string = true
end
end
local sort_func = seen_non_string and cross_type_order or nil
tsort(keys, sort_func)
local index = 1
return function()
if keys[index] == nil then
return nil
else
local key = keys[index]
local value = t[key]
index = index + 1
return key, value
end
end, keys
end
local function find_longstring_nest_level(s)
local level = 0
while s:find(']' .. string.rep('=', level) .. ']', 1, true) do
level = level + 1
end
return level
end
local function dump(params)
local pieces = params.pieces
local seen = params.seen
local path = params.path
local v = params.value
local indent = params.indent
local t = type(v)
if t == 'nil' or t == 'boolean' or t == 'number' then
pieces[#pieces + 1] = colormap[t](tostring(v))
elseif t == 'string' then
if v:match '\n' then
local level = find_longstring_nest_level(v)
pieces[#pieces + 1] = colormap.string('[' .. string.rep('=', level) .. '[' .. v .. ']' .. string.rep('=', level) .. ']')
else
pieces[#pieces + 1] = colormap.string(format('%q', v))
end
elseif t == 'table' then
if seen[v] then
pieces[#pieces + 1] = colormap.path(seen[v])
return
end
seen[v] = path
local lastintkey = 0
pieces[#pieces + 1] = colormap.punctuation '{\n'
for i, v in ipairs(v) do
for j = 1, indent do
pieces[#pieces + 1] = ' '
end
dump {
pieces = pieces,
seen = seen,
path = path .. '[' .. tostring(i) .. ']',
value = v,
indent = indent + 1,
}
pieces[#pieces + 1] = colormap.punctuation ',\n'
lastintkey = i
end
for k, v in sortedpairs(v) do
if not (isinteger(k) and k <= lastintkey and k > 0) then
for j = 1, indent do
pieces[#pieces + 1] = ' '
end
if isident(k) then
pieces[#pieces + 1] = colormap.ident(k)
else
pieces[#pieces + 1] = colormap.punctuation '['
dump {
pieces = pieces,
seen = seen,
path = path .. '.' .. tostring(k),
value = k,
indent = indent + 1,
}
pieces[#pieces + 1] = colormap.punctuation ']'
end
pieces[#pieces + 1] = colormap.punctuation ' = '
dump {
pieces = pieces,
seen = seen,
path = path .. '.' .. tostring(k),
value = v,
indent = indent + 1,
}
pieces[#pieces + 1] = colormap.punctuation ',\n'
end
end
for j = 1, indent - 1 do
pieces[#pieces + 1] = ' '
end
pieces[#pieces + 1] = colormap.punctuation '}'
else
pieces[#pieces + 1] = colormap.misc(tostring(v))
end
end
repl:requirefeature 'console'
function override:displayresults(results)
local pieces = {}
for i = 1, results.n do
dump {
pieces = pieces,
seen = {},
path = '<topvalue>',
value = results[i],
indent = 1,
}
pieces[#pieces + 1] = '\n'
end
stderr:write(tconcat(pieces, ''))
end