Skip to content

Commit

Permalink
feat(plugins): add API to open new tabs with a LayoutInfo (#3305)
Browse files Browse the repository at this point in the history
* feat(plugins): add api to open new tabs with a layout info

* fix tests
  • Loading branch information
imsnif authored Apr 26, 2024
1 parent cd37864 commit 7562a81
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 6 deletions.
2 changes: 2 additions & 0 deletions zellij-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -978,12 +978,14 @@ fn init_session(
let client_attributes = client_attributes.clone();
let default_shell = default_shell.clone();
let capabilities = capabilities.clone();
let layout_dir = config_options.layout_dir.clone();
move || {
plugin_thread_main(
plugin_bus,
store,
data_dir,
layout,
layout_dir,
path_to_default_shell,
zellij_cwd,
capabilities,
Expand Down
2 changes: 2 additions & 0 deletions zellij-server/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ pub(crate) fn plugin_thread_main(
store: Store,
data_dir: PathBuf,
mut layout: Box<Layout>,
layout_dir: Option<PathBuf>,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
capabilities: PluginCapabilities,
Expand Down Expand Up @@ -217,6 +218,7 @@ pub(crate) fn plugin_thread_main(
client_attributes,
default_shell,
layout.clone(),
layout_dir,
);

loop {
Expand Down
17 changes: 17 additions & 0 deletions zellij-server/src/plugins/plugin_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
}

impl<'a> PluginLoader<'a> {
Expand All @@ -84,6 +85,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Result<()> {
let err_context = || format!("failed to reload plugin {plugin_id} from memory");
let mut connected_clients: Vec<ClientId> =
Expand All @@ -108,6 +110,7 @@ impl<'a> PluginLoader<'a> {
client_attributes,
default_shell,
default_layout,
layout_dir,
)?;
plugin_loader
.load_module_from_memory()
Expand Down Expand Up @@ -149,6 +152,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
skip_cache: bool,
layout_dir: Option<PathBuf>,
) -> Result<()> {
let err_context = || format!("failed to start plugin {plugin_id} for client {client_id}");
let mut plugin_loader = PluginLoader::new(
Expand All @@ -168,6 +172,7 @@ impl<'a> PluginLoader<'a> {
client_attributes,
default_shell,
default_layout,
layout_dir,
)?;
if skip_cache {
plugin_loader
Expand Down Expand Up @@ -231,6 +236,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Result<()> {
let mut new_plugins = HashSet::new();
for plugin_id in plugin_map.lock().unwrap().plugin_ids() {
Expand All @@ -252,6 +258,7 @@ impl<'a> PluginLoader<'a> {
client_attributes.clone(),
default_shell.clone(),
default_layout.clone(),
layout_dir.clone(),
)?;
plugin_loader
.load_module_from_memory()
Expand Down Expand Up @@ -285,6 +292,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Result<()> {
let err_context = || format!("failed to reload plugin id {plugin_id}");

Expand All @@ -310,6 +318,7 @@ impl<'a> PluginLoader<'a> {
client_attributes,
default_shell,
default_layout,
layout_dir,
)?;
plugin_loader
.compile_module()
Expand Down Expand Up @@ -347,6 +356,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Result<Self> {
let plugin_own_data_dir = ZELLIJ_SESSION_CACHE_DIR
.join(Url::from(&plugin.location).to_string())
Expand All @@ -373,6 +383,7 @@ impl<'a> PluginLoader<'a> {
client_attributes,
default_shell,
default_layout,
layout_dir,
})
}
pub fn new_from_existing_plugin_attributes(
Expand All @@ -390,6 +401,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let (running_plugin, _subscriptions, _workers) = {
Expand Down Expand Up @@ -423,6 +435,7 @@ impl<'a> PluginLoader<'a> {
client_attributes,
default_shell,
default_layout,
layout_dir,
)
}
pub fn new_from_different_client_id(
Expand All @@ -440,6 +453,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let running_plugin = {
Expand Down Expand Up @@ -474,6 +488,7 @@ impl<'a> PluginLoader<'a> {
client_attributes,
default_shell,
default_layout,
layout_dir,
)
}
pub fn load_module_from_memory(&mut self) -> Result<Module> {
Expand Down Expand Up @@ -737,6 +752,7 @@ impl<'a> PluginLoader<'a> {
self.client_attributes.clone(),
self.default_shell.clone(),
self.default_layout.clone(),
self.layout_dir.clone(),
)?;
plugin_loader_for_client
.load_module_from_memory()
Expand Down Expand Up @@ -845,6 +861,7 @@ impl<'a> PluginLoader<'a> {
plugin_cwd: self.zellij_cwd.clone(),
input_pipes_to_unblock: Arc::new(Mutex::new(HashSet::new())),
input_pipes_to_block: Arc::new(Mutex::new(HashSet::new())),
layout_dir: self.layout_dir.clone(),
};

let subscriptions = Arc::new(Mutex::new(HashSet::new()));
Expand Down
1 change: 1 addition & 0 deletions zellij-server/src/plugins/plugin_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ pub struct PluginEnv {
pub client_attributes: ClientAttributes,
pub default_shell: Option<TerminalAction>,
pub default_layout: Box<Layout>,
pub layout_dir: Option<PathBuf>,
pub plugin_cwd: PathBuf,
pub input_pipes_to_unblock: Arc<Mutex<HashSet<String>>>,
pub input_pipes_to_block: Arc<Mutex<HashSet<String>>>,
Expand Down
4 changes: 4 additions & 0 deletions zellij-server/src/plugins/unit/plugin_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ fn create_plugin_thread(
store,
data_dir,
Box::new(Layout::default()),
None,
default_shell,
zellij_cwd,
plugin_capabilities,
Expand Down Expand Up @@ -349,6 +350,7 @@ fn create_plugin_thread_with_server_receiver(
store,
data_dir,
Box::new(Layout::default()),
None,
default_shell,
zellij_cwd,
plugin_capabilities,
Expand Down Expand Up @@ -433,6 +435,7 @@ fn create_plugin_thread_with_pty_receiver(
store,
data_dir,
Box::new(Layout::default()),
None,
default_shell,
zellij_cwd,
plugin_capabilities,
Expand Down Expand Up @@ -512,6 +515,7 @@ fn create_plugin_thread_with_background_jobs_receiver(
store,
data_dir,
Box::new(Layout::default()),
None,
default_shell,
zellij_cwd,
plugin_capabilities,
Expand Down
9 changes: 9 additions & 0 deletions zellij-server/src/plugins/wasm_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub struct WasmBridge {
cached_plugin_map:
HashMap<RunPluginLocation, HashMap<PluginUserConfiguration, Vec<(PluginId, ClientId)>>>,
pending_pipes: PendingPipes,
layout_dir: Option<PathBuf>,
}

impl WasmBridge {
Expand All @@ -114,6 +115,7 @@ impl WasmBridge {
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
) -> Self {
let plugin_map = Arc::new(Mutex::new(PluginMap::default()));
let connected_clients: Arc<Mutex<Vec<ClientId>>> = Arc::new(Mutex::new(vec![]));
Expand Down Expand Up @@ -143,6 +145,7 @@ impl WasmBridge {
default_layout,
cached_plugin_map: HashMap::new(),
pending_pipes: Default::default(),
layout_dir,
}
}
pub fn load_plugin(
Expand Down Expand Up @@ -198,6 +201,7 @@ impl WasmBridge {
let client_attributes = self.client_attributes.clone();
let default_shell = self.default_shell.clone();
let default_layout = self.default_layout.clone();
let layout_dir = self.layout_dir.clone();
async move {
let _ = senders.send_to_background_jobs(
BackgroundJob::AnimatePluginLoading(plugin_id),
Expand Down Expand Up @@ -244,6 +248,7 @@ impl WasmBridge {
default_shell,
default_layout,
skip_cache,
layout_dir,
) {
Ok(_) => handle_plugin_successful_loading(&senders, plugin_id),
Err(e) => handle_plugin_loading_failure(
Expand Down Expand Up @@ -334,6 +339,7 @@ impl WasmBridge {
let client_attributes = self.client_attributes.clone();
let default_shell = self.default_shell.clone();
let default_layout = self.default_layout.clone();
let layout_dir = self.layout_dir.clone();
async move {
match PluginLoader::reload_plugin(
first_plugin_id,
Expand All @@ -350,6 +356,7 @@ impl WasmBridge {
client_attributes.clone(),
default_shell.clone(),
default_layout.clone(),
layout_dir.clone(),
) {
Ok(_) => {
handle_plugin_successful_loading(&senders, first_plugin_id);
Expand All @@ -374,6 +381,7 @@ impl WasmBridge {
client_attributes.clone(),
default_shell.clone(),
default_layout.clone(),
layout_dir.clone(),
) {
Ok(_) => handle_plugin_successful_loading(&senders, *plugin_id),
Err(e) => handle_plugin_loading_failure(
Expand Down Expand Up @@ -425,6 +433,7 @@ impl WasmBridge {
self.client_attributes.clone(),
self.default_shell.clone(),
self.default_layout.clone(),
self.layout_dir.clone(),
) {
Ok(_) => {
let _ = self
Expand Down
18 changes: 17 additions & 1 deletion zellij-server/src/plugins/zellij_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
PluginCommand::NewTabsWithLayout(raw_layout) => {
new_tabs_with_layout(env, &raw_layout)?
},
PluginCommand::NewTabsWithLayoutInfo(layout_info) => {
new_tabs_with_layout_info(env, layout_info)?
},
PluginCommand::NewTab => new_tab(env),
PluginCommand::GoToNextTab => go_to_next_tab(env),
PluginCommand::GoToPreviousTab => go_to_previous_tab(env),
Expand Down Expand Up @@ -859,6 +862,19 @@ fn new_tabs_with_layout(env: &ForeignFunctionEnv, raw_layout: &str) -> Result<()
None,
)
.map_err(|e| anyhow!("Failed to parse layout: {:?}", e))?;
apply_layout(env, layout);
Ok(())
}

fn new_tabs_with_layout_info(env: &ForeignFunctionEnv, layout_info: LayoutInfo) -> Result<()> {
// TODO: cwd
let layout = Layout::from_layout_info(&env.plugin_env.layout_dir, layout_info)
.map_err(|e| anyhow!("Failed to parse layout: {:?}", e))?;
apply_layout(env, layout);
Ok(())
}

fn apply_layout(env: &ForeignFunctionEnv, layout: Layout) {
let mut tabs_to_open = vec![];
let tabs = layout.tabs();
if tabs.is_empty() {
Expand Down Expand Up @@ -890,7 +906,6 @@ fn new_tabs_with_layout(env: &ForeignFunctionEnv, raw_layout: &str) -> Result<()
let error_msg = || format!("Failed to create layout tab");
apply_action!(action, error_msg, env);
}
Ok(())
}

fn new_tab(env: &ForeignFunctionEnv) {
Expand Down Expand Up @@ -1526,6 +1541,7 @@ fn check_command_permission(
PluginCommand::SwitchTabTo(..)
| PluginCommand::SwitchToMode(..)
| PluginCommand::NewTabsWithLayout(..)
| PluginCommand::NewTabsWithLayoutInfo(..)
| PluginCommand::NewTab
| PluginCommand::GoToNextTab
| PluginCommand::GoToPreviousTab
Expand Down
8 changes: 8 additions & 0 deletions zellij-tile/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ pub fn new_tabs_with_layout(layout: &str) {
unsafe { host_run_plugin_command() };
}

/// Provide a LayoutInfo to be applied to the current session in a new tab. If the layout has multiple tabs, they will all be opened.
pub fn new_tabs_with_layout_info(layout_info: LayoutInfo) {
let plugin_command = PluginCommand::NewTabsWithLayoutInfo(layout_info);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() };
}

/// Open a new tab with the default layout
pub fn new_tab() {
let plugin_command = PluginCommand::NewTab;
Expand Down
13 changes: 12 additions & 1 deletion zellij-utils/assets/prost/api.plugin_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub struct PluginCommand {
pub name: i32,
#[prost(
oneof = "plugin_command::Payload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61"
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62"
)]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
Expand Down Expand Up @@ -116,10 +116,18 @@ pub mod plugin_command {
KillSessionsPayload(super::KillSessionsPayload),
#[prost(string, tag = "61")]
ScanHostFolderPayload(::prost::alloc::string::String),
#[prost(message, tag = "62")]
NewTabsWithLayoutInfoPayload(super::NewTabsWithLayoutInfoPayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct NewTabsWithLayoutInfoPayload {
#[prost(message, optional, tag = "1")]
pub layout_info: ::core::option::Option<super::event::LayoutInfo>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct KillSessionsPayload {
#[prost(string, repeated, tag = "1")]
pub session_names: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
Expand Down Expand Up @@ -422,6 +430,7 @@ pub enum CommandName {
WatchFilesystem = 83,
DumpSessionLayout = 84,
CloseSelf = 85,
NewTabsWithLayoutInfo = 86,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -516,6 +525,7 @@ impl CommandName {
CommandName::WatchFilesystem => "WatchFilesystem",
CommandName::DumpSessionLayout => "DumpSessionLayout",
CommandName::CloseSelf => "CloseSelf",
CommandName::NewTabsWithLayoutInfo => "NewTabsWithLayoutInfo",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -607,6 +617,7 @@ impl CommandName {
"WatchFilesystem" => Some(Self::WatchFilesystem),
"DumpSessionLayout" => Some(Self::DumpSessionLayout),
"CloseSelf" => Some(Self::CloseSelf),
"NewTabsWithLayoutInfo" => Some(Self::NewTabsWithLayoutInfo),
_ => None,
}
}
Expand Down
1 change: 1 addition & 0 deletions zellij-utils/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1380,4 +1380,5 @@ pub enum PluginCommand {
WatchFilesystem,
DumpSessionLayout,
CloseSelf,
NewTabsWithLayoutInfo(LayoutInfo),
}
Loading

0 comments on commit 7562a81

Please sign in to comment.