diff options
Diffstat (limited to 'zellij-utils/src/input/layout.rs')
-rw-r--r-- | zellij-utils/src/input/layout.rs | 196 |
1 files changed, 194 insertions, 2 deletions
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 3dfadb402..ad2fca896 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -23,7 +23,7 @@ use std::cmp::Ordering; use std::fmt::{Display, Formatter}; use std::str::FromStr; -use super::plugins::{PluginTag, PluginsConfigError}; +use super::plugins::{PluginAliases, PluginTag, PluginsConfigError}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::vec::Vec; @@ -80,10 +80,122 @@ impl SplitSize { } } +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +pub enum RunPluginOrAlias { + RunPlugin(RunPlugin), + Alias(PluginAlias), +} + +impl Default for RunPluginOrAlias { + fn default() -> Self { + RunPluginOrAlias::RunPlugin(Default::default()) + } +} + +impl RunPluginOrAlias { + pub fn location_string(&self) -> String { + match self { + RunPluginOrAlias::RunPlugin(run_plugin) => run_plugin.location.display(), + RunPluginOrAlias::Alias(plugin_alias) => plugin_alias.name.clone(), + } + } + pub fn populate_run_plugin_if_needed(&mut self, plugin_aliases: &PluginAliases) { + if let RunPluginOrAlias::Alias(run_plugin_alias) = self { + if run_plugin_alias.run_plugin.is_some() { + log::warn!("Overriding plugin alias"); + } + let merged_run_plugin = plugin_aliases + .aliases + .get(run_plugin_alias.name.as_str()) + .map(|r| { + r.clone().merge_configuration( + &run_plugin_alias + .configuration + .as_ref() + .map(|c| c.inner().clone()), + ) + }); + run_plugin_alias.run_plugin = merged_run_plugin; + } + } + pub fn get_run_plugin(&self) -> Option<RunPlugin> { + match self { + RunPluginOrAlias::RunPlugin(run_plugin) => Some(run_plugin.clone()), + RunPluginOrAlias::Alias(plugin_alias) => plugin_alias.run_plugin.clone(), + } + } + pub fn get_configuration(&self) -> Option<PluginUserConfiguration> { + self.get_run_plugin().map(|r| r.configuration.clone()) + } + pub fn from_url( + url: &str, + configuration: &Option<BTreeMap<String, String>>, + alias_dict: Option<&PluginAliases>, + cwd: Option<PathBuf>, + ) -> Result<Self, String> { + match RunPluginLocation::parse(&url, cwd) { + Ok(location) => Ok(RunPluginOrAlias::RunPlugin(RunPlugin { + _allow_exec_host_cmd: false, + location, + configuration: configuration + .as_ref() + .map(|c| PluginUserConfiguration::new(c.clone())) + .unwrap_or_default(), + })), + Err(PluginsConfigError::InvalidUrlScheme(_)) + | Err(PluginsConfigError::InvalidUrl(..)) => { + let mut plugin_alias = PluginAlias::new(&url, configuration); + if let Some(alias_dict) = alias_dict { + plugin_alias.run_plugin = alias_dict + .aliases + .get(url) + .map(|r| r.clone().merge_configuration(configuration)); + } + Ok(RunPluginOrAlias::Alias(plugin_alias)) + }, + Err(e) => { + return Err(format!("Failed to parse plugin location {url}: {}", e)); + }, + } + } + pub fn is_equivalent_to_run(&self, run: &Option<Run>) -> bool { + match (self, run) { + ( + RunPluginOrAlias::Alias(self_alias), + Some(Run::Plugin(RunPluginOrAlias::Alias(run_alias))), + ) => { + self_alias.name == run_alias.name + && self_alias + .configuration + .as_ref() + // we do the is_empty() checks because an empty configuration is the same as no + // configuration (i.e. None) + .and_then(|c| if c.inner().is_empty() { None } else { Some(c) }) + == run_alias.configuration.as_ref().and_then(|c| { + if c.inner().is_empty() { + None + } else { + Some(c) + } + }) + }, + ( + RunPluginOrAlias::Alias(self_alias), + Some(Run::Plugin(RunPluginOrAlias::RunPlugin(other_run_plugin))), + ) => self_alias.run_plugin.as_ref() == Some(other_run_plugin), + ( + RunPluginOrAlias::RunPlugin(self_run_plugin), + Some(Run::Plugin(RunPluginOrAlias::RunPlugin(other_run_plugin))), + ) => self_run_plugin == other_run_plugin, + _ => false, + } + } +} + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum Run { #[serde(rename = "plugin")] - Plugin(RunPlugin), + Plugin(RunPluginOrAlias), #[serde(rename = "command")] Command(RunCommand), EditFile(PathBuf, Option<usize>, Option<PathBuf>), // TODO: merge this with TerminalAction::OpenFile @@ -227,6 +339,23 @@ impl Run { Run::Cwd(cwd) => Some(cwd.clone()), } } + pub fn get_run_plugin(&self) -> Option<RunPlugin> { + match self { + Run::Plugin(RunPluginOrAlias::RunPlugin(run_plugin)) => Some(run_plugin.clone()), + Run::Plugin(RunPluginOrAlias::Alias(plugin_alias)) => { + plugin_alias.run_plugin.as_ref().map(|r| r.clone()) + }, + _ => None, + } + } + pub fn populate_run_plugin_if_needed(&mut self, alias_dict: &PluginAliases) { + match self { + Run::Plugin(run_plugin_alias) => { + run_plugin_alias.populate_run_plugin_if_needed(alias_dict) + }, + _ => {}, + } + } } #[allow(clippy::derive_hash_xor_eq)] @@ -246,6 +375,35 @@ impl RunPlugin { ..Default::default() }) } + pub fn with_configuration(mut self, configuration: BTreeMap<String, String>) -> Self { + self.configuration = PluginUserConfiguration::new(configuration); + self + } + pub fn merge_configuration(mut self, configuration: &Option<BTreeMap<String, String>>) -> Self { + if let Some(configuration) = configuration { + self.configuration.merge(configuration); + } + self + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Hash, Default, PartialEq, Eq)] +pub struct PluginAlias { + pub name: String, + pub configuration: Option<PluginUserConfiguration>, + pub run_plugin: Option<RunPlugin>, +} + +impl PluginAlias { + pub fn new(name: &str, configuration: &Option<BTreeMap<String, String>>) -> Self { + PluginAlias { + name: name.to_owned(), + configuration: configuration + .as_ref() + .map(|c| PluginUserConfiguration::new(c.clone())), + ..Default::default() + } + } } #[allow(clippy::derive_hash_xor_eq)] @@ -280,6 +438,11 @@ impl PluginUserConfiguration { pub fn insert(&mut self, config_key: impl Into<String>, config_value: impl Into<String>) { self.0.insert(config_key.into(), config_value.into()); } + pub fn merge(&mut self, other_config: &BTreeMap<String, String>) { + for (key, value) in other_config { + self.0.insert(key.to_owned(), value.clone()); + } + } } impl FromStr for PluginUserConfiguration { @@ -706,6 +869,15 @@ impl TiledPaneLayout { child.add_cwd_to_layout(cwd); } } + pub fn populate_plugin_aliases_in_layout(&mut self, plugin_aliases: &PluginAliases) { + match self.run.as_mut() { + Some(run) => run.populate_run_plugin_if_needed(plugin_aliases), + _ => {}, + } + for child in self.children.iter_mut() { + child.populate_plugin_aliases_in_layout(plugin_aliases); + } + } pub fn deepest_depth(&self) -> usize { let mut deepest_child_depth = 0; for child in self.children.iter() { @@ -1141,6 +1313,26 @@ impl Layout { Err(_e) => None, } } + pub fn populate_plugin_aliases_in_layout(&mut self, plugin_aliases: &PluginAliases) { + for tab in self.tabs.iter_mut() { + tab.1.populate_plugin_aliases_in_layout(plugin_aliases); + for floating_pane_layout in tab.2.iter_mut() { + floating_pane_layout + .run + .as_mut() + .map(|f| f.populate_run_plugin_if_needed(&plugin_aliases)); + } + } + if let Some(template) = self.template.as_mut() { + template.0.populate_plugin_aliases_in_layout(plugin_aliases); + for floating_pane_layout in template.1.iter_mut() { + floating_pane_layout + .run + .as_mut() + .map(|f| f.populate_run_plugin_if_needed(&plugin_aliases)); + } + } + } } fn split_space( |