From 64b106c13e18c33be0f2b0de532054e0ed3f731d Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 13 Dec 2023 16:10:08 +1100 Subject: add a cool lua repl --- lib/lua-repl/repl/plugins/pretty_print.lua | 262 +++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 lib/lua-repl/repl/plugins/pretty_print.lua (limited to 'lib/lua-repl/repl/plugins/pretty_print.lua') 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 +-- +-- 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 = '', + value = results[i], + indent = 1, + } + pieces[#pieces + 1] = '\n' + end + + stderr:write(tconcat(pieces, '')) +end -- cgit v1.2.3