summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2023-09-02 12:27:14 +0200
committerAram Drevekenin <aram@poor.dev>2023-09-02 12:27:14 +0200
commit89b8c2ade0d7d7e93e3c903ab763daa2ca106fa4 (patch)
tree806ab886e4ef8187b793f13f6c7a96520e2d98fa
parent697723ddd30715e2997ad12352ea3c89ebdb7e17 (diff)
prototype
-rw-r--r--src/main.rs4
-rw-r--r--zellij-client/src/cli_client.rs3
-rw-r--r--zellij-client/src/input_handler.rs10
-rw-r--r--zellij-client/src/lib.rs1
-rw-r--r--zellij-client/src/os_input_output.rs5
-rw-r--r--zellij-server/src/os_input_output.rs1
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs5
-rw-r--r--zellij-server/src/pty.rs108
-rw-r--r--zellij-server/src/route.rs46
-rw-r--r--zellij-server/src/screen.rs90
-rw-r--r--zellij-server/src/tab/mod.rs70
-rw-r--r--zellij-utils/src/cli.rs16
-rw-r--r--zellij-utils/src/errors.rs2
-rw-r--r--zellij-utils/src/input/actions.rs18
-rw-r--r--zellij-utils/src/ipc.rs2
-rw-r--r--zellij-utils/src/plugin_api/action.rs5
16 files changed, 351 insertions, 35 deletions
diff --git a/src/main.rs b/src/main.rs
index f32d76cd8..da059b7fb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,6 +25,7 @@ fn main() {
direction,
cwd,
floating,
+ in_place,
name,
close_on_exit,
start_suspended,
@@ -36,6 +37,7 @@ fn main() {
direction,
cwd,
floating,
+ in_place,
name,
close_on_exit,
start_suspended,
@@ -49,6 +51,7 @@ fn main() {
direction,
line_number,
floating,
+ in_place,
cwd,
})) = opts.command
{
@@ -64,6 +67,7 @@ fn main() {
direction,
line_number,
floating,
+ in_place,
cwd,
};
commands::send_action_to_session(command_cli_action, opts.session, config);
diff --git a/zellij-client/src/cli_client.rs b/zellij-client/src/cli_client.rs
index 197efa141..2a2543adf 100644
--- a/zellij-client/src/cli_client.rs
+++ b/zellij-client/src/cli_client.rs
@@ -18,8 +18,9 @@ pub fn start_cli_client(os_input: Box<dyn ClientOsApi>, session_name: &str, acti
sock_dir
};
os_input.connect_to_server(&*zellij_ipc_pipe);
+ let pane_id = os_input.env_variable("ZELLIJ_PANE_ID").and_then(|e| e.trim().parse().ok());
for action in actions {
- let msg = ClientToServerMsg::Action(action, None);
+ let msg = ClientToServerMsg::Action(action, pane_id, None);
os_input.send_to_server(msg);
}
loop {
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index 2781d3afb..cf5b3d326 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -283,13 +283,13 @@ impl InputHandler {
Action::NoOp => {},
Action::Quit => {
self.os_input
- .send_to_server(ClientToServerMsg::Action(action, client_id));
+ .send_to_server(ClientToServerMsg::Action(action, None, client_id));
self.exit(ExitReason::Normal);
should_break = true;
},
Action::Detach => {
self.os_input
- .send_to_server(ClientToServerMsg::Action(action, client_id));
+ .send_to_server(ClientToServerMsg::Action(action, None, client_id));
self.exit(ExitReason::NormalDetached);
should_break = true;
},
@@ -298,7 +298,7 @@ impl InputHandler {
// server later that atomically changes the mode as well
self.mode = mode;
self.os_input
- .send_to_server(ClientToServerMsg::Action(action, None));
+ .send_to_server(ClientToServerMsg::Action(action, None, None));
},
Action::CloseFocus
| Action::ClearScreen
@@ -318,7 +318,7 @@ impl InputHandler {
| Action::MoveFocusOrTab(_) => {
self.command_is_executing.blocking_input_thread();
self.os_input
- .send_to_server(ClientToServerMsg::Action(action, client_id));
+ .send_to_server(ClientToServerMsg::Action(action, None, client_id));
self.command_is_executing
.wait_until_input_thread_is_unblocked();
},
@@ -333,7 +333,7 @@ impl InputHandler {
},
_ => self
.os_input
- .send_to_server(ClientToServerMsg::Action(action, client_id)),
+ .send_to_server(ClientToServerMsg::Action(action, None, client_id)),
}
should_break
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index 6aeb22e5a..a472cd1dc 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -316,6 +316,7 @@ pub fn start_client(
os_api.send_to_server(ClientToServerMsg::Action(
on_force_close.into(),
None,
+ None,
));
}
}),
diff --git a/zellij-client/src/os_input_output.rs b/zellij-client/src/os_input_output.rs
index 212ed2f7d..bfc8cc076 100644
--- a/zellij-client/src/os_input_output.rs
+++ b/zellij-client/src/os_input_output.rs
@@ -114,6 +114,7 @@ pub trait ClientOsApi: Send + Sync {
fn disable_mouse(&self) -> Result<()>;
// Repeatedly send action, until stdin is readable again
fn stdin_poller(&self) -> StdinPoller;
+ fn env_variable(&self, _name: &str) -> Option<String> { None }
}
impl ClientOsApi for ClientOsInputOutput {
@@ -282,6 +283,10 @@ impl ClientOsApi for ClientOsInputOutput {
fn stdin_poller(&self) -> StdinPoller {
StdinPoller::default()
}
+
+ fn env_variable(&self, name: &str) -> Option<String> {
+ std::env::var(name).ok()
+ }
}
impl Clone for Box<dyn ClientOsApi> {
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index f4cbf9b4a..28cb124c8 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -183,6 +183,7 @@ fn handle_openpty(
}
command
.args(&cmd.args)
+ .env("ZELLIJ_PANE_ID", &format!("{}", terminal_id))
.pre_exec(move || -> std::io::Result<()> {
if libc::login_tty(pid_secondary) != 0 {
panic!("failed to set controlling terminal");
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index 6ec50c3a5..908bfeb08 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -51,6 +51,7 @@ macro_rules! apply_action {
if let Err(e) = route_action(
$action,
$env.plugin_env.client_id,
+ Some(PaneId::Plugin($env.plugin_env.plugin_id)),
$env.plugin_env.senders.clone(),
$env.plugin_env.capabilities.clone(),
$env.plugin_env.client_attributes.clone(),
@@ -347,12 +348,14 @@ fn get_zellij_version(env: &ForeignFunctionEnv) {
fn open_file(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
let error_msg = || format!("failed to open file in plugin {}", env.plugin_env.name());
let floating = false;
+ let in_place = false;
let action = Action::EditFile(
file_to_open.path,
file_to_open.line_number,
file_to_open.cwd,
None,
floating,
+ in_place,
);
apply_action!(action, error_msg, env);
}
@@ -360,12 +363,14 @@ fn open_file(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
fn open_file_floating(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
let error_msg = || format!("failed to open file in plugin {}", env.plugin_env.name());
let floating = true;
+ let in_place = false;
let action = Action::EditFile(
file_to_open.path,
file_to_open.line_number,
file_to_open.cwd,
None,
floating,
+ in_place,
);
apply_action!(action, error_msg, env);
}
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index a785f536d..be363a63f 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -26,9 +26,10 @@ pub type VteBytes = Vec<u8>;
pub type TabIndex = u32;
#[derive(Clone, Copy, Debug)]
-pub enum ClientOrTabIndex {
+pub enum ClientTabIndexOrPaneId {
ClientId(ClientId),
TabIndex(usize),
+ PaneId(PaneId),
}
/// Instructions related to PTYs (pseudoterminals).
@@ -38,7 +39,7 @@ pub enum PtyInstruction {
Option<TerminalAction>,
Option<bool>,
Option<String>,
- ClientOrTabIndex,
+ ClientTabIndexOrPaneId,
), // bool (if Some) is
// should_float, String is an optional pane name
OpenInPlaceEditor(PathBuf, Option<usize>, ClientId), // Option<usize> is the optional line number
@@ -62,6 +63,11 @@ pub enum PtyInstruction {
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
ReRunCommandInPane(PaneId, RunCommand),
+ SpawnInPlaceTerminal(
+ Option<TerminalAction>,
+ Option<String>,
+ ClientTabIndexOrPaneId,
+ ), // String is an optional pane name
Exit,
}
@@ -78,6 +84,7 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab(..) => PtyContext::NewTab,
PtyInstruction::ReRunCommandInPane(..) => PtyContext::ReRunCommandInPane,
+ PtyInstruction::SpawnInPlaceTerminal(..) => PtyContext::SpawnInPlaceTerminal,
PtyInstruction::Exit => PtyContext::Exit,
}
}
@@ -164,13 +171,76 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
},
}
},
+ PtyInstruction::SpawnInPlaceTerminal(
+ terminal_action,
+ name,
+ client_id_tab_index_or_pane_id,
+ ) => {
+ let err_context =
+ || format!("failed to spawn terminal for {:?}", client_id_tab_index_or_pane_id);
+ let (hold_on_close, run_command, pane_title) = match &terminal_action {
+ Some(TerminalAction::RunCommand(run_command)) => (
+ run_command.hold_on_close,
+ Some(run_command.clone()),
+ Some(name.unwrap_or_else(|| run_command.to_string())),
+ ),
+ _ => (false, None, name),
+ };
+ match pty
+ .spawn_terminal(terminal_action, client_id_tab_index_or_pane_id)
+ .with_context(err_context)
+ {
+ Ok((pid, starts_held)) => {
+ let hold_for_command = if starts_held { run_command } else { None };
+ pty.bus
+ .senders
+ .send_to_screen(ScreenInstruction::ReplacePane(
+ PaneId::Terminal(pid),
+ hold_for_command,
+ pane_title,
+ client_id_tab_index_or_pane_id,
+ ))
+ .with_context(err_context)?;
+ },
+ Err(err) => match err.downcast_ref::<ZellijError>() {
+ Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
+ if hold_on_close {
+ let hold_for_command = None; // we do not hold an "error" pane
+ pty.bus
+ .senders
+ .send_to_screen(ScreenInstruction::ReplacePane(
+ PaneId::Terminal(*terminal_id),
+ hold_for_command,
+ pane_title,
+ client_id_tab_index_or_pane_id,
+ ))
+ .with_context(err_context)?;
+ if let Some(run_command) = run_command {
+ send_command_not_found_to_screen(
+ pty.bus.senders.clone(),
+ *terminal_id,
+ run_command.clone(),
+ None,
+ )
+ .with_context(err_context)?;
+ }
+ } else {
+ log::error!("Failed to spawn terminal: {:?}", err);
+ pty.close_pane(PaneId::Terminal(*terminal_id))
+ .with_context(err_context)?;
+ }
+ },
+ _ => Err::<(), _>(err).non_fatal(),
+ },
+ }
+ },
PtyInstruction::OpenInPlaceEditor(temp_file, line_number, client_id) => {
let err_context =
|| format!("failed to open in-place editor for client {}", client_id);
match pty.spawn_terminal(
Some(TerminalAction::OpenFile(temp_file, line_number, None)),
- ClientOrTabIndex::ClientId(client_id),
+ ClientTabIndexOrPaneId::ClientId(client_id),
) {
Ok((pid, _starts_held)) => {
pty.bus
@@ -199,7 +269,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty
- .spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id))
+ .spawn_terminal(terminal_action, ClientTabIndexOrPaneId::ClientId(client_id))
.with_context(err_context)
{
Ok((pid, starts_held)) => {
@@ -270,7 +340,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty
- .spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id))
+ .spawn_terminal(terminal_action, ClientTabIndexOrPaneId::ClientId(client_id))
.with_context(err_context)
{
Ok((pid, starts_held)) => {
@@ -501,25 +571,47 @@ impl Pty {
};
};
}
+ fn fill_cwd_from_pane_id(&self, terminal_action: &mut TerminalAction, pane_id: &u32) {
+ if let TerminalAction::RunCommand(run_command) = terminal_action {
+ if run_command.cwd.is_none() {
+ run_command.cwd = self
+ .id_to_child_pid.get(pane_id)
+ .and_then(|&id| {
+ self.bus
+ .os_input
+ .as_ref()
+ .and_then(|input| input.get_cwd(Pid::from_raw(id)))
+ });
+ };
+ };
+ }
pub fn spawn_terminal(
&mut self,
terminal_action: Option<TerminalAction>,
- client_or_tab_index: ClientOrTabIndex,
+ client_or_tab_index: ClientTabIndexOrPaneId,
) -> Result<(u32, bool)> {
// bool is starts_held
let err_context = || format!("failed to spawn terminal for {:?}", client_or_tab_index);
// returns the terminal id
let terminal_action = match client_or_tab_index {
- ClientOrTabIndex::ClientId(client_id) => {
+ ClientTabIndexOrPaneId::ClientId(client_id) => {
let mut terminal_action =
terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None));
self.fill_cwd(&mut terminal_action, client_id);
terminal_action
},
- ClientOrTabIndex::TabIndex(_) => {
+ ClientTabIndexOrPaneId::TabIndex(_) => {
terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None))
},
+ ClientTabIndexOrPaneId::PaneId(pane_id) => {
+ let mut terminal_action =
+ terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None));
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ self.fill_cwd_from_pane_id(&mut terminal_action, &terminal_pane_id);
+ }
+ terminal_action
+ }
};
let (hold_on_start, hold_on_close) = match &terminal_action {
TerminalAction::RunCommand(run_command) => {
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index b29dd934f..54489584d 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -6,7 +6,7 @@ use crate::{
os_input_output::ServerOsApi,
panes::PaneId,
plugins::PluginInstruction,
- pty::{ClientOrTabIndex, PtyInstruction},
+ pty::{ClientTabIndexOrPaneId, PtyInstruction},
screen::ScreenInstruction,
ServerInstruction, SessionMetaData, SessionState,
};
@@ -30,6 +30,7 @@ use crate::ClientId;
pub(crate) fn route_action(
action: Action,
client_id: ClientId,
+ pane_id: Option<PaneId>,
senders: ThreadSenders,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
@@ -257,12 +258,12 @@ pub(crate) fn route_action(
shell,
None,
name,
- ClientOrTabIndex::ClientId(client_id),
+ ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
},
- Action::EditFile(path_to_file, line_number, cwd, split_direction, should_float) => {
+ Action::EditFile(path_to_file, line_number, cwd, split_direction, should_float, should_open_in_place) => {
let title = format!("Editing: {}", path_to_file.display());
let open_file = TerminalAction::OpenFile(path_to_file, line_number, cwd);
let pty_instr = match (split_direction, should_float) {
@@ -287,7 +288,7 @@ pub(crate) fn route_action(
Some(open_file),
Some(should_float),
Some(title),
- ClientOrTabIndex::ClientId(client_id),
+ ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
@@ -319,10 +320,38 @@ pub(crate) fn route_action(
run_cmd,
Some(should_float),
name,
- ClientOrTabIndex::ClientId(client_id),
+ ClientTabIndexOrPaneId::ClientId(client_id),
))
.with_context(err_context)?;
},
+ Action::NewInPlacePane(run_command, name) => {
+ let run_cmd = run_command
+ .map(|cmd| TerminalAction::RunCommand(cmd.into()))
+ .or_else(|| default_shell.clone());
+ match pane_id {
+ Some(pane_id) => {
+ senders
+ .send_to_pty(PtyInstruction::SpawnInPlaceTerminal(
+ run_cmd,
+ name,
+ ClientTabIndexOrPaneId::PaneId(pane_id),
+ ))
+ .with_context(err_context)?;
+ },
+ None => {
+ senders
+ .send_to_pty(PtyInstruction::SpawnInPlaceTerminal(
+ run_cmd,
+ name,
+ ClientTabIndexOrPaneId::ClientId(client_id)
+ ))
+ .with_context(err_context)?;
+ },
+ _ => {
+ log::error!("To open an in place editor, we must have either a pane id or a client id")
+ }
+ }
+ },
Action::NewTiledPane(direction, run_command, name) => {
let should_float = false;
let run_cmd = run_command
@@ -346,7 +375,7 @@ pub(crate) fn route_action(
run_cmd,
Some(should_float),
name,
- ClientOrTabIndex::ClientId(client_id),
+ ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
@@ -394,7 +423,7 @@ pub(crate) fn route_action(
run_cmd,
None,
None,
- ClientOrTabIndex::ClientId(client_id),
+ ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
@@ -752,7 +781,7 @@ pub(crate) fn route_thread_main(
-> Result<bool> {
let mut should_break = false;
match instruction {
- ClientToServerMsg::Action(action, maybe_client_id) => {
+ ClientToServerMsg::Action(action, maybe_pane_id, maybe_client_id) => {
let client_id = maybe_client_id.unwrap_or(client_id);
if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
if let Action::SwitchToMode(input_mode) = action {
@@ -769,6 +798,7 @@ pub(crate) fn route_thread_main(
if route_action(
action,
client_id,
+ maybe_pane_id.map(|p| PaneId::Terminal(p)),
rlocked_sessions.senders.clone(),
rlocked_sessions.capabilities.clone(),
rlocked_sessions.client_attributes.clone(),
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index efa9110b8..b4832541f 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -32,7 +32,7 @@ use crate::{
panes::sixel::SixelImageStore,
panes::PaneId,
plugins::PluginInstruction,
- pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
+ pty::{ClientTabIndexOrPaneId, PtyInstruction, VteBytes},
tab::Tab,
thread_bus::Bus,
ui::{
@@ -141,7 +141,7 @@ pub enum ScreenInstruction {
Option<InitialTitle>,
Option<ShouldFloat>,
HoldForCommand,
- ClientOrTabIndex,
+ ClientTabIndexOrPaneId,
),
OpenInPlaceEditor(PaneId, ClientId),
TogglePaneEmbedOrFloating(ClientId),
@@ -294,6 +294,12 @@ pub enum ScreenInstruction {
BreakPaneRight(ClientId),
BreakPaneLeft(ClientId),
UpdateSessionInfos(BTreeMap<String, SessionInfo>), // String is the session name
+ ReplacePane(
+ PaneId,
+ HoldForCommand,
+ Option<InitialTitle>,
+ ClientTabIndexOrPaneId
+ ),
}
impl From<&ScreenInstruction> for ScreenContext {
@@ -468,6 +474,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::BreakPaneRight(..) => ScreenContext::BreakPaneRight,
ScreenInstruction::BreakPaneLeft(..) => ScreenContext::BreakPaneLeft,
ScreenInstruction::UpdateSessionInfos(..) => ScreenContext::UpdateSessionInfos,
+ ScreenInstruction::ReplacePane(..) => ScreenContext::ReplacePane,
}
}
}
@@ -1822,7 +1829,66 @@ impl Screen {
self.render()?;
Ok(())
}
-
+ pub fn replace_pane(
+ &mut self,
+ new_pane_id: PaneId,
+ hold_for_command: HoldForCommand,
+ pane_title: Option<InitialTitle>,
+ client_id_tab_index_or_pane_id: ClientTabIndexOrPaneId
+ ) -> Result<()> {
+ let err_context = || format!("failed to replace pane");
+ let suppress_pane = |tab: &mut Tab, pane_id: PaneId, new_pane_id: PaneId| {
+ tab.suppress_pane_and_replace_with_pid(pane_id, new_pane_id);
+ if let Some(pane_title) = pane_title {
+ tab.rename_pane(pane_title.as_bytes().to_vec(), new_pane_id);
+ }
+ if let Some(hold_for_command) = hold_for_command {
+ let is_first_run = true;
+ tab.hold_pane(
+ new_pane_id,
+ None,
+ is_first_run,
+ hold_for_command
+ )
+ }
+ };
+ match client_id_tab_index_