1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-20 11:17:15 +03:00

lua-api-crates/mux: refactor

split out multiple multiples
This commit is contained in:
Wez Furlong 2022-09-11 06:44:18 -07:00
parent 295677ed52
commit a99920da0b
4 changed files with 302 additions and 289 deletions

View File

@ -14,6 +14,14 @@ use std::rc::Rc;
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_term::TerminalSize;
mod pane;
mod tab;
mod window;
pub use pane::MuxPane;
pub use tab::MuxTab;
pub use window::MuxWindow;
fn get_mux() -> mlua::Result<Rc<Mux>> {
Mux::get()
.ok_or_else(|| mlua::Error::external("cannot get Mux: not running on the mux thread?"))
@ -144,66 +152,6 @@ impl Default for HandySplitDirection {
}
}
#[derive(Debug, Default, FromDynamic, ToDynamic)]
struct SplitPane {
#[dynamic(flatten)]
cmd_builder: CommandBuilderFrag,
#[dynamic(default = "spawn_tab_default_domain")]
domain: SpawnTabDomain,
#[dynamic(default)]
direction: HandySplitDirection,
#[dynamic(default)]
top_level: bool,
#[dynamic(default = "default_split_size")]
size: f32,
}
impl_lua_conversion_dynamic!(SplitPane);
fn default_split_size() -> f32 {
0.5
}
impl SplitPane {
async fn run(self, pane: MuxPane) -> mlua::Result<MuxPane> {
let (command, command_dir) = self.cmd_builder.to_command_builder();
let source = SplitSource::Spawn {
command,
command_dir,
};
let size = if self.size == 0.0 {
SplitSize::Percent(50)
} else if self.size < 1.0 {
SplitSize::Percent((self.size * 100.).floor() as u8)
} else {
SplitSize::Cells(self.size as usize)
};
let direction = match self.direction {
HandySplitDirection::Right | HandySplitDirection::Left => SplitDirection::Horizontal,
HandySplitDirection::Top | HandySplitDirection::Bottom => SplitDirection::Vertical,
};
let request = SplitRequest {
direction,
target_is_second: match self.direction {
HandySplitDirection::Top | HandySplitDirection::Left => false,
HandySplitDirection::Bottom | HandySplitDirection::Right => true,
},
top_level: self.top_level,
size,
};
let mux = get_mux()?;
let (pane, _size) = mux
.split_pane(pane.0, request, source, self.domain)
.await
.map_err(|e| mlua::Error::external(format!("{:#?}", e)))?;
Ok(MuxPane(pane.pane_id()))
}
}
#[derive(Debug, FromDynamic, ToDynamic)]
struct SpawnWindow {
#[dynamic(default = "spawn_tab_default_domain")]
@ -255,25 +203,6 @@ impl SpawnWindow {
}
}
#[derive(Clone, Copy, Debug)]
pub struct MuxWindow(pub WindowId);
#[derive(Clone, Copy, Debug)]
pub struct MuxTab(pub TabId);
#[derive(Clone, Copy, Debug)]
pub struct MuxPane(pub PaneId);
impl MuxWindow {
fn resolve<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<Ref<'a, Window>> {
mux.get_window(self.0)
.ok_or_else(|| mlua::Error::external(format!("window id {} not found in mux", self.0)))
}
fn resolve_mut<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<RefMut<'a, Window>> {
mux.get_window_mut(self.0)
.ok_or_else(|| mlua::Error::external(format!("window id {} not found in mux", self.0)))
}
}
#[derive(Debug, FromDynamic, ToDynamic)]
struct SpawnTab {
#[dynamic(default)]
@ -331,136 +260,6 @@ struct MuxTabInfo {
}
impl_lua_conversion_dynamic!(MuxTabInfo);
impl UserData for MuxWindow {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, _: ()| {
Ok(format!(
"MuxWindow(mux_window_id:{}, pid:{})",
this.0,
unsafe { libc::getpid() }
))
});
methods.add_method("window_id", |_, this, _: ()| Ok(this.0));
methods.add_async_method("gui_window", |lua, this, _: ()| async move {
// Weakly bound to the gui module; mux cannot hard-depend
// on wezterm-gui, but we can runtime resolve the appropriate module
let wezterm_mod = get_or_create_module(lua, "wezterm")
.map_err(|err| mlua::Error::external(format!("{err:#}")))?;
let gui: mlua::Table = wezterm_mod.get("gui")?;
let func: mlua::Function = gui.get("gui_window_for_mux_window")?;
func.call_async::<_, mlua::Value>(this.0).await
});
methods.add_method("get_workspace", |_, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
Ok(window.get_workspace().to_string())
});
methods.add_method("set_workspace", |_, this, new_name: String| {
let mux = get_mux()?;
let mut window = this.resolve_mut(&mux)?;
Ok(window.set_workspace(&new_name))
});
methods.add_async_method("spawn_tab", |_, this, spawn: SpawnTab| async move {
spawn.spawn(this).await
});
methods.add_method("get_title", |_, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
Ok(window.get_title().to_string())
});
methods.add_method("set_title", |_, this, title: String| {
let mux = get_mux()?;
let mut window = this.resolve_mut(&mux)?;
Ok(window.set_title(&title))
});
methods.add_method("tabs", |_, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
Ok(window
.iter()
.map(|tab| MuxTab(tab.tab_id()))
.collect::<Vec<MuxTab>>())
});
methods.add_method("tabs_with_info", |lua, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
let result = lua.create_table()?;
let active_idx = window.get_active_idx();
for (index, tab) in window.iter().enumerate() {
let info = MuxTabInfo {
index,
is_active: index == active_idx,
};
let info = luahelper::dynamic_to_lua_value(lua, info.to_dynamic())?;
match &info {
LuaValue::Table(t) => {
t.set("tab", MuxTab(tab.tab_id()))?;
}
_ => {}
}
result.set(index + 1, info)?;
}
Ok(result)
});
}
}
impl MuxPane {
fn resolve<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<Rc<dyn Pane>> {
mux.get_pane(self.0)
.ok_or_else(|| mlua::Error::external(format!("pane id {} not found in mux", self.0)))
}
}
impl UserData for MuxPane {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, _: ()| {
Ok(format!("MuxPane(pane_id:{}, pid:{})", this.0, unsafe {
libc::getpid()
}))
});
methods.add_method("pane_id", |_, this, _: ()| Ok(this.0));
methods.add_async_method("split", |_, this, args: Option<SplitPane>| async move {
let args = args.unwrap_or_default();
args.run(this).await
});
methods.add_method("send_paste", |_, this, text: String| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
pane.send_paste(&text)
.map_err(|e| mlua::Error::external(format!("{:#}", e)))?;
Ok(())
});
methods.add_method("send_text", |_, this, text: String| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
pane.writer()
.write_all(text.as_bytes())
.map_err(|e| mlua::Error::external(format!("{:#}", e)))?;
Ok(())
});
methods.add_method("window", |_, this, _: ()| {
let mux = get_mux()?;
Ok(mux
.resolve_pane_id(this.0)
.map(|(_domain_id, window_id, _tab_id)| MuxWindow(window_id)))
});
methods.add_method("tab", |_, this, _: ()| {
let mux = get_mux()?;
Ok(mux
.resolve_pane_id(this.0)
.map(|(_domain_id, _window_id, tab_id)| MuxTab(tab_id)))
});
}
}
impl MuxTab {
fn resolve<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<Rc<Tab>> {
mux.get_tab(self.0)
.ok_or_else(|| mlua::Error::external(format!("tab id {} not found in mux", self.0)))
}
}
#[derive(Clone, FromDynamic, ToDynamic)]
struct MuxPaneInfo {
/// The topological pane index that can be used to reference this pane
@ -483,83 +282,3 @@ struct MuxPaneInfo {
pub pixel_height: usize,
}
impl_lua_conversion_dynamic!(MuxPaneInfo);
impl UserData for MuxTab {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, _: ()| {
Ok(format!("MuxTab(tab_id:{}, pid:{})", this.0, unsafe {
libc::getpid()
}))
});
methods.add_method("tab_id", |_, this, _: ()| Ok(this.0));
methods.add_method("window", |_, this, _: ()| {
let mux = get_mux()?;
for window_id in mux.iter_windows() {
if let Some(window) = mux.get_window(window_id) {
for tab in window.iter() {
if tab.tab_id() == this.0 {
return Ok(Some(MuxWindow(window_id)));
}
}
}
}
Ok(None)
});
methods.add_method("get_title", |_, this, _: ()| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
Ok(tab.get_title().to_string())
});
methods.add_method("set_title", |_, this, title: String| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
Ok(tab.set_title(&title))
});
methods.add_method("panes", |_, this, _: ()| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
Ok(tab
.iter_panes()
.into_iter()
.map(|info| MuxPane(info.pane.pane_id()))
.collect::<Vec<MuxPane>>())
});
methods.add_method("set_zoomed", |_, this, zoomed: bool| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
let was_zoomed = tab.set_zoomed(zoomed);
Ok(was_zoomed)
});
methods.add_method("panes_with_info", |lua, this, _: ()| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
let result = lua.create_table()?;
for (idx, pos) in tab.iter_panes().into_iter().enumerate() {
let info = MuxPaneInfo {
index: pos.index,
is_active: pos.is_active,
is_zoomed: pos.is_zoomed,
left: pos.left,
top: pos.top,
width: pos.width,
pixel_width: pos.pixel_width,
height: pos.height,
pixel_height: pos.pixel_height,
};
let info = luahelper::dynamic_to_lua_value(lua, info.to_dynamic())?;
match &info {
LuaValue::Table(t) => {
t.set("pane", MuxPane(pos.pane.pane_id()))?;
}
_ => {}
}
result.set(idx + 1, info)?;
}
Ok(result)
});
}
}

