1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-25 10:22:43 +03:00

config: split out lua functions into their own crates

This shaves off some build time and allows more parallism in the build.
This commit is contained in:
Wez Furlong 2022-05-18 22:49:53 -07:00
parent 42a7c1d481
commit 2f14d640e8
25 changed files with 785 additions and 531 deletions

84
Cargo.lock generated
View File

@ -334,6 +334,17 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
name = "base91" name = "base91"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "battery"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"luahelper",
"starship-battery",
"wezterm-dynamic",
]
[[package]] [[package]]
name = "benchmarking" name = "benchmarking"
version = "0.4.11" version = "0.4.11"
@ -643,13 +654,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags", "bitflags",
"bstr 0.2.17",
"chrono", "chrono",
"colorgrad", "colorgrad",
"dirs-next", "dirs-next",
"enum-display-derive", "enum-display-derive",
"env_logger", "env_logger",
"filenamegen",
"hostname", "hostname",
"lazy_static", "lazy_static",
"libc", "libc",
@ -657,7 +666,6 @@ dependencies = [
"luahelper", "luahelper",
"mlua", "mlua",
"notify", "notify",
"open",
"ordered-float", "ordered-float",
"portable-pty", "portable-pty",
"promise", "promise",
@ -665,16 +673,12 @@ dependencies = [
"serde_json", "serde_json",
"shlex", "shlex",
"smol", "smol",
"starship-battery",
"terminfo",
"termwiz", "termwiz",
"toml", "toml",
"umask", "umask",
"unicode-segmentation",
"wezterm-bidi", "wezterm-bidi",
"wezterm-dynamic", "wezterm-dynamic",
"wezterm-input-types", "wezterm-input-types",
"wezterm-ssh",
"wezterm-term", "wezterm-term",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -1070,16 +1074,22 @@ name = "env-bootstrap"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"battery",
"chrono", "chrono",
"cocoa", "cocoa",
"config", "config",
"dirs-next", "dirs-next",
"env_logger", "env_logger",
"filesystem",
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"logging",
"objc", "objc",
"spawn-funcs",
"ssh-funcs",
"termwiz", "termwiz",
"termwiz-funcs",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -1173,6 +1183,17 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "filesystem"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"filenamegen",
"luahelper",
"smol",
]
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.16" version = "0.2.16"
@ -1996,6 +2017,16 @@ dependencies = [
"value-bag", "value-bag",
] ]
[[package]]
name = "logging"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"log",
"luahelper",
]
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.7.5" version = "0.7.5"
@ -2280,6 +2311,7 @@ dependencies = [
"smol", "smol",
"terminfo", "terminfo",
"termwiz", "termwiz",
"termwiz-funcs",
"textwrap 0.15.0", "textwrap 0.15.0",
"thiserror", "thiserror",
"unicode-segmentation", "unicode-segmentation",
@ -3840,6 +3872,19 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "spawn-funcs"
version = "0.1.0"
dependencies = [
"anyhow",
"bstr 0.2.17",
"config",
"luahelper",
"open",
"smol",
"wezterm-dynamic",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.5.2" version = "0.5.2"
@ -3855,6 +3900,16 @@ dependencies = [
"lock_api 0.4.7", "lock_api 0.4.7",
] ]
[[package]]
name = "ssh-funcs"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"luahelper",
"wezterm-ssh",
]
[[package]] [[package]]
name = "ssh2" name = "ssh2"
version = "0.9.3" version = "0.9.3"
@ -4088,6 +4143,20 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "termwiz-funcs"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"lazy_static",
"luahelper",
"terminfo",
"termwiz",
"unicode-segmentation",
"wezterm-dynamic",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"
@ -4806,6 +4875,7 @@ dependencies = [
"tabout", "tabout",
"terminfo", "terminfo",
"termwiz", "termwiz",
"termwiz-funcs",
"textwrap 0.15.0", "textwrap 0.15.0",
"thiserror", "thiserror",
"tiny-skia", "tiny-skia",

View File

@ -12,14 +12,11 @@ env_logger = "0.9"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
starship-battery = "0.7"
bitflags = "1.3" bitflags = "1.3"
bstr = "0.2"
chrono = {version="0.4", features=["unstable-locales"]} chrono = {version="0.4", features=["unstable-locales"]}
colorgrad = "0.5" colorgrad = "0.5"
dirs-next = "2.0" dirs-next = "2.0"
enum-display-derive = "0.1" enum-display-derive = "0.1"
filenamegen = "0.2"
hostname = "0.3" hostname = "0.3"
lazy_static = "1.4" lazy_static = "1.4"
libc = "0.2" libc = "0.2"
@ -28,7 +25,6 @@ luahelper = { path = "../luahelper" }
mlua = {version="0.8.0-beta.4", features=["vendored", "lua54", "async", "send"]} mlua = {version="0.8.0-beta.4", features=["vendored", "lua54", "async", "send"]}
# file change notification # file change notification
notify = "4.0" notify = "4.0"
open = "2.0"
ordered-float = { version = "3.0", features = ["serde"] } ordered-float = { version = "3.0", features = ["serde"] }
portable-pty = { path = "../pty", features = ["serde_support"]} portable-pty = { path = "../pty", features = ["serde_support"]}
promise = { path = "../promise" } promise = { path = "../promise" }
@ -36,15 +32,12 @@ serde = {version="1.0", features = ["rc", "derive"]}
serde_json = "1.0" serde_json = "1.0"
shlex = "1.1" shlex = "1.1"
smol = "1.2" smol = "1.2"
terminfo = "0.7"
termwiz = { path = "../termwiz", features=["use_serde"] } termwiz = { path = "../termwiz", features=["use_serde"] }
toml = "0.5" toml = "0.5"
umask = { path = "../umask" } umask = { path = "../umask" }
unicode-segmentation = "1.8"
wezterm-dynamic = { path = "../wezterm-dynamic" } wezterm-dynamic = { path = "../wezterm-dynamic" }
wezterm-bidi = { path = "../bidi" } wezterm-bidi = { path = "../bidi" }
wezterm-input-types = { path = "../wezterm-input-types" } wezterm-input-types = { path = "../wezterm-input-types" }
wezterm-ssh = { path = "../wezterm-ssh" }
wezterm-term = { path = "../term", features=["use_serde"] } wezterm-term = { path = "../term", features=["use_serde"] }
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]

View File

@ -1,4 +1,3 @@
use crate::lua::{format_as_escapes, FormatItem};
use crate::*; use crate::*;
use luahelper::impl_lua_conversion_dynamic; use luahelper::impl_lua_conversion_dynamic;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -297,7 +296,7 @@ impl Default for TabBarStyle {
} }
fn default_new_tab() -> String { fn default_new_tab() -> String {
format_as_escapes(vec![FormatItem::Text(" + ".to_string())]).unwrap() " + ".to_string()
} }
#[derive(Debug, Clone, FromDynamic, ToDynamic)] #[derive(Debug, Clone, FromDynamic, ToDynamic)]

