aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2021-08-24 10:58:36 +0200
committerAram Drevekenin <aram@poor.dev>2021-08-24 10:58:36 +0200
commit618c2c376bb4edca8693ea4b871279bc04501559 (patch)
treeb4d4426b353955e8af1ed8ad6531f95860eda5f6
parentdocs(changelog): add `ToggleTab` action (diff)
parentAdd `template` section in `layout` file (diff)
downloadsl0ppij-618c2c376bb4edca8693ea4b871279bc04501559.tar.gz
sl0ppij-618c2c376bb4edca8693ea4b871279bc04501559.tar.xz
sl0ppij-618c2c376bb4edca8693ea4b871279bc04501559.zip
Merge branch 'tab-layout' of https://github.com/a-kenji/zellij into a-kenji-tab-layout
-rw-r--r--example/multiple_tabs_layout.yaml82
-rw-r--r--example/multiple_tabs_layout_htop_command.yaml85
-rw-r--r--src/main.rs24
-rw-r--r--src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml2
-rw-r--r--src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml3
-rw-r--r--src/tests/fixtures/layouts/three-panes-with-nesting.yaml24
-rw-r--r--zellij-client/src/input_handler.rs2
-rw-r--r--zellij-client/src/lib.rs4
-rw-r--r--zellij-server/src/lib.rs57
-rw-r--r--zellij-server/src/pty.rs15
-rw-r--r--zellij-server/src/route.rs4
-rw-r--r--zellij-utils/assets/config/default.yaml2
-rw-r--r--zellij-utils/assets/layouts/default.yaml32
-rw-r--r--zellij-utils/assets/layouts/disable-status-bar.yaml20
-rw-r--r--zellij-utils/assets/layouts/strider.yaml32
-rw-r--r--zellij-utils/src/input/actions.rs7
-rw-r--r--zellij-utils/src/input/config.rs92
-rw-r--r--zellij-utils/src/input/layout.rs300
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml43
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml18
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml35
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml31
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml21
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml29
-rw-r--r--zellij-utils/src/input/unit/keybinds_test.rs6
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs685
-rw-r--r--zellij-utils/src/ipc.rs23
-rw-r--r--zellij-utils/src/setup.rs22
28 files changed, 1572 insertions, 128 deletions
diff --git a/example/multiple_tabs_layout.yaml b/example/multiple_tabs_layout.yaml
new file mode 100644
index 00000000..6c4d1598
--- /dev/null
+++ b/example/multiple_tabs_layout.yaml
@@ -0,0 +1,82 @@
+---
+direction: Horizontal
+parts:
+ - direction: Vertical
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Vertical
+ tabs:
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ - direction: Vertical
+ - direction: Vertical
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 20
+ run:
+ plugin: strider
+ - direction: Horizontal
+ split_size:
+ Percent: 80
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 40
+ - direction: Horizontal
+ split_size:
+ Percent: 60
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Fixed: 2
+ run:
+ plugin: status-bar
diff --git a/example/multiple_tabs_layout_htop_command.yaml b/example/multiple_tabs_layout_htop_command.yaml
new file mode 100644
index 00000000..6739eba8
--- /dev/null
+++ b/example/multiple_tabs_layout_htop_command.yaml
@@ -0,0 +1,85 @@
+---
+direction: Horizontal
+parts:
+ - direction: Vertical
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Vertical
+ tabs:
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ run:
+ command: {cmd: htop}
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ - direction: Vertical
+ run:
+ command: {cmd: htop, args: ["-C"]}
+ - direction: Vertical
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 20
+ run:
+ plugin: strider
+ - direction: Horizontal
+ split_size:
+ Percent: 80
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 40
+ - direction: Horizontal
+ split_size:
+ Percent: 60
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Fixed: 2
+ run:
+ plugin: status-bar
diff --git a/src/main.rs b/src/main.rs
index 1a3a16ed..f0d00f77 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,14 +24,6 @@ pub fn main() {
list_sessions();
}
- let (config, layout, config_options) = match Setup::from_options(&opts) {
- Ok(results) => results,
- Err(e) => {
- eprintln!("{}", e);
- process::exit(1);
- }
- };
-
atomic_create_dir(&*ZELLIJ_TMP_DIR).unwrap();
atomic_create_dir(&*ZELLIJ_TMP_LOG_DIR).unwrap();
if let Some(path) = opts.server {
@@ -62,6 +54,14 @@ pub fn main() {
session_name = Some(get_active_session());
}
+ let (config, _, config_options) = match Setup::from_options(&opts) {
+ Ok(results) => results,
+ Err(e) => {
+ eprintln!("{}", e);
+ process::exit(1);
+ }
+ };
+
start_client(
Box::new(os_input),
opts,
@@ -70,6 +70,14 @@ pub fn main() {
None,
);
} else {
+ let (config, layout, _) = match Setup::from_options(&opts) {
+ Ok(results) => results,
+ Err(e) => {
+ eprintln!("{}", e);
+ process::exit(1);
+ }
+ };
+
let session_name = opts
.session
.clone()
diff --git a/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml b/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml
index e1a1a607..f0f66a32 100644
--- a/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml
+++ b/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml
@@ -9,6 +9,8 @@
- direction: Horizontal
split_size:
Percent: 50
+ tabs:
+ - direction: Horizontal
split_size:
Percent: 80
- direction: Vertical
diff --git a/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml b/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml
index 33d94225..2b55547d 100644
--- a/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml
+++ b/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml
@@ -9,6 +9,9 @@
- direction: Horizontal
split_size:
Percent: 90
+ - direction: Horizontal
+ tabs:
+ - direction: Horizontal
split_size:
Percent: 80
- direction: Vertical
diff --git a/src/tests/fixtures/layouts/three-panes-with-nesting.yaml b/src/tests/fixtures/layouts/three-panes-with-nesting.yaml
index f1e0dd7e..20a647f3 100644
--- a/src/tests/fixtures/layouts/three-panes-with-nesting.yaml
+++ b/src/tests/fixtures/layouts/three-panes-with-nesting.yaml
@@ -1,16 +1,18 @@
---
direction: Horizontal
-parts:
- - direction: Vertical
+tabs:
+ - direction: Horizontal
parts:
- - direction: Horizontal
- split_size:
- Percent: 20
- - direction: Horizontal
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 20
+ - direction: Horizontal
+ split_size:
+ Percent: 80
split_size:
Percent: 80
- split_size:
- Percent: 80
- - direction: Vertical
- split_size:
- Percent: 20
+ - direction: Vertical
+ split_size:
+ Percent: 20
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index 03d223b2..217b10f9 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -185,7 +185,7 @@ impl InputHandler {
}
Action::CloseFocus
| Action::NewPane(_)
- | Action::NewTab
+ | Action::NewTab(_)
| Action::GoToNextTab
| Action::GoToPreviousTab
| Action::CloseTab
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index 5c832e86..f74a74dd 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -19,7 +19,7 @@ use zellij_utils::{
channels::{self, ChannelWithContext, SenderWithContext},
consts::{SESSION_NAME, ZELLIJ_IPC_PIPE},
errors::{ClientContext, ContextType, ErrorInstruction},
- input::{actions::Action, config::Config, layout::Layout, options::Options},
+ input::{actions::Action, config::Config, layout::LayoutTemplate, options::Options},
ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg},
};
@@ -87,7 +87,7 @@ pub fn start_client(
opts: CliArgs,
config: Config,
info: ClientInfo,
- layout: Option<Layout>,
+ layout: Option<LayoutTemplate>,
) {
info!("Starting Zellij client!");
let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}12l\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l\u{1b}[?12l";
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 690304bb..d0b3b5d2 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -11,11 +11,13 @@ mod ui;
mod wasm_vm;
use log::info;
+use std::{
+ path::PathBuf,
+ sync::{Arc, Mutex, RwLock},
+ thread,
+};
use zellij_utils::zellij_tile;
-use std::path::PathBuf;
-use std::sync::{Arc, Mutex, RwLock};
-use std::thread;
use wasmer::Store;
use zellij_tile::data::{Event, Palette, PluginCapabilities};
@@ -34,7 +36,7 @@ use zellij_utils::{
input::{
command::{RunCommand, TerminalAction},
get_mode_info,
- layout::Layout,
+ layout::LayoutTemplate,
options::Options,
},
ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg},
@@ -44,7 +46,12 @@ use zellij_utils::{
/// Instructions related to server-side application
#[derive(Debug, Clone)]
pub(crate) enum ServerInstruction {
- NewClient(ClientAttributes, Box<CliArgs>, Box<Options>, Option<Layout>),
+ NewClient(
+ ClientAttributes,
+ Box<CliArgs>,
+ Box<Options>,
+ Option<LayoutTemplate>,
+ ),
Render(Option<String>),
UnblockInputThread,
ClientExit,
@@ -207,7 +214,7 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
to_server.clone(),
client_attributes,
session_state.clone(),
- layout,
+ layout.clone(),
);
*session_data.write().unwrap() = Some(session);
*session_state.write().unwrap() = SessionState::Attached;
@@ -219,14 +226,34 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
})
});
- session_data
- .read()
- .unwrap()
- .as_ref()
- .unwrap()
- .senders
- .send_to_pty(PtyInstruction::NewTab(default_shell.clone()))
- .unwrap();
+ let spawn_tabs = |tab_layout| {
+ session_data
+ .read()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .senders
+ .send_to_pty(PtyInstruction::NewTab(default_shell.clone(), tab_layout))
+ .unwrap()
+ };
+
+ match layout {
+ None => {
+ spawn_tabs(None);
+ }
+ Some(layout) => {
+ if !&layout.tabs.is_empty() {
+ for tab_layout in layout.tabs {
+ spawn_tabs(Some(tab_layout.clone()));
+ // Spawning tabs in too quick succession might mess up the layout
+ // TODO: investigate why
+ thread::sleep(std::time::Duration::from_millis(250));
+ }
+ } else {
+ spawn_tabs(None);
+ }
+ }
+ }
}
ServerInstruction::AttachClient(attrs, _, options) => {
*session_state.write().unwrap() = SessionState::Attached;
@@ -302,7 +329,7 @@ fn init_session(
to_server: SenderWithContext<ServerInstruction>,
client_attributes: ClientAttributes,
session_state: Arc<RwLock<SessionState>>,
- layout: Option<Layout>,
+ layout: Option<LayoutTemplate>,
) -> SessionMetaData {
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
let to_screen = SenderWithContext::new(to_screen);
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index 5ef3b321..5c0e4a26 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -20,7 +20,7 @@ use zellij_utils::{
errors::{get_current_ctx, ContextType, PtyContext},
input::{
command::TerminalAction,
- layout::{Layout, Run},
+ layout::{Layout, LayoutTemplate, Run, TabLayout},
},
logging::debug_to_file,
};
@@ -33,7 +33,7 @@ pub(crate) enum PtyInstruction {
SpawnTerminal(Option<TerminalAction>),
SpawnTerminalVertically(Option<TerminalAction>),
SpawnTerminalHorizontally(Option<TerminalAction>),
- NewTab(Option<TerminalAction>),
+ NewTab(Option<TerminalAction>, Option<TabLayout>),
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
Exit,
@@ -47,7 +47,7 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
- PtyInstruction::NewTab(_) => PtyContext::NewTab,
+ PtyInstruction::NewTab(..) => PtyContext::NewTab,
PtyInstruction::Exit => PtyContext::Exit,
}
}
@@ -60,7 +60,7 @@ pub(crate) struct Pty {
task_handles: HashMap<RawFd, JoinHandle<()>>,
}
-pub(crate) fn pty_thread_main(mut pty: Pty, maybe_layout: Option<Layout>) {
+pub(crate) fn pty_thread_main(mut pty: Pty, maybe_layout: Option<LayoutTemplate>) {
loop {
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty((&event).into()));
@@ -86,11 +86,12 @@ pub(crate) fn pty_thread_main(mut pty: Pty, maybe_layout: Option<Layout>) {
.send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
.unwrap();
}
- PtyInstruction::NewTab(terminal_action) => {
+ PtyInstruction::NewTab(terminal_action, tab_layout) => {
if let Some(layout) = maybe_layout.clone() {
- pty.spawn_terminals_for_layout(layout, terminal_action);
+ let merged_layout = layout.construct_tab_layout(tab_layout);
+ pty.spawn_terminals_for_layout(merged_layout, terminal_action.clone());
} else {
- let pid = pty.spawn_terminal(terminal_action);
+ let pid = pty.spawn_terminal(terminal_action.clone());
pty.bus
.senders
.send_to_screen(ScreenInstruction::NewTab(pid))
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 54ddfe1b..a88d04b0 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -197,11 +197,11 @@ fn route_action(
.send_to_screen(ScreenInstruction::CloseFocusedPane)
.unwrap();
}
- Action::NewTab => {
+ Action::NewTab(tab_layout) => {
let shell = session.default_shell.clone();
session
.senders
- .send_to_pty(PtyInstruction::NewTab(shell))
+ .send_to_pty(PtyInstruction::NewTab(shell, tab_layout))
.unwrap();
}
Action::GoToNextTab => {
diff --git a/zellij-utils/assets/config/default.yaml b/zellij-utils/assets/config/default.yaml
index 94f12f33..42f2287b 100644
--- a/zellij-utils/assets/config/default.yaml
+++ b/zellij-utils/assets/config/default.yaml
@@ -136,7 +136,7 @@ keybinds:
key: [ Char: 'h', Left, Up, Char: 'k',]
- action: [GoToNextTab,]
key: [ Char: 'l', Right,Down, Char: 'j']
- - action: [NewTab,]
+ - action: [NewTab: ,]
key: [ Char: 'n',]
- action: [CloseTab,]
key: [ Char: 'x',]
diff --git a/zellij-utils/assets/layouts/default.yaml b/zellij-utils/assets/layouts/default.yaml
index b163821d..e0a28da1 100644
--- a/zellij-utils/assets/layouts/default.yaml
+++ b/zellij-utils/assets/layouts/default.yaml
@@ -1,16 +1,20 @@
---
-direction: Horizontal
-parts:
+template:
+ direction: Horizontal
+ parts:
+ - direction: Vertical
+ borderless: true
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Vertical
+ body: true
+ - direction: Vertical
+ borderless: true
+ split_size:
+ Fixed: 2
+ run:
+ plugin: status-bar
+tabs:
- direction: Vertical
- borderless: true
- split_size:
- Fixed: 1
- run:
- plugin: tab-bar
- - direction: Vertical
- - direction: Vertical
- borderless: true
- split_size:
- Fixed: 2
- run:
- plugin: status-bar
diff --git a/zellij-utils/assets/layouts/disable-status-bar.yaml b/zellij-utils/assets/layouts/disable-status-bar.yaml
index a03c71c3..a58ef4cf 100644
--- a/zellij-utils/assets/layouts/disable-status-bar.yaml
+++ b/zellij-utils/assets/layouts/disable-status-bar.yaml
@@ -1,10 +1,12 @@
---
-direction: Horizontal
-parts:
- - direction: Vertical
- borderless: true
- split_size:
- Fixed: 1
- run:
- plugin: tab-bar
- - direction: Vertical
+template:
+ direction: Horizontal
+ parts:
+ - direction: Vertical
+ borderless: true
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Vertical
+ body: true
diff --git a/zellij-utils/assets/layouts/strider.yaml b/zellij-utils/assets/layouts/strider.yaml
index 28814389..96e3c290 100644
--- a/zellij-utils/assets/layouts/strider.yaml
+++ b/zellij-utils/assets/layouts/strider.yaml
@@ -1,12 +1,22 @@
---
-direction: Horizontal
-parts:
- - direction: Vertical
- borderless: true
- split_size:
- Fixed: 1
- run:
- plugin: tab-bar
+template:
+ direction: Horizontal
+ parts:
+ - direction: Vertical
+ borderless: true
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Vertical
+ body: true
+ - direction: Vertical
+ borderless: true
+ split_size:
+ Fixed: 2
+ run:
+ plugin: status-bar
+tabs:
- direction: Vertical
parts:
- direction: Horizontal
@@ -15,9 +25,3 @@ parts:
run:
plugin: strider
- direction: Horizontal
- - direction: Vertical
- borderless: true
- split_size:
- Fixed: 2
- run:
- plugin: status-bar
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index 23c01eef..cd4ca464 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -1,6 +1,7 @@
//! Definition of the actions that can be bound to keys.
use super::command::RunCommandAction;
+use super::layout::TabLayout;
use crate::input::options::OnForceClose;
use serde::{Deserialize, Serialize};
use zellij_tile::data::InputMode;
@@ -19,7 +20,7 @@ pub enum Direction {
// As these actions are bound to the default config, please
// do take care when refactoring - or renaming.
// They might need to be adjusted in the default config
-// as well `../../../assets/config/default.yaml`
+// as well `../../assets/config/default.yaml`
/// Actions that can be bound to keys.
#[derive(Eq, Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Action {
@@ -65,8 +66,8 @@ pub enum Action {
NewPane(Option<Direction>),
/// Close the focus pane.
CloseFocus,
- /// Create a new tab.
- NewTab,
+ /// Create a new tab, optionally with a specified tab layout.
+ NewTab(Option<TabLayout>),
/// Do nothing.
NoOp,
/// Go to the next tab.
diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs
index 6a65109a..930fe07a 100644
--- a/zellij-utils/src/input/config.rs
+++ b/zellij-utils/src/input/config.rs
@@ -45,6 +45,9 @@ pub enum ConfigError {
IoPath(io::Error, PathBuf),
// Internal Deserialization Error
FromUtf8(std::string::FromUtf8Error),
+ // Missing the tab section in the layout.
+ Layout(LayoutMissingTabSectionError),
+ LayoutPartAndTab(LayoutPartAndTabError),
}
impl Default for Config {
@@ -129,6 +132,75 @@ impl Config {
}
}
+// TODO: Split errors up into separate modules
+#[derive(Debug, Clone)]
+pub struct LayoutMissingTabSectionError;
+#[derive(Debug, Clone)]
+pub struct LayoutPartAndTabError;
+
+impl fmt::Display for LayoutMissingTabSectionError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "MissingTabSectionError:
+There needs to be exactly one `tabs` section specified in the layout file, for example:
+---
+direction: Horizontal
+parts:
+ - direction: Vertical
+ - direction: Vertical
+ tabs:
+ - direction: Vertical
+ - direction: Vertical
+ - direction: Vertical
+"
+ )
+ }
+}
+
+impl std::error::Error for LayoutMissingTabSectionError {
+ fn description(&self) -> &str {
+ "One tab must be specified per Layout."
+ }
+}
+
+impl fmt::Display for LayoutPartAndTabError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "LayoutPartAndTabError:
+The `tabs` and `parts` section should not be specified on the same level in the layout file, for example:
+---
+direction: Horizontal
+parts:
+ - direction: Vertical
+ - direction: Vertical
+tabs:
+ - direction: Vertical
+ - direction: Vertical
+ - direction: Vertical
+
+should rather be specified as:
+---
+direction: Horizontal
+parts:
+ - direction: Vertical
+ - direction: Vertical
+ tabs:
+ - direction: Vertical
+ - direction: Vertical
+ - direction: Vertical
+"
+ )
+ }
+}
+
+impl std::error::Error for LayoutPartAndTabError {
+ fn description(&self) -> &str {
+ "The `tabs` and parts section should not be specified on the same level."
+ }
+}
+
impl Display for ConfigError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
@@ -138,6 +210,12 @@ impl Display for ConfigError {
}
ConfigError::Serde(ref err) => write!(formatter, "Deserialization error: {}", err),
ConfigError::FromUtf8(ref err) => write!(formatter, "FromUtf8Error: {}", err),
+ ConfigError::Layout(ref err) => {
+ write!(formatter, "There was an error in the layout file, {}", err)
+ }
+ ConfigError::LayoutPartAndTab(ref err) => {
+ write!(formatter, "There was an error in the layout file, {}", err)
+ }
}
}
}
@@ -149,6 +227,8 @@ impl std::error::Error for ConfigError {
ConfigError::IoPath(ref err, _) => Some(err),
ConfigError::Serde(ref err) => Some(err),
ConfigError::FromUtf8(ref err) => Some(err),
+ ConfigError::Layout(ref err) => Some(err),
+ ConfigError::LayoutPartAndTab(ref err) => Some(err),
}
}
}
@@ -171,6 +251,18 @@ impl From<std::string::FromUtf8Error> for ConfigError {
}
}
+impl From<LayoutMissingTabSectionError> for ConfigError {
+ fn from(err: LayoutMissingTabSectionError) -> ConfigError {
+ ConfigError::Layout(err)
+ }
+}
+
+impl From<LayoutPartAndTabError> for ConfigError {
+ fn from(err: LayoutPartAndTabError) -> ConfigError {
+ ConfigError::LayoutPartAndTab(err)
+ }
+}
+
// The unit test location.
#[cfg(test)]
mod config_test {
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 33e08bfd..96f6189c 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -17,23 +17,26 @@ use crate::{serde, serde_yaml};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
+use std::vec::Vec;
use std::{fs::File, io::prelude::*};
-#[derive(Debug, Serialize, Deserialize, Clone)]
+use super::config::{LayoutMissingTabSectionError, LayoutPartAndTabError};
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(crate = "self::serde")]
pub enum Direction {
Horizontal,
Vertical,
}
-#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
#[serde(crate = "self::serde")]
pub enum SplitSize {
Percent(u8), // 1 to 100
Fixed(u16), // An absolute number of columns or rows
}
-#[derive(Debug, Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(crate = "self::serde")]
pub enum Run {
#[serde(rename = "plugin")]
@@ -42,35 +45,49 @@ pub enum Run {
Command(RunCommand),
}
-#[derive(Debug, Serialize, Deserialize, Clone)]
+// The layout struct ultimately used to build the layouts.
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(crate = "self::serde")]
pub struct Layout {
pub direction: Direction,
#[serde(default)]
pub parts: Vec<Layout>,
+ #[serde(default)]
+ pub tabs: Vec<TabLayout>,
pub split_size: Option<SplitSize>,
pub run: Option<Run>,
#[serde(default)]
pub borderless: bool,
}
-type LayoutResult = Result<Layout, ConfigError>;
+// The struct that is used to deserialize the layout from
+// a yaml configuration file
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+#[serde(crate = "self::serde")]
+pub struct LayoutFromYaml {
+ //#[serde(default)]
+ pub template: LayoutTemplateFromYaml,
+ #[serde(default)]
+ pub tabs: Vec<TabLayout>,
+}
-impl Layout {
- pub fn new(layout_path: &Path) -> LayoutResult {
+type LayoutFromYamlResult = Result<LayoutFromYaml, ConfigError>;
+
+impl LayoutFromYaml {
+ pub fn new(layout_path: &Path) -> LayoutFromYamlResult {
let mut layout_file = File::open(&layout_path)
.or_else(|_| File::open(&layout_path.with_extension("yaml")))
.map_err(|e| ConfigError::IoPath(e, layout_path.into()))?;
let mut layout = String::new();
layout_file.read_to_string(&mut layout)?;
- let layout: Layout = serde_yaml::from_str(&layout)?;
+ let layout: LayoutFromYaml = serde_yaml::from_str(&layout)?;
Ok(layout)
}
// It wants to use Path here, but that doesn't compile.
#[allow(clippy::ptr_arg)]
- pub fn from_dir(layout: &PathBuf, layout_dir: Option<&PathBuf>) -> LayoutResult {
+ pub fn from_dir(layout: &PathBuf, layout_dir: Option<&PathBuf>) -> LayoutFromYamlResult {
match layout_dir {
Some(dir) => Self::new(&dir.join(layout))
.or_else(|_| Self::from_default_assets(layout.as_path())),
@@ -82,22 +99,31 @@ impl Layout {
layout: Option<&PathBuf>,
layout_path: Option<&PathBuf>,
layout_dir: Option<PathBuf>,
- ) -> Option<Result<Layout, ConfigError>> {
+ ) -> Option<LayoutFromYamlResult> {
layout
- .map(|p| Layout::from_dir(p, layout_dir.as_ref()))
- .or_else(|| layout_path.map(|p| Layout::new(p)))
+ .map(|p| LayoutFromYaml::from_dir(p, layout_dir.as_ref()))
+ .or_else(|| layout_path.map(|p| LayoutFromYaml::new(p)))
.or_else(|| {
- Some(Layout::from_dir(
+ Some(LayoutFromYaml::from_dir(
&std::path::PathBuf::from("default"),
layout_dir.as_ref(),
))
})
}
+ pub fn construct_layout_template(&self) -> LayoutTemplate {
+ let (pre_tab, post_tab) = self.template.split_template().unwrap();
+ LayoutTemplate {
+ pre_tab: pre_tab.into(),
+ post_tab: Layout::from_vec_template_layout(post_tab),
+ tabs: self.tabs.clone(),
+ }
+ }
+
// Currently still needed but on nightly
// this is already possible:
// HashMap<&'static str, Vec<u8>>
- pub fn from_default_assets(path: &Path) -> LayoutResult {
+ pub fn from_default_assets(path: &Path) -> LayoutFromYamlResult {
match path.to_str() {
Some("default") => Self::default_from_assets(),
Some("strider") => Self::strider_from_assets(),
@@ -111,24 +137,123 @@ impl Layout {
// TODO Deserialize the assets from bytes &[u8],
// once serde-yaml supports zero-copy
- pub fn default_from_assets() -> LayoutResult {
- let layout: Layout =
+ pub fn default_from_assets() -> LayoutFromYamlResult {
+ let layout: LayoutFromYaml =
serde_yaml::from_str(String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?.as_str())?;
Ok(layout)
}
- pub fn strider_from_assets() -> LayoutResult {
- let layout: Layout =
+ pub fn strider_from_assets() -> LayoutFromYamlResult {
+ let layout: LayoutFromYaml =
serde_yaml::from_str(String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?.as_str())?;
Ok(layout)
}
- pub fn disable_status_from_assets() -> LayoutResult {
- let layout: Layout =
+ pub fn disable_status_from_assets() -> LayoutFromYamlResult {
+ let layout: LayoutFromYaml =
serde_yaml::from_str(String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?.as_str())?;
Ok(layout)
}
+}
+
+// The struct that carries the information template that is used to
+// construct the layout
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+#[serde(crate = "self::serde")]
+pub struct LayoutTemplateFromYaml {
+ pub direction: Direction,
+ #[serde(default)]
+ pub parts: Vec<LayoutTemplateFromYaml>,
+ #[serde(default)]
+ pub body: bool,
+ pub split_size: Option<SplitSize>,
+ pub run: Option<Run>,
+}
+
+impl LayoutTemplateFromYaml {
+ // Split the layout into parts that can be reassebled per tab
+ // returns the layout pre tab and the parts post tab
+ pub fn split_template(
+ &self,
+ ) -> Result<(LayoutTemplateFromYaml, Vec<LayoutTemplateFromYaml>), LayoutPartAndTabError> {
+ let mut main_layout = self.clone();
+ let mut pre_tab_layout = self.clone();
+ let mut post_tab_layout = vec![];
+ let mut post_tab = false;
+
+ pre_tab_layout.parts.clear();
+
+ if main_layout.body {
+ post_tab = true;
+ }
+
+ for part in main_layout.parts.drain(..) {
+ let (curr_pre_layout, mut curr_post_layout) = part.split_template()?;
+
+ // Leaf
+ if !post_tab && !part.body {
+ pre_tab_layout.parts.push(curr_pre_layout);
+ }
+
+ // Node
+ if part.body {
+ post_tab = true;
+ // Leaf
+ } else if post_tab {
+ if curr_post_layout.is_empty() {
+ let mut part_no_tab = part.clone();
+ part_no_tab.parts.clear();
+ post_tab_layout.push(part_no_tab);
+ } else {
+ post_tab_layout.append(&mut curr_post_layout);
+ }
+ }
+ }
+ Ok((pre_tab_layout, post_tab_layout))
+ }
+}
+
+// The tab-layout struct used to specify each individual tab.
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+#[serde(crate = "self::serde")]
+pub struct TabLayout {
+ pub direction: Direction,
+ #[serde(default)]
+ pub parts: Vec<TabLayout>,
+ pub split_size: Option<SplitSize>,
+ pub run: Option<Run>,
+}
+
+// Main template layout struct, that carries information based on position of
+// tabs in relation to the whole layout.
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+#[serde(crate = "self::serde")]
+pub struct LayoutTemplate {
+ pub pre_tab: Layout,
+ pub post_tab: Vec<Layout>,
+ pub tabs: Vec<TabLayout>,
+}
+
+impl LayoutTemplate {
+ pub fn construct_tab_layout(&self, tab_layout: Option<TabLayout>) -> Layout {
+ if let Some(tab_layout) = tab_layout {
+ let mut pre_tab_layout = self.pre_tab.clone();
+ let post_tab_layout = &self.post_tab;
+ pre_tab_layout.merge_tab_layout(tab_layout);
+ pre_tab_layout.merge_layout_parts(post_tab_layout.to_owned());
+ pre_tab_layout
+ } else {
+ let mut pre_tab_layout = self.pre_tab.clone();
+ let post_tab_layout = &self.post_tab;
+ let default_tab_layout = TabLayout::default();
+ pre_tab_layout.merge_tab_layout(default_tab_layout);
+ pre_tab_layout.merge_layout_parts(post_tab_layout.to_owned());
+ pre_tab_layout
+ }
+ }
+}
+impl Layout {
pub fn total_terminal_panes(&self) -> usize {
let mut total_panes = 0;
total_panes += self.parts.len();
@@ -169,6 +294,101 @@ impl Layout {
) -> Vec<(Layout, PositionAndSize)> {
split_space(space, self)
}
+
+ // Split the layout into parts that can be reassebled per tab
+ // returns the layout pre tab, the parts post tab and the tab layouts
+ pub fn split_main_and_tab_layout(
+ &self,
+ ) -> Result<(Layout, Vec<Layout>, Vec<TabLayout>), LayoutPartAndTabError> {
+ let mut main_layout = self.clone();
+ let mut pre_tab_layout = self.clone();
+ let mut post_tab_layout = vec![];
+ let mut tabs = vec![];
+ let mut post_tab = false;
+
+ pre_tab_layout.parts.clear();
+ pre_tab_layout.tabs.clear();
+
+ if !main_layout.tabs.is_empty() {
+ tabs.append(&mut main_layout.tabs);
+ post_tab = true;
+ }
+
+ if !main_layout.tabs.is_empty() && !main_layout.parts.is_empty() {
+ return Err(LayoutPartAndTabError);
+ }
+
+ for part in main_layout.parts.drain(..) {
+ let (curr_pre_layout, mut curr_post_layout, mut curr_tabs) =
+ part.split_main_and_tab_layout()?;
+
+ // Leaf
+ if !post_tab && part.tabs.is_empty() {
+ pre_tab_layout.parts.push(curr_pre_layout);
+ }
+
+ if !part.tabs.is_empty() && !part.parts.is_empty() {
+ return Err(LayoutPartAndTabError);
+ }
+
+ // Node
+ if !part.tabs.is_empty() {
+ tabs.append(&mut part.tabs.clone());
+ post_tab = true;
+ // Node
+ } else if !curr_tabs.is_empty() {
+ tabs.append(&mut curr_tabs);
+ post_tab = true;
+ // Leaf
+ } else if post_tab {
+ if curr_post_layout.is_empty() {
+ let mut part_no_tab = part.clone();
+ part_no_tab.tabs.clear();
+ part_no_tab.parts.clear();
+ post_tab_layout.push(part_no_tab);
+ } else {
+ post_tab_layout.append(&mut curr_post_layout);
+ }
+ }
+ }
+ Ok((pre_tab_layout, post_tab_layout, tabs))
+ }
+
+ pub fn merge_tab_layout(&mut self, tab: TabLayout) {
+ self.parts.push(tab.into());
+ }
+
+ pub fn merge_layout_parts(&mut self, mut parts: Vec<Layout>) {
+ self.parts.append(&mut parts);
+ }
+
+ pub fn construct_layout_template(&self) -> Result<LayoutTemplate, ConfigError> {
+ let (pre_tab, post_tab, tabs) = self.split_main_and_tab_layout()?;
+
+ if tabs.is_empty() {
+ return Err(ConfigError::Layout(LayoutMissingTabSectionError));
+ }
+
+ Ok(LayoutTemplate {
+ pre_tab,
+ post_tab,
+ tabs,
+ })
+ }
+
+ fn from_vec_tab_layout(tab_layout: Vec<TabLayout>) -> Vec<Self> {
+ tab_layout
+ .iter()
+ .map(|tab_layout| Layout::from(tab_layout.to_owned()))
+ .collect()
+ }
+
+ fn from_vec_template_layout(layout_template: Vec<LayoutTemplateFromYaml>) -> Vec<Self> {
+ layout_template
+ .iter()
+ .map(|layout_template| Layout::from(layout_template.to_owned()))
+ .collect()
+ }
}
fn split_space_to_parts_vertically(
@@ -323,3 +543,43 @@ fn split_space(
}
pane_positions
}
+
+impl From<TabLayout> for Layout {
+ fn from(tab: TabLayout) -> Self {
+ Layout {
+ direction: tab.direction,
+ parts: Layout::from_vec_tab_layout(tab.parts),
+ tabs: vec![],
+ split_size: tab.split_size,
+ run: tab.run,
+ }
+ }
+}
+
+impl From<LayoutTemplateFromYaml> for Layout {
+ fn from(template: LayoutTemplateFromYaml) -> Self {
+ Layout {
+ direction: template.direction,
+ parts: Layout::from_vec_template_layout(template.parts),
+ tabs: vec![],
+ split_size: template.split_size,
+ run: template.run,
+ }
+ }
+}
+
+impl Default for TabLayout {
+ fn default() -> Self {
+ Self {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ split_size: None,
+ run: None,
+ }
+ }
+}
+
+// The unit test location.
+#[cfg(test)]
+#[path = "./unit/layout_test.rs"]
+mod layout_test;
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml b/zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml
new file mode 100644
index 00000000..633a1fae
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml
@@ -0,0 +1,43 @@
+---
+template:
+ direction: Horizontal
+ parts:
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 21
+ - direction: Vertical
+ split_size:
+ Percent: 79
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 22
+ - direction: Horizontal
+ split_size:
+ Percent: 78
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 23
+ - direction: Vertical
+ split_size:
+ Percent: 77
+ body: true
+ split_size:
+ Percent: 90
+ - direction: Vertical
+ split_size:
+ Percent: 15
+ - direction: Vertical
+ split_size:
+ Percent: 15
+ - direction: Vertical
+ split_size:
+ Percent: 15
+
+tabs:
+ - direction: Horizontal
+ split_size:
+ Percent: 24
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml b/zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml
new file mode 100644
index 00000000..65378759
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml
@@ -0,0 +1,18 @@
+---
+direction: Horizontal
+parts:
+ - direction: Horizontal
+ parts:
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ tabs:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml
new file mode 100644
index 00000000..9ad1034d
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml
@@ -0,0 +1,35 @@
+---
+template:
+ direction: Horizontal
+ parts:
+ - direction: Vertical
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Horizontal
+ body: true
+ - direction: Vertical
+ split_size:
+ Fixed: 2
+ run:
+ plugin: status-bar
+
+tabs:
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ run:
+ command: {cmd: htop}
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ run:
+ command: {cmd: htop, args: ["-C"]}
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml
new file mode 100644
index 00000000..88046395
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml
@@ -0,0 +1,31 @@
+---
+template:
+ direction: Horizontal
+ parts:
+ - direction: Vertical
+ split_size:
+ Fixed: 1
+ run:
+ plugin: tab-bar
+ - direction: Horizontal
+ body: true
+ - direction: Vertical
+ split_size:
+ Fixed: 2
+ run:
+ plugin: status-bar
+
+tabs:
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml
new file mode 100644
index 00000000..83f05076
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml
@@ -0,0 +1,21 @@
+---
+template:
+ direction: Horizontal
+ parts:
+ - direction: Horizontal
+ body: true
+
+tabs:
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Vertical
+ split_size:
+ Percent: 50
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml
new file mode 100644
index 00000000..e2a09a73
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml
@@ -0,0 +1,29 @@
+---
+template:
+ direction: Vertical
+ parts:
+ - direction: Horizontal
+ body: true
+ - direction: Horizontal
+
+
+tabs:
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 50
+ - direction: Horizontal
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ parts:
+ - direction: Vertical
+ split_size:
+ Percent: 50
+ - direction: Horizontal
diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs
index 4767fc15..e8a776d7 100644
--- a/zellij-utils/src/input/unit/keybinds_test.rs
+++ b/zellij-utils/src/input/unit/keybinds_test.rs
@@ -150,7 +150,7 @@ fn no_unbind_unbinds_none() {
#[test]
fn last_keybind_is_taken() {
- let actions_1 = vec![Action::NoOp, Action::NewTab];
+ let actions_1 = vec![Action::NoOp, Action::NewTab(None)];
let keyaction_1 = KeyActionFromYaml {
action: actions_1.clone(),
key: vec![Key::F(1), Key::Backspace, Key::Char('t')],
@@ -171,7 +171,7 @@ fn last_keybind_is_taken() {
#[test]
fn last_keybind_overwrites() {
- let actions_1 = vec![Action::NoOp, Action::NewTab];
+ let actions_1 = vec![Action::NoOp, Action::NewTab(None)];
let keyaction_1 = KeyActionFromYaml {
action: actions_1.clone(),
key: vec![Key::F(1), Key::Backspace, Key::Char('t')],
@@ -764,7 +764,7 @@ fn unbind_single_toplevel_multiple_keys_multiple_modes() {
fn uppercase_and_lowercase_are_distinct() {
let key_action_n = KeyActionFromYaml {
key: vec![Key::Char('n')],
- action: vec![Action::NewTab],
+ action: vec![Action::NewTab(None)],
};
let key_action_large_n = KeyActionFromYaml {
key: vec![Key::Char('N')],
diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs
new file mode 100644
index 00000000..d09d328c
--- /dev/null
+++ b/zellij-utils/src/input/unit/layout_test.rs
@@ -0,0 +1,685 @@
+use super::super::layout::*;
+
+fn layout_test_dir(layout: String) -> PathBuf {
+ let root = Path::new(env!("CARGO_MANIFEST_DIR"));
+ let layout_dir = root.join("src/input/unit/fixtures/layouts");
+ layout_dir.join(layout)
+}
+
+fn default_layout_dir(layout: String) -> PathBuf {
+ let root = Path::new(env!("CARGO_MANIFEST_DIR"));
+ let layout_dir = root.join("assets/layouts");
+ layout_dir.join(layout)
+}
+
+#[test]
+fn default_layout_is_ok() {
+ let path = default_layout_dir("default.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ assert!(layout.is_ok());
+}
+
+#[test]
+fn default_layout_has_one_tab() {
+ let path = default_layout_dir("default.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ assert_eq!(layout_template.tabs.len(), 1);
+}
+
+#[test]
+fn default_layout_has_one_pre_tab() {
+ let path = default_layout_dir("default.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ assert_eq!(layout_template.pre_tab.parts.len(), 1);
+}
+
+#[test]
+fn default_layout_has_one_post_tab() {
+ let path = default_layout_dir("default.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ assert_eq!(layout_template.post_tab.len(), 1);
+}
+
+#[test]
+fn default_layout_merged_correctly() {
+ let path = default_layout_dir("default.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml
+ .as_ref()
+ .unwrap()
+ .construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[0].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(1)),
+ run: Some(Run::Plugin(Some("tab-bar".into()))),
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(2)),
+ run: Some(Run::Plugin(Some("status-bar".into()))),
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn default_layout_new_tab_correct() {
+ let path = default_layout_dir("default.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml
+ .as_ref()
+ .unwrap()
+ .construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(None);
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(1)),
+ run: Some(Run::Plugin(Some("tab-bar".into()))),
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(2)),
+ run: Some(Run::Plugin(Some("status-bar".into()))),
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn default_strider_layout_is_ok() {
+ let path = default_layout_dir("strider.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ assert!(layout_from_yaml.is_ok());
+}
+
+#[test]
+fn default_disable_status_layout_is_ok() {
+ let path = default_layout_dir("disable-status-bar.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ assert!(layout_from_yaml.is_ok());
+}
+
+#[test]
+fn default_disable_status_layout_has_no_tab() {
+ let path = default_layout_dir("disable-status-bar.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml
+ .as_ref()
+ .unwrap()
+ .construct_layout_template();
+ assert_eq!(layout_template.tabs.len(), 0);
+}
+
+#[test]
+fn default_disable_status_layout_has_one_pre_tab() {
+ let path = default_layout_dir("disable-status-bar.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml
+ .as_ref()
+ .unwrap()
+ .construct_layout_template();
+ assert_eq!(layout_template.pre_tab.parts.len(), 1);
+}
+
+#[test]
+fn default_disable_status_layout_has_no_post_tab() {
+ let path = default_layout_dir("disable-status-bar.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ assert!(layout_template.post_tab.is_empty());
+}
+
+#[test]
+fn three_panes_with_tab_is_ok() {
+ let path = layout_test_dir("three-panes-with-tab.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ assert!(layout.is_ok());
+}
+
+#[test]
+fn three_panes_with_tab_has_one_tab() {
+ let path = layout_test_dir("three-panes-with-tab.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert_eq!(layout_template.tabs.len(), 1);
+}
+
+#[test]
+fn three_panes_with_tab_no_post_tab() {
+ let path = layout_test_dir("three-panes-with-tab.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert!(layout_template.post_tab.is_empty());
+}
+
+#[test]
+fn three_panes_with_tab_no_pre_tab() {
+ let path = layout_test_dir("three-panes-with-tab.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert!(layout_template.pre_tab.parts.is_empty());
+}
+
+#[test]
+fn three_panes_with_tab_merged_correctly() {
+ let path = layout_test_dir("three-panes-with-tab.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[0].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ }],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn three_panes_with_tab_new_tab_is_correct() {
+ let path = layout_test_dir("three-panes-with-tab.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(None);
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ }],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn three_panes_with_tab_and_default_plugins_is_ok() {
+ let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ assert!(layout.is_ok());
+}
+
+#[test]
+fn three_panes_with_tab_and_default_plugins_has_one_tab() {
+ let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert_eq!(layout_template.tabs.len(), 1);
+}
+
+#[test]
+fn three_panes_with_tab_and_default_plugins_one_post_tab() {
+ let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert_eq!(layout_template.post_tab.len(), 1);
+}
+
+#[test]
+fn three_panes_with_tab_and_default_plugins_has_pre_tab() {
+ let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert!(!layout_template.pre_tab.parts.is_empty());
+}
+
+#[test]
+fn three_panes_with_tab_and_default_plugins_merged_correctly() {
+ let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[0].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(1)),
+ run: Some(Run::Plugin(Some("tab-bar".into()))),
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(2)),
+ run: Some(Run::Plugin(Some("status-bar".into()))),
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() {
+ let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(None);
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(1)),
+ run: Some(Run::Plugin(Some("tab-bar".into()))),
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Fixed(2)),
+ run: Some(Run::Plugin(Some("status-bar".into()))),
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn deeply_nested_tab_is_ok() {
+ let path = layout_test_dir("deeply-nested-tab-layout.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ assert!(layout.is_ok());
+}
+
+#[test]
+fn deeply_nested_tab_has_one_tab() {
+ let path = layout_test_dir("deeply-nested-tab-layout.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert_eq!(layout_template.tabs.len(), 1);
+}
+
+#[test]
+fn deeply_nested_tab_three_post_tab() {
+ let path = layout_test_dir("deeply-nested-tab-layout.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert_eq!(layout_template.post_tab.len(), 3);
+}
+
+#[test]
+fn deeply_nested_tab_has_many_pre_tab() {
+ let path = layout_test_dir("deeply-nested-tab-layout.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.unwrap().construct_layout_template();
+ assert!(!layout_template.pre_tab.parts.is_empty());
+}
+
+#[test]
+fn deeply_nested_tab_merged_correctly() {
+ let path = layout_test_dir("deeply-nested-tab-layout.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[0].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(21)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(22)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(23)),
+ run: None,
+ }],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(78)),
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(79)),
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(90)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(24)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(15)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(15)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(15)),
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn three_tabs_is_ok() {
+ let path = layout_test_dir("three-tabs-merged-correctly.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ assert!(layout_from_yaml.is_ok());
+}
+
+#[test]
+fn three_tabs_has_three_tabs() {
+ let path = layout_test_dir("three-tabs-merged-correctly.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml.unwrap().construct_layout_template();
+ assert_eq!(layout_template.tabs.len(), 3);
+}
+
+#[test]
+fn three_tabs_has_one_post_tab() {
+ let path = layout_test_dir("three-tabs-merged-correctly.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml.unwrap().construct_layout_template();
+ assert_eq!(layout_template.post_tab.len(), 1);
+}
+
+#[test]
+fn three_tabs_tab_one_merged_correctly() {
+ let path = layout_test_dir("three-tabs-merged-correctly.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml
+ .as_ref()
+ .unwrap()
+ .construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[0].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn three_tabs_tab_two_merged_correctly() {
+ let path = layout_test_dir("three-tabs-merged-correctly.yaml".into());
+ let layout_from_yaml = LayoutFromYaml::new(&path);
+ let layout_template = layout_from_yaml
+ .as_ref()
+ .unwrap()
+ .construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[1].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+
+ assert_eq!(merged_layout, tab_layout);
+}
+
+#[test]
+fn three_tabs_tab_three_merged_correctly() {
+ let path = layout_test_dir("three-tabs-merged-correctly.yaml".into());
+ let layout = LayoutFromYaml::new(&path);
+ let layout_template = layout.as_ref().unwrap().construct_layout_template();
+ let tab_layout = layout_template.construct_tab_layout(Some(layout_template.tabs[2].clone()));
+ let merged_layout = Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![
+ Layout {
+ direction: Direction::Vertical,
+ parts: vec![],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: Some(SplitSize::Percent(50)),
+ run: None,
+ },
+ Layout {
+ direction: Direction::Horizontal,
+ parts: vec![],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ },
+ ],
+ tabs: vec![],
+ split_size: None,
+ run: None,
+ };
+ assert_eq!(merged_layout, tab_layout);
+}
diff --git a/zellij-utils/src/ipc.rs b/zellij-utils/src/ipc.rs
index cf952bdc..d0799d4b 100644
--- a/zellij-utils/src/ipc.rs
+++ b/zellij-utils/src/ipc.rs
@@ -1,18 +1,20 @@
//! IPC stuff for starting to split things into a client and server model.
-use crate::cli::CliArgs;
-use crate::pane_size::PositionAndSize;
use crate::{
+ cli::CliArgs,
errors::{get_current_ctx, ErrorContext},
- input::{actions::Action, layout::Layout, options::Options},
+ input::{actions::Action, layout::LayoutTemplate, options::Options},
+ pane_size::PositionAndSize,
};
use interprocess::local_socket::LocalSocketStream;
use nix::unistd::dup;
use serde::{Deserialize, Serialize};
-use std::fmt::{Display, Error, Formatter};
-use std::io::{self, Write};
-use std::marker::PhantomData;
-use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::{
+ fmt::{Display, Error, Formatter},
+ io::{self, Write},
+ marker::PhantomData,
+ os::unix::io::{AsRawFd, FromRawFd},
+};
use zellij_tile::data::Palette;
@@ -56,7 +58,12 @@ pub enum ClientToServerMsg {
// Disconnect from the session we're connected to
DisconnectFromSession,*/
TerminalResize(PositionAndSize),
- NewClient(ClientAttributes, Box<CliArgs>, Box<Options>, Option<Layout>),
+ NewClient(
+ ClientAttributes,
+ Box<CliArgs>,
+ Box<Options>,
+ Option<LayoutTemplate>,
+ ),
AttachClient(ClientAttributes, bool, Options),
Action(Action),
ClientExited,
diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs
index e3960951..c680822d 100644
--- a/zellij-utils/src/setup.rs
+++ b/zellij-utils/src/setup.rs
@@ -1,12 +1,13 @@
-use crate::consts::{
- FEATURES, SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION, ZELLIJ_PROJ_DIR,
-};
-use crate::input::options::Options;
use crate::{
cli::{CliArgs, Command},
+ consts::{
+ FEATURES, SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION,
+ ZELLIJ_PROJ_DIR,
+ },
input::{
config::{Config, ConfigError},
- layout::Layout,
+ layout::{LayoutFromYaml, LayoutTemplate},
+ options::Options,
},
};
use directories_next::BaseDirs;
@@ -150,7 +151,9 @@ impl Setup {
/// file options:
/// 1. command line options (`zellij options`)
/// 2. config options (`config.yaml`)
- pub fn from_options(opts: &CliArgs) -> Result<(Config, Option<Layout>, Options), ConfigError> {
+ pub fn from_options(
+ opts: &CliArgs,
+ ) -> Result<(Config, Option<LayoutTemplate>, Options), ConfigError> {
let clean = match &opts.command {
Some(Command::Setup(ref setup)) => setup.clean,
_ => false,
@@ -160,7 +163,6 @@ impl Setup {
match Config::try_from(opts) {
Ok(config) => config,
Err(e) => {
- eprintln!("There was an error in the config file:");
return Err(e);
}
}
@@ -174,7 +176,7 @@ impl Setup {
.layout_dir
.clone()
.or_else(|| get_layout_dir(opts.config_dir.clone().or_else(find_default_config_dir)));
- let layout_result = crate::input::layout::Layout::from_path_or_default(
+ let layout_result = LayoutFromYaml::from_path_or_default(
opts.layout.as_ref(),
opts.layout_path.as_ref(),
layout_dir,
@@ -183,10 +185,10 @@ impl Setup {
None => None,
Some(Ok(layout)) => Some(layout),
Some(Err(e)) => {
- eprintln!("There was an error in the layout file:");
return Err(e);
}
- };
+ }
+ .map(|layout| layout.construct_layout_template());
if let Some(Command::Setup(ref setup)) = &opts.command {
setup.from_cli(opts, &config_options).map_or_else(