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

Introduce 'pipes', allowing users to pipe data to and control plugins from the command line #3066

Merged
merged 32 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift click to select a range
c755ab3
prototype - working with message from the cli
imsnif Dec 3, 2023
6e3ac24
prototype - pipe from the CLI to plugins
imsnif Dec 5, 2023
dfe90cc
prototype - pipe from the CLI to plugins and back again
imsnif Dec 5, 2023
dcdb31e
prototype - working with better cli interface
imsnif Dec 18, 2023
76710e7
prototype - working after removing unused stuff
imsnif Dec 19, 2023
806a72a
prototype - working with launching plugin if it is not launched, also…
imsnif Dec 22, 2023
54f75e1
refactor: change message to cli-message
imsnif Dec 22, 2023
6c9cf4b
prototype - allow plugins to send messages to each other
imsnif Dec 26, 2023
e1fb349
fix: allow cli messages to send plugin parameters (and implement back…
imsnif Dec 27, 2023
0aa5324
fix: use input_pipe_id to identify cli pipes instead of their message…
imsnif Dec 27, 2023
e23ba43
fix: come cleanups and add skip_cache parameter
imsnif Dec 27, 2023
124c675
fix: pipe/client-server communication robustness
imsnif Dec 28, 2023
22d3306
fix: leaking messages between plugins while loading
imsnif Dec 28, 2023
9ade600
feat: allow plugins to specify how a new plugin instance is launched …
imsnif Dec 29, 2023
ac31898
fix: add permissions
imsnif Dec 29, 2023
cf4a8a6
refactor: adjust cli api
imsnif Dec 31, 2023
a3959e0
fix: improve cli plugin loading error messages
imsnif Jan 1, 2024
a0c28ba
docs: cli pipe
imsnif Jan 1, 2024
5ce97e0
fix: take plugin configuration into account when messaging between pl…
imsnif Jan 5, 2024
3538ce5
refactor: pipe message protobuf interface
imsnif Jan 8, 2024
b9c3d89
refactor: update(event) -> pipe
imsnif Jan 9, 2024
241168a
refactor - rename CliMessage to CliPipe
imsnif Jan 10, 2024
9b45f91
fix: add is_private to pipes and change some naming
imsnif Jan 10, 2024
6a8d5f6
refactor - cli client
imsnif Jan 10, 2024
7dd6b4d
refactor: various cleanups
imsnif Jan 11, 2024
4915877
style(fmt): rustfmt
imsnif Jan 11, 2024
c3e8121
fix(pipes): backpressure across multiple plugins
imsnif Jan 16, 2024
463202b
style: some cleanups
imsnif Jan 16, 2024
981e92a
style(fmt): rustfmt
imsnif Jan 16, 2024
423cfe8
Merge branch 'main' into plugin-cli-interface
imsnif Jan 16, 2024
b1a096f
style: fix merge conflict mistake
imsnif Jan 16, 2024
ead3566
style(wording): clarify pipe permission
imsnif Jan 17, 2024
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
Next Next commit
prototype - working with message from the cli
  • Loading branch information
imsnif committed Dec 3, 2023
commit c755ab31fbb57d4167063a9d2a6d02a231a670db
30 changes: 30 additions & 0 deletions zellij-server/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 100,8 @@ pub enum PluginInstruction {
),
DumpLayout(SessionLayoutMetadata, ClientId),
LogLayoutToHd(SessionLayoutMetadata),
SubscribePluginToCustomMessage(PluginId, ClientId, String), // String -> custom message name
Message(String, String, ClientId), // Strings -> name, payload
Exit,
}

Expand Down Expand Up @@ -131,6 133,8 @@ impl From<&PluginInstruction> for PluginContext {
},
PluginInstruction::DumpLayout(..) => PluginContext::DumpLayout,
PluginInstruction::LogLayoutToHd(..) => PluginContext::LogLayoutToHd,
PluginInstruction::SubscribePluginToCustomMessage(..) => PluginContext::SubscribePluginToCustomMessage,
PluginInstruction::Message(..) => PluginContext::Message,
}
}
}
Expand Down Expand Up @@ -171,6 175,8 @@ pub(crate) fn plugin_thread_main(
layout.clone(),
);