View File

@ -4,23 +4,50 @@ use crate::{
TextStyle, TextStyle,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use bstr::BString; use luahelper::from_lua_value_dynamic;
pub use luahelper::*; use mlua::{FromLua, Lua, Table, ToLuaMulti, Value, Variadic};
use mlua::{FromLua, Lua, Table, ToLua, ToLuaMulti, Value, Variadic};
use ordered_float::NotNan; use ordered_float::NotNan;
use smol::prelude::*;
use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::path::Path; use std::path::Path;
use termwiz::cell::{grapheme_column_width, unicode_column_width, AttributeChange, CellAttributes}; use std::sync::Mutex;
use termwiz::color::{AnsiColor, ColorAttribute, ColorSpec, RgbColor};
use termwiz::input::Modifiers;
use termwiz::surface::change::Change;
use unicode_segmentation::UnicodeSegmentation;
use wezterm_dynamic::{FromDynamic, ToDynamic}; use wezterm_dynamic::{FromDynamic, ToDynamic};
pub use mlua;
static LUA_REGISTRY_USER_CALLBACK_COUNT: &str = "wezterm-user-callback-count"; static LUA_REGISTRY_USER_CALLBACK_COUNT: &str = "wezterm-user-callback-count";
pub type SetupFunc = fn(&Lua) -> anyhow::Result<()>;
lazy_static::lazy_static! {
static ref SETUP_FUNCS: Mutex<Vec<SetupFunc>> = Mutex::new(vec![]);
}
pub fn add_context_setup_func(func: SetupFunc) {
SETUP_FUNCS.lock().unwrap().push(func);
}
pub fn get_or_create_module<'lua>(lua: &'lua Lua, name: &str) -> anyhow::Result<mlua::Table<'lua>> {
let globals = lua.globals();
let package: Table = globals.get("package")?;
let loaded: Table = package.get("loaded")?;
let module = loaded.get(name)?;
match module {
Value::Nil => {
let module = lua.create_table()?;
loaded.set(name, module.clone())?;
Ok(module)
}
Value::Table(table) => Ok(table),
wat => anyhow::bail!(
"cannot register module {} as package.loaded.{} is already set to a value of type {}",
name,
name,
wat.type_name()
),
}
}
/// Set up a lua context for executing some code. /// Set up a lua context for executing some code.
/// The path to the directory containing the configuration is /// The path to the directory containing the configuration is
/// passed in and is used to pre-set some global values in /// passed in and is used to pre-set some global values in
@ -54,7 +81,7 @@ pub fn make_lua_context(config_file: &Path) -> anyhow::Result<Lua> {
{ {
let globals = lua.globals(); let globals = lua.globals();
// This table will be the `wezterm` module in the script // This table will be the `wezterm` module in the script
let wezterm_mod = lua.create_table()?; let wezterm_mod = get_or_create_module(&lua, "wezterm")?;
let package: Table = globals.get("package")?; let package: Table = globals.get("package")?;
let package_path: String = package.get("path")?; let package_path: String = package.get("path")?;
@ -100,7 +127,6 @@ pub fn make_lua_context(config_file: &Path) -> anyhow::Result<Lua> {
wezterm_mod.set("target_triple", crate::wezterm_target_triple())?; wezterm_mod.set("target_triple", crate::wezterm_target_triple())?;
wezterm_mod.set("version", crate::wezterm_version())?; wezterm_mod.set("version", crate::wezterm_version())?;
wezterm_mod.set("nerdfonts", NerdFonts {})?;
wezterm_mod.set("home_dir", crate::HOME_DIR.to_str())?; wezterm_mod.set("home_dir", crate::HOME_DIR.to_str())?;
wezterm_mod.set( wezterm_mod.set(
"running_under_wsl", "running_under_wsl",
@ -117,92 +143,6 @@ pub fn make_lua_context(config_file: &Path) -> anyhow::Result<Lua> {
lua.create_function(|_, ()| Ok(crate::COLOR_SCHEMES.clone()))?, lua.create_function(|_, ()| Ok(crate::COLOR_SCHEMES.clone()))?,
)?; )?;
fn print_helper(args: Variadic<Value>) -> String {
let mut output = String::new();
for (idx, item) in args.into_iter().enumerate() {
if idx > 0 {
output.push(' ');
}
match item {
Value::String(s) => match s.to_str() {
Ok(s) => output.push_str(s),
Err(_) => {
let item = String::from_utf8_lossy(s.as_bytes());
output.push_str(&item);
}
},
item @ _ => {
let item = format!("{:#?}", ValuePrinter(item));
output.push_str(&item);
}
}
}
output
}
wezterm_mod.set(
"log_error",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::error!("lua: {}", output);
Ok(())
})?,
)?;
wezterm_mod.set(
"log_info",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::info!("lua: {}", output);
Ok(())
})?,
)?;
wezterm_mod.set(
"log_warn",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::warn!("lua: {}", output);
Ok(())
})?,
)?;
globals.set(
"print",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::info!("lua: {}", output);
Ok(())
})?,
)?;
wezterm_mod.set(
"column_width",
lua.create_function(|_, s: String| Ok(unicode_column_width(&s, None)))?,
)?;
wezterm_mod.set(
"pad_right",
lua.create_function(|_, (s, width): (String, usize)| Ok(pad_right(s, width)))?,
)?;
wezterm_mod.set(
"pad_left",
lua.create_function(|_, (s, width): (String, usize)| Ok(pad_left(s, width)))?,
)?;
wezterm_mod.set(
"truncate_right",
lua.create_function(|_, (s, max_width): (String, usize)| {
Ok(truncate_right(&s, max_width))
})?,
)?;
wezterm_mod.set(
"truncate_left",
lua.create_function(|_, (s, max_width): (String, usize)| {
Ok(truncate_left(&s, max_width))
})?,
)?;
wezterm_mod.set("font", lua.create_function(font)?)?; wezterm_mod.set("font", lua.create_function(font)?)?;
wezterm_mod.set( wezterm_mod.set(
"font_with_fallback", "font_with_fallback",
@ -212,220 +152,37 @@ pub fn make_lua_context(config_file: &Path) -> anyhow::Result<Lua> {
wezterm_mod.set("action", lua.create_function(action)?)?; wezterm_mod.set("action", lua.create_function(action)?)?;
lua.set_named_registry_value(LUA_REGISTRY_USER_CALLBACK_COUNT, 0)?; lua.set_named_registry_value(LUA_REGISTRY_USER_CALLBACK_COUNT, 0)?;
wezterm_mod.set("action_callback", lua.create_function(action_callback)?)?; wezterm_mod.set("action_callback", lua.create_function(action_callback)?)?;
wezterm_mod.set("permute_any_mods", lua.create_function(permute_any_mods)?)?;
wezterm_mod.set(
"permute_any_or_no_mods",
lua.create_function(permute_any_or_no_mods)?,
)?;
wezterm_mod.set("read_dir", lua.create_async_function(read_dir)?)?;
wezterm_mod.set("glob", lua.create_async_function(glob)?)?;
wezterm_mod.set("utf16_to_utf8", lua.create_function(utf16_to_utf8)?)?; wezterm_mod.set("utf16_to_utf8", lua.create_function(utf16_to_utf8)?)?;
wezterm_mod.set("split_by_newlines", lua.create_function(split_by_newlines)?)?; wezterm_mod.set("split_by_newlines", lua.create_function(split_by_newlines)?)?;
wezterm_mod.set(
"run_child_process",
lua.create_async_function(run_child_process)?,
)?;
wezterm_mod.set(
"background_child_process",
lua.create_async_function(background_child_process)?,
)?;
wezterm_mod.set("open_with", lua.create_function(open_with)?)?;
wezterm_mod.set("on", lua.create_function(register_event)?)?; wezterm_mod.set("on", lua.create_function(register_event)?)?;
wezterm_mod.set("emit", lua.create_async_function(emit_event)?)?; wezterm_mod.set("emit", lua.create_async_function(emit_event)?)?;
wezterm_mod.set("sleep_ms", lua.create_async_function(sleep_ms)?)?; wezterm_mod.set("sleep_ms", lua.create_async_function(sleep_ms)?)?;
wezterm_mod.set("format", lua.create_function(format)?)?;
wezterm_mod.set("strftime", lua.create_function(strftime)?)?; wezterm_mod.set("strftime", lua.create_function(strftime)?)?;
wezterm_mod.set("battery_info", lua.create_function(battery_info)?)?;
wezterm_mod.set("gradient_colors", lua.create_function(gradient_colors)?)?; wezterm_mod.set("gradient_colors", lua.create_function(gradient_colors)?)?;
wezterm_mod.set(
"enumerate_ssh_hosts",
lua.create_function(enumerate_ssh_hosts)?,
)?;
package.set("path", path_array.join(";"))?; package.set("path", path_array.join(";"))?;
}
let loaded: Table = package.get("loaded")?; for func in SETUP_FUNCS.lock().unwrap().iter() {
loaded.set("wezterm", wezterm_mod)?; func(&lua)?;
} }
Ok(lua) Ok(lua)
} }
use termwiz::caps::{Capabilities, ColorLevel, ProbeHints};
use termwiz::render::terminfo::TerminfoRenderer;
lazy_static::lazy_static! {
static ref CAPS: Capabilities = {
let data = include_bytes!("../../termwiz/data/xterm-256color");
let db = terminfo::Database::from_buffer(&data[..]).unwrap();
Capabilities::new_with_hints(
ProbeHints::new_from_env()
.term(Some("xterm-256color".into()))
.terminfo_db(Some(db))
.color_level(Some(ColorLevel::TrueColor))
.colorterm(None)
.colorterm_bce(None)
.term_program(Some("WezTerm".into()))
.term_program_version(Some(crate::wezterm_version().into())),
)
.expect("cannot fail to make internal Capabilities")
};
}
pub fn new_wezterm_terminfo_renderer() -> TerminfoRenderer {
TerminfoRenderer::new(CAPS.clone())
}
#[derive(Debug, FromDynamic, ToDynamic, Clone, PartialEq, Eq)]
pub enum FormatColor {
AnsiColor(AnsiColor),
Color(String),
Default,
}
impl FormatColor {
fn to_attr(self) -> ColorAttribute {
let spec: ColorSpec = self.into();
let attr: ColorAttribute = spec.into();
attr
}
}
impl Into<ColorSpec> for FormatColor {
fn into(self) -> ColorSpec {
match self {
FormatColor::AnsiColor(c) => c.into(),
FormatColor::Color(s) => {
let rgb = RgbColor::from_named_or_rgb_string(&s)
.unwrap_or(RgbColor::new_8bpc(0xff, 0xff, 0xff));
rgb.into()
}
FormatColor::Default => ColorSpec::Default,
}
}
}
#[derive(Debug, FromDynamic, ToDynamic, Clone, PartialEq, Eq)]
pub enum FormatItem {
Foreground(FormatColor),
Background(FormatColor),
Attribute(AttributeChange),
Text(String),
}
impl_lua_conversion_dynamic!(FormatItem);
impl Into<Change> for FormatItem {
fn into(self) -> Change {
match self {
Self::Attribute(change) => change.into(),
Self::Text(t) => t.into(),
Self::Foreground(c) => AttributeChange::Foreground(c.to_attr()).into(),
Self::Background(c) => AttributeChange::Background(c.to_attr()).into(),
}
}
}
struct FormatTarget {
target: Vec<u8>,
}
impl std::io::Write for FormatTarget {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
std::io::Write::write(&mut self.target, buf)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl termwiz::render::RenderTty for FormatTarget {
fn get_size_in_cells(&mut self) -> termwiz::Result<(usize, usize)> {
Ok((80, 24))
}
}
fn strftime<'lua>(_: &'lua Lua, format: String) -> mlua::Result<String> { fn strftime<'lua>(_: &'lua Lua, format: String) -> mlua::Result<String> {
use chrono::prelude::*; use chrono::prelude::*;
let local: DateTime<Local> = Local::now(); let local: DateTime<Local> = Local::now();
Ok(local.format(&format).to_string()) Ok(local.format(&format).to_string())
} }
pub fn format_as_escapes(items: Vec<FormatItem>) -> anyhow::Result<String> {
let mut changes: Vec<Change> = items.into_iter().map(Into::into).collect();
changes.push(Change::AllAttributes(CellAttributes::default()).into());
let mut renderer = new_wezterm_terminfo_renderer();
let mut target = FormatTarget { target: vec![] };
renderer.render_to(&changes, &mut target)?;
Ok(String::from_utf8(target.target)?)
}
fn format<'lua>(_: &'lua Lua, items: Vec<FormatItem>) -> mlua::Result<String> {
format_as_escapes(items).map_err(|e| mlua::Error::external(e))
}
#[derive(FromDynamic, ToDynamic, Debug)]
struct BatteryInfo {
state_of_charge: f32,
vendor: String,
model: String,
state: String,
serial: String,
time_to_full: Option<f32>,
time_to_empty: Option<f32>,
}
impl_lua_conversion_dynamic!(BatteryInfo);
fn opt_string(s: Option<&str>) -> String {
match s {
Some(s) => s,
None => "unknown",
}
.to_string()
}
fn battery_info<'lua>(_: &'lua Lua, _: ()) -> mlua::Result<Vec<BatteryInfo>> {
use starship_battery::{Manager, State};
let manager = Manager::new().map_err(|e| mlua::Error::external(e))?;
let mut result = vec![];
for b in manager.batteries().map_err(|e| mlua::Error::external(e))? {
let bat = b.map_err(|e| mlua::Error::external(e))?;
result.push(BatteryInfo {
state_of_charge: bat.state_of_charge().value,
vendor: opt_string(bat.vendor()),
model: opt_string(bat.model()),
serial: opt_string(bat.serial_number()),
state: match bat.state() {
State::Charging => "Charging",
State::Discharging => "Discharging",
State::Empty => "Empty",
State::Full => "Full",
State::Unknown | _ => "Unknown",
}
.to_string(),
time_to_full: bat.time_to_full().map(|q| q.value),
time_to_empty: bat.time_to_empty().map(|q| q.value),
})
}
Ok(result)
}
async fn sleep_ms<'lua>(_: &'lua Lua, milliseconds: u64) -> mlua::Result<()> { async fn sleep_ms<'lua>(_: &'lua Lua, milliseconds: u64) -> mlua::Result<()> {
let duration = std::time::Duration::from_millis(milliseconds); let duration = std::time::Duration::from_millis(milliseconds);
smol::Timer::after(duration).await; smol::Timer::after(duration).await;
Ok(()) Ok(())
} }
fn open_with<'lua>(_: &'lua Lua, (url, app): (String, Option<String>)) -> mlua::Result<()> {
if let Some(app) = app {
open::with_in_background(url, app);
} else {
open::that_in_background(url);
}
Ok(())
}
/// Returns the system hostname. /// Returns the system hostname.
/// Errors may occur while retrieving the hostname from the system, /// Errors may occur while retrieving the hostname from the system,
/// or if the hostname isn't a UTF-8 string. /// or if the hostname isn't a UTF-8 string.
@ -669,49 +426,6 @@ fn action_callback<'lua>(lua: &'lua Lua, callback: mlua::Function) -> mlua::Resu
return Ok(KeyAssignment::EmitEvent(user_event_id)); return Ok(KeyAssignment::EmitEvent(user_event_id));
} }
async fn read_dir<'lua>(_: &'lua Lua, path: String) -> mlua::Result<Vec<String>> {
let mut dir = smol::fs::read_dir(path)
.await
.map_err(|e| mlua::Error::external(e))?;
let mut entries = vec![];
for entry in dir.next().await {
let entry = entry.map_err(|e| mlua::Error::external(e))?;
if let Some(utf8) = entry.path().to_str() {
entries.push(utf8.to_string());
} else {
return Err(mlua::Error::external(anyhow!(
"path entry {} is not representable as utf8",
entry.path().display()
)));
}
}
Ok(entries)
}
async fn glob<'lua>(
_: &'lua Lua,
(pattern, path): (String, Option<String>),
) -> mlua::Result<Vec<String>> {
let entries = smol::unblock(move || {
let mut entries = vec![];
let glob = filenamegen::Glob::new(&pattern)?;
for path in glob.walk(path.as_ref().map(|s| s.as_str()).unwrap_or(".")) {
if let Some(utf8) = path.to_str() {
entries.push(utf8.to_string());
} else {
return Err(anyhow!(
"path entry {} is not representable as utf8",
path.display()
));
}
}
Ok(entries)
})
.await
.map_err(|e| mlua::Error::external(e))?;
Ok(entries)
}
fn split_by_newlines<'lua>(_: &'lua Lua, text: String) -> mlua::Result<Vec<String>> { fn split_by_newlines<'lua>(_: &'lua Lua, text: String) -> mlua::Result<Vec<String>> {
Ok(text Ok(text
.lines() .lines()
@ -847,94 +561,6 @@ fn utf16_to_utf8<'lua>(_: &'lua Lua, text: mlua::String) -> mlua::Result<String>
String::from_utf16(wide).map_err(|e| mlua::Error::external(e)) String::from_utf16(wide).map_err(|e| mlua::Error::external(e))
} }
async fn run_child_process<'lua>(
_: &'lua Lua,
args: Vec<String>,
) -> mlua::Result<(bool, BString, BString)> {
let mut cmd = smol::process::Command::new(&args[0]);
if args.len() > 1 {
cmd.args(&args[1..]);
}
#[cfg(windows)]
{
use smol::process::windows::CommandExt;
cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
}
let output = cmd.output().await.map_err(|e| mlua::Error::external(e))?;
Ok((
output.status.success(),
output.stdout.into(),
output.stderr.into(),
))
}
async fn background_child_process<'lua>(_: &'lua Lua, args: Vec<String>) -> mlua::Result<()> {
let mut cmd = smol::process::Command::new(&args[0]);
if args.len() > 1 {
cmd.args(&args[1..]);
}
#[cfg(windows)]
{
use smol::process::windows::CommandExt;
cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
}
cmd.stdin(smol::process::Stdio::null())
.spawn()
.map_err(|e| mlua::Error::external(e))?;
Ok(())
}
fn permute_any_mods<'lua>(
lua: &'lua Lua,
item: mlua::Table,
) -> mlua::Result<Vec<mlua::Value<'lua>>> {
permute_mods(lua, item, false)
}
fn permute_any_or_no_mods<'lua>(
lua: &'lua Lua,
item: mlua::Table,
) -> mlua::Result<Vec<mlua::Value<'lua>>> {
permute_mods(lua, item, true)
}
fn permute_mods<'lua>(
lua: &'lua Lua,
item: mlua::Table,
allow_none: bool,
) -> mlua::Result<Vec<mlua::Value<'lua>>> {
let mut result = vec![];
for ctrl in &[Modifiers::NONE, Modifiers::CTRL] {
for shift in &[Modifiers::NONE, Modifiers::SHIFT] {
for alt in &[Modifiers::NONE, Modifiers::ALT] {
for sup in &[Modifiers::NONE, Modifiers::SUPER] {
let flags = *ctrl | *shift | *alt | *sup;
if flags == Modifiers::NONE && !allow_none {
continue;
}
let new_item = lua.create_table()?;
for pair in item.clone().pairs::<mlua::Value, mlua::Value>() {
let (k, v) = pair?;
new_item.set(k, v)?;
}
new_item.set("mods", format!("{:?}", flags))?;
result.push(new_item.to_lua(lua)?);
}
}
}
}
Ok(result)
}
fn gradient_colors<'lua>( fn gradient_colors<'lua>(
_lua: &'lua Lua, _lua: &'lua Lua,
(gradient, num_colors): (Gradient, usize), (gradient, num_colors): (Gradient, usize),
@ -946,7 +572,7 @@ fn gradient_colors<'lua>(
.collect()) .collect())
} }
fn add_to_config_reload_watch_list<'lua>( pub fn add_to_config_reload_watch_list<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
args: Variadic<String>, args: Variadic<String>,
) -> mlua::Result<()> { ) -> mlua::Result<()> {
@ -956,33 +582,6 @@ fn add_to_config_reload_watch_list<'lua>(
Ok(()) Ok(())
} }
fn enumerate_ssh_hosts<'lua>(
lua: &'lua Lua,
config_files: Variadic<String>,
) -> mlua::Result<HashMap<String, wezterm_ssh::ConfigMap>> {
let mut config = wezterm_ssh::Config::new();
for file in config_files {
config.add_config_file(file);
}
config.add_default_config_files();
// Trigger a config reload if any of the parsed ssh config files change
let files: Variadic<String> = config
.loaded_config_files()
.into_iter()
.filter_map(|p| p.to_str().map(|s| s.to_string()))
.collect();
add_to_config_reload_watch_list(lua, files)?;
let mut map = HashMap::new();
for host in config.enumerate_hosts() {
let host_config = config.for_host(&host);
map.insert(host, host_config);
}
Ok(map)
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -1047,7 +646,7 @@ mod test {
local wezterm = require 'wezterm'; local wezterm = require 'wezterm';
wezterm.on('foo', function (n) wezterm.on('foo', function (n)
wezterm.log_error("lua hook recording " .. n); print("lua hook recording " .. n);
end); end);
-- one of the foo handlers returns false, so the emit -- one of the foo handlers returns false, so the emit
@ -1056,7 +655,7 @@ end);
assert(wezterm.emit('foo', 2) == false) assert(wezterm.emit('foo', 2) == false)
wezterm.on('bar', function (n, str) wezterm.on('bar', function (n, str)
wezterm.log_error("bar says " .. n .. " " .. str) print("bar says " .. n .. " " .. str)
end); end);
-- None of the bar handlers return anything, so the -- None of the bar handlers return anything, so the
@ -1073,68 +672,3 @@ assert(wezterm.emit('bar', 42, 'woot') == true)
Ok(()) Ok(())
} }
} }
pub fn pad_right(mut result: String, width: usize) -> String {
let mut len = unicode_column_width(&result, None);
while len < width {
result.push(' ');
len += 1;
}
result
}
pub fn pad_left(mut result: String, width: usize) -> String {
let mut len = unicode_column_width(&result, None);
while len < width {
result.insert(0, ' ');
len += 1;
}
result
}
pub fn truncate_left(s: &str, max_width: usize) -> String {
let mut result = vec![];
let mut len = 0;
for g in s.graphemes(true).rev() {
let g_len = grapheme_column_width(g, None);
if g_len + len > max_width {
break;
}
result.push(g);
len += g_len;
}
result.reverse();
result.join("")
}
pub fn truncate_right(s: &str, max_width: usize) -> String {
let mut result = String::new();
let mut len = 0;
for g in s.graphemes(true) {
let g_len = grapheme_column_width(g, None);
if g_len + len > max_width {
break;
}
result.push_str(g);
len += g_len;
}
result
}
struct NerdFonts {}
impl mlua::UserData for NerdFonts {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(
mlua::MetaMethod::Index,
|_, _, key: String| -> mlua::Result<Option<String>> {
Ok(termwiz::nerdfonts::NERD_FONTS
.get(key.as_str())
.map(|c| c.to_string()))
},
);
}
}

