use mlua::{Lua, MetaMethod, MultiValue, UserData, UserDataMethods, Value};
use crate::with_engine;
#[derive(Clone, Copy, Debug)]
pub struct LLang(pub u32);
impl UserData for LLang {
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
m.add_meta_method(MetaMethod::ToString, |_, this, ()| {
Ok(format!("<language {}>", this.0))
});
}
}
fn lang_of(v: &Value) -> Option<u32> {
match v {
Value::UserData(u) => u.borrow::<LLang>().ok().map(|l| l.0),
Value::Integer(n) => Some(*n as u32),
_ => None,
}
}
pub fn install(lua: &Lua) -> mlua::Result<()> {
let t = lua.create_table()?;
t.set(
"new",
lua.create_function(|lua, n: Option<i64>| {
let id = match n {
Some(n) => n as u32,
None => with_engine(lua, |e| {
let id = e.languages.len() as u32;
e.language_mut(id);
id
})?,
};
Ok(LLang(id))
})?,
)?;
t.set(
"id",
lua.create_function(|_, v: Value| Ok(lang_of(&v).map(i64::from)))?,
)?;
t.set(
"patterns",
lua.create_function(|lua, (l, text): (Value, Option<String>)| {
let Some(n) = lang_of(&l) else {
return Ok(Value::Nil);
};
match text {
Some(s) => {
with_engine(lua, |e| e.language_mut(n).add_patterns(&s))?;
Ok(Value::Nil)
}
None => {
let s = with_engine(lua, |e| e.language(n).map(|l| l.patterns_text()))?;
match s {
Some(s) => Ok(Value::String(lua.create_string(&s)?)),
None => Ok(Value::Nil),
}
}
}
})?,
)?;
t.set(
"clear_patterns",
lua.create_function(|lua, l: Value| {
if let Some(n) = lang_of(&l) {
with_engine(lua, |e| e.language_mut(n).clear_patterns())?;
}
Ok(())
})?,
)?;
t.set(
"hyphenation",
lua.create_function(|lua, (l, text): (Value, Option<String>)| {
let Some(n) = lang_of(&l) else {
return Ok(Value::Nil);
};
match text {
Some(s) => {
with_engine(lua, |e| {
let _ = e.language_mut(n).add_exceptions(&s);
})?;
Ok(Value::Nil)
}
None => {
let s = with_engine(lua, |e| e.language(n).map(|l| l.exceptions_text()))?;
match s {
Some(s) => Ok(Value::String(lua.create_string(&s)?)),
None => Ok(Value::Nil),
}
}
}
})?,
)?;
t.set(
"clear_hyphenation",
lua.create_function(|lua, l: Value| {
if let Some(n) = lang_of(&l) {
with_engine(lua, |e| e.language_mut(n).clear_exceptions())?;
}
Ok(())
})?,
)?;
t.set(
"clean",
lua.create_function(|_, args: MultiValue| {
let v: Vec<Value> = args.into_iter().collect();
let s = match v.last() {
Some(Value::String(s)) => s.to_str()?.to_owned(),
_ => return Ok(None),
};
Ok(retex_hyph::exception::clean(&s))
})?,
)?;
for (name, which) in [
("prehyphenchar", 0),
("posthyphenchar", 1),
("preexhyphenchar", 2),
("postexhyphenchar", 3),
("hyphenationmin", 4),
] {
t.set(
name,
lua.create_function(move |lua, (l, v): (Value, Option<i64>)| {
let Some(n) = lang_of(&l) else {
return Ok(None);
};
with_engine(lua, |e| {
let s = &mut e.language_mut(n).settings;
let field = match which {
0 => &mut s.pre_hyphen_char,
1 => &mut s.post_hyphen_char,
2 => &mut s.pre_ex_hyphen_char,
3 => &mut s.post_ex_hyphen_char,
_ => &mut s.hyphenation_min,
};
match v {
Some(x) => {
*field = x as i32;
None
}
None => Some(i64::from(*field)),
}
})
})?,
)?;
}
t.set(
"sethjcode",
lua.create_function(|lua, (l, c, v): (Value, i64, i64)| {
if let Some(n) = lang_of(&l) {
with_engine(lua, |e| e.language_mut(n).set_hj_code(c as u32, v as u32))?;
}
Ok(())
})?,
)?;
t.set(
"gethjcode",
lua.create_function(|lua, (l, c): (Value, i64)| {
let Some(n) = lang_of(&l) else {
return Ok(0);
};
with_engine(lua, |e| {
e.language(n).map_or(0, |l| i64::from(l.hj_code(c as u32)))
})
})?,
)?;
t.set(
"hyphenate",
lua.create_function(|lua, args: MultiValue| {
let first = args.front().cloned().unwrap_or(Value::Nil);
let Some(h) =
crate::nodelib::to_ref(&first).filter(|_| !matches!(first, Value::Integer(_)))
else {
return Err(crate::texlib::not_a_node(lua, &first));
};
with_engine(lua, |e| {
e.lua_node_pass(retex_engine::nodeproc::NodePass::Hyphenate, h)
})?;
Ok(true)
})?,
)?;
lua.globals().set("lang", t)?;
Ok(())
}