1use super::{PluginId, PluginInstruction};
2use crate::plugins::pipes::{
3 apply_pipe_message_to_plugin, pipes_to_block_or_unblock, PendingPipes, PipeStateChange,
4};
5use crate::plugins::plugin_loader::PluginLoader;
6use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugin, Subscriptions};
7
8use crate::plugins::plugin_worker::MessageToWorker;
9use crate::plugins::watch_filesystem::watch_filesystem;
10use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object};
11use async_channel::Sender;
12use async_std::task::{self, JoinHandle};
13use highway::{HighwayHash, PortableHash};
14use log::info;
15use notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
16use std::{
17 collections::{BTreeMap, HashMap, HashSet},
18 path::PathBuf,
19 str::FromStr,
20 sync::{Arc, Mutex},
21};
22use wasmtime::{Engine, Module};
23use zellij_utils::consts::{ZELLIJ_CACHE_DIR, ZELLIJ_TMP_DIR};
24use zellij_utils::data::{
25 FloatingPaneCoordinates, InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource,
26};
27use zellij_utils::downloader::Downloader;
28use zellij_utils::input::keybinds::Keybinds;
29use zellij_utils::input::permission::PermissionCache;
30use zellij_utils::plugin_api::event::ProtobufEvent;
31
32use prost::Message;
33
34use crate::panes::PaneId;
35use crate::{
36 background_jobs::BackgroundJob, screen::ScreenInstruction, thread_bus::ThreadSenders,
37 ui::loading_indication::LoadingIndication, ClientId, ServerInstruction,
38};
39use zellij_utils::{
40 data::{Event, EventType, PluginCapabilities},
41 errors::prelude::*,
42 input::{
43 command::TerminalAction,
44 layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias},
45 plugins::PluginConfig,
46 },
47 ipc::ClientAttributes,
48 pane_size::Size,
49};
50
51#[derive(Debug, Clone)]
52pub enum EventOrPipeMessage {
53 Event(Event),
54 PipeMessage(PipeMessage),
55}
56
57#[derive(Debug, Clone, Default)]
58pub struct PluginRenderAsset {
59 pub client_id: ClientId,
61 pub plugin_id: PluginId,
62 pub bytes: Vec<u8>,
63 pub cli_pipes: HashMap<String, PipeStateChange>,
64}
65
66impl PluginRenderAsset {
67 pub fn new(plugin_id: PluginId, client_id: ClientId, bytes: Vec<u8>) -> Self {
68 PluginRenderAsset {
69 client_id,
70 plugin_id,
71 bytes,
72 ..Default::default()
73 }
74 }
75 pub fn with_pipes(mut self, cli_pipes: HashMap<String, PipeStateChange>) -> Self {
76 self.cli_pipes = cli_pipes;
77 self
78 }
79}
80
81pub struct WasmBridge {
82 connected_clients: Arc<Mutex<Vec<ClientId>>>,
83 senders: ThreadSenders,
84 engine: Engine,
85 plugin_dir: PathBuf,
86 plugin_cache: Arc<Mutex<HashMap<PathBuf, Module>>>,
87 plugin_map: Arc<Mutex<PluginMap>>,
88 next_plugin_id: PluginId,
89 plugin_ids_waiting_for_permission_request: HashSet<PluginId>,
90 cached_events_for_pending_plugins: HashMap<PluginId, Vec<EventOrPipeMessage>>,
91 cached_resizes_for_pending_plugins: HashMap<PluginId, (usize, usize)>, cached_worker_messages: HashMap<PluginId, Vec<(ClientId, String, String, String)>>, loading_plugins: HashMap<(PluginId, RunPlugin), JoinHandle<()>>, pending_plugin_reloads: HashSet<RunPlugin>,
98 path_to_default_shell: PathBuf,
99 watcher: Option<Debouncer<RecommendedWatcher, FileIdMap>>,
100 zellij_cwd: PathBuf,
101 capabilities: PluginCapabilities,
102 client_attributes: ClientAttributes,
103 default_shell: Option<TerminalAction>,
104 default_layout: Box<Layout>,
105 cached_plugin_map:
106 HashMap<RunPluginLocation, HashMap<PluginUserConfiguration, Vec<(PluginId, ClientId)>>>,
107 pending_pipes: PendingPipes,
108 layout_dir: Option<PathBuf>,
109 default_mode: InputMode,
110 default_keybinds: Keybinds,
111 keybinds: HashMap<ClientId, Keybinds>,
112 base_modes: HashMap<ClientId, InputMode>,
113 downloader: Downloader,
114}
115
116impl WasmBridge {
117 pub fn new(
118 senders: ThreadSenders,
119 engine: Engine,
120 plugin_dir: PathBuf,
121 path_to_default_shell: PathBuf,
122 zellij_cwd: PathBuf,
123 capabilities: PluginCapabilities,
124 client_attributes: ClientAttributes,
125 default_shell: Option<TerminalAction>,
126 default_layout: Box<Layout>,
127 layout_dir: Option<PathBuf>,
128 default_mode: InputMode,
129 default_keybinds: Keybinds,
130 ) -> Self {
131 let plugin_map = Arc::new(Mutex::new(PluginMap::default()));
132 let connected_clients: Arc<Mutex<Vec<ClientId>>> = Arc::new(Mutex::new(vec![]));
133 let plugin_cache: Arc<Mutex<HashMap<PathBuf, Module>>> =
134 Arc::new(Mutex::new(HashMap::new()));
135 let watcher = None;
136 let downloader = Downloader::new(ZELLIJ_CACHE_DIR.to_path_buf());
137 WasmBridge {
138 connected_clients,
139 senders,
140 engine,
141 plugin_dir,
142 plugin_cache,
143 plugin_map,
144 path_to_default_shell,
145 watcher,
146 next_plugin_id: 0,
147 cached_events_for_pending_plugins: HashMap::new(),
148 plugin_ids_waiting_for_permission_request: HashSet::new(),
149 cached_resizes_for_pending_plugins: HashMap::new(),
150 cached_worker_messages: HashMap::new(),
151 loading_plugins: HashMap::new(),
152 pending_plugin_reloads: HashSet::new(),
153 zellij_cwd,
154 capabilities,
155 client_attributes,
156 default_shell,
157 default_layout,
158 cached_plugin_map: HashMap::new(),
159 pending_pipes: Default::default(),
160 layout_dir,
161 default_mode,
162 default_keybinds,
163 keybinds: HashMap::new(),
164 base_modes: HashMap::new(),
165 downloader,
166 }
167 }
168 pub fn load_plugin(
169 &mut self,
170 run: &Option<RunPlugin>,
171 tab_index: Option<usize>,
172 size: Size,
173 cwd: Option<PathBuf>,
174 skip_cache: bool,
175 client_id: Option<ClientId>,
176 cli_client_id: Option<ClientId>,
177 ) -> Result<(PluginId, ClientId)> {
178 let err_context = move || format!("failed to load plugin");
180
181 let client_id = client_id
182 .or_else(|| {
183 self.connected_clients
184 .lock()
185 .unwrap()
186 .iter()
187 .next()
188 .copied()
189 })
190 .with_context(|| {
191 "Plugins must have a client id, none was provided and none are connected"
192 })?;
193
194 let plugin_id = self.next_plugin_id;
195
196 match run {
197 Some(run) => {
198 let mut plugin = PluginConfig::from_run_plugin(run)
199 .with_context(|| format!("failed to resolve plugin {run:?}"))
200 .with_context(err_context)?;
201 let plugin_name = run.location.to_string();
202
203 self.cached_events_for_pending_plugins
204 .insert(plugin_id, vec![]);
205 self.cached_resizes_for_pending_plugins
206 .insert(plugin_id, (size.rows, size.cols));
207
208 let load_plugin_task = task::spawn({
209 let plugin_dir = self.plugin_dir.clone();
210 let plugin_cache = self.plugin_cache.clone();
211 let senders = self.senders.clone();
212 let engine = self.engine.clone();
213 let plugin_map = self.plugin_map.clone();
214 let connected_clients = self.connected_clients.clone();
215 let path_to_default_shell = self.path_to_default_shell.clone();
216 let zellij_cwd = cwd.unwrap_or_else(|| self.zellij_cwd.clone());
217 let capabilities = self.capabilities.clone();
218 let client_attributes = self.client_attributes.clone();
219 let default_shell = self.default_shell.clone();
220 let default_layout = self.default_layout.clone();
221 let layout_dir = self.layout_dir.clone();
222 let downloader = self.downloader.clone();
223 let default_mode = self
224 .base_modes
225 .get(&client_id)
226 .copied()
227 .unwrap_or(self.default_mode);
228 let keybinds = self
229 .keybinds
230 .get(&client_id)
231 .cloned()
232 .unwrap_or_else(|| self.default_keybinds.clone());
233 async move {
234 let _ = senders.send_to_background_jobs(
235 BackgroundJob::AnimatePluginLoading(plugin_id),
236 );
237 let mut loading_indication = LoadingIndication::new(plugin_name.clone());
238
239 if let RunPluginLocation::Remote(url) = &plugin.location {
240 let file_name: String = PortableHash::default()
241 .hash128(url.as_bytes())
242 .iter()
243 .map(ToString::to_string)
244 .collect();
245
246 match downloader.download(url, Some(&file_name)).await {
249 Ok(_) => plugin.path = ZELLIJ_CACHE_DIR.join(&file_name),
250 Err(e) => handle_plugin_loading_failure(
251 &senders,
252 plugin_id,
253 &mut loading_indication,
254 e,
255 cli_client_id,
256 ),
257 }
258 }
259
260 match PluginLoader::start_plugin(
261 plugin_id,
262 client_id,
263 &plugin,
264 tab_index,
265 plugin_dir,
266 plugin_cache,
267 senders.clone(),
268 engine,
269 plugin_map.clone(),
270 size,
271 connected_clients.clone(),
272 &mut loading_indication,
273 path_to_default_shell,
274 zellij_cwd.clone(),
275 capabilities,
276 client_attributes,
277 default_shell,
278 default_layout,
279 skip_cache,
280 layout_dir,
281 default_mode,
282 keybinds,
283 ) {
284 Ok(_) => {
285 let plugin_list = plugin_map.lock().unwrap().list_plugins();
286 handle_plugin_successful_loading(&senders, plugin_id, plugin_list);
287 },
288 Err(e) => handle_plugin_loading_failure(
289 &senders,
290 plugin_id,
291 &mut loading_indication,
292 e,
293 cli_client_id,
294 ),
295 }
296 let _ = senders.send_to_plugin(PluginInstruction::ApplyCachedEvents {
297 plugin_ids: vec![plugin_id],
298 done_receiving_permissions: false,
299 });
300 }
301 });
302 self.loading_plugins
303 .insert((plugin_id, run.clone()), load_plugin_task);
304 self.next_plugin_id += 1;
305 },
306 None => {
307 self.next_plugin_id += 1;
308 let mut loading_indication = LoadingIndication::new(format!("{}", plugin_id));
309 handle_plugin_loading_failure(
310 &self.senders,
311 plugin_id,
312 &mut loading_indication,
313 "Failed to resolve plugin alias",
314 None,
315 );
316 },
317 }
318 Ok((plugin_id, client_id))
319 }
320 pub fn unload_plugin(&mut self, pid: PluginId) -> Result<()> {
321 info!("Bye from plugin {}", &pid);
322 let mut plugin_map = self.plugin_map.lock().unwrap();
323 for ((plugin_id, client_id), (running_plugin, subscriptions, workers)) in
324 plugin_map.remove_plugins(pid)
325 {
326 for (_worker_name, worker_sender) in workers {
327 drop(worker_sender.send(MessageToWorker::Exit));
328 }
329 {
330 if running_plugin.lock().unwrap().intercepting_key_presses() {
333 let _ = self
334 .senders
335 .send_to_screen(ScreenInstruction::ClearKeyPressesIntercepts(client_id));
336 }
337 }
338 let subscriptions = subscriptions.lock().unwrap();
339 if subscriptions.contains(&EventType::BeforeClose) {
340 let mut running_plugin = running_plugin.lock().unwrap();
341 match apply_before_close_event_to_plugin(
342 pid,
343 client_id,
344 &mut running_plugin,
345 self.senders.clone(),
346 ) {
347 Ok(()) => {},
348 Err(e) => {
349 log::error!("{:?}", e);
350
351 let stringified_error = format!("{:?}", e).replace("\n", "\n\r");
353
354 handle_plugin_crash(plugin_id, stringified_error, self.senders.clone());
355 },
356 }
357 let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone();
358 if let Err(e) = std::fs::remove_dir_all(cache_dir) {
359 log::error!("Failed to remove cache dir for plugin: {:?}", e);
360 }
361 } else {
362 let running_plugin = running_plugin.lock().unwrap();
365 let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone();
366 if let Err(e) = std::fs::remove_dir_all(cache_dir) {
367 log::error!("Failed to remove cache dir for plugin: {:?}", e);
368 }
369 }
370 }
371 self.cached_plugin_map.clear();
372 let mut pipes_to_unblock = self.pending_pipes.unload_plugin(&pid);
373 for pipe_name in pipes_to_unblock.drain(..) {
374 let _ = self
375 .senders
376 .send_to_server(ServerInstruction::UnblockCliPipeInput(pipe_name))
377 .context("failed to unblock input pipe");
378 }
379 let plugin_list = plugin_map.list_plugins();
380 let _ = self
381 .senders
382 .send_to_background_jobs(BackgroundJob::ReportPluginList(plugin_list));
383 Ok(())
384 }
385 pub fn reload_plugin_with_id(&mut self, plugin_id: u32) -> Result<()> {
386 let Some(run_plugin) = self.run_plugin_of_plugin_id(plugin_id).map(|r| r.clone()) else {
387 log::error!("Failed to find plugin with id: {}", plugin_id);
388 return Ok(());
389 };
390
391 let (rows, columns) = self.size_of_plugin_id(plugin_id).unwrap_or((0, 0));
392 self.cached_events_for_pending_plugins
393 .insert(plugin_id, vec![]);
394 self.cached_resizes_for_pending_plugins
395 .insert(plugin_id, (rows, columns));
396
397 let mut loading_indication = LoadingIndication::new(run_plugin.location.to_string());
398 self.start_plugin_loading_indication(&[plugin_id], &loading_indication);
399 let load_plugin_task = task::spawn({
400 let plugin_dir = self.plugin_dir.clone();
401 let plugin_cache = self.plugin_cache.clone();
402 let senders = self.senders.clone();
403 let engine = self.engine.clone();
404 let plugin_map = self.plugin_map.clone();
405 let connected_clients = self.connected_clients.clone();
406 let path_to_default_shell = self.path_to_default_shell.clone();
407 let capabilities = self.capabilities.clone();
408 let client_attributes = self.client_attributes.clone();
409 let default_shell = self.default_shell.clone();
410 let default_layout = self.default_layout.clone();
411 let layout_dir = self.layout_dir.clone();
412 async move {
413 match PluginLoader::reload_plugin(
414 plugin_id,
415 plugin_dir.clone(),
416 plugin_cache.clone(),
417 senders.clone(),
418 engine.clone(),
419 plugin_map.clone(),
420 connected_clients.clone(),
421 &mut loading_indication,
422 path_to_default_shell.clone(),
423 capabilities.clone(),
424 client_attributes.clone(),
425 default_shell.clone(),
426 default_layout.clone(),
427 layout_dir.clone(),
428 ) {
429 Ok(_) => {
430 let plugin_list = plugin_map.lock().unwrap().list_plugins();
431 handle_plugin_successful_loading(&senders, plugin_id, plugin_list);
432 },
433 Err(e) => {
434 handle_plugin_loading_failure(
435 &senders,
436 plugin_id,
437 &mut loading_indication,
438 &e,
439 None,
440 );
441 },
442 }
443 let _ = senders.send_to_plugin(PluginInstruction::ApplyCachedEvents {
444 plugin_ids: vec![plugin_id],
445 done_receiving_permissions: false,
446 });
447 }
448 });
449 self.loading_plugins
450 .insert((plugin_id, run_plugin.clone()), load_plugin_task);
451 Ok(())
452 }
453 pub fn reload_plugin(&mut self, run_plugin: &RunPlugin) -> Result<()> {
454 if self.plugin_is_currently_being_loaded(&run_plugin.location) {
455 self.pending_plugin_reloads.insert(run_plugin.clone());
456 return Ok(());
457 }
458
459 let plugin_ids = self
460 .all_plugin_ids_for_plugin_location(&run_plugin.location, &run_plugin.configuration)?;
461 for plugin_id in &plugin_ids {
462 let (rows, columns) = self.size_of_plugin_id(*plugin_id).unwrap_or((0, 0));
463 self.cached_events_for_pending_plugins
464 .insert(*plugin_id, vec![]);
465 self.cached_resizes_for_pending_plugins
466 .insert(*plugin_id, (rows, columns));
467 }
468
469 let first_plugin_id = *plugin_ids.get(0).unwrap(); let mut loading_indication = LoadingIndication::new(run_plugin.location.to_string());
472 self.start_plugin_loading_indication(&plugin_ids, &loading_indication);
473 let load_plugin_task = task::spawn({
474 let plugin_dir = self.plugin_dir.clone();
475 let plugin_cache = self.plugin_cache.clone();
476 let senders = self.senders.clone();
477 let engine = self.engine.clone();
478 let plugin_map = self.plugin_map.clone();
479 let connected_clients = self.connected_clients.clone();
480 let path_to_default_shell = self.path_to_default_shell.clone();
481 let zellij_cwd = self.zellij_cwd.clone();
482 let capabilities = self.capabilities.clone();
483 let client_attributes = self.client_attributes.clone();
484 let default_shell = self.default_shell.clone();
485 let default_layout = self.default_layout.clone();
486 let layout_dir = self.layout_dir.clone();
487 async move {
488 match PluginLoader::reload_plugin(
489 first_plugin_id,
490 plugin_dir.clone(),
491 plugin_cache.clone(),
492 senders.clone(),
493 engine.clone(),
494 plugin_map.clone(),
495 connected_clients.clone(),
496 &mut loading_indication,
497 path_to_default_shell.clone(),
498 capabilities.clone(),
499 client_attributes.clone(),
500 default_shell.clone(),
501 default_layout.clone(),
502 layout_dir.clone(),
503 ) {
504 Ok(_) => {
505 let plugin_list = plugin_map.lock().unwrap().list_plugins();
506 handle_plugin_successful_loading(&senders, first_plugin_id, plugin_list);
507 for plugin_id in &plugin_ids {
508 if plugin_id == &first_plugin_id {
509 continue;
511 }
512 let mut loading_indication = LoadingIndication::new("".into());
513 match PluginLoader::reload_plugin_from_memory(
514 *plugin_id,
515 plugin_dir.clone(),
516 plugin_cache.clone(),
517 senders.clone(),
518 engine.clone(),
519 plugin_map.clone(),
520 connected_clients.clone(),
521 &mut loading_indication,
522 path_to_default_shell.clone(),
523 zellij_cwd.clone(),
524 capabilities.clone(),
525 client_attributes.clone(),
526 default_shell.clone(),
527 default_layout.clone(),
528 layout_dir.clone(),
529 ) {
530 Ok(_) => {
531 let plugin_list = plugin_map.lock().unwrap().list_plugins();
532 handle_plugin_successful_loading(
533 &senders,
534 *plugin_id,
535 plugin_list,
536 );
537 },
538 Err(e) => handle_plugin_loading_failure(
539 &senders,
540 *plugin_id,
541 &mut loading_indication,
542 e,
543 None,
544 ),
545 }
546 }
547 },
548 Err(e) => {
549 for plugin_id in &plugin_ids {
550 handle_plugin_loading_failure(
551 &senders,
552 *plugin_id,
553 &mut loading_indication,
554 &e,
555 None,
556 );
557 }
558 },
559 }
560 let _ = senders.send_to_plugin(PluginInstruction::ApplyCachedEvents {
561 plugin_ids,
562 done_receiving_permissions: false,
563 });
564 }
565 });
566 self.loading_plugins
567 .insert((first_plugin_id, run_plugin.clone()), load_plugin_task);
568 Ok(())
569 }
570 pub fn add_client(&mut self, client_id: ClientId) -> Result<()> {
571 let mut loading_indication = LoadingIndication::new("".into());
572 match PluginLoader::add_client(
573 client_id,
574 self.plugin_dir.clone(),
575 self.plugin_cache.clone(),
576 self.senders.clone(),
577 self.engine.clone(),
578 self.plugin_map.clone(),
579 self.connected_clients.clone(),
580 &mut loading_indication,
581 self.path_to_default_shell.clone(),
582 self.zellij_cwd.clone(),
583 self.capabilities.clone(),
584 self.client_attributes.clone(),
585 self.default_shell.clone(),
586 self.default_layout.clone(),
587 self.layout_dir.clone(),
588 self.default_mode,
589 self.keybinds
590 .get(&client_id)
591 .cloned()
592 .unwrap_or_else(|| self.default_keybinds.clone()),
593 ) {
594 Ok(_) => {
595 let _ = self
596 .senders
597 .send_to_screen(ScreenInstruction::RequestStateUpdateForPlugins);
598 Ok(())
599 },
600 Err(e) => Err(e),
601 }
602 }
603 pub fn resize_plugin(
604 &mut self,
605 pid: PluginId,
606 new_columns: usize,
607 new_rows: usize,
608 shutdown_sender: Sender<()>,
609 ) -> Result<()> {
610 let err_context = move || format!("failed to resize plugin {pid}");
611
612 let plugins_to_resize: Vec<(PluginId, ClientId, Arc<Mutex<RunningPlugin>>)> = self
613 .plugin_map
614 .lock()
615 .unwrap()
616 .running_plugins()
617 .iter()
618 .cloned()
619 .filter(|(plugin_id, _client_id, _running_plugin)| {
620 !self
621 .cached_resizes_for_pending_plugins
622 .contains_key(&plugin_id)
623 })
624 .collect();
625 for (plugin_id, client_id, running_plugin) in plugins_to_resize {
626 if plugin_id == pid {
627 let event_id = running_plugin
628 .lock()
629 .unwrap()
630 .next_event_id(AtomicEvent::Resize);
631 task::spawn({
632 let senders = self.senders.clone();
633 let running_plugin = running_plugin.clone();
634 let _s = shutdown_sender.clone();
635 async move {
636 let mut running_plugin = running_plugin.lock().unwrap();
637 let _s = _s; if running_plugin.apply_event_id(AtomicEvent::Resize, event_id) {
639 let old_rows = running_plugin.rows;
640 let old_columns = running_plugin.columns;
641 running_plugin.rows = new_rows;
642 running_plugin.columns = new_columns;
643
644 if old_rows != new_rows || old_columns != new_columns || event_id == 0 {
647 let rendered_bytes = running_plugin
648 .instance
649 .clone()
650 .get_typed_func::<(i32, i32), ()>(
651 &mut running_plugin.store,
652 "render",
653 )
654 .and_then(|render| {
655 render.call(
656 &mut running_plugin.store,
657 (new_rows as i32, new_columns as i32),
658 )
659 })
660 .and_then(|_| wasi_read_string(running_plugin.store.data()))
661 .with_context(err_context);
662 match rendered_bytes {
663 Ok(rendered_bytes) => {
664 let plugin_render_asset = PluginRenderAsset::new(
665 plugin_id,
666 client_id,
667 rendered_bytes.as_bytes().to_vec(),
668 );
669 senders
670 .send_to_screen(ScreenInstruction::PluginBytes(vec![
671 plugin_render_asset,
672 ]))
673 .unwrap();
674 },
675 Err(e) => log::error!("{}", e),
676 }
677 }
678 }
679 }
680 });
681 }
682 }
683 for (plugin_id, current_size) in self.cached_resizes_for_pending_plugins.iter_mut() {
684 if *plugin_id == pid {
685 current_size.0 = new_rows;
686 current_size.1 = new_columns;
687 }
688 }
689 Ok(())
690 }
691 pub fn update_plugins(
692 &mut self,
693 mut updates: Vec<(Option<PluginId>, Option<ClientId>, Event)>,
694 shutdown_sender: Sender<()>,
695 ) -> Result<()> {
696 let plugins_to_update: Vec<(
697 PluginId,
698 ClientId,
699 Arc<Mutex<RunningPlugin>>,
700 Arc<Mutex<Subscriptions>>,
701 )> = self
702 .plugin_map
703 .lock()
704 .unwrap()
705 .running_plugins_and_subscriptions()
706 .iter()
707 .cloned()
708 .filter(|(plugin_id, _client_id, _running_plugin, _subscriptions)| {
709 !&self
710 .cached_events_for_pending_plugins
711 .contains_key(&plugin_id)
712 })
713 .collect();
714 task::spawn({
715 let mut updates = updates.clone();
716 let senders = self.senders.clone();
717 let s = shutdown_sender.clone();
718 async move {
719 let _s = s;
720 for (pid, cid, event) in updates.drain(..) {
721 for (plugin_id, client_id, running_plugin, subscriptions) in &plugins_to_update
722 {
723 let subs = subscriptions.lock().unwrap().clone();
724 if let Ok(event_type) = EventType::from_str(&event.to_string()) {
726 if (subs.contains(&event_type)
727 || event_type == EventType::PermissionRequestResult)
728 && Self::message_is_directed_at_plugin(
729 pid, cid, plugin_id, client_id,
730 )
731 {
732 let mut running_plugin = running_plugin.lock().unwrap();
733 let mut plugin_render_assets = vec![];
734 match apply_event_to_plugin(
735 *plugin_id,
736 *client_id,
737 &mut running_plugin,
738 &event,
739 &mut plugin_render_assets,
740 senders.clone(),
741 ) {
742 Ok(()) => {
743 let _ = senders.send_to_screen(
744 ScreenInstruction::PluginBytes(plugin_render_assets),
745 );
746 },
747 Err(e) => {
748 log::error!("{:?}", e);
749
750 let stringified_error =
752 format!("{:?}", e).replace("\n", "\n\r");
753
754 handle_plugin_crash(
755 *plugin_id,
756 stringified_error,
757 senders.clone(),
758 );
759 },
760 }
761 }
762 }
763 }
764 }
765 }
766 });
767 for (pid, _cid, event) in updates.drain(..) {
770 for (plugin_id, cached_events) in self.cached_events_for_pending_plugins.iter_mut() {
771 if pid.is_none() || pid.as_ref() == Some(plugin_id) {
772 cached_events.push(EventOrPipeMessage::Event(event.clone()));
773 }
774 }
775 }
776 Ok(())
777 }
778 pub fn get_plugin_cwd(&self, plugin_id: PluginId, client_id: ClientId) -> Option<PathBuf> {
779 self.plugin_map
780 .lock()
781 .unwrap()
782 .running_plugins()
783 .iter()
784 .find_map(|(p_id, c_id, running_plugin)| {
785 if p_id == &plugin_id && c_id == &client_id {
786 let plugin_cwd = running_plugin
787 .lock()
788 .unwrap()
789 .store
790 .data()
791 .plugin_cwd
792 .clone();
793 Some(plugin_cwd)
794 } else {
795 None
796 }
797 })
798 }
799 pub fn change_plugin_host_dir(
800 &mut self,
801 new_host_dir: PathBuf,
802 plugin_id_to_update: PluginId,
803 client_id_to_update: ClientId,
804 ) -> Result<()> {
805 let plugins_to_change: Vec<(
806 PluginId,
807 ClientId,
808 Arc<Mutex<RunningPlugin>>,
809 Arc<Mutex<Subscriptions>>,
810 )> = self
811 .plugin_map
812 .lock()
813 .unwrap()
814 .running_plugins_and_subscriptions()
815 .iter()
816 .cloned()
817 .collect();
818 task::spawn({
819 let senders = self.senders.clone();
820 async move {
821 match new_host_dir.try_exists() {
822 Ok(false) => {
823 log::error!(
824 "Failed to change folder to {},: folder does not exist",
825 new_host_dir.display()
826 );
827 let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
828 Some(plugin_id_to_update),
829 Some(client_id_to_update),
830 Event::FailedToChangeHostFolder(Some(format!(
831 "Folder {} does not exist",
832 new_host_dir.display()
833 ))),
834 )]));
835 return;
836 },
837 Err(e) => {
838 log::error!(
839 "Failed to change folder to {},: {}",
840 new_host_dir.display(),
841 e
842 );
843 let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
844 Some(plugin_id_to_update),
845 Some(client_id_to_update),
846 Event::FailedToChangeHostFolder(Some(e.to_string())),
847 )]));
848 return;
849 },
850 _ => {},
851 }
852 for (plugin_id, client_id, running_plugin, _subscriptions) in &plugins_to_change {
853 if plugin_id == &plugin_id_to_update && client_id == &client_id_to_update {
854 let mut running_plugin = running_plugin.lock().unwrap();
855 let plugin_env = running_plugin.store.data_mut();
856 let stdin_pipe = plugin_env.stdin_pipe.clone();
857 let stdout_pipe = plugin_env.stdout_pipe.clone();
858 let wasi_ctx = PluginLoader::create_wasi_ctx(
859 &new_host_dir,
860 &plugin_env.plugin_own_data_dir,
861 &plugin_env.plugin_own_cache_dir,
862 &ZELLIJ_TMP_DIR,
863 &plugin_env.plugin.location.to_string(),
864 plugin_env.plugin_id,
865 stdin_pipe.clone(),
866 stdout_pipe.clone(),
867 );
868 match wasi_ctx {
869 Ok(wasi_ctx) => {
870 drop(std::mem::replace(&mut plugin_env.wasi_ctx, wasi_ctx));
871 plugin_env.plugin_cwd = new_host_dir.clone();
872
873 let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
874 Some(*plugin_id),
875 Some(*client_id),
876 Event::HostFolderChanged(new_host_dir.clone()),
877 )]));
878 },
879 Err(e) => {
880 let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
881 Some(*plugin_id),
882 Some(*client_id),
883 Event::FailedToChangeHostFolder(Some(e.to_string())),
884 )]));
885 log::error!("Failed to create wasi ctx: {}", e);
886 },
887 }
888 }
889 }
890 }
891 });
892 Ok(())
893 }
894 pub fn pipe_messages(
895 &mut self,
896 mut messages: Vec<(Option<PluginId>, Option<ClientId>, PipeMessage)>,
897 shutdown_sender: Sender<()>,
898 ) -> Result<()> {
899 let plugins_to_update: Vec<(
900 PluginId,
901 ClientId,
902 Arc<Mutex<RunningPlugin>>,
903 Arc<Mutex<Subscriptions>>,
904 )> = self
905 .plugin_map
906 .lock()
907 .unwrap()
908 .running_plugins_and_subscriptions()
909 .iter()
910 .cloned()
911 .filter(|(plugin_id, _client_id, _running_plugin, _subscriptions)| {
912 !&self
913 .cached_events_for_pending_plugins
914 .contains_key(&plugin_id)
915 })
916 .collect();
917 for (message_pid, message_cid, pipe_message) in messages.drain(..) {
918 for (plugin_id, client_id, running_plugin, _subscriptions) in &plugins_to_update {
919 if Self::message_is_directed_at_plugin(
920 message_pid,
921 message_cid,
922 plugin_id,
923 client_id,
924 ) {
925 if let PipeSource::Cli(pipe_id) = &pipe_message.source {
926 self.pending_pipes
927 .mark_being_processed(pipe_id, plugin_id, client_id);
928 }
929 task::spawn({
930 let senders = self.senders.clone();
931 let running_plugin = running_plugin.clone();
932 let pipe_message = pipe_message.clone();
933 let plugin_id = *plugin_id;
934 let client_id = *client_id;
935 let _s = shutdown_sender.clone();
936 async move {
937 let mut running_plugin = running_plugin.lock().unwrap();
938 let mut plugin_render_assets = vec![];
939 let _s = _s; match apply_pipe_message_to_plugin(
941 plugin_id,
942 client_id,
943 &mut running_plugin,
944 &pipe_message,
945 &mut plugin_render_assets,
946 &senders,
947 ) {
948 Ok(()) => {
949 let _ = senders.send_to_screen(ScreenInstruction::PluginBytes(
950 plugin_render_assets,
951 ));
952 },
953 Err(e) => {
954 log::error!("{:?}", e);
955
956 let stringified_error =
958 format!("{:?}", e).replace("\n", "\n\r");
959
960 handle_plugin_crash(
961 plugin_id,
962 stringified_error,
963 senders.clone(),
964 );
965 },
966 }
967 }
968 });
969 }
970 }
971 let all_connected_clients: Vec<ClientId> = self
972 .connected_clients
973 .lock()
974 .unwrap()
975 .iter()
976 .copied()
977 .collect();
978 for (plugin_id, cached_events) in self.cached_events_for_pending_plugins.iter_mut() {
979 if message_pid.is_none() || message_pid.as_ref() == Some(plugin_id) {
980 cached_events.push(EventOrPipeMessage::PipeMessage(pipe_message.clone()));
981 if let PipeSource::Cli(pipe_id) = &pipe_message.source {
982 for client_id in &all_connected_clients {
983 if Self::message_is_directed_at_plugin(
984 message_pid,
985 message_cid,
986 plugin_id,
987 client_id,
988 ) {
989 self.pending_pipes
990 .mark_being_processed(pipe_id, plugin_id, client_id);
991 }
992 }
993 }
994 }
995 }
996 }
997 Ok(())
998 }
999 pub fn apply_cached_events(
1000 &mut self,
1001 plugin_ids: Vec<PluginId>,
1002 done_receiving_permissions: bool,
1003 shutdown_sender: Sender<()>,
1004 ) -> Result<()> {
1005 let mut applied_plugin_paths = HashSet::new();
1006 for plugin_id in plugin_ids {
1007 if !done_receiving_permissions
1008 && self
1009 .plugin_ids_waiting_for_permission_request
1010 .contains(&plugin_id)
1011 {
1012 continue;
1013 }
1014 self.plugin_ids_waiting_for_permission_request
1015 .remove(&plugin_id);
1016 self.apply_cached_events_and_resizes_for_plugin(plugin_id, shutdown_sender.clone())?;
1017 if let Some(run_plugin) = self.run_plugin_of_loading_plugin_id(plugin_id) {
1018 applied_plugin_paths.insert(run_plugin.clone());
1019 }
1020 self.loading_plugins
1021 .retain(|(p_id, _run_plugin), _| p_id != &plugin_id);
1022 self.clear_plugin_map_cache();
1023 }
1024 for run_plugin in applied_plugin_paths.drain() {
1025 if self.pending_plugin_reloads.remove(&run_plugin) {
1026 let _ = self.reload_plugin(&run_plugin);
1027 }
1028 }
1029 Ok(())
1030 }
1031 pub fn remove_client(&mut self, client_id: ClientId) {
1032 self.connected_clients
1033 .lock()
1034 .unwrap()
1035 .retain(|c| c != &client_id);
1036 }
1037 pub fn cleanup(&mut self) {
1038 for (_plugin_id, loading_plugin_task) in self.loading_plugins.drain() {
1039 drop(loading_plugin_task.cancel());
1040 }
1041 let plugin_ids = self.plugin_map.lock().unwrap().plugin_ids();
1042 for plugin_id in &plugin_ids {
1043 drop(self.unload_plugin(*plugin_id));
1044 }
1045 if let Some(watcher) = self.watcher.take() {
1046 watcher.stop_nonblocking();
1047 }
1048 }
1049 pub fn run_plugin_of_loading_plugin_id(&self, plugin_id: PluginId) -> Option<&RunPlugin> {
1050 self.loading_plugins
1051 .iter()
1052 .find(|((p_id, _run_plugin), _)| p_id == &plugin_id)
1053 .map(|((_p_id, run_plugin), _)| run_plugin)
1054 }
1055 pub fn run_plugin_of_plugin_id(&self, plugin_id: PluginId) -> Option<RunPlugin> {
1056 self.plugin_map
1057 .lock()
1058 .unwrap()
1059 .run_plugin_of_plugin_id(plugin_id)
1060 }
1061
1062 pub fn reconfigure(
1063 &mut self,
1064 client_id: ClientId,
1065 keybinds: Option<Keybinds>,
1066 default_mode: Option<InputMode>,
1067 default_shell: Option<TerminalAction>,
1068 ) -> Result<()> {
1069 let plugins_to_reconfigure: Vec<Arc<Mutex<RunningPlugin>>> = self
1070 .plugin_map
1071 .lock()
1072 .unwrap()
1073 .running_plugins()
1074 .iter()
1075 .cloned()
1076 .filter_map(|(_plugin_id, c_id, running_plugin)| {
1077 if c_id == client_id {
1078 Some(running_plugin.clone())
1079 } else {
1080 None
1081 }
1082 })
1083 .collect();
1084 if let Some(default_mode) = default_mode.as_ref() {
1085 self.base_modes.insert(client_id, *default_mode);
1086 }
1087 if let Some(keybinds) = keybinds.as_ref() {
1088 self.keybinds.insert(client_id, keybinds.clone());
1089 }
1090 self.default_shell = default_shell.clone();
1091 for running_plugin in plugins_to_reconfigure {
1092 task::spawn({
1093 let running_plugin = running_plugin.clone();
1094 let keybinds = keybinds.clone();
1095 let default_shell = default_shell.clone();
1096 async move {
1097 let mut running_plugin = running_plugin.lock().unwrap();
1098 if let Some(keybinds) = keybinds {
1099 running_plugin.update_keybinds(keybinds);
1100 }
1101 if let Some(default_mode) = default_mode {
1102 running_plugin.update_default_mode(default_mode);
1103 }
1104 running_plugin.update_default_shell(default_shell);
1105 }
1106 });
1107 }
1108 Ok(())
1109 }
1110 fn apply_cached_events_and_resizes_for_plugin(
1111 &mut self,
1112 plugin_id: PluginId,
1113 shutdown_sender: Sender<()>,
1114 ) -> Result<()> {
1115 let err_context = || format!("Failed to apply cached events to plugin");
1116 if let Some(events_or_pipe_messages) =
1117 self.cached_events_for_pending_plugins.remove(&plugin_id)
1118 {
1119 let all_connected_clients: Vec<ClientId> = self
1120 .connected_clients
1121 .lock()
1122 .unwrap()
1123 .iter()
1124 .copied()
1125 .collect();
1126 for client_id in &all_connected_clients {
1127 if let Some((running_plugin, subscriptions)) = self
1128 .plugin_map
1129 .lock()
1130 .unwrap()
1131 .get_running_plugin_and_subscriptions(plugin_id, *client_id)
1132 {
1133 task::spawn({
1134 let senders = self.senders.clone();
1135 let running_plugin = running_plugin.clone();
1136 let client_id = *client_id;
1137 let _s = shutdown_sender.clone();
1138 let events_or_pipe_messages = events_or_pipe_messages.clone();
1139 async move {
1140 let subs = subscriptions.lock().unwrap().clone();
1141 let _s = _s; for event_or_pipe_message in events_or_pipe_messages {
1143 match event_or_pipe_message {
1144 EventOrPipeMessage::Event(event) => {
1145 match EventType::from_str(&event.to_string())
1146 .with_context(err_context)
1147 {
1148 Ok(event_type) => {
1149 if !subs.contains(&event_type) {
1150 continue;
1151 }
1152 let mut running_plugin =
1153 running_plugin.lock().unwrap();
1154 let mut plugin_render_assets = vec![];
1155 match apply_event_to_plugin(
1156 plugin_id,
1157 client_id,
1158 &mut running_plugin,
1159 &event,
1160 &mut plugin_render_assets,
1161 senders.clone(),
1162 ) {
1163 Ok(()) => {
1164 let _ = senders.send_to_screen(
1165 ScreenInstruction::PluginBytes(
1166 plugin_render_assets,
1167 ),
1168 );
1169 },
1170 Err(e) => {
1171 log::error!("{}", e);
1172 },
1173 }
1174 },
1175 Err(e) => {
1176 log::error!("Failed to apply event: {:?}", e);
1177 },
1178 }
1179 },
1180 EventOrPipeMessage::PipeMessage(pipe_message) => {
1181 let mut running_plugin = running_plugin.lock().unwrap();
1182 let mut plugin_render_assets = vec![];
1183
1184 match apply_pipe_message_to_plugin(
1185 plugin_id,
1186 client_id,
1187 &mut running_plugin,
1188 &pipe_message,
1189 &mut plugin_render_assets,
1190 &senders,
1191 ) {
1192 Ok(()) => {
1193 let _ = senders.send_to_screen(
1194 ScreenInstruction::PluginBytes(
1195 plugin_render_assets,
1196 ),
1197 );
1198 },
1199 Err(e) => {
1200 log::error!("{:?}", e);
1201
1202 let stringified_error =
1204 format!("{:?}", e).replace("\n", "\n\r");
1205
1206 handle_plugin_crash(
1207 plugin_id,
1208 stringified_error,
1209 senders.clone(),
1210 );
1211 },
1212 }
1213 },
1214 }
1215 }
1216 }
1217 });
1218 }
1219 }
1220 }
1221 if let Some((rows, columns)) = self.cached_resizes_for_pending_plugins.remove(&plugin_id) {
1222 self.resize_plugin(plugin_id, columns, rows, shutdown_sender.clone())?;
1223 }
1224 self.apply_cached_worker_messages(plugin_id)?;
1225 Ok(())
1226 }
1227 pub fn apply_cached_worker_messages(&mut self, plugin_id: PluginId) -> Result<()> {
1228 if let Some(mut messages) = self.cached_worker_messages.remove(&plugin_id) {
1229 let mut worker_messages: HashMap<(ClientId, String), Vec<(String, String)>> =
1230 HashMap::new();
1231 for (client_id, worker_name, message, payload) in messages.drain(..) {
1232 worker_messages
1233 .entry((client_id, worker_name))
1234 .or_default()
1235 .push((message, payload));
1236 }
1237 for ((client_id, worker_name), messages) in worker_messages.drain() {
1238 self.post_messages_to_plugin_worker(plugin_id, client_id, worker_name, messages)?;
1239 }
1240 }
1241 Ok(())
1242 }
1243 fn plugin_is_currently_being_loaded(&self, plugin_location: &RunPluginLocation) -> bool {
1244 self.loading_plugins
1245 .iter()
1246 .find(|((_plugin_id, run_plugin), _)| &run_plugin.location == plugin_location)
1247 .is_some()
1248 }
1249 fn plugin_id_of_loading_plugin(
1250 &self,
1251 plugin_location: &RunPluginLocation,
1252 plugin_configuration: &PluginUserConfiguration,
1253 ) -> Option<PluginId> {
1254 self.loading_plugins
1255 .iter()
1256 .find_map(|((plugin_id, run_plugin), _)| {
1257 if &run_plugin.location == plugin_location
1258 && &run_plugin.configuration == plugin_configuration
1259 {
1260 Some(*plugin_id)
1261 } else {
1262 None
1263 }
1264 })
1265 }
1266 fn all_plugin_ids_for_plugin_location(
1267 &self,
1268 plugin_location: &RunPluginLocation,
1269 plugin_configuration: &PluginUserConfiguration,
1270 ) -> Result<Vec<PluginId>> {
1271 self.plugin_map
1272 .lock()
1273 .unwrap()
1274 .all_plugin_ids_for_plugin_location(plugin_location, plugin_configuration)
1275 }
1276 pub fn all_plugin_and_client_ids_for_plugin_location(
1277 &mut self,
1278 plugin_location: &RunPluginLocation,
1279 plugin_configuration: &PluginUserConfiguration,
1280 ) -> Vec<(PluginId, Option<ClientId>)> {
1281 if self.cached_plugin_map.is_empty() {
1282 self.cached_plugin_map = self.plugin_map.lock().unwrap().clone_plugin_assets();
1283 }
1284 match self
1285 .cached_plugin_map
1286 .get(plugin_location)
1287 .and_then(|m| m.get(plugin_configuration))
1288 {
1289 Some(plugin_and_client_ids) => plugin_and_client_ids
1290 .iter()
1291 .map(|(plugin_id, client_id)| (*plugin_id, Some(*client_id)))
1292 .collect(),
1293 None => vec![],
1294 }
1295 }
1296 pub fn all_plugin_ids(&self) -> Vec<(PluginId, ClientId)> {
1297 self.plugin_map.lock().unwrap().all_plugin_ids()
1298 }
1299 fn size_of_plugin_id(&self, plugin_id: PluginId) -> Option<(usize, usize)> {
1300 self.plugin_map
1302 .lock()
1303 .unwrap()
1304 .get_running_plugin(plugin_id, None)
1305 .map(|r| {
1306 let r = r.lock().unwrap();
1307 (r.rows, r.columns)
1308 })
1309 }
1310 fn start_plugin_loading_indication(
1311 &self,
1312 plugin_ids: &[PluginId],
1313 loading_indication: &LoadingIndication,
1314 ) {
1315 for plugin_id in plugin_ids {
1316 let _ = self
1317 .senders
1318 .send_to_screen(ScreenInstruction::StartPluginLoadingIndication(
1319 *plugin_id,
1320 loading_indication.clone(),
1321 ));
1322 let _ = self
1323 .senders
1324 .send_to_background_jobs(BackgroundJob::AnimatePluginLoading(*plugin_id));
1325 }
1326 }
1327 pub fn post_messages_to_plugin_worker(
1328 &mut self,
1329 plugin_id: PluginId,
1330 client_id: ClientId,
1331 worker_name: String,
1332 mut messages: Vec<(String, String)>,
1333 ) -> Result<()> {
1334 let worker =
1335 self.plugin_map
1336 .lock()
1337 .unwrap()
1338 .worker_sender(plugin_id, client_id, &worker_name);
1339 match worker {
1340 Some(worker) => {
1341 for (message, payload) in messages.drain(..) {
1342 if let Err(e) = worker.try_send(MessageToWorker::Message(message, payload)) {
1343 log::error!("Failed to send message to worker: {:?}", e);
1344 }
1345 }
1346 },
1347 None => {
1348 log::warn!("Worker {worker_name} not found, caching messages");
1349 for (message, payload) in messages.drain(..) {
1350 self.cached_worker_messages
1351 .entry(plugin_id)
1352 .or_default()
1353 .push((client_id, worker_name.clone(), message, payload));
1354 }
1355 },
1356 }
1357 Ok(())
1358 }
1359 pub fn start_fs_watcher_if_not_started(&mut self) {
1360 if self.watcher.is_none() {
1361 self.watcher = match watch_filesystem(self.senders.clone(), &self.zellij_cwd) {
1362 Ok(watcher) => Some(watcher),
1363 Err(e) => {
1364 log::error!("Failed to watch filesystem: {:?}", e);
1365 None
1366 },
1367 };
1368 }
1369 }
1370 pub fn cache_plugin_permissions(
1371 &mut self,
1372 plugin_id: PluginId,
1373 client_id: Option<ClientId>,
1374 permissions: Vec<PermissionType>,
1375 status: PermissionStatus,
1376 cache_path: Option<PathBuf>,
1377 ) -> Result<()> {
1378 let err_context = || format!("Failed to write plugin permission {plugin_id}");
1379
1380 let running_plugin = self
1381 .plugin_map
1382 .lock()
1383 .unwrap()
1384 .get_running_plugin(plugin_id, client_id)
1385 .ok_or_else(|| anyhow!("Failed to get running plugin"))?;
1386
1387 let mut running_plugin = running_plugin.lock().unwrap();
1388
1389 let permissions = if status == PermissionStatus::Granted {
1390 permissions
1391 } else {
1392 vec![]
1393 };
1394
1395 running_plugin
1396 .store
1397 .data_mut()
1398 .set_permissions(HashSet::from_iter(permissions.clone()));
1399
1400 let mut permission_cache = PermissionCache::from_path_or_default(cache_path);
1401 permission_cache.cache(
1402 running_plugin.store.data().plugin.location.to_string(),
1403 permissions,
1404 );
1405
1406 permission_cache.write_to_file().with_context(err_context)
1407 }
1408 pub fn cache_plugin_events(&mut self, plugin_id: PluginId) {
1409 self.plugin_ids_waiting_for_permission_request
1410 .insert(plugin_id);
1411 self.cached_events_for_pending_plugins
1412 .entry(plugin_id)
1413 .or_insert_with(Default::default);
1414 }
1415
1416 pub fn get_or_load_plugins(
1419 &mut self,
1420 run_plugin_or_alias: RunPluginOrAlias,
1421 size: Size,
1422 cwd: Option<PathBuf>,
1423 skip_cache: bool,
1424 should_float: bool,
1425 should_be_open_in_place: bool,
1426 pane_title: Option<String>,
1427 pane_id_to_replace: Option<PaneId>,
1428 cli_client_id: Option<ClientId>,
1429 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
1430 should_focus: bool,
1431 ) -> Vec<(PluginId, Option<ClientId>)> {
1432 let run_plugin = run_plugin_or_alias.get_run_plugin();
1433 match run_plugin {
1434 Some(run_plugin) => {
1435 let all_plugin_ids = self.all_plugin_and_client_ids_for_plugin_location(
1436 &run_plugin.location,
1437 &run_plugin.configuration,
1438 );
1439 if all_plugin_ids.is_empty() {
1440 if let Some(loading_plugin_id) = self.plugin_id_of_loading_plugin(
1441 &run_plugin.location,
1442 &run_plugin.configuration,
1443 ) {
1444 return vec![(loading_plugin_id, None)];
1445 }
1446 match self.load_plugin(
1447 &Some(run_plugin),
1448 None,
1449 size,
1450 cwd.clone(),
1451 skip_cache,
1452 None,
1453 cli_client_id,
1454 ) {
1455 Ok((plugin_id, client_id)) => {
1456 let start_suppressed = false;
1457 drop(self.senders.send_to_screen(ScreenInstruction::AddPlugin(
1458 Some(should_float),
1459 should_be_open_in_place,
1460 run_plugin_or_alias,
1461 pane_title,
1462 None,
1463 plugin_id,
1464 pane_id_to_replace,
1465 cwd,
1466 start_suppressed,
1467 floating_pane_coordinates,
1468 Some(should_focus),
1469 Some(client_id),
1470 )));
1471 vec![(plugin_id, Some(client_id))]
1472 },
1473 Err(e) => {
1474 log::error!("Failed to load plugin: {e}");
1475 if let Some(cli_client_id) = cli_client_id {
1476 let _ = self.senders.send_to_server(ServerInstruction::LogError(
1477 vec![format!("Failed to log plugin: {e}")],
1478 cli_client_id,
1479 ));
1480 }
1481 vec![]
1482 },
1483 }
1484 } else {
1485 all_plugin_ids
1486 }
1487 },
1488 None => {
1489 log::error!("Plugin not found for alias");
1490 vec![]
1491 },
1492 }
1493 }
1494 pub fn clear_plugin_map_cache(&mut self) {
1495 self.cached_plugin_map.clear();
1496 }
1497 pub fn update_cli_pipe_state(
1499 &mut self,
1500 pipe_state_changes: Vec<PluginRenderAsset>,
1501 ) -> Vec<String> {
1502 let mut pipe_names_to_unblock = vec![];
1503 for pipe_state_change in pipe_state_changes {
1504 let client_id = pipe_state_change.client_id;
1505 let plugin_id = pipe_state_change.plugin_id;
1506 for (cli_pipe_name, pipe_state_change) in pipe_state_change.cli_pipes {
1507 pipe_names_to_unblock.append(&mut self.pending_pipes.update_pipe_state_change(
1508 &cli_pipe_name,
1509 pipe_state_change,
1510 &plugin_id,
1511 &client_id,
1512 ));
1513 }
1514 }
1515 let pipe_names_to_unblock =
1516 pipe_names_to_unblock
1517 .into_iter()
1518 .fold(HashSet::new(), |mut acc, p| {
1519 acc.insert(p);
1520 acc
1521 });
1522 pipe_names_to_unblock.into_iter().collect()
1523 }
1524 fn message_is_directed_at_plugin(
1525 message_pid: Option<PluginId>,
1526 message_cid: Option<ClientId>,
1527 plugin_id: &PluginId,
1528 client_id: &ClientId,
1529 ) -> bool {
1530 message_pid.is_none() && message_cid.is_none()
1531 || (message_pid.is_none() && message_cid == Some(*client_id))
1532 || (message_cid.is_none() && message_pid == Some(*plugin_id))
1533 || (message_cid == Some(*client_id) && message_pid == Some(*plugin_id))
1534 }
1535 pub fn client_is_connected(&self, client_id: &ClientId) -> bool {
1536 self.connected_clients.lock().unwrap().contains(client_id)
1537 }
1538 pub fn get_first_client_id(&self) -> Option<ClientId> {
1539 self.connected_clients
1540 .lock()
1541 .unwrap()
1542 .iter()
1543 .next()
1544 .copied()
1545 }
1546}
1547
1548fn handle_plugin_successful_loading(
1549 senders: &ThreadSenders,
1550 plugin_id: PluginId,
1551 plugin_list: BTreeMap<PluginId, RunPlugin>,
1552) {
1553 let _ = senders.send_to_background_jobs(BackgroundJob::StopPluginLoadingAnimation(plugin_id));
1554 let _ = senders.send_to_screen(ScreenInstruction::RequestStateUpdateForPlugins);
1555 let _ = senders.send_to_background_jobs(BackgroundJob::ReportPluginList(plugin_list));
1556}
1557
1558fn handle_plugin_loading_failure(
1559 senders: &ThreadSenders,
1560 plugin_id: PluginId,
1561 loading_indication: &mut LoadingIndication,
1562 error: impl std::fmt::Debug,
1563 cli_client_id: Option<ClientId>,
1564) {
1565 log::error!("{:?}", error);
1566 let _ = senders.send_to_background_jobs(BackgroundJob::StopPluginLoadingAnimation(plugin_id));
1567 loading_indication.indicate_loading_error(format!("{:?}", error));
1568 let _ = senders.send_to_screen(ScreenInstruction::UpdatePluginLoadingStage(
1569 plugin_id,
1570 loading_indication.clone(),
1571 ));
1572 if let Some(cli_client_id) = cli_client_id {
1573 let _ = senders.send_to_server(ServerInstruction::LogError(
1574 vec![format!("{:?}", error)],
1575 cli_client_id,
1576 ));
1577 }
1578}
1579
1580fn check_event_permission(
1582 plugin_env: &PluginEnv,
1583 event: &Event,
1584) -> (PermissionStatus, Option<PermissionType>) {
1585 if plugin_env.plugin.is_builtin() {
1586 return (PermissionStatus::Granted, None);
1589 }
1590 let permission = match event {
1591 Event::ModeUpdate(..)
1592 | Event::TabUpdate(..)
1593 | Event::PaneUpdate(..)
1594 | Event::SessionUpdate(..)
1595 | Event::CopyToClipboard(..)
1596 | Event::SystemClipboardFailure
1597 | Event::CommandPaneOpened(..)
1598 | Event::CommandPaneExited(..)
1599 | Event::PaneClosed(..)
1600 | Event::EditPaneOpened(..)
1601 | Event::EditPaneExited(..)
1602 | Event::FailedToWriteConfigToDisk(..)
1603 | Event::CommandPaneReRun(..)
1604 | Event::InputReceived => PermissionType::ReadApplicationState,
1605 Event::WebServerStatus(..) => PermissionType::StartWebServer,
1606 _ => return (PermissionStatus::Granted, None),
1607 };
1608
1609 if let Some(permissions) = plugin_env.permissions.lock().unwrap().as_ref() {
1610 if permissions.contains(&permission) {
1611 return (PermissionStatus::Granted, None);
1612 }
1613 }
1614
1615 (PermissionStatus::Denied, Some(permission))
1616}
1617
1618pub fn apply_event_to_plugin(
1619 plugin_id: PluginId,
1620 client_id: ClientId,
1621 running_plugin: &mut RunningPlugin,
1622 event: &Event,
1623 plugin_render_assets: &mut Vec<PluginRenderAsset>,
1624 senders: ThreadSenders,
1625) -> Result<()> {
1626 let instance = &running_plugin.instance;
1627 let rows = running_plugin.rows;
1628 let columns = running_plugin.columns;
1629
1630 let err_context = || format!("Failed to apply event to plugin {plugin_id}");
1631 match check_event_permission(running_plugin.store.data(), event) {
1632 (PermissionStatus::Granted, _) => {
1633 let mut event = event.clone();
1634 if let Event::ModeUpdate(mode_info) = &mut event {
1635 mode_info.keybinds = running_plugin.store.data().keybinds.to_keybinds_vec();
1643 mode_info.base_mode = Some(running_plugin.store.data().default_mode);
1644 }
1645 let protobuf_event: ProtobufEvent = event
1646 .clone()
1647 .try_into()
1648 .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
1649 let update = instance
1650 .get_typed_func::<(), i32>(&mut running_plugin.store, "update")
1651 .with_context(err_context)?;
1652 wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec())
1653 .with_context(err_context)?;
1654 let should_render = update
1655 .call(&mut running_plugin.store, ())
1656 .with_context(err_context)?;
1657 let mut should_render = should_render == 1;
1658 if let Event::PermissionRequestResult(..) = event {
1659 should_render = true;
1662 }
1663 if rows > 0 && columns > 0 && should_render {
1664 let rendered_bytes = instance
1665 .get_typed_func::<(i32, i32), ()>(&mut running_plugin.store, "render")
1666 .and_then(|render| {
1667 render.call(&mut running_plugin.store, (rows as i32, columns as i32))
1668 })
1669 .and_then(|_| wasi_read_string(running_plugin.store.data()))
1670 .with_context(err_context)?;
1671 let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
1672 let plugin_render_asset = PluginRenderAsset::new(
1673 plugin_id,
1674 client_id,
1675 rendered_bytes.as_bytes().to_vec(),
1676 )
1677 .with_pipes(pipes_to_block_or_unblock);
1678 plugin_render_assets.push(plugin_render_asset);
1679 } else {
1680 let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
1683 let plugin_render_asset = PluginRenderAsset::new(plugin_id, client_id, vec![])
1684 .with_pipes(pipes_to_block_or_unblock);
1685 let _ = senders
1686 .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![
1687 plugin_render_asset,
1688 ]))
1689 .context("failed to unblock input pipe");
1690 }
1691 },
1692 (PermissionStatus::Denied, permission) => {
1693 log::error!(
1694 "PluginId '{}' permission '{}' is not allowed - Event '{:?}' denied",
1695 plugin_id,
1696 permission
1697 .map(|p| p.to_string())
1698 .unwrap_or("UNKNOWN".to_owned()),
1699 EventType::from_str(&event.to_string()).with_context(err_context)?
1700 );
1701 },
1702 }
1703 Ok(())
1704}
1705
1706pub fn handle_plugin_crash(plugin_id: PluginId, message: String, senders: ThreadSenders) {
1707 let mut loading_indication = LoadingIndication::new("Panic!".to_owned());
1708 loading_indication.indicate_loading_error(message);
1709 let _ = senders.send_to_screen(ScreenInstruction::UpdatePluginLoadingStage(
1710 plugin_id,
1711 loading_indication,
1712 ));
1713}
1714
1715pub fn apply_before_close_event_to_plugin(
1716 plugin_id: PluginId,
1717 client_id: ClientId,
1718 running_plugin: &mut RunningPlugin,
1719 senders: ThreadSenders,
1720) -> Result<()> {
1721 let instance = &running_plugin.instance;
1722
1723 let err_context = || format!("Failed to apply event to plugin {plugin_id}");
1724 let event = Event::BeforeClose;
1725 let protobuf_event: ProtobufEvent = event
1726 .clone()
1727 .try_into()
1728 .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
1729 let update = instance
1730 .get_typed_func::<(), i32>(&mut running_plugin.store, "update")
1731 .with_context(err_context)?;
1732 wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec())
1733 .with_context(err_context)?;
1734 let _should_render = update
1735 .call(&mut running_plugin.store, ())
1736 .with_context(err_context)?;
1737 let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
1738 let plugin_render_asset =
1739 PluginRenderAsset::new(plugin_id, client_id, vec![]).with_pipes(pipes_to_block_or_unblock);
1740 let _ = senders
1741 .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![
1742 plugin_render_asset,
1743 ]))
1744 .context("failed to unblock input pipe");
1745 Ok(())
1746}