manen

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

commit 4bb7ec6d8f73fd0608b5f3cd39902426e75dce5b
parent e656936cf80ad19b0fce27ca114428f6202fe58e
Author: Sylvia Ivory <git@sivory.net>
Date:   Mon, 23 Jun 2025 01:57:13 -0700

Prepare removal of inspect.lua

Diffstat:
Msrc/editor.rs | 9++++++---
Msrc/format.rs | 12++++++------
Msrc/inspect.rs | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 180 insertions(+), 16 deletions(-)

diff --git a/src/editor.rs b/src/editor.rs @@ -5,7 +5,10 @@ use reedline::{ }; use crate::{ - format::TableFormat, highlight::LuaHighlighter, inspect::rewrite_types, validator::LuaValidator, + format::TableFormat, + highlight::LuaHighlighter, + inspect::{display_basic, display_table}, + validator::LuaValidator, }; pub struct Editor { @@ -115,8 +118,8 @@ impl Editor { let value: LuaValue = self.lua.load(line).set_name("=stdin").eval()?; let stringify = match value { - LuaValue::Table(tbl) => self.table_format.format(&self.lua, &tbl, true)?, - value => rewrite_types(&value, true), + LuaValue::Table(tbl) => display_table(&tbl, true).unwrap(), // self.table_format.format(&self.lua, &tbl, true)?, + value => display_basic(&value, true), }; // TODO; colorize diff --git a/src/format.rs b/src/format.rs @@ -5,7 +5,7 @@ use mlua::prelude::*; use nu_ansi_term::Color; use reedline::Highlighter; -use crate::{highlight::LuaHighlighter, inspect::rewrite_types}; +use crate::{highlight::LuaHighlighter, inspect::display_basic}; const INSPECT_CODE: &str = include_str!("inspect.lua"); @@ -43,7 +43,7 @@ fn print_array(tbl: &LuaTable) -> String { if let LuaValue::Table(inner) = value { buff.push(print_array(&inner)); } else { - buff.push(rewrite_types(&value, true)); + buff.push(display_basic(&value, true)); } } @@ -78,17 +78,17 @@ fn comfy_table( let (key_str, value_str) = if let LuaValue::Table(sub) = value { if recursive { ( - rewrite_types(&key, false), + display_basic(&key, false), comfy_table(&sub, recursive, visited)?, ) } else { ( - rewrite_types(&key, false), - rewrite_types(&LuaValue::Table(sub), false), + display_basic(&key, false), + display_basic(&LuaValue::Table(sub), false), ) } } else { - (rewrite_types(&key, false), rewrite_types(&value, false)) + (display_basic(&key, false), display_basic(&value, false)) }; table.add_row(vec![key_str, value_str]); diff --git a/src/inspect.rs b/src/inspect.rs @@ -1,3 +1,8 @@ +use std::{ + collections::{HashMap, HashSet}, + fmt::{self, Write}, +}; + use aho_corasick::AhoCorasick; use lazy_static::lazy_static; use mlua::prelude::*; @@ -42,19 +47,45 @@ lazy_static! { .iter() .map(|s| format!("{}{}", Color::Cyan.paint(s), Color::Green.prefix())) .collect(); + static ref KEYWORDS: HashSet<&'static str> = HashSet::from_iter([ + "and", "break", "do", "else", "elseif", "end", "else", "false", "for", "function", "goto", + "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", + "while", + ]); } fn escape_control(s: &str) -> String { ESCAPER .replace_all(s, &AC_REPLACEMENTS.1) - .replace("\\\\x", "\\x") + .replace("\u{FFFD}", "\\x") } fn escape_control_color(s: &str) -> String { - ESCAPER.replace_all(s, &REPLACEMENT_COLOR).replace( - "\\\\x", - &format!("{}{}", Color::Cyan.paint("\\x"), Color::Green.prefix()), - ) + let s = ESCAPER.replace_all(s, &REPLACEMENT_COLOR); + let mut chars = s.chars(); + let mut new = String::new(); + + while let Some(c) = chars.next() { + if c != '\u{FFFD}' { + new.push(c); + continue; + } + + let (hex1, hex2) = (chars.next(), chars.next()); + let escape = format!( + "\\x{}{}", + hex1.unwrap_or_default(), + hex2.unwrap_or_default() + ); + + new.push_str(&format!( + "{}{}", + Color::Cyan.paint(escape), + Color::Green.prefix() + )); + } + + new } fn remove_invalid(mut bytes: &[u8]) -> String { @@ -81,7 +112,8 @@ fn remove_invalid(mut bytes: &[u8]) -> String { }; for bad_byte in &invalid[..error_len] { - buffer.push_str(&format!("\\x{:X?}", bad_byte)); + // this *might* cause some false positives + buffer.push_str(&format!("\u{FFFD}{:X?}", bad_byte)); } bytes = &invalid[error_len..]; @@ -131,7 +163,7 @@ fn handle_strings<'a>(colorize: bool, strings: AnsiStrings<'a>) -> String { } } -pub fn rewrite_types(val: &LuaValue, colorize: bool) -> String { +pub fn display_basic(val: &LuaValue, colorize: bool) -> String { match addr_color(val) { Some((addr, color)) => { let strings: &[AnsiString<'static>] = &[ @@ -156,3 +188,132 @@ pub fn rewrite_types(val: &LuaValue, colorize: bool) -> String { } } } + +fn is_array(tbl: &LuaTable) -> (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; + } + } + } + + (is_arr, has_table) +} + +fn print_array(tbl: &LuaTable) -> String { + let mut buff = Vec::new(); + + if tbl.is_empty() { + return String::from("{}"); + } + + 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 is_valid_identifier(s: &str) -> bool { + if KEYWORDS.contains(s) { + return false; + } + + let mut chars = s.chars(); + let first = if let Some(c) = chars.next() { + c + } else { + return false; + }; + + // [A-Z_a-z] + if !first.is_ascii_alphabetic() && first != '_' { + return false; + } + + let s = chars.as_str(); + + if s.is_empty() { + return true; + } + + // [A-Z_a-z0-9] + if s.find(|c: char| !c.is_ascii_alphanumeric() && c != '_') + .is_some() + { + return false; + } + + true +} + +fn display_table_inner( + tbl: &LuaTable, + colorize: bool, + seen: &mut HashMap<usize, usize>, + indent: usize, +) -> Result<String, fmt::Error> { + let ptr = tbl.to_pointer() as usize; + if let Some(id) = seen.get(&ptr) { + return Ok(format!("<{id}>")); + } + + let id = seen.len(); + seen.insert(ptr, id); + + let (is_array, has_table) = is_array(tbl); + + if is_array && !has_table { + return Ok(print_array(tbl)); + } + + let mut buffer = String::new(); + + buffer.push_str("{\n"); + + for (key, value) in tbl.pairs::<LuaValue, LuaValue>().flatten() { + buffer.push_str(&(" ".repeat(indent + 1))); + + if let LuaValue::String(ref s) = key { + let clean = cleanup_string(s); + + if is_valid_identifier(&clean) { + write!(&mut buffer, "{clean} = ")? + } else { + write!(&mut buffer, "[{}] = ", display_basic(&key, colorize))? + } + } else { + write!(&mut buffer, "[{}] = ", display_basic(&key, colorize))?; + } + + if let LuaValue::Table(t) = value { + todo!() + } else { + writeln!(&mut buffer, "{},", display_basic(&value, colorize))?; + } + } + + write!(&mut buffer, "{}}}", " ".repeat(indent))?; + + Ok(buffer) +} + +pub fn display_table(tbl: &LuaTable, colorize: bool) -> Result<String, fmt::Error> { + let mut seen = HashMap::new(); + + display_table_inner(tbl, colorize, &mut seen, 0) +}