View File

@ -16,6 +16,12 @@ libc = "0.2"
log = "0.4" log = "0.4"
env_logger = "0.9" env_logger = "0.9"
termwiz = { path = "../termwiz" } termwiz = { path = "../termwiz" }
battery = { path = "../lua-api-crates/battery" }
termwiz-funcs = { path = "../lua-api-crates/termwiz-funcs" }
logging = { path = "../lua-api-crates/logging" }
filesystem = { path = "../lua-api-crates/filesystem" }
ssh-funcs = { path = "../lua-api-crates/ssh-funcs" }
spawn-funcs = { path = "../lua-api-crates/spawn-funcs" }
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
winapi = "0.3" winapi = "0.3"

View File

@ -158,6 +158,19 @@ fn register_panic_hook() {
})); }));
} }
fn register_lua_modules() {
for func in [
battery::register,
termwiz_funcs::register,
logging::register,
filesystem::register,
ssh_funcs::register,
spawn_funcs::register,
] {
config::lua::add_context_setup_func(func);
}
}
pub fn bootstrap() { pub fn bootstrap() {
setup_logger(); setup_logger();
register_panic_hook(); register_panic_hook();
@ -169,6 +182,8 @@ pub fn bootstrap() {
fixup_appimage(); fixup_appimage();
register_lua_modules();
// Remove this env var to avoid weirdness with some vim configurations. // Remove this env var to avoid weirdness with some vim configurations.
// wezterm never sets WINDOWID and we don't want to inherit it from a // wezterm never sets WINDOWID and we don't want to inherit it from a
// parent process. // parent process.

6
lua-api-crates/README.md Normal file
View File

@ -0,0 +1,6 @@
The crates in this directory provide modules and functions
to wezterm's lua config file and interface.
They are registered into the lua config via env-bootstrap.
It is advantageous to build times to have multiple, smaller, crates.

View File

@ -0,0 +1,13 @@
[package]
name = "battery"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
starship-battery = "0.7"
config = { path = "../../config" }
wezterm-dynamic = { path = "../../wezterm-dynamic" }
luahelper = { path = "../../luahelper" }

View File

@ -0,0 +1,56 @@
use config::lua::get_or_create_module;
use config::lua::mlua::{self, Lua};
use luahelper::impl_lua_conversion_dynamic;
use wezterm_dynamic::{FromDynamic, ToDynamic};
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set("battery_info", lua.create_function(battery_info)?)?;
Ok(())
}
#[derive(FromDynamic, ToDynamic, Debug)]
struct BatteryInfo {
state_of_charge: f32,
vendor: String,
model: String,
state: String,
serial: String,
time_to_full: Option<f32>,
time_to_empty: Option<f32>,
}
impl_lua_conversion_dynamic!(BatteryInfo);
fn battery_info<'lua>(_: &'lua Lua, _: ()) -> mlua::Result<Vec<BatteryInfo>> {
use starship_battery::{Manager, State};
let manager = Manager::new().map_err(|e| mlua::Error::external(e))?;
let mut result = vec![];
for b in manager.batteries().map_err(|e| mlua::Error::external(e))? {
let bat = b.map_err(|e| mlua::Error::external(e))?;
result.push(BatteryInfo {
state_of_charge: bat.state_of_charge().value,
vendor: opt_string(bat.vendor()),
model: opt_string(bat.model()),
serial: opt_string(bat.serial_number()),
state: match bat.state() {
State::Charging => "Charging",
State::Discharging => "Discharging",
State::Empty => "Empty",
State::Full => "Full",
State::Unknown | _ => "Unknown",
}
.to_string(),
time_to_full: bat.time_to_full().map(|q| q.value),
time_to_empty: bat.time_to_empty().map(|q| q.value),
})
}
Ok(result)
}
fn opt_string(s: Option<&str>) -> String {
match s {
Some(s) => s,
None => "unknown",
}
.to_string()
}

View File

@ -0,0 +1,13 @@
[package]
name = "filesystem"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
filenamegen = "0.2"
anyhow = "1.0"
config = { path = "../../config" }
luahelper = { path = "../../luahelper" }
smol = "1.2"

View File

@ -0,0 +1,54 @@
use anyhow::anyhow;
use config::lua::get_or_create_module;
use config::lua::mlua::{self, Lua};
use smol::prelude::*;
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set("read_dir", lua.create_async_function(read_dir)?)?;
wezterm_mod.set("glob", lua.create_async_function(glob)?)?;
Ok(())
}
async fn read_dir<'lua>(_: &'lua Lua, path: String) -> mlua::Result<Vec<String>> {
let mut dir = smol::fs::read_dir(path)
.await
.map_err(|e| mlua::Error::external(e))?;
let mut entries = vec![];
for entry in dir.next().await {
let entry = entry.map_err(|e| mlua::Error::external(e))?;
if let Some(utf8) = entry.path().to_str() {
entries.push(utf8.to_string());
} else {
return Err(mlua::Error::external(anyhow!(
"path entry {} is not representable as utf8",
entry.path().display()
)));
}
}
Ok(entries)
}
async fn glob<'lua>(
_: &'lua Lua,
(pattern, path): (String, Option<String>),
) -> mlua::Result<Vec<String>> {
let entries = smol::unblock(move || {
let mut entries = vec![];
let glob = filenamegen::Glob::new(&pattern)?;
for path in glob.walk(path.as_ref().map(|s| s.as_str()).unwrap_or(".")) {
if let Some(utf8) = path.to_str() {
entries.push(utf8.to_string());
} else {
return Err(anyhow!(
"path entry {} is not representable as utf8",
path.display()
));
}
}
Ok(entries)
})
.await
.map_err(|e| mlua::Error::external(e))?;
Ok(entries)
}

