Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugins): add API to open new tabs with a LayoutInfo #3305

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading