manen

Fancy Lua REPL
Log | Files | Refs | README | LICENSE

commit 89f2de646e9dce8693ae30873249ad11de27c464
parent 4bb7ec6d8f73fd0608b5f3cd39902426e75dce5b
Author: Sylvia Ivory <git@sivory.net>
Date:   Mon, 23 Jun 2025 02:13:18 -0700

Remove inspect.lua

Diffstat:
Msrc/editor.rs | 7++-----
Msrc/format.rs | 63++++++---------------------------------------------------------
Dsrc/inspect.lua | 371-------------------------------------------------------------------------------
Msrc/inspect.rs | 44++++++++++++++++++++++++++++++--------------
4 files changed, 38 insertions(+), 447 deletions(-)

diff --git a/src/editor.rs b/src/editor.rs @@ -5,10 +5,7 @@ use reedline::{ }; use crate::{ - format::TableFormat, - highlight::LuaHighlighter, - inspect::{display_basic, display_table}, - validator::LuaValidator, + format::TableFormat, highlight::LuaHighlighter, inspect::display_basic, validator::LuaValidator, }; pub struct Editor { @@ -118,7 +115,7 @@ impl Editor { let value: LuaValue = self.lua.load(line).set_name("=stdin").eval()?; let stringify = match value { - LuaValue::Table(tbl) => display_table(&tbl, true).unwrap(), // self.table_format.format(&self.lua, &tbl, true)?, + LuaValue::Table(tbl) => self.table_format.format(&tbl, true)?, value => display_basic(&value, true), }; diff --git a/src/format.rs b/src/format.rs @@ -1,13 +1,10 @@ -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use comfy_table::{Table, presets::UTF8_FULL_CONDENSED}; use mlua::prelude::*; use nu_ansi_term::Color; -use reedline::Highlighter; -use crate::{highlight::LuaHighlighter, inspect::display_basic}; - -const INSPECT_CODE: &str = include_str!("inspect.lua"); +use crate::inspect::{display_basic, display_table, is_short_printable, print_array}; pub enum TableFormat { ComfyTable(bool), @@ -15,41 +12,6 @@ pub enum TableFormat { Address, } -fn is_array(tbl: &LuaTable) -> LuaResult<(bool, bool)> { - let mut is_arr = true; - let mut has_table = false; - - for (key, value) in tbl.pairs::<LuaValue, LuaValue>().flatten() { - if !(key.is_integer() || key.is_number()) { - is_arr = false; - } - - if let LuaValue::Table(inner) = value { - let (is_arr, has_tbl) = is_array(&inner)?; - - if !is_arr || has_tbl { - has_table = true; - } - } - } - - Ok((is_arr, has_table)) -} - -fn print_array(tbl: &LuaTable) -> String { - let mut buff = Vec::new(); - - for (_, value) in tbl.pairs::<LuaValue, LuaValue>().flatten() { - if let LuaValue::Table(inner) = value { - buff.push(print_array(&inner)); - } else { - buff.push(display_basic(&value, true)); - } - } - - format!("{{ {} }}", buff.join(", ")) -} - fn comfy_table( tbl: &LuaTable, recursive: bool, @@ -64,9 +26,9 @@ fn comfy_table( let id = visited.len(); visited.insert(addr, id); - let (is_array, has_table) = is_array(tbl)?; + let printable = is_short_printable(tbl); - if is_array && !has_table { + if printable { return Ok(print_array(tbl)); } @@ -102,7 +64,7 @@ fn comfy_table( } impl TableFormat { - pub fn format(&self, lua: &Lua, tbl: &LuaTable, colorize: bool) -> LuaResult<String> { + pub fn format(&self, tbl: &LuaTable, colorize: bool) -> LuaResult<String> { match self { TableFormat::Address => { if colorize { @@ -117,20 +79,7 @@ impl TableFormat { } } TableFormat::Inspect => { - if let Some(inspect) = lua.globals().get::<Option<LuaTable>>("_inspect")? { - let out = inspect.call::<String>(tbl)?; - - if colorize { - Ok(LuaHighlighter::new().highlight(&out, 0).render_simple()) - } else { - Ok(out) - } - } else { - let inspect: LuaTable = lua.load(INSPECT_CODE).eval()?; - lua.globals().set("_inspect", inspect)?; - - self.format(lua, tbl, colorize) - } + display_table(tbl, colorize).map_err(|e| LuaError::ExternalError(Arc::new(e))) } TableFormat::ComfyTable(recursive) => { let mut visited = HashMap::new(); diff --git a/src/inspect.lua b/src/inspect.lua @@ -1,371 +0,0 @@ -local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table -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' -inspect._LICENSE = [[ - MIT LICENSE - - Copyright (c) 2022 Enrique GarcĂ­a Cota - - 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. -]] -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 -local match = string.match -local char = string.char -local gsub = string.gsub -local fmt = string.format - -local _rawget -if rawget then - _rawget = rawget -else - _rawget = function(t, k) return t[k] end -end - -local function rawpairs(t) - return next, t, nil -end - - - -local function smartQuote(str) - if match(str, '"') and not match(str, "'") then - return "'" .. str .. "'" - end - return '"' .. gsub(str, '"', '\\"') .. '"' -end - - -local shortControlCharEscapes = { - ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", - ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\127"] = "\\127", -} -local longControlCharEscapes = { ["\127"] = "\127" } -for i = 0, 31 do - local ch = char(i) - if not shortControlCharEscapes[ch] then - shortControlCharEscapes[ch] = "\\" .. i - longControlCharEscapes[ch] = fmt("\\%03d", i) - end -end - -local function escape(str) - return (gsub(gsub(gsub(str, "\\", "\\\\"), - "(%c)%f[0-9]", longControlCharEscapes), - "%c", shortControlCharEscapes)) -end - -local luaKeywords = { - ['and'] = true, - ['break'] = true, - ['do'] = true, - ['else'] = true, - ['elseif'] = true, - ['end'] = true, - ['false'] = true, - ['for'] = true, - ['function'] = true, - ['goto'] = 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 isIdentifier(str) - return type(str) == "string" and - not not str:match("^[_%a][_%a%d]*$") and - not luaKeywords[str] -end - -local flr = math.floor -local function isSequenceKey(k, sequenceLength) - return type(k) == "number" and - flr(k) == k and - 1 <= (k) and - k <= sequenceLength -end - -local defaultTypeOrders = { - ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, - ['function'] = 5, ['userdata'] = 6, ['thread'] = 7, -} - -local function sortKeys(a, b) - local ta, tb = type(a), type(b) - - - if ta == tb and (ta == 'string' or ta == 'number') then - return (a) < (b) - end - - local dta = defaultTypeOrders[ta] or 100 - local dtb = defaultTypeOrders[tb] or 100 - - - return dta == dtb and ta < tb or dta < dtb -end - -local function getKeys(t) - - local seqLen = 1 - while _rawget(t, seqLen) ~= nil do - seqLen = seqLen + 1 - end - seqLen = seqLen - 1 - - local keys, keysLen = {}, 0 - for k in rawpairs(t) do - if not isSequenceKey(k, seqLen) then - keysLen = keysLen + 1 - keys[keysLen] = k - end - end - table.sort(keys, sortKeys) - return keys, keysLen, seqLen -end - -local function countCycles(x, cycles) - if type(x) == "table" then - if cycles[x] then - cycles[x] = cycles[x] + 1 - else - cycles[x] = 1 - for k, v in rawpairs(x) do - countCycles(k, cycles) - countCycles(v, cycles) - end - countCycles(getmetatable(x), cycles) - end - end -end - -local function makePath(path, a, b) - local newPath = {} - local len = #path - for i = 1, len do newPath[i] = path[i] end - - newPath[len + 1] = a - newPath[len + 2] = b - - return newPath -end - - -local function processRecursive(process, - item, - path, - visited) - if item == nil then return nil end - if visited[item] then return visited[item] end - - local processed = process(item, path) - if type(processed) == "table" then - local processedCopy = {} - visited[item] = processedCopy - local processedKey - - for k, v in rawpairs(processed) do - processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) - if processedKey ~= nil then - processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) - end - end - - local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) - if type(mt) ~= 'table' then mt = nil end - setmetatable(processedCopy, mt) - processed = processedCopy - end - return processed -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 - if not id then - local tv = type(v) - id = (ids[tv] or 0) + 1 - ids[v], ids[tv] = id, id - end - return tostring(id) -end - -function Inspector:putValue(v) - local buf = self.buf - 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 - local t = v - - if t == inspect.KEY or t == inspect.METATABLE then - puts(buf, tostring(t)) - elseif self.level >= self.depth then - puts(buf, '{...}') - else - if self.cycles[t] > 1 then puts(buf, fmt('<%d>', self:getId(t))) end - - local keys, keysLen, seqLen = getKeys(t) - - 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]) - else - local k = keys[i - seqLen] - tabify(self) - if isIdentifier(k) then - puts(buf, k) - else - puts(buf, "[") - self:putValue(k) - puts(buf, "]") - end - puts(buf, ' = ') - self:putValue(t[k]) - end - end - - local mt = getmetatable(t) - if type(mt) == 'table' then - if seqLen + keysLen > 0 then puts(buf, ',') end - tabify(self) - puts(buf, '<metatable> = ') - self:putValue(mt) - end - - self.level = self.level - 1 - - if keysLen > 0 or type(mt) == 'table' then - tabify(self) - elseif seqLen > 0 then - puts(buf, ' ') - end - - puts(buf, '}') - end - - else - puts(buf, fmt('<%s %d>', tv, self:getId(v))) - end -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 process = options.process - - if process then - root = processRecursive(process, root, {}, {}) - end - - local cycles = {} - countCycles(root, cycles) - - local inspector = setmetatable({ - buf = { n = 0 }, - ids = {}, - cycles = cycles, - depth = depth, - level = 0, - newline = newline, - indent = indent, - }, Inspector_mt) - - inspector:putValue(root) - - return table.concat(inspector.buf) -end - -setmetatable(inspect, { - __call = function(_, root, options) - return inspect.inspect(root, options) - end, -}) - -return inspect diff --git a/src/inspect.rs b/src/inspect.rs @@ -189,28 +189,39 @@ pub fn display_basic(val: &LuaValue, colorize: bool) -> String { } } -fn is_array(tbl: &LuaTable) -> (bool, bool) { - let mut is_arr = true; - let mut has_table = false; +fn is_short_printable_inner(tbl: &LuaTable, seen: &mut HashSet<usize>) -> bool { + let addr = tbl.to_pointer() as usize; + + if seen.contains(&addr) { + return false; + } + + seen.insert(addr); for (key, value) in tbl.pairs::<LuaValue, LuaValue>().flatten() { - if !(key.is_integer() || key.is_number()) { - is_arr = false; + if !key.is_integer() { + return false; } if let LuaValue::Table(inner) = value { - let (is_arr, has_tbl) = is_array(&inner); + let printable = is_short_printable_inner(&inner, seen); - if !is_arr || has_tbl { - has_table = true; + if !printable { + return false; } } } - (is_arr, has_table) + true +} + +pub fn is_short_printable(tbl: &LuaTable) -> bool { + let mut seen = HashSet::new(); + + is_short_printable_inner(tbl, &mut seen) } -fn print_array(tbl: &LuaTable) -> String { +pub fn print_array(tbl: &LuaTable) -> String { let mut buff = Vec::new(); if tbl.is_empty() { @@ -275,15 +286,16 @@ fn display_table_inner( let id = seen.len(); seen.insert(ptr, id); - let (is_array, has_table) = is_array(tbl); + let printable = is_short_printable(tbl); - if is_array && !has_table { + if printable { return Ok(print_array(tbl)); } let mut buffer = String::new(); - buffer.push_str("{\n"); + // TODO; only output id if necessary + writeln!(&mut buffer, "<{id}>{{")?; for (key, value) in tbl.pairs::<LuaValue, LuaValue>().flatten() { buffer.push_str(&(" ".repeat(indent + 1))); @@ -301,7 +313,11 @@ fn display_table_inner( } if let LuaValue::Table(t) = value { - todo!() + writeln!( + &mut buffer, + "{},", + display_table_inner(&t, colorize, seen, indent + 1)? + )?; } else { writeln!(&mut buffer, "{},", display_basic(&value, colorize))?; }