let mut plugin_custom_message_subscriptions: HashMap<(PluginId, ClientId), HashSet<String>> = HashMap::new();

loop {
let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Plugin((&event).into()));
Expand Down Expand Up @@ -398,6 404,30 @@ pub(crate) fn plugin_thread_main(
.send_to_pty(PtyInstruction::LogLayoutToHd(session_layout_metadata)),
);
},
PluginInstruction::SubscribePluginToCustomMessage(plugin_id, client_id, custom_message_name) => {
plugin_custom_message_subscriptions.entry((plugin_id, client_id)).or_insert_with(HashSet::new).insert(custom_message_name);
},
PluginInstruction::Message(name, payload, client_id) => { // TODO: remove client_id,
// it's from the cli
let mut updates = vec![];
let mut found = false;
for ((plugin_id, client_id), subscriptions) in &plugin_custom_message_subscriptions {
if subscriptions.contains(&name) {
found = true;
updates.push((Some(*plugin_id), Some(*client_id), Event::CustomMessage(name.clone(), payload.clone())));
}

}
wasm_bridge.update_plugins(updates, shutdown_send.clone())?;
if !found {
// TODO: send an error back to the cli or wherever

}
let _ = bus
.senders
.send_to_server(ServerInstruction::UnblockInputThread)
.context("failed to unblock input");
},
PluginInstruction::Exit => {
break;
},
Expand Down
71 changes: 38 additions & 33 deletions zellij-server/src/plugins/wasm_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,42 410,47 @@ impl WasmBridge {
let mut running_plugin = running_plugin.lock().unwrap();
let _s = _s; // guard to allow the task to complete before cleanup/shutdown
if running_plugin.apply_event_id(AtomicEvent::Resize, event_id) {
let old_rows = running_plugin.rows;
let old_columns = running_plugin.columns;
running_plugin.rows = new_rows;
running_plugin.columns = new_columns;

let rendered_bytes = running_plugin
.instance
.clone()
.exports
.get_function("render")
.map_err(anyError::new)
.and_then(|render| {
render
.call(
&mut running_plugin.store,
&[
Value::I32(new_rows as i32),
Value::I32(new_columns as i32),
],
)
.map_err(anyError::new)
})
.and_then(|_| wasi_read_string(&running_plugin.plugin_env.wasi_env))
.with_context(err_context);
match rendered_bytes {
Ok(rendered_bytes) => {
let plugin_bytes = vec![(
plugin_id,
client_id,
rendered_bytes.as_bytes().to_vec(),
)];
senders
.send_to_screen(ScreenInstruction::PluginBytes(
plugin_bytes,
))
.unwrap();
},
Err(e) => log::error!("{}", e),
// TODO: better, right now the plugin doesn't render on first render?
if old_rows != new_rows || old_columns != new_columns {
let rendered_bytes = running_plugin
.instance
.clone()
.exports
.get_function("render")
.map_err(anyError::new)
.and_then(|render| {
render
.call(
&mut running_plugin.store,
&[
Value::I32(new_rows as i32),
Value::I32(new_columns as i32),
],
)
.map_err(anyError::new)
})
.and_then(|_| wasi_read_string(&running_plugin.plugin_env.wasi_env))
.with_context(err_context);
match rendered_bytes {
Ok(rendered_bytes) => {
let plugin_bytes = vec![(
plugin_id,
client_id,
rendered_bytes.as_bytes().to_vec(),
)];
senders
.send_to_screen(ScreenInstruction::PluginBytes(
plugin_bytes,
))
.unwrap();
},
Err(e) => log::error!("{}", e),
}
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions zellij-server/src/plugins/zellij_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 240,9 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
PluginCommand::RenameSession(new_session_name) => {
rename_session(env, new_session_name)
},
PluginCommand::SubscribeToCustomMessage(custom_message_name) => {
subscribe_to_custom_message(env, custom_message_name)?
},
},
(PermissionStatus::Denied, permission) => {
log::error!(
Expand Down Expand Up @@ -272,6 275,17 @@ fn subscribe(env: &ForeignFunctionEnv, event_list: HashSet<EventType>) -> Result
))
}

// TODO: permissions!!!111oneoneone
fn subscribe_to_custom_message(env: &ForeignFunctionEnv, custom_message_name: String) -> Result<()> {
env.plugin_env
.senders
.send_to_plugin(PluginInstruction::SubscribePluginToCustomMessage(
env.plugin_env.plugin_id,
env.plugin_env.client_id,
custom_message_name,
))
}

fn unsubscribe(env: &ForeignFunctionEnv, event_list: HashSet<EventType>) -> Result<()> {
env.subscriptions
.lock()
Expand Down
6 changes: 6 additions & 0 deletions zellij-server/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 803,12 @@ pub(crate) fn route_action(
.send_to_screen(ScreenInstruction::RenameSession(name, client_id))
.with_context(err_context)?;
},
Action::Message(name, payload) => {
log::info!("Action::Message");
senders
.send_to_plugin(PluginInstruction::Message(name, payload, client_id))
.with_context(err_context)?;
},
}
Ok(should_break)
}
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 @@ -694,6 694,14 @@ pub fn rename_session(name: &str) {
unsafe { host_run_plugin_command() };
}

/// Allow sending this plugin custom messages with the given name
pub fn subscribe_to_custom_message(name: &str) {
let plugin_command = PluginCommand::SubscribeToCustomMessage(name.to_owned());
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() };
}

