summaryrefslogtreecommitdiff
path: root/lib/lua-repl/repl/plugins/pretty_print.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lua-repl/repl/plugins/pretty_print.lua')
-rw-r--r--lib/lua-repl/repl/plugins/pretty_print.lua262
1 files changed, 262 insertions, 0 deletions
diff --git a/lib/lua-repl/repl/plugins/pretty_print.lua b/lib/lua-repl/repl/plugins/pretty_print.lua
new file mode 100644
index 00000000..2318f444
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/pretty_print.lua
@@ -0,0 +1,262 @@
+-- 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