summaryrefslogtreecommitdiffstats
path: root/zellij-server
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2023-06-07 12:43:35 +0200
committerGitHub <noreply@github.com>2023-06-07 12:43:35 +0200
commitc11d75f9157873fc99fe0d40933de8ec5fbb4f6b (patch)
treefc55dc7f9132395585613dd9cce94c98c8c76600 /zellij-server
parentb8f095330a57c905f23563ca7c2bfae3171abf57 (diff)
feat(wasm-plugin-system): major overhaul and some goodies (#2510)
* strider resiliency * worker channel prototype * finalized ui * show hide plugin * fs events to plugins * tests for events and new screen instructions * various refactoringz * report plugin errors instead of crashing zellij * fix plugin loading with workers * refactor: move watch filesystem * some fixes and refactoring * refactor(panes): combine pane insertion logic * refactor(screen): launch or focus * refactor(pty): consolidate default shell fetching * refactor: various cleanups * initial refactoring * more initial refactoring * refactor(strider): search * style(fmt): rustfmt * style(pty): cleanup * style(clippy): ok clippy * style(fmt): rustfmt
Diffstat (limited to 'zellij-server')
-rw-r--r--zellij-server/src/lib.rs9
-rw-r--r--zellij-server/src/panes/floating_panes/mod.rs17
-rw-r--r--zellij-server/src/panes/plugin_pane.rs2
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs20
-rw-r--r--zellij-server/src/plugins/mod.rs13
-rw-r--r--zellij-server/src/plugins/plugin_loader.rs43
-rw-r--r--zellij-server/src/plugins/plugin_map.rs70
-rw-r--r--zellij-server/src/plugins/plugin_worker.rs89
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs82
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__can_subscribe_to_hd_events.snap12
-rw-r--r--zellij-server/src/plugins/wasm_bridge.rs150
-rw-r--r--zellij-server/src/plugins/watch_filesystem.rs63
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs123
-rw-r--r--zellij-server/src/pty.rs14
-rw-r--r--zellij-server/src/route.rs10
-rw-r--r--zellij-server/src/screen.rs77
-rw-r--r--zellij-server/src/tab/mod.rs399
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs457
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs26
-rw-r--r--zellij-server/src/ui/loading_indication.rs16
-rw-r--r--zellij-server/src/unit/screen_tests.rs156
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__screen_can_suppress_pane-2.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__screen_can_suppress_pane-3.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__screen_can_suppress_pane.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_launch_or_focus_plugin_action.snap25
25 files changed, 1297 insertions, 634 deletions
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 166a60c5f..4e4911429 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -32,7 +32,7 @@ use wasmer::Store;
use crate::{
os_input_output::ServerOsApi,
plugins::{plugin_thread_main, PluginInstruction},
- pty::{pty_thread_main, Pty, PtyInstruction},
+ pty::{get_default_shell, pty_thread_main, Pty, PtyInstruction},
screen::{screen_thread_main, ScreenInstruction},
thread_bus::{Bus, ThreadSenders},
};
@@ -705,6 +705,10 @@ fn init_session(
..Default::default()
})
});
+ let path_to_default_shell = config_options
+ .default_shell
+ .clone()
+ .unwrap_or_else(|| get_default_shell());
let pty_thread = thread::Builder::new()
.name("pty".to_string())
@@ -757,6 +761,7 @@ fn init_session(
})
.unwrap();
+ let zellij_cwd = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
let plugin_thread = thread::Builder::new()
.name("wasm".to_string())
.spawn({
@@ -780,6 +785,8 @@ fn init_session(
data_dir,
plugins.unwrap_or_default(),
layout,
+ path_to_default_shell,
+ zellij_cwd,
)
.fatal()
}
diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs
index a85cf80fd..071f6e7c2 100644
--- a/zellij-server/src/panes/floating_panes/mod.rs
+++ b/zellij-server/src/panes/floating_panes/mod.rs
@@ -25,7 +25,7 @@ use zellij_utils::{
data::{ModeInfo, Style},
errors::prelude::*,
input::command::RunCommand,
- input::layout::FloatingPaneLayout,
+ input::layout::{FloatingPaneLayout, Run, RunPlugin},
pane_size::{Dimension, Offset, PaneGeom, Size, SizeInPixels, Viewport},
};
@@ -870,4 +870,19 @@ impl FloatingPanes {
self.focus_pane_for_all_clients(active_pane_id);
}
}
+ pub fn get_plugin_pane_id(&self, run_plugin: &RunPlugin) -> Option<PaneId> {
+ let run = Some(Run::Plugin(run_plugin.clone()));
+ self.panes
+ .iter()
+ .find(|(_id, s_p)| s_p.invoked_with() == &run)
+ .map(|(id, _)| *id)
+ }
+ pub fn focus_pane_if_exists(&mut self, pane_id: PaneId, client_id: ClientId) -> Result<()> {
+ if self.panes.get(&pane_id).is_some() {
+ self.focus_pane(pane_id, client_id);
+ Ok(())
+ } else {
+ Err(anyhow!("Pane not found"))
+ }
+ }
}
diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs
index 7915069c5..28fdb3d10 100644
--- a/zellij-server/src/panes/plugin_pane.rs
+++ b/zellij-server/src/panes/plugin_pane.rs
@@ -537,7 +537,7 @@ impl Pane for PluginPane {
self.pane_title = title;
}
fn update_loading_indication(&mut self, loading_indication: LoadingIndication) {
- if self.loading_indication.ended {
+ if self.loading_indication.ended && !loading_indication.is_error() {
return;
}
self.loading_indication.merge(loading_indication);
diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index 08a633b0a..3734214af 100644
--- a/zellij-server/src/panes/tiled_panes/mod.rs
+++ b/zellij-server/src/panes/tiled_panes/mod.rs
@@ -20,7 +20,10 @@ use stacked_panes::StackedPanes;
use zellij_utils::{
data::{Direction, ModeInfo, ResizeStrategy, Style},
errors::prelude::*,
- input::{command::RunCommand, layout::SplitDirection},
+ input::{
+ command::RunCommand,
+ layout::{Run, RunPlugin, SplitDirection},
+ },
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
};
@@ -529,6 +532,14 @@ impl TiledPanes {
}
self.reset_boundaries();
}
+ pub fn focus_pane_if_exists(&mut self, pane_id: PaneId, client_id: ClientId) -> Result<()> {
+ if self.panes.get(&pane_id).is_some() {
+ self.focus_pane(pane_id, client_id);
+ Ok(())
+ } else {
+ Err(anyhow!("Pane not found"))
+ }
+ }
pub fn focus_pane_at_position(&mut self, position_and_size: PaneGeom, client_id: ClientId) {
if let Some(pane_id) = self
.panes
@@ -1691,6 +1702,13 @@ impl TiledPanes {
fn reset_boundaries(&mut self) {
self.client_id_to_boundaries.clear();
}
+ pub fn get_plugin_pane_id(&self, run_plugin: &RunPlugin) -> Option<PaneId> {
+ let run = Some(Run::Plugin(run_plugin.clone()));
+ self.panes
+ .iter()
+ .find(|(_id, s_p)| s_p.invoked_with() == &run)
+ .map(|(id, _)| *id)
+ }
}
#[allow(clippy::borrowed_box)]
diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs
index 9384b3f62..7b322b054 100644
--- a/zellij-server/src/plugins/mod.rs
+++ b/zellij-server/src/plugins/mod.rs
@@ -1,6 +1,8 @@
mod plugin_loader;
mod plugin_map;
+mod plugin_worker;
mod wasm_bridge;
+mod watch_filesystem;
mod zellij_exports;
use log::info;
use std::{collections::HashMap, fs, path::PathBuf};
@@ -104,13 +106,22 @@ pub(crate) fn plugin_thread_main(
data_dir: PathBuf,
plugins: PluginsConfig,
layout: Box<Layout>,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<()> {
info!("Wasm main thread starts");
let plugin_dir = data_dir.join("plugins/");
let plugin_global_data_dir = plugin_dir.join("data");
- let mut wasm_bridge = WasmBridge::new(plugins, bus.senders.clone(), store, plugin_dir);
+ let mut wasm_bridge = WasmBridge::new(
+ plugins,
+ bus.senders.clone(),
+ store,
+ plugin_dir,
+ path_to_default_shell,
+ zellij_cwd,
+ );
loop {
let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
diff --git a/zellij-server/src/plugins/plugin_loader.rs b/zellij-server/src/plugins/plugin_loader.rs
index 91eb6c93e..4d77bd9e9 100644
--- a/zellij-server/src/plugins/plugin_loader.rs
+++ b/zellij-server/src/plugins/plugin_loader.rs
@@ -1,6 +1,5 @@
-use crate::plugins::plugin_map::{
- PluginEnv, PluginMap, RunningPlugin, RunningWorker, Subscriptions,
-};
+use crate::plugins::plugin_map::{PluginEnv, PluginMap, RunningPlugin, Subscriptions};
+use crate::plugins::plugin_worker::{plugin_worker, RunningWorker};
use crate::plugins::zellij_exports::{wasi_read_string, zellij_exports};
use crate::plugins::PluginId;
use highway::{HighwayHash, PortableHash};
@@ -164,6 +163,8 @@ pub struct PluginLoader<'a> {
plugin_own_data_dir: PathBuf,
size: Size,
wasm_blob_on_hd: Option<(Vec<u8>, PathBuf)>,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
}
impl<'a> PluginLoader<'a> {
@@ -176,6 +177,8 @@ impl<'a> PluginLoader<'a> {
plugin_map: Arc<Mutex<PluginMap>>,
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<()> {
let err_context = || format!("failed to reload plugin {plugin_id} from memory");
let mut connected_clients: Vec<ClientId> =
@@ -194,6 +197,8 @@ impl<'a> PluginLoader<'a> {
first_client_id,
&store,
&plugin_dir,
+ path_to_default_shell,
+ zellij_cwd,
)?;
plugin_loader
.load_module_from_memory()
@@ -227,6 +232,8 @@ impl<'a> PluginLoader<'a> {
size: Size,
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<()> {
let err_context = || format!("failed to start plugin {plugin_id} for client {client_id}");
let mut plugin_loader = PluginLoader::new(
@@ -240,6 +247,8 @@ impl<'a> PluginLoader<'a> {
&plugin_dir,
tab_index,
size,
+ path_to_default_shell,
+ zellij_cwd,
)?;
plugin_loader
.load_module_from_memory()
@@ -273,6 +282,8 @@ impl<'a> PluginLoader<'a> {
plugin_map: Arc<Mutex<PluginMap>>,
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<()> {
let mut new_plugins = HashSet::new();
for plugin_id in plugin_map.lock().unwrap().plugin_ids() {
@@ -288,6 +299,8 @@ impl<'a> PluginLoader<'a> {
existing_client_id,
&store,
&plugin_dir,
+ path_to_default_shell.clone(),
+ zellij_cwd.clone(),
)?;
plugin_loader
.load_module_from_memory()
@@ -314,6 +327,8 @@ impl<'a> PluginLoader<'a> {
plugin_map: Arc<Mutex<PluginMap>>,
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<()> {
let err_context = || format!("failed to reload plugin id {plugin_id}");
@@ -333,6 +348,8 @@ impl<'a> PluginLoader<'a> {
first_client_id,
&store,
&plugin_dir,
+ path_to_default_shell,
+ zellij_cwd,
)?;
plugin_loader
.compile_module()
@@ -363,6 +380,8 @@ impl<'a> PluginLoader<'a> {
plugin_dir: &'a PathBuf,
tab_index: usize,
size: Size,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<Self> {
let plugin_own_data_dir = ZELLIJ_SESSION_CACHE_DIR
.join(Url::from(&plugin.location).to_string())
@@ -383,6 +402,8 @@ impl<'a> PluginLoader<'a> {
plugin_own_data_dir,
size,
wasm_blob_on_hd: None,
+ path_to_default_shell,
+ zellij_cwd,
})
}
pub fn new_from_existing_plugin_attributes(
@@ -394,6 +415,8 @@ impl<'a> PluginLoader<'a> {
client_id: ClientId,
store: &Store,
plugin_dir: &'a PathBuf,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let (running_plugin, _subscriptions, _workers) = {
@@ -421,6 +444,8 @@ impl<'a> PluginLoader<'a> {
plugin_dir,
tab_index,
size,
+ path_to_default_shell,
+ zellij_cwd,
)
}
pub fn new_from_different_client_id(
@@ -432,6 +457,8 @@ impl<'a> PluginLoader<'a> {
client_id: ClientId,
store: &Store,
plugin_dir: &'a PathBuf,
+ path_to_default_shell: PathBuf,
+ zellij_cwd: PathBuf,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let running_plugin = {
@@ -460,6 +487,8 @@ impl<'a> PluginLoader<'a> {
plugin_dir,
tab_index,
size,
+ path_to_default_shell,
+ zellij_cwd,
)
}
pub fn load_module_from_memory(&mut self) -> Result<Module> {
@@ -625,7 +654,8 @@ impl<'a> PluginLoader<'a> {
let worker =
RunningWorker::new(instance, &function_name, plugin_config, plugin_env);
- workers.insert(function_name.into(), Arc::new(Mutex::new(worker)));
+ let worker_sender = plugin_worker(worker);
+ workers.insert(function_name.into(), worker_sender);
}
}
start_function.call(&[]).with_context(err_context)?;
@@ -689,6 +719,8 @@ impl<'a> PluginLoader<'a> {
*client_id,
&self.store,
&self.plugin_dir,
+ self.path_to_default_shell.clone(),
+ self.zellij_cwd.clone(),
)?;
plugin_loader_for_client
.load_module_from_memory()
@@ -746,7 +778,7 @@ impl<'a> PluginLoader<'a> {
};
let mut wasi_env = WasiState::new("Zellij")
.env("CLICOLOR_FORCE", "1")
- .map_dir("/host", ".")
+ .map_dir("/host", self.zellij_cwd.clone())
.and_then(|wasi| wasi.map_dir("/data", &self.plugin_own_data_dir))
.and_then(|wasi| wasi.map_dir("/tmp", ZELLIJ_TMP_DIR.as_path()))
.and_then(|wasi| {
@@ -771,6 +803,7 @@ impl<'a> PluginLoader<'a> {
wasi_env,
plugin_own_data_dir: self.plugin_own_data_dir.clone(),
tab_index: self.tab_index,
+ path_to_default_shell: self.path_to_default_shell.clone(),
};
let subscriptions = Arc::new(Mutex::new(HashSet::new()));
diff --git a/zellij-server/src/plugins/plugin_map.rs b/zellij-server/src/plugins/plugin_map.rs
index 0c3df931e..040c9f9fd 100644
--- a/zellij-server/src/plugins/plugin_map.rs
+++ b/zellij-server/src/plugins/plugin_map.rs
@@ -1,5 +1,4 @@
-use crate::plugins::plugin_loader::{PluginLoader, VersionMismatchError};
-use crate::plugins::zellij_exports::wasi_write_object;
+use crate::plugins::plugin_worker::MessageToWorker;
use crate::plugins::PluginId;
use std::{
collections::{HashMap, HashSet},
@@ -11,10 +10,10 @@ use wasmer_wasi::WasiEnv;
use crate::{thread_bus::ThreadSenders, ClientId};
+use zellij_utils::async_channel::Sender;
use zellij_utils::errors::prelude::*;
use zellij_utils::{
- consts::VERSION, data::EventType, input::layout::RunPluginLocation,
- input::plugins::PluginConfig,
+ data::EventType, input::layout::RunPluginLocation, input::plugins::PluginConfig,
};
// the idea here is to provide atomicity when adding/removing plugins from the map (eg. when a new
@@ -29,7 +28,7 @@ pub struct PluginMap {
(
Arc<Mutex<RunningPlugin>>,
Arc<Mutex<Subscriptions>>,
- HashMap<String, Arc<Mutex<RunningWorker>>>,
+ HashMap<String, Sender<MessageToWorker>>,
),
>,
}
@@ -41,7 +40,7 @@ impl PluginMap {
) -> Vec<(
Arc<Mutex<RunningPlugin>>,
Arc<Mutex<Subscriptions>>,
- HashMap<String, Arc<Mutex<RunningWorker>>>,
+ HashMap<String, Sender<MessageToWorker>>,
)> {
let mut removed = vec![];
let ids_in_plugin_map: Vec<(PluginId, ClientId)> =
@@ -62,7 +61,7 @@ impl PluginMap {
) -> Option<(
Arc<Mutex<RunningPlugin>>,
Arc<Mutex<Subscriptions>>,
- HashMap<String, Arc<Mutex<RunningWorker>>>,
+ HashMap<String, Sender<MessageToWorker>>,
)> {
self.plugin_assets.remove(&(plugin_id, client_id))
}
@@ -132,12 +131,12 @@ impl PluginMap {
.and_then(|(_, (running_plugin, _, _))| Some(running_plugin.clone())),
}
}
- pub fn clone_worker(
+ pub fn worker_sender(
&self,
plugin_id: PluginId,
client_id: ClientId,
worker_name: &str,
- ) -> Option<Arc<Mutex<RunningWorker>>> {
+ ) -> Option<Sender<MessageToWorker>> {
self.plugin_assets
.iter()
.find(|((p_id, c_id), _)| p_id == &plugin_id && c_id == &client_id)
@@ -174,7 +173,7 @@ impl PluginMap {
client_id: ClientId,
running_plugin: Arc<Mutex<RunningPlugin>>,
subscriptions: Arc<Mutex<Subscriptions>>,
- running_workers: HashMap<String, Arc<Mutex<RunningWorker>>>,
+ running_workers: HashMap<String, Sender<MessageToWorker>>,
) {
self.plugin_assets.insert(
(plugin_id, client_id),
@@ -195,6 +194,7 @@ pub struct PluginEnv {
pub client_id: ClientId,
#[allow(dead_code)]
pub plugin_own_data_dir: PathBuf,
+ pub path_to_default_shell: PathBuf,
}
impl PluginEnv {
@@ -256,53 +256,3 @@ impl RunningPlugin {
}
}
}
-
-pub struct RunningWorker {
- pub instance: Instance,
- pub name: String,
- pub plugin_config: PluginConfig,
- pub plugin_env: PluginEnv,
-}
-
-impl RunningWorker {
- pub fn new(
- instance: Instance,
- name: &str,
- plugin_config: PluginConfig,
- plugin_env: PluginEnv,
- ) -> Self {
- RunningWorker {
- instance,
- name: name.into(),
- plugin_config,
- plugin_env,
- }
- }
- pub fn send_message(&self, message: String, payload: String) -> Result<()> {
- let err_context = || format!("Failed to send message to worker");
-
- let work_function = self
- .instance
- .exports
- .get_function(&self.name)
- .with_context(err_context)?;
- wasi_write_object(&self.plugin_env.wasi_env, &(message, payload))
- .with_context(err_context)?;
- work_function.call(&[]).or_else::<anyError, _>(|e| {
- match e.downcast::<serde_json::Error>() {
- Ok(_) => panic!(
- "{}",
- anyError::new(VersionMismatchError::new(
- VERSION,
- "Unavailable",
- &self.plugin_config.path,
- self.plugin_config.is_builtin(),
- ))
- ),
- Err(e) => Err(e).with_context(err_context),
- }
- })?;
-
- Ok(())
- }
-}
diff --git a/zellij-server/src/plugins/plugin_worker.rs b/zellij-server/src/plugins/plugin_worker.rs
new file mode 100644
index 000000000..bc7303c7c
--- /dev/null
+++ b/zellij-server/src/plugins/plugin_worker.rs
@@ -0,0 +1,89 @@