View File

@ -0,0 +1,12 @@
[package]
name = "logging"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
config = { path = "../../config" }
log = "0.4"
luahelper = { path = "../../luahelper" }

View File

@ -0,0 +1,67 @@
use config::lua::get_or_create_module;
use config::lua::mlua::{Lua, Value, Variadic};
use luahelper::ValuePrinter;
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set(
"log_error",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::error!("lua: {}", output);
Ok(())
})?,
)?;
wezterm_mod.set(
"log_info",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::info!("lua: {}", output);
Ok(())
})?,
)?;
wezterm_mod.set(
"log_warn",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::warn!("lua: {}", output);
Ok(())
})?,
)?;
lua.globals().set(
"print",
lua.create_function(|_, args: Variadic<Value>| {
let output = print_helper(args);
log::info!("lua: {}", output);
Ok(())
})?,
)?;
Ok(())
}
fn print_helper(args: Variadic<Value>) -> String {
let mut output = String::new();
for (idx, item) in args.into_iter().enumerate() {
if idx > 0 {
output.push(' ');
}
match item {
Value::String(s) => match s.to_str() {
Ok(s) => output.push_str(s),
Err(_) => {
let item = String::from_utf8_lossy(s.as_bytes());
output.push_str(&item);
}
},
item @ _ => {
let item = format!("{:#?}", ValuePrinter(item));
output.push_str(&item);
}
}
}
output
}