View File

@ -0,0 +1,113 @@
use super::*;
#[derive(Clone, Copy, Debug)]
pub struct MuxPane(pub PaneId);
impl MuxPane {
pub fn resolve<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<Rc<dyn Pane>> {
mux.get_pane(self.0)
.ok_or_else(|| mlua::Error::external(format!("pane id {} not found in mux", self.0)))
}
}
impl UserData for MuxPane {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, _: ()| {
Ok(format!("MuxPane(pane_id:{}, pid:{})", this.0, unsafe {
libc::getpid()
}))
});
methods.add_method("pane_id", |_, this, _: ()| Ok(this.0));
methods.add_async_method("split", |_, this, args: Option<SplitPane>| async move {
let args = args.unwrap_or_default();
args.run(this).await
});
methods.add_method("send_paste", |_, this, text: String| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
pane.send_paste(&text)
.map_err(|e| mlua::Error::external(format!("{:#}", e)))?;
Ok(())
});
methods.add_method("send_text", |_, this, text: String| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
pane.writer()
.write_all(text.as_bytes())
.map_err(|e| mlua::Error::external(format!("{:#}", e)))?;
Ok(())
});
methods.add_method("window", |_, this, _: ()| {
let mux = get_mux()?;
Ok(mux
.resolve_pane_id(this.0)
.map(|(_domain_id, window_id, _tab_id)| MuxWindow(window_id)))
});
methods.add_method("tab", |_, this, _: ()| {
let mux = get_mux()?;
Ok(mux
.resolve_pane_id(this.0)
.map(|(_domain_id, _window_id, tab_id)| MuxTab(tab_id)))
});
}
}
#[derive(Debug, Default, FromDynamic, ToDynamic)]
struct SplitPane {
#[dynamic(flatten)]
cmd_builder: CommandBuilderFrag,
#[dynamic(default = "spawn_tab_default_domain")]
domain: SpawnTabDomain,
#[dynamic(default)]
direction: HandySplitDirection,
#[dynamic(default)]
top_level: bool,
#[dynamic(default = "default_split_size")]
size: f32,
}
impl_lua_conversion_dynamic!(SplitPane);
fn default_split_size() -> f32 {
0.5
}
impl SplitPane {
async fn run(self, pane: MuxPane) -> mlua::Result<MuxPane> {
let (command, command_dir) = self.cmd_builder.to_command_builder();
let source = SplitSource::Spawn {
command,
command_dir,
};
let size = if self.size == 0.0 {
SplitSize::Percent(50)
} else if self.size < 1.0 {
SplitSize::Percent((self.size * 100.).floor() as u8)
} else {
SplitSize::Cells(self.size as usize)
};
let direction = match self.direction {
HandySplitDirection::Right | HandySplitDirection::Left => SplitDirection::Horizontal,
HandySplitDirection::Top | HandySplitDirection::Bottom => SplitDirection::Vertical,
};
let request = SplitRequest {
direction,
target_is_second: match self.direction {
HandySplitDirection::Top | HandySplitDirection::Left => false,
HandySplitDirection::Bottom | HandySplitDirection::Right => true,
},
top_level: self.top_level,
size,
};
let mux = get_mux()?;
let (pane, _size) = mux
.split_pane(pane.0, request, source, self.domain)
.await
.map_err(|e| mlua::Error::external(format!("{:#?}", e)))?;
Ok(MuxPane(pane.pane_id()))
}
}

