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"),
+ }
+ }
}