View File

@ -0,0 +1,15 @@
[package]
name = "spawn-funcs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
config = { path = "../../config" }
wezterm-dynamic = { path = "../../wezterm-dynamic" }
luahelper = { path = "../../luahelper" }
smol = "1.2"
bstr = "0.2"
open = "2.0"

View File

@ -0,0 +1,71 @@
use bstr::BString;
use config::lua::get_or_create_module;
use config::lua::mlua::{self, Lua};
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set("open_with", lua.create_function(open_with)?)?;
wezterm_mod.set(
"run_child_process",
lua.create_async_function(run_child_process)?,
)?;
wezterm_mod.set(
"background_child_process",
lua.create_async_function(background_child_process)?,
)?;
Ok(())
}
fn open_with<'lua>(_: &'lua Lua, (url, app): (String, Option<String>)) -> mlua::Result<()> {
if let Some(app) = app {
open::with_in_background(url, app);
} else {
open::that_in_background(url);
}
Ok(())
}
async fn run_child_process<'lua>(
_: &'lua Lua,
args: Vec<String>,
) -> mlua::Result<(bool, BString, BString)> {
let mut cmd = smol::process::Command::new(&args[0]);
if args.len() > 1 {
cmd.args(&args[1..]);
}
#[cfg(windows)]
{
use smol::process::windows::CommandExt;
cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
}
let output = cmd.output().await.map_err(|e| mlua::Error::external(e))?;
Ok((
output.status.success(),
output.stdout.into(),
output.stderr.into(),
))
}
async fn background_child_process<'lua>(_: &'lua Lua, args: Vec<String>) -> mlua::Result<()> {
let mut cmd = smol::process::Command::new(&args[0]);
if args.len() > 1 {
cmd.args(&args[1..]);
}
#[cfg(windows)]
{
use smol::process::windows::CommandExt;
cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
}
cmd.stdin(smol::process::Stdio::null())
.spawn()
.map_err(|e| mlua::Error::external(e))?;
Ok(())
}