// Utility Functions

#[allow(unused)]
Expand Down
7 changes: 6 additions & 1 deletion zellij-utils/assets/prost/api.action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 5,7 @@ pub struct Action {
pub name: i32,
#[prost(
oneof = "action::OptionalPayload",
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"
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"
)]
pub optional_payload: ::core::option::Option<action::OptionalPayload>,
}
Expand Down Expand Up @@ -104,6 104,8 @@ pub mod action {
RenameSessionPayload(::prost::alloc::string::String),
#[prost(message, tag = "46")]
LaunchPluginPayload(super::LaunchOrFocusPluginPayload),
#[prost(message, tag = "47")]
MessagePayload(super::NameAndValue),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down Expand Up @@ -410,6 412,7 @@ pub enum ActionName {
BreakPaneLeft = 79,
RenameSession = 80,
LaunchPlugin = 81,
Message = 82,
}
impl ActionName {
/// String value of the enum field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -500,6 503,7 @@ impl ActionName {
ActionName::BreakPaneLeft => "BreakPaneLeft",
ActionName::RenameSession => "RenameSession",
ActionName::LaunchPlugin => "LaunchPlugin",
ActionName::Message => "Message",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -587,6 591,7 @@ impl ActionName {
"BreakPaneLeft" => Some(Self::BreakPaneLeft),
"RenameSession" => Some(Self::RenameSession),
"LaunchPlugin" => Some(Self::LaunchPlugin),
"Message" => Some(Self::Message),
_ => None,
}
}
Expand Down
7 changes: 6 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"
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"
)]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
Expand Down Expand Up @@ -104,6 104,8 @@ pub mod plugin_command {
DeleteDeadSessionPayload(::prost::alloc::string::String),
#[prost(string, tag = "46")]
RenameSessionPayload(::prost::alloc::string::String),
#[prost(string, tag = "47")]
SubscribeToCustomMessagePayload(::prost::alloc::string::String),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down Expand Up @@ -318,6 320,7 @@ pub enum CommandName {
DeleteDeadSession = 73,
DeleteAllDeadSessions = 74,
RenameSession = 75,
SubscribeToCustomMessage = 76,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -402,6 405,7 @@ impl CommandName {
CommandName::DeleteDeadSession => "DeleteDeadSession",
CommandName::DeleteAllDeadSessions => "DeleteAllDeadSessions",
CommandName::RenameSession => "RenameSession",
CommandName::SubscribeToCustomMessage => "SubscribeToCustomMessage",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -483,6 487,7 @@ impl CommandName {
"DeleteDeadSession" => Some(Self::DeleteDeadSession),
"DeleteAllDeadSessions" => Some(Self::DeleteAllDeadSessions),
"RenameSession" => Some(Self::RenameSession),
"SubscribeToCustomMessage" => Some(Self::SubscribeToCustomMessage),
_ => None,
}
}
Expand Down
4 changes: 4 additions & 0 deletions zellij-utils/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,4 549,8 @@ pub enum CliAction {
RenameSession {
name: String,
},
Message {
name: String,
payload: String,
},
}
5 changes: 5 additions & 0 deletions zellij-utils/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 511,10 @@ pub enum Event {
// headers,
// body,
// context
// TODO CONTINUE HERE:
// * add a subscribe_to_external_message() plugin command
// * allow sending messages through zellij message (zm) <message>, <payload> and send to the
// relevant plugins through the CustomMessage event (change its docs to reflect this)
}

#[derive(
Expand Down Expand Up @@ -1105,4 1109,5 @@ pub enum PluginCommand {
BTreeMap<String, String>, // context
),
RenameSession(String), // String -> new session name
SubscribeToCustomMessage(String), // String -> message name
}
2 changes: 2 additions & 0 deletions zellij-utils/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 393,8 @@ pub enum PluginContext {
PermissionRequestResult,
DumpLayout,
LogLayoutToHd,
SubscribePluginToCustomMessage,
Message,
}

/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
Expand Down
2 changes: 2 additions & 0 deletions zellij-utils/src/input/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 256,7 @@ pub enum Action {
BreakPaneRight,
BreakPaneLeft,
RenameSession(String),
Message(String, String), // name, payload
}

impl Action {
Expand Down Expand Up @@ -582,6 583,7 @@ impl Action {
)])
},
CliAction::RenameSession { name } => Ok(vec![Action::RenameSession(name)]),
CliAction::Message { name, payload } => Ok(vec![Action::Message(name, payload)]),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions zellij-utils/src/plugin_api/action.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 53,7 @@ message Action {
IdAndName rename_tab_payload = 44;
string rename_session_payload = 45;
LaunchOrFocusPluginPayload launch_plugin_payload = 46;
NameAndValue message_payload = 47;
}
}

