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
Prev Previous commit
Next Next commit
refactor: change message to cli-message
  • Loading branch information
imsnif committed Dec 22, 2023
commit 54f75e1874839a22fbcfeb4d6aea5592edf40754
8 changes: 4 additions & 4 deletions default-plugins/fixture-plugin-for-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 60,7 @@ impl ZellijPlugin for State {
EventType::FileSystemCreate,
EventType::FileSystemUpdate,
EventType::FileSystemDelete,
EventType::Message,
EventType::CliMessage,
]);
}

Expand Down Expand Up @@ -283,11 283,11 @@ impl ZellijPlugin for State {
self.received_payload = Some(payload.clone());
}
},
Event::Message{name, payload, ..} => {
Event::CliMessage{name, payload, ..} => {
if name == "message_name" && payload == &Some("message_payload".to_owned()) {
unblock_pipe_input(name);
unblock_cli_pipe_input(name);
} else if name == "pipe_output" {
pipe_output(name, "this_is_my_output");
cli_pipe_output(name, "this_is_my_output");
}
},
Event::SystemClipboardFailure => {
Expand Down
10 changes: 5 additions & 5 deletions zellij-client/src/cli_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 27,7 @@ pub fn start_cli_client(mut os_input: Box<dyn ClientOsApi>, session_name: &str,

for action in actions {
match action {
Action::Message { name, payload, plugin, args } if payload.is_none() => {
Action::CliMessage { name, payload, plugin, args } if payload.is_none() => {
pipe_client(&mut os_input, name, plugin, args, pane_id);
},
action => {
Expand All @@ -46,21 46,21 @@ fn pipe_client(os_input: &mut Box<dyn ClientOsApi>, mut pipe_name: Option<String
let mut buffer = String::new();
handle.read_line(&mut buffer).unwrap(); // TODO: no unwrap etc.
if buffer.is_empty() {
let msg = ClientToServerMsg::Action(Action::Message{ name: name.clone(), payload: None, args: args.clone(), plugin: plugin.clone() }, pane_id, None);
let msg = ClientToServerMsg::Action(Action::CliMessage{ name: name.clone(), payload: None, args: args.clone(), plugin: plugin.clone() }, pane_id, None);
os_input.send_to_server(msg);
break;
} else {
let msg = ClientToServerMsg::Action(Action::Message{ name: name.clone(), payload: Some(buffer), args: args.clone(), plugin: plugin.clone() }, pane_id, None);
let msg = ClientToServerMsg::Action(Action::CliMessage{ name: name.clone(), payload: Some(buffer), args: args.clone(), plugin: plugin.clone() }, pane_id, None);
os_input.send_to_server(msg);
}
loop {
match os_input.recv_from_server() {
Some((ServerToClientMsg::UnblockPipeInput(pipe_name), _)) => {
Some((ServerToClientMsg::UnblockCliPipeInput(pipe_name), _)) => {
if Some(pipe_name) == name {
break;
}
},
Some((ServerToClientMsg::PipeOutput(pipe_name, output), _)) => {
Some((ServerToClientMsg::CliPipeOutput(pipe_name, output), _)) => {
let err_context = "Failed to write to stdout";
if Some(pipe_name) == name {
let mut stdout = os_input.get_stdout_writer();
Expand Down
16 changes: 8 additions & 8 deletions zellij-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 49,8 @@ pub(crate) enum ClientInstruction {
LogError(Vec<String>),
SwitchSession(ConnectToSession),
SetSynchronizedOutput(Option<SyncOutput>),
UnblockPipeInput(String), // String -> pipe name
PipeOutput(String, String), // String -> pipe name, String -> output
UnblockCliPipeInput(String), // String -> pipe name
CliPipeOutput(String, String), // String -> pipe name, String -> output
}

impl From<ServerToClientMsg> for ClientInstruction {
Expand All @@ -69,11 69,11 @@ impl From<ServerToClientMsg> for ClientInstruction {
ServerToClientMsg::SwitchSession(connect_to_session) => {
ClientInstruction::SwitchSession(connect_to_session)
},
ServerToClientMsg::UnblockPipeInput(pipe_name) => {
ClientInstruction::UnblockPipeInput(pipe_name)
ServerToClientMsg::UnblockCliPipeInput(pipe_name) => {
ClientInstruction::UnblockCliPipeInput(pipe_name)
},
ServerToClientMsg::PipeOutput(pipe_name, output) => {
ClientInstruction::PipeOutput(pipe_name, output)
ServerToClientMsg::CliPipeOutput(pipe_name, output) => {
ClientInstruction::CliPipeOutput(pipe_name, output)
},
}
}
Expand All @@ -95,8 95,8 @@ impl From<&ClientInstruction> for ClientContext {
ClientInstruction::DoneParsingStdinQuery => ClientContext::DoneParsingStdinQuery,
ClientInstruction::SwitchSession(..) => ClientContext::SwitchSession,
ClientInstruction::SetSynchronizedOutput(..) => ClientContext::SetSynchronisedOutput,
ClientInstruction::UnblockPipeInput(..) => ClientContext::UnblockPipeInput,
ClientInstruction::PipeOutput(..) => ClientContext::PipeOutput,
ClientInstruction::UnblockCliPipeInput(..) => ClientContext::UnblockCliPipeInput,
ClientInstruction::CliPipeOutput(..) => ClientContext::CliPipeOutput,
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions zellij-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 86,8 @@ pub enum ServerInstruction {
ActiveClients(ClientId),
Log(Vec<String>, ClientId),
SwitchSession(ConnectToSession, ClientId),
UnblockPipeInput(String), // String -> Pipe name
PipeOutput(String, String), // String -> Pipe name, String -> Output
UnblockCliPipeInput(String), // String -> Pipe name
CliPipeOutput(String, String), // String -> Pipe name, String -> Output
}

impl From<&ServerInstruction> for ServerContext {
Expand All @@ -106,8 106,8 @@ impl From<&ServerInstruction> for ServerContext {
ServerInstruction::ActiveClients(_) => ServerContext::ActiveClients,
ServerInstruction::Log(..) => ServerContext::Log,
ServerInstruction::SwitchSession(..) => ServerContext::SwitchSession,
ServerInstruction::UnblockPipeInput(..) => ServerContext::UnblockPipeInput,
ServerInstruction::PipeOutput(..) => ServerContext::PipeOutput,
ServerInstruction::UnblockCliPipeInput(..) => ServerContext::UnblockCliPipeInput,
ServerInstruction::CliPipeOutput(..) => ServerContext::CliPipeOutput,
}
}
}
Expand Down Expand Up @@ -494,22 494,22 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
);
}
},
ServerInstruction::UnblockPipeInput(pipe_name) => {
ServerInstruction::UnblockCliPipeInput(pipe_name) => {
for client_id in session_state.read().unwrap().clients.keys() {
send_to_client!(
*client_id,
os_input,
ServerToClientMsg::UnblockPipeInput(pipe_name.clone()),
ServerToClientMsg::UnblockCliPipeInput(pipe_name.clone()),
session_state
);
}
},
ServerInstruction::PipeOutput(pipe_name, output) => {
ServerInstruction::CliPipeOutput(pipe_name, output) => {
for client_id in session_state.read().unwrap().clients.keys() {
send_to_client!(
*client_id,
os_input,
ServerToClientMsg::PipeOutput(pipe_name.clone(), output.clone()),
ServerToClientMsg::CliPipeOutput(pipe_name.clone(), output.clone()),
session_state
);
}
Expand Down
14 changes: 9 additions & 5 deletions zellij-server/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 100,7 @@ pub enum PluginInstruction {
),
DumpLayout(SessionLayoutMetadata, ClientId),
LogLayoutToHd(SessionLayoutMetadata),
Message {
CliMessage {
name: String,
payload: Option<String>,
plugin: Option<String>,
Expand Down Expand Up @@ -138,7 138,7 @@ impl From<&PluginInstruction> for PluginContext {
},
PluginInstruction::DumpLayout(..) => PluginContext::DumpLayout,
PluginInstruction::LogLayoutToHd(..) => PluginContext::LogLayoutToHd,
PluginInstruction::Message{..} => PluginContext::Message,
PluginInstruction::CliMessage {..} => PluginContext::CliMessage,
PluginInstruction::CachePluginEvents{..} => PluginContext::CachePluginEvents,
}
}
Expand Down Expand Up @@ -409,7 409,7 @@ pub(crate) fn plugin_thread_main(
.send_to_pty(PtyInstruction::LogLayoutToHd(session_layout_metadata)),
);
},
PluginInstruction::Message{name, payload, plugin, args} => { // TODO: remove client_id,
PluginInstruction::CliMessage {name, payload, plugin, args} => { // TODO: remove client_id,
// it's from the cli
// TODO CONTINUE HERE(18/12):
// * make plugin pretty and make POC with pausing and filtering - DONE
Expand All @@ -426,6 426,10 @@ pub(crate) fn plugin_thread_main(
// - test that we're not losing messages when we already have permission - DONE
// - same plugin 2 different places in path (with/without permissions) - DONE
// * allow plugins to send/pipe messages to each other
// - change Message (all the way) to CliMessage (also the unblock and pipeoutput
// methods' names should reflect this change)
// - create a send_message plugin api that would act like Message but without
// backpressure
// * work on cli error messages, must be clearer

// TODO:
Expand Down Expand Up @@ -485,7 489,7 @@ pub(crate) fn plugin_thread_main(
};

for (plugin_id, client_id) in all_plugin_ids {
updates.push((Some(plugin_id), Some(client_id), Event::Message {name: name.clone(), payload: payload.clone(), args: args.clone() }));
updates.push((Some(plugin_id), Some(client_id), Event::CliMessage {name: name.clone(), payload: payload.clone(), args: args.clone() }));
}
},
Err(e) => {
Expand All @@ -498,7 502,7 @@ pub(crate) fn plugin_thread_main(
// send to all plugins
let all_plugin_ids = wasm_bridge.all_plugin_ids();
for (plugin_id, client_id) in all_plugin_ids {
updates.push((Some(plugin_id), Some(client_id), Event::Message{ name: name.clone(), payload: payload.clone(), args: args.clone()}));
updates.push((Some(plugin_id), Some(client_id), Event::CliMessage{ name: name.clone(), payload: payload.clone(), args: args.clone()}));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion zellij-server/src/plugins/wasm_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 886,7 @@ fn check_event_permission(
| Event::CopyToClipboard(..)
| Event::SystemClipboardFailure
| Event::InputReceived => PermissionType::ReadApplicationState,
Event::Message {..} => PermissionType::ReadCliMessages,
Event::CliMessage {..} => PermissionType::ReadCliMessages,
_ => return (PermissionStatus::Granted, None),
};

Expand Down
18 changes: 9 additions & 9 deletions zellij-server/src/plugins/zellij_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 240,11 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
PluginCommand::RenameSession(new_session_name) => {
rename_session(env, new_session_name)
},
PluginCommand::UnblockPipeInput(pipe_name) => {
unblock_pipe_input(env, pipe_name)?
PluginCommand::UnblockCliPipeInput(pipe_name) => {
unblock_cli_pipe_input(env, pipe_name)?
},
PluginCommand::PipeOutput(pipe_name, output) => {
pipe_output(env, pipe_name, output)?
PluginCommand::CliPipeOutput(pipe_name, output) => {
cli_pipe_output(env, pipe_name, output)?
},
},
(PermissionStatus::Denied, permission) => {
Expand Down Expand Up @@ -278,15 278,15 @@ fn subscribe(env: &ForeignFunctionEnv, event_list: HashSet<EventType>) -> Result
))
}

fn unblock_pipe_input(env: &ForeignFunctionEnv, pipe_name: String) -> Result<()> {
fn unblock_cli_pipe_input(env: &ForeignFunctionEnv, pipe_name: String) -> Result<()> {
env.plugin_env.input_pipes_to_unblock.lock().unwrap().insert(pipe_name);
Ok(()) // TODO: no result return
}

fn pipe_output(env: &ForeignFunctionEnv, pipe_name: String, output: String) -> Result<()> {
fn cli_pipe_output(env: &ForeignFunctionEnv, pipe_name: String, output: String) -> Result<()> {
env.plugin_env
.senders
.send_to_server(ServerInstruction::PipeOutput(pipe_name, output))
.send_to_server(ServerInstruction::CliPipeOutput(pipe_name, output))
.context("failed to send pipe output")
}

Expand Down Expand Up @@ -1379,8 1379,8 @@ fn check_command_permission(
| PluginCommand::DeleteAllDeadSessions
| PluginCommand::RenameSession(..)
| PluginCommand::RenameTab(..) => PermissionType::ChangeApplicationState,
PluginCommand::UnblockPipeInput(..)
| PluginCommand::PipeOutput(..) => PermissionType::ReadCliMessages,
PluginCommand::UnblockCliPipeInput(..)
| PluginCommand::CliPipeOutput(..) => PermissionType::ReadCliMessages,
_ => return (PermissionStatus::Granted, None),
};

Expand Down
4 changes: 2 additions & 2 deletions zellij-server/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,10 803,10 @@ pub(crate) fn route_action(
.send_to_screen(ScreenInstruction::RenameSession(name, client_id))
.with_context(err_context)?;
},
Action::Message{ mut name, payload, plugin, args } => {
Action::CliMessage{ mut name, payload, plugin, args } => {
if let Some(name) = name.take() {
senders
.send_to_plugin(PluginInstruction::Message{ name, payload, plugin, args})
.send_to_plugin(PluginInstruction::CliMessage { name, payload, plugin, args})
.with_context(err_context)?;
} else {
log::error!("Message must have a name");
Expand Down
2 changes: 1 addition & 1 deletion zellij-server/src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2174,7 2174,7 @@ pub(crate) fn screen_thread_main(
for pipe_name in input_pipes_to_unblock {
screen.bus
.senders
.send_to_server(ServerInstruction::UnblockPipeInput(pipe_name))
.send_to_server(ServerInstruction::UnblockCliPipeInput(pipe_name))
.context("failed to unblock input pipe");
}
}
Expand Down
8 changes: 4 additions & 4 deletions zellij-tile/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,16 695,16 @@ pub fn rename_session(name: &str) {
}

/// Unblock the input side of a pipe, requesting the next message be sent if there is one
pub fn unblock_pipe_input(pipe_name: &str) {
let plugin_command = PluginCommand::UnblockPipeInput(pipe_name.to_owned());
pub fn unblock_cli_pipe_input(pipe_name: &str) {
let plugin_command = PluginCommand::UnblockCliPipeInput(pipe_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() };
}

/// Send output to the output side of a pipe, ths does not affect the input side of same pipe
pub fn pipe_output(pipe_name: &str, output: &str) {
let plugin_command = PluginCommand::PipeOutput(pipe_name.to_owned(), output.to_owned());
pub fn cli_pipe_output(pipe_name: &str, output: &str) {
let plugin_command = PluginCommand::CliPipeOutput(pipe_name.to_owned(), output.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() };
Expand Down
10 changes: 5 additions & 5 deletions zellij-utils/assets/prost/api.action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 105,12 @@ pub mod action {
#[prost(message, tag = "46")]
LaunchPluginPayload(super::LaunchOrFocusPluginPayload),
#[prost(message, tag = "47")]
MessagePayload(super::MessagePayload),
MessagePayload(super::CliMessagePayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MessagePayload {
pub struct CliMessagePayload {
#[prost(string, optional, tag = "1")]
pub name: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "2")]
Expand Down Expand Up @@ -424,7 424,7 @@ pub enum ActionName {
BreakPaneLeft = 79,
RenameSession = 80,
LaunchPlugin = 81,
Message = 82,
CliMessage = 82,
}
impl ActionName {
/// String value of the enum field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -515,7 515,7 @@ impl ActionName {
ActionName::BreakPaneLeft => "BreakPaneLeft",
ActionName::RenameSession => "RenameSession",
ActionName::LaunchPlugin => "LaunchPlugin",
ActionName::Message => "Message",
ActionName::CliMessage => "CliMessage",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -603,7 603,7 @@ impl ActionName {
"BreakPaneLeft" => Some(Self::BreakPaneLeft),
"RenameSession" => Some(Self::RenameSession),
"LaunchPlugin" => Some(Self::LaunchPlugin),
"Message" => Some(Self::Message),
"CliMessage" => Some(Self::CliMessage),
_ => None,
}
}
Expand Down
10 changes: 5 additions & 5 deletions zellij-utils/assets/prost/api.event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 49,7 @@ pub mod event {
#[prost(message, tag = "15")]
WebRequestResultPayload(super::WebRequestResultPayload),
#[prost(message, tag = "16")]
MessagePayload(super::MessagePayload),
CliMessagePayload(super::CliMessagePayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down Expand Up @@ -122,7 122,7 @@ pub struct CustomMessagePayload {
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MessagePayload {
pub struct CliMessagePayload {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
#[prost(string, optional, tag = "2")]
Expand Down Expand Up @@ -332,7 332,7 @@ pub enum EventType {
SessionUpdate = 16,
RunCommandResult = 17,
WebRequestResult = 18,
Message = 19,
CliMessage = 19,
}
impl EventType {
/// String value of the enum field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -360,7 360,7 @@ impl EventType {
EventType::SessionUpdate => "SessionUpdate",
EventType::RunCommandResult => "RunCommandResult",
EventType::WebRequestResult => "WebRequestResult",
EventType::Message => "Message",
EventType::CliMessage => "CliMessage",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand All @@ -385,7 385,7 @@ impl EventType {
"SessionUpdate" => Some(Self::SessionUpdate),
"RunCommandResult" => Some(Self::RunCommandResult),
"WebRequestResult" => Some(Self::WebRequestResult),
"Message" => Some(Self::Message),
"CliMessage" => Some(Self::CliMessage),
_ => None,
}
}
Expand Down
Loading