View File

@ -0,0 +1,12 @@
[package]
name = "ssh-funcs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
config = { path = "../../config" }
luahelper = { path = "../../luahelper" }
wezterm-ssh = { path = "../../wezterm-ssh" }

View File

@ -0,0 +1,39 @@
use config::lua::get_or_create_module;
use config::lua::mlua::{self, Lua, Variadic};
use std::collections::HashMap;
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set(
"enumerate_ssh_hosts",
lua.create_function(enumerate_ssh_hosts)?,
)?;
Ok(())
}
fn enumerate_ssh_hosts<'lua>(
lua: &'lua Lua,
config_files: Variadic<String>,
) -> mlua::Result<HashMap<String, wezterm_ssh::ConfigMap>> {
let mut config = wezterm_ssh::Config::new();
for file in config_files {
config.add_config_file(file);
}
config.add_default_config_files();
// Trigger a config reload if any of the parsed ssh config files change
let files: Variadic<String> = config
.loaded_config_files()
.into_iter()
.filter_map(|p| p.to_str().map(|s| s.to_string()))
.collect();
config::lua::add_to_config_reload_watch_list(lua, files)?;
let mut map = HashMap::new();
for host in config.enumerate_hosts() {
let host_config = config.for_host(&host);
map.insert(host, host_config);
}
Ok(map)
}

