zellij/zellij-utils/src/input/plugins.rs
Aram Drevekenin 79bf6ab868
feat(config): switch to kdl (#1759)
* chore(config): default kdl keybindings config

* tests

* work

* refactor(config): move stuff around

* work

* tab merge layout

* work

* work

* layouts working

* work

* layout tests

* work

* work

* feat(parsing): kdl layouts without config

* refactor(kdl): move stuff around

* work

* tests(layout): add cases and fix bugs

* work

* fix(kdl): various bugs

* chore(layouts): move all layouts to kdl

* feat(kdl): shared keybidns

* fix(layout): do not count fixed panes toward percentile

* fix(keybinds): missing keybinds and actions

* fix(config): adjust default tips

* refactor(config): move stuff around

* fix(tests): make e2e tests pass

* fix(kdl): add verbose parsing errors

* fix(kdl): focused tab

* fix(layout): corret default_tab_template behavior

* style(code): fix compile warnings

* feat(cli): send actions through the cli

* fix(cli): exit only when action is done

* fix(cli): open embedded pane from floating pane

* fix(cli): send actions to other sessions

* feat(cli): command alias

* feat(converter): convert old config

* feat(converter): convert old layout and theme files

* feat(kdl): pretty errors

* feat(client): convert old YAML files on startup

* fix: various bugs and styling issues

* fix: e2e tests

* fix(screen): propagate errors after merge

* style(clippy): lower clippy level

* fix(tests): own session_name variable

* style(fmt): rustfmt

* fix(cli): various action fixes

* style(fmt): rustfmt

* fix(themes): loading of theme files

* style(fmt): rustfmt

* fix(tests): theme fixtures

* fix(layouts): better errors on unknown nodes

* fix(kdl): clarify valid node terminator error

* fix(e2e): adjust close tab test

* fix(e2e): adjust close tab test again

* style(code): cleanup some comments
2022-10-05 07:44:00 +02:00

147 lines
4.8 KiB
Rust

//! Plugins configuration metadata
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use thiserror::Error;
use serde::{Deserialize, Serialize};
use url::Url;
use super::layout::{RunPlugin, RunPluginLocation};
pub use crate::data::PluginTag;
use std::collections::BTreeMap;
use std::fmt;
/// Used in the config struct for plugin metadata
#[derive(Clone, PartialEq, Deserialize, Serialize)]
pub struct PluginsConfig(pub HashMap<PluginTag, PluginConfig>);
impl fmt::Debug for PluginsConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut stable_sorted = BTreeMap::new();
for (plugin_tag, plugin_config) in self.0.iter() {
stable_sorted.insert(plugin_tag, plugin_config);
}
write!(f, "{:#?}", stable_sorted)
}
}
impl PluginsConfig {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn from_data(data: HashMap<PluginTag, PluginConfig>) -> Self {
PluginsConfig(data)
}
/// Get plugin config from run configuration specified in layout files.
pub fn get(&self, run: impl Borrow<RunPlugin>) -> Option<PluginConfig> {
let run = run.borrow();
match &run.location {
RunPluginLocation::File(path) => Some(PluginConfig {
path: path.clone(),
run: PluginType::Pane(None),
_allow_exec_host_cmd: run._allow_exec_host_cmd,
location: run.location.clone(),
}),
RunPluginLocation::Zellij(tag) => self.0.get(tag).cloned().map(|plugin| PluginConfig {
_allow_exec_host_cmd: run._allow_exec_host_cmd,
..plugin
}),
}
}
pub fn iter(&self) -> impl Iterator<Item = &PluginConfig> {
self.0.values()
}
/// Merges two PluginConfig structs into one PluginConfig struct
/// `other` overrides the PluginConfig of `self`.
pub fn merge(&self, other: Self) -> Self {
let mut plugin_config = self.0.clone();
plugin_config.extend(other.0);
Self(plugin_config)
}
}
impl Default for PluginsConfig {
fn default() -> Self {
PluginsConfig(HashMap::new())
}
}
/// Plugin metadata
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct PluginConfig {
/// Path of the plugin, see resolve_wasm_bytes for resolution semantics
pub path: PathBuf,
/// Plugin type
pub run: PluginType,
/// Allow command execution from plugin
pub _allow_exec_host_cmd: bool,
/// Original location of the
pub location: RunPluginLocation,
}
impl PluginConfig {
/// Resolve wasm plugin bytes for the plugin path and given plugin directory. Attempts to first
/// resolve the plugin path as an absolute path, then adds a ".wasm" extension to the path and
/// resolves that, finally we use the plugin directory joined with the path with an appended
/// ".wasm" extension. So if our path is "tab-bar" and the given plugin dir is
/// "/home/bob/.zellij/plugins" the lookup chain will be this:
///
/// ```bash
/// /tab-bar
/// /tab-bar.wasm
/// /home/bob/.zellij/plugins/tab-bar.wasm
/// ```
///
pub fn resolve_wasm_bytes(&self, plugin_dir: &Path) -> Option<Vec<u8>> {
fs::read(&self.path)
.or_else(|_| fs::read(&self.path.with_extension("wasm")))
.or_else(|_| fs::read(plugin_dir.join(&self.path).with_extension("wasm")))
.ok()
}
/// Sets the tab index inside of the plugin type of the run field.
pub fn set_tab_index(&mut self, tab_index: usize) {
match self.run {
PluginType::Pane(..) => {
self.run = PluginType::Pane(Some(tab_index));
},
PluginType::Headless => {},
}
}
}
/// Type of the plugin. Defaults to Pane.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum PluginType {
// TODO: A plugin with output that's cloned across every pane in a tab, or across the entire
// application might be useful
// Tab
// Static
/// Starts immediately when Zellij is started and runs without a visible pane
Headless,
/// Runs once per pane declared inside a layout file
Pane(Option<usize>), // tab_index
}
impl Default for PluginType {
fn default() -> Self {
Self::Pane(None)
}
}
#[derive(Error, Debug, PartialEq)]
pub enum PluginsConfigError {
#[error("Duplication in plugin tag names is not allowed: '{}'", String::from(.0.clone()))]
DuplicatePlugins(PluginTag),
#[error("Only 'file:' and 'zellij:' url schemes are supported for plugin lookup. '{0}' does not match either.")]
InvalidUrl(Url),
#[error("Could not find plugin at the path: '{0:?}'")]
InvalidPluginLocation(PathBuf),
}