mirror of
https://github.com/wez/wezterm.git
synced 2024-12-22 21:01:36 +03:00
restore pretty printing lua values in repl
This time with more correctly working cycle detection
This commit is contained in:
parent
55e7d845e9
commit
92eea8e064
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2229,7 +2229,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mlua"
|
||||
version = "0.8.0-beta.4"
|
||||
source = "git+https://github.com/wez/mlua?branch=table_to_pointer#7884481691169044c18a77ec495886db0d02bee9"
|
||||
source = "git+https://github.com/khvzak/mlua?branch=master#bcf2cbea3798504471d88345327671bf2da7e8e1"
|
||||
dependencies = [
|
||||
"bstr 0.2.17",
|
||||
"cc",
|
||||
|
@ -24,4 +24,4 @@ split-debuginfo = "unpacked"
|
||||
|
||||
[patch.crates-io]
|
||||
xcb = {version="1.1", git="https://github.com/wez/rust-xcb", branch="ffi"}
|
||||
mlua = {version="0.8.0-beta.4", git="https://github.com/wez/mlua", branch="table_to_pointer"}
|
||||
mlua = {version="0.8.0-beta.4", git="https://github.com/khvzak/mlua", branch="master"}
|
||||
|
@ -133,7 +133,7 @@ pub fn make_lua_context(config_file: &Path) -> anyhow::Result<Lua> {
|
||||
}
|
||||
},
|
||||
item @ _ => {
|
||||
let item = format!("{:?}", item);
|
||||
let item = format!("{:#?}", ValuePrinter(item));
|
||||
output.push_str(&item);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
pub use mlua;
|
||||
use mlua::{ToLua, Value as LuaValue};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
use wezterm_dynamic::{FromDynamic, ToDynamic, Value as DynValue};
|
||||
|
||||
/// Implement lua conversion traits for a type.
|
||||
@ -85,6 +87,13 @@ fn lua_value_to_dynamic_impl(
|
||||
value: LuaValue,
|
||||
visited: &mut HashSet<usize>,
|
||||
) -> mlua::Result<DynValue> {
|
||||
let ptr = value.to_pointer() as usize;
|
||||
if visited.contains(&ptr) {
|
||||
// Skip this one, as we've seen it before.
|
||||
// Treat it as a Null value.
|
||||
return Ok(DynValue::Null);
|
||||
}
|
||||
visited.insert(ptr);
|
||||
Ok(match value {
|
||||
LuaValue::Nil => DynValue::Null,
|
||||
LuaValue::String(s) => DynValue::String(s.to_str()?.to_string()),
|
||||
@ -114,14 +123,6 @@ fn lua_value_to_dynamic_impl(
|
||||
}
|
||||
LuaValue::Error(e) => return Err(e),
|
||||
LuaValue::Table(table) => {
|
||||
let ptr = table.to_pointer() as usize;
|
||||
if visited.contains(&ptr) {
|
||||
// Skip this one, as we've seen it before.
|
||||
// Treat it as a Null value.
|
||||
return Ok(DynValue::Null);
|
||||
}
|
||||
visited.insert(ptr);
|
||||
|
||||
if let Ok(true) = table.contains_key(1) {
|
||||
let mut array = vec![];
|
||||
for value in table.sequence_values() {
|
||||
@ -155,3 +156,136 @@ pub struct ValueLua {
|
||||
pub value: wezterm_dynamic::Value,
|
||||
}
|
||||
impl_lua_conversion_dynamic!(ValueLua);
|
||||
|
||||
pub struct ValuePrinter<'lua>(pub LuaValue<'lua>);
|
||||
|
||||
impl<'lua> std::fmt::Debug for ValuePrinter<'lua> {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||
let visited = Rc::new(RefCell::new(HashSet::new()));
|
||||
ValuePrinterHelper {
|
||||
visited,
|
||||
value: self.0.clone(),
|
||||
}
|
||||
.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
struct ValuePrinterHelper<'lua> {
|
||||
visited: Rc<RefCell<HashSet<usize>>>,
|
||||
value: LuaValue<'lua>,
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for ValuePrinterHelper<'lua> {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.value.eq(&rhs.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Eq for ValuePrinterHelper<'lua> {}
|
||||
|
||||
impl<'lua> PartialOrd for ValuePrinterHelper<'lua> {
|
||||
fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
|
||||
let lhs = lua_value_to_dynamic(self.value.clone()).unwrap_or(DynValue::Null);
|
||||
let rhs = lua_value_to_dynamic(rhs.value.clone()).unwrap_or(DynValue::Null);
|
||||
lhs.partial_cmp(&rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Ord for ValuePrinterHelper<'lua> {
|
||||
fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
|
||||
let lhs = lua_value_to_dynamic(self.value.clone()).unwrap_or(DynValue::Null);
|
||||
let rhs = lua_value_to_dynamic(rhs.value.clone()).unwrap_or(DynValue::Null);
|
||||
lhs.cmp(&rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ValuePrinterHelper<'lua> {
|
||||
fn has_cycle(&self, value: &mlua::Value) -> bool {
|
||||
self.visited
|
||||
.borrow()
|
||||
.contains(&(value.to_pointer() as usize))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> std::fmt::Debug for ValuePrinterHelper<'lua> {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||
match &self.value {
|
||||
LuaValue::Nil => fmt.write_str("nil"),
|
||||
LuaValue::Boolean(b) => fmt.write_str(if *b { "true" } else { "false" }),
|
||||
LuaValue::Integer(i) => fmt.write_fmt(format_args!("{}", i)),
|
||||
LuaValue::Number(i) => fmt.write_fmt(format_args!("{}", i)),
|
||||
LuaValue::String(s) => match s.to_str() {
|
||||
Ok(s) => fmt.write_fmt(format_args!("{:?}", s)),
|
||||
Err(_) => fmt.write_fmt(format_args!("{:?}", s.as_bytes())),
|
||||
},
|
||||
LuaValue::Table(t) => {
|
||||
self.visited
|
||||
.borrow_mut()
|
||||
.insert(self.value.to_pointer() as usize);
|
||||
if let Ok(true) = t.contains_key(1) {
|
||||
// Treat as list
|
||||
let mut list = fmt.debug_list();
|
||||
for (idx, value) in t.clone().sequence_values().enumerate() {
|
||||
match value {
|
||||
Ok(value) => {
|
||||
if !self.has_cycle(&value) {
|
||||
list.entry(&Self {
|
||||
visited: Rc::clone(&self.visited),
|
||||
value,
|
||||
});
|
||||
} else {
|
||||
log::warn!("Ignoring value at ordinal position {} which has cyclical reference", idx);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
list.entry(&err);
|
||||
}
|
||||
}
|
||||
}
|
||||
list.finish()?;
|
||||
drop(list);
|
||||
Ok(())
|
||||
} else {
|
||||
// Treat as map; put it into a BTreeMap so that we have a stable
|
||||
// order for our tests.
|
||||
let mut map = BTreeMap::new();
|
||||
for pair in t.clone().pairs::<LuaValue, LuaValue>() {
|
||||
match pair {
|
||||
Ok(pair) => {
|
||||
if !self.has_cycle(&pair.1) {
|
||||
map.insert(
|
||||
Self {
|
||||
visited: Rc::clone(&self.visited),
|
||||
value: pair.0,
|
||||
},
|
||||
Self {
|
||||
visited: Rc::clone(&self.visited),
|
||||
value: pair.1,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
log::warn!(
|
||||
"Ignoring field {:?} which has cyclical reference",
|
||||
Self {
|
||||
visited: Rc::clone(&self.visited),
|
||||
value: pair.0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("error while retrieving map entry: {}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.debug_map().entries(&map).finish()
|
||||
}
|
||||
}
|
||||
LuaValue::UserData(_) | LuaValue::LightUserData(_) => fmt.write_str("userdata"),
|
||||
LuaValue::Thread(_) => fmt.write_str("thread"),
|
||||
LuaValue::Function(_) => fmt.write_str("function"),
|
||||
LuaValue::Error(e) => fmt.write_fmt(format_args!("error {}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::scripting::guiwin::GuiWin;
|
||||
use chrono::prelude::*;
|
||||
use log::Level;
|
||||
use luahelper::ValuePrinter;
|
||||
use mlua::Value;
|
||||
use mux::termwiztermtab::TermWizTerminal;
|
||||
use termwiz::cell::{AttributeChange, CellAttributes, Intensity};
|
||||
@ -133,7 +134,7 @@ pub fn show_debug_overlay(mut term: TermWizTerminal, gui_win: GuiWin) -> anyhow:
|
||||
let chunk = host.lua.load(&expr);
|
||||
match smol::block_on(chunk.eval_async::<Value>()) {
|
||||
Ok(result) => {
|
||||
let value = luahelper::lua_value_to_dynamic(result);
|
||||
let value = ValuePrinter(result);
|
||||
let text = format!("{:#?}", value);
|
||||
term.render(&[Change::Text(format!("{}\r\n", text.replace("\n", "\r\n")))])?;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user