View File

@ -0,0 +1,16 @@
[package]
name = "termwiz-funcs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
config = { path = "../../config" }
terminfo = "0.7"
wezterm-dynamic = { path = "../../wezterm-dynamic" }
luahelper = { path = "../../luahelper" }
termwiz = { path = "../../termwiz", features=["use_serde"] }
unicode-segmentation = "1.8"
lazy_static = "1.4"

View File

@ -0,0 +1,261 @@
use config::lua::get_or_create_module;
use config::lua::mlua::{self, Lua, ToLua};
use luahelper::impl_lua_conversion_dynamic;
use termwiz::caps::{Capabilities, ColorLevel, ProbeHints};
use termwiz::cell::{grapheme_column_width, unicode_column_width, AttributeChange, CellAttributes};
use termwiz::color::{AnsiColor, ColorAttribute, ColorSpec, RgbColor};
use termwiz::input::Modifiers;
use termwiz::render::terminfo::TerminfoRenderer;
use termwiz::surface::change::Change;
use unicode_segmentation::UnicodeSegmentation;
use wezterm_dynamic::{FromDynamic, ToDynamic};
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set("nerdfonts", NerdFonts {})?;
wezterm_mod.set("format", lua.create_function(format)?)?;
wezterm_mod.set(
"column_width",
lua.create_function(|_, s: String| Ok(unicode_column_width(&s, None)))?,
)?;
wezterm_mod.set(
"pad_right",
lua.create_function(|_, (s, width): (String, usize)| Ok(pad_right(s, width)))?,
)?;
wezterm_mod.set(
"pad_left",
lua.create_function(|_, (s, width): (String, usize)| Ok(pad_left(s, width)))?,
)?;
wezterm_mod.set(
"truncate_right",
lua.create_function(|_, (s, max_width): (String, usize)| {
Ok(truncate_right(&s, max_width))
})?,
)?;
wezterm_mod.set(
"truncate_left",
lua.create_function(|_, (s, max_width): (String, usize)| Ok(truncate_left(&s, max_width)))?,
)?;
wezterm_mod.set("permute_any_mods", lua.create_function(permute_any_mods)?)?;
wezterm_mod.set(
"permute_any_or_no_mods",
lua.create_function(permute_any_or_no_mods)?,
)?;
Ok(())
}
struct NerdFonts {}
impl mlua::UserData for NerdFonts {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(
mlua::MetaMethod::Index,
|_, _, key: String| -> mlua::Result<Option<String>> {
Ok(termwiz::nerdfonts::NERD_FONTS
.get(key.as_str())
.map(|c| c.to_string()))
},
);
}
}
#[derive(Debug, FromDynamic, ToDynamic, Clone, PartialEq, Eq)]
pub enum FormatColor {
AnsiColor(AnsiColor),
Color(String),
Default,
}
impl FormatColor {
fn to_attr(self) -> ColorAttribute {
let spec: ColorSpec = self.into();
let attr: ColorAttribute = spec.into();
attr
}
}
impl Into<ColorSpec> for FormatColor {
fn into(self) -> ColorSpec {
match self {
FormatColor::AnsiColor(c) => c.into(),
FormatColor::Color(s) => {
let rgb = RgbColor::from_named_or_rgb_string(&s)
.unwrap_or(RgbColor::new_8bpc(0xff, 0xff, 0xff));
rgb.into()
}
FormatColor::Default => ColorSpec::Default,
}
}
}
#[derive(Debug, FromDynamic, ToDynamic, Clone, PartialEq, Eq)]
pub enum FormatItem {
Foreground(FormatColor),
Background(FormatColor),
Attribute(AttributeChange),
Text(String),
}
impl_lua_conversion_dynamic!(FormatItem);
impl Into<Change> for FormatItem {
fn into(self) -> Change {
match self {
Self::Attribute(change) => change.into(),
Self::Text(t) => t.into(),
Self::Foreground(c) => AttributeChange::Foreground(c.to_attr()).into(),
Self::Background(c) => AttributeChange::Background(c.to_attr()).into(),
}
}
}
struct FormatTarget {
target: Vec<u8>,
}
impl std::io::Write for FormatTarget {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
std::io::Write::write(&mut self.target, buf)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl termwiz::render::RenderTty for FormatTarget {
fn get_size_in_cells(&mut self) -> termwiz::Result<(usize, usize)> {
Ok((80, 24))
}
}
pub fn format_as_escapes(items: Vec<FormatItem>) -> anyhow::Result<String> {
let mut changes: Vec<Change> = items.into_iter().map(Into::into).collect();
changes.push(Change::AllAttributes(CellAttributes::default()).into());
let mut renderer = new_wezterm_terminfo_renderer();
let mut target = FormatTarget { target: vec![] };
renderer.render_to(&changes, &mut target)?;
Ok(String::from_utf8(target.target)?)
}
fn format<'lua>(_: &'lua Lua, items: Vec<FormatItem>) -> mlua::Result<String> {
format_as_escapes(items).map_err(|e| mlua::Error::external(e))
}
pub fn pad_right(mut result: String, width: usize) -> String {
let mut len = unicode_column_width(&result, None);
while len < width {
result.push(' ');
len += 1;
}
result
}
pub fn pad_left(mut result: String, width: usize) -> String {
let mut len = unicode_column_width(&result, None);
while len < width {
result.insert(0, ' ');
len += 1;
}
result
}
pub fn truncate_left(s: &str, max_width: usize) -> String {
let mut result = vec![];
let mut len = 0;
for g in s.graphemes(true).rev() {
let g_len = grapheme_column_width(g, None);
if g_len + len > max_width {
break;
}
result.push(g);
len += g_len;
}
result.reverse();
result.join("")
}
pub fn truncate_right(s: &str, max_width: usize) -> String {
let mut result = String::new();
let mut len = 0;
for g in s.graphemes(true) {
let g_len = grapheme_column_width(g, None);
if g_len + len > max_width {
break;
}
result.push_str(g);
len += g_len;
}
result
}
fn permute_mods<'lua>(
lua: &'lua Lua,
item: mlua::Table,
allow_none: bool,
) -> mlua::Result<Vec<mlua::Value<'lua>>> {
let mut result = vec![];
for ctrl in &[Modifiers::NONE, Modifiers::CTRL] {
for shift in &[Modifiers::NONE, Modifiers::SHIFT] {
for alt in &[Modifiers::NONE, Modifiers::ALT] {
for sup in &[Modifiers::NONE, Modifiers::SUPER] {
let flags = *ctrl | *shift | *alt | *sup;
if flags == Modifiers::NONE && !allow_none {
continue;
}
let new_item = lua.create_table()?;
for pair in item.clone().pairs::<mlua::Value, mlua::Value>() {
let (k, v) = pair?;
new_item.set(k, v)?;
}
new_item.set("mods", format!("{:?}", flags))?;
result.push(new_item.to_lua(lua)?);
}
}
}
}
Ok(result)
}
fn permute_any_mods<'lua>(
lua: &'lua Lua,
item: mlua::Table,
) -> mlua::Result<Vec<mlua::Value<'lua>>> {
permute_mods(lua, item, false)
}
fn permute_any_or_no_mods<'lua>(
lua: &'lua Lua,
item: mlua::Table,
) -> mlua::Result<Vec<mlua::Value<'lua>>> {
permute_mods(lua, item, true)
}
lazy_static::lazy_static! {
static ref CAPS: Capabilities = {
let data = include_bytes!("../../../termwiz/data/xterm-256color");
let db = terminfo::Database::from_buffer(&data[..]).unwrap();
Capabilities::new_with_hints(
ProbeHints::new_from_env()
.term(Some("xterm-256color".into()))
.terminfo_db(Some(db))
.color_level(Some(ColorLevel::TrueColor))
.colorterm(None)
.colorterm_bce(None)
.term_program(Some("WezTerm".into()))
.term_program_version(Some(config::wezterm_version().into())),
)
.expect("cannot fail to make internal Capabilities")
};
}
pub fn new_wezterm_terminfo_renderer() -> TerminfoRenderer {
TerminfoRenderer::new(CAPS.clone())
}