View File

@ -0,0 +1,91 @@
use super::*;
#[derive(Clone, Copy, Debug)]
pub struct MuxTab(pub TabId);
impl MuxTab {
pub fn resolve<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<Rc<Tab>> {
mux.get_tab(self.0)
.ok_or_else(|| mlua::Error::external(format!("tab id {} not found in mux", self.0)))
}
}
impl UserData for MuxTab {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, _: ()| {
Ok(format!("MuxTab(tab_id:{}, pid:{})", this.0, unsafe {
libc::getpid()
}))
});
methods.add_method("tab_id", |_, this, _: ()| Ok(this.0));
methods.add_method("window", |_, this, _: ()| {
let mux = get_mux()?;
for window_id in mux.iter_windows() {
if let Some(window) = mux.get_window(window_id) {
for tab in window.iter() {
if tab.tab_id() == this.0 {
return Ok(Some(MuxWindow(window_id)));
}
}
}
}
Ok(None)
});
methods.add_method("get_title", |_, this, _: ()| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
Ok(tab.get_title().to_string())
});
methods.add_method("set_title", |_, this, title: String| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
Ok(tab.set_title(&title))
});
methods.add_method("panes", |_, this, _: ()| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
Ok(tab
.iter_panes()
.into_iter()
.map(|info| MuxPane(info.pane.pane_id()))
.collect::<Vec<MuxPane>>())
});
methods.add_method("set_zoomed", |_, this, zoomed: bool| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
let was_zoomed = tab.set_zoomed(zoomed);
Ok(was_zoomed)
});
methods.add_method("panes_with_info", |lua, this, _: ()| {
let mux = get_mux()?;
let tab = this.resolve(&mux)?;
let result = lua.create_table()?;
for (idx, pos) in tab.iter_panes().into_iter().enumerate() {
let info = MuxPaneInfo {
index: pos.index,
is_active: pos.is_active,
is_zoomed: pos.is_zoomed,
left: pos.left,
top: pos.top,
width: pos.width,
pixel_width: pos.pixel_width,
height: pos.height,
pixel_height: pos.pixel_height,
};
let info = luahelper::dynamic_to_lua_value(lua, info.to_dynamic())?;
match &info {
LuaValue::Table(t) => {
t.set("pane", MuxPane(pos.pane.pane_id()))?;
}
_ => {}
}
result.set(idx + 1, info)?;
}
Ok(result)
});
}
}