Expand Down Expand Up @@ -227,6 228,7 @@ enum ActionName {
BreakPaneLeft = 79;
RenameSession = 80;
LaunchPlugin = 81;
Message = 82;
}

message Position {
Expand Down
1 change: 1 addition & 0 deletions zellij-utils/src/plugin_api/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 1246,7 @@ impl TryFrom<Action> for ProtobufAction {
| Action::Deny
| Action::Copy
| Action::DumpLayout
| Action::Message(..) // TODO: let's allow this one
| Action::SkipConfirm(..) => Err("Unsupported action"),
}
}
Expand Down
2 changes: 2 additions & 0 deletions zellij-utils/src/plugin_api/plugin_command.proto
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 87,7 @@ enum CommandName {
DeleteDeadSession = 73;
DeleteAllDeadSessions = 74;
RenameSession = 75;
SubscribeToCustomMessage = 76;
}

message PluginCommand {
Expand Down Expand Up @@ -137,6 138,7 @@ message PluginCommand {
WebRequestPayload web_request_payload = 44;
string delete_dead_session_payload = 45;
string rename_session_payload = 46;
string subscribe_to_custom_message_payload = 47;
}
}

Expand Down
10 changes: 10 additions & 0 deletions zellij-utils/src/plugin_api/plugin_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 641,12 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for RenameSession"),
},
Some(CommandName::SubscribeToCustomMessage) => match protobuf_plugin_command.payload {
Some(Payload::SubscribeToCustomMessagePayload(custom_message_name)) => {
Ok(PluginCommand::SubscribeToCustomMessage(custom_message_name))
},
_ => Err("Mismatched payload for SubscribeToCustomMessage"),
},
None => Err("Unrecognized plugin command"),
}
}
Expand Down Expand Up @@ -1069,6 1075,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::RenameSession as i32,
payload: Some(Payload::RenameSessionPayload(new_session_name)),
}),
PluginCommand::SubscribeToCustomMessage(custom_message_name) => Ok(ProtobufPluginCommand {
name: CommandName::SubscribeToCustomMessage as i32,
payload: Some(Payload::SubscribeToCustomMessagePayload(custom_message_name)),
}),
}
}
}