feat: stack pane action (#4255)
* refactor: group placement properties * add stackpane cli and keybinding * add test * refactor: move spawn vertically/horizontally to spawnterminal * fix tests and cleanups * some cleanups and minor fixes * more cleanups * add stack action to the UI * style(fmt): rustfmt * fix serialization * add to default config * fix e2e tests * style(fmt): rustfmt * fix cli * fix tests * docs(changelog): add PR
This commit is contained in:
parent
02a0d055b6
commit
ca0048bdcb
44 changed files with 1202 additions and 863 deletions
|
|
@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||
* feat: web-client, allowing users to share sessions in the browser (https://github.com/zellij-org/zellij/pull/4242)
|
||||
* performance: consolidate renders (https://github.com/zellij-org/zellij/pull/4245)
|
||||
* feat: add plugin API to replace a pane with another existing pane (https://github.com/zellij-org/zellij/pull/4246)
|
||||
* feat: add "stack" keybinding and CLI action to add a stacked pane to the current pane (https://github.com/zellij-org/zellij/pull/4255)
|
||||
|
||||
## [0.42.2] - 2025-04-15
|
||||
* refactor(terminal): track scroll_region as tuple rather than Option (https://github.com/zellij-org/zellij/pull/4082)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ keybinds clear-defaults=true {{
|
|||
bind "n" {{ NewPane; SwitchToMode "Locked"; }}
|
||||
bind "d" {{ NewPane "Down"; SwitchToMode "Locked"; }}
|
||||
bind "r" {{ NewPane "Right"; SwitchToMode "Locked"; }}
|
||||
bind "s" {{ NewPane "stacked"; SwitchToMode "Locked"; }}
|
||||
bind "x" {{ CloseFocus; SwitchToMode "Locked"; }}
|
||||
bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Locked"; }}
|
||||
bind "z" {{ TogglePaneFrames; SwitchToMode "Locked"; }}
|
||||
|
|
@ -244,6 +245,7 @@ keybinds clear-defaults=true {{
|
|||
bind "n" {{ NewPane; SwitchToMode "Normal"; }}
|
||||
bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
|
||||
bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
|
||||
bind "s" {{ NewPane "stacked"; SwitchToMode "Normal"; }}
|
||||
bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
|
||||
bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
|
||||
bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
|
||||
|
|
@ -468,6 +470,7 @@ keybinds clear-defaults=true {{
|
|||
bind "n" {{ NewPane; SwitchToMode "Normal"; }}
|
||||
bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
|
||||
bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
|
||||
bind "s" {{ NewPane "stacked"; SwitchToMode "Normal"; }}
|
||||
bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
|
||||
bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
|
||||
bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
|
||||
|
|
@ -663,6 +666,7 @@ keybinds clear-defaults=true {{
|
|||
bind "n" {{ NewPane; SwitchToMode "Normal"; }}
|
||||
bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
|
||||
bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
|
||||
bind "s" {{ NewPane "stacked"; SwitchToMode "Normal"; }}
|
||||
bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
|
||||
bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
|
||||
bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
|
||||
|
|
@ -865,6 +869,7 @@ keybinds clear-defaults=true {{
|
|||
bind "n" {{ NewPane; SwitchToMode "Normal"; }}
|
||||
bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
|
||||
bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
|
||||
bind "s" {{ NewPane "stacked"; SwitchToMode "Normal"; }}
|
||||
bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
|
||||
bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
|
||||
bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
|
||||
|
|
@ -1042,6 +1047,7 @@ keybinds clear-defaults=true {{
|
|||
bind "n" {{ NewPane; SwitchToMode "Normal"; }}
|
||||
bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
|
||||
bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
|
||||
bind "s" {{ NewPane "stacked"; SwitchToMode "Normal"; }}
|
||||
bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
|
||||
bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
|
||||
bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
|
||||
|
|
|
|||
|
|
@ -1276,6 +1276,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
|
|||
(s("Toggle Embed"), s("Embed"), single_action_key(&km, &[A::TogglePaneEmbedOrFloating, TO_NORMAL])),
|
||||
(s("Split Right"), s("Right"), single_action_key(&km, &[A::NewPane(Some(Direction::Right), None, false), TO_NORMAL])),
|
||||
(s("Split Down"), s("Down"), single_action_key(&km, &[A::NewPane(Some(Direction::Down), None, false), TO_NORMAL])),
|
||||
(s("Stack"), s("Stack"), single_action_key(&km, &[A::NewStackedPane(None, None), TO_NORMAL])),
|
||||
(s("Select pane"), s("Select"), to_basemode_key),
|
||||
]} else if mi.mode == IM::Tab {
|
||||
// With the default bindings, "Move focus" for tabs is tricky: It binds all the arrow keys
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ fn main() {
|
|||
width,
|
||||
height,
|
||||
pinned,
|
||||
stacked,
|
||||
})) = opts.command
|
||||
{
|
||||
let cwd = cwd.or_else(|| std::env::current_dir().ok());
|
||||
|
|
@ -59,6 +60,7 @@ fn main() {
|
|||
width,
|
||||
height,
|
||||
pinned,
|
||||
stacked,
|
||||
};
|
||||
commands::send_action_to_session(command_cli_action, opts.session, config);
|
||||
std::process::exit(0);
|
||||
|
|
@ -77,6 +79,7 @@ fn main() {
|
|||
})) = opts.command
|
||||
{
|
||||
let cwd = None;
|
||||
let stacked = false;
|
||||
let command_cli_action = CliAction::NewPane {
|
||||
command: vec![],
|
||||
plugin: Some(url),
|
||||
|
|
@ -94,6 +97,7 @@ fn main() {
|
|||
width,
|
||||
height,
|
||||
pinned,
|
||||
stacked,
|
||||
};
|
||||
commands::send_action_to_session(command_cli_action, opts.session, config);
|
||||
std::process::exit(0);
|
||||
|
|
|
|||
|
|
@ -1045,7 +1045,6 @@ pub fn resize_terminal_window() {
|
|||
name: "wait for terminal to be resized and app to be re-rendered",
|
||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||
let mut step_is_complete = false;
|
||||
eprintln!("current_snapshot: {}", remote_terminal.current_snapshot());
|
||||
if remote_terminal.cursor_position_is(53, 2) && remote_terminal.ctrl_plus_appears()
|
||||
{
|
||||
// size has been changed
|
||||
|
|
|
|||
|
|
@ -395,6 +395,72 @@ impl TiledPanes {
|
|||
},
|
||||
}
|
||||
}
|
||||
pub fn add_pane_to_stack_of_active_pane(
|
||||
&mut self,
|
||||
pane_id: PaneId,
|
||||
mut pane: Box<dyn Pane>,
|
||||
client_id: ClientId,
|
||||
) {
|
||||
let mut pane_grid = TiledPaneGrid::new(
|
||||
&mut self.panes,
|
||||
&self.panes_to_hide,
|
||||
*self.display_area.borrow(),
|
||||
*self.viewport.borrow(),
|
||||
);
|
||||
let Some(active_pane_id) = self.active_panes.get(&client_id) else {
|
||||
log::error!("Could not find active pane id for client_id");
|
||||
return;
|
||||
};
|
||||
let pane_id_is_stacked = pane_grid
|
||||
.get_pane_geom(active_pane_id)
|
||||
.map(|p| p.is_stacked())
|
||||
.unwrap_or(false);
|
||||
if !pane_id_is_stacked {
|
||||
let _ = pane_grid.make_pane_stacked(&active_pane_id);
|
||||
}
|
||||
match pane_grid.make_room_in_stack_of_pane_id_for_pane(active_pane_id) {
|
||||
Ok(new_pane_geom) => {
|
||||
pane.set_geom(new_pane_geom);
|
||||
self.panes.insert(pane_id, pane);
|
||||
self.set_force_render(); // TODO: why do we need this?
|
||||
return;
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to add pane to stack: {}", e);
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn add_pane_to_stack_of_pane_id(
|
||||
&mut self,
|
||||
pane_id: PaneId,
|
||||
mut pane: Box<dyn Pane>,
|
||||
root_pane_id: PaneId,
|
||||
) {
|
||||
let mut pane_grid = TiledPaneGrid::new(
|
||||
&mut self.panes,
|
||||
&self.panes_to_hide,
|
||||
*self.display_area.borrow(),
|
||||
*self.viewport.borrow(),
|
||||
);
|
||||
let pane_id_is_stacked = pane_grid
|
||||
.get_pane_geom(&root_pane_id)
|
||||
.map(|p| p.is_stacked())
|
||||
.unwrap_or(false);
|
||||
if !pane_id_is_stacked {
|
||||
let _ = pane_grid.make_pane_stacked(&root_pane_id);
|
||||
}
|
||||
match pane_grid.make_room_in_stack_of_pane_id_for_pane(&root_pane_id) {
|
||||
Ok(new_pane_geom) => {
|
||||
pane.set_geom(new_pane_geom);
|
||||
self.panes.insert(pane_id, pane);
|
||||
self.set_force_render(); // TODO: why do we need this?
|
||||
return;
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to add pane to stack: {}", e);
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn fixed_pane_geoms(&self) -> Vec<Viewport> {
|
||||
self.panes
|
||||
.values()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 6878
|
||||
expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")"
|
||||
expression: "format!(\"{:#?}\",\nnew_tab_event).replace(&format!(\"{:?}\", temp_folder.path()), \"\\\"CWD\\\"\")"
|
||||
---
|
||||
Some(
|
||||
SpawnTerminal(
|
||||
|
|
@ -29,8 +28,7 @@ Some(
|
|||
),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
NoPreference,
|
||||
true,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4557
|
||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -26,11 +25,10 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
true,
|
||||
None,
|
||||
Floating(
|
||||
None,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4479
|
||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -26,11 +25,10 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
false,
|
||||
None,
|
||||
Tiled(
|
||||
None,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 3996
|
||||
expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")"
|
||||
expression: "format!(\"{:#?}\",\nnew_tab_event).replace(&format!(\"{:?}\", temp_folder.path()), \"\\\"CWD\\\"\")"
|
||||
---
|
||||
Some(
|
||||
SpawnTerminal(
|
||||
|
|
@ -23,13 +22,12 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
true,
|
||||
),
|
||||
Some(
|
||||
"Editing: /path/to/my/file.rs",
|
||||
),
|
||||
None,
|
||||
Floating(
|
||||
None,
|
||||
),
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4078
|
||||
expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")"
|
||||
expression: "format!(\"{:#?}\",\nnew_tab_event).replace(&format!(\"{:?}\", temp_folder.path()), \"\\\"CWD\\\"\")"
|
||||
---
|
||||
Some(
|
||||
SpawnTerminal(
|
||||
|
|
@ -23,13 +22,12 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
false,
|
||||
),
|
||||
Some(
|
||||
"Editing: /path/to/my/file.rs",
|
||||
),
|
||||
None,
|
||||
Tiled(
|
||||
None,
|
||||
),
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4243
|
||||
expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")"
|
||||
expression: "format!(\"{:#?}\",\nnew_tab_event).replace(&format!(\"{:?}\", temp_folder.path()), \"\\\"CWD\\\"\")"
|
||||
---
|
||||
Some(
|
||||
SpawnTerminal(
|
||||
|
|
@ -25,13 +24,12 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
true,
|
||||
),
|
||||
Some(
|
||||
"Editing: /path/to/my/file.rs",
|
||||
),
|
||||
None,
|
||||
Floating(
|
||||
None,
|
||||
),
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4161
|
||||
expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")"
|
||||
expression: "format!(\"{:#?}\",\nnew_tab_event).replace(&format!(\"{:?}\", temp_folder.path()), \"\\\"CWD\\\"\")"
|
||||
---
|
||||
Some(
|
||||
SpawnTerminal(
|
||||
|
|
@ -25,13 +24,12 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
false,
|
||||
),
|
||||
Some(
|
||||
"Editing: /path/to/my/file.rs",
|
||||
),
|
||||
None,
|
||||
Tiled(
|
||||
None,
|
||||
),
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4401
|
||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -19,11 +18,10 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
true,
|
||||
None,
|
||||
Floating(
|
||||
None,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4323
|
||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -19,11 +18,10 @@ Some(
|
|||
},
|
||||
),
|
||||
),
|
||||
Some(
|
||||
false,
|
||||
None,
|
||||
Tiled(
|
||||
None,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
ClientId(
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::PluginInstruction;
|
|||
use crate::background_jobs::BackgroundJob;
|
||||
use crate::plugins::plugin_map::PluginEnv;
|
||||
use crate::plugins::wasm_bridge::handle_plugin_crash;
|
||||
use crate::pty::{ClientTabIndexOrPaneId, PtyInstruction};
|
||||
use crate::pty::{ClientTabIndexOrPaneId, NewPanePlacement, PtyInstruction};
|
||||
use crate::route::route_action;
|
||||
use crate::ServerInstruction;
|
||||
use async_std::task;
|
||||
|
|
@ -744,14 +744,12 @@ fn open_file_near_plugin(
|
|||
OriginatingPlugin::new(env.plugin_id, env.client_id, context),
|
||||
);
|
||||
let title = format!("Editing: {}", open_file_payload.path.display());
|
||||
let should_float = false;
|
||||
let start_suppressed = false;
|
||||
let open_file = TerminalAction::OpenFile(open_file_payload);
|
||||
let pty_instr = PtyInstruction::SpawnTerminal(
|
||||
Some(open_file),
|
||||
Some(should_float),
|
||||
Some(title),
|
||||
None,
|
||||
NewPanePlacement::default(),
|
||||
start_suppressed,
|
||||
ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)),
|
||||
);
|
||||
|
|
@ -774,14 +772,12 @@ fn open_file_floating_near_plugin(
|
|||
OriginatingPlugin::new(env.plugin_id, env.client_id, context),
|
||||
);
|
||||
let title = format!("Editing: {}", open_file_payload.path.display());
|
||||
let should_float = true;
|
||||
let start_suppressed = false;
|
||||
let open_file = TerminalAction::OpenFile(open_file_payload);
|
||||
let pty_instr = PtyInstruction::SpawnTerminal(
|
||||
Some(open_file),
|
||||
Some(should_float),
|
||||
Some(title),
|
||||
floating_pane_coordinates,
|
||||
NewPanePlacement::Floating(floating_pane_coordinates),
|
||||
start_suppressed,
|
||||
ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)),
|
||||
);
|
||||
|
|
@ -834,7 +830,6 @@ fn open_terminal(env: &PluginEnv, cwd: PathBuf) {
|
|||
|
||||
fn open_terminal_near_plugin(env: &PluginEnv, cwd: PathBuf) {
|
||||
let cwd = env.plugin_cwd.join(cwd);
|
||||
let should_float = false;
|
||||
let mut default_shell = env.default_shell.clone().unwrap_or_else(|| {
|
||||
TerminalAction::RunCommand(RunCommand {
|
||||
command: env.path_to_default_shell.clone(),
|
||||
|
|
@ -845,9 +840,8 @@ fn open_terminal_near_plugin(env: &PluginEnv, cwd: PathBuf) {
|
|||
default_shell.change_cwd(cwd);
|
||||
let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
Some(default_shell),
|
||||
Some(should_float),
|
||||
name,
|
||||
None,
|
||||
NewPanePlacement::Tiled(None),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)),
|
||||
));
|
||||
|
|
@ -881,7 +875,6 @@ fn open_terminal_floating_near_plugin(
|
|||
floating_pane_coordinates: Option<FloatingPaneCoordinates>,
|
||||
) {
|
||||
let cwd = env.plugin_cwd.join(cwd);
|
||||
let should_float = true;
|
||||
let mut default_shell = env.default_shell.clone().unwrap_or_else(|| {
|
||||
TerminalAction::RunCommand(RunCommand {
|
||||
command: env.path_to_default_shell.clone(),
|
||||
|
|
@ -892,9 +885,8 @@ fn open_terminal_floating_near_plugin(
|
|||
let name = None;
|
||||
let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
Some(default_shell),
|
||||
Some(should_float),
|
||||
name,
|
||||
floating_pane_coordinates,
|
||||
NewPanePlacement::Floating(floating_pane_coordinates),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)),
|
||||
));
|
||||
|
|
@ -1021,7 +1013,6 @@ fn open_command_pane_near_plugin(
|
|||
let hold_on_close = true;
|
||||
let hold_on_start = false;
|
||||
let name = None;
|
||||
let should_float = false;
|
||||
let run_command_action = RunCommandAction {
|
||||
command,
|
||||
args,
|
||||
|
|
@ -1038,9 +1029,8 @@ fn open_command_pane_near_plugin(
|
|||
let run_cmd = TerminalAction::RunCommand(run_command_action.into());
|
||||
let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
Some(run_cmd),
|
||||
Some(should_float),
|
||||
name,
|
||||
None,
|
||||
NewPanePlacement::Tiled(None),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)),
|
||||
));
|
||||
|
|
@ -1090,7 +1080,6 @@ fn open_command_pane_floating_near_plugin(
|
|||
let hold_on_close = true;
|
||||
let hold_on_start = false;
|
||||
let name = None;
|
||||
let should_float = true;
|
||||
let run_command_action = RunCommandAction {
|
||||
command,
|
||||
args,
|
||||
|
|
@ -1107,9 +1096,8 @@ fn open_command_pane_floating_near_plugin(
|
|||
let run_cmd = TerminalAction::RunCommand(run_command_action.into());
|
||||
let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
Some(run_cmd),
|
||||
Some(should_float),
|
||||
name,
|
||||
floating_pane_coordinates,
|
||||
NewPanePlacement::Floating(floating_pane_coordinates),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)),
|
||||
));
|
||||
|
|
@ -1177,9 +1165,8 @@ fn open_command_pane_background(
|
|||
let run_cmd = TerminalAction::RunCommand(run_command_action.into());
|
||||
let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
Some(run_cmd),
|
||||
None,
|
||||
name,
|
||||
None,
|
||||
NewPanePlacement::default(),
|
||||
start_suppressed,
|
||||
ClientTabIndexOrPaneId::ClientId(env.client_id),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use nix::unistd::Pid;
|
|||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, os::unix::io::RawFd, path::PathBuf};
|
||||
use zellij_utils::{
|
||||
data::{Event, FloatingPaneCoordinates, OriginatingPlugin},
|
||||
data::{Direction, Event, FloatingPaneCoordinates, OriginatingPlugin},
|
||||
errors::prelude::*,
|
||||
errors::{ContextType, PtyContext},
|
||||
input::{
|
||||
|
|
@ -37,26 +37,95 @@ pub enum ClientTabIndexOrPaneId {
|
|||
PaneId(PaneId),
|
||||
}
|
||||
|
||||
// TODO: move elsewhere
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NewPanePlacement {
|
||||
NoPreference,
|
||||
Tiled(Option<Direction>),
|
||||
Floating(Option<FloatingPaneCoordinates>),
|
||||
InPlace {
|
||||
pane_id_to_replace: Option<PaneId>,
|
||||
close_replaced_pane: bool,
|
||||
},
|
||||
Stacked(Option<PaneId>),
|
||||
}
|
||||
|
||||
impl Default for NewPanePlacement {
|
||||
fn default() -> Self {
|
||||
NewPanePlacement::NoPreference
|
||||
}
|
||||
}
|
||||
|
||||
impl NewPanePlacement {
|
||||
pub fn with_floating_pane_coordinates(
|
||||
floating_pane_coordinates: Option<FloatingPaneCoordinates>,
|
||||
) -> Self {
|
||||
NewPanePlacement::Floating(floating_pane_coordinates)
|
||||
}
|
||||
pub fn with_should_be_in_place(
|
||||
self,
|
||||
should_be_in_place: bool,
|
||||
close_replaced_pane: bool,
|
||||
) -> Self {
|
||||
if should_be_in_place {
|
||||
NewPanePlacement::InPlace {
|
||||
pane_id_to_replace: None,
|
||||
close_replaced_pane,
|
||||
}
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
pub fn with_pane_id_to_replace(
|
||||
pane_id_to_replace: Option<PaneId>,
|
||||
close_replaced_pane: bool,
|
||||
) -> Self {
|
||||
NewPanePlacement::InPlace {
|
||||
pane_id_to_replace,
|
||||
close_replaced_pane,
|
||||
}
|
||||
}
|
||||
pub fn should_float(&self) -> Option<bool> {
|
||||
match self {
|
||||
NewPanePlacement::Floating(_) => Some(true),
|
||||
NewPanePlacement::Tiled(_) => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn floating_pane_coordinates(&self) -> Option<FloatingPaneCoordinates> {
|
||||
match self {
|
||||
NewPanePlacement::Floating(floating_pane_coordinates) => {
|
||||
floating_pane_coordinates.clone()
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn should_stack(&self) -> bool {
|
||||
match self {
|
||||
NewPanePlacement::Stacked(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn id_of_stack_root(&self) -> Option<PaneId> {
|
||||
match self {
|
||||
NewPanePlacement::Stacked(id) => *id,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Instructions related to PTYs (pseudoterminals).
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PtyInstruction {
|
||||
SpawnTerminal(
|
||||
Option<TerminalAction>,
|
||||
Option<bool>,
|
||||
Option<String>,
|
||||
Option<FloatingPaneCoordinates>,
|
||||
NewPanePlacement,
|
||||
bool, // start suppressed
|
||||
ClientTabIndexOrPaneId,
|
||||
), // bool (if Some) is
|
||||
// should_float, String is an optional pane name
|
||||
OpenInPlaceEditor(PathBuf, Option<usize>, ClientTabIndexOrPaneId), // Option<usize> is the optional line number
|
||||
SpawnTerminalVertically(Option<TerminalAction>, Option<String>, ClientId), // String is an
|
||||
// optional pane
|
||||
// name
|
||||
// bool is start_suppressed
|
||||
SpawnTerminalHorizontally(Option<TerminalAction>, Option<String>, ClientId), // String is an
|
||||
// optional pane
|
||||
// name
|
||||
UpdateActivePane(Option<PaneId>, ClientId),
|
||||
GoToTab(TabIndex, ClientId),
|
||||
NewTab(
|
||||
|
|
@ -114,8 +183,6 @@ impl From<&PtyInstruction> for PtyContext {
|
|||
match *pty_instruction {
|
||||
PtyInstruction::SpawnTerminal(..) => PtyContext::SpawnTerminal,
|
||||
PtyInstruction::OpenInPlaceEditor(..) => PtyContext::OpenInPlaceEditor,
|
||||
PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically,
|
||||
PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally,
|
||||
PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane,
|
||||
PtyInstruction::GoToTab(..) => PtyContext::GoToTab,
|
||||
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
|
||||
|
|
@ -153,9 +220,8 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
|||
match event {
|
||||
PtyInstruction::SpawnTerminal(
|
||||
terminal_action,
|
||||
should_float,
|
||||
name,
|
||||
floating_pane_coordinates,
|
||||
new_pane_placement,
|
||||
start_suppressed,
|
||||
client_or_tab_index,
|
||||
) => {
|
||||
|
|
@ -235,10 +301,9 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
|||
.send_to_screen(ScreenInstruction::NewPane(
|
||||
PaneId::Terminal(pid),
|
||||
pane_title,
|
||||
should_float,
|
||||
hold_for_command,
|
||||
invoked_with,
|
||||
floating_pane_coordinates,
|
||||
new_pane_placement,
|
||||
start_suppressed,
|
||||
client_or_tab_index,
|
||||
))
|
||||
|
|
@ -253,10 +318,9 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
|||
.send_to_screen(ScreenInstruction::NewPane(
|
||||
PaneId::Terminal(*terminal_id),
|
||||
pane_title,
|
||||
should_float,
|
||||
hold_for_command,
|
||||
invoked_with,
|
||||
floating_pane_coordinates,
|
||||
new_pane_placement,
|
||||
start_suppressed,
|
||||
client_or_tab_index,
|
||||
))
|
||||
|
|
@ -390,144 +454,6 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
|||
},
|
||||
}
|
||||
},
|
||||
PtyInstruction::SpawnTerminalVertically(terminal_action, name, client_id) => {
|
||||
let err_context =
|
||||
|| format!("failed to spawn terminal vertically for client {client_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, ClientTabIndexOrPaneId::ClientId(client_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::VerticalSplit(
|
||||
PaneId::Terminal(pid),
|
||||
pane_title,
|
||||
hold_for_command,
|
||||
client_id,
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
},
|
||||
Err(err) => match err.downcast_ref::<ZellijError>() {
|
||||
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
||||
let hold_for_command = None; // we do not hold an "error" pane
|
||||
if hold_on_close {
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::VerticalSplit(
|
||||
PaneId::Terminal(*terminal_id),
|
||||
pane_title,
|
||||
hold_for_command,
|
||||
client_id,
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
if let Some(run_command) = run_command {
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::PtyBytes(
|
||||
*terminal_id,
|
||||
format!(
|
||||
"Command not found: {}",
|
||||
run_command.command.display()
|
||||
)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::HoldPane(
|
||||
PaneId::Terminal(*terminal_id),
|
||||
Some(2), // exit status
|
||||
run_command,
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => Err::<(), _>(err).non_fatal(),
|
||||
},
|
||||
}
|
||||
},
|
||||
PtyInstruction::SpawnTerminalHorizontally(terminal_action, name, client_id) => {
|
||||
let err_context =
|
||||
|| format!("failed to spawn terminal horizontally for client {client_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, ClientTabIndexOrPaneId::ClientId(client_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::HorizontalSplit(
|
||||
PaneId::Terminal(pid),
|
||||
pane_title,
|
||||
hold_for_command,
|
||||
client_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::HorizontalSplit(
|
||||
PaneId::Terminal(*terminal_id),
|
||||
pane_title,
|
||||
hold_for_command,
|
||||
client_id,
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
if let Some(run_command) = run_command {
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::PtyBytes(
|
||||
*terminal_id,
|
||||
format!(
|
||||
"Command not found: {}",
|
||||
run_command.command.display()
|
||||
)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::HoldPane(
|
||||
PaneId::Terminal(*terminal_id),
|
||||
Some(2), // exit status
|
||||
run_command,
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => Err::<(), _>(err).non_fatal(),
|
||||
},
|
||||
}
|
||||
},
|
||||
PtyInstruction::UpdateActivePane(pane_id, client_id) => {
|
||||
pty.set_active_pane(pane_id, client_id);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
os_input_output::ServerOsApi,
|
||||
panes::PaneId,
|
||||
plugins::PluginInstruction,
|
||||
pty::{ClientTabIndexOrPaneId, PtyInstruction},
|
||||
pty::{ClientTabIndexOrPaneId, NewPanePlacement, PtyInstruction},
|
||||
screen::ScreenInstruction,
|
||||
ServerInstruction, SessionMetaData, SessionState,
|
||||
};
|
||||
|
|
@ -257,30 +257,13 @@ pub(crate) fn route_action(
|
|||
},
|
||||
Action::NewPane(direction, name, start_suppressed) => {
|
||||
let shell = default_shell.clone();
|
||||
let pty_instr = match direction {
|
||||
Some(Direction::Left) => {
|
||||
PtyInstruction::SpawnTerminalVertically(shell, name, client_id)
|
||||
},
|
||||
Some(Direction::Right) => {
|
||||
PtyInstruction::SpawnTerminalVertically(shell, name, client_id)
|
||||
},
|
||||
Some(Direction::Up) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(shell, name, client_id)
|
||||
},
|
||||
Some(Direction::Down) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(shell, name, client_id)
|
||||
},
|
||||
// No direction specified - try to put it in the biggest available spot
|
||||
None => PtyInstruction::SpawnTerminal(
|
||||
shell,
|
||||
None,
|
||||
name,
|
||||
None,
|
||||
start_suppressed,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
),
|
||||
};
|
||||
senders.send_to_pty(pty_instr).with_context(err_context)?;
|
||||
senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
shell,
|
||||
name,
|
||||
NewPanePlacement::Tiled(direction),
|
||||
start_suppressed,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
));
|
||||
},
|
||||
Action::EditFile(
|
||||
open_file_payload,
|
||||
|
|
@ -292,25 +275,8 @@ pub(crate) fn route_action(
|
|||
) => {
|
||||
let title = format!("Editing: {}", open_file_payload.path.display());
|
||||
let open_file = TerminalAction::OpenFile(open_file_payload);
|
||||
let pty_instr = match (split_direction, should_float, should_open_in_place) {
|
||||
(Some(Direction::Left), false, false) => {
|
||||
PtyInstruction::SpawnTerminalVertically(Some(open_file), Some(title), client_id)
|
||||
},
|
||||
(Some(Direction::Right), false, false) => {
|
||||
PtyInstruction::SpawnTerminalVertically(Some(open_file), Some(title), client_id)
|
||||
},
|
||||
(Some(Direction::Up), false, false) => PtyInstruction::SpawnTerminalHorizontally(
|
||||
Some(open_file),
|
||||
Some(title),
|
||||
client_id,
|
||||
),
|
||||
(Some(Direction::Down), false, false) => PtyInstruction::SpawnTerminalHorizontally(
|
||||
Some(open_file),
|
||||
Some(title),
|
||||
client_id,
|
||||
),
|
||||
// open terminal in place
|
||||
(_, _, true) => match pane_id {
|
||||
let pty_instr = if should_open_in_place {
|
||||
match pane_id {
|
||||
Some(pane_id) => PtyInstruction::SpawnInPlaceTerminal(
|
||||
Some(open_file),
|
||||
Some(title),
|
||||
|
|
@ -323,17 +289,19 @@ pub(crate) fn route_action(
|
|||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
),
|
||||
},
|
||||
// Open either floating terminal if we were asked with should_float or defer
|
||||
// placement to screen
|
||||
(None, _, _) | (_, true, _) => PtyInstruction::SpawnTerminal(
|
||||
}
|
||||
} else {
|
||||
PtyInstruction::SpawnTerminal(
|
||||
Some(open_file),
|
||||
Some(should_float),
|
||||
Some(title),
|
||||
floating_pane_coordinates,
|
||||
if should_float {
|
||||
NewPanePlacement::Floating(floating_pane_coordinates)
|
||||
} else {
|
||||
NewPanePlacement::Tiled(split_direction)
|
||||
},
|
||||
start_suppressed,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
),
|
||||
)
|
||||
};
|
||||
senders.send_to_pty(pty_instr).with_context(err_context)?;
|
||||
},
|
||||
|
|
@ -368,16 +336,14 @@ pub(crate) fn route_action(
|
|||
.with_context(err_context)?;
|
||||
},
|
||||
Action::NewFloatingPane(run_command, name, floating_pane_coordinates) => {
|
||||
let should_float = true;
|
||||
let run_cmd = run_command
|
||||
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
|
||||
.or_else(|| default_shell.clone());
|
||||
senders
|
||||
.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
Some(should_float),
|
||||
name,
|
||||
floating_pane_coordinates,
|
||||
NewPanePlacement::Floating(floating_pane_coordinates),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
))
|
||||
|
|
@ -410,35 +376,46 @@ pub(crate) fn route_action(
|
|||
},
|
||||
}
|
||||
},
|
||||
Action::NewTiledPane(direction, run_command, name) => {
|
||||
let should_float = false;
|
||||
Action::NewStackedPane(run_command, name) => {
|
||||
let run_cmd = run_command
|
||||
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
|
||||
.or_else(|| default_shell.clone());
|
||||
let pty_instr = match direction {
|
||||
Some(Direction::Left) => {
|
||||
PtyInstruction::SpawnTerminalVertically(run_cmd, name, client_id)
|
||||
match pane_id {
|
||||
Some(pane_id) => {
|
||||
senders
|
||||
.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
name,
|
||||
NewPanePlacement::Stacked(Some(pane_id)),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::PaneId(pane_id),
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
},
|
||||
Some(Direction::Right) => {
|
||||
PtyInstruction::SpawnTerminalVertically(run_cmd, name, client_id)
|
||||
None => {
|
||||
senders
|
||||
.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
name,
|
||||
NewPanePlacement::Stacked(None),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
},
|
||||
Some(Direction::Up) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(run_cmd, name, client_id)
|
||||
},
|
||||
Some(Direction::Down) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(run_cmd, name, client_id)
|
||||
},
|
||||
// No direction specified - try to put it in the biggest available spot
|
||||
None => PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
Some(should_float),
|
||||
name,
|
||||
None,
|
||||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
),
|
||||
};
|
||||
senders.send_to_pty(pty_instr).with_context(err_context)?;
|
||||
}
|
||||
},
|
||||
Action::NewTiledPane(direction, run_command, name) => {
|
||||
let run_cmd = run_command
|
||||
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
|
||||
.or_else(|| default_shell.clone());
|
||||
let _ = senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
name,
|
||||
NewPanePlacement::Tiled(direction),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
));
|
||||
},
|
||||
Action::TogglePaneEmbedOrFloating => {
|
||||
senders
|
||||
|
|
@ -465,30 +442,13 @@ pub(crate) fn route_action(
|
|||
},
|
||||
Action::Run(command) => {
|
||||
let run_cmd = Some(TerminalAction::RunCommand(command.clone().into()));
|
||||
let pty_instr = match command.direction {
|
||||
Some(Direction::Left) => {
|
||||
PtyInstruction::SpawnTerminalVertically(run_cmd, None, client_id)
|
||||
},
|
||||
Some(Direction::Right) => {
|
||||
PtyInstruction::SpawnTerminalVertically(run_cmd, None, client_id)
|
||||
},
|
||||
Some(Direction::Up) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(run_cmd, None, client_id)
|
||||
},
|
||||
Some(Direction::Down) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(run_cmd, None, client_id)
|
||||
},
|
||||
// No direction specified - try to put it in the biggest available spot
|
||||
None => PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
),
|
||||
};
|
||||
senders.send_to_pty(pty_instr).with_context(err_context)?;
|
||||
let _ = senders.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
run_cmd,
|
||||
None,
|
||||
NewPanePlacement::Tiled(command.direction),
|
||||
false,
|
||||
ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
));
|
||||
},
|
||||
Action::CloseFocus => {
|
||||
senders
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use crate::{
|
|||
panes::sixel::SixelImageStore,
|
||||
panes::PaneId,
|
||||
plugins::{PluginId, PluginInstruction, PluginRenderAsset},
|
||||
pty::{get_default_shell, ClientTabIndexOrPaneId, PtyInstruction, VteBytes},
|
||||
pty::{get_default_shell, ClientTabIndexOrPaneId, NewPanePlacement, PtyInstruction, VteBytes},
|
||||
tab::{SuppressedPanes, Tab},
|
||||
thread_bus::Bus,
|
||||
ui::{
|
||||
|
|
@ -138,7 +138,6 @@ macro_rules! active_tab_and_connected_client_id {
|
|||
}
|
||||
|
||||
type InitialTitle = String;
|
||||
type ShouldFloat = bool;
|
||||
type HoldForCommand = Option<RunCommand>;
|
||||
|
||||
/// Instructions that can be sent to the [`Screen`].
|
||||
|
|
@ -151,10 +150,9 @@ pub enum ScreenInstruction {
|
|||
NewPane(
|
||||
PaneId,
|
||||
Option<InitialTitle>,
|
||||
Option<ShouldFloat>,
|
||||
HoldForCommand,
|
||||
Option<Run>, // invoked with
|
||||
Option<FloatingPaneCoordinates>,
|
||||
NewPanePlacement,
|
||||
bool, // start suppressed
|
||||
ClientTabIndexOrPaneId,
|
||||
),
|
||||
|
|
@ -3335,10 +3333,9 @@ pub(crate) fn screen_thread_main(
|
|||
ScreenInstruction::NewPane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
should_float,
|
||||
hold_for_command,
|
||||
invoked_with,
|
||||
floating_pane_coordinates,
|
||||
new_pane_placement,
|
||||
start_suppressed,
|
||||
client_or_tab_index,
|
||||
) => {
|
||||
|
|
@ -3347,11 +3344,10 @@ pub(crate) fn screen_thread_main(
|
|||
active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| {
|
||||
tab.new_pane(pid,
|
||||
initial_pane_title,
|
||||
should_float,
|
||||
invoked_with,
|
||||
floating_pane_coordinates,
|
||||
start_suppressed,
|
||||
true,
|
||||
new_pane_placement,
|
||||
Some(client_id)
|
||||
)
|
||||
}, ?);
|
||||
|
|
@ -3374,11 +3370,10 @@ pub(crate) fn screen_thread_main(
|
|||
active_tab.new_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
should_float,
|
||||
invoked_with,
|
||||
floating_pane_coordinates,
|
||||
start_suppressed,
|
||||
true,
|
||||
new_pane_placement,
|
||||
None,
|
||||
)?;
|
||||
if let Some(hold_for_command) = hold_for_command {
|
||||
|
|
@ -3397,11 +3392,10 @@ pub(crate) fn screen_thread_main(
|
|||
tab.new_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
should_float,
|
||||
invoked_with,
|
||||
floating_pane_coordinates,
|
||||
start_suppressed,
|
||||
true,
|
||||
new_pane_placement,
|
||||
None,
|
||||
)?;
|
||||
if let Some(hold_for_command) = hold_for_command {
|
||||
|
|
@ -4659,9 +4653,28 @@ pub(crate) fn screen_thread_main(
|
|||
should_focus_plugin,
|
||||
client_id,
|
||||
) => {
|
||||
let close_replaced_pane = false; // TODO: support this
|
||||
let mut new_pane_placement = NewPanePlacement::default();
|
||||
let maybe_should_float = should_float;
|
||||
let should_be_tiled = maybe_should_float.map(|f| !f).unwrap_or(false);
|
||||
let should_float = maybe_should_float.unwrap_or(false);
|
||||
if floating_pane_coordinates.is_some() || should_float {
|
||||
new_pane_placement = NewPanePlacement::with_floating_pane_coordinates(
|
||||
floating_pane_coordinates.clone(),
|
||||
);
|
||||
}
|
||||
if should_be_tiled {
|
||||
new_pane_placement = NewPanePlacement::Tiled(None);
|
||||
}
|
||||
if should_be_in_place {
|
||||
new_pane_placement = NewPanePlacement::with_pane_id_to_replace(
|
||||
pane_id_to_replace,
|
||||
close_replaced_pane,
|
||||
);
|
||||
}
|
||||
if screen.active_tab_indices.is_empty() && tab_index.is_none() {
|
||||
pending_events_waiting_for_client.push(ScreenInstruction::AddPlugin(
|
||||
should_float,
|
||||
maybe_should_float,
|
||||
should_be_in_place,
|
||||
run_plugin_or_alias,
|
||||
pane_title,
|
||||
|
|
@ -4718,11 +4731,10 @@ pub(crate) fn screen_thread_main(
|
|||
active_tab.new_pane(
|
||||
PaneId::Plugin(plugin_id),
|
||||
Some(pane_title),
|
||||
should_float,
|
||||
Some(run_plugin),
|
||||
floating_pane_coordinates,
|
||||
start_suppressed,
|
||||
should_focus_plugin.unwrap_or(true),
|
||||
new_pane_placement,
|
||||
Some(client_id),
|
||||
)
|
||||
}, ?);
|
||||
|
|
@ -4732,11 +4744,10 @@ pub(crate) fn screen_thread_main(
|
|||
active_tab.new_pane(
|
||||
PaneId::Plugin(plugin_id),
|
||||
Some(pane_title),
|
||||
should_float,
|
||||
Some(run_plugin),
|
||||
None,
|
||||
start_suppressed,
|
||||
should_focus_plugin.unwrap_or(true),
|
||||
new_pane_placement,
|
||||
None,
|
||||
)?;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ use crate::{
|
|||
panes::{FloatingPanes, TiledPanes},
|
||||
panes::{LinkHandler, PaneId, PluginPane, TerminalPane},
|
||||
plugins::PluginInstruction,
|
||||
pty::{ClientTabIndexOrPaneId, PtyInstruction, VteBytes},
|
||||
pty::{ClientTabIndexOrPaneId, NewPanePlacement, PtyInstruction, VteBytes},
|
||||
thread_bus::ThreadSenders,
|
||||
ClientId, ServerInstruction,
|
||||
};
|
||||
|
|
@ -1238,7 +1238,6 @@ impl Tab {
|
|||
},
|
||||
None => {
|
||||
let name = None;
|
||||
let should_float = true;
|
||||
let client_id_or_tab_index = match client_id {
|
||||
Some(client_id) => ClientTabIndexOrPaneId::ClientId(client_id),
|
||||
None => ClientTabIndexOrPaneId::TabIndex(self.index),
|
||||
|
|
@ -1246,9 +1245,8 @@ impl Tab {
|
|||
let should_start_suppressed = false;
|
||||
let instruction = PtyInstruction::SpawnTerminal(
|
||||
default_shell,
|
||||
Some(should_float),
|
||||
name,
|
||||
None,
|
||||
NewPanePlacement::Floating(None),
|
||||
should_start_suppressed,
|
||||
client_id_or_tab_index,
|
||||
);
|
||||
|
|
@ -1266,21 +1264,81 @@ impl Tab {
|
|||
&mut self,
|
||||
pid: PaneId,
|
||||
initial_pane_title: Option<String>,
|
||||
should_float: Option<bool>,
|
||||
invoked_with: Option<Run>,
|
||||
floating_pane_coordinates: Option<FloatingPaneCoordinates>,
|
||||
start_suppressed: bool,
|
||||
should_focus_pane: bool,
|
||||
new_pane_placement: NewPanePlacement,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<()> {
|
||||
match new_pane_placement {
|
||||
NewPanePlacement::NoPreference => self.new_no_preference_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
start_suppressed,
|
||||
should_focus_pane,
|
||||
client_id,
|
||||
),
|
||||
NewPanePlacement::Tiled(None) => self.new_tiled_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
start_suppressed,
|
||||
should_focus_pane,
|
||||
client_id,
|
||||
),
|
||||
NewPanePlacement::Tiled(Some(direction)) => {
|
||||
if let Some(client_id) = client_id {
|
||||
if direction == Direction::Left || direction == Direction::Right {
|
||||
self.vertical_split(pid, initial_pane_title, client_id)?;
|
||||
} else {
|
||||
self.horizontal_split(pid, initial_pane_title, client_id)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
NewPanePlacement::Floating(floating_pane_coordinates) => self.new_floating_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
start_suppressed,
|
||||
should_focus_pane,
|
||||
floating_pane_coordinates,
|
||||
),
|
||||
NewPanePlacement::InPlace {
|
||||
pane_id_to_replace,
|
||||
close_replaced_pane,
|
||||
} => self.new_in_place_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
start_suppressed,
|
||||
should_focus_pane,
|
||||
pane_id_to_replace,
|
||||
close_replaced_pane,
|
||||
client_id,
|
||||
),
|
||||
NewPanePlacement::Stacked(pane_id_to_stack_under) => self.new_stacked_pane(
|
||||
pid,
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
start_suppressed,
|
||||
should_focus_pane,
|
||||
pane_id_to_stack_under,
|
||||
client_id,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub fn new_no_preference_pane(
|
||||
&mut self,
|
||||
pid: PaneId,
|
||||
initial_pane_title: Option<String>,
|
||||
invoked_with: Option<Run>,
|
||||
start_suppressed: bool,
|
||||
should_focus_pane: bool,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<()> {
|
||||
let err_context = || format!("failed to create new pane with id {pid:?}");
|
||||
if should_focus_pane {
|
||||
match should_float {
|
||||
Some(true) => self.show_floating_panes(),
|
||||
Some(false) => self.hide_floating_panes(),
|
||||
None => {},
|
||||
};
|
||||
}
|
||||
self.close_down_to_max_terminals()
|
||||
.with_context(err_context)?;
|
||||
let mut new_pane = match pid {
|
||||
|
|
@ -1357,23 +1415,340 @@ impl Tab {
|
|||
Ok(())
|
||||
} else if should_focus_pane {
|
||||
if self.floating_panes.panes_are_visible() {
|
||||
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, true)
|
||||
self.add_floating_pane(new_pane, pid, None, true)
|
||||
} else {
|
||||
self.add_tiled_pane(new_pane, pid, client_id)
|
||||
}
|
||||
} else {
|
||||
match should_float {
|
||||
Some(true) => {
|
||||
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, false)
|
||||
if self.floating_panes.panes_are_visible() {
|
||||
self.add_floating_pane(new_pane, pid, None, false)
|
||||
} else {
|
||||
self.add_tiled_pane(new_pane, pid, client_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn new_tiled_pane(
|
||||
&mut self,
|
||||
pid: PaneId,
|
||||
initial_pane_title: Option<String>,
|
||||
invoked_with: Option<Run>,
|
||||
start_suppressed: bool,
|
||||
should_focus_pane: bool,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<()> {
|
||||
let err_context = || format!("failed to create new pane with id {pid:?}");
|
||||
if should_focus_pane {
|
||||
self.hide_floating_panes();
|
||||
}
|
||||
self.close_down_to_max_terminals()
|
||||
.with_context(err_context)?;
|
||||
let mut new_pane = match pid {
|
||||
PaneId::Terminal(term_pid) => {
|
||||
let next_terminal_position = self.get_next_terminal_position();
|
||||
Box::new(TerminalPane::new(
|
||||
term_pid,
|
||||
PaneGeom::default(), // this will be filled out later
|
||||
self.style,
|
||||
next_terminal_position,
|
||||
String::new(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
self.debug,
|
||||
self.arrow_fonts,
|
||||
self.styled_underlines,
|
||||
self.explicitly_disable_kitty_keyboard_protocol,
|
||||
)) as Box<dyn Pane>
|
||||
},
|
||||
PaneId::Plugin(plugin_pid) => {
|
||||
Box::new(PluginPane::new(
|
||||
plugin_pid,
|
||||
PaneGeom::default(), // this will be filled out later
|
||||
self.senders
|
||||
.to_plugin
|
||||
.as_ref()
|
||||
.with_context(err_context)?
|
||||
.clone(),
|
||||
initial_pane_title.unwrap_or("".to_owned()),
|
||||
String::new(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.connected_clients.borrow().iter().copied().collect(),
|
||||
self.style,
|
||||
invoked_with,
|
||||
self.debug,
|
||||
self.arrow_fonts,
|
||||
self.styled_underlines,
|
||||
)) as Box<dyn Pane>
|
||||
},
|
||||
};
|
||||
|
||||
if start_suppressed {
|
||||
// this pane needs to start in the background (suppressed), only accessible if a plugin takes it out
|
||||
// of there in one way or another
|
||||
// we need to do some bookkeeping for this pane, namely setting its geom and
|
||||
// content_offset so that things will appear properly in the terminal - we set it to
|
||||
// the default geom of the first floating pane - this is just in order to give it some
|
||||
// reasonable size, when it is shown - if needed - it will be given the proper geom as if it were
|
||||
// resized
|
||||
let viewport = { self.viewport.borrow().clone() };
|
||||
let new_pane_geom = half_size_middle_geom(&viewport, 0);
|
||||
new_pane.set_active_at(Instant::now());
|
||||
new_pane.set_geom(new_pane_geom);
|
||||
new_pane.set_content_offset(Offset::frame(1));
|
||||
resize_pty!(
|
||||
new_pane,
|
||||
self.os_api,
|
||||
self.senders,
|
||||
self.character_cell_size
|
||||
)
|
||||
.with_context(err_context)?;
|
||||
let is_scrollback_editor = false;
|
||||
self.suppressed_panes
|
||||
.insert(pid, (is_scrollback_editor, new_pane));
|
||||
Ok(())
|
||||
} else {
|
||||
self.add_tiled_pane(new_pane, pid, client_id)
|
||||
}
|
||||
}
|
||||
pub fn new_floating_pane(
|
||||
&mut self,
|
||||
pid: PaneId,
|
||||
initial_pane_title: Option<String>,
|
||||
invoked_with: Option<Run>,
|
||||
start_suppressed: bool,
|
||||
should_focus_pane: bool,
|
||||
floating_pane_coordinates: Option<FloatingPaneCoordinates>,
|
||||
) -> Result<()> {
|
||||
let err_context = || format!("failed to create new pane with id {pid:?}");
|
||||
if should_focus_pane {
|
||||
self.show_floating_panes();
|
||||
}
|
||||
self.close_down_to_max_terminals()
|
||||
.with_context(err_context)?;
|
||||
let mut new_pane = match pid {
|
||||
PaneId::Terminal(term_pid) => {
|
||||
let next_terminal_position = self.get_next_terminal_position();
|
||||
Box::new(TerminalPane::new(
|
||||
term_pid,
|
||||
PaneGeom::default(), // this will be filled out later
|
||||
self.style,
|
||||
next_terminal_position,
|
||||
String::new(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
self.debug,
|
||||
self.arrow_fonts,
|
||||
self.styled_underlines,
|
||||
self.explicitly_disable_kitty_keyboard_protocol,
|
||||
)) as Box<dyn Pane>
|
||||
},
|
||||
PaneId::Plugin(plugin_pid) => {
|
||||
Box::new(PluginPane::new(
|
||||
plugin_pid,
|
||||
PaneGeom::default(), // this will be filled out later
|
||||
self.senders
|
||||
.to_plugin
|
||||
.as_ref()
|
||||
.with_context(err_context)?
|
||||
.clone(),
|
||||
initial_pane_title.unwrap_or("".to_owned()),
|
||||
String::new(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.connected_clients.borrow().iter().copied().collect(),
|
||||
self.style,
|
||||
invoked_with,
|
||||
self.debug,
|
||||
self.arrow_fonts,
|
||||
self.styled_underlines,
|
||||
)) as Box<dyn Pane>
|
||||
},
|
||||
};
|
||||
|
||||
if start_suppressed {
|
||||
// this pane needs to start in the background (suppressed), only accessible if a plugin takes it out
|
||||
// of there in one way or another
|
||||
// we need to do some bookkeeping for this pane, namely setting its geom and
|
||||
// content_offset so that things will appear properly in the terminal - we set it to
|
||||
// the default geom of the first floating pane - this is just in order to give it some
|
||||
// reasonable size, when it is shown - if needed - it will be given the proper geom as if it were
|
||||
// resized
|
||||
let viewport = { self.viewport.borrow().clone() };
|
||||
let new_pane_geom = half_size_middle_geom(&viewport, 0);
|
||||
new_pane.set_active_at(Instant::now());
|
||||
new_pane.set_geom(new_pane_geom);
|
||||
new_pane.set_content_offset(Offset::frame(1));
|
||||
resize_pty!(
|
||||
new_pane,
|
||||
self.os_api,
|
||||
self.senders,
|
||||
self.character_cell_size
|
||||
)
|
||||
.with_context(err_context)?;
|
||||
let is_scrollback_editor = false;
|
||||
self.suppressed_panes
|
||||
.insert(pid, (is_scrollback_editor, new_pane));
|
||||
Ok(())
|
||||
} else {
|
||||
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, should_focus_pane)
|
||||
}
|
||||
}
|
||||
pub fn new_in_place_pane(
|
||||
&mut self,
|
||||
pid: PaneId,
|
||||
initial_pane_title: Option<String>,
|
||||
invoked_with: Option<Run>,
|
||||
start_suppressed: bool,
|
||||
should_focus_pane: bool,
|
||||
pane_id_to_replace: Option<PaneId>,
|
||||
close_replaced_pane: bool,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<()> {
|
||||
match (pane_id_to_replace, client_id) {
|
||||
(Some(pane_id_to_replace), _) => {
|
||||
self.suppress_pane_and_replace_with_pid(
|
||||
pane_id_to_replace,
|
||||
pid,
|
||||
close_replaced_pane,
|
||||
invoked_with,
|
||||
)?;
|
||||
},
|
||||
(None, Some(client_id)) => match self.get_active_pane_id(client_id) {
|
||||
Some(active_pane_id) => {
|
||||
self.suppress_pane_and_replace_with_pid(
|
||||
active_pane_id,
|
||||
pid,
|
||||
close_replaced_pane,
|
||||
invoked_with,
|
||||
)?;
|
||||
},
|
||||
Some(false) => self.add_tiled_pane(new_pane, pid, client_id),
|
||||
None => {
|
||||
if self.floating_panes.panes_are_visible() {
|
||||
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, false)
|
||||
} else {
|
||||
self.add_tiled_pane(new_pane, pid, client_id)
|
||||
}
|
||||
log::error!("Cannot find active pane");
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
log::error!("Must have pane id to replace or client id to start pane in place>");
|
||||
},
|
||||
}
|
||||
if let Some(initial_pane_title) = initial_pane_title {
|
||||
let _ = self.rename_pane(initial_pane_title.as_bytes().to_vec(), pid);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn new_stacked_pane(
|
||||
&mut self,
|
||||
pid: PaneId,
|
||||
initial_pane_title: Option<String>,
|
||||
invoked_with: Option<Run>,
|
||||
start_suppressed: bool,
|
||||
should_focus_pane: bool,
|
||||
pane_id_to_stack_under: Option<PaneId>,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<()> {
|
||||
let err_context = || format!("failed to create new pane with id {pid:?}");
|
||||
if should_focus_pane {
|
||||
self.hide_floating_panes();
|
||||
}
|
||||
self.close_down_to_max_terminals()
|
||||
.with_context(err_context)?;
|
||||
let mut new_pane = match pid {
|
||||
PaneId::Terminal(term_pid) => {
|
||||
let next_terminal_position = self.get_next_terminal_position();
|
||||
Box::new(TerminalPane::new(
|
||||
term_pid,
|
||||
PaneGeom::default(), // this will be filled out later
|
||||
self.style,
|
||||
next_terminal_position,
|
||||
String::new(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
initial_pane_title,
|
||||
invoked_with,
|
||||
self.debug,
|
||||
self.arrow_fonts,
|
||||
self.styled_underlines,
|
||||
self.explicitly_disable_kitty_keyboard_protocol,
|
||||
)) as Box<dyn Pane>
|
||||
},
|
||||
PaneId::Plugin(plugin_pid) => {
|
||||
Box::new(PluginPane::new(
|
||||
plugin_pid,
|
||||
PaneGeom::default(), // this will be filled out later
|
||||
self.senders
|
||||
.to_plugin
|
||||
.as_ref()
|
||||
.with_context(err_context)?
|
||||
.clone(),
|
||||
initial_pane_title.unwrap_or("".to_owned()),
|
||||
String::new(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.connected_clients.borrow().iter().copied().collect(),
|
||||
self.style,
|
||||
invoked_with,
|
||||
self.debug,
|
||||
self.arrow_fonts,
|
||||
self.styled_underlines,
|
||||
)) as Box<dyn Pane>
|
||||
},
|
||||
};
|
||||
|
||||
if start_suppressed {
|
||||
// this pane needs to start in the background (suppressed), only accessible if a plugin takes it out
|
||||
// of there in one way or another
|
||||
// we need to do some bookkeeping for this pane, namely setting its geom and
|
||||
// content_offset so that things will appear properly in the terminal - we set it to
|
||||
// the default geom of the first floating pane - this is just in order to give it some
|
||||
// reasonable size, when it is shown - if needed - it will be given the proper geom as if it were
|
||||
// resized
|
||||
let viewport = { self.viewport.borrow().clone() };
|
||||
let new_pane_geom = half_size_middle_geom(&viewport, 0);
|
||||
new_pane.set_active_at(Instant::now());
|
||||
new_pane.set_geom(new_pane_geom);
|
||||
new_pane.set_content_offset(Offset::frame(1));
|
||||
resize_pty!(
|
||||
new_pane,
|
||||
self.os_api,
|
||||
self.senders,
|
||||
self.character_cell_size
|
||||
)
|
||||
.with_context(err_context)?;
|
||||
let is_scrollback_editor = false;
|
||||
self.suppressed_panes
|
||||
.insert(pid, (is_scrollback_editor, new_pane));
|
||||
Ok(())
|
||||
} else {
|
||||
if let Some(pane_id_to_stack_under) = pane_id_to_stack_under {
|
||||
// TODO: also focus pane if should_focus_pane? in cases where we did this from the CLI in an unfocused
|
||||
// pane...
|
||||
self.add_stacked_pane_to_pane_id(new_pane, pid, pane_id_to_stack_under)
|
||||
} else if let Some(client_id) = client_id {
|
||||
self.add_stacked_pane_to_active_pane(new_pane, pid, client_id)
|
||||
} else {
|
||||
log::error!("Must have client id or pane id to stack pane");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4653,6 +5028,38 @@ impl Tab {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn add_stacked_pane_to_pane_id(
|
||||
&mut self,
|
||||
pane: Box<dyn Pane>,
|
||||
pane_id: PaneId,
|
||||
root_pane_id: PaneId,
|
||||
) -> Result<()> {
|
||||
if self.tiled_panes.fullscreen_is_active() {
|
||||
self.tiled_panes.unset_fullscreen();
|
||||
}
|
||||
self.tiled_panes
|
||||
.add_pane_to_stack_of_pane_id(pane_id, pane, root_pane_id);
|
||||
self.set_should_clear_display_before_rendering();
|
||||
self.tiled_panes.expand_pane_in_stack(pane_id); // so that it will get focused by all
|
||||
// clients
|
||||
self.swap_layouts.set_is_tiled_damaged();
|
||||
Ok(())
|
||||
}
|
||||
pub fn add_stacked_pane_to_active_pane(
|
||||
&mut self,
|
||||
pane: Box<dyn Pane>,
|
||||
pane_id: PaneId,
|
||||
client_id: ClientId,
|
||||
) -> Result<()> {
|
||||
if self.tiled_panes.fullscreen_is_active() {
|
||||
self.tiled_panes.unset_fullscreen();
|
||||
}
|
||||
self.tiled_panes
|
||||
.add_pane_to_stack_of_active_pane(pane_id, pane, client_id);
|
||||
self.tiled_panes.focus_pane(pane_id, client_id);
|
||||
self.swap_layouts.set_is_tiled_damaged();
|
||||
Ok(())
|
||||
}
|
||||
pub fn request_plugin_permissions(&mut self, pid: u32, permissions: Option<PluginPermission>) {
|
||||
let mut should_focus_pane = false;
|
||||
if let Some(plugin_pane) = self
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||
expression: snapshot
|
||||
---
|
||||
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
01 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
02 (C): │ │
|
||||
03 (C): │ │
|
||||
04 (C): │ │
|
||||
05 (C): │ │
|
||||
06 (C): │ │
|
||||
07 (C): │ │
|
||||
08 (C): │ │
|
||||
09 (C): │ │
|
||||
10 (C): │ │
|
||||
11 (C): │ │
|
||||
12 (C): │ │
|
||||
13 (C): │ │
|
||||
14 (C): │ │
|
||||
15 (C): │ │
|
||||
16 (C): │ │
|
||||
17 (C): │ │
|
||||
18 (C): │ │
|
||||
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
|||
use super::Tab;
|
||||
use crate::pane_groups::PaneGroups;
|
||||
use crate::panes::sixel::SixelImageStore;
|
||||
use crate::pty::NewPanePlacement;
|
||||
use crate::screen::CopyOptions;
|
||||
use crate::{
|
||||
os_input_output::{AsyncReader, Pid, ServerOsApi},
|
||||
|
|
@ -625,8 +626,16 @@ fn split_largest_pane() {
|
|||
let mut tab = create_new_tab(size, stacked_resize);
|
||||
for i in 2..5 {
|
||||
let new_pane_id = PaneId::Terminal(i);
|
||||
tab.new_pane(new_pane_id, None, None, None, None, false, true, Some(1))
|
||||
.unwrap();
|
||||
tab.new_pane(
|
||||
new_pane_id,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
assert_eq!(tab.tiled_panes.panes.len(), 4, "The tab has four panes");
|
||||
|
||||
|
|
@ -838,10 +847,9 @@ pub fn cannot_split_largest_pane_when_there_is_no_room() {
|
|||
PaneId::Terminal(2),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -888,8 +896,16 @@ pub fn toggle_focused_pane_fullscreen() {
|
|||
let mut tab = create_new_tab(size, stacked_resize);
|
||||
for i in 2..5 {
|
||||
let new_pane_id = PaneId::Terminal(i);
|
||||
tab.new_pane(new_pane_id, None, None, None, None, false, true, Some(1))
|
||||
.unwrap();
|
||||
tab.new_pane(
|
||||
new_pane_id,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
tab.toggle_active_pane_fullscreen(1);
|
||||
assert_eq!(
|
||||
|
|
@ -964,8 +980,16 @@ pub fn toggle_focused_pane_fullscreen_with_stacked_resizes() {
|
|||
let mut tab = create_new_tab(size, stacked_resize);
|
||||
for i in 2..5 {
|
||||
let new_pane_id = PaneId::Terminal(i);
|
||||
tab.new_pane(new_pane_id, None, None, None, None, false, true, Some(1))
|
||||
.unwrap();
|
||||
tab.new_pane(
|
||||
new_pane_id,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
tab.toggle_active_pane_fullscreen(1);
|
||||
assert_eq!(
|
||||
|
|
@ -1044,10 +1068,9 @@ fn switch_to_next_pane_fullscreen() {
|
|||
PaneId::Terminal(1),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1056,10 +1079,9 @@ fn switch_to_next_pane_fullscreen() {
|
|||
PaneId::Terminal(2),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1068,10 +1090,9 @@ fn switch_to_next_pane_fullscreen() {
|
|||
PaneId::Terminal(3),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1080,10 +1101,9 @@ fn switch_to_next_pane_fullscreen() {
|
|||
PaneId::Terminal(4),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1121,10 +1141,9 @@ fn switch_to_prev_pane_fullscreen() {
|
|||
PaneId::Terminal(1),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1133,10 +1152,9 @@ fn switch_to_prev_pane_fullscreen() {
|
|||
PaneId::Terminal(2),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1145,10 +1163,9 @@ fn switch_to_prev_pane_fullscreen() {
|
|||
PaneId::Terminal(3),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1157,10 +1174,9 @@ fn switch_to_prev_pane_fullscreen() {
|
|||
PaneId::Terminal(4),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -14758,10 +14774,9 @@ fn correctly_resize_frameless_panes_on_pane_close() {
|
|||
PaneId::Terminal(2),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use std::sync::{Arc, Mutex};
|
|||
|
||||
use crate::{
|
||||
plugins::PluginInstruction,
|
||||
pty::{ClientTabIndexOrPaneId, PtyInstruction},
|
||||
pty::{ClientTabIndexOrPaneId, NewPanePlacement, PtyInstruction},
|
||||
};
|
||||
use zellij_utils::ipc::PixelDimensions;
|
||||
|
||||
|
|
@ -1235,10 +1235,9 @@ fn switch_to_tab_with_fullscreen() {
|
|||
PaneId::Terminal(2),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1359,10 +1358,9 @@ fn attach_after_first_tab_closed() {
|
|||
PaneId::Terminal(2),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::default(),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1385,22 +1383,20 @@ fn open_new_floating_pane_with_custom_coordinates() {
|
|||
|
||||
new_tab(&mut screen, 1, 0);
|
||||
let active_tab = screen.get_active_tab_mut(1).unwrap();
|
||||
let should_float = Some(true);
|
||||
active_tab
|
||||
.new_pane(
|
||||
PaneId::Terminal(2),
|
||||
None,
|
||||
should_float,
|
||||
None,
|
||||
Some(FloatingPaneCoordinates {
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::Floating(Some(FloatingPaneCoordinates {
|
||||
x: Some(SplitSize::Percent(10)),
|
||||
y: Some(SplitSize::Fixed(5)),
|
||||
width: Some(SplitSize::Percent(1)),
|
||||
height: Some(SplitSize::Fixed(2)),
|
||||
pinned: None,
|
||||
}),
|
||||
false,
|
||||
true,
|
||||
})),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1421,22 +1417,20 @@ fn open_new_floating_pane_with_custom_coordinates_exceeding_viewport() {
|
|||
|
||||
new_tab(&mut screen, 1, 0);
|
||||
let active_tab = screen.get_active_tab_mut(1).unwrap();
|
||||
let should_float = Some(true);
|
||||
active_tab
|
||||
.new_pane(
|
||||
PaneId::Terminal(2),
|
||||
None,
|
||||
should_float,
|
||||
None,
|
||||
Some(FloatingPaneCoordinates {
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::Floating(Some(FloatingPaneCoordinates {
|
||||
x: Some(SplitSize::Fixed(122)),
|
||||
y: Some(SplitSize::Fixed(21)),
|
||||
width: Some(SplitSize::Fixed(10)),
|
||||
height: Some(SplitSize::Fixed(10)),
|
||||
pinned: None,
|
||||
}),
|
||||
false,
|
||||
true,
|
||||
})),
|
||||
Some(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1611,17 +1605,15 @@ fn group_panes_following_focus() {
|
|||
|
||||
{
|
||||
let active_tab = screen.get_active_tab_mut(client_id).unwrap();
|
||||
let should_float = Some(false);
|
||||
for i in 2..5 {
|
||||
active_tab
|
||||
.new_pane(
|
||||
PaneId::Terminal(i),
|
||||
None,
|
||||
should_float,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::Tiled(None),
|
||||
Some(client_id),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -1670,17 +1662,15 @@ fn break_group_with_mouse() {
|
|||
|
||||
{
|
||||
let active_tab = screen.get_active_tab_mut(client_id).unwrap();
|
||||
let should_float = Some(false);
|
||||
for i in 2..5 {
|
||||
active_tab
|
||||
.new_pane(
|
||||
PaneId::Terminal(i),
|
||||
None,
|
||||
should_float,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
NewPanePlacement::Tiled(None),
|
||||
Some(client_id),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -2596,6 +2586,7 @@ pub fn send_cli_new_pane_action_with_default_parameters() {
|
|||
width: None,
|
||||
height: None,
|
||||
pinned: None,
|
||||
stacked: false,
|
||||
};
|
||||
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
|
||||
|
|
@ -2640,6 +2631,7 @@ pub fn send_cli_new_pane_action_with_split_direction() {
|
|||
width: None,
|
||||
height: None,
|
||||
pinned: None,
|
||||
stacked: false,
|
||||
};
|
||||
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
|
||||
|
|
@ -2684,6 +2676,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() {
|
|||
width: None,
|
||||
height: None,
|
||||
pinned: None,
|
||||
stacked: false,
|
||||
};
|
||||
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
|
||||
|
|
@ -2694,7 +2687,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() {
|
|||
.unwrap()
|
||||
.iter()
|
||||
.find(|instruction| match instruction {
|
||||
PtyInstruction::SpawnTerminalVertically(..) => true,
|
||||
PtyInstruction::SpawnTerminal(..) => true,
|
||||
_ => false,
|
||||
})
|
||||
.cloned();
|
||||
|
|
@ -2739,6 +2732,7 @@ pub fn send_cli_new_pane_action_with_floating_pane_and_coordinates() {
|
|||
width: Some("20%".to_owned()),
|
||||
height: None,
|
||||
pinned: None,
|
||||
stacked: false,
|
||||
};
|
||||
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
|
||||
---
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), Exit]
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(None), false, ClientId(10)), Exit]
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
|
||||
---
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), Exit]
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(None), false, ClientId(10)), Exit]
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
|
||||
---
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminalHorizontally(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), 10), Exit]
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(Some(Down)), false, ClientId(10)), Exit]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
assertion_line: 2339
|
||||
expression: "format!(\"{:?}\", new_pane_instruction)"
|
||||
---
|
||||
Some(SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None })), None, 10))
|
||||
Some(SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None })), None, Tiled(Some(Right)), false, ClientId(10)))
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
|
||||
---
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, Some(false), None, None, false, ClientId(10)), Exit]
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled(None), false, ClientId(10)), Exit]
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
|
||||
---
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None })), Some(true), None, Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None, pinned: None }), false, ClientId(10)), Exit]
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None })), None, Floating(Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None, pinned: None })), false, ClientId(10)), Exit]
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
|
||||
---
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminalVertically(None, None, 10), Exit]
|
||||
[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled(Some(Right)), false, ClientId(10)), Exit]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ keybinds {
|
|||
bind "n" { NewPane; SwitchToMode "Normal"; }
|
||||
bind "d" { NewPane "Down"; SwitchToMode "Normal"; }
|
||||
bind "r" { NewPane "Right"; SwitchToMode "Normal"; }
|
||||
bind "s" { NewPane "stacked"; SwitchToMode "Normal"; }
|
||||
bind "x" { CloseFocus; SwitchToMode "Normal"; }
|
||||
bind "f" { ToggleFocusFullscreen; SwitchToMode "Normal"; }
|
||||
bind "z" { TogglePaneFrames; SwitchToMode "Normal"; }
|
||||
|
|
|
|||
|
|
@ -475,6 +475,7 @@ pub enum ActionName {
|
|||
MouseEvent = 86,
|
||||
TogglePaneInGroup = 87,
|
||||
ToggleGroupMarking = 88,
|
||||
NewStackedPane = 89,
|
||||
}
|
||||
impl ActionName {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
|
|
@ -569,6 +570,7 @@ impl ActionName {
|
|||
ActionName::MouseEvent => "MouseEvent",
|
||||
ActionName::TogglePaneInGroup => "TogglePaneInGroup",
|
||||
ActionName::ToggleGroupMarking => "ToggleGroupMarking",
|
||||
ActionName::NewStackedPane => "NewStackedPane",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
|
|
@ -660,6 +662,7 @@ impl ActionName {
|
|||
"MouseEvent" => Some(Self::MouseEvent),
|
||||
"TogglePaneInGroup" => Some(Self::TogglePaneInGroup),
|
||||
"ToggleGroupMarking" => Some(Self::ToggleGroupMarking),
|
||||
"NewStackedPane" => Some(Self::NewStackedPane),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -313,6 +313,15 @@ pub enum Sessions {
|
|||
/// Whether to pin a floating pane so that it is always on top
|
||||
#[clap(long, requires("floating"))]
|
||||
pinned: Option<bool>,
|
||||
#[clap(
|
||||
long,
|
||||
conflicts_with("floating"),
|
||||
conflicts_with("direction"),
|
||||
value_parser,
|
||||
default_value("false"),
|
||||
takes_value(false)
|
||||
)]
|
||||
stacked: bool,
|
||||
},
|
||||
/// Load a plugin
|
||||
#[clap(visible_alias = "p")]
|
||||
|
|
@ -602,6 +611,15 @@ pub enum CliAction {
|
|||
/// Whether to pin a floating pane so that it is always on top
|
||||
#[clap(long, requires("floating"))]
|
||||
pinned: Option<bool>,
|
||||
#[clap(
|
||||
long,
|
||||
conflicts_with("floating"),
|
||||
conflicts_with("direction"),
|
||||
value_parser,
|
||||
default_value("false"),
|
||||
takes_value(false)
|
||||
)]
|
||||
stacked: bool,
|
||||
},
|
||||
/// Open the specified file in a new zellij pane with your default EDITOR
|
||||
Edit {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,8 @@ pub enum Action {
|
|||
/// Open a new pane in place of the focused one, suppressing it instead
|
||||
NewInPlacePane(Option<RunCommandAction>, Option<String>), // String is an
|
||||
// optional pane
|
||||
NewStackedPane(Option<RunCommandAction>, Option<String>), // String is an
|
||||
// optional pane
|
||||
// name
|
||||
/// Embed focused pane in tab if floating or float focused pane if embedded
|
||||
TogglePaneEmbedOrFloating,
|
||||
|
|
@ -362,6 +364,7 @@ impl Action {
|
|||
width,
|
||||
height,
|
||||
pinned,
|
||||
stacked,
|
||||
} => {
|
||||
let current_dir = get_current_dir();
|
||||
// cwd should only be specified in a plugin alias if it was explicitly given to us,
|
||||
|
|
@ -443,6 +446,8 @@ impl Action {
|
|||
)])
|
||||
} else if in_place {
|
||||
Ok(vec![Action::NewInPlacePane(Some(run_command_action), name)])
|
||||
} else if stacked {
|
||||
Ok(vec![Action::NewStackedPane(Some(run_command_action), name)])
|
||||
} else {
|
||||
Ok(vec![Action::NewTiledPane(
|
||||
direction,
|
||||
|
|
@ -459,6 +464,8 @@ impl Action {
|
|||
)])
|
||||
} else if in_place {
|
||||
Ok(vec![Action::NewInPlacePane(None, name)])
|
||||
} else if stacked {
|
||||
Ok(vec![Action::NewStackedPane(None, name)])
|
||||
} else {
|
||||
Ok(vec![Action::NewTiledPane(direction, None, name)])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,6 +534,8 @@ impl Action {
|
|||
"NewPane" => {
|
||||
if string.is_empty() {
|
||||
return Ok(Action::NewPane(None, None, false));
|
||||
} else if string == "stacked" {
|
||||
return Ok(Action::NewStackedPane(None, None));
|
||||
} else {
|
||||
let direction = Direction::from_str(string.as_str()).map_err(|_| {
|
||||
ConfigError::new_kdl_error(
|
||||
|
|
@ -911,6 +913,48 @@ impl Action {
|
|||
}
|
||||
Some(node)
|
||||
},
|
||||
Action::NewStackedPane(run_command_action, name) => match run_command_action {
|
||||
Some(run_command_action) => {
|
||||
let mut node = KdlNode::new("Run");
|
||||
let mut node_children = KdlDocument::new();
|
||||
node.push(run_command_action.command.display().to_string());
|
||||
for arg in &run_command_action.args {
|
||||
node.push(arg.clone());
|
||||
}
|
||||
let mut stacked_node = KdlNode::new("stacked");
|
||||
stacked_node.push(KdlValue::Bool(true));
|
||||
node_children.nodes_mut().push(stacked_node);
|
||||
if let Some(cwd) = &run_command_action.cwd {
|
||||
let mut cwd_node = KdlNode::new("cwd");
|
||||
cwd_node.push(cwd.display().to_string());
|
||||
node_children.nodes_mut().push(cwd_node);
|
||||
}
|
||||
if run_command_action.hold_on_start {
|
||||
let mut hos_node = KdlNode::new("hold_on_start");
|
||||
hos_node.push(KdlValue::Bool(true));
|
||||
node_children.nodes_mut().push(hos_node);
|
||||
}
|
||||
if !run_command_action.hold_on_close {
|
||||
let mut hoc_node = KdlNode::new("hold_on_close");
|
||||
hoc_node.push(KdlValue::Bool(false));
|
||||
node_children.nodes_mut().push(hoc_node);
|
||||
}
|
||||
if let Some(name) = name {
|
||||
let mut name_node = KdlNode::new("name");
|
||||
name_node.push(name.clone());
|
||||
node_children.nodes_mut().push(name_node);
|
||||
}
|
||||
if !node_children.nodes().is_empty() {
|
||||
node.set_children(node_children);
|
||||
}
|
||||
Some(node)
|
||||
},
|
||||
None => {
|
||||
let mut node = KdlNode::new("NewPane");
|
||||
node.push("stacked");
|
||||
Some(node)
|
||||
},
|
||||
},
|
||||
Action::Detach => Some(KdlNode::new("Detach")),
|
||||
Action::LaunchOrFocusPlugin(
|
||||
run_plugin_or_alias,
|
||||
|
|
@ -1557,6 +1601,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
|
|||
let in_place = command_metadata
|
||||
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "in_place"))
|
||||
.unwrap_or(false);
|
||||
let stacked = command_metadata
|
||||
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "stacked"))
|
||||
.unwrap_or(false);
|
||||
let run_command_action = RunCommandAction {
|
||||
command: PathBuf::from(command),
|
||||
args,
|
||||
|
|
@ -1588,6 +1635,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
|
|||
))
|
||||
} else if in_place {
|
||||
Ok(Action::NewInPlacePane(Some(run_command_action), name))
|
||||
} else if stacked {
|
||||
Ok(Action::NewStackedPane(Some(run_command_action), name))
|
||||
} else {
|
||||
Ok(Action::NewTiledPane(
|
||||
direction,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ keybinds clear-defaults=true {
|
|||
bind "p" { SwitchFocus; }
|
||||
bind "Ctrl p" { SwitchToMode "normal"; }
|
||||
bind "r" { NewPane "right"; SwitchToMode "normal"; }
|
||||
bind "s" { NewPane "stacked"; SwitchToMode "normal"; }
|
||||
bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; }
|
||||
bind "z" { TogglePaneFrames; SwitchToMode "normal"; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ keybinds clear-defaults=true {
|
|||
bind "p" { SwitchFocus; }
|
||||
bind "Ctrl p" { SwitchToMode "normal"; }
|
||||
bind "r" { NewPane "right"; SwitchToMode "normal"; }
|
||||
bind "s" { NewPane "stacked"; SwitchToMode "normal"; }
|
||||
bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; }
|
||||
bind "z" { TogglePaneFrames; SwitchToMode "normal"; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ enum ActionName {
|
|||
MouseEvent = 86;
|
||||
TogglePaneInGroup = 87;
|
||||
ToggleGroupMarking = 88;
|
||||
NewStackedPane = 89;
|
||||
}
|
||||
|
||||
message Position {
|
||||
|
|
|
|||
|
|
@ -720,6 +720,10 @@ impl TryFrom<ProtobufAction> for Action {
|
|||
plugin_id: None,
|
||||
}),
|
||||
},
|
||||
Some(ProtobufActionName::NewStackedPane) => match protobuf_action.optional_payload {
|
||||
Some(_) => Err("NewStackedPane should not have a payload"),
|
||||
None => Ok(Action::NewStackedPane(None, None)),
|
||||
},
|
||||
_ => Err("Unknown Action"),
|
||||
}
|
||||
}
|
||||
|
|
@ -1254,6 +1258,10 @@ impl TryFrom<Action> for ProtobufAction {
|
|||
name: ProtobufActionName::ToggleGroupMarking as i32,
|
||||
optional_payload: None,
|
||||
}),
|
||||
Action::NewStackedPane(..) => Ok(ProtobufAction {
|
||||
name: ProtobufActionName::NewStackedPane as i32,
|
||||
optional_payload: None,
|
||||
}),
|
||||
Action::NoOp
|
||||
| Action::Confirm
|
||||
| Action::NewInPlacePane(..)
|
||||
|
|
|
|||
|
|
@ -1339,6 +1339,20 @@ Config {
|
|||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
),
|
||||
key_modifiers: {},
|
||||
}: [
|
||||
NewStackedPane(
|
||||
None,
|
||||
None,
|
||||
),
|
||||
SwitchToMode(
|
||||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
|
|
|
|||
|
|
@ -1339,6 +1339,20 @@ Config {
|
|||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
),
|
||||
key_modifiers: {},
|
||||
}: [
|
||||
NewStackedPane(
|
||||
None,
|
||||
None,
|
||||
),
|
||||
SwitchToMode(
|
||||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
|
|
|
|||
|
|
@ -1339,6 +1339,20 @@ Config {
|
|||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
),
|
||||
key_modifiers: {},
|
||||
}: [
|
||||
NewStackedPane(
|
||||
None,
|
||||
None,
|
||||
),
|
||||
SwitchToMode(
|
||||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
|
|
|
|||
|
|
@ -1339,6 +1339,20 @@ Config {
|
|||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
),
|
||||
key_modifiers: {},
|
||||
}: [
|
||||
NewStackedPane(
|
||||
None,
|
||||
None,
|
||||
),
|
||||
SwitchToMode(
|
||||
Normal,
|
||||
),
|
||||
],
|
||||
KeyWithModifier {
|
||||
bare_key: Char(
|
||||
's',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue