summaryrefslogtreecommitdiffstats
path: root/2
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-12-29 11:38:33 +0100
committerAram Drevekenin <aram@poor.dev>2022-12-29 11:38:33 +0100
commit843eb26d3a9d904ee825b9bdbf57b0ae494ac9d5 (patch)
treed51bd96c0453776b95bf14ca10d04b7e7d88179d /2
parentd1aae90d278ef47ae0240a2844d75883cb0ec37c (diff)
tests passing
Diffstat (limited to '2')
-rw-r--r--21838
1 files changed, 1838 insertions, 0 deletions
diff --git a/2 b/2
new file mode 100644
index 000000000..41a1b0d9e
--- /dev/null
+++ b/2
@@ -0,0 +1,1838 @@
+use crate::input::{
+ command::RunCommand,
+ config::ConfigError,
+ layout::{
+ FloatingPaneLayout, Layout, TiledPaneLayout, PercentOrFixed, Run, RunPlugin, RunPluginLocation,
+ SplitDirection, SplitSize,
+ },
+};
+
+use kdl::*;
+
+use std::collections::{HashMap, HashSet};
+use std::str::FromStr;
+
+use crate::{
+ kdl_child_with_name, kdl_children_nodes, kdl_get_bool_property_or_child_value,
+ kdl_get_bool_property_or_child_value_with_error, kdl_get_child,
+ kdl_get_int_property_or_child_value, kdl_get_property_or_child,
+ kdl_get_string_property_or_child_value, kdl_get_string_property_or_child_value_with_error,
+ kdl_name, kdl_parsing_error, kdl_property_names, kdl_property_or_child_value_node,
+ kdl_string_arguments,
+};
+
+use std::convert::TryFrom;
+use std::path::PathBuf;
+use std::vec::Vec;
+use url::Url;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PaneOrFloatingPane {
+ Pane(TiledPaneLayout),
+ FloatingPane(FloatingPaneLayout),
+ Either(TiledPaneLayout),
+}
+
+pub struct KdlLayoutParser<'a> {
+ global_cwd: Option<PathBuf>,
+ raw_layout: &'a str,
+ tab_templates: HashMap<String, (TiledPaneLayout, Vec<FloatingPaneLayout>, KdlNode)>,
+ pane_templates: HashMap<String, (PaneOrFloatingPane, KdlNode)>,
+ default_tab_template: Option<(TiledPaneLayout, Vec<FloatingPaneLayout>, KdlNode)>,
+}
+
+impl<'a> KdlLayoutParser<'a> {
+ pub fn new(raw_layout: &'a str, global_cwd: Option<PathBuf>) -> Self {
+ KdlLayoutParser {
+ raw_layout,
+ tab_templates: HashMap::new(),
+ pane_templates: HashMap::new(),
+ default_tab_template: None,
+ global_cwd,
+ }
+ }
+ fn is_a_reserved_word(&self, word: &str) -> bool {
+ word == "pane"
+ || word == "layout"
+ || word == "pane_template"
+ || word == "tab_template"
+ || word == "default_tab_template"
+ || word == "command"
+ || word == "edit"
+ || word == "plugin"
+ || word == "children"
+ || word == "tab"
+ || word == "args"
+ || word == "close_on_exit"
+ || word == "start_suspended"
+ || word == "borderless"
+ || word == "focus"
+ || word == "name"
+ || word == "size"
+ || word == "cwd"
+ || word == "split_direction"
+ }
+ fn is_a_valid_pane_property(&self, property_name: &str) -> bool {
+ property_name == "borderless"
+ || property_name == "focus"
+ || property_name == "name"
+ || property_name == "size"
+ || property_name == "plugin"
+ || property_name == "command"
+ || property_name == "edit"
+ || property_name == "cwd"
+ || property_name == "args"
+ || property_name == "close_on_exit"
+ || property_name == "start_suspended"
+ || property_name == "split_direction"
+ || property_name == "pane"
+ || property_name == "children"
+ }
+ fn is_a_valid_floating_pane_property(&self, property_name: &str) -> bool {
+ property_name == "borderless"
+ || property_name == "focus"
+ || property_name == "name"
+ || property_name == "plugin"
+ || property_name == "command"
+ || property_name == "edit"
+ || property_name == "cwd"
+ || property_name == "args"
+ || property_name == "close_on_exit"
+ || property_name == "start_suspended"
+ || property_name == "x"
+ || property_name == "y"
+ || property_name == "width"
+ || property_name == "height"
+ }
+ fn is_a_valid_tab_property(&self, property_name: &str) -> bool {
+ property_name == "focus"
+ || property_name == "name"
+ || property_name == "split_direction"
+ || property_name == "cwd"
+ || property_name == "floating_panes"
+ }
+ fn assert_legal_node_name(&self, name: &str, kdl_node: &KdlNode) -> Result<(), ConfigError> {
+ if name.contains(char::is_whitespace) {
+ Err(ConfigError::new_layout_kdl_error(
+ format!("Node names ({}) cannot contain whitespace.", name),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ } else if self.is_a_reserved_word(&name) {
+ Err(ConfigError::new_layout_kdl_error(
+ format!("Node name '{}' is a reserved word.", name),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ } else {
+ Ok(())
+ }
+ }
+ fn assert_legal_template_name(
+ &self,
+ name: &str,
+ kdl_node: &KdlNode,
+ ) -> Result<(), ConfigError> {
+ if name.is_empty() {
+ Err(ConfigError::new_layout_kdl_error(
+ format!("Template names cannot be empty"),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ } else if name.contains(')') || name.contains('(') {
+ Err(ConfigError::new_layout_kdl_error(
+ format!("Template names cannot contain parantheses"),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ } else if name
+ .chars()
+ .next()
+ .map(|first_char| first_char.is_numeric())
+ .unwrap_or(false)
+ {
+ Err(ConfigError::new_layout_kdl_error(
+ format!("Template names cannot start with numbers"),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ } else {
+ Ok(())
+ }
+ }
+ fn parse_split_size(&self, kdl_node: &KdlNode) -> Result<Option<SplitSize>, ConfigError> {
+ if let Some(size) = kdl_get_string_property_or_child_value!(kdl_node, "size") {
+ match SplitSize::from_str(size) {
+ Ok(size) => Ok(Some(size)),
+ Err(_e) => Err(kdl_parsing_error!(
+ format!(
+ "size should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")"
+ ),
+ kdl_node
+ )),
+ }
+ } else if let Some(size) = kdl_get_int_property_or_child_value!(kdl_node, "size") {
+ if size == 0 {
+ return Err(kdl_parsing_error!(
+ format!("size should be greater than 0"),
+ kdl_node
+ ));
+ }
+ Ok(Some(SplitSize::Fixed(size as usize)))
+ } else if let Some(node) = kdl_property_or_child_value_node!(kdl_node, "size") {
+ Err(kdl_parsing_error!(
+ format!("size should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")"),
+ node
+ ))
+ } else if let Some(node) = kdl_child_with_name!(kdl_node, "size") {
+ Err(kdl_parsing_error!(
+ format!(
+ "size cannot be bare, it should have a value (eg. 'size 1', or 'size \"50%\"')"
+ ),
+ node
+ ))
+ } else {
+ Ok(None)
+ }
+ }
+ fn parse_percent_or_fixed(
+ &self,
+ kdl_node: &KdlNode,
+ value_name: &str,
+ can_be_zero: bool,
+ ) -> Result<Option<PercentOrFixed>, ConfigError> {
+ if let Some(size) = kdl_get_string_property_or_child_value!(kdl_node, value_name) {
+ match PercentOrFixed::from_str(size) {
+ Ok(size) => {
+ if !can_be_zero && size.is_zero() {
+ Err(kdl_parsing_error!(
+ format!("{} should be greater than 0", value_name),
+ kdl_node
+ ))
+ } else {
+ Ok(Some(size))
+ }
+ },
+ Err(_e) => Err(kdl_parsing_error!(
+ format!(
+ "{} should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")",
+ value_name
+ ),
+ kdl_node
+ )),
+ }
+ } else if let Some(size) = kdl_get_int_property_or_child_value!(kdl_node, value_name) {
+ if size == 0 && !can_be_zero {
+ return Err(kdl_parsing_error!(
+ format!("{} should be greater than 0", value_name),
+ kdl_node
+ ));
+ }
+ Ok(Some(PercentOrFixed::Fixed(size as usize)))
+ } else if let Some(node) = kdl_property_or_child_value_node!(kdl_node, "size") {
+ Err(kdl_parsing_error!(
+ format!(
+ "{} should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")",
+ value_name
+ ),
+ node
+ ))
+ } else if let Some(node) = kdl_child_with_name!(kdl_node, "size") {
+ Err(kdl_parsing_error!(
+ format!(
+ "{} cannot be bare, it should have a value (eg. 'size 1', or 'size \"50%\"')",
+ value_name
+ ),
+ node
+ ))
+ } else {
+ Ok(None)
+ }
+ }
+ fn parse_plugin_block(&self, plugin_block: &KdlNode) -> Result<Option<Run>, ConfigError> {
+ let _allow_exec_host_cmd =
+ kdl_get_bool_property_or_child_value_with_error!(plugin_block, "_allow_exec_host_cmd")
+ .unwrap_or(false);
+ let string_url =
+ kdl_get_string_property_or_child_value_with_error!(plugin_block, "location").ok_or(
+ ConfigError::new_layout_kdl_error(
+ "Plugins must have a location".into(),
+ plugin_block.span().offset(),
+ plugin_block.span().len(),
+ ),
+ )?;
+ let url_node = kdl_get_property_or_child!(plugin_block, "location").ok_or(
+ ConfigError::new_layout_kdl_error(
+ "Plugins must have a location".into(),
+ plugin_block.span().offset(),
+ plugin_block.span().len(),
+ ),
+ )?;
+ let url = Url::parse(string_url).map_err(|e| {
+ ConfigError::new_layout_kdl_error(
+ format!("Failed to parse url: {:?}", e),
+ url_node.span().offset(),
+ url_node.span().len(),
+ )
+ })?;
+ let location = RunPluginLocation::try_from(url)?;
+ Ok(Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd,
+ location,
+ })))
+ }
+ fn parse_args(&self, pane_node: &KdlNode) -> Result<Option<Vec<String>>, ConfigError> {
+ match kdl_get_child!(pane_node, "args") {
+ Some(kdl_args) => {
+ if kdl_args.entries().is_empty() {
+ return Err(kdl_parsing_error!(format!("args cannot be empty and should contain one or more command arguments (eg. args \"-h\" \"-v\")"), kdl_args));
+ }
+ Ok(Some(
+ kdl_string_arguments!(kdl_args)
+ .iter()
+ .map(|s| String::from(*s))
+ .collect(),
+ ))
+ },
+ None => Ok(None),
+ }
+ }
+ fn cwd_prefix(&self, tab_cwd: Option<&PathBuf>) -> Result<Option<PathBuf>, ConfigError> {
+ Ok(match (&self.global_cwd, tab_cwd) {
+ (Some(global_cwd), Some(tab_cwd)) => Some(global_cwd.join(tab_cwd)),
+ (None, Some(tab_cwd)) => Some(tab_cwd.clone()),
+ (Some(global_cwd), None) => Some(global_cwd.clone()),
+ (None, None) => None,
+ })
+ }
+ fn parse_cwd(&self, kdl_node: &KdlNode) -> Result<Option<PathBuf>, ConfigError> {
+ Ok(
+ kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd")
+ .map(|cwd| PathBuf::from(cwd)),
+ )
+ }
+ fn parse_pane_command(
+ &self,
+ pane_node: &KdlNode,
+ is_template: bool,
+ ) -> Result<Option<Run>, ConfigError> {
+ let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command")
+ .map(|c| PathBuf::from(c));
+ let edit = kdl_get_string_property_or_child_value_with_error!(pane_node, "edit")
+ .map(|c| PathBuf::from(c));
+ let cwd = self.parse_cwd(pane_node)?;
+ let args = self.parse_args(pane_node)?;
+ let close_on_exit =
+ kdl_get_bool_property_or_child_value_with_error!(pane_node, "close_on_exit");
+ let start_suspended =
+ kdl_get_bool_property_or_child_value_with_error!(pane_node, "start_suspended");
+ if !is_template {
+ self.assert_no_bare_attributes_in_pane_node(
+ &command,
+ &args,
+ &close_on_exit,
+ &start_suspended,
+ pane_node,
+ )?;
+ }
+ let hold_on_close = close_on_exit.map(|c| !c).unwrap_or(true);
+ let hold_on_start = start_suspended.map(|c| c).unwrap_or(false);
+ match (command, edit, cwd) {
+ (None, None, Some(cwd)) => Ok(Some(Run::Cwd(cwd))),
+ (Some(command), None, cwd) => Ok(Some(Run::Command(RunCommand {
+ command,
+ args: args.unwrap_or_else(|| vec![]),
+ cwd,
+ hold_on_close,
+ hold_on_start,
+ }))),
+ (None, Some(edit), Some(cwd)) => Ok(Some(Run::EditFile(cwd.join(edit), None))),
+ (None, Some(edit), None) => Ok(Some(Run::EditFile(edit, None))),
+ (Some(_command), Some(_edit), _) => Err(ConfigError::new_layout_kdl_error(
+ "cannot have both a command and an edit instruction for the same pane".into(),
+ pane_node.span().offset(),
+ pane_node.span().len(),
+ )),
+ _ => Ok(None),
+ }
+ }
+ fn parse_command_plugin_or_edit_block(
+ &self,
+ kdl_node: &KdlNode,
+ ) -> Result<Option<Run>, ConfigError> {
+ let mut run = self.parse_pane_command(kdl_node, false)?;
+ if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
+ let has_non_cwd_run_prop = run
+ .map(|r| match r {
+ Run::Cwd(_) => false,
+ _ => true,
+ })
+ .unwrap_or(false);
+ if has_non_cwd_run_prop {
+ return Err(ConfigError::new_layout_kdl_error(
+ "Cannot have both a command/edit and a plugin block for a single pane".into(),
+ plugin_block.span().offset(),
+ plugin_block.span().len(),
+ ));
+ }
+ run = self.parse_plugin_block(plugin_block)?;
+ }
+ Ok(run)
+ }
+ fn parse_command_plugin_or_edit_block_for_template(
+ &self,
+ kdl_node: &KdlNode,
+ ) -> Result<Option<Run>, ConfigError> {
+ let mut run = self.parse_pane_command(kdl_node, true)?;
+ if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
+ let has_non_cwd_run_prop = run
+ .map(|r| match r {
+ Run::Cwd(_) => false,
+ _ => true,
+ })
+ .unwrap_or(false);
+ if has_non_cwd_run_prop {
+ return Err(ConfigError::new_layout_kdl_error(
+ "Cannot have both a command/edit and a plugin block for a single pane".into(),
+ plugin_block.span().offset(),
+ plugin_block.span().len(),
+ ));
+ }
+ run = self.parse_plugin_block(plugin_block)?;
+ }
+ Ok(run)
+ }
+ fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result<TiledPaneLayout, ConfigError> {
+ self.assert_valid_pane_properties(kdl_node)?;
+ let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless");
+ let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
+ let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
+ .map(|name| name.to_string());
+ let split_size = self.parse_split_size(kdl_node)?;
+ let run = self.parse_command_plugin_or_edit_block(kdl_node)?;
+ let children_split_direction = self.parse_split_direction(kdl_node)?;
+ let (external_children_index, children) = match kdl_children_nodes!(kdl_node) {
+ Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
+ None => (None, vec![]),
+ };
+ self.assert_no_mixed_children_and_properties(kdl_node)?;
+ Ok(TiledPaneLayout {
+ borderless: borderless.unwrap_or_default(),
+ focus,
+ name,
+ split_size,
+ run,
+ children_split_direction,
+ external_children_index,
+ children,
+ ..Default::default()
+ })
+ }
+ fn parse_floating_pane_node(
+ &self,
+ kdl_node: &KdlNode,
+ ) -> Result<FloatingPaneLayout, ConfigError> {
+ self.assert_valid_floating_pane_properties(kdl_node)?;
+ let height = self.parse_percent_or_fixed(kdl_node, "height", false)?;
+ let width = self.parse_percent_or_fixed(kdl_node, "width", false)?;
+ let x = self.parse_percent_or_fixed(kdl_node, "x", true)?;
+ let y = self.parse_percent_or_fixed(kdl_node, "y", true)?;
+ let run = self.parse_command_plugin_or_edit_block(kdl_node)?;
+ let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
+ let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
+ .map(|name| name.to_string());
+ self.assert_no_mixed_children_and_properties(kdl_node)?;
+ Ok(FloatingPaneLayout {
+ name,
+ height,
+ width,
+ x,
+ y,
+ run,
+ focus,
+ ..Default::default()
+ })
+ }
+ fn insert_children_to_pane_template(
+ &self,
+ kdl_node: &KdlNode,
+ pane_template: &mut TiledPaneLayout,
+ pane_template_kdl_node: &KdlNode,
+ ) -> Result<(), ConfigError> {
+ let children_split_direction = self.parse_split_direction(kdl_node)?;
+ let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
+ Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
+ None => (None, vec![]),
+ };
+ if pane_parts.len() > 0 {
+ let child_panes_layout = TiledPaneLayout {
+ children_split_direction,
+ children: pane_parts,
+ external_children_index,
+ ..Default::default()
+ };
+ self.assert_one_children_block(&pane_template, pane_template_kdl_node)?;
+ self.insert_layout_children_or_error(
+ pane_template,
+ child_panes_layout,
+ pane_template_kdl_node,
+ )?;
+ }
+ Ok(())
+ }
+ fn parse_pane_node_with_template(
+ &self,
+ kdl_node: &KdlNode,
+ pane_template: PaneOrFloatingPane,
+ pane_template_kdl_node: &KdlNode,
+ ) -> Result<TiledPaneLayout, ConfigError> {
+ match pane_template {
+ PaneOrFloatingPane::Pane(mut pane_template)
+ | PaneOrFloatingPane::Either(mut pane_template) => {
+ let borderless =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless");
+ let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
+ let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
+ .map(|name| name.to_string());
+ let args = self.parse_args(kdl_node)?;
+ let close_on_exit =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit");
+ let start_suspended =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "start_suspended");
+ let split_size = self.parse_split_size(kdl_node)?;
+ let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
+ self.assert_no_bare_attributes_in_pane_node_with_template(
+ &run,
+ &pane_template.run,
+ &args,
+ &close_on_exit,
+ &start_suspended,
+ kdl_node,
+ )?;
+ self.insert_children_to_pane_template(
+ kdl_node,
+ &mut pane_template,
+ pane_template_kdl_node,
+ )?;
+ pane_template.run = Run::merge(&pane_template.run, &run);
+ if let Some(pane_template_run_command) = pane_template.run.as_mut() {
+ // we need to do this because panes consuming a pane_template
+ // can have bare args without a command
+ pane_template_run_command.add_args(args);
+ pane_template_run_command.add_close_on_exit(close_on_exit);
+ pane_template_run_command.add_start_suspended(start_suspended);
+ };
+ if let Some(borderless) = borderless {
+ pane_template.borderless = borderless;
+ }
+ if let Some(focus) = focus {
+ pane_template.focus = Some(focus);
+ }
+ if let Some(name) = name {
+ pane_template.name = Some(name);
+ }
+ if let Some(split_size) = split_size {
+ pane_template.split_size = Some(split_size);
+ }
+ if let Some(index_of_children) = pane_template.external_children_index {
+ pane_template
+ .children
+ .insert(index_of_children, TiledPaneLayout::default());
+ }
+ pane_template.external_children_index = None;
+ Ok(pane_template)
+ },
+ PaneOrFloatingPane::FloatingPane(_) => {
+ let pane_template_name = kdl_get_string_property_or_child_value_with_error!(
+ pane_template_kdl_node,
+ "name"
+ )
+ .map(|name| name.to_string());
+ Err(ConfigError::new_layout_kdl_error(
+ format!("pane_template {}, is a floating pane template (derived from its properties) and cannot be applied to a tiled pane", pane_template_name.unwrap_or("".into())),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ },
+ }
+ }
+ fn parse_floating_pane_node_with_template(
+ &self,
+ kdl_node: &KdlNode,
+ pane_template: PaneOrFloatingPane,
+ pane_template_kdl_node: &KdlNode,
+ ) -> Result<FloatingPaneLayout, ConfigError> {
+ match pane_template {
+ PaneOrFloatingPane::Pane(_) => {
+ let pane_template_name = kdl_get_string_property_or_child_value_with_error!(
+ pane_template_kdl_node,
+ "name"
+ )
+ .map(|name| name.to_string());
+ Err(ConfigError::new_layout_kdl_error(
+ format!("pane_template {}, is a non-floating pane template (derived from its properties) and cannot be applied to a floating pane", pane_template_name.unwrap_or("".into())),
+ kdl_node.span().offset(),
+ kdl_node.span().len(),
+ ))
+ },
+ PaneOrFloatingPane::FloatingPane(mut pane_template) => {
+ let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
+ let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
+ .map(|name| name.to_string());
+ let args = self.parse_args(kdl_node)?;
+ let close_on_exit =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit");
+ let start_suspended =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "start_suspended");
+ let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
+ self.assert_no_bare_attributes_in_pane_node_with_template(
+ &run,
+ &pane_template.run,
+ &args,
+ &close_on_exit,
+ &start_suspended,
+ kdl_node,
+ )?;
+ pane_template.run = Run::merge(&pane_template.run, &run);
+ if let Some(pane_template_run_command) = pane_template.run.as_mut() {
+ // we need to do this because panes consuming a pane_template
+ // can have bare args without a command
+ pane_template_run_command.add_args(args);
+ pane_template_run_command.add_close_on_exit(close_on_exit);
+ pane_template_run_command.add_start_suspended(start_suspended);
+ };
+ if let Some(focus) = focus {
+ pane_template.focus = Some(focus);
+ }
+ if let Some(name) = name {
+ pane_template.name = Some(name);
+ }
+ let height = self.parse_percent_or_fixed(kdl_node, "height", false)?;
+ let width = self.parse_percent_or_fixed(kdl_node, "width", false)?;
+ let x = self.parse_percent_or_fixed(kdl_node, "x", true)?;
+ let y = self.parse_percent_or_fixed(kdl_node, "y", true)?;
+ // let mut floating_pane = FloatingPaneLayout::from(&pane_template);
+ if let Some(height) = height {
+ pane_template.height = Some(height);
+ }
+ if let Some(width) = width {
+ pane_template.width = Some(width);
+ }
+ if let Some(y) = y {
+ pane_template.y = Some(y);
+ }
+ if let Some(x) = x {
+ pane_template.x = Some(x);
+ }
+ Ok(pane_template)
+ },
+ PaneOrFloatingPane::Either(mut pane_template) => {
+ let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
+ let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
+ .map(|name| name.to_string());
+ let args = self.parse_args(kdl_node)?;
+ let close_on_exit =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit");
+ let start_suspended =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "start_suspended");
+ let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
+ self.assert_no_bare_attributes_in_pane_node_with_template(
+ &run,
+ &pane_template.run,
+ &args,
+ &close_on_exit,
+ &start_suspended,
+ kdl_node,
+ )?;
+ pane_template.run = Run::merge(&pane_template.run, &run);
+ if let Some(pane_template_run_command) = pane_template.run.as_mut() {
+ // we need to do this because panes consuming a pane_template
+ // can