View File

@ -36,6 +36,7 @@ shell-words = "1.1"
smol = "1.2" smol = "1.2"
terminfo = "0.7" terminfo = "0.7"
termwiz = { path = "../termwiz" } termwiz = { path = "../termwiz" }
termwiz-funcs = { path = "../lua-api-crates/termwiz-funcs" }
textwrap = "0.15" textwrap = "0.15"
thiserror = "1.0" thiserror = "1.0"
unicode-segmentation = "1.8" unicode-segmentation = "1.8"

View File

@ -409,7 +409,7 @@ fn connect_ssh_session(
} }
} }
let renderer = config::lua::new_wezterm_terminfo_renderer(); let renderer = termwiz_funcs::new_wezterm_terminfo_renderer();
let mut shim = TerminalShim { let mut shim = TerminalShim {
stdout: &mut StdoutShim { stdout: &mut StdoutShim {
stdout: stdout_write, stdout: stdout_write,

View File

@ -409,7 +409,7 @@ pub fn allocate(size: PtySize) -> (TermWizTerminal, Rc<dyn Pane>) {
let (input_tx, input_rx) = channel(); let (input_tx, input_rx) = channel();
let renderer = config::lua::new_wezterm_terminfo_renderer(); let renderer = termwiz_funcs::new_wezterm_terminfo_renderer();
let tw_term = TermWizTerminal { let tw_term = TermWizTerminal {
render_tx: TermWizTerminalRenderTty { render_tx: TermWizTerminalRenderTty {
@ -457,7 +457,7 @@ pub async fn run<
let (input_tx, input_rx) = channel(); let (input_tx, input_rx) = channel();
let should_close_window = window_id.is_none(); let should_close_window = window_id.is_none();
let renderer = config::lua::new_wezterm_terminfo_renderer(); let renderer = termwiz_funcs::new_wezterm_terminfo_renderer();
let tw_term = TermWizTerminal { let tw_term = TermWizTerminal {
render_tx: TermWizTerminalRenderTty { render_tx: TermWizTerminalRenderTty {

View File

@ -60,6 +60,7 @@ structopt = "0.3"
tabout = { path = "../tabout" } tabout = { path = "../tabout" }
terminfo = "0.7" terminfo = "0.7"
termwiz = { path = "../termwiz" } termwiz = { path = "../termwiz" }
termwiz-funcs = { path = "../lua-api-crates/termwiz-funcs" }
textwrap = "0.15" textwrap = "0.15"
thiserror = "1.0" thiserror = "1.0"
tiny-skia = "0.6" tiny-skia = "0.6"

View File

@ -9,7 +9,6 @@ use crate::inputmap::InputMap;
use crate::termwindow::TermWindowNotif; use crate::termwindow::TermWindowNotif;
use config::configuration; use config::configuration;
use config::keyassignment::{KeyAssignment, SpawnCommand, SpawnTabDomain}; use config::keyassignment::{KeyAssignment, SpawnCommand, SpawnTabDomain};
use config::lua::truncate_right;
use fuzzy_matcher::skim::SkimMatcherV2; use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher; use fuzzy_matcher::FuzzyMatcher;
use mux::domain::{DomainId, DomainState}; use mux::domain::{DomainId, DomainState};
@ -24,6 +23,7 @@ use termwiz::color::ColorAttribute;
use termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers, MouseButtons, MouseEvent}; use termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers, MouseButtons, MouseEvent};
use termwiz::surface::{Change, Position}; use termwiz::surface::{Change, Position};
use termwiz::terminal::Terminal; use termwiz::terminal::Terminal;
use termwiz_funcs::truncate_right;
use window::WindowOps; use window::WindowOps;
pub use config::keyassignment::LauncherFlags; pub use config::keyassignment::LauncherFlags;

View File

@ -1,5 +1,4 @@
use crate::termwindow::{PaneInformation, TabInformation, UIItem, UIItemType}; use crate::termwindow::{PaneInformation, TabInformation, UIItem, UIItemType};
use config::lua::{format_as_escapes, FormatItem};
use config::{ConfigHandle, TabBarColors}; use config::{ConfigHandle, TabBarColors};
use mlua::FromLua; use mlua::FromLua;
use termwiz::cell::{unicode_column_width, Cell, CellAttributes}; use termwiz::cell::{unicode_column_width, Cell, CellAttributes};
@ -8,6 +7,7 @@ use termwiz::escape::csi::Sgr;
use termwiz::escape::parser::Parser; use termwiz::escape::parser::Parser;
use termwiz::escape::{Action, ControlCode, CSI}; use termwiz::escape::{Action, ControlCode, CSI};
use termwiz::surface::SEQ_ZERO; use termwiz::surface::SEQ_ZERO;
use termwiz_funcs::{format_as_escapes, FormatItem};
use wezterm_term::Line; use wezterm_term::Line;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]