View File

@ -0,0 +1,90 @@
use super::*;
#[derive(Clone, Copy, Debug)]
pub struct MuxWindow(pub WindowId);
impl MuxWindow {
pub fn resolve<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<Ref<'a, Window>> {
mux.get_window(self.0)
.ok_or_else(|| mlua::Error::external(format!("window id {} not found in mux", self.0)))
}
pub fn resolve_mut<'a>(&self, mux: &'a Rc<Mux>) -> mlua::Result<RefMut<'a, Window>> {
mux.get_window_mut(self.0)
.ok_or_else(|| mlua::Error::external(format!("window id {} not found in mux", self.0)))
}
}
impl UserData for MuxWindow {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, _: ()| {
Ok(format!(
"MuxWindow(mux_window_id:{}, pid:{})",
this.0,
unsafe { libc::getpid() }
))
});
methods.add_method("window_id", |_, this, _: ()| Ok(this.0));
methods.add_async_method("gui_window", |lua, this, _: ()| async move {
// Weakly bound to the gui module; mux cannot hard-depend
// on wezterm-gui, but we can runtime resolve the appropriate module
let wezterm_mod = get_or_create_module(lua, "wezterm")
.map_err(|err| mlua::Error::external(format!("{err:#}")))?;
let gui: mlua::Table = wezterm_mod.get("gui")?;
let func: mlua::Function = gui.get("gui_window_for_mux_window")?;
func.call_async::<_, mlua::Value>(this.0).await
});
methods.add_method("get_workspace", |_, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
Ok(window.get_workspace().to_string())
});
methods.add_method("set_workspace", |_, this, new_name: String| {
let mux = get_mux()?;
let mut window = this.resolve_mut(&mux)?;
Ok(window.set_workspace(&new_name))
});
methods.add_async_method("spawn_tab", |_, this, spawn: SpawnTab| async move {
spawn.spawn(this).await
});
methods.add_method("get_title", |_, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
Ok(window.get_title().to_string())
});
methods.add_method("set_title", |_, this, title: String| {
let mux = get_mux()?;
let mut window = this.resolve_mut(&mux)?;
Ok(window.set_title(&title))
});
methods.add_method("tabs", |_, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
Ok(window
.iter()
.map(|tab| MuxTab(tab.tab_id()))
.collect::<Vec<MuxTab>>())
});
methods.add_method("tabs_with_info", |lua, this, _: ()| {
let mux = get_mux()?;
let window = this.resolve(&mux)?;
let result = lua.create_table()?;
let active_idx = window.get_active_idx();
for (index, tab) in window.iter().enumerate() {
let info = MuxTabInfo {
index,
is_active: index == active_idx,
};
let info = luahelper::dynamic_to_lua_value(lua, info.to_dynamic())?;
match &info {
LuaValue::Table(t) => {
t.set("tab", MuxTab(tab.tab_id()))?;
}
_ => {}
}
result.set(index + 1, info)?;
}
Ok(result)
});
}
}