use mlua::{Function, Lua, Table, Value};
pub const KNOWN: &[&str] = &[
"append_to_vlist_filter",
"build_page_insert",
"buildpage_filter",
"call_edit",
"contribute_filter",
"define_font",
"find_cidmap_file",
"find_data_file",
"find_enc_file",
"find_font_file",
"find_format_file",
"find_image_file",
"find_map_file",
"find_opentype_file",
"find_output_file",
"find_pk_file",
"find_read_file",
"find_truetype_file",
"find_type1_file",
"find_vf_file",
"find_write_file",
"finish_pdffile",
"finish_pdfpage",
"finish_synctex",
"font_descriptor_objnum_provider",
"glyph_info",
"glyph_not_found",
"glyph_stream_provider",
"hpack_filter",
"hpack_quality",
"hyphenate",
"input_level_string",
"insert_local_par",
"kerning",
"ligaturing",
"linebreak_filter",
"make_extensible",
"mlist_to_hlist",
"new_graf",
"open_read_file",
"page_order_index",
"post_linebreak_filter",
"pre_dump",
"pre_linebreak_filter",
"pre_output_filter",
"process_input_buffer",
"process_jobname",
"process_output_buffer",
"process_pdf_image_content",
"process_rule",
"provide_charproc_data",
"read_cidmap_file",
"read_data_file",
"read_enc_file",
"read_font_file",
"read_map_file",
"read_opentype_file",
"read_pk_file",
"read_truetype_file",
"read_type1_file",
"read_vf_file",
"show_error_hook",
"show_error_message",
"show_ignored_error_message",
"show_lua_error_hook",
"show_warning_message",
"start_file",
"start_page_number",
"start_run",
"stop_file",
"stop_page_number",
"stop_run",
"vpack_filter",
"vpack_quality",
"wrapup_run",
];
pub fn install(lua: &Lua) -> mlua::Result<()> {
let registry = lua.create_table()?;
lua.set_named_registry_value("retex.callbacks", registry)?;
lua.set_app_data(Hot::default());
let t = lua.create_table()?;
t.set(
"register",
lua.create_function(|lua, (name, f): (String, Value)| {
if !KNOWN.contains(&name.as_str()) {
return Ok((
Value::Nil,
Value::String(lua.create_string("No such callback exists.")?),
));
}
let reg: Table = lua.named_registry_value("retex.callbacks")?;
if let Some(i) = HOT.iter().position(|n| *n == name)
&& let Some(mut hot) = lua.app_data_mut::<Hot>()
{
let bit = 1u32 << i;
hot.function &= !bit;
hot.disabled &= !bit;
match f {
Value::Function(_) => hot.function |= bit,
Value::Boolean(false) => hot.disabled |= bit,
_ => {}
}
}
match f {
Value::Function(_) | Value::Boolean(false) => reg.set(name.as_str(), f)?,
_ => reg.set(name.as_str(), Value::Nil)?,
}
let id = KNOWN.iter().position(|n| *n == name).unwrap_or(0) as i64;
Ok((Value::Integer(id), Value::Nil))
})?,
)?;
t.set(
"find",
lua.create_function(|lua, name: String| {
let reg: Table = lua.named_registry_value("retex.callbacks")?;
reg.get::<Value>(name)
})?,
)?;
t.set(
"list",
lua.create_function(|lua, ()| {
let reg: Table = lua.named_registry_value("retex.callbacks")?;
let out = lua.create_table()?;
for name in KNOWN {
let set = matches!(reg.get::<Value>(*name)?, Value::Function(_));
out.set(*name, set)?;
}
Ok(out)
})?,
)?;
t.set(
"known",
lua.create_function(|lua, ()| {
let out = lua.create_table()?;
for name in KNOWN {
out.set(*name, true)?;
}
Ok(out)
})?,
)?;
lua.globals().set("callback", t)?;
Ok(())
}
const HOT: &[&str] = &[
"hyphenate",
"ligaturing",
"kerning",
"buildpage_filter",
"contribute_filter",
"hpack_quality",
"vpack_quality",
"linebreak_filter",
];
#[derive(Default)]
struct Hot {
function: u32,
disabled: u32,
}
pub fn hot(lua: &Lua, name: &str) -> Option<(bool, bool)> {
let i = HOT.iter().position(|n| *n == name)?;
let hot = lua.app_data_ref::<Hot>()?;
Some((hot.function >> i & 1 == 1, hot.disabled >> i & 1 == 1))
}
pub fn get(lua: &Lua, name: &str) -> Option<Function> {
let reg: Table = lua.named_registry_value("retex.callbacks").ok()?;
reg.get::<Function>(name).ok()
}