diff --git a/src/lua/util.rs b/src/lua/util.rs index cb38766..c370f61 100644 --- a/src/lua/util.rs +++ b/src/lua/util.rs @@ -5,6 +5,7 @@ use crate::msg::in_::external::ExplorerConfig; use crate::path; use crate::path::RelativityConfig; use crate::ui; +use crate::ui::Layout; use crate::ui::Style; use crate::ui::WrapOptions; use anyhow::Result; @@ -42,6 +43,7 @@ pub(crate) fn create_table(lua: &Lua) -> Result { util = relative_to(util, lua)?; util = shortened(util, lua)?; util = textwrap(util, lua)?; + util = layout_replaced(util, lua)?; Ok(util) } @@ -511,3 +513,49 @@ pub fn textwrap<'a>(util: Table<'a>, lua: &Lua) -> Result> { util.set("textwrap", func)?; Ok(util) } + +/// Find the target layout in the given layout and replace it with the replacement layout, +/// returning a new layout. +/// +/// Type: function( layout:[Layout][4], target:[Layout][4], replacement:[Layout][4] ) -> layout:[Layout][4] +/// +/// Example: +/// +/// ```lua +/// local layout = { +/// Horizontal = { +/// splits = { +/// "Table", -- Target +/// "HelpMenu", +/// }, +/// config = ..., +/// } +/// } +/// +/// xplr.util.layout_replaced(layout, "Table", "Selection") +/// -- { +/// -- Horizontal = { +/// -- splits = { +/// -- "Selection", -- Replaced +/// -- "HelpMenu", +/// -- }, +/// -- config = ... +/// -- } +/// -- } +/// ``` +pub fn layout_replaced<'a>(util: Table<'a>, lua: &Lua) -> Result> { + let func = lua.create_function( + move |lua, (layout, target, replacement): (Table, Table, Table)| { + let layout: Layout = lua.from_value(Value::Table(layout))?; + let target: Layout = lua.from_value(Value::Table(target))?; + let replacement: Layout = lua.from_value(Value::Table(replacement))?; + + let res = layout.replace(&target, &replacement); + let res = lua.to_value(&res)?; + + Ok(res) + }, + )?; + util.set("layout_replaced", func)?; + Ok(util) +} diff --git a/src/ui.rs b/src/ui.rs index 703fb3c..7e042ec 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -168,6 +168,32 @@ impl Layout { (_, other) => other.to_owned(), } } + + pub fn replace(self, target: &Self, replacement: &Self) -> Self { + match self { + Self::Horizontal { splits, config } => Self::Horizontal { + splits: splits + .into_iter() + .map(|s| s.replace(target, replacement)) + .collect(), + config, + }, + Self::Vertical { splits, config } => Self::Vertical { + splits: splits + .into_iter() + .map(|s| s.replace(target, replacement)) + .collect(), + config, + }, + other => { + if other == *target { + replacement.to_owned() + } else { + other + } + } + } + } } #[derive( @@ -1540,4 +1566,42 @@ mod tests { } ); } + + #[test] + fn test_layout_replace() { + let layout = Layout::Horizontal { + config: LayoutOptions { + margin: Some(2), + horizontal_margin: Some(3), + vertical_margin: Some(4), + constraints: Some(vec![ + Constraint::Percentage(80), + Constraint::Percentage(20), + ]), + }, + splits: vec![Layout::Table, Layout::HelpMenu], + }; + + let res = layout.clone().replace(&Layout::Table, &Layout::Selection); + + match (res, layout) { + ( + Layout::Horizontal { + config: res_config, + splits: res_splits, + }, + Layout::Horizontal { + config: layout_config, + splits: layout_splits, + }, + ) => { + assert_eq!(res_config, layout_config); + assert_eq!(res_splits.len(), layout_splits.len()); + assert_eq!(res_splits[0], Layout::Selection); + assert_ne!(res_splits[0], layout_splits[0]); + assert_eq!(res_splits[1], layout_splits[1]); + } + _ => panic!("Unexpected layout"), + } + } }