mirror of
https://github.com/wez/wezterm.git
synced 2024-08-16 17:50:28 +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:
parent
42a7c1d481
commit
2f14d640e8
84
Cargo.lock
generated
84
Cargo.lock
generated
@ -334,6 +334,17 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
name = "base91"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "battery"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"config",
|
||||
"luahelper",
|
||||
"starship-battery",
|
||||
"wezterm-dynamic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "benchmarking"
|
||||
version = "0.4.11"
|
||||
@ -643,13 +654,11 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"bstr 0.2.17",
|
||||
"chrono",
|
||||
"colorgrad",
|
||||
"dirs-next",
|
||||
"enum-display-derive",
|
||||
"env_logger",
|
||||
"filenamegen",
|
||||
"hostname",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@ -657,7 +666,6 @@ dependencies = [
|
||||
"luahelper",
|
||||
"mlua",
|
||||
"notify",
|
||||
"open",
|
||||
"ordered-float",
|
||||
"portable-pty",
|
||||
"promise",
|
||||
@ -665,16 +673,12 @@ dependencies = [
|
||||
"serde_json",
|
||||
"shlex",
|
||||
"smol",
|
||||
"starship-battery",
|
||||
"terminfo",
|
||||
"termwiz",
|
||||
"toml",
|
||||
"umask",
|
||||
"unicode-segmentation",
|
||||
"wezterm-bidi",
|
||||
"wezterm-dynamic",
|
||||
"wezterm-input-types",
|
||||
"wezterm-ssh",
|
||||
"wezterm-term",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
@ -1070,16 +1074,22 @@ name = "env-bootstrap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"battery",
|
||||
"chrono",
|
||||
"cocoa",
|
||||
"config",
|
||||
"dirs-next",
|
||||
"env_logger",
|
||||
"filesystem",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"logging",
|
||||
"objc",
|
||||
"spawn-funcs",
|
||||
"ssh-funcs",
|
||||
"termwiz",
|
||||
"termwiz-funcs",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@ -1173,6 +1183,17 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filesystem"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"config",
|
||||
"filenamegen",
|
||||
"luahelper",
|
||||
"smol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.16"
|
||||
@ -1996,6 +2017,16 @@ dependencies = [
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logging"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"config",
|
||||
"log",
|
||||
"luahelper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.7.5"
|
||||
@ -2280,6 +2311,7 @@ dependencies = [
|
||||
"smol",
|
||||
"terminfo",
|
||||
"termwiz",
|
||||
"termwiz-funcs",
|
||||
"textwrap 0.15.0",
|
||||
"thiserror",
|
||||
"unicode-segmentation",
|
||||
@ -3840,6 +3872,19 @@ dependencies = [
|
||||
"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]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
@ -3855,6 +3900,16 @@ dependencies = [
|
||||
"lock_api 0.4.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh-funcs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"config",
|
||||
"luahelper",
|
||||
"wezterm-ssh",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
version = "0.9.3"
|
||||
@ -4088,6 +4143,20 @@ dependencies = [
|
||||
"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]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
@ -4806,6 +4875,7 @@ dependencies = [
|
||||
"tabout",
|
||||
"terminfo",
|
||||
"termwiz",
|
||||
"termwiz-funcs",
|
||||
"textwrap 0.15.0",
|
||||
"thiserror",
|
||||
"tiny-skia",
|
||||
|
@ -12,14 +12,11 @@ env_logger = "0.9"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
starship-battery = "0.7"
|
||||
bitflags = "1.3"
|
||||
bstr = "0.2"
|
||||
chrono = {version="0.4", features=["unstable-locales"]}
|
||||
colorgrad = "0.5"
|
||||
dirs-next = "2.0"
|
||||
enum-display-derive = "0.1"
|
||||
filenamegen = "0.2"
|
||||
hostname = "0.3"
|
||||
lazy_static = "1.4"
|
||||
libc = "0.2"
|
||||
@ -28,7 +25,6 @@ luahelper = { path = "../luahelper" }
|
||||
mlua = {version="0.8.0-beta.4", features=["vendored", "lua54", "async", "send"]}
|
||||
# file change notification
|
||||
notify = "4.0"
|
||||
open = "2.0"
|
||||
ordered-float = { version = "3.0", features = ["serde"] }
|
||||
portable-pty = { path = "../pty", features = ["serde_support"]}
|
||||
promise = { path = "../promise" }
|
||||
@ -36,15 +32,12 @@ serde = {version="1.0", features = ["rc", "derive"]}
|
||||
serde_json = "1.0"
|
||||
shlex = "1.1"
|
||||
smol = "1.2"
|
||||
terminfo = "0.7"
|
||||
termwiz = { path = "../termwiz", features=["use_serde"] }
|
||||
toml = "0.5"
|
||||
umask = { path = "../umask" }
|
||||
unicode-segmentation = "1.8"
|
||||
wezterm-dynamic = { path = "../wezterm-dynamic" }
|
||||
wezterm-bidi = { path = "../bidi" }
|
||||
wezterm-input-types = { path = "../wezterm-input-types" }
|
||||
wezterm-ssh = { path = "../wezterm-ssh" }
|
||||
wezterm-term = { path = "../term", features=["use_serde"] }
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::lua::{format_as_escapes, FormatItem};
|
||||
use crate::*;
|
||||
use luahelper::impl_lua_conversion_dynamic;
|
||||
use std::convert::TryFrom;
|
||||
@ -297,7 +296,7 @@ impl Default for TabBarStyle {
|
||||
}
|
||||
|
||||
fn default_new_tab() -> String {
|
||||
format_as_escapes(vec![FormatItem::Text(" + ".to_string())]).unwrap()
|
||||
" + ".to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
|
||||
|
@ -4,23 +4,50 @@ use crate::{
|
||||
TextStyle,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use bstr::BString;
|
||||
pub use luahelper::*;
|
||||
use mlua::{FromLua, Lua, Table, ToLua, ToLuaMulti, Value, Variadic};
|
||||
use luahelper::from_lua_value_dynamic;
|
||||
use mlua::{FromLua, Lua, Table, ToLuaMulti, Value, Variadic};
|
||||
use ordered_float::NotNan;
|
||||
use smol::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::path::Path;
|
||||
use termwiz::cell::{grapheme_column_width, unicode_column_width, AttributeChange, CellAttributes};
|
||||
use termwiz::color::{AnsiColor, ColorAttribute, ColorSpec, RgbColor};
|
||||
use termwiz::input::Modifiers;
|
||||
use termwiz::surface::change::Change;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use std::sync::Mutex;
|
||||
use wezterm_dynamic::{FromDynamic, ToDynamic};
|
||||
|
||||
pub use mlua;
|
||||
|
||||
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.
|
||||
/// The path to the directory containing the configuration is
|
||||
/// 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();
|
||||
// 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_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("version", crate::wezterm_version())?;
|
||||
wezterm_mod.set("nerdfonts", NerdFonts {})?;
|
||||
wezterm_mod.set("home_dir", crate::HOME_DIR.to_str())?;
|
||||
wezterm_mod.set(
|
||||
"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()))?,
|
||||
)?;
|
||||
|
||||
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_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)?)?;
|
||||
lua.set_named_registry_value(LUA_REGISTRY_USER_CALLBACK_COUNT, 0)?;
|
||||
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("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("emit", lua.create_async_function(emit_event)?)?;
|
||||
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("battery_info", lua.create_function(battery_info)?)?;
|
||||
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(";"))?;
|
||||
}
|
||||
|
||||
let loaded: Table = package.get("loaded")?;
|
||||
loaded.set("wezterm", wezterm_mod)?;
|
||||
for func in SETUP_FUNCS.lock().unwrap().iter() {
|
||||
func(&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> {
|
||||
use chrono::prelude::*;
|
||||
let local: DateTime<Local> = Local::now();
|
||||
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<()> {
|
||||
let duration = std::time::Duration::from_millis(milliseconds);
|
||||
smol::Timer::after(duration).await;
|
||||
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.
|
||||
/// Errors may occur while retrieving the hostname from the system,
|
||||
/// 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));
|
||||
}
|
||||
|
||||
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>> {
|
||||
Ok(text
|
||||
.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))
|
||||
}
|
||||
|
||||
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>(
|
||||
_lua: &'lua Lua,
|
||||
(gradient, num_colors): (Gradient, usize),
|
||||
@ -946,7 +572,7 @@ fn gradient_colors<'lua>(
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn add_to_config_reload_watch_list<'lua>(
|
||||
pub fn add_to_config_reload_watch_list<'lua>(
|
||||
lua: &'lua Lua,
|
||||
args: Variadic<String>,
|
||||
) -> mlua::Result<()> {
|
||||
@ -956,33 +582,6 @@ fn add_to_config_reload_watch_list<'lua>(
|
||||
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)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@ -1047,7 +646,7 @@ mod test {
|
||||
local wezterm = require 'wezterm';
|
||||
|
||||
wezterm.on('foo', function (n)
|
||||
wezterm.log_error("lua hook recording " .. n);
|
||||
print("lua hook recording " .. n);
|
||||
end);
|
||||
|
||||
-- one of the foo handlers returns false, so the emit
|
||||
@ -1056,7 +655,7 @@ end);
|
||||
assert(wezterm.emit('foo', 2) == false)
|
||||
|
||||
wezterm.on('bar', function (n, str)
|
||||
wezterm.log_error("bar says " .. n .. " " .. str)
|
||||
print("bar says " .. n .. " " .. str)
|
||||
end);
|
||||
|
||||
-- None of the bar handlers return anything, so the
|
||||
@ -1073,68 +672,3 @@ assert(wezterm.emit('bar', 42, 'woot') == true)
|
||||
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()))
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,12 @@ libc = "0.2"
|
||||
log = "0.4"
|
||||
env_logger = "0.9"
|
||||
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]
|
||||
winapi = "0.3"
|
||||
|
@ -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() {
|
||||
setup_logger();
|
||||
register_panic_hook();
|
||||
@ -169,6 +182,8 @@ pub fn bootstrap() {
|
||||
|
||||
fixup_appimage();
|
||||
|
||||
register_lua_modules();
|
||||
|
||||
// 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
|
||||
// parent process.
|
||||
|
6
lua-api-crates/README.md
Normal file
6
lua-api-crates/README.md
Normal 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.
|
13
lua-api-crates/battery/Cargo.toml
Normal file
13
lua-api-crates/battery/Cargo.toml
Normal 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" }
|
56
lua-api-crates/battery/src/lib.rs
Normal file
56
lua-api-crates/battery/src/lib.rs
Normal 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()
|
||||
}
|
13
lua-api-crates/filesystem/Cargo.toml
Normal file
13
lua-api-crates/filesystem/Cargo.toml
Normal 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"
|
54
lua-api-crates/filesystem/src/lib.rs
Normal file
54
lua-api-crates/filesystem/src/lib.rs
Normal 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)
|
||||
}
|
12
lua-api-crates/logging/Cargo.toml
Normal file
12
lua-api-crates/logging/Cargo.toml
Normal 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" }
|
67
lua-api-crates/logging/src/lib.rs
Normal file
67
lua-api-crates/logging/src/lib.rs
Normal 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
|
||||
}
|
15
lua-api-crates/spawn-funcs/Cargo.toml
Normal file
15
lua-api-crates/spawn-funcs/Cargo.toml
Normal 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"
|
71
lua-api-crates/spawn-funcs/src/lib.rs
Normal file
71
lua-api-crates/spawn-funcs/src/lib.rs
Normal 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(())
|
||||
}
|
12
lua-api-crates/ssh-funcs/Cargo.toml
Normal file
12
lua-api-crates/ssh-funcs/Cargo.toml
Normal 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" }
|
39
lua-api-crates/ssh-funcs/src/lib.rs
Normal file
39
lua-api-crates/ssh-funcs/src/lib.rs
Normal 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)
|
||||
}
|
16
lua-api-crates/termwiz-funcs/Cargo.toml
Normal file
16
lua-api-crates/termwiz-funcs/Cargo.toml
Normal 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"
|
261
lua-api-crates/termwiz-funcs/src/lib.rs
Normal file
261
lua-api-crates/termwiz-funcs/src/lib.rs
Normal 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())
|
||||
}
|
@ -36,6 +36,7 @@ shell-words = "1.1"
|
||||
smol = "1.2"
|
||||
terminfo = "0.7"
|
||||
termwiz = { path = "../termwiz" }
|
||||
termwiz-funcs = { path = "../lua-api-crates/termwiz-funcs" }
|
||||
textwrap = "0.15"
|
||||
thiserror = "1.0"
|
||||
unicode-segmentation = "1.8"
|
||||
|
@ -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 {
|
||||
stdout: &mut StdoutShim {
|
||||
stdout: stdout_write,
|
||||
|
@ -409,7 +409,7 @@ pub fn allocate(size: PtySize) -> (TermWizTerminal, Rc<dyn Pane>) {
|
||||
|
||||
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 {
|
||||
render_tx: TermWizTerminalRenderTty {
|
||||
@ -457,7 +457,7 @@ pub async fn run<
|
||||
let (input_tx, input_rx) = channel();
|
||||
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 {
|
||||
render_tx: TermWizTerminalRenderTty {
|
||||
|
@ -60,6 +60,7 @@ structopt = "0.3"
|
||||
tabout = { path = "../tabout" }
|
||||
terminfo = "0.7"
|
||||
termwiz = { path = "../termwiz" }
|
||||
termwiz-funcs = { path = "../lua-api-crates/termwiz-funcs" }
|
||||
textwrap = "0.15"
|
||||
thiserror = "1.0"
|
||||
tiny-skia = "0.6"
|
||||
|
@ -9,7 +9,6 @@ use crate::inputmap::InputMap;
|
||||
use crate::termwindow::TermWindowNotif;
|
||||
use config::configuration;
|
||||
use config::keyassignment::{KeyAssignment, SpawnCommand, SpawnTabDomain};
|
||||
use config::lua::truncate_right;
|
||||
use fuzzy_matcher::skim::SkimMatcherV2;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
use mux::domain::{DomainId, DomainState};
|
||||
@ -24,6 +23,7 @@ use termwiz::color::ColorAttribute;
|
||||
use termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers, MouseButtons, MouseEvent};
|
||||
use termwiz::surface::{Change, Position};
|
||||
use termwiz::terminal::Terminal;
|
||||
use termwiz_funcs::truncate_right;
|
||||
use window::WindowOps;
|
||||
|
||||
pub use config::keyassignment::LauncherFlags;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::termwindow::{PaneInformation, TabInformation, UIItem, UIItemType};
|
||||
use config::lua::{format_as_escapes, FormatItem};
|
||||
use config::{ConfigHandle, TabBarColors};
|
||||
use mlua::FromLua;
|
||||
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::{Action, ControlCode, CSI};
|
||||
use termwiz::surface::SEQ_ZERO;
|
||||
use termwiz_funcs::{format_as_escapes, FormatItem};
|
||||
use wezterm_term::Line;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
Loading…
Reference in New Issue
Block a user