diff options
author | Aram Drevekenin <aram@poor.dev> | 2023-06-07 12:43:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-07 12:43:35 +0200 |
commit | c11d75f9157873fc99fe0d40933de8ec5fbb4f6b (patch) | |
tree | fc55dc7f9132395585613dd9cce94c98c8c76600 /zellij-server | |
parent | b8f095330a57c905f23563ca7c2bfae3171abf57 (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')
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 @@ |