mame/plugins/pretty-print/init.lua

363 lines
9.3 KiB
Lua

--[[
Copyright 2014-2015 The Luvit Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
-- Luv port by Tim Caswell <tim@creationix.com>
local exports = {}
exports.name = "luvit/pretty-print"
exports.version = "1.0.3"
exports.homepage = "https://github.com/luvit/luvit/blob/master/deps/pretty-print.lua"
exports.description = "A lua value pretty printer and colorizer for terminals."
exports.tags = {"colors", "tty"}
exports.license = "Apache 2"
exports.author = { name = "Tim Caswell" }
local uv = require('luv')
local prettyPrint, dump, strip, color, colorize, loadColors
local theme = {}
local useColors = false
local defaultTheme
local stdout, stdin, stderr, width
local quote, quote2, dquote, dquote2, obracket, cbracket, obrace, cbrace, comma, equals, controls
local themes = {
-- nice color theme using 16 ansi colors
[16] = {
property = "0;37", -- white
sep = "1;30", -- bright-black
braces = "1;30", -- bright-black
["nil"] = "1;30", -- bright-black
boolean = "0;33", -- yellow
number = "1;33", -- bright-yellow
string = "0;32", -- green
quotes = "1;32", -- bright-green
escape = "1;32", -- bright-green
["function"] = "0;35", -- purple
thread = "1;35", -- bright-purple
table = "1;34", -- bright blue
userdata = "1;36", -- bright cyan
cdata = "0;36", -- cyan
err = "1;31", -- bright red
success = "1;33;42", -- bright-yellow on green
failure = "1;33;41", -- bright-yellow on red
highlight = "1;36;44", -- bright-cyan on blue
},
-- nice color theme using ansi 256-mode colors
[256] = {
property = "38;5;253",
braces = "38;5;247",
sep = "38;5;240",
["nil"] = "38;5;244",
boolean = "38;5;220", -- yellow-orange
number = "38;5;202", -- orange
string = "38;5;34", -- darker green
quotes = "38;5;40", -- green
escape = "38;5;46", -- bright green
["function"] = "38;5;129", -- purple
thread = "38;5;199", -- pink
table = "38;5;27", -- blue
userdata = "38;5;39", -- blue2
cdata = "38;5;69", -- teal
err = "38;5;196", -- bright red
success = "38;5;120;48;5;22", -- bright green on dark green
failure = "38;5;215;48;5;52", -- bright red on dark red
highlight = "38;5;45;48;5;236", -- bright teal on dark grey
},
}
local special = {
[7] = 'a',
[8] = 'b',
[9] = 't',
[10] = 'n',
[11] = 'v',
[12] = 'f',
[13] = 'r'
}
function strip(str)
return string.gsub(str, '\027%[[^m]*m', '')
end
function loadColors(index)
if index == nil then index = defaultTheme end
-- Remove the old theme
for key in pairs(theme) do
theme[key] = nil
end
if index then
local new = themes[index]
if not new then error("Invalid theme index: " .. tostring(index)) end
-- Add the new theme
for key in pairs(new) do
theme[key] = new[key]
end
useColors = true
else
useColors = false
end
quote = colorize('quotes', "'", 'string')
quote2 = colorize('quotes', "'")
dquote = colorize('quotes', '"', 'string')
dquote2 = colorize('quotes', '"')
obrace = colorize('braces', '{ ')
cbrace = colorize('braces', '}')
obracket = colorize('property', '[')
cbracket = colorize('property', ']')
comma = colorize('sep', ', ')
equals = colorize('sep', ' = ')
controls = {}
for i = 0, 31 do
local c = special[i]
if not c then
if i < 10 then
c = "00" .. tostring(i)
else
c = "0" .. tostring(i)
end
end
controls[i] = colorize('escape', '\\' .. c, 'string')
end
controls[92] = colorize('escape', '\\\\', 'string')
controls[34] = colorize('escape', '\\"', 'string')
controls[39] = colorize('escape', "\\'", 'string')
for i = 128, 255 do
local c
if i < 100 then
c = "0" .. tostring(i)
else
c = tostring(i)
end
controls[i] = colorize('escape', '\\' .. c, 'string')
end
end
function color(colorName)
return '\27[' .. (theme[colorName] or '0') .. 'm'
end
function colorize(colorName, string, resetName)
return useColors and
(color(colorName) .. tostring(string) .. color(resetName)) or
tostring(string)
end
local function stringEscape(c)
return controls[string.byte(c, 1)]
end
function dump(value, recurse, nocolor)
local seen = {}
local output = {}
local offset = 0
local stack = {}
local function recalcOffset(index)
for i = index + 1, #output do
local m = string.match(output[i], "\n([^\n]*)$")
if m then
offset = #(strip(m))
else
offset = offset + #(strip(output[i]))
end
end
end
local function write(text, length)
if not length then length = #(strip(text)) end
-- Create room for data by opening parent blocks
-- Start at the root and go down.
local i = 1
while offset + length > width and stack[i] do
local entry = stack[i]
if not entry.opened then
entry.opened = true
table.insert(output, entry.index + 1, "\n" .. string.rep(" ", i))
-- Recalculate the offset
recalcOffset(entry.index)
-- Bump the index of all deeper entries
for j = i + 1, #stack do
stack[j].index = stack[j].index + 1
end
end
i = i + 1
end
output[#output + 1] = text
offset = offset + length
if offset > width then
return dump(stack)
end
end
local function indent()
stack[#stack + 1] = {
index = #output,
opened = false,
}
end
local function unindent()
stack[#stack] = nil
end
local function process(value)
local typ = type(value)
if typ == 'string' then
if string.match(value, "'") and not string.match(value, '"') then
write(dquote .. string.gsub(value, '[%c\\\128-\255]', stringEscape) .. dquote2)
else
write(quote .. string.gsub(value, "[%c\\'\128-\255]", stringEscape) .. quote2)
end
elseif typ == 'table' and not seen[value] then
if not recurse then seen[value] = true end
write(obrace)
local i = 1
-- Count the number of keys so we know when to stop adding commas
local total = 0
for _ in pairs(value) do total = total + 1 end
local nextIndex = 1
for k, v in pairs(value) do
indent()
if k == nextIndex then
-- if the key matches the last numerical index + 1
-- This is how lists print without keys
nextIndex = k + 1
process(v)
else
if type(k) == "string" and string.find(k,"^[%a_][%a%d_]*$") then
write(colorize("property", k) .. equals)
else
write(obracket)
process(k)
write(cbracket .. equals)
end
if type(v) == "table" then
process(v)
else
indent()
process(v)
unindent()
end
end
if i < total then
write(comma)
else
write(" ")
end
i = i + 1
unindent()
end
write(cbrace)
else
write(colorize(typ, tostring(value)))
end
end
process(value)
local s = table.concat(output, "")
return nocolor and strip(s) or s
end
-- Print replacement that goes through libuv. This is useful on windows
-- to use libuv's code to translate ansi escape codes to windows API calls.
function _G.print(...)
local n = select('#', ...)
local arguments = {...}
for i = 1, n do
arguments[i] = tostring(arguments[i])
end
uv.write(stdout, table.concat(arguments, "\t") .. "\n")
end
function prettyPrint(...)
local n = select('#', ...)
local arguments = { ... }
for i = 1, n do
arguments[i] = dump(arguments[i])
end
print(table.concat(arguments, "\t"))
end
function strip(str)
return string.gsub(str, '\027%[[^m]*m', '')
end
if uv.guess_handle(0) == 'tty' then
stdin = assert(uv.new_tty(0, true))
else
stdin = uv.new_pipe(false)
uv.pipe_open(stdin, 0)
end
if uv.guess_handle(1) == 'tty' then
stdout = assert(uv.new_tty(1, false))
width = uv.tty_get_winsize(stdout)
if width == 0 then width = 80 end
-- auto-detect when 16 color mode should be used
local term = os.getenv("TERM")
if term == 'xterm' or term == 'xterm-256color' then
defaultTheme = 256
else
defaultTheme = 16
end
else
stdout = uv.new_pipe(false)
uv.pipe_open(stdout, 1)
width = 80
end
loadColors()
if uv.guess_handle(2) == 'tty' then
stderr = assert(uv.new_tty(2, false))
else
stderr = uv.new_pipe(false)
uv.pipe_open(stderr, 2)
end
exports.loadColors = loadColors
exports.theme = theme
exports.print = print
exports.prettyPrint = prettyPrint
exports.dump = dump
exports.color = color
exports.colorize = colorize
exports.stdin = stdin
exports.stdout = stdout
exports.stderr = stderr
exports.strip = strip
return exports