Add tabs to layouts
fixes #603, fixes #349 * The layout has now a unique `tabs` section, that can be used, like the `parts` section, everything that is not inside the tabs section is assumed to be present on every single tab that is opened. This is a BREAKING CHANGE for people that use custom `layouts` already, since the `tabs` section is not optional - for clarity and intentionality reasons. The functionality to specify multiple tabs is already there, but is still gated behind a panic, until #621 is fixed. So for now one tab can be specified to load on startup. * The `NewTab` action can optionally be bound to open a layout that is assumed to be in the new `tabs` section This is a BREAKING CHANGE for people that have the `NewTab` action already bound in the config file: ``` - action: [NewTab, ] key: [F: 5,] ``` must now be specified as: ``` - action: [NewTab: ,] key: [F: 5,] ``` Optionally a layout that should be opened on the new tab can be specified: ``` - action: [NewTab: { direction: Vertical, parts: [ {direction: Horizontal, split_size: {Percent: 50}}, {direction: Horizontal, run: {command: {cmd: "htop"}}},], key: [F: 6,] ``` or: ``` - action: [NewTab: {direction: Vertical, run: {command: {cmd: "htop"} }},] key: [F: 7,] ``` or ``` - action: [NewTab: { direction: Vertical, parts: [ {direction: Vertical, split_size: {Percent: 25},run: {plugin: "strider" }}, {direction: Horizontal}],}, MoveFocus: Left,] key: [F: 8,] ```
This commit is contained in:
parent
3df0210647
commit
5ede25dc37
22 changed files with 993 additions and 56 deletions
|
|
@ -94,7 +94,8 @@ pub fn main() {
|
|||
opts.layout.as_ref(),
|
||||
opts.layout_path.as_ref(),
|
||||
layout_dir,
|
||||
);
|
||||
)
|
||||
.map(|layout| layout.construct_main_layout());
|
||||
|
||||
start_client(
|
||||
Box::new(os_input),
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ impl InputHandler {
|
|||
}
|
||||
Action::CloseFocus
|
||||
| Action::NewPane(_)
|
||||
| Action::NewTab
|
||||
| Action::NewTab(_)
|
||||
| Action::GoToNextTab
|
||||
| Action::GoToPreviousTab
|
||||
| Action::CloseTab
|
||||
|
|
|
|||
|
|
@ -18,7 +18,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::MainLayout, options::Options},
|
||||
ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg},
|
||||
};
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ pub fn start_client(
|
|||
opts: CliArgs,
|
||||
config: Config,
|
||||
info: ClientInfo,
|
||||
layout: Option<Layout>,
|
||||
layout: Option<MainLayout>,
|
||||
) {
|
||||
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";
|
||||
let take_snapshot = "\u{1b}[?1049h";
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ mod thread_bus;
|
|||
mod ui;
|
||||
mod wasm_vm;
|
||||
|
||||
use zellij_utils::zellij_tile;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::thread;
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
thread,
|
||||
};
|
||||
use wasmer::Store;
|
||||
use zellij_tile::data::{Event, Palette, PluginCapabilities};
|
||||
|
||||
|
|
@ -32,17 +32,23 @@ use zellij_utils::{
|
|||
input::{
|
||||
command::{RunCommand, TerminalAction},
|
||||
get_mode_info,
|
||||
layout::Layout,
|
||||
layout::MainLayout,
|
||||
options::Options,
|
||||
},
|
||||
ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg},
|
||||
setup::get_default_data_dir,
|
||||
zellij_tile,
|
||||
};
|
||||
|
||||
/// 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<MainLayout>,
|
||||
),
|
||||
Render(Option<String>),
|
||||
UnblockInputThread,
|
||||
ClientExit,
|
||||
|
|
@ -204,7 +210,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;
|
||||
|
|
@ -216,14 +222,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;
|
||||
|
|
@ -299,7 +325,7 @@ fn init_session(
|
|||
to_server: SenderWithContext<ServerInstruction>,
|
||||
client_attributes: ClientAttributes,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
layout: Option<Layout>,
|
||||
layout: Option<MainLayout>,
|
||||
) -> SessionMetaData {
|
||||
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
|
||||
let to_screen = SenderWithContext::new(to_screen);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use zellij_utils::{
|
|||
errors::{get_current_ctx, ContextType, PtyContext},
|
||||
input::{
|
||||
command::TerminalAction,
|
||||
layout::{Layout, Run},
|
||||
layout::{Layout, MainLayout, 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<MainLayout>) {
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -175,11 +175,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 => {
|
||||
|
|
|
|||
|
|
@ -134,7 +134,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',]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ parts:
|
|||
run:
|
||||
plugin: tab-bar
|
||||
- direction: Vertical
|
||||
tabs:
|
||||
- direction: Vertical
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 2
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@ parts:
|
|||
run:
|
||||
plugin: tab-bar
|
||||
- direction: Vertical
|
||||
tabs:
|
||||
- direction: Vertical
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@ parts:
|
|||
run:
|
||||
plugin: tab-bar
|
||||
- direction: Vertical
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
split_size:
|
||||
Percent: 20
|
||||
run:
|
||||
plugin: strider
|
||||
- direction: Horizontal
|
||||
tabs:
|
||||
- direction: Vertical
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
split_size:
|
||||
Percent: 20
|
||||
run:
|
||||
plugin: strider
|
||||
- direction: Horizontal
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 2
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -61,8 +62,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.
|
||||
|
|
|
|||
|
|
@ -17,23 +17,24 @@ 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)]
|
||||
#[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,16 +43,58 @@ pub enum Run {
|
|||
Command(RunCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
// The layout struct that is 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>,
|
||||
}
|
||||
|
||||
#[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 layout struct, that carries information based on
|
||||
// position of tabs
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(crate = "self::serde")]
|
||||
pub struct MainLayout {
|
||||
pub pre_tab: Layout,
|
||||
pub post_tab: Vec<Layout>,
|
||||
pub tabs: Vec<TabLayout>,
|
||||
}
|
||||
|
||||
impl MainLayout {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type LayoutResult = Result<Layout, ConfigError>;
|
||||
|
||||
impl Layout {
|
||||
|
|
@ -168,6 +211,113 @@ 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) -> (Layout, Vec<Layout>, Vec<TabLayout>) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Todo: Convert into actual Error, or use the future logging system.
|
||||
if !part.tabs.is_empty() && !part.parts.is_empty() {
|
||||
panic!("Tabs and Parts need to be specified separately.");
|
||||
}
|
||||
|
||||
// Todo: Convert into actual Error, or use the future logging system.
|
||||
if (!part.tabs.is_empty() || !curr_tabs.is_empty()) && post_tab {
|
||||
panic!("Only one tab section should be specified.");
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
(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_full_layout(&self, tab_layout: Option<TabLayout>) -> Self {
|
||||
if let Some(tab_layout) = tab_layout {
|
||||
let (mut pre_tab_layout, post_tab_layout, _) = self.split_main_and_tab_layout();
|
||||
pre_tab_layout.merge_tab_layout(tab_layout);
|
||||
pre_tab_layout.merge_layout_parts(post_tab_layout);
|
||||
pre_tab_layout
|
||||
} else {
|
||||
let (mut pre_tab_layout, post_tab_layout, _) = self.split_main_and_tab_layout();
|
||||
let default_tab_layout = TabLayout::default();
|
||||
pre_tab_layout.merge_tab_layout(default_tab_layout);
|
||||
pre_tab_layout.merge_layout_parts(post_tab_layout);
|
||||
pre_tab_layout
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct_main_layout(&self) -> MainLayout {
|
||||
let (pre_tab, post_tab, tabs) = self.split_main_and_tab_layout();
|
||||
|
||||
if tabs.is_empty() {
|
||||
panic!("The layout file should have a `tabs` section specified");
|
||||
}
|
||||
|
||||
if tabs.len() > 1 {
|
||||
panic!("The layout file should have one single tab in the `tabs` section specified");
|
||||
}
|
||||
|
||||
MainLayout {
|
||||
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 split_space_to_parts_vertically(
|
||||
|
|
@ -322,3 +472,31 @@ 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 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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
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
|
||||
tabs:
|
||||
- direction: Horizontal
|
||||
split_size:
|
||||
Percent: 24
|
||||
split_size:
|
||||
Percent: 90
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 15
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 15
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 15
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
split_size:
|
||||
Percent: 50
|
||||
- direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 50
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 50
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
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
|
||||
tabs:
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 50
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Percent: 50
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 1
|
||||
run:
|
||||
plugin: tab-bar
|
||||
- direction: Horizontal
|
||||
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"]}
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 2
|
||||
run:
|
||||
plugin: status-bar
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 1
|
||||
run:
|
||||
plugin: tab-bar
|
||||
- direction: Horizontal
|
||||
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
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 2
|
||||
run:
|
||||
plugin: status-bar
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Horizontal
|
||||
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
|
||||
|
|
@ -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')],
|
||||
|
|
|
|||
538
zellij-utils/src/input/unit/layout_test.rs
Normal file
538
zellij-utils/src/input/unit/layout_test.rs
Normal file
|
|
@ -0,0 +1,538 @@
|
|||
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 = Layout::new(&path);
|
||||
assert!(layout.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_layout_has_one_tab() {
|
||||
let path = default_layout_dir("default.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.tabs.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_layout_has_one_pre_tab() {
|
||||
let path = default_layout_dir("default.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.pre_tab.parts.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_layout_has_one_post_tab() {
|
||||
let path = default_layout_dir("default.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.post_tab.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_layout_merged_correctly() {
|
||||
let path = default_layout_dir("default.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.construct_tab_layout(Some(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.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 = Layout::new(&path);
|
||||
assert!(layout.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_disable_status_layout_is_ok() {
|
||||
let path = default_layout_dir("disable-status-bar.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
assert!(layout.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_disable_status_layout_has_one_tab() {
|
||||
let path = default_layout_dir("disable-status-bar.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.tabs.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_disable_status_layout_has_one_pre_tab() {
|
||||
let path = default_layout_dir("disable-status-bar.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
assert!(main_layout.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 = Layout::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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.construct_tab_layout(Some(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.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 = Layout::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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert!(!main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.construct_tab_layout(Some(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.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 = Layout::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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.tabs.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deeply_nested_tab_three_post_tab() {
|
||||
let path = layout_test_dir("deeply-nested-tab-layout.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert_eq!(main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.unwrap().construct_main_layout();
|
||||
assert!(!main_layout.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 = Layout::new(&path);
|
||||
let main_layout = layout.as_ref().unwrap().construct_main_layout();
|
||||
let tab_layout = main_layout.construct_tab_layout(Some(main_layout.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]
|
||||
#[should_panic]
|
||||
// TODO Make error out of this
|
||||
fn no_tabs_specified_should_panic() {
|
||||
let path = layout_test_dir("no-tabs-should-panic.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let _main_layout = layout.unwrap().construct_main_layout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
// TODO Make error out of this
|
||||
// Only untill #631 is fixed
|
||||
fn multiple_tabs_specified_should_panic() {
|
||||
let path = layout_test_dir("multiple-tabs-should-panic.yaml".into());
|
||||
let layout = Layout::new(&path);
|
||||
let _main_layout = layout.unwrap().construct_main_layout();
|
||||
}
|
||||
|
|
@ -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::MainLayout, 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<MainLayout>,
|
||||
),
|
||||
AttachClient(ClientAttributes, bool, Options),
|
||||
Action(Action),
|
||||
ClientExited,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue