summaryrefslogtreecommitdiffstats
path: root/zellij-server
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2023-08-09 22:26:00 +0200
committerGitHub <noreply@github.com>2023-08-09 22:26:00 +0200
commit1bedfc90021558cb201695444107afe5bddd2c17 (patch)
tree38dd31b5ab112aa9b1c3a54edb07331013bf7d87 /zellij-server
parentc3e140cb4b3c0897329bf07ee7f51e9fd402b3df (diff)
feat(plugins): use protocol buffers for serializing across the wasm boundary (#2686)
* work * almost done with command protobuffers * done translating command data structures * mid transferring of every command to protobuff command * transferred plugin_command.rs, now moving on to shim.rs * plugin command working with protobufs * protobuffers in update * protobuf event tests * various TODOs and comments * fix zellij-tile * clean up prost deps * remove version mismatch error * fix panic * some cleanups * clean up event protobuffers * clean up command protobuffers * clean up various protobufs * refactor protobufs * update comments * some transformation fixes * use protobufs for workers * style(fmt): rustfmt * style(fmt): rustfmt * chore(build): add protoc * chore(build): authenticate protoc
Diffstat (limited to 'zellij-server')
-rw-r--r--zellij-server/src/plugins/plugin_loader.rs131
-rw-r--r--zellij-server/src/plugins/plugin_worker.rs31
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap4
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap4
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap4
-rw-r--r--zellij-server/src/plugins/wasm_bridge.rs29
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs1045
7 files changed, 494 insertions, 754 deletions
diff --git a/zellij-server/src/plugins/plugin_loader.rs b/zellij-server/src/plugins/plugin_loader.rs
index 73957cf7c..92a4c0480 100644
--- a/zellij-server/src/plugins/plugin_loader.rs
+++ b/zellij-server/src/plugins/plugin_loader.rs
@@ -1,27 +1,28 @@
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, wasi_write_object, zellij_exports};
+use crate::plugins::zellij_exports::{wasi_write_object, zellij_exports};
use crate::plugins::PluginId;
use highway::{HighwayHash, PortableHash};
use log::info;
-use semver::Version;
use std::{
collections::{HashMap, HashSet},
- fmt, fs,
+ fs,
path::PathBuf,
sync::{Arc, Mutex},
};
use url::Url;
use wasmer::{ChainableNamedResolver, Instance, Module, Store};
use wasmer_wasi::{Pipe, WasiState};
+use zellij_utils::prost::Message;
use crate::{
logging_pipe::LoggingPipe, screen::ScreenInstruction, thread_bus::ThreadSenders,
ui::loading_indication::LoadingIndication, ClientId,
};
+use zellij_utils::plugin_api::action::ProtobufPluginConfiguration;
use zellij_utils::{
- consts::{VERSION, ZELLIJ_CACHE_DIR, ZELLIJ_SESSION_CACHE_DIR, ZELLIJ_TMP_DIR},
+ consts::{ZELLIJ_CACHE_DIR, ZELLIJ_SESSION_CACHE_DIR, ZELLIJ_TMP_DIR},
data::PluginCapabilities,
errors::prelude::*,
input::command::TerminalAction,
@@ -43,116 +44,6 @@ macro_rules! display_loading_stage {
}};
}
-/// Custom error for plugin version mismatch.
-///
-/// This is thrown when, during starting a plugin, it is detected that the plugin version doesn't
-/// match the zellij version. This is treated as a fatal error and leads to instantaneous
-/// termination.
-#[derive(Debug)]
-pub struct VersionMismatchError {
- zellij_version: String,
- plugin_version: String,
- plugin_path: PathBuf,
- // true for builtin plugins
- builtin: bool,
-}
-
-impl std::error::Error for VersionMismatchError {}
-
-impl VersionMismatchError {
- pub fn new(
- zellij_version: &str,
- plugin_version: &str,
- plugin_path: &PathBuf,
- builtin: bool,
- ) -> Self {
- VersionMismatchError {
- zellij_version: zellij_version.to_owned(),
- plugin_version: plugin_version.to_owned(),
- plugin_path: plugin_path.to_owned(),
- builtin,
- }
- }
-}
-
-impl fmt::Display for VersionMismatchError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let first_line = if self.builtin {
- "It seems your version of zellij was built with outdated core plugins."
- } else {
- "If you're seeing this error a plugin version doesn't match the current
-zellij version."
- };
-
- write!(
- f,
- "{}
-Detected versions:
-
-- Plugin version: {}
-- Zellij version: {}
-- Offending plugin: {}
-
-If you're a user:
- Please contact the distributor of your zellij version and report this error
- to them.
-
-If you're a developer:
- Please run zellij with updated plugins. The easiest way to achieve this
- is to build zellij with `cargo xtask install`. Also refer to the docs:
- https://github.com/zellij-org/zellij/blob/main/CONTRIBUTING.md#building
-",
- first_line,
- self.plugin_version.trim_end(),
- self.zellij_version.trim_end(),
- self.plugin_path.display()
- )
- }
-}
-
-// Returns `Ok` if the plugin version matches the zellij version.
-// Returns an `Err` otherwise.
-fn assert_plugin_version(instance: &Instance, plugin_env: &PluginEnv) -> Result<()> {
- let err_context = || {
- format!(
- "failed to determine plugin version for plugin {}",
- plugin_env.plugin.path.display()
- )
- };
-
- let plugin_version_func = match instance.exports.get_function("plugin_version") {
- Ok(val) => val,
- Err(_) => {
- return Err(anyError::new(VersionMismatchError::new(
- VERSION,
- "Unavailable",
- &plugin_env.plugin.path,
- plugin_env.plugin.is_builtin(),
- )))
- },
- };
-
- let plugin_version = plugin_version_func
- .call(&[])
- .map_err(anyError::new)
- .and_then(|_| wasi_read_string(&plugin_env.wasi_env))
- .and_then(|string| Version::parse(&string).context("failed to parse plugin version"))
- .with_context(err_context)?;
- let zellij_version = Version::parse(VERSION)
- .context("failed to parse zellij version")
- .with_context(err_context)?;
- if plugin_version != zellij_version {
- return Err(anyError::new(VersionMismatchError::new(
- VERSION,
- &plugin_version.to_string(),
- &plugin_env.plugin.path,
- plugin_env.plugin.is_builtin(),
- )));
- }
-
- Ok(())
-}
-
pub struct PluginLoader<'a> {
plugin_cache: Arc<Mutex<HashMap<PathBuf, Module>>>,
plugin_path: PathBuf,
@@ -645,10 +536,8 @@ impl<'a> PluginLoader<'a> {
&mut self,
module: Module,
) -> Result<(Instance, PluginEnv, Arc<Mutex<Subscriptions>>)> {
- let err_context = || format!("Failed to create environment for plugin");
let (instance, plugin_env, subscriptions) =
self.create_plugin_instance_env_and_subscriptions(&module)?;
- assert_plugin_version(&instance, &plugin_env).with_context(err_context)?;
// Only do an insert when everything went well!
let cloned_plugin = self.plugin.clone();
self.plugin_cache
@@ -724,9 +613,17 @@ impl<'a> PluginLoader<'a> {
}
start_function.call(&[]).with_context(err_context)?;
+ let protobuf_plugin_configuration: ProtobufPluginConfiguration = self
+ .plugin
+ .userspace_configuration
+ .clone()
+ .try_into()
+ .map_err(|e| anyhow!("Failed to serialize user configuration: {:?}", e))?;
+ let protobuf_bytes = protobuf_plugin_configuration.encode_to_vec();
wasi_write_object(
&plugin_env.wasi_env,
- &self.plugin.userspace_configuration.inner(),
+ &protobuf_bytes,
+ // &self.plugin.userspace_configuration.inner(),
)
.with_context(err_context)?;
load_function.call(&[]).with_context(err_context)?;
diff --git a/zellij-server/src/plugins/plugin_worker.rs b/zellij-server/src/plugins/plugin_worker.rs
index bc7303c7c..9aae0bab8 100644
--- a/zellij-server/src/plugins/plugin_worker.rs
+++ b/zellij-server/src/plugins/plugin_worker.rs
@@ -1,4 +1,3 @@
-use crate::plugins::plugin_loader::VersionMismatchError;
use crate::plugins::plugin_map::PluginEnv;
use crate::plugins::zellij_exports::wasi_write_object;
use wasmer::Instance;
@@ -6,7 +5,9 @@ use wasmer::Instance;
use zellij_utils::async_channel::{unbounded, Receiver, Sender};
use zellij_utils::async_std::task;
use zellij_utils::errors::prelude::*;
-use zellij_utils::{consts::VERSION, input::plugins::PluginConfig};
+use zellij_utils::input::plugins::PluginConfig;
+use zellij_utils::plugin_api::message::ProtobufMessage;
+use zellij_utils::prost::Message;
pub struct RunningWorker {
pub instance: Instance,
@@ -31,29 +32,19 @@ impl RunningWorker {
}
pub fn send_message(&self, message: String, payload: String) -> Result<()> {
let err_context = || format!("Failed to send message to worker");
-
+ let protobuf_message = ProtobufMessage {
+ name: message,
+ payload,
+ ..Default::default()
+ };
+ let protobuf_bytes = protobuf_message.encode_to_vec();
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),
- }
- })?;
-
+ wasi_write_object(&self.plugin_env.wasi_env, &protobuf_bytes).with_context(err_context)?;
+ work_function.call(&[]).with_context(err_context)?;
Ok(())
}
}
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap
index 7934131f4..a66449c60 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap
@@ -1,11 +1,11 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 2334
+assertion_line: 2709
expression: "format!(\"{:#?}\", new_tab_event)"
---
Some(
GoToTabName(
- "my tab name\n\r",
+ "my tab name",
(
[],
[],
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap
index 01cb4b6b4..cfc94db2a 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap
@@ -1,11 +1,11 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 4220
+assertion_line: 4225
expression: "format!(\"{:#?}\", go_to_tab_event)"
---
Some(
GoToTabName(
- "{\"fake_config_key_1\": \"fake_config_value_1\", \"fake_config_key_2\": \"fake_config_value_2\"}\n\r",
+ "{\"fake_config_key_1\": \"fake_config_value_1\", \"fake_config_key_2\": \"fake_config_value_2\"}",
(
[],
[],
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap
index 504134d01..8c881def6 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 1171
+assertion_line: 1449
expression: "format!(\"{:#?}\", new_tab_event)"
---
Some(
@@ -9,8 +9,6 @@ Some(
102,
111,
111,
- 10,
- 13,
],
1,
),
diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs
index 5ae23be4f..d6fcf1fc6 100644
--- a/zellij-server/src/plugins/wasm_bridge.rs
+++ b/zellij-server/src/plugins/wasm_bridge.rs
@@ -1,5 +1,5 @@
use super::{PluginId, PluginInstruction};
-use crate::plugins::plugin_loader::{PluginLoader, VersionMismatchError};
+use crate::plugins::plugin_loader::PluginLoader;
use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugin, Subscriptions};
use crate::plugins::plugin_worker::MessageToWorker;
use crate::plugins::watch_filesystem::watch_filesystem;
@@ -14,13 +14,15 @@ use std::{
use wasmer::{Instance, Module, Store, Value};
use zellij_utils::async_std::task::{self, JoinHandle};
use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
+use zellij_utils::plugin_api::event::ProtobufEvent;
+
+use zellij_utils::prost::Message;
use crate::{
background_jobs::BackgroundJob, screen::ScreenInstruction, thread_bus::ThreadSenders,
ui::loading_indication::LoadingIndication, ClientId,
};
use zellij_utils::{
- consts::VERSION,
data::{Event, EventType, PluginCapabilities},
errors::prelude::*,
input::{
@@ -737,26 +739,17 @@ pub fn apply_event_to_plugin(
plugin_bytes: &mut Vec<(PluginId, ClientId, Vec<u8>)>,
) -> Result<()> {
let err_context = || format!("Failed to apply event to plugin {plugin_id}");
+ let protobuf_event: ProtobufEvent = event
+ .clone()
+ .try_into()
+ .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
let update = instance
.exports
.get_function("update")
.with_context(err_context)?;
- wasi_write_object(&plugin_env.wasi_env, &event).with_context(err_context)?;
- let update_return =
- update
- .call(&[])
- .or_else::<anyError, _>(|e| match e.downcast::<serde_json::Error>() {
- Ok(_) => panic!(
- "{}",
- anyError::new(VersionMismatchError::new(
- VERSION,
- "Unavailable",
- &plugin_env.plugin.path,
- plugin_env.plugin.is_builtin(),
- ))
- ),
- Err(e) => Err(e).with_context(err_context),
- })?;
+ wasi_write_object(&plugin_env.wasi_env, &protobuf_event.encode_to_vec())
+ .with_context(err_context)?;
+ let update_return = update.call(&[]).with_context(err_context)?;
let should_render = match update_return.get(0) {
Some(Value::I32(n)) => *n == 1,
_ => false,
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index c73068470..275de13e6 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -3,7 +3,7 @@ use crate::plugins::plugin_map::{PluginEnv, Subscriptions};
use crate::plugins::wasm_bridge::handle_plugin_crash;
use crate::route::route_action;
use log::{debug, warn};
-use serde::{de::DeserializeOwned, Serialize};
+use serde::Serialize;
use std::{
collections::{BTreeMap, HashSet},
path::PathBuf,
@@ -21,7 +21,10 @@ use crate::{panes::PaneId, screen::ScreenInstruction};
use zellij_utils::{
consts::VERSION,
- data::{Direction, Event, EventType, InputMode, PluginIds, Resize},
+ data::{
+ CommandToRun, Direction, Event, EventType, FileToOpen, InputMode, PluginCommand, PluginIds,
+ PluginMessage, Resize, ResizeStrategy,
+ },
errors::prelude::*,
input::{
actions::Action,
@@ -29,6 +32,11 @@ use zellij_utils::{
layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation},
plugins::PluginType,
},
+ plugin_api::{
+ plugin_command::ProtobufPluginCommand,
+ plugin_ids::{ProtobufPluginIds, ProtobufZellijVersion},
+ },
+ prost::Message,
serde,
};
@@ -53,87 +61,13 @@ pub fn zellij_exports(
plugin_env: &PluginEnv,
subscriptions: &Arc<Mutex<Subscriptions>>,
) -> ImportObject {
- macro_rules! zellij_export {
- ($($host_function:ident),+ $(,)?) => {
- imports! {
- "zellij" => {
- $(stringify!($host_function) =>
- Function::new_native_with_env(store, ForeignFunctionEnv::new(plugin_env, subscriptions), $host_function),)+
- }
- }
+ imports! {
+ "zellij" => {
+ "host_run_plugin_command" => {
+ Function::new_native_with_env(store, ForeignFunctionEnv::new(plugin_env, subscriptions), host_run_plugin_command)
+ }
}
}
-
- zellij_export! {
- host_subscribe,
- host_unsubscribe,
- host_set_selectable,
- host_get_plugin_ids,
- host_get_zellij_version,
- host_open_file,
- host_open_file_floating,
- host_open_file_with_line,
- host_open_file_with_line_floating,
- host_open_terminal,
- host_open_terminal_floating,
- host_open_command_pane,
- host_open_command_pane_floating,
- host_switch_tab_to,
- host_set_timeout,
- host_exec_cmd,
- host_report_panic,
- host_post_message_to,
- host_post_message_to_plugin,
- host_hide_self,
- host_show_self,
- host_switch_to_mode,
- host_new_tabs_with_layout,
- host_new_tab,
- host_go_to_next_tab,
- host_go_to_previous_tab,
- host_resize,
- host_resize_with_direction,
- host_focus_next_pane,
- host_focus_previous_pane,
- host_move_focus,
- host_move_focus_or_tab,
- host_detach,
- host_edit_scrollback,
- host_write,
- host_write_chars,
- host_toggle_tab,
- host_move_pane,
- host_move_pane_with_direction,
- host_clear_screen,
- host_scroll_up,
- host_scroll_down,
- host_scroll_to_top,
- host_scroll_to_bottom,
- host_page_scroll_up,
- host_page_scroll_down,
- host_toggle_focus_fullscreen,
- host_toggle_pane_frames,
- host_toggle_pane_embed_or_eject,
- host_undo_rename_pane,
- host_close_focus,
- host_toggle_active_tab_sync,
- host_close_focused_tab,
- host_undo_rename_tab,
- host_quit_zellij,
- host_previous_swap_layout,
- host_next_swap_layout,
- host_go_to_tab_name,
- host_focus_or_create_tab,
- host_go_to_tab,
- host_start_or_reload_plugin,
- host_close_terminal_pane,
- host_close_plugin_pane,
- host_focus_terminal_pane,
- host_focus_plugin_pane,
- host_rename_terminal_pane,
- host_rename_plugin_pane,
- host_rename_tab,
- }
}
#[derive(WasmerEnv, Clone)]
@@ -151,42 +85,151 @@ impl ForeignFunctionEnv {
}
}
-fn host_subscribe(env: &ForeignFunctionEnv) {
- wasi_read_object::<HashSet<EventType>>(&env.plugin_env.wasi_env)
- .and_then(|new| {
- env.subscriptions.lock().to_anyhow()?.extend(new.clone());
- Ok(new)
- })
- .and_then(|new| {
- env.plugin_env
- .senders
- .send_to_plugin(PluginInstruction::PluginSubscribedToEvents(
- env.plugin_env.plugin_id,
- env.plugin_env.client_id,
- new,
- ))
+fn host_run_plugin_command(env: &ForeignFunctionEnv) {
+ wasi_read_bytes(&env.plugin_env.wasi_env)
+ .and_then(|bytes| {
+ let command: ProtobufPluginCommand = ProtobufPluginCommand::decode(bytes.as_slice())?;
+ let command: PluginCommand = command
+ .try_into()
+ .map_err(|e| anyhow!("failed to convert serialized command: {}", e))?;
+ match command {
+ PluginCommand::Subscribe(event_list) => subscribe(env, event_list)?,
+ PluginCommand::Unsubscribe(event_list) => unsubscribe(env, event_list)?,
+ PluginCommand::SetSelectable(selectable) => set_selectable(env, selectable),
+ PluginCommand::GetPluginIds => get_plugin_ids(env),
+ PluginCommand::GetZellijVersion => get_zellij_version(env),
+ PluginCommand::OpenFile(file_to_open) => open_file(env, file_to_open),
+ PluginCommand::OpenFileFloating(file_to_open) => {
+ open_file_floating(env, file_to_open)
+ },
+ PluginCommand::OpenTerminal(cwd) => open_terminal(env, cwd.path.try_into()?),
+ PluginCommand::OpenTerminalFloating(cwd) => {
+ open_terminal_floating(env, cwd.path.try_into()?)
+ },
+ PluginCommand::OpenCommandPane(command_to_run) => {
+ open_command_pane(env, command_to_run)
+ },
+ PluginCommand::OpenCommandPaneFloating(command_to_run) => {
+ open_command_pane_floating(env, command_to_run)
+ },
+ PluginCommand::SwitchTabTo(tab_index) => switch_tab_to(env, tab_index),
+ PluginCommand::SetTimeout(seconds) => set_timeout(env, seconds),
+ PluginCommand::ExecCmd(command_line) => exec_cmd(env, command_line),
+ PluginCommand::PostMessageTo(plugin_message) => {
+ post_message_to(env, plugin_message)?
+ },
+ PluginCommand::PostMessageToPlugin(plugin_message) => {
+ post_message_to_plugin(env, plugin_message)?
+ },
+ PluginCommand::HideSelf => hide_self(env)?,
+ PluginCommand::ShowSelf(should_float_if_hidden) => {
+ show_self(env, should_float_if_hidden)
+ },
+ PluginCommand::SwitchToMode(input_mode) => {
+ switch_to_mode(env, input_mode.try_into()?)
+ },
+ PluginCommand::NewTabsWithLayout(raw_layout) => {
+ new_tabs_with_layout(env, &raw_layout)?
+ },
+ PluginCommand::NewTab => new_tab(env),
+ PluginCommand::GoToNextTab => go_to_next_tab(env),
+ PluginCommand::GoToPreviousTab => go_to_previous_tab(env),
+ PluginCommand::Resize(resize_payload) => resize(env, resize_payload),
+ PluginCommand::ResizeWithDirection(resize_strategy) => {
+ resize_with_direction(env, resize_strategy)
+ },
+ PluginCommand::FocusNextPane => focus_next_pane(env),
+ PluginCommand::FocusPreviousPane => focus_previous_pane(env),
+ PluginCommand::MoveFocus(direction) => move_focus(env, direction),
+ PluginCommand::MoveFocusOrTab(direction) => move_focus_or_tab(env, direction),
+ PluginCommand::Detach => detach(env),
+ PluginCommand::EditScrollback => edit_scrollback(env),
+ PluginCommand::Write(bytes) => write(env, bytes),
+ PluginCommand::WriteChars(chars) => write_chars(env, chars),
+ PluginCommand::ToggleTab => toggle_tab(env),
+ PluginCommand::MovePane => move_pane(env),
+ PluginCommand::MovePaneWithDirection(direction) => {
+ move_pane_with_direction(env, direction)
+ },
+ PluginCommand::ClearScreen => clear_screen(env),
+ PluginCommand::ScrollUp => scroll_up(env),
+ PluginCommand::ScrollDown => scroll_down(env),
+ PluginCommand::ScrollToTop => scroll_to_top(env),
+ PluginCommand::ScrollToBottom => scroll_to_bottom(env),
+ PluginCommand::PageScrollUp => page_scroll_up(env),
+ PluginCommand::PageScrollDown => page_scroll_down(env),
+ PluginCommand::ToggleFocusFullscreen => toggle_focus_fullscreen(env),
+ PluginCommand::TogglePaneFrames => toggle_pane_frames(env),
+ PluginCommand::TogglePaneEmbedOrEject => toggle_pane_embed_or_eject(env),
+ PluginCommand::UndoRenamePane => undo_rename_pane(env),
+ PluginCommand::CloseFocus => close_focus(env),
+ PluginCommand::ToggleActiveTabSync => toggle_active_tab_sync(env),
+ PluginCommand::CloseFocusedTab => close_focused_tab(env),
+ PluginCommand::UndoRenameTab => undo_rename_tab(env),
+ PluginCommand::QuitZellij => quit_zellij(env),
+ PluginCommand::PreviousSwapLayout => previous_swap_layout(env),
+ PluginCommand::NextSwapLayout => next_swap_layout(env),