From 9131d6362a80a69b39300c007c058c6fd7fe87e8 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 14 Oct 2021 15:30:11 +0200 Subject: [PATCH 01/18] refactor(screen): support multiple mirrored clients --- zellij-server/src/lib.rs | 2 +- zellij-server/src/pty.rs | 84 ++-- zellij-server/src/route.rs | 94 ++-- zellij-server/src/screen.rs | 590 ++++++++++++++----------- zellij-server/src/tab.rs | 35 +- zellij-server/src/unit/screen_tests.rs | 124 +++--- zellij-server/src/wasm_vm.rs | 6 +- 7 files changed, 504 insertions(+), 431 deletions(-) diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 5ba0490978..3422054a45 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -331,7 +331,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { let mode = mode_info.mode; session_data .senders - .send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone())) + .send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone(), client_id)) .unwrap(); session_data .senders diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 9a1f1423cd..80a26c17d8 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -29,13 +29,19 @@ use zellij_utils::{ pub type VteBytes = Vec; +#[derive(Clone, Copy, Debug)] +pub enum ClientOrTabIndex { + ClientId(ClientId), + TabIndex(usize), +} + /// Instructions related to PTYs (pseudoterminals). #[derive(Clone, Debug)] pub(crate) enum PtyInstruction { - SpawnTerminal(Option), - SpawnTerminalVertically(Option), - SpawnTerminalHorizontally(Option), - UpdateActivePane(Option), + SpawnTerminal(Option, ClientOrTabIndex), + SpawnTerminalVertically(Option, ClientId), + SpawnTerminalHorizontally(Option, ClientId), + UpdateActivePane(Option, ClientId), NewTab(Option, Option, ClientId), ClosePane(PaneId), CloseTab(Vec), @@ -45,10 +51,10 @@ pub(crate) enum PtyInstruction { impl From<&PtyInstruction> for PtyContext { fn from(pty_instruction: &PtyInstruction) -> Self { match *pty_instruction { - PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal, - PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically, - PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally, - PtyInstruction::UpdateActivePane(_) => PtyContext::UpdateActivePane, + PtyInstruction::SpawnTerminal(..) => PtyContext::SpawnTerminal, + PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically, + PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally, + PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane, PtyInstruction::ClosePane(_) => PtyContext::ClosePane, PtyInstruction::CloseTab(_) => PtyContext::CloseTab, PtyInstruction::NewTab(..) => PtyContext::NewTab, @@ -58,7 +64,7 @@ impl From<&PtyInstruction> for PtyContext { } pub(crate) struct Pty { - pub active_pane: Option, + pub active_panes: HashMap, pub bus: Bus, pub id_to_child_pid: HashMap, debug_to_file: bool, @@ -72,29 +78,29 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) { let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Pty((&event).into())); match event { - PtyInstruction::SpawnTerminal(terminal_action) => { - let pid = pty.spawn_terminal(terminal_action); + PtyInstruction::SpawnTerminal(terminal_action, client_or_tab_index) => { + let pid = pty.spawn_terminal(terminal_action, client_or_tab_index); pty.bus .senders - .send_to_screen(ScreenInstruction::NewPane(PaneId::Terminal(pid))) + .send_to_screen(ScreenInstruction::NewPane(PaneId::Terminal(pid), client_or_tab_index)) .unwrap(); } - PtyInstruction::SpawnTerminalVertically(terminal_action) => { - let pid = pty.spawn_terminal(terminal_action); + PtyInstruction::SpawnTerminalVertically(terminal_action, client_id) => { + let pid = pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)); pty.bus .senders - .send_to_screen(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid))) + .send_to_screen(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid), client_id)) .unwrap(); } - PtyInstruction::SpawnTerminalHorizontally(terminal_action) => { - let pid = pty.spawn_terminal(terminal_action); + PtyInstruction::SpawnTerminalHorizontally(terminal_action, client_id) => { + let pid = pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)); pty.bus .senders - .send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid))) + .send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid), client_id)) .unwrap(); } - PtyInstruction::UpdateActivePane(pane_id) => { - pty.set_active_pane(pane_id); + PtyInstruction::UpdateActivePane(pane_id, client_id) => { + pty.set_active_pane(pane_id, client_id); } PtyInstruction::NewTab(terminal_action, tab_layout, client_id) => { let tab_name = tab_layout.as_ref().and_then(|layout| { @@ -115,11 +121,11 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) { // clear current name at first pty.bus .senders - .send_to_screen(ScreenInstruction::UpdateTabName(vec![0])) + .send_to_screen(ScreenInstruction::UpdateTabName(vec![0], client_id)) .unwrap(); pty.bus .senders - .send_to_screen(ScreenInstruction::UpdateTabName(tab_name.into_bytes())) + .send_to_screen(ScreenInstruction::UpdateTabName(tab_name.into_bytes(), client_id)) .unwrap(); } } @@ -229,10 +235,10 @@ fn stream_terminal_bytes( } async_send_to_screen(senders.clone(), ScreenInstruction::Render).await; - // this is a little hacky, and is because the tests end the file as soon as - // we read everything, rather than hanging until there is new data - // a better solution would be to fix the test fakes, but this will do for now - async_send_to_screen(senders, ScreenInstruction::ClosePane(PaneId::Terminal(pid))) + // we send ClosePane here so that the screen knows to close this tab if the process + // inside the terminal exited on its own (eg. the user typed "exit" inside a + // bash shell) + async_send_to_screen(senders, ScreenInstruction::ClosePane(PaneId::Terminal(pid), None)) .await; } }) @@ -241,19 +247,22 @@ fn stream_terminal_bytes( impl Pty { pub fn new(bus: Bus, debug_to_file: bool) -> Self { Pty { - active_pane: None, + active_panes: HashMap::new(), bus, id_to_child_pid: HashMap::new(), debug_to_file, task_handles: HashMap::new(), } } - pub fn get_default_terminal(&self) -> TerminalAction { + pub fn get_default_terminal(&self, client_id: Option) -> TerminalAction { TerminalAction::RunCommand(RunCommand { args: vec![], command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")), - cwd: self - .active_pane + cwd: client_id + .and_then(|client_id| self + .active_panes + .get(&client_id) + ) .and_then(|pane| match pane { PaneId::Plugin(..) => None, PaneId::Terminal(id) => self.id_to_child_pid.get(&id).and_then(|id| id.shell), @@ -262,8 +271,11 @@ impl Pty { .flatten(), }) } - pub fn spawn_terminal(&mut self, terminal_action: Option) -> RawFd { - let terminal_action = terminal_action.unwrap_or_else(|| self.get_default_terminal()); + pub fn spawn_terminal(&mut self, terminal_action: Option, client_or_tab_index: ClientOrTabIndex) -> RawFd { + let terminal_action = match client_or_tab_index { + ClientOrTabIndex::ClientId(client_id) => terminal_action.unwrap_or_else(|| self.get_default_terminal(Some(client_id))), + ClientOrTabIndex::TabIndex(_) => terminal_action.unwrap_or_else(|| self.get_default_terminal(None)), + }; let (pid_primary, child_id): (RawFd, ChildId) = self .bus .os_input @@ -286,7 +298,7 @@ impl Pty { default_shell: Option, client_id: ClientId, ) { - let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal()); + let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(Some(client_id))); let extracted_run_instructions = layout.extract_run_instructions(); let mut new_pane_pids = vec![]; for run_instruction in extracted_run_instructions { @@ -368,8 +380,10 @@ impl Pty { self.close_pane(id); }); } - pub fn set_active_pane(&mut self, pane_id: Option) { - self.active_pane = pane_id; + pub fn set_active_pane(&mut self, pane_id: Option, client_id: ClientId) { + if let Some(pane_id) = pane_id { + self.active_panes.insert(client_id, pane_id); + } } } diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 4a22b5c5c8..8edd851a00 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock}; use zellij_utils::zellij_tile::data::Event; use crate::{ - os_input_output::ServerOsApi, pty::PtyInstruction, screen::ScreenInstruction, + os_input_output::ServerOsApi, pty::{PtyInstruction, ClientOrTabIndex}, screen::ScreenInstruction, wasm_vm::PluginInstruction, ServerInstruction, SessionMetaData, SessionState, }; use zellij_utils::{ @@ -34,17 +34,17 @@ fn route_action( Action::ToggleTab => { session .senders - .send_to_screen(ScreenInstruction::ToggleTab) + .send_to_screen(ScreenInstruction::ToggleTab(client_id)) .unwrap(); } Action::Write(val) => { session .senders - .send_to_screen(ScreenInstruction::ClearScroll) + .send_to_screen(ScreenInstruction::ClearScroll(client_id)) .unwrap(); session .senders - .send_to_screen(ScreenInstruction::WriteCharacter(val)) + .send_to_screen(ScreenInstruction::WriteCharacter(val, client_id)) .unwrap(); } Action::SwitchToMode(mode) => { @@ -65,7 +65,7 @@ fn route_action( mode, palette, session.capabilities, - ))) + ), client_id)) .unwrap(); session .senders @@ -74,44 +74,44 @@ fn route_action( } Action::Resize(direction) => { let screen_instr = match direction { - Direction::Left => ScreenInstruction::ResizeLeft, - Direction::Right => ScreenInstruction::ResizeRight, - Direction::Up => ScreenInstruction::ResizeUp, - Direction::Down => ScreenInstruction::ResizeDown, + Direction::Left => ScreenInstruction::ResizeLeft(client_id), + Direction::Right => ScreenInstruction::ResizeRight(client_id), + Direction::Up => ScreenInstruction::ResizeUp(client_id), + Direction::Down => ScreenInstruction::ResizeDown(client_id), }; session.senders.send_to_screen(screen_instr).unwrap(); } Action::SwitchFocus => { session .senders - .send_to_screen(ScreenInstruction::SwitchFocus) + .send_to_screen(ScreenInstruction::SwitchFocus(client_id)) .unwrap(); } Action::FocusNextPane => { session .senders - .send_to_screen(ScreenInstruction::FocusNextPane) + .send_to_screen(ScreenInstruction::FocusNextPane(client_id)) .unwrap(); } Action::FocusPreviousPane => { session .senders - .send_to_screen(ScreenInstruction::FocusPreviousPane) + .send_to_screen(ScreenInstruction::FocusPreviousPane(client_id)) .unwrap(); } Action::MoveFocus(direction) => { let screen_instr = match direction { - Direction::Left => ScreenInstruction::MoveFocusLeft, - Direction::Right => ScreenInstruction::MoveFocusRight, - Direction::Up => ScreenInstruction::MoveFocusUp, - Direction::Down => ScreenInstruction::MoveFocusDown, + Direction::Left => ScreenInstruction::MoveFocusLeft(client_id), + Direction::Right => ScreenInstruction::MoveFocusRight(client_id), + Direction::Up => ScreenInstruction::MoveFocusUp(client_id), + Direction::Down => ScreenInstruction::MoveFocusDown(client_id), }; session.senders.send_to_screen(screen_instr).unwrap(); } Action::MoveFocusOrTab(direction) => { let screen_instr = match direction { - Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab, - Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab, + Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id), + Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab(client_id), _ => unreachable!(), }; session.senders.send_to_screen(screen_instr).unwrap(); @@ -119,49 +119,49 @@ fn route_action( Action::ScrollUp => { session .senders - .send_to_screen(ScreenInstruction::ScrollUp) + .send_to_screen(ScreenInstruction::ScrollUp(client_id)) .unwrap(); } Action::ScrollUpAt(point) => { session .senders - .send_to_screen(ScreenInstruction::ScrollUpAt(point)) + .send_to_screen(ScreenInstruction::ScrollUpAt(point, client_id)) .unwrap(); } Action::ScrollDown => { session .senders - .send_to_screen(ScreenInstruction::ScrollDown) + .send_to_screen(ScreenInstruction::ScrollDown(client_id)) .unwrap(); } Action::ScrollDownAt(point) => { session .senders - .send_to_screen(ScreenInstruction::ScrollDownAt(point)) + .send_to_screen(ScreenInstruction::ScrollDownAt(point, client_id)) .unwrap(); } Action::ScrollToBottom => { session .senders - .send_to_screen(ScreenInstruction::ScrollToBottom) + .send_to_screen(ScreenInstruction::ScrollToBottom(client_id)) .unwrap(); } Action::PageScrollUp => { session .senders - .send_to_screen(ScreenInstruction::PageScrollUp) + .send_to_screen(ScreenInstruction::PageScrollUp(client_id)) .unwrap(); } Action::PageScrollDown => { session .senders - .send_to_screen(ScreenInstruction::PageScrollDown) + .send_to_screen(ScreenInstruction::PageScrollDown(client_id)) .unwrap(); } Action::ToggleFocusFullscreen => { session .senders - .send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen) + .send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen(client_id)) .unwrap(); } Action::TogglePaneFrames => { @@ -173,31 +173,31 @@ fn route_action( Action::NewPane(direction) => { let shell = session.default_shell.clone(); let pty_instr = match direction { - Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell), - Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell), - Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell), - Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(shell), + Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell, client_id), + Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell, client_id), + Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell, client_id), + Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(shell, client_id), // No direction specified - try to put it in the biggest available spot - None => PtyInstruction::SpawnTerminal(shell), + None => PtyInstruction::SpawnTerminal(shell, ClientOrTabIndex::ClientId(client_id)), }; session.senders.send_to_pty(pty_instr).unwrap(); } Action::Run(command) => { let run_cmd = Some(TerminalAction::RunCommand(command.clone().into())); let pty_instr = match command.direction { - Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(run_cmd), - Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(run_cmd), - Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(run_cmd), - Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(run_cmd), + Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(run_cmd, client_id), + Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(run_cmd, client_id), + Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id), + Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id), // No direction specified - try to put it in the biggest available spot - None => PtyInstruction::SpawnTerminal(run_cmd), + None => PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id)), }; session.senders.send_to_pty(pty_instr).unwrap(); } Action::CloseFocus => { session .senders - .send_to_screen(ScreenInstruction::CloseFocusedPane) + .send_to_screen(ScreenInstruction::CloseFocusedPane(client_id)) .unwrap(); } Action::NewTab(tab_layout) => { @@ -210,37 +210,37 @@ fn route_action( Action::GoToNextTab => { session .senders - .send_to_screen(ScreenInstruction::SwitchTabNext) + .send_to_screen(ScreenInstruction::SwitchTabNext(client_id)) .unwrap(); } Action::GoToPreviousTab => { session .senders - .send_to_screen(ScreenInstruction::SwitchTabPrev) + .send_to_screen(ScreenInstruction::SwitchTabPrev(client_id)) .unwrap(); } Action::ToggleActiveSyncTab => { session .senders - .send_to_screen(ScreenInstruction::ToggleActiveSyncTab) + .send_to_screen(ScreenInstruction::ToggleActiveSyncTab(client_id)) .unwrap(); } Action::CloseTab => { session .senders - .send_to_screen(ScreenInstruction::CloseTab) + .send_to_screen(ScreenInstruction::CloseTab(client_id)) .unwrap(); } Action::GoToTab(i) => { session .senders - .send_to_screen(ScreenInstruction::GoToTab(i)) + .send_to_screen(ScreenInstruction::GoToTab(i, client_id)) .unwrap(); } Action::TabNameInput(c) => { session .senders - .send_to_screen(ScreenInstruction::UpdateTabName(c)) + .send_to_screen(ScreenInstruction::UpdateTabName(c, client_id)) .unwrap(); } Action::Quit => { @@ -258,25 +258,25 @@ fn route_action( Action::LeftClick(point) => { session .senders - .send_to_screen(ScreenInstruction::LeftClick(point)) + .send_to_screen(ScreenInstruction::LeftClick(point, client_id)) .unwrap(); } Action::MouseRelease(point) => { session .senders - .send_to_screen(ScreenInstruction::MouseRelease(point)) + .send_to_screen(ScreenInstruction::MouseRelease(point, client_id)) .unwrap(); } Action::MouseHold(point) => { session .senders - .send_to_screen(ScreenInstruction::MouseHold(point)) + .send_to_screen(ScreenInstruction::MouseHold(point, client_id)) .unwrap(); } Action::Copy => { session .senders - .send_to_screen(ScreenInstruction::Copy) + .send_to_screen(ScreenInstruction::Copy(client_id)) .unwrap(); } Action::NoOp => {} diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 1c344f5481..7f85eec891 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -1,6 +1,6 @@ //! Things related to [`Screen`]s. -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::os::unix::io::RawFd; use std::str; @@ -9,8 +9,8 @@ use zellij_utils::{input::layout::Layout, position::Position, zellij_tile}; use crate::{ panes::PaneId, - pty::{PtyInstruction, VteBytes}, - tab::Tab, + pty::{PtyInstruction, VteBytes, ClientOrTabIndex}, + tab::{Tab, Output}, thread_bus::Bus, wasm_vm::PluginInstruction, ClientId, ServerInstruction, @@ -27,51 +27,51 @@ use zellij_utils::{ pub(crate) enum ScreenInstruction { PtyBytes(RawFd, VteBytes), Render, - NewPane(PaneId), - HorizontalSplit(PaneId), - VerticalSplit(PaneId), - WriteCharacter(Vec), - ResizeLeft, - ResizeRight, - ResizeDown, - ResizeUp, - SwitchFocus, - FocusNextPane, - FocusPreviousPane, - MoveFocusLeft, - MoveFocusLeftOrPreviousTab, - MoveFocusDown, - MoveFocusUp, - MoveFocusRight, - MoveFocusRightOrNextTab, + NewPane(PaneId, ClientOrTabIndex), + HorizontalSplit(PaneId, ClientId), + VerticalSplit(PaneId, ClientId), + WriteCharacter(Vec, ClientId), + ResizeLeft(ClientId), + ResizeRight(ClientId), + ResizeDown(ClientId), + ResizeUp(ClientId), + SwitchFocus(ClientId), + FocusNextPane(ClientId), + FocusPreviousPane(ClientId), + MoveFocusLeft(ClientId), + MoveFocusLeftOrPreviousTab(ClientId), + MoveFocusDown(ClientId), + MoveFocusUp(ClientId), + MoveFocusRight(ClientId), + MoveFocusRightOrNextTab(ClientId), Exit, - ScrollUp, - ScrollUpAt(Position), - ScrollDown, - ScrollDownAt(Position), - ScrollToBottom, - PageScrollUp, - PageScrollDown, - ClearScroll, - CloseFocusedPane, - ToggleActiveTerminalFullscreen, + ScrollUp(ClientId), + ScrollUpAt(Position, ClientId), + ScrollDown(ClientId), + ScrollDownAt(Position, ClientId), + ScrollToBottom(ClientId), + PageScrollUp(ClientId), + PageScrollDown(ClientId), + ClearScroll(ClientId), + CloseFocusedPane(ClientId), + ToggleActiveTerminalFullscreen(ClientId), TogglePaneFrames, SetSelectable(PaneId, bool, usize), - ClosePane(PaneId), + ClosePane(PaneId, Option), NewTab(Layout, Vec, ClientId), - SwitchTabNext, - SwitchTabPrev, - ToggleActiveSyncTab, - CloseTab, - GoToTab(u32), - ToggleTab, - UpdateTabName(Vec), + SwitchTabNext(ClientId), + SwitchTabPrev(ClientId), + ToggleActiveSyncTab(ClientId), + CloseTab(ClientId), + GoToTab(u32, ClientId), + ToggleTab(ClientId), + UpdateTabName(Vec, ClientId), TerminalResize(Size), - ChangeMode(ModeInfo), - LeftClick(Position), - MouseRelease(Position), - MouseHold(Position), - Copy, + ChangeMode(ModeInfo, ClientId), + LeftClick(Position, ClientId), + MouseRelease(Position, ClientId), + MouseHold(Position, ClientId), + Copy(ClientId), AddClient(ClientId), RemoveClient(ClientId), } @@ -81,55 +81,55 @@ impl From<&ScreenInstruction> for ScreenContext { match *screen_instruction { ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes, ScreenInstruction::Render => ScreenContext::Render, - ScreenInstruction::NewPane(_) => ScreenContext::NewPane, - ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit, - ScreenInstruction::VerticalSplit(_) => ScreenContext::VerticalSplit, - ScreenInstruction::WriteCharacter(_) => ScreenContext::WriteCharacter, - ScreenInstruction::ResizeLeft => ScreenContext::ResizeLeft, - ScreenInstruction::ResizeRight => ScreenContext::ResizeRight, - ScreenInstruction::ResizeDown => ScreenContext::ResizeDown, - ScreenInstruction::ResizeUp => ScreenContext::ResizeUp, - ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus, - ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane, - ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane, - ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft, - ScreenInstruction::MoveFocusLeftOrPreviousTab => { + ScreenInstruction::NewPane(..) => ScreenContext::NewPane, + ScreenInstruction::HorizontalSplit(..) => ScreenContext::HorizontalSplit, + ScreenInstruction::VerticalSplit(..) => ScreenContext::VerticalSplit, + ScreenInstruction::WriteCharacter(..) => ScreenContext::WriteCharacter, + ScreenInstruction::ResizeLeft(..) => ScreenContext::ResizeLeft, + ScreenInstruction::ResizeRight(..) => ScreenContext::ResizeRight, + ScreenInstruction::ResizeDown(..) => ScreenContext::ResizeDown, + ScreenInstruction::ResizeUp(..) => ScreenContext::ResizeUp, + ScreenInstruction::SwitchFocus(..) => ScreenContext::SwitchFocus, + ScreenInstruction::FocusNextPane(..) => ScreenContext::FocusNextPane, + ScreenInstruction::FocusPreviousPane(..) => ScreenContext::FocusPreviousPane, + ScreenInstruction::MoveFocusLeft(..) => ScreenContext::MoveFocusLeft, + ScreenInstruction::MoveFocusLeftOrPreviousTab(..) => { ScreenContext::MoveFocusLeftOrPreviousTab } - ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown, - ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp, - ScreenInstruction::MoveFocusRight => ScreenContext::MoveFocusRight, - ScreenInstruction::MoveFocusRightOrNextTab => ScreenContext::MoveFocusRightOrNextTab, + ScreenInstruction::MoveFocusDown(..) => ScreenContext::MoveFocusDown, + ScreenInstruction::MoveFocusUp(..) => ScreenContext::MoveFocusUp, + ScreenInstruction::MoveFocusRight(..) => ScreenContext::MoveFocusRight, + ScreenInstruction::MoveFocusRightOrNextTab(..) => ScreenContext::MoveFocusRightOrNextTab, ScreenInstruction::Exit => ScreenContext::Exit, - ScreenInstruction::ScrollUp => ScreenContext::ScrollUp, - ScreenInstruction::ScrollDown => ScreenContext::ScrollDown, - ScreenInstruction::ScrollToBottom => ScreenContext::ScrollToBottom, - ScreenInstruction::PageScrollUp => ScreenContext::PageScrollUp, - ScreenInstruction::PageScrollDown => ScreenContext::PageScrollDown, - ScreenInstruction::ClearScroll => ScreenContext::ClearScroll, - ScreenInstruction::CloseFocusedPane => ScreenContext::CloseFocusedPane, - ScreenInstruction::ToggleActiveTerminalFullscreen => { + ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp, + ScreenInstruction::ScrollDown(..) => ScreenContext::ScrollDown, + ScreenInstruction::ScrollToBottom(..) => ScreenContext::ScrollToBottom, + ScreenInstruction::PageScrollUp(..) => ScreenContext::PageScrollUp, + ScreenInstruction::PageScrollDown(..) => ScreenContext::PageScrollDown, + ScreenInstruction::ClearScroll(..) => ScreenContext::ClearScroll, + ScreenInstruction::CloseFocusedPane(..) => ScreenContext::CloseFocusedPane, + ScreenInstruction::ToggleActiveTerminalFullscreen(..) => { ScreenContext::ToggleActiveTerminalFullscreen } ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames, ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable, - ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane, + ScreenInstruction::ClosePane(..) => ScreenContext::ClosePane, ScreenInstruction::NewTab(..) => ScreenContext::NewTab, - ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext, - ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev, - ScreenInstruction::CloseTab => ScreenContext::CloseTab, - ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab, - ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName, - ScreenInstruction::TerminalResize(_) => ScreenContext::TerminalResize, - ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode, - ScreenInstruction::ToggleActiveSyncTab => ScreenContext::ToggleActiveSyncTab, - ScreenInstruction::ScrollUpAt(_) => ScreenContext::ScrollUpAt, - ScreenInstruction::ScrollDownAt(_) => ScreenContext::ScrollDownAt, - ScreenInstruction::LeftClick(_) => ScreenContext::LeftClick, - ScreenInstruction::MouseRelease(_) => ScreenContext::MouseRelease, - ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold, - ScreenInstruction::Copy => ScreenContext::Copy, - ScreenInstruction::ToggleTab => ScreenContext::ToggleTab, + ScreenInstruction::SwitchTabNext(..) => ScreenContext::SwitchTabNext, + ScreenInstruction::SwitchTabPrev(..) => ScreenContext::SwitchTabPrev, + ScreenInstruction::CloseTab(..) => ScreenContext::CloseTab, + ScreenInstruction::GoToTab(..) => ScreenContext::GoToTab, + ScreenInstruction::UpdateTabName(..) => ScreenContext::UpdateTabName, + ScreenInstruction::TerminalResize(..) => ScreenContext::TerminalResize, + ScreenInstruction::ChangeMode(..) => ScreenContext::ChangeMode, + ScreenInstruction::ToggleActiveSyncTab(..) => ScreenContext::ToggleActiveSyncTab, + ScreenInstruction::ScrollUpAt(..) => ScreenContext::ScrollUpAt, + ScreenInstruction::ScrollDownAt(..) => ScreenContext::ScrollDownAt, + ScreenInstruction::LeftClick(..) => ScreenContext::LeftClick, + ScreenInstruction::MouseRelease(..) => ScreenContext::MouseRelease, + ScreenInstruction::MouseHold(..) => ScreenContext::MouseHold, + ScreenInstruction::Copy(..) => ScreenContext::Copy, + ScreenInstruction::ToggleTab(..) => ScreenContext::ToggleTab, ScreenInstruction::AddClient(..) => ScreenContext::AddClient, ScreenInstruction::RemoveClient(..) => ScreenContext::RemoveClient, } @@ -147,9 +147,9 @@ pub(crate) struct Screen { tabs: BTreeMap, /// The full size of this [`Screen`]. size: Size, - /// The index of this [`Screen`]'s active [`Tab`]. - active_tab_index: Option, - tab_history: Vec>, + /// The indices of this [`Screen`]'s active [`Tab`]s. + active_tab_indices: BTreeMap, + tab_history: BTreeMap>, mode_info: ModeInfo, colors: Palette, draw_pane_frames: bool, @@ -169,9 +169,9 @@ impl Screen { max_panes, size: client_attributes.size, colors: client_attributes.palette, - active_tab_index: None, + active_tab_indices: BTreeMap::new(), tabs: BTreeMap::new(), - tab_history: Vec::with_capacity(32), + tab_history: BTreeMap::new(), mode_info, draw_pane_frames, } @@ -188,6 +188,20 @@ impl Screen { } } + fn move_clients_from_closed_tab(&mut self, previous_tab_index: usize) { + let client_ids_in_closed_tab: Vec = self.active_tab_indices + .iter() + .filter(|(_c_id, t_index)| **t_index == previous_tab_index) + .map(|(c_id, _t_index)| c_id) + .copied() + .collect(); + for client_id in client_ids_in_closed_tab { + let client_previous_tab = self.tab_history.get_mut(&client_id).unwrap().pop().unwrap(); + self.active_tab_indices.insert(client_id, client_previous_tab); + self.tabs.get_mut(&client_previous_tab).unwrap().add_client(client_id); + } + + } fn move_clients(&mut self, source_index: usize, destination_index: usize) { let connected_clients_in_source_tab = { let source_tab = self.tabs.get_mut(&source_index).unwrap(); @@ -197,9 +211,9 @@ impl Screen { destination_tab.add_multiple_clients(&connected_clients_in_source_tab); } /// A helper function to switch to a new tab at specified position. - fn switch_active_tab(&mut self, new_tab_pos: usize) { + fn switch_active_tab(&mut self, new_tab_pos: usize, client_id: ClientId) { if let Some(new_tab) = self.tabs.values().find(|t| t.position == new_tab_pos) { - let current_tab = self.get_active_tab().unwrap(); + let current_tab = self.get_active_tab(client_id).unwrap(); // If new active tab is same as the current one, do nothing. if current_tab.position == new_tab_pos { @@ -213,15 +227,15 @@ impl Screen { new_tab.set_force_render(); new_tab.visible(true); - let old_active_index = self.active_tab_index.replace(new_tab_index); - if let Some(ref i) = old_active_index { - if let Some(t) = self.tabs.get_mut(i) { - t.is_active = false; - } + // currently all clients are just mirrors, so we perform the action for every entry in + // tab_history + // TODO: receive a client_id and only do it for the client + for (client_id, tab_history) in self.tab_history.iter_mut() { + let old_active_index = self.active_tab_indices.remove(client_id).unwrap(); + self.active_tab_indices.insert(*client_id, new_tab_index); + tab_history.retain(|&e| e != new_tab_pos); + tab_history.push(old_active_index); } - self.tabs.get_mut(&new_tab_index).unwrap().is_active = true; - self.tab_history.retain(|&e| e != Some(new_tab_pos)); - self.tab_history.push(old_active_index); self.move_clients(current_tab_index, new_tab_index); @@ -231,35 +245,32 @@ impl Screen { } /// Sets this [`Screen`]'s active [`Tab`] to the next tab. - pub fn switch_tab_next(&mut self) { - let active_tab_pos = self.get_active_tab().unwrap().position; + pub fn switch_tab_next(&mut self, client_id: ClientId) { + let active_tab_pos = self.get_active_tab(client_id).unwrap().position; let new_tab_pos = (active_tab_pos + 1) % self.tabs.len(); - self.switch_active_tab(new_tab_pos); + self.switch_active_tab(new_tab_pos, client_id); } /// Sets this [`Screen`]'s active [`Tab`] to the previous tab. - pub fn switch_tab_prev(&mut self) { - let active_tab_pos = self.get_active_tab().unwrap().position; + pub fn switch_tab_prev(&mut self, client_id: ClientId) { + let active_tab_pos = self.get_active_tab(client_id).unwrap().position; let new_tab_pos = if active_tab_pos == 0 { self.tabs.len() - 1 } else { active_tab_pos - 1 }; - self.switch_active_tab(new_tab_pos); + self.switch_active_tab(new_tab_pos, client_id); } - pub fn go_to_tab(&mut self, tab_index: usize) { - self.switch_active_tab(tab_index - 1); + pub fn go_to_tab(&mut self, tab_index: usize, client_id: ClientId) { + self.switch_active_tab(tab_index - 1, client_id); } - /// Closes this [`Screen`]'s active [`Tab`], exiting the application if it happens - /// to be the last tab. - pub fn close_tab(&mut self) { - let active_tab_index = self.active_tab_index.unwrap(); - let active_tab = self.tabs.remove(&active_tab_index).unwrap(); - let pane_ids = active_tab.get_pane_ids(); + fn close_tab_at_index(&mut self, tab_index: usize) { + let tab_to_close = self.tabs.remove(&tab_index).unwrap(); + let pane_ids = tab_to_close.get_pane_ids(); // below we don't check the result of sending the CloseTab instruction to the pty thread // because this might be happening when the app is closing, at which point the pty thread // has already closed and this would result in an error @@ -268,23 +279,20 @@ impl Screen { .send_to_pty(PtyInstruction::CloseTab(pane_ids)) .unwrap(); if self.tabs.is_empty() { - self.active_tab_index = None; + self.active_tab_indices.clear(); self.bus .senders .send_to_server(ServerInstruction::Render(None)) .unwrap(); } else { - if let Some(tab) = self.get_active_tab() { - tab.visible(false); - } - self.active_tab_index = self.tab_history.pop().unwrap(); + self.move_clients_from_closed_tab(tab_index); + let visible_tab_indices: HashSet = self.active_tab_indices.values().copied().collect(); for t in self.tabs.values_mut() { - if t.index == self.active_tab_index.unwrap() { + if visible_tab_indices.contains(&t.index) { t.set_force_render(); - t.is_active = true; t.visible(true); } - if t.position > active_tab.position { + if t.position > tab_to_close.position { t.position -= 1; } } @@ -293,29 +301,39 @@ impl Screen { } } + // Closes the client_id's focused tab + pub fn close_tab(&mut self, client_id: ClientId) { + let active_tab_index = *self.active_tab_indices.get(&client_id).unwrap(); + self.close_tab_at_index(active_tab_index); + } + pub fn resize_to_screen(&mut self, new_screen_size: Size) { self.size = new_screen_size; for (_, tab) in self.tabs.iter_mut() { tab.resize_whole_tab(new_screen_size); + tab.set_force_render(); } - let _ = self.get_active_tab_mut().map(|t| t.set_force_render()); self.render(); } /// Renders this [`Screen`], which amounts to rendering its active [`Tab`]. pub fn render(&mut self) { - if let Some(active_tab) = self.get_active_tab_mut() { - if active_tab.get_active_pane().is_some() { - if let Some(output) = active_tab.render() { - self.bus - .senders - .send_to_server(ServerInstruction::Render(Some(output))) - .unwrap(); - } + let mut output = Output::new(); + let mut tabs_to_close = vec![]; + for (tab_index, tab) in self.tabs.iter_mut() { + if tab.get_active_pane().is_some() { + tab.render(&mut output); } else { - self.close_tab(); + tabs_to_close.push(*tab_index); } - }; + } + for tab_index in tabs_to_close { + self.close_tab_at_index(tab_index); + } + self.bus + .senders + .send_to_server(ServerInstruction::Render(Some(output))) + .unwrap(); } /// Returns a mutable reference to this [`Screen`]'s tabs. @@ -324,8 +342,8 @@ impl Screen { } /// Returns an immutable reference to this [`Screen`]'s active [`Tab`]. - pub fn get_active_tab(&self) -> Option<&Tab> { - match self.active_tab_index { + pub fn get_active_tab(&self, client_id: ClientId) -> Option<&Tab> { + match self.active_tab_indices.get(&client_id) { Some(tab) => self.tabs.get(&tab), None => None, } @@ -333,19 +351,17 @@ impl Screen { /// Returns an immutable reference to this [`Screen`]'s previous active [`Tab`]. /// Consumes the last entry in tab history. - pub fn get_previous_tab(&mut self) -> Option<&Tab> { - let last = self.tab_history.pop(); - last?; - match last.unwrap() { + pub fn get_previous_tab(&mut self, client_id: ClientId) -> Option<&Tab> { + match self.tab_history.get_mut(&client_id).unwrap().pop() { Some(tab) => self.tabs.get(&tab), None => None, } } /// Returns a mutable reference to this [`Screen`]'s active [`Tab`]. - pub fn get_active_tab_mut(&mut self) -> Option<&mut Tab> { - match self.active_tab_index { - Some(tab) => self.get_tabs_mut().get_mut(&tab), + pub fn get_active_tab_mut(&mut self, client_id: ClientId) -> Option<&mut Tab> { + match self.active_tab_indices.get(&client_id) { + Some(tab) => self.tabs.get_mut(&tab), None => None, } } @@ -374,50 +390,78 @@ impl Screen { client_id, ); tab.apply_layout(layout, new_pids, tab_index); - if let Some(active_tab) = self.get_active_tab_mut() { + if let Some(active_tab) = self.get_active_tab_mut(client_id) { active_tab.visible(false); - active_tab.is_active = false; let connected_clients = active_tab.drain_connected_clients(); tab.add_multiple_clients(&connected_clients); } - self.tab_history - .push(self.active_tab_index.replace(tab_index)); + for (client_id, tab_history) in self.tab_history.iter_mut() { + let old_active_index = self.active_tab_indices.remove(client_id).unwrap(); + self.active_tab_indices.insert(*client_id, tab_index); + tab_history.retain(|&e| e != tab_index); + tab_history.push(old_active_index); + } tab.visible(true); self.tabs.insert(tab_index, tab); + if !self.active_tab_indices.contains_key(&client_id) { + self.add_client(client_id); + } self.update_tabs(); self.render(); } pub fn add_client(&mut self, client_id: ClientId) { - self.get_active_tab_mut().unwrap().add_client(client_id); + let mut tab_index = 0; + let mut tab_history = vec![]; + if let Some((_first_client, first_active_tab_index)) = self.active_tab_indices.iter().next() { + tab_index = *first_active_tab_index; + } + if let Some((_first_client, first_tab_history)) = self.tab_history.iter().next() { + tab_history = first_tab_history.clone(); + } + self.active_tab_indices.insert(client_id, tab_index); + self.tab_history.insert(client_id, tab_history); + self.tabs.get_mut(&tab_index).unwrap().add_client(client_id); } pub fn remove_client(&mut self, client_id: ClientId) { - self.get_active_tab_mut().unwrap().remove_client(client_id); + if let Some(client_tab) = self.get_active_tab_mut(client_id) { + client_tab.remove_client(client_id); + } + if self.active_tab_indices.contains_key(&client_id) { + self.active_tab_indices.remove(&client_id); + } + if self.tab_history.contains_key(&client_id) { + self.tab_history.remove(&client_id); + } } pub fn update_tabs(&self) { let mut tab_data = vec![]; - let active_tab_index = self.active_tab_index.unwrap(); - for tab in self.tabs.values() { - tab_data.push(TabInfo { - position: tab.position, - name: tab.name.clone(), - active: active_tab_index == tab.index, - panes_to_hide: tab.panes_to_hide.len(), - is_fullscreen_active: tab.is_fullscreen_active(), - is_sync_panes_active: tab.is_sync_panes_active(), - }); + // TODO: right now all clients are synced, so we just take the first active_tab which is + // the same for everyone - when this is no longer the case, we need to update the TabInfo + // to account for this (or send multiple TabInfos) + if let Some((_first_client, first_active_tab_index)) = self.active_tab_indices.iter().next() { + for tab in self.tabs.values() { + tab_data.push(TabInfo { + position: tab.position, + name: tab.name.clone(), + active: *first_active_tab_index == tab.index, + panes_to_hide: tab.panes_to_hide.len(), + is_fullscreen_active: tab.is_fullscreen_active(), + is_sync_panes_active: tab.is_sync_panes_active(), + }); + } + self.bus + .senders + .send_to_plugin(PluginInstruction::Update(None, Event::TabUpdate(tab_data))) + .unwrap(); } - self.bus - .senders - .send_to_plugin(PluginInstruction::Update(None, Event::TabUpdate(tab_data))) - .unwrap(); } - pub fn update_active_tab_name(&mut self, buf: Vec) { + pub fn update_active_tab_name(&mut self, buf: Vec, client_id: ClientId) { let s = str::from_utf8(&buf).unwrap(); - let active_tab = self.get_active_tab_mut().unwrap(); + let active_tab = self.get_active_tab_mut(client_id).unwrap(); match s { "\0" => { active_tab.name = String::new(); @@ -432,11 +476,11 @@ impl Screen { } self.update_tabs(); } - pub fn change_mode(&mut self, mode_info: ModeInfo) { + pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) { if self.mode_info.mode == InputMode::Scroll && (mode_info.mode == InputMode::Normal || mode_info.mode == InputMode::Locked) { - self.get_active_tab_mut() + self.get_active_tab_mut(client_id) .unwrap() .clear_active_terminal_scroll(); } @@ -447,21 +491,21 @@ impl Screen { tab.mark_active_pane_for_rerender(); } } - pub fn move_focus_left_or_previous_tab(&mut self) { - if !self.get_active_tab_mut().unwrap().move_focus_left() { - self.switch_tab_prev(); + pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) { + if !self.get_active_tab_mut(client_id).unwrap().move_focus_left() { + self.switch_tab_prev(client_id); } } - pub fn move_focus_right_or_next_tab(&mut self) { - if !self.get_active_tab_mut().unwrap().move_focus_right() { - self.switch_tab_next(); + pub fn move_focus_right_or_next_tab(&mut self, client_id: ClientId) { + if !self.get_active_tab_mut(client_id).unwrap().move_focus_right() { + self.switch_tab_next(client_id); } } - pub fn toggle_tab(&mut self) { - let tab = self.get_previous_tab(); + pub fn toggle_tab(&mut self, client_id: ClientId) { + let tab = self.get_previous_tab(client_id); if let Some(t) = tab { let position = t.position; - self.go_to_tab(position + 1); + self.go_to_tab(position + 1, client_id); }; self.update_tabs(); @@ -502,28 +546,26 @@ pub(crate) fn screen_thread_main( err_ctx.add_call(ContextType::Screen((&event).into())); match event { ScreenInstruction::PtyBytes(pid, vte_bytes) => { - let active_tab = screen.get_active_tab_mut().unwrap(); - if active_tab.has_terminal_pid(pid) { - // it's most likely that this event is directed at the active tab - // look there first - active_tab.handle_pty_bytes(pid, vte_bytes); - } else { - // if this event wasn't directed at the active tab, start looking - // in other tabs - let all_tabs = screen.get_tabs_mut(); - for tab in all_tabs.values_mut() { - if tab.has_terminal_pid(pid) { - tab.handle_pty_bytes(pid, vte_bytes); - break; - } + let all_tabs = screen.get_tabs_mut(); + for tab in all_tabs.values_mut() { + if tab.has_terminal_pid(pid) { + tab.handle_pty_bytes(pid, vte_bytes); + break; } } } ScreenInstruction::Render => { screen.render(); } - ScreenInstruction::NewPane(pid) => { - screen.get_active_tab_mut().unwrap().new_pane(pid); + ScreenInstruction::NewPane(pid, client_or_tab_index) => { + match client_or_tab_index { + ClientOrTabIndex::ClientId(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().new_pane(pid); + } + ClientOrTabIndex::TabIndex(tab_index) => { + screen.tabs.get_mut(&tab_index).unwrap().new_pane(pid); + } + }; screen .bus .senders @@ -533,8 +575,8 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::HorizontalSplit(pid) => { - screen.get_active_tab_mut().unwrap().horizontal_split(pid); + ScreenInstruction::HorizontalSplit(pid, client_id) => { + screen.get_active_tab_mut(client_id).unwrap().horizontal_split(pid); screen .bus .senders @@ -544,8 +586,8 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::VerticalSplit(pid) => { - screen.get_active_tab_mut().unwrap().vertical_split(pid); + ScreenInstruction::VerticalSplit(pid, client_id) => { + screen.get_active_tab_mut(client_id).unwrap().vertical_split(pid); screen .bus .senders @@ -555,55 +597,55 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::WriteCharacter(bytes) => { - let active_tab = screen.get_active_tab_mut().unwrap(); + ScreenInstruction::WriteCharacter(bytes, client_id) => { + let active_tab = screen.get_active_tab_mut(client_id).unwrap(); match active_tab.is_sync_panes_active() { true => active_tab.write_to_terminals_on_current_tab(bytes), false => active_tab.write_to_active_terminal(bytes), } } - ScreenInstruction::ResizeLeft => { - screen.get_active_tab_mut().unwrap().resize_left(); + ScreenInstruction::ResizeLeft(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().resize_left(); screen.render(); } - ScreenInstruction::ResizeRight => { - screen.get_active_tab_mut().unwrap().resize_right(); + ScreenInstruction::ResizeRight(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().resize_right(); screen.render(); } - ScreenInstruction::ResizeDown => { - screen.get_active_tab_mut().unwrap().resize_down(); + ScreenInstruction::ResizeDown(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().resize_down(); screen.render(); } - ScreenInstruction::ResizeUp => { - screen.get_active_tab_mut().unwrap().resize_up(); + ScreenInstruction::ResizeUp(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().resize_up(); screen.render(); } - ScreenInstruction::SwitchFocus => { - screen.get_active_tab_mut().unwrap().move_focus(); + ScreenInstruction::SwitchFocus(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().move_focus(); screen.render(); } - ScreenInstruction::FocusNextPane => { - screen.get_active_tab_mut().unwrap().focus_next_pane(); + ScreenInstruction::FocusNextPane(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().focus_next_pane(); screen.render(); } - ScreenInstruction::FocusPreviousPane => { - screen.get_active_tab_mut().unwrap().focus_previous_pane(); + ScreenInstruction::FocusPreviousPane(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().focus_previous_pane(); screen.render(); } - ScreenInstruction::MoveFocusLeft => { - screen.get_active_tab_mut().unwrap().move_focus_left(); + ScreenInstruction::MoveFocusLeft(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().move_focus_left(); screen.render(); } - ScreenInstruction::MoveFocusLeftOrPreviousTab => { - screen.move_focus_left_or_previous_tab(); + ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id) => { + screen.move_focus_left_or_previous_tab(client_id); screen .bus .senders @@ -612,18 +654,18 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::MoveFocusDown => { - screen.get_active_tab_mut().unwrap().move_focus_down(); + ScreenInstruction::MoveFocusDown(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().move_focus_down(); screen.render(); } - ScreenInstruction::MoveFocusRight => { - screen.get_active_tab_mut().unwrap().move_focus_right(); + ScreenInstruction::MoveFocusRight(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().move_focus_right(); screen.render(); } - ScreenInstruction::MoveFocusRightOrNextTab => { - screen.move_focus_right_or_next_tab(); + ScreenInstruction::MoveFocusRightOrNextTab(client_id) => { + screen.move_focus_right_or_next_tab(client_id); screen .bus .senders @@ -632,77 +674,77 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::MoveFocusUp => { - screen.get_active_tab_mut().unwrap().move_focus_up(); + ScreenInstruction::MoveFocusUp(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().move_focus_up(); screen.render(); } - ScreenInstruction::ScrollUp => { + ScreenInstruction::ScrollUp(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_active_terminal_up(); screen.render(); } - ScreenInstruction::ScrollUpAt(point) => { + ScreenInstruction::ScrollUpAt(point, client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_terminal_up(&point, 3); screen.render(); } - ScreenInstruction::ScrollDown => { + ScreenInstruction::ScrollDown(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_active_terminal_down(); screen.render(); } - ScreenInstruction::ScrollDownAt(point) => { + ScreenInstruction::ScrollDownAt(point, client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_terminal_down(&point, 3); screen.render(); } - ScreenInstruction::ScrollToBottom => { + ScreenInstruction::ScrollToBottom(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_active_terminal_to_bottom(); screen.render(); } - ScreenInstruction::PageScrollUp => { + ScreenInstruction::PageScrollUp(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_active_terminal_up_page(); screen.render(); } - ScreenInstruction::PageScrollDown => { + ScreenInstruction::PageScrollDown(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .scroll_active_terminal_down_page(); screen.render(); } - ScreenInstruction::ClearScroll => { + ScreenInstruction::ClearScroll(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .clear_active_terminal_scroll(); screen.render(); } - ScreenInstruction::CloseFocusedPane => { - screen.get_active_tab_mut().unwrap().close_focused_pane(); + ScreenInstruction::CloseFocusedPane(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().close_focused_pane(); screen.update_tabs(); // update_tabs eventually calls render through the plugin thread } ScreenInstruction::SetSelectable(id, selectable, tab_index) => { @@ -719,13 +761,25 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::ClosePane(id) => { - screen.get_active_tab_mut().unwrap().close_pane(id); + ScreenInstruction::ClosePane(id, client_id) => { + match client_id { + Some(client_id) => { + screen.get_active_tab_mut(client_id).unwrap().close_pane(id); + }, + None => { + for (_index, tab) in screen.tabs.iter_mut() { + if tab.get_pane_ids().iter().find(|pid| **pid == id).is_some() { + tab.close_pane(id); + break; + } + } + } + } screen.update_tabs(); } - ScreenInstruction::ToggleActiveTerminalFullscreen => { + ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .toggle_active_pane_fullscreen(); screen.update_tabs(); @@ -739,8 +793,8 @@ pub(crate) fn screen_thread_main( } screen.render(); } - ScreenInstruction::SwitchTabNext => { - screen.switch_tab_next(); + ScreenInstruction::SwitchTabNext(client_id) => { + screen.switch_tab_next(client_id); screen .bus .senders @@ -749,8 +803,8 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::SwitchTabPrev => { - screen.switch_tab_prev(); + ScreenInstruction::SwitchTabPrev(client_id) => { + screen.switch_tab_prev(client_id); screen .bus .senders @@ -759,8 +813,8 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::CloseTab => { - screen.close_tab(); + ScreenInstruction::CloseTab(client_id) => { + screen.close_tab(client_id); screen .bus .senders @@ -779,8 +833,8 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::GoToTab(tab_index) => { - screen.go_to_tab(tab_index as usize); + ScreenInstruction::GoToTab(tab_index, client_id) => { + screen.go_to_tab(tab_index as usize, client_id); screen .bus .senders @@ -789,8 +843,8 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::UpdateTabName(c) => { - screen.update_active_tab_name(c); + ScreenInstruction::UpdateTabName(c, client_id) => { + screen.update_active_tab_name(c, client_id); screen.render(); } @@ -799,54 +853,54 @@ pub(crate) fn screen_thread_main( screen.render(); } - ScreenInstruction::ChangeMode(mode_info) => { - screen.change_mode(mode_info); + ScreenInstruction::ChangeMode(mode_info, client_id) => { + screen.change_mode(mode_info, client_id); screen.render(); } - ScreenInstruction::ToggleActiveSyncTab => { + ScreenInstruction::ToggleActiveSyncTab(client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .toggle_sync_panes_is_active(); screen.update_tabs(); screen.render(); } - ScreenInstruction::LeftClick(point) => { + ScreenInstruction::LeftClick(point, client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .handle_left_click(&point); screen.render(); } - ScreenInstruction::MouseRelease(point) => { + ScreenInstruction::MouseRelease(point, client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .handle_mouse_release(&point); screen.render(); } - ScreenInstruction::MouseHold(point) => { + ScreenInstruction::MouseHold(point, client_id) => { screen - .get_active_tab_mut() + .get_active_tab_mut(client_id) .unwrap() .handle_mouse_hold(&point); screen.render(); } - ScreenInstruction::Copy => { - screen.get_active_tab().unwrap().copy_selection(); + ScreenInstruction::Copy(client_id) => { + screen.get_active_tab(client_id).unwrap().copy_selection(); screen.render(); } ScreenInstruction::Exit => { break; } - ScreenInstruction::ToggleTab => { - screen.toggle_tab(); + ScreenInstruction::ToggleTab(client_id) => { + screen.toggle_tab(client_id); screen .bus .senders diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index ec570c5e61..2ddd0e3712 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -102,13 +102,14 @@ pub struct Output { } impl Output { - pub fn new(client_ids: &HashSet) -> Self { - let mut client_render_instructions = HashMap::new(); - for client_id in client_ids { - client_render_instructions.insert(*client_id, String::new()); - } + pub fn new() -> Self { Output { - client_render_instructions, + client_render_instructions: HashMap::new(), + } + } + pub fn add_clients(&mut self, client_ids: &HashSet) { + for client_id in client_ids { + self.client_render_instructions.insert(*client_id, String::new()); } } pub fn push_str_to_all_clients(&mut self, to_push: &str) { @@ -135,7 +136,6 @@ pub(crate) struct Tab { should_clear_display_before_rendering: bool, pub mode_info: ModeInfo, pub colors: Palette, - pub is_active: bool, connected_clients: HashSet, draw_pane_frames: bool, pending_vte_events: HashMap>, @@ -322,7 +322,6 @@ impl Tab { colors, draw_pane_frames, pending_vte_events: HashMap::new(), - is_active: true, connected_clients, } } @@ -795,14 +794,17 @@ impl Tab { } } } - pub fn render(&mut self) -> Option { + pub fn render(&mut self, output: &mut Output) { if self.connected_clients.is_empty() || self.active_terminal.is_none() { - return None; + return; } - self.senders - .send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal)) - .unwrap(); - let mut output = Output::new(&self.connected_clients); + for connected_client in self.connected_clients.iter() { + // TODO: move this out of the render function + self.senders + .send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal, *connected_client)) + .unwrap(); + } + output.add_clients(&self.connected_clients); let mut boundaries = Boundaries::new(self.viewport); let hide_cursor = "\u{1b}[?25l"; output.push_str_to_all_clients(hide_cursor); @@ -873,7 +875,6 @@ impl Tab { output.push_str_to_all_clients(hide_cursor); } } - Some(output) } fn get_panes(&self) -> impl Iterator)> { self.panes.iter() @@ -2240,6 +2241,7 @@ impl Tab { // if we reached here, this is either the last pane or there's some sort of // configuration error (eg. we're trying to close a pane surrounded by fixed panes) self.panes.remove(&id); + self.active_terminal = None; self.resize_whole_tab(self.display_area); } } @@ -2408,7 +2410,8 @@ impl Tab { } fn write_selection_to_clipboard(&self, selection: &str) { - let mut output = Output::new(&self.connected_clients); + let mut output = Output::new(); + output.add_clients(&self.connected_clients); output.push_str_to_all_clients(&format!( "\u{1b}]52;c;{}\u{1b}\\", base64::encode(selection) diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index d1805b8af6..b4d367e4b0 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -116,7 +116,7 @@ fn open_new_tab() { assert_eq!(screen.tabs.len(), 2, "Screen now has two tabs"); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab switched to new tab" ); @@ -132,10 +132,10 @@ pub fn switch_to_prev_tab() { new_tab(&mut screen, 1); new_tab(&mut screen, 2); - screen.switch_tab_prev(); + screen.switch_tab_prev(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 0, "Active tab switched to previous tab" ); @@ -151,11 +151,11 @@ pub fn switch_to_next_tab() { new_tab(&mut screen, 1); new_tab(&mut screen, 2); - screen.switch_tab_prev(); - screen.switch_tab_next(); + screen.switch_tab_prev(1); + screen.switch_tab_next(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab switched to next tab" ); @@ -171,11 +171,11 @@ pub fn close_tab() { new_tab(&mut screen, 1); new_tab(&mut screen, 2); - screen.close_tab(); + screen.close_tab(1); assert_eq!(screen.tabs.len(), 1, "Only one tab left"); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 0, "Active tab switched to previous tab" ); @@ -197,13 +197,13 @@ pub fn close_the_middle_tab() { .values() .map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids())) .collect::>()); - screen.switch_tab_prev(); + screen.switch_tab_prev(1); dbg!(screen .tabs .values() .map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids())) .collect::>()); - screen.close_tab(); + screen.close_tab(1); dbg!(screen .tabs .values() @@ -212,7 +212,7 @@ pub fn close_the_middle_tab() { assert_eq!(screen.tabs.len(), 2, "Two tabs left"); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab switched to previous tab" ); @@ -229,11 +229,11 @@ fn move_focus_left_at_left_screen_edge_changes_tab() { new_tab(&mut screen, 1); new_tab(&mut screen, 2); new_tab(&mut screen, 3); - screen.switch_tab_prev(); - screen.move_focus_left_or_previous_tab(); + screen.switch_tab_prev(1); + screen.move_focus_left_or_previous_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 0, "Active tab switched to previous" ); @@ -250,11 +250,11 @@ fn move_focus_right_at_right_screen_edge_changes_tab() { new_tab(&mut screen, 1); new_tab(&mut screen, 2); new_tab(&mut screen, 3); - screen.switch_tab_prev(); - screen.move_focus_right_or_next_tab(); + screen.switch_tab_prev(1); + screen.move_focus_right_or_next_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 2, "Active tab switched to next" ); @@ -270,19 +270,19 @@ pub fn toggle_to_previous_tab_simple() { new_tab(&mut screen, 1); new_tab(&mut screen, 2); - screen.go_to_tab(1); - screen.go_to_tab(2); + screen.go_to_tab(1, 1); + screen.go_to_tab(2, 1); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 0, "Active tab toggler to previous tab" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab toggler to previous tab" ); @@ -301,38 +301,38 @@ pub fn toggle_to_previous_tab_create_tabs_only() { new_tab(&mut screen, 3); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(1)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 1], "Tab history is invalid" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab toggler to previous tab" ); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(2)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 2], "Tab history is invalid" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 2, "Active tab toggler to previous tab" ); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(1)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 1], "Tab history is invalid" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab toggler to previous tab" ); @@ -352,84 +352,84 @@ pub fn toggle_to_previous_tab_delete() { new_tab(&mut screen, 4); // 3 assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(1), Some(2)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 1, 2], "Tab history is invalid" ); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 3, "Active tab toggler to previous tab" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(1), Some(3)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 1, 3], "Tab history is invalid" ); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 2, "Active tab toggler to previous tab" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(1), Some(2)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 1, 2], "Tab history is invalid" ); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 3, "Active tab toggler to previous tab" ); - screen.switch_tab_prev(); + screen.switch_tab_prev(1); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(1), Some(3)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 1, 3], "Tab history is invalid" ); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 2, "Active tab toggler to previous tab" ); - screen.switch_tab_prev(); + screen.switch_tab_prev(1); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(3), Some(2)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 3, 2], "Tab history is invalid" ); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab toggler to previous tab" ); - screen.close_tab(); + screen.close_tab(1); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(3)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 3], "Tab history is invalid" ); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 1, "Active tab toggler to previous tab" ); - screen.toggle_tab(); + screen.toggle_tab(1); assert_eq!( - screen.get_active_tab().unwrap().position, + screen.get_active_tab(1).unwrap().position, 2, "Active tab toggler to previous tab" ); assert_eq!( - screen.tab_history, - vec![None, Some(0), Some(2)], + screen.tab_history.get(&1).unwrap(), + &vec![0, 2], "Tab history is invalid" ); } diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index 1d67e8477e..63c4e300bb 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -20,7 +20,7 @@ use zellij_tile::data::{Event, EventType, PluginIds}; use crate::{ logging_pipe::LoggingPipe, panes::PaneId, - pty::PtyInstruction, + pty::{PtyInstruction, ClientOrTabIndex}, screen::ScreenInstruction, thread_bus::{Bus, ThreadSenders}, }; @@ -60,6 +60,7 @@ pub(crate) struct PluginEnv { pub senders: ThreadSenders, pub wasi_env: WasiEnv, pub subscriptions: Arc>>, + pub tab_index: usize, plugin_own_data_dir: PathBuf, } @@ -208,6 +209,7 @@ fn start_plugin( wasi_env, subscriptions: Arc::new(Mutex::new(HashSet::new())), plugin_own_data_dir, + tab_index, }; let zellij = zellij_exports(store, &plugin_env); @@ -294,7 +296,7 @@ fn host_open_file(plugin_env: &PluginEnv) { .senders .send_to_pty(PtyInstruction::SpawnTerminal(Some( TerminalAction::OpenFile(path), - ))) + ), ClientOrTabIndex::TabIndex(plugin_env.tab_index))) .unwrap(); } From d72f99cbe2fc8f4241a4b24bed7d4ffa55783a94 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 14 Oct 2021 15:33:14 +0200 Subject: [PATCH 02/18] style(fmt): make rustfmt happy --- zellij-server/src/pty.rs | 55 +++++++++++++++------- zellij-server/src/route.rs | 40 ++++++++++------ zellij-server/src/screen.rs | 88 +++++++++++++++++++++++++++--------- zellij-server/src/tab.rs | 8 +++- zellij-server/src/wasm_vm.rs | 9 ++-- 5 files changed, 143 insertions(+), 57 deletions(-) diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 80a26c17d8..f81ecf098e 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -82,21 +82,32 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) { let pid = pty.spawn_terminal(terminal_action, client_or_tab_index); pty.bus .senders - .send_to_screen(ScreenInstruction::NewPane(PaneId::Terminal(pid), client_or_tab_index)) + .send_to_screen(ScreenInstruction::NewPane( + PaneId::Terminal(pid), + client_or_tab_index, + )) .unwrap(); } PtyInstruction::SpawnTerminalVertically(terminal_action, client_id) => { - let pid = pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)); + let pid = + pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)); pty.bus .senders - .send_to_screen(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid), client_id)) + .send_to_screen(ScreenInstruction::VerticalSplit( + PaneId::Terminal(pid), + client_id, + )) .unwrap(); } PtyInstruction::SpawnTerminalHorizontally(terminal_action, client_id) => { - let pid = pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)); + let pid = + pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)); pty.bus .senders - .send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid), client_id)) + .send_to_screen(ScreenInstruction::HorizontalSplit( + PaneId::Terminal(pid), + client_id, + )) .unwrap(); } PtyInstruction::UpdateActivePane(pane_id, client_id) => { @@ -125,7 +136,10 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) { .unwrap(); pty.bus .senders - .send_to_screen(ScreenInstruction::UpdateTabName(tab_name.into_bytes(), client_id)) + .send_to_screen(ScreenInstruction::UpdateTabName( + tab_name.into_bytes(), + client_id, + )) .unwrap(); } } @@ -238,8 +252,11 @@ fn stream_terminal_bytes( // we send ClosePane here so that the screen knows to close this tab if the process // inside the terminal exited on its own (eg. the user typed "exit" inside a // bash shell) - async_send_to_screen(senders, ScreenInstruction::ClosePane(PaneId::Terminal(pid), None)) - .await; + async_send_to_screen( + senders, + ScreenInstruction::ClosePane(PaneId::Terminal(pid), None), + ) + .await; } }) } @@ -259,10 +276,7 @@ impl Pty { args: vec![], command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")), cwd: client_id - .and_then(|client_id| self - .active_panes - .get(&client_id) - ) + .and_then(|client_id| self.active_panes.get(&client_id)) .and_then(|pane| match pane { PaneId::Plugin(..) => None, PaneId::Terminal(id) => self.id_to_child_pid.get(&id).and_then(|id| id.shell), @@ -271,10 +285,18 @@ impl Pty { .flatten(), }) } - pub fn spawn_terminal(&mut self, terminal_action: Option, client_or_tab_index: ClientOrTabIndex) -> RawFd { + pub fn spawn_terminal( + &mut self, + terminal_action: Option, + client_or_tab_index: ClientOrTabIndex, + ) -> RawFd { let terminal_action = match client_or_tab_index { - ClientOrTabIndex::ClientId(client_id) => terminal_action.unwrap_or_else(|| self.get_default_terminal(Some(client_id))), - ClientOrTabIndex::TabIndex(_) => terminal_action.unwrap_or_else(|| self.get_default_terminal(None)), + ClientOrTabIndex::ClientId(client_id) => { + terminal_action.unwrap_or_else(|| self.get_default_terminal(Some(client_id))) + } + ClientOrTabIndex::TabIndex(_) => { + terminal_action.unwrap_or_else(|| self.get_default_terminal(None)) + } }; let (pid_primary, child_id): (RawFd, ChildId) = self .bus @@ -298,7 +320,8 @@ impl Pty { default_shell: Option, client_id: ClientId, ) { - let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(Some(client_id))); + let default_shell = + default_shell.unwrap_or_else(|| self.get_default_terminal(Some(client_id))); let extracted_run_instructions = layout.extract_run_instructions(); let mut new_pane_pids = vec![]; for run_instruction in extracted_run_instructions { diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 8edd851a00..15867eaa36 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -3,8 +3,11 @@ use std::sync::{Arc, RwLock}; use zellij_utils::zellij_tile::data::Event; use crate::{ - os_input_output::ServerOsApi, pty::{PtyInstruction, ClientOrTabIndex}, screen::ScreenInstruction, - wasm_vm::PluginInstruction, ServerInstruction, SessionMetaData, SessionState, + os_input_output::ServerOsApi, + pty::{ClientOrTabIndex, PtyInstruction}, + screen::ScreenInstruction, + wasm_vm::PluginInstruction, + ServerInstruction, SessionMetaData, SessionState, }; use zellij_utils::{ channels::SenderWithContext, @@ -61,11 +64,10 @@ fn route_action( .unwrap(); session .senders - .send_to_screen(ScreenInstruction::ChangeMode(get_mode_info( - mode, - palette, - session.capabilities, - ), client_id)) + .send_to_screen(ScreenInstruction::ChangeMode( + get_mode_info(mode, palette, session.capabilities), + client_id, + )) .unwrap(); session .senders @@ -176,7 +178,9 @@ fn route_action( Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell, client_id), Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell, client_id), Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell, client_id), - Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(shell, client_id), + Some(Direction::Down) => { + PtyInstruction::SpawnTerminalHorizontally(shell, client_id) + } // No direction specified - try to put it in the biggest available spot None => PtyInstruction::SpawnTerminal(shell, ClientOrTabIndex::ClientId(client_id)), }; @@ -185,12 +189,22 @@ fn route_action( Action::Run(command) => { let run_cmd = Some(TerminalAction::RunCommand(command.clone().into())); let pty_instr = match command.direction { - Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(run_cmd, client_id), - Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(run_cmd, client_id), - Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id), - Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id), + Some(Direction::Left) => { + PtyInstruction::SpawnTerminalVertically(run_cmd, client_id) + } + Some(Direction::Right) => { + PtyInstruction::SpawnTerminalVertically(run_cmd, client_id) + } + Some(Direction::Up) => { + PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id) + } + Some(Direction::Down) => { + PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id) + } // No direction specified - try to put it in the biggest available spot - None => PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id)), + None => { + PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id)) + } }; session.senders.send_to_pty(pty_instr).unwrap(); } diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 7f85eec891..171417d9c9 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -9,8 +9,8 @@ use zellij_utils::{input::layout::Layout, position::Position, zellij_tile}; use crate::{ panes::PaneId, - pty::{PtyInstruction, VteBytes, ClientOrTabIndex}, - tab::{Tab, Output}, + pty::{ClientOrTabIndex, PtyInstruction, VteBytes}, + tab::{Output, Tab}, thread_bus::Bus, wasm_vm::PluginInstruction, ClientId, ServerInstruction, @@ -99,7 +99,9 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::MoveFocusDown(..) => ScreenContext::MoveFocusDown, ScreenInstruction::MoveFocusUp(..) => ScreenContext::MoveFocusUp, ScreenInstruction::MoveFocusRight(..) => ScreenContext::MoveFocusRight, - ScreenInstruction::MoveFocusRightOrNextTab(..) => ScreenContext::MoveFocusRightOrNextTab, + ScreenInstruction::MoveFocusRightOrNextTab(..) => { + ScreenContext::MoveFocusRightOrNextTab + } ScreenInstruction::Exit => ScreenContext::Exit, ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp, ScreenInstruction::ScrollDown(..) => ScreenContext::ScrollDown, @@ -189,7 +191,8 @@ impl Screen { } fn move_clients_from_closed_tab(&mut self, previous_tab_index: usize) { - let client_ids_in_closed_tab: Vec = self.active_tab_indices + let client_ids_in_closed_tab: Vec = self + .active_tab_indices .iter() .filter(|(_c_id, t_index)| **t_index == previous_tab_index) .map(|(c_id, _t_index)| c_id) @@ -197,10 +200,13 @@ impl Screen { .collect(); for client_id in client_ids_in_closed_tab { let client_previous_tab = self.tab_history.get_mut(&client_id).unwrap().pop().unwrap(); - self.active_tab_indices.insert(client_id, client_previous_tab); - self.tabs.get_mut(&client_previous_tab).unwrap().add_client(client_id); + self.active_tab_indices + .insert(client_id, client_previous_tab); + self.tabs + .get_mut(&client_previous_tab) + .unwrap() + .add_client(client_id); } - } fn move_clients(&mut self, source_index: usize, destination_index: usize) { let connected_clients_in_source_tab = { @@ -286,7 +292,8 @@ impl Screen { .unwrap(); } else { self.move_clients_from_closed_tab(tab_index); - let visible_tab_indices: HashSet = self.active_tab_indices.values().copied().collect(); + let visible_tab_indices: HashSet = + self.active_tab_indices.values().copied().collect(); for t in self.tabs.values_mut() { if visible_tab_indices.contains(&t.index) { t.set_force_render(); @@ -414,7 +421,8 @@ impl Screen { pub fn add_client(&mut self, client_id: ClientId) { let mut tab_index = 0; let mut tab_history = vec![]; - if let Some((_first_client, first_active_tab_index)) = self.active_tab_indices.iter().next() { + if let Some((_first_client, first_active_tab_index)) = self.active_tab_indices.iter().next() + { tab_index = *first_active_tab_index; } if let Some((_first_client, first_tab_history)) = self.tab_history.iter().next() { @@ -441,7 +449,8 @@ impl Screen { // TODO: right now all clients are synced, so we just take the first active_tab which is // the same for everyone - when this is no longer the case, we need to update the TabInfo // to account for this (or send multiple TabInfos) - if let Some((_first_client, first_active_tab_index)) = self.active_tab_indices.iter().next() { + if let Some((_first_client, first_active_tab_index)) = self.active_tab_indices.iter().next() + { for tab in self.tabs.values() { tab_data.push(TabInfo { position: tab.position, @@ -492,12 +501,20 @@ impl Screen { } } pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) { - if !self.get_active_tab_mut(client_id).unwrap().move_focus_left() { + if !self + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_left() + { self.switch_tab_prev(client_id); } } pub fn move_focus_right_or_next_tab(&mut self, client_id: ClientId) { - if !self.get_active_tab_mut(client_id).unwrap().move_focus_right() { + if !self + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_right() + { self.switch_tab_next(client_id); } } @@ -576,7 +593,10 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::HorizontalSplit(pid, client_id) => { - screen.get_active_tab_mut(client_id).unwrap().horizontal_split(pid); + screen + .get_active_tab_mut(client_id) + .unwrap() + .horizontal_split(pid); screen .bus .senders @@ -587,7 +607,10 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::VerticalSplit(pid, client_id) => { - screen.get_active_tab_mut(client_id).unwrap().vertical_split(pid); + screen + .get_active_tab_mut(client_id) + .unwrap() + .vertical_split(pid); screen .bus .senders @@ -630,17 +653,26 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::FocusNextPane(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().focus_next_pane(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .focus_next_pane(); screen.render(); } ScreenInstruction::FocusPreviousPane(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().focus_previous_pane(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .focus_previous_pane(); screen.render(); } ScreenInstruction::MoveFocusLeft(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().move_focus_left(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_left(); screen.render(); } @@ -655,12 +687,18 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::MoveFocusDown(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().move_focus_down(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_down(); screen.render(); } ScreenInstruction::MoveFocusRight(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().move_focus_right(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_right(); screen.render(); } @@ -675,7 +713,10 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::MoveFocusUp(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().move_focus_up(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_up(); screen.render(); } @@ -744,7 +785,10 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::CloseFocusedPane(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().close_focused_pane(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .close_focused_pane(); screen.update_tabs(); // update_tabs eventually calls render through the plugin thread } ScreenInstruction::SetSelectable(id, selectable, tab_index) => { @@ -765,7 +809,7 @@ pub(crate) fn screen_thread_main( match client_id { Some(client_id) => { screen.get_active_tab_mut(client_id).unwrap().close_pane(id); - }, + } None => { for (_index, tab) in screen.tabs.iter_mut() { if tab.get_pane_ids().iter().find(|pid| **pid == id).is_some() { diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index 2ddd0e3712..042d90efdb 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -109,7 +109,8 @@ impl Output { } pub fn add_clients(&mut self, client_ids: &HashSet) { for client_id in client_ids { - self.client_render_instructions.insert(*client_id, String::new()); + self.client_render_instructions + .insert(*client_id, String::new()); } } pub fn push_str_to_all_clients(&mut self, to_push: &str) { @@ -801,7 +802,10 @@ impl Tab { for connected_client in self.connected_clients.iter() { // TODO: move this out of the render function self.senders - .send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal, *connected_client)) + .send_to_pty(PtyInstruction::UpdateActivePane( + self.active_terminal, + *connected_client, + )) .unwrap(); } output.add_clients(&self.connected_clients); diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index 63c4e300bb..1c22f60bc2 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -20,7 +20,7 @@ use zellij_tile::data::{Event, EventType, PluginIds}; use crate::{ logging_pipe::LoggingPipe, panes::PaneId, - pty::{PtyInstruction, ClientOrTabIndex}, + pty::{ClientOrTabIndex, PtyInstruction}, screen::ScreenInstruction, thread_bus::{Bus, ThreadSenders}, }; @@ -294,9 +294,10 @@ fn host_open_file(plugin_env: &PluginEnv) { let path: PathBuf = wasi_read_object(&plugin_env.wasi_env); plugin_env .senders - .send_to_pty(PtyInstruction::SpawnTerminal(Some( - TerminalAction::OpenFile(path), - ), ClientOrTabIndex::TabIndex(plugin_env.tab_index))) + .send_to_pty(PtyInstruction::SpawnTerminal( + Some(TerminalAction::OpenFile(path)), + ClientOrTabIndex::TabIndex(plugin_env.tab_index), + )) .unwrap(); } From 16afa27c9ba5294b45ce7c2c2aaf682301ac729f Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 14 Oct 2021 17:15:01 +0200 Subject: [PATCH 03/18] style(clippy): make clippy happy --- zellij-server/src/pty.rs | 2 +- zellij-server/src/screen.rs | 8 ++++---- zellij-server/src/tab.rs | 9 ++------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index f81ecf098e..76ff7978b6 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -279,7 +279,7 @@ impl Pty { .and_then(|client_id| self.active_panes.get(&client_id)) .and_then(|pane| match pane { PaneId::Plugin(..) => None, - PaneId::Terminal(id) => self.id_to_child_pid.get(&id).and_then(|id| id.shell), + PaneId::Terminal(id) => self.id_to_child_pid.get(id).and_then(|id| id.shell), }) .and_then(|id| self.bus.os_input.as_ref().map(|input| input.get_cwd(id))) .flatten(), diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 171417d9c9..5073bbc937 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -325,7 +325,7 @@ impl Screen { /// Renders this [`Screen`], which amounts to rendering its active [`Tab`]. pub fn render(&mut self) { - let mut output = Output::new(); + let mut output = Output::default(); let mut tabs_to_close = vec![]; for (tab_index, tab) in self.tabs.iter_mut() { if tab.get_active_pane().is_some() { @@ -351,7 +351,7 @@ impl Screen { /// Returns an immutable reference to this [`Screen`]'s active [`Tab`]. pub fn get_active_tab(&self, client_id: ClientId) -> Option<&Tab> { match self.active_tab_indices.get(&client_id) { - Some(tab) => self.tabs.get(&tab), + Some(tab) => self.tabs.get(tab), None => None, } } @@ -368,7 +368,7 @@ impl Screen { /// Returns a mutable reference to this [`Screen`]'s active [`Tab`]. pub fn get_active_tab_mut(&mut self, client_id: ClientId) -> Option<&mut Tab> { match self.active_tab_indices.get(&client_id) { - Some(tab) => self.tabs.get_mut(&tab), + Some(tab) => self.tabs.get_mut(tab), None => None, } } @@ -812,7 +812,7 @@ pub(crate) fn screen_thread_main( } None => { for (_index, tab) in screen.tabs.iter_mut() { - if tab.get_pane_ids().iter().find(|pid| **pid == id).is_some() { + if tab.get_pane_ids().iter().any(|pid| *pid == id) { tab.close_pane(id); break; } diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index 042d90efdb..2fbfb52510 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -96,17 +96,12 @@ fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (us (columns_offset, rows_offset) } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Output { pub client_render_instructions: HashMap, } impl Output { - pub fn new() -> Self { - Output { - client_render_instructions: HashMap::new(), - } - } pub fn add_clients(&mut self, client_ids: &HashSet) { for client_id in client_ids { self.client_render_instructions @@ -2414,7 +2409,7 @@ impl Tab { } fn write_selection_to_clipboard(&self, selection: &str) { - let mut output = Output::new(); + let mut output = Output::default(); output.add_clients(&self.connected_clients); output.push_str_to_all_clients(&format!( "\u{1b}]52;c;{}\u{1b}\\", From 9724a4170d7487a7878fd91d073695336d179651 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 14 Oct 2021 18:55:11 +0200 Subject: [PATCH 04/18] whitespace --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1d179c916c..0f7e5a5332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,4 @@ + [package] name = "zellij" version = "0.19.0" From fbbcd63363060719d9f714595a6c323258fe9129 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 14 Oct 2021 18:55:33 +0200 Subject: [PATCH 05/18] github, y u no update CI?! --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0f7e5a5332..1d179c916c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,3 @@ - [package] name = "zellij" version = "0.19.0" From f83f97ccbd62af40f0793f3107f768b401f1818b Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 09:30:53 +0200 Subject: [PATCH 06/18] is this a cache issue? --- .github/workflows/rust.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 744bfdb0ee..16a6caeb99 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -58,14 +58,14 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Install cargo-make run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make - name: Check Lints From 634f262b110c2543b96ff0a71ebc266edc853608 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 09:47:23 +0200 Subject: [PATCH 07/18] is it the checkout cache? --- .github/workflows/rust.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 744bfdb0ee..bbe3ec5013 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,14 +16,14 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Add WASM target run: rustup target add wasm32-wasi - name: Install cargo-make From 646bdb27f2db72512b4fbdd58d8d275c7accc658 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 09:52:56 +0200 Subject: [PATCH 08/18] no cache at all? --- .github/workflows/rust.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index bbe3ec5013..914f98acb4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -39,14 +39,14 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Install cargo-make run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make - name: Check Format @@ -58,14 +58,14 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Install cargo-make run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make - name: Check Lints From 738f80f80f1be4a3bfab046de1c907b0a3d71802 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 10:03:32 +0200 Subject: [PATCH 09/18] Debug --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 914f98acb4..effefa6742 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -66,6 +66,8 @@ jobs: # ~/.cargo/git # target # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: debug github + run: git log --name-status HEAD^..HEAD - name: Install cargo-make run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make - name: Check Lints From 1e810e53f32e20df675181b5dcf34d4576b0d743 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 10:49:05 +0200 Subject: [PATCH 10/18] fix gototab --- zellij-server/src/route.rs | 2 +- zellij-server/src/screen.rs | 20 +++++++++++--------- zellij-server/src/wasm_vm.rs | 7 +++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 15867eaa36..496bd65be2 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -248,7 +248,7 @@ fn route_action( Action::GoToTab(i) => { session .senders - .send_to_screen(ScreenInstruction::GoToTab(i, client_id)) + .send_to_screen(ScreenInstruction::GoToTab(i, Some(client_id))) .unwrap(); } Action::TabNameInput(c) => { diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 5073bbc937..5d7dfff397 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -63,7 +63,7 @@ pub(crate) enum ScreenInstruction { SwitchTabPrev(ClientId), ToggleActiveSyncTab(ClientId), CloseTab(ClientId), - GoToTab(u32, ClientId), + GoToTab(u32, Option), // this Option is a hacky workaround, please do not copy thie behaviour ToggleTab(ClientId), UpdateTabName(Vec, ClientId), TerminalResize(Size), @@ -878,14 +878,16 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::GoToTab(tab_index, client_id) => { - screen.go_to_tab(tab_index as usize, client_id); - screen - .bus - .senders - .send_to_server(ServerInstruction::UnblockInputThread) - .unwrap(); - - screen.render(); + if let Some(client_id) = client_id.or_else(|| screen.active_tab_indices.keys().next().copied()) { + screen.go_to_tab(tab_index as usize, client_id); + screen + .bus + .senders + .send_to_server(ServerInstruction::UnblockInputThread) + .unwrap(); + + screen.render(); + } } ScreenInstruction::UpdateTabName(c, client_id) => { screen.update_active_tab_name(c, client_id); diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index 1c22f60bc2..b06c937986 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -301,6 +301,13 @@ fn host_open_file(plugin_env: &PluginEnv) { .unwrap(); } +fn host_switch_tab_to(plugin_env: &PluginEnv, tab_idx: u32) { + plugin_env + .senders + .send_to_screen(ScreenInstruction::GoToTab(tab_idx, None)) // this is a hack, we should be able to return the client id here + .unwrap(); +} + fn host_set_timeout(plugin_env: &PluginEnv, secs: f64) { // There is a fancy, high-performance way to do this with zero additional threads: // If the plugin thread keeps a BinaryHeap of timer structs, it can manage multiple and easily `.peek()` at the From 5ae0129562944d138956984520ed7618b426e386 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 10:50:25 +0200 Subject: [PATCH 11/18] decoment --- .github/workflows/rust.yml | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index effefa6742..0ec8adcbc0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,14 +16,14 @@ jobs: steps: - uses: actions/checkout@v2 - # - uses: actions/cache@v2 - # with: - # path: | - # ~/.cargo/bin/ - # ~/.cargo/registry - # ~/.cargo/git - # target - # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Add WASM target run: rustup target add wasm32-wasi - name: Install cargo-make @@ -39,14 +39,14 @@ jobs: steps: - uses: actions/checkout@v2 - # - uses: actions/cache@v2 - # with: - # path: | - # ~/.cargo/bin/ - # ~/.cargo/registry - # ~/.cargo/git - # target - # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Install cargo-make run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make - name: Check Format @@ -58,14 +58,14 @@ jobs: steps: - uses: actions/checkout@v2 - # - uses: actions/cache@v2 - # with: - # path: | - # ~/.cargo/bin/ - # ~/.cargo/registry - # ~/.cargo/git - # target - # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: debug github run: git log --name-status HEAD^..HEAD - name: Install cargo-make From ec49aab7cb236ef4204e9e34548b1b131840057a Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 11:07:29 +0200 Subject: [PATCH 12/18] gototab none in wasm_vm --- .github/workflows/rust.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0ec8adcbc0..744bfdb0ee 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -66,8 +66,6 @@ jobs: ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: debug github - run: git log --name-status HEAD^..HEAD - name: Install cargo-make run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make - name: Check Lints From 7992433fad7142821cf51f32ef9d84c396c8908c Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 11:07:48 +0200 Subject: [PATCH 13/18] gototab none in wasm_vm --- zellij-server/src/wasm_vm.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index 38e3c49e54..de42b6116b 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -309,13 +309,6 @@ fn host_switch_tab_to(plugin_env: &PluginEnv, tab_idx: u32) { .unwrap(); } -fn host_switch_tab_to(plugin_env: &PluginEnv, tab_idx: u32) { - plugin_env - .senders - .send_to_screen(ScreenInstruction::GoToTab(tab_idx)) - .unwrap(); -} - fn host_set_timeout(plugin_env: &PluginEnv, secs: f64) { // There is a fancy, high-performance way to do this with zero additional threads: // If the plugin thread keeps a BinaryHeap of timer structs, it can manage multiple and easily `.peek()` at the From 765e3b7104d4614bad08211e0a51927838ea6737 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Oct 2021 11:10:39 +0200 Subject: [PATCH 14/18] the fun never ends --- zellij-server/src/screen.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 5d7dfff397..7beed8d828 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -878,7 +878,9 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::GoToTab(tab_index, client_id) => { - if let Some(client_id) = client_id.or_else(|| screen.active_tab_indices.keys().next().copied()) { + if let Some(client_id) = + client_id.or_else(|| screen.active_tab_indices.keys().next().copied()) + { screen.go_to_tab(tab_index as usize, client_id); screen .bus From 5ef56115b6c702cb77c41ffe87b088991995aad0 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 11 Nov 2021 13:40:40 +0100 Subject: [PATCH 15/18] tests(e2e): update infra and add multiple user mirroring test --- src/tests/e2e/cases.rs | 1433 +++++++++++------ src/tests/e2e/remote_runner.rs | 394 ++--- ...lly_when_active_terminal_is_too_small.snap | 2 +- .../zellij__tests__e2e__cases__close_tab.snap | 50 +- ...ests__e2e__cases__mirrored_sessions-2.snap | 29 + ..._tests__e2e__cases__mirrored_sessions.snap | 8 +- ...__e2e__cases__typing_exit_closes_pane.snap | 29 + 7 files changed, 1195 insertions(+), 750 deletions(-) create mode 100644 src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap create mode 100644 src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index cdabef81f1..b45c45e31b 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -75,8 +75,11 @@ pub fn starts_with_one_terminal() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("starts_with_one_terminal", fake_win_size) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for app to load", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -86,8 +89,13 @@ pub fn starts_with_one_terminal() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -99,23 +107,27 @@ pub fn split_terminals_vertically() { rows: 24, }; - let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for new pane to appear", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -125,8 +137,14 @@ pub fn split_terminals_vertically() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -134,54 +152,17 @@ pub fn split_terminals_vertically() { #[ignore] pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { let fake_win_size = Size { cols: 8, rows: 20 }; - let last_snapshot = RemoteRunner::new( - "cannot_split_terminals_vertically_when_active_terminal_is_too_small", - fake_win_size, - ) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Make sure only one pane appears", - instruction: |remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("...") - { - // ... is the truncated tip line - step_is_complete = true; - } - step_is_complete - }, - }) - .run_all_steps(); - assert_snapshot!(last_snapshot); -} - -#[test] -#[ignore] -pub fn scrolling_inside_a_pane() { - let fake_win_size = Size { - cols: 120, - rows: 24, - }; - let last_snapshot = RemoteRunner::new("scrolling_inside_a_pane", fake_win_size) + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new( + fake_win_size, + ) .add_step(Step { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); // back to normal mode after split @@ -190,52 +171,108 @@ pub fn scrolling_inside_a_pane() { } step_is_complete }, - }) - .add_step(Step { - name: "Fill terminal with text", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // cursor is in the newly opened second pane - remote_terminal.send_key(format!("{:0<56}", "line1 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line2 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line3 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line4 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line5 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line6 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line7 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line8 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line9 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line10 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line11 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line12 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line13 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line14 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line15 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line16 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line17 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line18 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line19 ").as_bytes()); - remote_terminal.send_key(format!("{:0<57}", "line20 ").as_bytes()); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Scroll up inside pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { + name: "Make sure only one pane appears", + instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(118, 20) { - // all lines have been written to the pane - remote_terminal.send_key(&SCROLL_MODE); - remote_terminal.send_key(&SCROLL_UP_IN_SCROLL_MODE); + // if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("...") + if remote_terminal.cursor_position_is(3, 2) + { + // ... is the truncated tip line step_is_complete = true; } step_is_complete }, - }) - .add_step(Step { + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; + assert_snapshot!(last_snapshot); +} + +#[test] +#[ignore] +pub fn scrolling_inside_a_pane() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .retry_pause_ms(1000) // we need a longer retry period here because it takes some time to fill the pty buffer + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Fill terminal with text", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + let mut content_to_send = String::new(); + content_to_send.push_str(&format!("{:0<56}", "line1 ")); + content_to_send.push_str(&format!("{:0<58}", "line2 ")); + content_to_send.push_str(&format!("{:0<58}", "line3 ")); + content_to_send.push_str(&format!("{:0<58}", "line4 ")); + content_to_send.push_str(&format!("{:0<58}", "line5 ")); + content_to_send.push_str(&format!("{:0<58}", "line6 ")); + content_to_send.push_str(&format!("{:0<58}", "line7 ")); + content_to_send.push_str(&format!("{:0<58}", "line8 ")); + content_to_send.push_str(&format!("{:0<58}", "line9 ")); + content_to_send.push_str(&format!("{:0<58}", "line10 ")); + content_to_send.push_str(&format!("{:0<58}", "line11 ")); + content_to_send.push_str(&format!("{:0<58}", "line12 ")); + content_to_send.push_str(&format!("{:0<58}", "line13 ")); + content_to_send.push_str(&format!("{:0<58}", "line14 ")); + content_to_send.push_str(&format!("{:0<58}", "line15 ")); + content_to_send.push_str(&format!("{:0<58}", "line16 ")); + content_to_send.push_str(&format!("{:0<58}", "line17 ")); + content_to_send.push_str(&format!("{:0<58}", "line18 ")); + content_to_send.push_str(&format!("{:0<58}", "line19 ")); + content_to_send.push_str(&format!("{:0<57}", "line20 ")); + + remote_terminal.send_key(content_to_send.as_bytes()); + + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Scroll up inside pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(118, 20) { + // all lines have been written to the pane + remote_terminal.send_key(&SCROLL_MODE); + remote_terminal.send_key(&SCROLL_UP_IN_SCROLL_MODE); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for scroll to finish", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -247,8 +284,14 @@ pub fn scrolling_inside_a_pane() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -259,38 +302,42 @@ pub fn toggle_pane_fullscreen() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("toggle_pane_fullscreen", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Change newly opened pane to be fullscreen", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // cursor is in the newly opened second pane - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE); - // back to normal mode after toggling fullscreen - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Change newly opened pane to be fullscreen", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE); + // back to normal mode after toggling fullscreen + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for pane to become fullscreen", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -300,8 +347,14 @@ pub fn toggle_pane_fullscreen() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -312,38 +365,42 @@ pub fn open_new_tab() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("open_new_tab", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Open new tab", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // cursor is in the newly opened second pane - remote_terminal.send_key(&TAB_MODE); - remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Open new tab", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for new tab to open", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -357,50 +414,140 @@ pub fn open_new_tab() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } #[test] #[ignore] -pub fn close_pane() { +pub fn close_tab() { let fake_win_size = Size { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("close_pane", fake_win_size) - .add_step(Step { - name: "Split pane to the right", + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Open new tab", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + // back to normal mode + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Close tab", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") + && remote_terminal.status_bar_appears() + { + // cursor is in the newly opened second tab + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&CLOSE_TAB_IN_TAB_MODE); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { + name: "Wait for tab to close", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); + if remote_terminal.cursor_position_is(3, 2) && !remote_terminal.snapshot_contains("Tab #2") { + // cursor is in the first tab again step_is_complete = true; } step_is_complete }, - }) - .add_step(Step { - name: "Close pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // cursor is in the newly opened second pane - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE); - // back to normal mode after close - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; + assert_snapshot!(last_snapshot); +} + +#[test] +#[ignore] +pub fn close_pane() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Close pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE); + // back to normal mode after close + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for pane to close", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -410,8 +557,14 @@ pub fn close_pane() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -422,20 +575,24 @@ pub fn exit_zellij() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("exit_zellij", fake_win_size) - .add_step(Step { - name: "Wait for app to load", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&QUIT); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&QUIT); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + break runner.take_snapshot_after(Step { name: "Wait for app to exit", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -446,8 +603,8 @@ pub fn exit_zellij() { } step_is_complete }, - }) - .run_all_steps(); + }); + }; assert!(last_snapshot.contains("Bye from Zellij!")); } @@ -458,21 +615,29 @@ pub fn closing_last_pane_exits_zellij() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("closing_last_pane_exits_zellij", fake_win_size) - .add_step(Step { - name: "Close pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Close pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } + break runner.take_snapshot_after(Step { name: "Wait for app to exit", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -481,50 +646,117 @@ pub fn closing_last_pane_exits_zellij() { } step_is_complete }, - }) - .run_all_steps(); + }); + }; assert!(last_snapshot.contains("Bye from Zellij!")); } #[test] #[ignore] -pub fn resize_pane() { +pub fn typing_exit_closes_pane() { let fake_win_size = Size { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("resize_pane", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Resize pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Type exit", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + remote_terminal.send_key(&"e".as_bytes()); + remote_terminal.send_key(&"x".as_bytes()); + remote_terminal.send_key(&"i".as_bytes()); + remote_terminal.send_key(&"t".as_bytes()); + remote_terminal.send_key(&"\n".as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { + name: "Wait for pane to close", + instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // cursor is in the newly opened second pane - remote_terminal.send_key(&RESIZE_MODE); - remote_terminal.send_key(&RESIZE_LEFT_IN_RESIZE_MODE); - // back to normal mode after resizing - remote_terminal.send_key(&ENTER); + // if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() { + if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() { + // cursor is in the original pane step_is_complete = true; } step_is_complete }, - }) - .add_step(Step { + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; + assert_snapshot!(last_snapshot); +} + +#[test] +#[ignore] +pub fn resize_pane() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Resize pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&RESIZE_MODE); + remote_terminal.send_key(&RESIZE_LEFT_IN_RESIZE_MODE); + // back to normal mode after resizing + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for pane to be resized", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -534,8 +766,14 @@ pub fn resize_pane() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -546,33 +784,37 @@ pub fn lock_mode() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("lock_mode", fake_win_size) - .add_step(Step { - name: "Enter lock mode", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&LOCK_MODE); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Send keys that should not be intercepted by the app", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.snapshot_contains("INTERFACE LOCKED") { - remote_terminal.send_key(&TAB_MODE); - remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); - remote_terminal.send_key("abc".as_bytes()); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Enter lock mode", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&LOCK_MODE); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Send keys that should not be intercepted by the app", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.snapshot_contains("INTERFACE LOCKED") { + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + remote_terminal.send_key("abc".as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for terminal to render sent keys", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -582,8 +824,14 @@ pub fn lock_mode() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -595,35 +843,39 @@ pub fn resize_terminal_window() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("resize_terminal_window", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Change terminal window size", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // new pane has been opened and focused - remote_terminal.change_size(100, 24); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Change terminal window size", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // new pane has been opened and focused + remote_terminal.change_size(100, 24); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "wait for terminal to be resized and app to be re-rendered", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -633,8 +885,14 @@ pub fn resize_terminal_window() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -645,60 +903,64 @@ pub fn detach_and_attach_session() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("detach_and_attach_session", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Send some text to the active pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // new pane has been opened and focused - remote_terminal.send_key("I am some text".as_bytes()); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Detach session", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(77, 2) { - remote_terminal.send_key(&SESSION_MODE); - remote_terminal.send_key(&DETACH_IN_SESSION_MODE); - // text has been entered - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Reattach session", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if !remote_terminal.status_bar_appears() { - // we don't see the toolbar, so we can assume we've already detached - remote_terminal.attach_to_original_session(); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Send some text to the active pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // new pane has been opened and focused + remote_terminal.send_key("I am some text".as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Detach session", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(77, 2) { + remote_terminal.send_key(&SESSION_MODE); + remote_terminal.send_key(&DETACH_IN_SESSION_MODE); + // text has been entered + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Reattach session", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if !remote_terminal.status_bar_appears() { + // we don't see the toolbar, so we can assume we've already detached + remote_terminal.attach_to_original_session(); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for session to be attached", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -708,8 +970,14 @@ pub fn detach_and_attach_session() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -721,8 +989,12 @@ pub fn accepts_basic_layout() { rows: 24, }; let layout_file_name = "three-panes-with-nesting.yaml"; - let last_snapshot = RemoteRunner::new_with_layout("accepts_basic_layout", fake_win_size, layout_file_name) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for app to load", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -733,8 +1005,14 @@ pub fn accepts_basic_layout() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -746,34 +1024,38 @@ fn focus_pane_with_mouse() { rows: 24, }; - let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Click left pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - remote_terminal.send_key(&normal_mouse_report(Position::new(5, 2), 0)); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Click left pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + remote_terminal.send_key(&normal_mouse_report(Position::new(5, 2), 0)); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for left pane to be focused", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -783,8 +1065,14 @@ fn focus_pane_with_mouse() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -795,66 +1083,71 @@ pub fn scrolling_inside_a_pane_with_mouse() { cols: 120, rows: 24, }; - let last_snapshot = RemoteRunner::new("scrolling_inside_a_pane_with_mouse", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Fill terminal with text", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - // cursor is in the newly opened second pane - remote_terminal.send_key(format!("{:0<56}", "line1 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line2 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line3 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line4 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line5 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line6 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line7 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line8 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line9 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line10 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line11 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line12 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line13 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line14 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line15 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line16 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line17 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line18 ").as_bytes()); - remote_terminal.send_key(format!("{:0<58}", "line19 ").as_bytes()); - remote_terminal.send_key(format!("{:0<57}", "line20 ").as_bytes()); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Scroll up inside pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(118, 20) { - // all lines have been written to the pane - remote_terminal.send_key(&normal_mouse_report(Position::new(2, 64), 64)); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new(fake_win_size) + .retry_pause_ms(1000) // we need a longer retry period here because it takes some time to fill the pty buffer + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Fill terminal with text", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(format!("{:0<56}", "line1 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line2 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line3 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line4 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line5 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line6 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line7 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line8 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line9 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line10 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line11 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line12 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line13 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line14 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line15 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line16 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line17 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line18 ").as_bytes()); + remote_terminal.send_key(format!("{:0<58}", "line19 ").as_bytes()); + remote_terminal.send_key(format!("{:0<57}", "line20 ").as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Scroll up inside pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(118, 20) { + // all lines have been written to the pane + remote_terminal.send_key(&normal_mouse_report(Position::new(2, 64), 64)); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for scroll to finish", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -866,8 +1159,14 @@ pub fn scrolling_inside_a_pane_with_mouse() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -879,23 +1178,27 @@ pub fn start_without_pane_frames() { rows: 24, }; - let last_snapshot = RemoteRunner::new_without_frames("no_pane_frames", fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 1) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { + let mut test_attempts = 10; + let last_snapshot = loop { + drop(RemoteRunner::kill_running_sessions(fake_win_size)); + let mut runner = RemoteRunner::new_without_frames(fake_win_size) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 1) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + runner.run_all_steps(); + let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for new pane to appear", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; @@ -905,8 +1208,14 @@ pub fn start_without_pane_frames() { } step_is_complete }, - }) - .run_all_steps(); + }); + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; assert_snapshot!(last_snapshot); } @@ -919,13 +1228,29 @@ pub fn mirrored_sessions() { }; let mut test_attempts = 10; let session_name = "mirrored_sessions"; - let mut last_snapshot = None; - loop { - // we run this test in a loop because there are some edge cases (especially in the CI) - // where the second runner times out and then we also need to restart the first runner - // if no test timed out, we break the loop and assert the snapshot + let (first_runner_snapshot, second_runner_snapshot) = loop { + // here we connect with one runner, then connect with another, perform some actions and + // then make sure they were also reflected (mirrored) in the first runner afterwards + drop(RemoteRunner::kill_running_sessions(fake_win_size)); let mut first_runner = - RemoteRunner::new_with_session_name("mirrored_sessions", fake_win_size, session_name) + RemoteRunner::new_with_session_name(fake_win_size, session_name) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); + first_runner.run_all_steps(); + + let mut second_runner = + RemoteRunner::new_existing_session(fake_win_size, session_name) .dont_panic() .add_step(Step { name: "Split pane to the right", @@ -944,52 +1269,102 @@ pub fn mirrored_sessions() { }, }) .add_step(Step { - name: "Wait for new pane to open", - instruction: |remote_terminal: RemoteTerminal| -> bool { + name: "Open new tab (second user)", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { // cursor is in the newly opened second pane + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); step_is_complete = true; } step_is_complete }, - }); - first_runner.run_all_steps(); - - let mut second_runner = - RemoteRunner::new_existing_session("mirrored_sessions", fake_win_size, session_name) - .dont_panic() + }) .add_step(Step { - name: "Make sure session appears correctly", + name: "Wait for new tab to open", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") + && remote_terminal.status_bar_appears() + { + // cursor is in the newly opened second tab + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Switch to previous tab", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") { // cursor is in the newly opened second pane + remote_terminal.send_key(&"some text".as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Wait for text to appear on screen", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.snapshot_contains("some text") { + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&MOVE_FOCUS_LEFT_IN_PANE_MODE); // same key as tab mode step_is_complete = true; } step_is_complete }, }); - let last_test_snapshot = second_runner.run_all_steps(); + second_runner.run_all_steps(); + + if first_runner.test_timed_out || second_runner.test_timed_out { + test_attempts -= 1; + continue; + } + let second_runner_snapshot = second_runner.take_snapshot_after(Step { + name: "take snapshot after", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("┐┌") { + // cursor is back in the first tab + step_is_complete = true; + } + step_is_complete + } + + }); + let first_runner_snapshot = first_runner.take_snapshot_after(Step { + name: "take snapshot after", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("┐┌") { + // cursor is back in the first tab + step_is_complete = true; + } + step_is_complete + } + + }); if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 { test_attempts -= 1; continue; } else { - last_snapshot = Some(last_test_snapshot); - break; - } - } - match last_snapshot { - Some(last_snapshot) => { - assert_snapshot!(last_snapshot); + break (first_runner_snapshot, second_runner_snapshot); } - None => { - panic!("test timed out before completing"); - } - } + }; + assert_snapshot!(first_runner_snapshot); + assert_snapshot!(second_runner_snapshot); } diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index db5c4685bf..2673a72bd2 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -1,4 +1,5 @@ use zellij_tile::data::Palette; +use std::sync::{Arc, Mutex}; use zellij_server::panes::TerminalPane; use zellij_utils::pane_size::{Dimension, PaneGeom, Size}; @@ -16,6 +17,7 @@ const CONNECTION_STRING: &str = "127.0.0.1:2222"; const CONNECTION_USERNAME: &str = "test"; const CONNECTION_PASSWORD: &str = "test"; const SESSION_NAME: &str = "e2e-test"; +const RETRIES: usize = 10; fn ssh_connect() -> ssh2::Session { let tcp = TcpStream::connect(CONNECTION_STRING).unwrap(); @@ -24,7 +26,16 @@ fn ssh_connect() -> ssh2::Session { sess.handshake().unwrap(); sess.userauth_password(CONNECTION_USERNAME, CONNECTION_PASSWORD) .unwrap(); - sess.set_timeout(3000); + sess +} + +fn ssh_connect_without_timeout() -> ssh2::Session { + let tcp = TcpStream::connect(CONNECTION_STRING).unwrap(); + let mut sess = Session::new().unwrap(); + sess.set_tcp_stream(tcp); + sess.handshake().unwrap(); + sess.userauth_password(CONNECTION_USERNAME, CONNECTION_PASSWORD) + .unwrap(); sess } @@ -107,6 +118,69 @@ fn start_zellij_with_layout(channel: &mut ssh2::Channel, layout_path: &str) { channel.flush().unwrap(); } +fn read_from_channel(channel: &Arc>, last_snapshot: &Arc>, cursor_coordinates: &Arc>, pane_geom: &PaneGeom) -> (Arc>,std::thread::JoinHandle<()>) { + let should_keep_running = Arc::new(Mutex::new(true)); + let thread = std::thread::Builder::new() + .name("read_thread".into()) + .spawn({ + let should_keep_running = should_keep_running.clone(); + let channel = channel.clone(); + let last_snapshot = last_snapshot.clone(); + let cursor_coordinates = cursor_coordinates.clone(); + let mut vte_parser = vte::Parser::new(); + let mut terminal_output = TerminalPane::new(0, *pane_geom, Palette::default(), 0); // 0 is the pane index + let mut retries_left = 3; + move || { + let mut should_sleep = false; + loop { + if !*should_keep_running.lock().unwrap() { + break; + } + if should_sleep { + std::thread::sleep(std::time::Duration::from_millis(10)); + should_sleep = false; + } + let mut buf = [0u8; 1280000]; + match channel.lock().unwrap().read(&mut buf) { + Ok(0) => { + let current_snapshot = take_snapshot(&mut terminal_output); + let mut last_snapshot = last_snapshot.lock().unwrap(); + *cursor_coordinates.lock().unwrap() = terminal_output.cursor_coordinates().unwrap_or((0, 0)); + *last_snapshot = current_snapshot; + should_sleep = true; + } + Ok(count) => { + for byte in buf.iter().take(count) { + vte_parser + .advance(&mut terminal_output.grid, *byte); + } + let current_snapshot = take_snapshot(&mut terminal_output); + let mut last_snapshot = last_snapshot.lock().unwrap(); + *cursor_coordinates.lock().unwrap() = terminal_output.grid.cursor_coordinates().unwrap_or((0, 0)); + *last_snapshot = current_snapshot; + should_sleep = true; + } + Err(e) => { + if e.kind() == std::io::ErrorKind::WouldBlock { + let current_snapshot = take_snapshot(&mut terminal_output); + let mut last_snapshot = last_snapshot.lock().unwrap(); + *cursor_coordinates.lock().unwrap() = terminal_output.cursor_coordinates().unwrap_or((0, 0)); + *last_snapshot = current_snapshot; + should_sleep = true; + } else if retries_left > 0 { + retries_left -= 1; + } else { + break; + } + } + } + } + } + }) + .unwrap(); + (should_keep_running, thread) +} + pub fn take_snapshot(terminal_output: &mut TerminalPane) -> String { let output_lines = terminal_output.read_buffer_as_lines(); let cursor_coordinates = terminal_output.cursor_coordinates(); @@ -128,42 +202,42 @@ pub fn take_snapshot(terminal_output: &mut TerminalPane) -> String { snapshot } -pub struct RemoteTerminal<'a> { - channel: &'a mut ssh2::Channel, +pub struct RemoteTerminal { + channel: Arc>, cursor_x: usize, cursor_y: usize, - current_snapshot: String, + last_snapshot: Arc>, } -impl<'a> std::fmt::Debug for RemoteTerminal<'a> { +impl std::fmt::Debug for RemoteTerminal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "cursor x: {}\ncursor_y: {}\ncurrent_snapshot:\n{}", - self.cursor_x, self.cursor_y, self.current_snapshot + self.cursor_x, self.cursor_y, *self.last_snapshot.lock().unwrap() ) } } -impl<'a> RemoteTerminal<'a> { +impl RemoteTerminal { pub fn cursor_position_is(&self, x: usize, y: usize) -> bool { x == self.cursor_x && y == self.cursor_y } pub fn tip_appears(&self) -> bool { - self.current_snapshot.contains("Tip:") + self.last_snapshot.lock().unwrap().contains("Tip:") } pub fn status_bar_appears(&self) -> bool { - self.current_snapshot.contains("Ctrl +") + self.last_snapshot.lock().unwrap().contains("Ctrl +") } pub fn snapshot_contains(&self, text: &str) -> bool { - self.current_snapshot.contains(text) + self.last_snapshot.lock().unwrap().contains(text) } #[allow(unused)] pub fn current_snapshot(&self) -> String { // convenience method for writing tests, // this should only be used when developing, // please prefer "snapsht_contains" instead - self.current_snapshot.clone() + self.last_snapshot.lock().unwrap().clone() } #[allow(unused)] pub fn current_cursor_position(&self) -> String { @@ -173,21 +247,23 @@ impl<'a> RemoteTerminal<'a> { format!("x: {}, y: {}", self.cursor_x, self.cursor_y) } pub fn send_key(&mut self, key: &[u8]) { - self.channel.write_all(key).unwrap(); - self.channel.flush().unwrap(); + let mut channel = self.channel.lock().unwrap(); + channel.write_all(key).unwrap(); + channel.flush().unwrap(); } pub fn change_size(&mut self, cols: u32, rows: u32) { self.channel + .lock() + .unwrap() .request_pty_size(cols, rows, Some(cols), Some(rows)) .unwrap(); } pub fn attach_to_original_session(&mut self) { - self.channel - .write_all( - format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(), - ) - .unwrap(); - self.channel.flush().unwrap(); + let mut channel = self.channel.lock().unwrap(); + channel.write_all( + format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(), + ).unwrap(); + channel.flush().unwrap(); } } @@ -200,26 +276,21 @@ pub struct Step { pub struct RemoteRunner { steps: Vec, current_step_index: usize, - vte_parser: vte::Parser, - terminal_output: TerminalPane, - channel: ssh2::Channel, - test_name: &'static str, + channel: Arc>, currently_running_step: Option, retries_left: usize, - win_size: Size, - layout_file_name: Option<&'static str>, - without_frames: bool, - session_name: Option, - attach_to_existing: bool, + retry_pause_ms: usize, panic_on_no_retries_left: bool, + last_snapshot: Arc>, + cursor_coordinates: Arc>, // x, y + reader_thread: (Arc>, std::thread::JoinHandle<()>), pub test_timed_out: bool, } impl RemoteRunner { - pub fn new(test_name: &'static str, win_size: Size) -> Self { + pub fn new(win_size: Size) -> Self { let sess = ssh_connect(); let mut channel = sess.channel_session().unwrap(); - let vte_parser = vte::Parser::new(); let mut rows = Dimension::fixed(win_size.rows); let mut cols = Dimension::fixed(win_size.cols); rows.set_inner(win_size.rows); @@ -230,35 +301,40 @@ impl RemoteRunner { rows, cols, }; - let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index setup_remote_environment(&mut channel, win_size); start_zellij(&mut channel); + let channel = Arc::new(Mutex::new(channel)); + let last_snapshot = Arc::new(Mutex::new(String::new())); + let cursor_coordinates = Arc::new(Mutex::new((0, 0))); + sess.set_blocking(false); + let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, - terminal_output, - vte_parser, - test_name, currently_running_step: None, current_step_index: 0, - retries_left: 10, - win_size, - layout_file_name: None, - without_frames: false, - session_name: None, - attach_to_existing: false, + retries_left: RETRIES, + retry_pause_ms: 100, test_timed_out: false, panic_on_no_retries_left: true, + last_snapshot, + cursor_coordinates, + reader_thread, } } + pub fn kill_running_sessions(win_size: Size) { + let sess = ssh_connect(); + let mut channel = sess.channel_session().unwrap(); + setup_remote_environment(&mut channel, win_size); + start_zellij(&mut channel); + } pub fn new_with_session_name( - test_name: &'static str, win_size: Size, session_name: &str, ) -> Self { - let sess = ssh_connect(); + // notice that this method does not have a timeout, so use with caution! + let sess = ssh_connect_without_timeout(); let mut channel = sess.channel_session().unwrap(); - let vte_parser = vte::Parser::new(); let mut rows = Dimension::fixed(win_size.rows); let mut cols = Dimension::fixed(win_size.cols); rows.set_inner(win_size.rows); @@ -269,35 +345,33 @@ impl RemoteRunner { rows, cols, }; - let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index setup_remote_environment(&mut channel, win_size); start_zellij_in_session(&mut channel, &session_name); + let channel = Arc::new(Mutex::new(channel)); + let last_snapshot = Arc::new(Mutex::new(String::new())); + let cursor_coordinates = Arc::new(Mutex::new((0, 0))); + sess.set_blocking(false); + let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, - terminal_output, - vte_parser, - test_name, currently_running_step: None, current_step_index: 0, - retries_left: 10, - win_size, - layout_file_name: None, - without_frames: false, - session_name: Some(String::from(session_name)), - attach_to_existing: false, + retries_left: RETRIES, + retry_pause_ms: 100, test_timed_out: false, panic_on_no_retries_left: true, + last_snapshot, + cursor_coordinates, + reader_thread, } } pub fn new_existing_session( - test_name: &'static str, win_size: Size, session_name: &str, ) -> Self { - let sess = ssh_connect(); + let sess = ssh_connect_without_timeout(); let mut channel = sess.channel_session().unwrap(); - let vte_parser = vte::Parser::new(); let mut rows = Dimension::fixed(win_size.rows); let mut cols = Dimension::fixed(win_size.cols); rows.set_inner(win_size.rows); @@ -308,31 +382,30 @@ impl RemoteRunner { rows, cols, }; - let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index setup_remote_environment(&mut channel, win_size); attach_to_existing_session(&mut channel, &session_name); + let channel = Arc::new(Mutex::new(channel)); + let last_snapshot = Arc::new(Mutex::new(String::new())); + let cursor_coordinates = Arc::new(Mutex::new((0, 0))); + sess.set_blocking(false); + let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, - terminal_output, - vte_parser, - test_name, currently_running_step: None, current_step_index: 0, - retries_left: 10, - win_size, - layout_file_name: None, - without_frames: false, - session_name: Some(String::from(session_name)), - attach_to_existing: true, + retries_left: RETRIES, + retry_pause_ms: 100, test_timed_out: false, panic_on_no_retries_left: true, + last_snapshot, + cursor_coordinates, + reader_thread, } } - pub fn new_without_frames(test_name: &'static str, win_size: Size) -> Self { + pub fn new_without_frames(win_size: Size) -> Self { let sess = ssh_connect(); let mut channel = sess.channel_session().unwrap(); - let vte_parser = vte::Parser::new(); let mut rows = Dimension::fixed(win_size.rows); let mut cols = Dimension::fixed(win_size.cols); rows.set_inner(win_size.rows); @@ -343,36 +416,34 @@ impl RemoteRunner { rows, cols, }; - let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index setup_remote_environment(&mut channel, win_size); start_zellij_without_frames(&mut channel); + let channel = Arc::new(Mutex::new(channel)); + let last_snapshot = Arc::new(Mutex::new(String::new())); + let cursor_coordinates = Arc::new(Mutex::new((0, 0))); + sess.set_blocking(false); + let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, - terminal_output, - vte_parser, - test_name, currently_running_step: None, current_step_index: 0, - retries_left: 10, - win_size, - layout_file_name: None, - without_frames: true, - session_name: None, - attach_to_existing: false, + retries_left: RETRIES, + retry_pause_ms: 100, test_timed_out: false, panic_on_no_retries_left: true, + last_snapshot, + cursor_coordinates, + reader_thread, } } pub fn new_with_layout( - test_name: &'static str, win_size: Size, layout_file_name: &'static str, ) -> Self { let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name); let sess = ssh_connect(); let mut channel = sess.channel_session().unwrap(); - let vte_parser = vte::Parser::new(); let mut rows = Dimension::fixed(win_size.rows); let mut cols = Dimension::fixed(win_size.cols); rows.set_inner(win_size.rows); @@ -383,162 +454,103 @@ impl RemoteRunner { rows, cols, }; - let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index setup_remote_environment(&mut channel, win_size); start_zellij_with_layout(&mut channel, &remote_path.to_string_lossy()); + let channel = Arc::new(Mutex::new(channel)); + let last_snapshot = Arc::new(Mutex::new(String::new())); + let cursor_coordinates = Arc::new(Mutex::new((0, 0))); + sess.set_blocking(false); + let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, - terminal_output, - vte_parser, - test_name, currently_running_step: None, current_step_index: 0, - retries_left: 10, - win_size, - layout_file_name: Some(layout_file_name), - without_frames: false, - session_name: None, - attach_to_existing: false, + retries_left: RETRIES, + retry_pause_ms: 100, test_timed_out: false, panic_on_no_retries_left: true, + last_snapshot, + cursor_coordinates, + reader_thread, } } pub fn dont_panic(mut self) -> Self { self.panic_on_no_retries_left = false; self } + pub fn retry_pause_ms(mut self, retry_pause_ms: usize) -> Self { + self.retry_pause_ms = retry_pause_ms; + self + } pub fn add_step(mut self, step: Step) -> Self { self.steps.push(step); self } - pub fn replace_steps(&mut self, steps: Vec) { - self.steps = steps; - } - fn display_informative_error(&mut self) { - let test_name = self.test_name; - let current_step_name = self.currently_running_step.as_ref().cloned(); - match current_step_name { - Some(current_step) => { - let remote_terminal = self.current_remote_terminal_state(); - eprintln!("Timed out waiting for data on the SSH channel for test {}. Was waiting for step: {}", test_name, current_step); - eprintln!("{:?}", remote_terminal); - } - None => { - let remote_terminal = self.current_remote_terminal_state(); - eprintln!("Timed out waiting for data on the SSH channel for test {}. Haven't begun running steps yet.", test_name); - eprintln!("{:?}", remote_terminal); - } - } - } - pub fn get_current_snapshot(&mut self) -> String { - take_snapshot(&mut self.terminal_output) - } - fn current_remote_terminal_state(&mut self) -> RemoteTerminal { - let current_snapshot = self.get_current_snapshot(); - let (cursor_x, cursor_y) = self.terminal_output.cursor_coordinates().unwrap_or((0, 0)); - RemoteTerminal { - cursor_x, - cursor_y, - current_snapshot, - channel: &mut self.channel, - } - } pub fn run_next_step(&mut self) { if let Some(next_step) = self.steps.get(self.current_step_index) { - let current_snapshot = take_snapshot(&mut self.terminal_output); - let (cursor_x, cursor_y) = self.terminal_output.cursor_coordinates().unwrap_or((0, 0)); + let (cursor_x, cursor_y) = *self.cursor_coordinates.lock().unwrap(); let remote_terminal = RemoteTerminal { cursor_x, cursor_y, - current_snapshot, - channel: &mut self.channel, + last_snapshot: self.last_snapshot.clone(), + channel: self.channel.clone(), }; let instruction = next_step.instruction; self.currently_running_step = Some(String::from(next_step.name)); if instruction(remote_terminal) { + self.retries_left = RETRIES; self.current_step_index += 1; + } else { + self.retries_left -= 1; + std::thread::sleep(std::time::Duration::from_millis(self.retry_pause_ms as u64)); } } } pub fn steps_left(&self) -> bool { self.steps.get(self.current_step_index).is_some() } - fn restart_test(&mut self) -> String { - if let Some(layout_file_name) = self.layout_file_name.as_ref() { - // let mut new_runner = RemoteRunner::new_with_layout(self.test_name, self.win_size, Path::new(&local_layout_path), session_name); - let mut new_runner = - RemoteRunner::new_with_layout(self.test_name, self.win_size, layout_file_name); - new_runner.retries_left = self.retries_left - 1; - new_runner.replace_steps(self.steps.clone()); - drop(std::mem::replace(self, new_runner)); - self.run_all_steps() - } else if self.without_frames { - let mut new_runner = RemoteRunner::new_without_frames(self.test_name, self.win_size); - new_runner.retries_left = self.retries_left - 1; - new_runner.replace_steps(self.steps.clone()); - drop(std::mem::replace(self, new_runner)); - self.run_all_steps() - } else if self.session_name.is_some() { - let mut new_runner = if self.attach_to_existing { - RemoteRunner::new_existing_session( - self.test_name, - self.win_size, - &self.session_name.as_ref().unwrap(), - ) - } else { - RemoteRunner::new_with_session_name( - self.test_name, - self.win_size, - &self.session_name.as_ref().unwrap(), - ) + pub fn take_snapshot_after(&mut self, step: Step) -> String { + let mut retries_left = RETRIES; + let instruction = step.instruction; + loop { + if retries_left == 0 { + self.test_timed_out = true; + return String::from(self.last_snapshot.lock().unwrap().clone()); + } + let (cursor_x, cursor_y) = *self.cursor_coordinates.lock().unwrap(); + let remote_terminal = RemoteTerminal { + cursor_x, + cursor_y, + last_snapshot: self.last_snapshot.clone(), + channel: self.channel.clone(), }; - new_runner.retries_left = self.retries_left - 1; - new_runner.replace_steps(self.steps.clone()); - drop(std::mem::replace(self, new_runner)); - self.run_all_steps() - } else { - let mut new_runner = RemoteRunner::new(self.test_name, self.win_size); - new_runner.retries_left = self.retries_left - 1; - new_runner.replace_steps(self.steps.clone()); - drop(std::mem::replace(self, new_runner)); - self.run_all_steps() + if instruction(remote_terminal) { + return String::from(self.last_snapshot.lock().unwrap().clone()) + } else { + retries_left -= 1; + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } } } - pub fn run_all_steps(&mut self) -> String { - // returns the last snapshot + pub fn run_all_steps(&mut self) { loop { - let mut buf = [0u8; 1024]; - match self.channel.read(&mut buf) { - Ok(0) => break, - Ok(_count) => { - for byte in buf.iter() { - self.vte_parser - .advance(&mut self.terminal_output.grid, *byte); - } - self.run_next_step(); - if !self.steps_left() { - break; - } - } - Err(e) => { - if self.retries_left > 0 { - return self.restart_test(); - } - self.test_timed_out = true; - if self.panic_on_no_retries_left { - self.display_informative_error(); - panic!("timed out waiting for test: {:?}", e); - } - } + self.run_next_step(); + if !self.steps_left() { + break; + } else if self.retries_left == 0 { + self.test_timed_out = true; + break; } } - take_snapshot(&mut self.terminal_output) } } impl Drop for RemoteRunner { fn drop(&mut self) { - let _ = self.channel.close(); + let _ = self.channel.lock().unwrap().close(); + let reader_thread_running = &mut self.reader_thread.0; + *reader_thread_running.lock().unwrap() = false; } } diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap index 796a596b9f..ed01a3b93a 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap @@ -22,4 +22,4 @@ expression: last_snapshot │ │ └──────┘ Ctrl + - ... + diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap index bbb5b068ac..4d02c0f6e1 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap @@ -1,29 +1,29 @@ --- -source: src/tests/integration/e2e.rs +source: src/tests/e2e/cases.rs expression: last_snapshot --- - Zellij  Tab #1  - -$ │$ █ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - │ - - Ctrl + LOCK 

PANE  TAB  RESIZE  SCROLL  SESSION  QUIT  - Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes. + Zellij (e2e-test)  Tab #1  +┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +│$ █ ││$ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + Ctrl + LOCK 

PANE  TAB  RESIZE  SCROLL  SESSION  QUIT  + <←↓↑→> Move focus / New / Close / Rename / Sync / Toggle / Select pane diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap new file mode 100644 index 0000000000..92d609ecd3 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: last_second_runner_snapshot + +--- + Zellij (mirrored_sessions)  Tab #1  Tab #2  +┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +│$ █ ││$ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + Ctrl + LOCK 

PANE  TAB  RESIZE  SCROLL  SESSION  QUIT  + <←↓↑→> Move focus / New / Close / Rename / Sync / Toggle / Select pane diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap index 3223680c87..a51d1a35b2 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap @@ -1,11 +1,11 @@ --- source: src/tests/e2e/cases.rs -expression: last_snapshot +expression: last_first_runner_snapshot --- - Zellij (mirrored_sessions)  Tab #1  + Zellij (mirrored_sessions)  Tab #1  Tab #2  ┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ -│$ ││$ █ │ +│$ █ ││$ │ │ ││ │ │ ││ │ │ ││ │ @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  SCROLL  SESSION  QUIT  - Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes. + <←↓↑→> Move focus / New / Close / Rename / Sync / Toggle / Select pane diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap new file mode 100644 index 0000000000..ba8356edd8 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: last_snapshot + +--- + Zellij (e2e-test)  Tab #1  +┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│$ █ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + Ctrl + LOCK 

PANE  TAB  RESIZE  SCROLL  SESSION  QUIT  + Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes. From c4cfd76ef0ba0dfff01ff065ebe5147af90d5ff5 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 11 Nov 2021 20:21:25 +0100 Subject: [PATCH 16/18] refactor(tab): change structs in tabs and terminal panes to support multiple users --- zellij-server/src/screen.rs | 66 +- zellij-server/src/tab.rs | 462 ++++++++----- zellij-server/src/unit/tab_tests.rs | 984 ++++++++++++++-------------- 3 files changed, 825 insertions(+), 687 deletions(-) diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 7beed8d828..b4faf250f0 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -328,7 +328,7 @@ impl Screen { let mut output = Output::default(); let mut tabs_to_close = vec![]; for (tab_index, tab) in self.tabs.iter_mut() { - if tab.get_active_pane().is_some() { + if tab.has_active_panes() { tab.render(&mut output); } else { tabs_to_close.push(*tab_index); @@ -491,20 +491,20 @@ impl Screen { { self.get_active_tab_mut(client_id) .unwrap() - .clear_active_terminal_scroll(); + .clear_active_terminal_scroll(client_id); } self.colors = mode_info.palette; self.mode_info = mode_info; for tab in self.tabs.values_mut() { tab.mode_info = self.mode_info.clone(); - tab.mark_active_pane_for_rerender(); + tab.mark_active_pane_for_rerender(client_id); } } pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) { if !self .get_active_tab_mut(client_id) .unwrap() - .move_focus_left() + .move_focus_left(client_id) { self.switch_tab_prev(client_id); } @@ -513,7 +513,7 @@ impl Screen { if !self .get_active_tab_mut(client_id) .unwrap() - .move_focus_right() + .move_focus_right(client_id) { self.switch_tab_next(client_id); } @@ -577,10 +577,10 @@ pub(crate) fn screen_thread_main( ScreenInstruction::NewPane(pid, client_or_tab_index) => { match client_or_tab_index { ClientOrTabIndex::ClientId(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().new_pane(pid); + screen.get_active_tab_mut(client_id).unwrap().new_pane(pid, Some(client_id)); } ClientOrTabIndex::TabIndex(tab_index) => { - screen.tabs.get_mut(&tab_index).unwrap().new_pane(pid); + screen.tabs.get_mut(&tab_index).unwrap().new_pane(pid, None); } }; screen @@ -596,7 +596,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .horizontal_split(pid); + .horizontal_split(pid, client_id); screen .bus .senders @@ -610,7 +610,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .vertical_split(pid); + .vertical_split(pid, client_id); screen .bus .senders @@ -624,31 +624,31 @@ pub(crate) fn screen_thread_main( let active_tab = screen.get_active_tab_mut(client_id).unwrap(); match active_tab.is_sync_panes_active() { true => active_tab.write_to_terminals_on_current_tab(bytes), - false => active_tab.write_to_active_terminal(bytes), + false => active_tab.write_to_active_terminal(bytes, client_id), } } ScreenInstruction::ResizeLeft(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_left(); + screen.get_active_tab_mut(client_id).unwrap().resize_left(client_id); screen.render(); } ScreenInstruction::ResizeRight(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_right(); + screen.get_active_tab_mut(client_id).unwrap().resize_right(client_id); screen.render(); } ScreenInstruction::ResizeDown(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_down(); + screen.get_active_tab_mut(client_id).unwrap().resize_down(client_id); screen.render(); } ScreenInstruction::ResizeUp(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_up(); + screen.get_active_tab_mut(client_id).unwrap().resize_up(client_id); screen.render(); } ScreenInstruction::SwitchFocus(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().move_focus(); + screen.get_active_tab_mut(client_id).unwrap().move_focus(client_id); screen.render(); } @@ -656,7 +656,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .focus_next_pane(); + .focus_next_pane(client_id); screen.render(); } @@ -664,7 +664,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .focus_previous_pane(); + .focus_previous_pane(client_id); screen.render(); } @@ -672,7 +672,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .move_focus_left(); + .move_focus_left(client_id); screen.render(); } @@ -690,7 +690,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .move_focus_down(); + .move_focus_down(client_id); screen.render(); } @@ -698,7 +698,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .move_focus_right(); + .move_focus_right(client_id); screen.render(); } @@ -716,7 +716,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .move_focus_up(); + .move_focus_up(client_id); screen.render(); } @@ -724,7 +724,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .scroll_active_terminal_up(); + .scroll_active_terminal_up(client_id); screen.render(); } @@ -740,7 +740,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .scroll_active_terminal_down(); + .scroll_active_terminal_down(client_id); screen.render(); } @@ -756,7 +756,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .scroll_active_terminal_to_bottom(); + .scroll_active_terminal_to_bottom(client_id); screen.render(); } @@ -764,7 +764,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .scroll_active_terminal_up_page(); + .scroll_active_terminal_up_page(client_id); screen.render(); } @@ -772,7 +772,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .scroll_active_terminal_down_page(); + .scroll_active_terminal_down_page(client_id); screen.render(); } @@ -780,7 +780,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .clear_active_terminal_scroll(); + .clear_active_terminal_scroll(client_id); screen.render(); } @@ -788,7 +788,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .close_focused_pane(); + .close_focused_pane(client_id); screen.update_tabs(); // update_tabs eventually calls render through the plugin thread } ScreenInstruction::SetSelectable(id, selectable, tab_index) => { @@ -825,7 +825,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .toggle_active_pane_fullscreen(); + .toggle_active_pane_fullscreen(client_id); screen.update_tabs(); screen.render(); @@ -919,7 +919,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .handle_left_click(&point); + .handle_left_click(&point, client_id); screen.render(); } @@ -927,7 +927,7 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .handle_mouse_release(&point); + .handle_mouse_release(&point, client_id); screen.render(); } @@ -935,12 +935,12 @@ pub(crate) fn screen_thread_main( screen .get_active_tab_mut(client_id) .unwrap() - .handle_mouse_hold(&point); + .handle_mouse_hold(&point, client_id); screen.render(); } ScreenInstruction::Copy(client_id) => { - screen.get_active_tab(client_id).unwrap().copy_selection(); + screen.get_active_tab(client_id).unwrap().copy_selection(client_id); screen.render(); } diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index 8f162a457a..e10d674c9f 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -120,7 +120,7 @@ pub(crate) struct Tab { pub name: String, panes: BTreeMap>, pub panes_to_hide: HashSet, - active_terminal: Option, + active_panes: HashMap, max_panes: Option, viewport: Viewport, // includes all non-UI panes display_area: Size, // includes all panes (including eg. the status bar and tab bar in the default layout) @@ -305,7 +305,7 @@ impl Tab { name, max_panes, panes_to_hide: HashSet::new(), - active_terminal: None, + active_panes: HashMap::new(), viewport: display_area.into(), display_area, fullscreen_is_active: false, @@ -413,18 +413,47 @@ impl Tab { } self.set_pane_frames(self.draw_pane_frames); // This is the end of the nasty viewport hack... - // FIXME: Active / new / current terminal, should be pane - self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next(); + let next_selectable_pane_id = self.panes.iter().filter(|(_id, pane)| pane.selectable()).map(|(id, _)| id.to_owned()).next(); + match next_selectable_pane_id { + Some(active_pane_id) => { + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, active_pane_id); + } + }, + None => { + // this is very likely a configuration error (layout with no selectable panes) + self.active_panes.clear(); + } + } } pub fn add_client(&mut self, client_id: ClientId) { - self.connected_clients.insert(client_id); + match self.connected_clients.iter().next() { + Some(first_client_id) => { + let first_active_pane_id = *self.active_panes.get(first_client_id).unwrap(); + self.connected_clients.insert(client_id); + self.active_panes.insert(client_id, first_active_pane_id); + }, + None => { + let mut pane_ids: Vec = self.panes.keys().copied().collect(); + if pane_ids.is_empty() { + // no panes here, bye bye + return; + } + pane_ids.sort(); // TODO: make this predictable + let first_pane_id = pane_ids.iter().next().unwrap(); + self.connected_clients.insert(client_id); + self.active_panes.insert(client_id, *first_pane_id); + } + + } // TODO: we might be able to avoid this, we do this so that newly connected clients will // necessarily get a full render self.set_force_render(); } pub fn add_multiple_clients(&mut self, client_ids: &[ClientId]) { for client_id in client_ids { - self.connected_clients.insert(*client_id); + self.add_client(*client_id); } } pub fn remove_client(&mut self, client_id: ClientId) { @@ -433,10 +462,10 @@ impl Tab { pub fn drain_connected_clients(&mut self) -> Vec { self.connected_clients.drain().collect() } - pub fn new_pane(&mut self, pid: PaneId) { + pub fn new_pane(&mut self, pid: PaneId, client_id: Option) { self.close_down_to_max_terminals(); if self.fullscreen_is_active { - self.toggle_active_pane_fullscreen(); + self.unset_fullscreen(); } // TODO: check minimum size of active terminal @@ -503,16 +532,23 @@ impl Tab { } } } - self.active_terminal = Some(pid); + if let Some(client_id) = client_id { + // right now we administratively change focus of all clients until the + // mirroring/multiplayer situation is sorted out + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, pid); + } + } } - pub fn horizontal_split(&mut self, pid: PaneId) { + pub fn horizontal_split(&mut self, pid: PaneId, client_id: ClientId) { self.close_down_to_max_terminals(); if self.fullscreen_is_active { - self.toggle_active_pane_fullscreen(); + self.toggle_active_pane_fullscreen(client_id); } if let PaneId::Terminal(term_pid) = pid { let next_terminal_position = self.get_next_terminal_position(); - let active_pane_id = &self.get_active_pane_id().unwrap(); + let active_pane_id = &self.get_active_pane_id(client_id).unwrap(); let active_pane = self.panes.get_mut(active_pane_id).unwrap(); if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 { self.senders @@ -531,20 +567,27 @@ impl Tab { ); active_pane.set_geom(top_winsize); self.panes.insert(pid, Box::new(new_terminal)); - self.active_terminal = Some(pid); + + // right now we administratively change focus of all clients until the + // mirroring/multiplayer situation is sorted out + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, pid); + } + self.relayout_tab(Direction::Vertical); } } } - pub fn vertical_split(&mut self, pid: PaneId) { + pub fn vertical_split(&mut self, pid: PaneId, client_id: ClientId) { self.close_down_to_max_terminals(); if self.fullscreen_is_active { - self.toggle_active_pane_fullscreen(); + self.toggle_active_pane_fullscreen(client_id); } if let PaneId::Terminal(term_pid) = pid { // TODO: check minimum size of active terminal let next_terminal_position = self.get_next_terminal_position(); - let active_pane_id = &self.get_active_pane_id().unwrap(); + let active_pane_id = &self.get_active_pane_id(client_id).unwrap(); let active_pane = self.panes.get_mut(active_pane_id).unwrap(); if active_pane.cols() < MIN_TERMINAL_WIDTH * 2 { self.senders @@ -559,19 +602,33 @@ impl Tab { active_pane.set_geom(left_winsize); self.panes.insert(pid, Box::new(new_terminal)); } - self.active_terminal = Some(pid); + + // right now we administratively change focus of all clients until the + // mirroring/multiplayer situation is sorted out + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, pid); + } + self.relayout_tab(Direction::Horizontal); } } - pub fn get_active_pane(&self) -> Option<&dyn Pane> { - self.get_active_pane_id() + pub fn has_active_panes(&self) -> bool { + // a tab without active panes is a dead tab and should close + // a pane can be active even if there are no connected clients, + // we remember that pane for one the client focuses the tab next + !self.active_panes.is_empty() + } + pub fn get_active_pane(&self, client_id: ClientId) -> Option<&dyn Pane> { + self.get_active_pane_id(client_id) .and_then(|ap| self.panes.get(&ap).map(Box::as_ref)) } - fn get_active_pane_id(&self) -> Option { - self.active_terminal + fn get_active_pane_id(&self, client_id: ClientId) -> Option { + // TODO: why do we need this? + self.active_panes.get(&client_id).copied() } - fn get_active_terminal_id(&self) -> Option { - if let Some(PaneId::Terminal(pid)) = self.active_terminal { + fn get_active_terminal_id(&self, client_id: ClientId) -> Option { + if let Some(PaneId::Terminal(pid)) = self.active_panes.get(&client_id).copied() { Some(pid) } else { None @@ -625,8 +682,8 @@ impl Tab { self.write_to_pane_id(input_bytes.clone(), pane_id); }); } - pub fn write_to_active_terminal(&mut self, input_bytes: Vec) { - self.write_to_pane_id(input_bytes, self.get_active_pane_id().unwrap()); + pub fn write_to_active_terminal(&mut self, input_bytes: Vec, client_id: ClientId) { + self.write_to_pane_id(input_bytes, self.get_active_pane_id(client_id).unwrap()); } pub fn write_to_pane_id(&mut self, input_bytes: Vec, pane_id: PaneId) { match pane_id { @@ -649,9 +706,9 @@ impl Tab { } } } - pub fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> { + pub fn get_active_terminal_cursor_position(&self, client_id: ClientId) -> Option<(usize, usize)> { // (x, y) - let active_terminal = &self.get_active_pane()?; + let active_terminal = &self.get_active_pane(client_id)?; active_terminal .cursor_coordinates() .map(|(x_in_terminal, y_in_terminal)| { @@ -660,26 +717,36 @@ impl Tab { (x, y) }) } - pub fn toggle_active_pane_fullscreen(&mut self) { - if let Some(active_pane_id) = self.get_active_pane_id() { + pub fn unset_fullscreen(&mut self) { + if self.fullscreen_is_active { + let first_client_id = self.connected_clients.iter().next().unwrap(); // this is a temporary hack until we fix the ui for multiple clients + let active_pane_id = self.active_panes.get(first_client_id).unwrap(); + for terminal_id in self.panes_to_hide.iter() { + let pane = self.panes.get_mut(terminal_id).unwrap(); + pane.set_should_render(true); + pane.set_should_render_boundaries(true); + } + let viewport_pane_ids: Vec<_> = self + .get_pane_ids() + .into_iter() + .filter(|id| !self.is_inside_viewport(id)) + .collect(); + for pid in viewport_pane_ids { + let viewport_pane = self.panes.get_mut(&pid).unwrap(); + viewport_pane.reset_size_and_position_override(); + } + self.panes_to_hide.clear(); + let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); + active_terminal.reset_size_and_position_override(); + self.set_force_render(); + self.resize_whole_tab(self.display_area); + self.toggle_fullscreen_is_active(); + } + } + pub fn toggle_active_pane_fullscreen(&mut self, client_id: ClientId) { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { if self.fullscreen_is_active { - for terminal_id in self.panes_to_hide.iter() { - let pane = self.panes.get_mut(terminal_id).unwrap(); - pane.set_should_render(true); - pane.set_should_render_boundaries(true); - } - let viewport_pane_ids: Vec<_> = self - .get_pane_ids() - .into_iter() - .filter(|id| !self.is_inside_viewport(id)) - .collect(); - for pid in viewport_pane_ids { - let viewport_pane = self.panes.get_mut(&pid).unwrap(); - viewport_pane.reset_size_and_position_override(); - } - self.panes_to_hide.clear(); - let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); - active_terminal.reset_size_and_position_override(); + self.unset_fullscreen(); } else { let panes = self.get_panes(); let pane_ids_to_hide = panes.filter_map(|(&id, _pane)| { @@ -714,10 +781,10 @@ impl Tab { }; active_terminal.get_geom_override(full_screen_geom); } + self.set_force_render(); + self.resize_whole_tab(self.display_area); + self.toggle_fullscreen_is_active(); } - self.set_force_render(); - self.resize_whole_tab(self.display_area); - self.toggle_fullscreen_is_active(); } } pub fn is_fullscreen_active(&self) -> bool { @@ -739,12 +806,9 @@ impl Tab { pub fn toggle_sync_panes_is_active(&mut self) { self.synchronize_is_active = !self.synchronize_is_active; } - pub fn mark_active_pane_for_rerender(&mut self) { - if let Some(active_terminal) = self - .active_terminal - .and_then(|active_terminal_id| self.panes.get_mut(&active_terminal_id)) - { - active_terminal.set_should_render(true) + pub fn mark_active_pane_for_rerender(&mut self, client_id: ClientId) { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + self.panes.get_mut(&active_pane_id).unwrap().set_should_render(true) } } pub fn set_pane_frames(&mut self, draw_pane_frames: bool) { @@ -790,14 +854,14 @@ impl Tab { } } pub fn render(&mut self, output: &mut Output) { - if self.connected_clients.is_empty() || self.active_terminal.is_none() { + if self.connected_clients.is_empty() || self.active_panes.is_empty() { return; } for connected_client in self.connected_clients.iter() { // TODO: move this out of the render function self.senders .send_to_pty(PtyInstruction::UpdateActivePane( - self.active_terminal, + self.active_panes.get(connected_client).copied(), *connected_client, )) .unwrap(); @@ -811,9 +875,10 @@ impl Tab { output.push_str_to_all_clients(clear_display); self.should_clear_display_before_rendering = false; } + let first_client_id = self.connected_clients.iter().next().unwrap(); // this is a temporary hack until we fix the ui for multiple clients for (_kind, pane) in self.panes.iter_mut() { if !self.panes_to_hide.contains(&pane.pid()) { - match self.active_terminal.unwrap() == pane.pid() { + match self.active_panes.get(first_client_id).copied().unwrap() == pane.pid() { true => { pane.set_active_at(Instant::now()); match self.mode_info.mode { @@ -855,10 +920,10 @@ impl Tab { output.push_str_to_all_clients(&boundaries.vte_output()); } - match self.get_active_terminal_cursor_position() { + match self.get_active_terminal_cursor_position(*first_client_id) { Some((cursor_position_x, cursor_position_y)) => { let show_cursor = "\u{1b}[?25h"; - let change_cursor_shape = self.get_active_pane().unwrap().cursor_shape_csi(); + let change_cursor_shape = self.get_active_pane(*first_client_id).unwrap().cursor_shape_csi(); let goto_cursor_position = &format!( "\u{1b}[{};{}H\u{1b}[m{}", cursor_position_y + 1, @@ -1760,9 +1825,9 @@ impl Tab { self.should_clear_display_before_rendering = true; self.set_pane_frames(self.draw_pane_frames); } - pub fn resize_left(&mut self) { + pub fn resize_left(&mut self, client_id: ClientId) { // TODO: find out by how much we actually reduced and only reduce by that much - if let Some(active_pane_id) = self.get_active_pane_id() { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { if self.can_increase_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT) { self.increase_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT); } else if self.can_reduce_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT) { @@ -1771,9 +1836,9 @@ impl Tab { } self.relayout_tab(Direction::Horizontal); } - pub fn resize_right(&mut self) { + pub fn resize_right(&mut self, client_id: ClientId) { // TODO: find out by how much we actually reduced and only reduce by that much - if let Some(active_pane_id) = self.get_active_pane_id() { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { if self.can_increase_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT) { self.increase_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT); } else if self.can_reduce_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT) { @@ -1782,9 +1847,9 @@ impl Tab { } self.relayout_tab(Direction::Horizontal); } - pub fn resize_down(&mut self) { + pub fn resize_down(&mut self, client_id: ClientId) { // TODO: find out by how much we actually reduced and only reduce by that much - if let Some(active_pane_id) = self.get_active_pane_id() { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { if self.can_increase_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT) { self.increase_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT); } else if self.can_reduce_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT) { @@ -1793,9 +1858,9 @@ impl Tab { } self.relayout_tab(Direction::Vertical); } - pub fn resize_up(&mut self) { + pub fn resize_up(&mut self, client_id: ClientId) { // TODO: find out by how much we actually reduced and only reduce by that much - if let Some(active_pane_id) = self.get_active_pane_id() { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { if self.can_increase_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT) { self.increase_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT); } else if self.can_reduce_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT) { @@ -1804,34 +1869,38 @@ impl Tab { } self.relayout_tab(Direction::Vertical); } - pub fn move_focus(&mut self) { + pub fn move_focus(&mut self, client_id: ClientId) { if !self.has_selectable_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal_id = self.get_active_pane_id().unwrap(); - let terminal_ids: Vec = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations - let active_terminal_id_position = terminal_ids + let current_active_pane_id = self.get_active_pane_id(client_id).unwrap(); + let pane_ids: Vec = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations + let active_pane_id_position = pane_ids .iter() - .position(|id| id == &active_terminal_id) + .position(|id| id == ¤t_active_pane_id) + .unwrap(); + let next_active_pane_id = pane_ids + .get(active_pane_id_position + 1) + .or_else(|| pane_ids.get(0)) + .copied() .unwrap(); - let active_terminal = terminal_ids - .get(active_terminal_id_position + 1) - .or_else(|| terminal_ids.get(0)) - .copied(); - self.active_terminal = active_terminal; + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, next_active_pane_id); + } } - pub fn focus_next_pane(&mut self) { + pub fn focus_next_pane(&mut self, client_id: ClientId) { if !self.has_selectable_panes() { return; } if self.fullscreen_is_active { return; } - let active_pane_id = self.get_active_pane_id().unwrap(); + let active_pane_id = self.get_active_pane_id(client_id).unwrap(); let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { if a_pane.y() == b_pane.y() { @@ -1845,21 +1914,25 @@ impl Tab { .position(|(id, _)| *id == &active_pane_id) // TODO: better .unwrap(); - let active_terminal = panes + let next_active_pane_id = panes .get(active_pane_position + 1) .or_else(|| panes.get(0)) - .map(|p| *p.0); + .map(|p| *p.0) + .unwrap(); - self.active_terminal = active_terminal; + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, next_active_pane_id); + } } - pub fn focus_previous_pane(&mut self) { + pub fn focus_previous_pane(&mut self, client_id: ClientId) { if !self.has_selectable_panes() { return; } if self.fullscreen_is_active { return; } - let active_pane_id = self.get_active_pane_id().unwrap(); + let active_pane_id = self.get_active_pane_id(client_id).unwrap(); let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { if a_pane.y() == b_pane.y() { @@ -1874,23 +1947,26 @@ impl Tab { .position(|(id, _)| *id == &active_pane_id) // TODO: better .unwrap(); - let active_terminal = if active_pane_position == 0 { - Some(*last_pane.0) + let next_active_pane_id = if active_pane_position == 0 { + *last_pane.0 } else { - Some(*panes.get(active_pane_position - 1).unwrap().0) + *panes.get(active_pane_position - 1).unwrap().0 }; - self.active_terminal = active_terminal; + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, next_active_pane_id); + } } // returns a boolean that indicates whether the focus moved - pub fn move_focus_left(&mut self) -> bool { + pub fn move_focus_left(&mut self, client_id: ClientId) -> bool { if !self.has_selectable_panes() { return false; } if self.fullscreen_is_active { return false; } - let active_terminal = self.get_active_pane(); - let updated_active_terminal = if let Some(active) = active_terminal { + let active_pane = self.get_active_pane(client_id); + let updated_active_pane = if let Some(active) = active_pane { let terminals = self.get_selectable_panes(); let next_index = terminals .enumerate() @@ -1904,33 +1980,48 @@ impl Tab { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = - self.panes.get_mut(&self.active_terminal.unwrap()).unwrap(); + self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); - self.active_terminal = Some(p); + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, p); + } return true; } None => Some(active.pid()), } } else { - Some(active_terminal.unwrap().pid()) + Some(active_pane.unwrap().pid()) }; - self.active_terminal = updated_active_terminal; + match updated_active_pane { + Some(updated_active_pane) => { + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, updated_active_pane); + } + }, + None => { + // TODO: can this happen? + self.active_panes.clear(); + } + } + false } - pub fn move_focus_down(&mut self) { + pub fn move_focus_down(&mut self, client_id: ClientId) { if !self.has_selectable_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal = self.get_active_pane(); - let updated_active_terminal = if let Some(active) = active_terminal { - let terminals = self.get_selectable_panes(); - let next_index = terminals + let active_pane = self.get_active_pane(client_id); + let updated_active_pane = if let Some(active) = active_pane { + let panes = self.get_selectable_panes(); + let next_index = panes .enumerate() .filter(|(_, (_, c))| { c.is_directly_below(active) && c.vertically_overlaps_with(active) @@ -1942,7 +2033,7 @@ impl Tab { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = - self.panes.get_mut(&self.active_terminal.unwrap()).unwrap(); + self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); @@ -1952,19 +2043,30 @@ impl Tab { None => Some(active.pid()), } } else { - Some(active_terminal.unwrap().pid()) + Some(active_pane.unwrap().pid()) }; - self.active_terminal = updated_active_terminal; + match updated_active_pane { + Some(updated_active_pane) => { + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, updated_active_pane); + } + }, + None => { + // TODO: can this happen? + self.active_panes.clear(); + } + } } - pub fn move_focus_up(&mut self) { + pub fn move_focus_up(&mut self, client_id: ClientId) { if !self.has_selectable_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal = self.get_active_pane(); - let updated_active_terminal = if let Some(active) = active_terminal { + let active_pane = self.get_active_pane(client_id); + let updated_active_pane = if let Some(active) = active_pane { let terminals = self.get_selectable_panes(); let next_index = terminals .enumerate() @@ -1978,7 +2080,7 @@ impl Tab { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = - self.panes.get_mut(&self.active_terminal.unwrap()).unwrap(); + self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); @@ -1988,20 +2090,31 @@ impl Tab { None => Some(active.pid()), } } else { - Some(active_terminal.unwrap().pid()) + Some(active_pane.unwrap().pid()) }; - self.active_terminal = updated_active_terminal; + match updated_active_pane { + Some(updated_active_pane) => { + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, updated_active_pane); + } + }, + None => { + // TODO: can this happen? + self.active_panes.clear(); + } + } } // returns a boolean that indicates whether the focus moved - pub fn move_focus_right(&mut self) -> bool { + pub fn move_focus_right(&mut self, client_id: ClientId) -> bool { if !self.has_selectable_panes() { return false; } if self.fullscreen_is_active { return false; } - let active_terminal = self.get_active_pane(); - let updated_active_terminal = if let Some(active) = active_terminal { + let active_pane = self.get_active_pane(client_id); + let updated_active_pane = if let Some(active) = active_pane { let terminals = self.get_selectable_panes(); let next_index = terminals .enumerate() @@ -2015,20 +2128,34 @@ impl Tab { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = - self.panes.get_mut(&self.active_terminal.unwrap()).unwrap(); + self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); - self.active_terminal = Some(p); + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, p); + } return true; } None => Some(active.pid()), } } else { - Some(active_terminal.unwrap().pid()) + Some(active_pane.unwrap().pid()) }; - self.active_terminal = updated_active_terminal; + match updated_active_pane { + Some(updated_active_pane) => { + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, updated_active_pane); + } + }, + None => { + // TODO: can this happen? + self.active_panes.clear(); + } + } false } fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet { @@ -2173,14 +2300,33 @@ impl Tab { pub fn set_pane_selectable(&mut self, id: PaneId, selectable: bool) { if let Some(pane) = self.panes.get_mut(&id) { pane.set_selectable(selectable); - if self.get_active_pane_id() == Some(id) && !selectable { - self.active_terminal = self.next_active_pane(&self.get_pane_ids()); + if !selectable { + // there are some edge cases in which this causes a hard crash when there are no + // other selectable panes - ideally this should never happen unless it's a + // configuration error - but this *does* sometimes happen with the default + // configuration as well since we set this at run time. I left this here because + // this should very rarely happen and I hope in my heart that we will stop setting + // this at runtime in the default configuration at some point + // + // If however this is not the case and we find this does cause crashes, we can + // solve it by adding a "dangling_clients" struct to Tab which we would fill with + // the relevant client ids in this case and drain as soon as a new selectable pane + // is opened + self.move_clients_out_of_pane(id); + } + } + } + fn move_clients_out_of_pane(&mut self, pane_id: PaneId) { + let active_panes: Vec<(ClientId, PaneId)> = self.active_panes.iter().map(|(cid, pid)| (*cid, *pid)).collect(); + for (client_id, active_pane_id) in active_panes { + if active_pane_id == pane_id { + self.active_panes.insert(client_id, self.next_active_pane(&self.get_pane_ids()).unwrap()); } } } pub fn close_pane(&mut self, id: PaneId) { if self.fullscreen_is_active { - self.toggle_active_pane_fullscreen(); + self.unset_fullscreen(); } if let Some(pane_to_close) = self.panes.get(&id) { let freed_space = pane_to_close.position_and_size(); @@ -2192,10 +2338,7 @@ impl Tab { self.increase_pane_width(pane_id, freed_width); } self.panes.remove(&id); - if self.active_terminal == Some(id) { - let next_active_pane = self.next_active_pane(&panes); - self.active_terminal = next_active_pane; - } + self.move_clients_out_of_pane(id); self.relayout_tab(Direction::Horizontal); return; } @@ -2204,10 +2347,7 @@ impl Tab { self.increase_pane_width(pane_id, freed_width); } self.panes.remove(&id); - if self.active_terminal == Some(id) { - let next_active_pane = self.next_active_pane(&panes); - self.active_terminal = next_active_pane; - } + self.move_clients_out_of_pane(id); self.relayout_tab(Direction::Horizontal); return; } @@ -2216,10 +2356,7 @@ impl Tab { self.increase_pane_height(pane_id, freed_height); } self.panes.remove(&id); - if self.active_terminal == Some(id) { - let next_active_pane = self.next_active_pane(&panes); - self.active_terminal = next_active_pane; - } + self.move_clients_out_of_pane(id); self.relayout_tab(Direction::Vertical); return; } @@ -2228,10 +2365,7 @@ impl Tab { self.increase_pane_height(pane_id, freed_height); } self.panes.remove(&id); - if self.active_terminal == Some(id) { - let next_active_pane = self.next_active_pane(&panes); - self.active_terminal = next_active_pane; - } + self.move_clients_out_of_pane(id); self.relayout_tab(Direction::Vertical); return; } @@ -2239,20 +2373,20 @@ impl Tab { // if we reached here, this is either the last pane or there's some sort of // configuration error (eg. we're trying to close a pane surrounded by fixed panes) self.panes.remove(&id); - self.active_terminal = None; + self.active_panes.clear(); self.resize_whole_tab(self.display_area); } } - pub fn close_focused_pane(&mut self) { - if let Some(active_pane_id) = self.get_active_pane_id() { + pub fn close_focused_pane(&mut self, client_id: ClientId) { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { self.close_pane(active_pane_id); self.senders .send_to_pty(PtyInstruction::ClosePane(active_pane_id)) .unwrap(); } } - pub fn scroll_active_terminal_up(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn scroll_active_terminal_up(&mut self, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = self .panes .get_mut(&PaneId::Terminal(active_terminal_id)) @@ -2260,8 +2394,8 @@ impl Tab { active_terminal.scroll_up(1); } } - pub fn scroll_active_terminal_down(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn scroll_active_terminal_down(&mut self, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = self .panes .get_mut(&PaneId::Terminal(active_terminal_id)) @@ -2272,8 +2406,8 @@ impl Tab { } } } - pub fn scroll_active_terminal_up_page(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn scroll_active_terminal_up_page(&mut self, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = self .panes .get_mut(&PaneId::Terminal(active_terminal_id)) @@ -2283,8 +2417,8 @@ impl Tab { active_terminal.scroll_up(scroll_columns); } } - pub fn scroll_active_terminal_down_page(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn scroll_active_terminal_down_page(&mut self, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = self .panes .get_mut(&PaneId::Terminal(active_terminal_id)) @@ -2297,8 +2431,8 @@ impl Tab { } } } - pub fn scroll_active_terminal_to_bottom(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn scroll_active_terminal_to_bottom(&mut self, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = self .panes .get_mut(&PaneId::Terminal(active_terminal_id)) @@ -2309,8 +2443,8 @@ impl Tab { } } } - pub fn clear_active_terminal_scroll(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn clear_active_terminal_scroll(&mut self, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = self .panes .get_mut(&PaneId::Terminal(active_terminal_id)) @@ -2350,7 +2484,8 @@ impl Tab { fn get_pane_id_at(&self, point: &Position, search_selectable: bool) -> Option { if self.fullscreen_is_active { - return self.get_active_pane_id(); + let first_client_id = self.connected_clients.iter().next().unwrap(); // this is a temporary hack until we fix the ui for multiple clients + return self.get_active_pane_id(*first_client_id); } if search_selectable { self.get_selectable_panes() @@ -2362,21 +2497,24 @@ impl Tab { .map(|(&id, _)| id) } } - pub fn handle_left_click(&mut self, position: &Position) { - self.focus_pane_at(position); + pub fn handle_left_click(&mut self, position: &Position, client_id: ClientId) { + self.focus_pane_at(position, client_id); if let Some(pane) = self.get_pane_at(position, false) { let relative_position = pane.relative_position(position); pane.start_selection(&relative_position); }; } - fn focus_pane_at(&mut self, point: &Position) { + fn focus_pane_at(&mut self, point: &Position, client_id: ClientId) { if let Some(clicked_pane) = self.get_pane_id_at(point, true) { - self.active_terminal = Some(clicked_pane); + let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, clicked_pane); + } } } - pub fn handle_mouse_release(&mut self, position: &Position) { - let active_pane_id = self.get_active_pane_id(); + pub fn handle_mouse_release(&mut self, position: &Position, client_id: ClientId) { + let active_pane_id = self.get_active_pane_id(client_id); // on release, get the selected text from the active pane, and reset it's selection let mut selected_text = None; if active_pane_id != self.get_pane_id_at(position, true) { @@ -2398,8 +2536,8 @@ impl Tab { self.write_selection_to_clipboard(&selected_text); } } - pub fn handle_mouse_hold(&mut self, position_on_screen: &Position) { - if let Some(active_pane_id) = self.get_active_pane_id() { + pub fn handle_mouse_hold(&mut self, position_on_screen: &Position, client_id: ClientId) { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { if let Some(active_pane) = self.panes.get_mut(&active_pane_id) { let relative_position = active_pane.relative_position(position_on_screen); active_pane.update_selection(&relative_position); @@ -2407,8 +2545,8 @@ impl Tab { } } - pub fn copy_selection(&self) { - let selected_text = self.get_active_pane().and_then(|p| p.get_selected_text()); + pub fn copy_selection(&self, client_id: ClientId) { + let selected_text = self.get_active_pane(client_id).and_then(|p| p.get_selected_text()); if let Some(selected_text) = selected_text { self.write_selection_to_clipboard(&selected_text); self.senders diff --git a/zellij-server/src/unit/tab_tests.rs b/zellij-server/src/unit/tab_tests.rs index 8c26421c71..d9905cadb4 100644 --- a/zellij-server/src/unit/tab_tests.rs +++ b/zellij-server/src/unit/tab_tests.rs @@ -114,7 +114,7 @@ fn split_panes_vertically() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id); + tab.vertical_split(new_pane_id, 1); assert_eq!(tab.panes.len(), 2, "The tab has two panes"); assert_eq!( tab.panes @@ -202,7 +202,7 @@ fn split_panes_horizontally() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); + tab.horizontal_split(new_pane_id, 1); assert_eq!(tab.panes.len(), 2, "The tab has two panes"); assert_eq!( @@ -293,7 +293,7 @@ fn split_largest_pane() { let mut tab = create_new_tab(size); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); - tab.new_pane(new_pane_id); + tab.new_pane(new_pane_id, Some(1)); } assert_eq!(tab.panes.len(), 4, "The tab has four panes"); @@ -458,7 +458,7 @@ fn split_largest_pane() { pub fn cannot_split_panes_vertically_when_active_pane_is_too_small() { let size = Size { cols: 8, rows: 20 }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); + tab.vertical_split(PaneId::Terminal(2), 1); assert_eq!(tab.panes.len(), 1, "Tab still has only one pane"); } @@ -466,7 +466,7 @@ pub fn cannot_split_panes_vertically_when_active_pane_is_too_small() { pub fn cannot_split_panes_horizontally_when_active_pane_is_too_small() { let size = Size { cols: 121, rows: 4 }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); + tab.horizontal_split(PaneId::Terminal(2), 1); assert_eq!(tab.panes.len(), 1, "Tab still has only one pane"); } @@ -474,7 +474,7 @@ pub fn cannot_split_panes_horizontally_when_active_pane_is_too_small() { pub fn cannot_split_largest_pane_when_there_is_no_room() { let size = Size { cols: 8, rows: 4 }; let mut tab = create_new_tab(size); - tab.new_pane(PaneId::Terminal(2)); + tab.new_pane(PaneId::Terminal(2), Some(1)); assert_eq!(tab.panes.len(), 1, "Tab still has only one pane"); } @@ -487,9 +487,9 @@ pub fn toggle_focused_pane_fullscreen() { let mut tab = create_new_tab(size); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); - tab.new_pane(new_pane_id); + tab.new_pane(new_pane_id, Some(1)); } - tab.toggle_active_pane_fullscreen(); + tab.toggle_active_pane_fullscreen(1); assert_eq!( tab.panes.get(&PaneId::Terminal(4)).unwrap().x(), 0, @@ -510,7 +510,7 @@ pub fn toggle_focused_pane_fullscreen() { 20, "Pane rows match fullscreen rows" ); - tab.toggle_active_pane_fullscreen(); + tab.toggle_active_pane_fullscreen(1); assert_eq!( tab.panes.get(&PaneId::Terminal(4)).unwrap().x(), 61, @@ -544,10 +544,10 @@ pub fn move_focus_is_disabled_in_fullscreen() { let mut tab = create_new_tab(size); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); - tab.new_pane(new_pane_id); + tab.new_pane(new_pane_id, Some(1)); } - tab.toggle_active_pane_fullscreen(); - tab.move_focus_left(); + tab.toggle_active_pane_fullscreen(1); + tab.move_focus_left(1); assert_eq!( tab.panes.get(&PaneId::Terminal(4)).unwrap().x(), 0, @@ -587,8 +587,8 @@ pub fn close_pane_with_another_pane_above_it() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); - tab.close_focused_pane(); + tab.horizontal_split(new_pane_id, 1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -648,9 +648,9 @@ pub fn close_pane_with_another_pane_below_it() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); - tab.move_focus_up(); - tab.close_focused_pane(); + tab.horizontal_split(new_pane_id, 1); + tab.move_focus_up(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -707,8 +707,8 @@ pub fn close_pane_with_another_pane_to_the_left() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id); - tab.close_focused_pane(); + tab.vertical_split(new_pane_id, 1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -765,9 +765,9 @@ pub fn close_pane_with_another_pane_to_the_right() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id); - tab.move_focus_left(); - tab.close_focused_pane(); + tab.vertical_split(new_pane_id, 1); + tab.move_focus_left(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -827,11 +827,11 @@ pub fn close_pane_with_multiple_panes_above_it() { let mut tab = create_new_tab(size); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.horizontal_split(new_pane_id_1); - tab.move_focus_up(); - tab.vertical_split(new_pane_id_2); - tab.move_focus_down(); - tab.close_focused_pane(); + tab.horizontal_split(new_pane_id_1, 1); + tab.move_focus_up(1); + tab.vertical_split(new_pane_id_2, 1); + tab.move_focus_down(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -930,10 +930,10 @@ pub fn close_pane_with_multiple_panes_below_it() { let mut tab = create_new_tab(size); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.horizontal_split(new_pane_id_1); - tab.vertical_split(new_pane_id_2); - tab.move_focus_up(); - tab.close_focused_pane(); + tab.horizontal_split(new_pane_id_1, 1); + tab.vertical_split(new_pane_id_2, 1); + tab.move_focus_up(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1032,11 +1032,11 @@ pub fn close_pane_with_multiple_panes_to_the_left() { let mut tab = create_new_tab(size); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.vertical_split(new_pane_id_1); - tab.move_focus_left(); - tab.horizontal_split(new_pane_id_2); - tab.move_focus_right(); - tab.close_focused_pane(); + tab.vertical_split(new_pane_id_1, 1); + tab.move_focus_left(1); + tab.horizontal_split(new_pane_id_2, 1); + tab.move_focus_right(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1135,10 +1135,10 @@ pub fn close_pane_with_multiple_panes_to_the_right() { let mut tab = create_new_tab(size); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.vertical_split(new_pane_id_1); - tab.horizontal_split(new_pane_id_2); - tab.move_focus_left(); - tab.close_focused_pane(); + tab.vertical_split(new_pane_id_1, 1); + tab.horizontal_split(new_pane_id_2, 1); + tab.move_focus_left(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1242,21 +1242,21 @@ pub fn close_pane_with_multiple_panes_above_it_away_from_screen_edges() { let new_pane_id_5 = PaneId::Terminal(6); let new_pane_id_6 = PaneId::Terminal(7); - tab.vertical_split(new_pane_id_1); - tab.vertical_split(new_pane_id_2); - tab.move_focus_left(); - tab.move_focus_left(); - tab.horizontal_split(new_pane_id_3); - tab.move_focus_right(); - tab.horizontal_split(new_pane_id_4); - tab.move_focus_right(); - tab.horizontal_split(new_pane_id_5); - tab.move_focus_left(); - tab.move_focus_up(); - tab.resize_down(); - tab.vertical_split(new_pane_id_6); - tab.move_focus_down(); - tab.close_focused_pane(); + tab.vertical_split(new_pane_id_1, 1); + tab.vertical_split(new_pane_id_2, 1); + tab.move_focus_left(1); + tab.move_focus_left(1); + tab.horizontal_split(new_pane_id_3, 1); + tab.move_focus_right(1); + tab.horizontal_split(new_pane_id_4, 1); + tab.move_focus_right(1); + tab.horizontal_split(new_pane_id_5, 1); + tab.move_focus_left(1); + tab.move_focus_up(1); + tab.resize_down(1); + tab.vertical_split(new_pane_id_6, 1); + tab.move_focus_down(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 6, "Six panes left in tab"); @@ -1518,20 +1518,20 @@ pub fn close_pane_with_multiple_panes_below_it_away_from_screen_edges() { let new_pane_id_5 = PaneId::Terminal(6); let new_pane_id_6 = PaneId::Terminal(7); - tab.vertical_split(new_pane_id_1); - tab.vertical_split(new_pane_id_2); - tab.move_focus_left(); - tab.move_focus_left(); - tab.horizontal_split(new_pane_id_3); - tab.move_focus_right(); - tab.horizontal_split(new_pane_id_4); - tab.move_focus_right(); - tab.horizontal_split(new_pane_id_5); - tab.move_focus_left(); - tab.resize_up(); - tab.vertical_split(new_pane_id_6); - tab.move_focus_up(); - tab.close_focused_pane(); + tab.vertical_split(new_pane_id_1, 1); + tab.vertical_split(new_pane_id_2, 1); + tab.move_focus_left(1); + tab.move_focus_left(1); + tab.horizontal_split(new_pane_id_3, 1); + tab.move_focus_right(1); + tab.horizontal_split(new_pane_id_4, 1); + tab.move_focus_right(1); + tab.horizontal_split(new_pane_id_5, 1); + tab.move_focus_left(1); + tab.resize_up(1); + tab.vertical_split(new_pane_id_6, 1); + tab.move_focus_up(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 6, "Six panes left in tab"); @@ -1795,23 +1795,23 @@ pub fn close_pane_with_multiple_panes_to_the_left_away_from_screen_edges() { let new_pane_id_5 = PaneId::Terminal(6); let new_pane_id_6 = PaneId::Terminal(7); - tab.horizontal_split(new_pane_id_1); - tab.horizontal_split(new_pane_id_2); - tab.move_focus_up(); - tab.move_focus_up(); - tab.vertical_split(new_pane_id_3); - tab.move_focus_down(); - tab.vertical_split(new_pane_id_4); - tab.move_focus_down(); - tab.vertical_split(new_pane_id_5); - tab.move_focus_up(); - tab.move_focus_left(); - tab.resize_right(); - tab.resize_up(); - tab.resize_up(); - tab.horizontal_split(new_pane_id_6); - tab.move_focus_right(); - tab.close_focused_pane(); + tab.horizontal_split(new_pane_id_1, 1); + tab.horizontal_split(new_pane_id_2, 1); + tab.move_focus_up(1); + tab.move_focus_up(1); + tab.vertical_split(new_pane_id_3, 1); + tab.move_focus_down(1); + tab.vertical_split(new_pane_id_4, 1); + tab.move_focus_down(1); + tab.vertical_split(new_pane_id_5, 1); + tab.move_focus_up(1); + tab.move_focus_left(1); + tab.resize_right(1); + tab.resize_up(1); + tab.resize_up(1); + tab.horizontal_split(new_pane_id_6, 1); + tab.move_focus_right(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 6, "Six panes left in tab"); @@ -2075,22 +2075,22 @@ pub fn close_pane_with_multiple_panes_to_the_right_away_from_screen_edges() { let new_pane_id_5 = PaneId::Terminal(6); let new_pane_id_6 = PaneId::Terminal(7); - tab.horizontal_split(new_pane_id_1); - tab.horizontal_split(new_pane_id_2); - tab.move_focus_up(); - tab.move_focus_up(); - tab.vertical_split(new_pane_id_3); - tab.move_focus_down(); - tab.vertical_split(new_pane_id_4); - tab.move_focus_down(); - tab.vertical_split(new_pane_id_5); - tab.move_focus_up(); - tab.resize_left(); - tab.resize_up(); - tab.resize_up(); - tab.horizontal_split(new_pane_id_6); - tab.move_focus_left(); - tab.close_focused_pane(); + tab.horizontal_split(new_pane_id_1, 1); + tab.horizontal_split(new_pane_id_2, 1); + tab.move_focus_up(1); + tab.move_focus_up(1); + tab.vertical_split(new_pane_id_3, 1); + tab.move_focus_down(1); + tab.vertical_split(new_pane_id_4, 1); + tab.move_focus_down(1); + tab.vertical_split(new_pane_id_5, 1); + tab.move_focus_up(1); + tab.resize_left(1); + tab.resize_up(1); + tab.resize_up(1); + tab.horizontal_split(new_pane_id_6, 1); + tab.move_focus_left(1); + tab.close_focused_pane(1); assert_eq!(tab.panes.len(), 6, "Six panes left in tab"); @@ -2338,12 +2338,12 @@ pub fn move_focus_down() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); - tab.move_focus_up(); - tab.move_focus_down(); + tab.horizontal_split(new_pane_id, 1); + tab.move_focus_up(1); + tab.move_focus_down(1); assert_eq!( - tab.get_active_pane().unwrap().y(), + tab.get_active_pane(1).unwrap().y(), 10, "Active pane is the bottom one" ); @@ -2360,19 +2360,19 @@ pub fn move_focus_down_to_the_most_recently_used_pane() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.horizontal_split(new_pane_id_1); - tab.vertical_split(new_pane_id_2); - tab.vertical_split(new_pane_id_3); - tab.move_focus_up(); - tab.move_focus_down(); + tab.horizontal_split(new_pane_id_1, 1); + tab.vertical_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_3, 1); + tab.move_focus_up(1); + tab.move_focus_down(1); assert_eq!( - tab.get_active_pane().unwrap().y(), + tab.get_active_pane(1).unwrap().y(), 10, "Active pane y position" ); assert_eq!( - tab.get_active_pane().unwrap().x(), + tab.get_active_pane(1).unwrap().x(), 91, "Active pane x position" ); @@ -2387,11 +2387,11 @@ pub fn move_focus_up() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); - tab.move_focus_up(); + tab.horizontal_split(new_pane_id, 1); + tab.move_focus_up(1); assert_eq!( - tab.get_active_pane().unwrap().y(), + tab.get_active_pane(1).unwrap().y(), 0, "Active pane is the top one" ); @@ -2408,20 +2408,20 @@ pub fn move_focus_up_to_the_most_recently_used_pane() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.horizontal_split(new_pane_id_1); - tab.move_focus_up(); - tab.vertical_split(new_pane_id_2); - tab.vertical_split(new_pane_id_3); - tab.move_focus_down(); - tab.move_focus_up(); + tab.horizontal_split(new_pane_id_1, 1); + tab.move_focus_up(1); + tab.vertical_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_3, 1); + tab.move_focus_down(1); + tab.move_focus_up(1); assert_eq!( - tab.get_active_pane().unwrap().y(), + tab.get_active_pane(1).unwrap().y(), 0, "Active pane y position" ); assert_eq!( - tab.get_active_pane().unwrap().x(), + tab.get_active_pane(1).unwrap().x(), 91, "Active pane x position" ); @@ -2436,11 +2436,11 @@ pub fn move_focus_left() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id); - tab.move_focus_left(); + tab.vertical_split(new_pane_id, 1); + tab.move_focus_left(1); assert_eq!( - tab.get_active_pane().unwrap().x(), + tab.get_active_pane(1).unwrap().x(), 0, "Active pane is the left one" ); @@ -2457,20 +2457,20 @@ pub fn move_focus_left_to_the_most_recently_used_pane() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.vertical_split(new_pane_id_1); - tab.move_focus_left(); - tab.horizontal_split(new_pane_id_2); - tab.horizontal_split(new_pane_id_3); - tab.move_focus_right(); - tab.move_focus_left(); + tab.vertical_split(new_pane_id_1, 1); + tab.move_focus_left(1); + tab.horizontal_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_3, 1); + tab.move_focus_right(1); + tab.move_focus_left(1); assert_eq!( - tab.get_active_pane().unwrap().y(), + tab.get_active_pane(1).unwrap().y(), 15, "Active pane y position" ); assert_eq!( - tab.get_active_pane().unwrap().x(), + tab.get_active_pane(1).unwrap().x(), 0, "Active pane x position" ); @@ -2485,12 +2485,12 @@ pub fn move_focus_right() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id); - tab.move_focus_left(); - tab.move_focus_right(); + tab.vertical_split(new_pane_id, 1); + tab.move_focus_left(1); + tab.move_focus_right(1); assert_eq!( - tab.get_active_pane().unwrap().x(), + tab.get_active_pane(1).unwrap().x(), 61, "Active pane is the right one" ); @@ -2507,19 +2507,19 @@ pub fn move_focus_right_to_the_most_recently_used_pane() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.vertical_split(new_pane_id_1); - tab.horizontal_split(new_pane_id_2); - tab.horizontal_split(new_pane_id_3); - tab.move_focus_left(); - tab.move_focus_right(); + tab.vertical_split(new_pane_id_1, 1); + tab.horizontal_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_3, 1); + tab.move_focus_left(1); + tab.move_focus_right(1); assert_eq!( - tab.get_active_pane().unwrap().y(), + tab.get_active_pane(1).unwrap().y(), 15, "Active pane y position" ); assert_eq!( - tab.get_active_pane().unwrap().x(), + tab.get_active_pane(1).unwrap().x(), 61, "Active pane x position" ); @@ -2542,8 +2542,8 @@ pub fn resize_down_with_pane_above() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); - tab.resize_down(); + tab.horizontal_split(new_pane_id, 1); + tab.resize_down(1); assert_eq!( tab.panes.get(&new_pane_id).unwrap().position_and_size().x, @@ -2632,9 +2632,9 @@ pub fn resize_down_with_pane_below() { }; let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id); - tab.move_focus_up(); - tab.resize_down(); + tab.horizontal_split(new_pane_id, 1); + tab.move_focus_up(1); + tab.resize_down(1); assert_eq!( tab.panes.get(&new_pane_id).unwrap().position_and_size().x, @@ -2729,10 +2729,10 @@ pub fn resize_down_with_panes_above_and_below() { let first_pane_id = PaneId::Terminal(1); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.horizontal_split(new_pane_id_1); - tab.horizontal_split(new_pane_id_2); - tab.move_focus_up(); - tab.resize_down(); + tab.horizontal_split(new_pane_id_1, 1); + tab.horizontal_split(new_pane_id_2, 1); + tab.move_focus_up(1); + tab.resize_down(1); assert_eq!( tab.panes.get(&new_pane_id_1).unwrap().position_and_size().x, @@ -2846,11 +2846,11 @@ pub fn resize_down_with_multiple_panes_above() { let first_pane_id = PaneId::Terminal(1); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.horizontal_split(new_pane_id_1); - tab.move_focus_up(); - tab.vertical_split(new_pane_id_2); - tab.move_focus_down(); - tab.resize_down(); + tab.horizontal_split(new_pane_id_1, 1); + tab.move_focus_up(1); + tab.vertical_split(new_pane_id_2, 1); + tab.move_focus_down(1); + tab.resize_down(1); assert_eq!( tab.panes.get(&new_pane_id_1).unwrap().position_and_size().x, @@ -2965,12 +2965,12 @@ pub fn resize_down_with_panes_above_aligned_left_with_current_pane() { let pane_to_the_left = PaneId::Terminal(2); let focused_pane = PaneId::Terminal(3); let pane_above = PaneId::Terminal(4); - tab.horizontal_split(pane_to_the_left); - tab.vertical_split(focused_pane); - tab.move_focus_up(); - tab.vertical_split(pane_above); - tab.move_focus_down(); - tab.resize_down(); + tab.horizontal_split(pane_to_the_left, 1); + tab.vertical_split(focused_pane, 1); + tab.move_focus_up(1); + tab.vertical_split(pane_above, 1); + tab.move_focus_down(1); + tab.resize_down(1); assert_eq!( tab.panes.get(&focused_pane).unwrap().position_and_size().x, @@ -3133,11 +3133,11 @@ pub fn resize_down_with_panes_below_aligned_left_with_current_pane() { let pane_below_and_left = PaneId::Terminal(2); let pane_below = PaneId::Terminal(3); let focused_pane = PaneId::Terminal(4); - tab.horizontal_split(pane_below_and_left); - tab.vertical_split(pane_below); - tab.move_focus_up(); - tab.vertical_split(focused_pane); - tab.resize_down(); + tab.horizontal_split(pane_below_and_left, 1); + tab.vertical_split(pane_below, 1); + tab.move_focus_up(1); + tab.vertical_split(focused_pane, 1); + tab.resize_down(1); assert_eq!( tab.panes.get(&focused_pane).unwrap().position_and_size().x, @@ -3300,13 +3300,13 @@ pub fn resize_down_with_panes_above_aligned_right_with_current_pane() { let focused_pane = PaneId::Terminal(2); let pane_to_the_right = PaneId::Terminal(3); let pane_above_and_right = PaneId::Terminal(4); - tab.horizontal_split(focused_pane); - tab.vertical_split(pane_to_the_right); - tab.move_focus_up(); - tab.vertical_split(pane_above_and_right); - tab.move_focus_down(); - tab.move_focus_left(); - tab.resize_down(); + tab.horizontal_split(focused_pane, 1); + tab.vertical_split(pane_to_the_right, 1); + tab.move_focus_up(1); + tab.vertical_split(pane_above_and_right, 1); + tab.move_focus_down(1); + tab.move_focus_left(1); + tab.resize_down(1); assert_eq!( tab.panes.get(&focused_pane).unwrap().position_and_size().x, @@ -3469,12 +3469,12 @@ pub fn resize_down_with_panes_below_aligned_right_with_current_pane() { let pane_below = PaneId::Terminal(2); let pane_below_and_right = PaneId::Terminal(3); let pane_to_the_right = PaneId::Terminal(4); - tab.horizontal_split(pane_below); - tab.vertical_split(pane_below_and_right); - tab.move_focus_up(); - tab.vertical_split(pane_to_the_right); - tab.move_focus_left(); - tab.resize_down(); + tab.horizontal_split(pane_below, 1); + tab.vertical_split(pane_below_and_right, 1); + tab.move_focus_up(1); + tab.vertical_split(pane_to_the_right, 1); + tab.move_focus_left(1); + tab.resize_down(1); assert_eq!( tab.panes.get(&focused_pane).unwrap().position_and_size().x, @@ -3633,15 +3633,15 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.move_focus_down(); - tab.resize_down(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.move_focus_down(1); + tab.resize_down(1); assert_eq!( tab.panes @@ -3894,14 +3894,14 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.resize_down(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.resize_down(1); assert_eq!( tab.panes @@ -4154,18 +4154,18 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_panes_to_the_lef rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.vertical_split(PaneId::Terminal(7)); - tab.vertical_split(PaneId::Terminal(8)); - tab.move_focus_left(); - tab.resize_down(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.vertical_split(PaneId::Terminal(7), 1); + tab.vertical_split(PaneId::Terminal(8), 1); + tab.move_focus_left(1); + tab.resize_down(1); assert_eq!( tab.panes @@ -4496,20 +4496,20 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_to_the_left_and_ rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_left(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(7)); - tab.vertical_split(PaneId::Terminal(8)); - tab.move_focus_left(); - tab.move_focus_up(); - tab.move_focus_left(); - tab.resize_down(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_left(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(7), 1); + tab.vertical_split(PaneId::Terminal(8), 1); + tab.move_focus_left(1); + tab.move_focus_up(1); + tab.move_focus_left(1); + tab.resize_down(1); assert_eq!( tab.panes @@ -4838,9 +4838,9 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() { rows: 10, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.resize_down(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.resize_down(1); assert_eq!( tab.panes @@ -4878,8 +4878,8 @@ pub fn resize_left_with_pane_to_the_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.resize_left(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.resize_left(1); assert_eq!( tab.panes @@ -4973,9 +4973,9 @@ pub fn resize_left_with_pane_to_the_right() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.resize_left(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -5070,10 +5070,10 @@ pub fn resize_left_with_panes_to_the_left_and_right() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_left(); - tab.resize_left(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_left(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -5206,11 +5206,11 @@ pub fn resize_left_with_multiple_panes_to_the_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_right(); - tab.resize_left(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_right(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -5344,12 +5344,12 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_down(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_down(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -5522,13 +5522,13 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_down(); - tab.move_focus_left(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_down(1); + tab.move_focus_left(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -5701,11 +5701,11 @@ pub fn resize_left_with_panes_to_the_left_aligned_bottom_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(4)); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.resize_left(1); assert_eq!( tab.panes @@ -5878,12 +5878,12 @@ pub fn resize_left_with_panes_to_the_right_aligned_bottom_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_left(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_left(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -6058,15 +6058,15 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_current_pa rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_down(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_down(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -6319,16 +6319,16 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_current_p rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_down(); - tab.move_focus_left(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_down(1); + tab.move_focus_left(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -6581,19 +6581,19 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abov rows: 70, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_down(); - tab.resize_down(); - tab.vertical_split(PaneId::Terminal(6)); - tab.horizontal_split(PaneId::Terminal(7)); - tab.horizontal_split(PaneId::Terminal(8)); - tab.move_focus_up(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_down(1); + tab.resize_down(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.horizontal_split(PaneId::Terminal(7), 1); + tab.horizontal_split(PaneId::Terminal(8), 1); + tab.move_focus_up(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -6924,20 +6924,20 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_panes_abo rows: 70, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_down(); - tab.resize_down(); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(7)); - tab.horizontal_split(PaneId::Terminal(8)); - tab.move_focus_up(); - tab.resize_left(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_down(1); + tab.resize_down(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(7), 1); + tab.horizontal_split(PaneId::Terminal(8), 1); + tab.move_focus_up(1); + tab.resize_left(1); assert_eq!( tab.panes @@ -7263,8 +7263,8 @@ pub fn cannot_resize_left_when_pane_to_the_left_is_at_minimum_width() { let size = Size { cols: 10, rows: 20 }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.resize_left(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.resize_left(1); assert_eq!( tab.panes @@ -7302,8 +7302,8 @@ pub fn resize_right_with_pane_to_the_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.resize_right(1); assert_eq!( tab.panes @@ -7398,9 +7398,9 @@ pub fn resize_right_with_pane_to_the_right() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -7495,10 +7495,10 @@ pub fn resize_right_with_panes_to_the_left_and_right() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_left(); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_left(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -7632,11 +7632,11 @@ pub fn resize_right_with_multiple_panes_to_the_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_right(); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_right(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -7770,12 +7770,12 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_with_current_pane() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_right(); - tab.horizontal_split(PaneId::Terminal(4)); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_right(1); + tab.horizontal_split(PaneId::Terminal(4), 1); + tab.resize_right(1); assert_eq!( tab.panes @@ -7947,13 +7947,13 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_with_current_pane() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_right(); - tab.horizontal_split(PaneId::Terminal(4)); - tab.move_focus_left(); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_right(1); + tab.horizontal_split(PaneId::Terminal(4), 1); + tab.move_focus_left(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -8126,13 +8126,13 @@ pub fn resize_right_with_panes_to_the_left_aligned_bottom_with_current_pane() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_right(); - tab.horizontal_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_right(1); + tab.horizontal_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -8305,14 +8305,14 @@ pub fn resize_right_with_panes_to_the_right_aligned_bottom_with_current_pane() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_right(); - tab.horizontal_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.move_focus_left(); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_right(1); + tab.horizontal_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.move_focus_left(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -8487,15 +8487,15 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_current_p rows: 20, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_down(); - tab.resize_right(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_down(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -8747,16 +8747,16 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_current_ rows: 20, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_down(); - tab.move_focus_left(); - tab.resize_right(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_down(1); + tab.move_focus_left(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -9008,19 +9008,19 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abo rows: 70, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_down(); - tab.resize_up(); - tab.vertical_split(PaneId::Terminal(6)); - tab.horizontal_split(PaneId::Terminal(7)); - tab.horizontal_split(PaneId::Terminal(8)); - tab.move_focus_up(); - tab.resize_right(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_down(1); + tab.resize_up(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.horizontal_split(PaneId::Terminal(7), 1); + tab.horizontal_split(PaneId::Terminal(8), 1); + tab.move_focus_up(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -9350,20 +9350,20 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_panes_ab rows: 70, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.move_focus_down(); - tab.resize_up(); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.horizontal_split(PaneId::Terminal(7)); - tab.horizontal_split(PaneId::Terminal(8)); - tab.move_focus_up(); - tab.resize_right(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.move_focus_down(1); + tab.resize_up(1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.horizontal_split(PaneId::Terminal(7), 1); + tab.horizontal_split(PaneId::Terminal(8), 1); + tab.move_focus_up(1); + tab.resize_right(1); assert_eq!( tab.panes @@ -9688,8 +9688,8 @@ pub fn cannot_resize_right_when_pane_to_the_left_is_at_minimum_width() { // █ == focused pane let size = Size { cols: 10, rows: 20 }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2)); - tab.resize_right(); + tab.vertical_split(PaneId::Terminal(2), 1); + tab.resize_right(1); assert_eq!( tab.panes @@ -9728,8 +9728,8 @@ pub fn resize_up_with_pane_above() { rows: 20, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.resize_up(1); assert_eq!( tab.panes @@ -9825,9 +9825,9 @@ pub fn resize_up_with_pane_below() { rows: 20, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -9926,10 +9926,10 @@ pub fn resize_up_with_panes_above_and_below() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.horizontal_split(PaneId::Terminal(3)); - tab.move_focus_up(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(3), 1); + tab.move_focus_up(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -10063,11 +10063,11 @@ pub fn resize_up_with_multiple_panes_above() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_down(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_down(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -10200,12 +10200,12 @@ pub fn resize_up_with_panes_above_aligned_left_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(4)); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.resize_up(1); assert_eq!( tab.panes @@ -10379,13 +10379,13 @@ pub fn resize_up_with_panes_below_aligned_left_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -10559,13 +10559,13 @@ pub fn resize_up_with_panes_above_aligned_right_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_left(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_left(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -10739,14 +10739,14 @@ pub fn resize_up_with_panes_below_aligned_right_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_left(); - tab.move_focus_up(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_left(1); + tab.move_focus_up(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -10920,14 +10920,14 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -11179,15 +11179,15 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_current_pane() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.move_focus_up(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.move_focus_up(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -11439,18 +11439,18 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_panes_to_the_left_ rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_left(); - tab.vertical_split(PaneId::Terminal(7)); - tab.vertical_split(PaneId::Terminal(8)); - tab.move_focus_left(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_left(1); + tab.vertical_split(PaneId::Terminal(7), 1); + tab.vertical_split(PaneId::Terminal(8), 1); + tab.move_focus_left(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -11780,19 +11780,19 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_to_the_left_and_ri rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.move_focus_up(); - tab.vertical_split(PaneId::Terminal(3)); - tab.vertical_split(PaneId::Terminal(4)); - tab.move_focus_down(); - tab.vertical_split(PaneId::Terminal(5)); - tab.vertical_split(PaneId::Terminal(6)); - tab.move_focus_up(); - tab.move_focus_left(); - tab.vertical_split(PaneId::Terminal(7)); - tab.vertical_split(PaneId::Terminal(8)); - tab.move_focus_left(); - tab.resize_up(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.move_focus_up(1); + tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(4), 1); + tab.move_focus_down(1); + tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(6), 1); + tab.move_focus_up(1); + tab.move_focus_left(1); + tab.vertical_split(PaneId::Terminal(7), 1); + tab.vertical_split(PaneId::Terminal(8), 1); + tab.move_focus_left(1); + tab.resize_up(1); assert_eq!( tab.panes @@ -12121,8 +12121,8 @@ pub fn cannot_resize_up_when_pane_above_is_at_minimum_height() { rows: 10, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2)); - tab.resize_down(); + tab.horizontal_split(PaneId::Terminal(2), 1); + tab.resize_down(1); assert_eq!( tab.panes From 8684bd4379b63fb5c2379c96db7bd2a5d67406bf Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 12 Nov 2021 16:46:52 +0100 Subject: [PATCH 17/18] style(fmt): make rustfmt happy --- src/tests/e2e/cases.rs | 395 +++++++++++++++++---------------- src/tests/e2e/remote_runner.rs | 69 +++--- zellij-server/src/screen.rs | 35 ++- zellij-server/src/tab.rs | 98 +++++--- 4 files changed, 334 insertions(+), 263 deletions(-) diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 6481bbfdba..fd014e753c 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -110,22 +110,21 @@ pub fn split_terminals_vertically() { let mut test_attempts = 10; let last_snapshot = loop { drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut runner = RemoteRunner::new(fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }); + let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); runner.run_all_steps(); let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for new pane to appear", @@ -155,14 +154,12 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { let mut test_attempts = 10; let last_snapshot = loop { drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut runner = RemoteRunner::new( - fake_win_size, - ) - .add_step(Step { + let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) { + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); // back to normal mode after split @@ -178,8 +175,7 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; // if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("...") - if remote_terminal.cursor_position_is(3, 2) - { + if remote_terminal.cursor_position_is(3, 2) { // ... is the truncated tip line step_is_complete = true; } @@ -212,7 +208,8 @@ pub fn scrolling_inside_a_pane() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -310,7 +307,8 @@ pub fn toggle_pane_fullscreen() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -373,7 +371,8 @@ pub fn open_new_tab() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -440,7 +439,8 @@ pub fn close_tab() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -488,7 +488,9 @@ pub fn close_tab() { name: "Wait for tab to close", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) && !remote_terminal.snapshot_contains("Tab #2") { + if remote_terminal.cursor_position_is(3, 2) + && !remote_terminal.snapshot_contains("Tab #2") + { // cursor is in the first tab again step_is_complete = true; } @@ -520,7 +522,8 @@ pub fn close_pane() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -578,19 +581,18 @@ pub fn exit_zellij() { let mut test_attempts = 10; let last_snapshot = loop { drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut runner = RemoteRunner::new(fake_win_size) - .add_step(Step { - name: "Wait for app to load", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&QUIT); - step_is_complete = true; - } - step_is_complete - }, - }); + let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&QUIT); + step_is_complete = true; + } + step_is_complete + }, + }); runner.run_all_steps(); break runner.take_snapshot_after(Step { name: "Wait for app to exit", @@ -618,20 +620,19 @@ pub fn closing_last_pane_exits_zellij() { let mut test_attempts = 10; let last_snapshot = loop { drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut runner = RemoteRunner::new(fake_win_size) - .add_step(Step { - name: "Close pane", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE); - step_is_complete = true; - } - step_is_complete - }, - }); + let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { + name: "Close pane", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE); + step_is_complete = true; + } + step_is_complete + }, + }); runner.run_all_steps(); if runner.test_timed_out && test_attempts > 0 { test_attempts -= 1; @@ -666,7 +667,9 @@ pub fn typing_exit_closes_pane() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) { + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); // back to normal mode after split @@ -729,7 +732,8 @@ pub fn resize_pane() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -792,7 +796,8 @@ pub fn lock_mode() { name: "Enter lock mode", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&LOCK_MODE); step_is_complete = true; @@ -851,7 +856,8 @@ pub fn resize_terminal_window() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -911,7 +917,8 @@ pub fn detach_and_attach_session() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -1032,7 +1039,8 @@ fn focus_pane_with_mouse() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -1092,7 +1100,8 @@ pub fn scrolling_inside_a_pane_with_mouse() { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) { remote_terminal.send_key(&PANE_MODE); remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); @@ -1181,22 +1190,21 @@ pub fn start_without_pane_frames() { let mut test_attempts = 10; let last_snapshot = loop { drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut runner = RemoteRunner::new_without_frames(fake_win_size) - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 1) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }); + let mut runner = RemoteRunner::new_without_frames(fake_win_size).add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 1) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); runner.run_all_steps(); let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for new pane to appear", @@ -1232,101 +1240,97 @@ pub fn mirrored_sessions() { // here we connect with one runner, then connect with another, perform some actions and // then make sure they were also reflected (mirrored) in the first runner afterwards drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut first_runner = - RemoteRunner::new_with_session_name(fake_win_size, session_name) - .dont_panic() - .add_step(Step { - name: "Wait for app to load", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() - && remote_terminal.cursor_position_is(3, 2) - { - step_is_complete = true; - } - step_is_complete - }, - }); + let mut first_runner = RemoteRunner::new_with_session_name(fake_win_size, session_name) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); first_runner.run_all_steps(); - let mut second_runner = - RemoteRunner::new_existing_session(fake_win_size, session_name) - .dont_panic() - .add_step(Step { - name: "Split pane to the right", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() - && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&PANE_MODE); - remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Open new tab (second user)", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(63, 2) - && remote_terminal.tip_appears() - { - // cursor is in the newly opened second pane - remote_terminal.send_key(&TAB_MODE); - remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); - // back to normal mode after split - remote_terminal.send_key(&ENTER); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Wait for new tab to open", - instruction: |remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) - && remote_terminal.tip_appears() - && remote_terminal.snapshot_contains("Tab #2") - && remote_terminal.status_bar_appears() - { - // cursor is in the newly opened second tab - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Switch to previous tab", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) - && remote_terminal.tip_appears() - && remote_terminal.snapshot_contains("Tab #2") - { - // cursor is in the newly opened second pane - remote_terminal.send_key(&"some text".as_bytes()); - step_is_complete = true; - } - step_is_complete - }, - }) - .add_step(Step { - name: "Wait for text to appear on screen", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.snapshot_contains("some text") { - remote_terminal.send_key(&TAB_MODE); - remote_terminal.send_key(&MOVE_FOCUS_LEFT_IN_PANE_MODE); // same key as tab mode - step_is_complete = true; - } - step_is_complete - }, - }); + let mut second_runner = RemoteRunner::new_existing_session(fake_win_size, session_name) + .dont_panic() + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Open new tab (second user)", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Wait for new tab to open", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") + && remote_terminal.status_bar_appears() + { + // cursor is in the newly opened second tab + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Switch to previous tab", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") + { + // cursor is in the newly opened second pane + remote_terminal.send_key(&"some text".as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Wait for text to appear on screen", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.snapshot_contains("some text") { + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&MOVE_FOCUS_LEFT_IN_PANE_MODE); // same key as tab mode + step_is_complete = true; + } + step_is_complete + }, + }); second_runner.run_all_steps(); if first_runner.test_timed_out || second_runner.test_timed_out { @@ -1337,25 +1341,27 @@ pub fn mirrored_sessions() { name: "take snapshot after", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("┐┌") { + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.snapshot_contains("┐┌") + { // cursor is back in the first tab step_is_complete = true; } step_is_complete - } - + }, }); let first_runner_snapshot = first_runner.take_snapshot_after(Step { name: "take snapshot after", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("┐┌") { + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.snapshot_contains("┐┌") + { // cursor is back in the first tab step_is_complete = true; } step_is_complete - } - + }, }); if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 { @@ -1383,23 +1389,22 @@ pub fn bracketed_paste() { let mut test_attempts = 10; let last_snapshot = loop { drop(RemoteRunner::kill_running_sessions(fake_win_size)); - let mut runner = RemoteRunner::new(fake_win_size) - .add_step(Step { - name: "Send pasted text followed by normal text", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) - { - remote_terminal.send_key(&BRACKETED_PASTE_START); - remote_terminal.send_key(&TAB_MODE); - remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); - remote_terminal.send_key(&BRACKETED_PASTE_END); - remote_terminal.send_key("abc".as_bytes()); - step_is_complete = true; - } - step_is_complete - }, - }); + let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { + name: "Send pasted text followed by normal text", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&BRACKETED_PASTE_START); + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + remote_terminal.send_key(&BRACKETED_PASTE_END); + remote_terminal.send_key("abc".as_bytes()); + step_is_complete = true; + } + step_is_complete + }, + }); runner.run_all_steps(); let last_snapshot = runner.take_snapshot_after(Step { diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 2673a72bd2..c43b41656a 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -1,5 +1,5 @@ -use zellij_tile::data::Palette; use std::sync::{Arc, Mutex}; +use zellij_tile::data::Palette; use zellij_server::panes::TerminalPane; use zellij_utils::pane_size::{Dimension, PaneGeom, Size}; @@ -118,7 +118,12 @@ fn start_zellij_with_layout(channel: &mut ssh2::Channel, layout_path: &str) { channel.flush().unwrap(); } -fn read_from_channel(channel: &Arc>, last_snapshot: &Arc>, cursor_coordinates: &Arc>, pane_geom: &PaneGeom) -> (Arc>,std::thread::JoinHandle<()>) { +fn read_from_channel( + channel: &Arc>, + last_snapshot: &Arc>, + cursor_coordinates: &Arc>, + pane_geom: &PaneGeom, +) -> (Arc>, std::thread::JoinHandle<()>) { let should_keep_running = Arc::new(Mutex::new(true)); let thread = std::thread::Builder::new() .name("read_thread".into()) @@ -142,21 +147,22 @@ fn read_from_channel(channel: &Arc>, last_snapshot: &Arc { + Ok(0) => { let current_snapshot = take_snapshot(&mut terminal_output); let mut last_snapshot = last_snapshot.lock().unwrap(); - *cursor_coordinates.lock().unwrap() = terminal_output.cursor_coordinates().unwrap_or((0, 0)); + *cursor_coordinates.lock().unwrap() = + terminal_output.cursor_coordinates().unwrap_or((0, 0)); *last_snapshot = current_snapshot; should_sleep = true; } Ok(count) => { for byte in buf.iter().take(count) { - vte_parser - .advance(&mut terminal_output.grid, *byte); + vte_parser.advance(&mut terminal_output.grid, *byte); } let current_snapshot = take_snapshot(&mut terminal_output); let mut last_snapshot = last_snapshot.lock().unwrap(); - *cursor_coordinates.lock().unwrap() = terminal_output.grid.cursor_coordinates().unwrap_or((0, 0)); + *cursor_coordinates.lock().unwrap() = + terminal_output.grid.cursor_coordinates().unwrap_or((0, 0)); *last_snapshot = current_snapshot; should_sleep = true; } @@ -164,7 +170,8 @@ fn read_from_channel(channel: &Arc>, last_snapshot: &Arc 0 { @@ -178,7 +185,7 @@ fn read_from_channel(channel: &Arc>, last_snapshot: &Arc String { @@ -214,7 +221,9 @@ impl std::fmt::Debug for RemoteTerminal { write!( f, "cursor x: {}\ncursor_y: {}\ncurrent_snapshot:\n{}", - self.cursor_x, self.cursor_y, *self.last_snapshot.lock().unwrap() + self.cursor_x, + self.cursor_y, + *self.last_snapshot.lock().unwrap() ) } } @@ -260,9 +269,11 @@ impl RemoteTerminal { } pub fn attach_to_original_session(&mut self) { let mut channel = self.channel.lock().unwrap(); - channel.write_all( - format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(), - ).unwrap(); + channel + .write_all( + format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(), + ) + .unwrap(); channel.flush().unwrap(); } } @@ -307,7 +318,8 @@ impl RemoteRunner { let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); sess.set_blocking(false); - let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); + let reader_thread = + read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, @@ -328,10 +340,7 @@ impl RemoteRunner { setup_remote_environment(&mut channel, win_size); start_zellij(&mut channel); } - pub fn new_with_session_name( - win_size: Size, - session_name: &str, - ) -> Self { + pub fn new_with_session_name(win_size: Size, session_name: &str) -> Self { // notice that this method does not have a timeout, so use with caution! let sess = ssh_connect_without_timeout(); let mut channel = sess.channel_session().unwrap(); @@ -351,7 +360,8 @@ impl RemoteRunner { let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); sess.set_blocking(false); - let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); + let reader_thread = + read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, @@ -366,10 +376,7 @@ impl RemoteRunner { reader_thread, } } - pub fn new_existing_session( - win_size: Size, - session_name: &str, - ) -> Self { + pub fn new_existing_session(win_size: Size, session_name: &str) -> Self { let sess = ssh_connect_without_timeout(); let mut channel = sess.channel_session().unwrap(); let mut rows = Dimension::fixed(win_size.rows); @@ -388,7 +395,8 @@ impl RemoteRunner { let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); sess.set_blocking(false); - let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); + let reader_thread = + read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, @@ -422,7 +430,8 @@ impl RemoteRunner { let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); sess.set_blocking(false); - let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); + let reader_thread = + read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, @@ -437,10 +446,7 @@ impl RemoteRunner { reader_thread, } } - pub fn new_with_layout( - win_size: Size, - layout_file_name: &'static str, - ) -> Self { + pub fn new_with_layout(win_size: Size, layout_file_name: &'static str) -> Self { let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name); let sess = ssh_connect(); let mut channel = sess.channel_session().unwrap(); @@ -460,7 +466,8 @@ impl RemoteRunner { let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); sess.set_blocking(false); - let reader_thread = read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); + let reader_thread = + read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); RemoteRunner { steps: vec![], channel, @@ -526,7 +533,7 @@ impl RemoteRunner { channel: self.channel.clone(), }; if instruction(remote_terminal) { - return String::from(self.last_snapshot.lock().unwrap().clone()) + return String::from(self.last_snapshot.lock().unwrap().clone()); } else { retries_left -= 1; std::thread::sleep(std::time::Duration::from_millis(100)); diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 37193e7ac8..c2c9d4237c 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -597,7 +597,10 @@ pub(crate) fn screen_thread_main( ScreenInstruction::NewPane(pid, client_or_tab_index) => { match client_or_tab_index { ClientOrTabIndex::ClientId(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().new_pane(pid, Some(client_id)); + screen + .get_active_tab_mut(client_id) + .unwrap() + .new_pane(pid, Some(client_id)); } ClientOrTabIndex::TabIndex(tab_index) => { screen.tabs.get_mut(&tab_index).unwrap().new_pane(pid, None); @@ -648,22 +651,34 @@ pub(crate) fn screen_thread_main( } } ScreenInstruction::ResizeLeft(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_left(client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .resize_left(client_id); screen.render(); } ScreenInstruction::ResizeRight(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_right(client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .resize_right(client_id); screen.render(); } ScreenInstruction::ResizeDown(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_down(client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .resize_down(client_id); screen.render(); } ScreenInstruction::ResizeUp(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().resize_up(client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .resize_up(client_id); screen.render(); } @@ -684,7 +699,10 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::SwitchFocus(client_id) => { - screen.get_active_tab_mut(client_id).unwrap().move_focus(client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus(client_id); screen.render(); } @@ -1040,7 +1058,10 @@ pub(crate) fn screen_thread_main( screen.render(); } ScreenInstruction::Copy(client_id) => { - screen.get_active_tab(client_id).unwrap().copy_selection(client_id); + screen + .get_active_tab(client_id) + .unwrap() + .copy_selection(client_id); screen.render(); } diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index 5522db2f5b..fbf1d8c388 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -428,14 +428,20 @@ impl Tab { } self.set_pane_frames(self.draw_pane_frames); // This is the end of the nasty viewport hack... - let next_selectable_pane_id = self.panes.iter().filter(|(_id, pane)| pane.selectable()).map(|(id, _)| id.to_owned()).next(); + let next_selectable_pane_id = self + .panes + .iter() + .filter(|(_id, pane)| pane.selectable()) + .map(|(id, _)| id.to_owned()) + .next(); match next_selectable_pane_id { Some(active_pane_id) => { - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, active_pane_id); } - }, + } None => { // this is very likely a configuration error (layout with no selectable panes) self.active_panes.clear(); @@ -448,7 +454,7 @@ impl Tab { let first_active_pane_id = *self.active_panes.get(first_client_id).unwrap(); self.connected_clients.insert(client_id); self.active_panes.insert(client_id, first_active_pane_id); - }, + } None => { let mut pane_ids: Vec = self.panes.keys().copied().collect(); if pane_ids.is_empty() { @@ -460,7 +466,6 @@ impl Tab { self.connected_clients.insert(client_id); self.active_panes.insert(client_id, *first_pane_id); } - } // TODO: we might be able to avoid this, we do this so that newly connected clients will // necessarily get a full render @@ -585,7 +590,8 @@ impl Tab { // right now we administratively change focus of all clients until the // mirroring/multiplayer situation is sorted out - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, pid); } @@ -721,7 +727,10 @@ impl Tab { } } } - pub fn get_active_terminal_cursor_position(&self, client_id: ClientId) -> Option<(usize, usize)> { + pub fn get_active_terminal_cursor_position( + &self, + client_id: ClientId, + ) -> Option<(usize, usize)> { // (x, y) let active_terminal = &self.get_active_pane(client_id)?; active_terminal @@ -823,7 +832,10 @@ impl Tab { } pub fn mark_active_pane_for_rerender(&mut self, client_id: ClientId) { if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - self.panes.get_mut(&active_pane_id).unwrap().set_should_render(true) + self.panes + .get_mut(&active_pane_id) + .unwrap() + .set_should_render(true) } } pub fn set_pane_frames(&mut self, draw_pane_frames: bool) { @@ -930,7 +942,10 @@ impl Tab { match self.get_active_terminal_cursor_position(*first_client_id) { Some((cursor_position_x, cursor_position_y)) => { let show_cursor = "\u{1b}[?25h"; - let change_cursor_shape = self.get_active_pane(*first_client_id).unwrap().cursor_shape_csi(); + let change_cursor_shape = self + .get_active_pane(*first_client_id) + .unwrap() + .cursor_shape_csi(); let goto_cursor_position = &format!( "\u{1b}[{};{}H\u{1b}[m{}", cursor_position_y + 1, @@ -2371,13 +2386,16 @@ impl Tab { Some(&p) => { // render previously active pane so that its frame does not remain actively // colored - let previously_active_pane = - self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); + let previously_active_pane = self + .panes + .get_mut(&self.active_panes.get(&client_id).unwrap()) + .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, p); } @@ -2390,11 +2408,12 @@ impl Tab { }; match updated_active_pane { Some(updated_active_pane) => { - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, updated_active_pane); } - }, + } None => { // TODO: can this happen? self.active_panes.clear(); @@ -2424,8 +2443,10 @@ impl Tab { Some(&p) => { // render previously active pane so that its frame does not remain actively // colored - let previously_active_pane = - self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); + let previously_active_pane = self + .panes + .get_mut(&self.active_panes.get(&client_id).unwrap()) + .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); @@ -2439,11 +2460,12 @@ impl Tab { }; match updated_active_pane { Some(updated_active_pane) => { - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, updated_active_pane); } - }, + } None => { // TODO: can this happen? self.active_panes.clear(); @@ -2471,8 +2493,10 @@ impl Tab { Some(&p) => { // render previously active pane so that its frame does not remain actively // colored - let previously_active_pane = - self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); + let previously_active_pane = self + .panes + .get_mut(&self.active_panes.get(&client_id).unwrap()) + .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); @@ -2486,11 +2510,12 @@ impl Tab { }; match updated_active_pane { Some(updated_active_pane) => { - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, updated_active_pane); } - }, + } None => { // TODO: can this happen? self.active_panes.clear(); @@ -2519,13 +2544,16 @@ impl Tab { Some(&p) => { // render previously active pane so that its frame does not remain actively // colored - let previously_active_pane = - self.panes.get_mut(&self.active_panes.get(&client_id).unwrap()).unwrap(); + let previously_active_pane = self + .panes + .get_mut(&self.active_panes.get(&client_id).unwrap()) + .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); next_active_pane.set_should_render(true); - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, p); } @@ -2538,11 +2566,12 @@ impl Tab { }; match updated_active_pane { Some(updated_active_pane) => { - let connected_clients: Vec = self.connected_clients.iter().copied().collect(); + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, updated_active_pane); } - }, + } None => { // TODO: can this happen? self.active_panes.clear(); @@ -2941,10 +2970,17 @@ impl Tab { } } fn move_clients_out_of_pane(&mut self, pane_id: PaneId) { - let active_panes: Vec<(ClientId, PaneId)> = self.active_panes.iter().map(|(cid, pid)| (*cid, *pid)).collect(); + let active_panes: Vec<(ClientId, PaneId)> = self + .active_panes + .iter() + .map(|(cid, pid)| (*cid, *pid)) + .collect(); for (client_id, active_pane_id) in active_panes { if active_pane_id == pane_id { - self.active_panes.insert(client_id, self.next_active_pane(&self.get_pane_ids()).unwrap()); + self.active_panes.insert( + client_id, + self.next_active_pane(&self.get_pane_ids()).unwrap(), + ); } } } @@ -3203,7 +3239,9 @@ impl Tab { } pub fn copy_selection(&self, client_id: ClientId) { - let selected_text = self.get_active_pane(client_id).and_then(|p| p.get_selected_text()); + let selected_text = self + .get_active_pane(client_id) + .and_then(|p| p.get_selected_text()); if let Some(selected_text) = selected_text { self.write_selection_to_clipboard(&selected_text); self.senders From 5d58dcf3f6262a97a2f4a0b03255fce1e6de9077 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 12 Nov 2021 16:52:32 +0100 Subject: [PATCH 18/18] style(fmt): make clippy happy --- src/tests/e2e/cases.rs | 75 ++++++++++++++++++++++------------ src/tests/e2e/remote_runner.rs | 8 ++-- zellij-server/src/tab.rs | 24 +++++------ 3 files changed, 64 insertions(+), 43 deletions(-) diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index fd014e753c..0198adfe06 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -77,7 +77,8 @@ pub fn starts_with_one_terminal() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size); let last_snapshot = runner.take_snapshot_after(Step { name: "Wait for app to load", @@ -109,7 +110,8 @@ pub fn split_terminals_vertically() { let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { @@ -153,7 +155,8 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { let fake_win_size = Size { cols: 8, rows: 20 }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { @@ -201,7 +204,8 @@ pub fn scrolling_inside_a_pane() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .retry_pause_ms(1000) // we need a longer retry period here because it takes some time to fill the pty buffer .add_step(Step { @@ -301,7 +305,8 @@ pub fn toggle_pane_fullscreen() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -365,7 +370,8 @@ pub fn open_new_tab() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -433,7 +439,8 @@ pub fn close_tab() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -516,7 +523,8 @@ pub fn close_pane() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -580,7 +588,8 @@ pub fn exit_zellij() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { name: "Wait for app to load", instruction: |mut remote_terminal: RemoteTerminal| -> bool { @@ -619,7 +628,8 @@ pub fn closing_last_pane_exits_zellij() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { name: "Close pane", instruction: |mut remote_terminal: RemoteTerminal| -> bool { @@ -661,7 +671,8 @@ pub fn typing_exit_closes_pane() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -684,11 +695,11 @@ pub fn typing_exit_closes_pane() { instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { - remote_terminal.send_key(&"e".as_bytes()); - remote_terminal.send_key(&"x".as_bytes()); - remote_terminal.send_key(&"i".as_bytes()); - remote_terminal.send_key(&"t".as_bytes()); - remote_terminal.send_key(&"\n".as_bytes()); + remote_terminal.send_key("e".as_bytes()); + remote_terminal.send_key("x".as_bytes()); + remote_terminal.send_key("i".as_bytes()); + remote_terminal.send_key("t".as_bytes()); + remote_terminal.send_key("\n".as_bytes()); step_is_complete = true; } step_is_complete @@ -726,7 +737,8 @@ pub fn resize_pane() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -790,7 +802,8 @@ pub fn lock_mode() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Enter lock mode", @@ -850,7 +863,8 @@ pub fn resize_terminal_window() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -911,7 +925,8 @@ pub fn detach_and_attach_session() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -998,7 +1013,8 @@ pub fn accepts_basic_layout() { let layout_file_name = "three-panes-with-nesting.yaml"; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); runner.run_all_steps(); let last_snapshot = runner.take_snapshot_after(Step { @@ -1033,7 +1049,8 @@ fn focus_pane_with_mouse() { let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .add_step(Step { name: "Split pane to the right", @@ -1093,7 +1110,8 @@ pub fn scrolling_inside_a_pane_with_mouse() { }; let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size) .retry_pause_ms(1000) // we need a longer retry period here because it takes some time to fill the pty buffer .add_step(Step { @@ -1189,7 +1207,8 @@ pub fn start_without_pane_frames() { let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new_without_frames(fake_win_size).add_step(Step { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { @@ -1239,7 +1258,8 @@ pub fn mirrored_sessions() { let (first_runner_snapshot, second_runner_snapshot) = loop { // here we connect with one runner, then connect with another, perform some actions and // then make sure they were also reflected (mirrored) in the first runner afterwards - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut first_runner = RemoteRunner::new_with_session_name(fake_win_size, session_name) .dont_panic() .add_step(Step { @@ -1313,7 +1333,7 @@ pub fn mirrored_sessions() { && remote_terminal.snapshot_contains("Tab #2") { // cursor is in the newly opened second pane - remote_terminal.send_key(&"some text".as_bytes()); + remote_terminal.send_key("some text".as_bytes()); step_is_complete = true; } step_is_complete @@ -1388,7 +1408,8 @@ pub fn bracketed_paste() { // paste, send some more text and make sure it's also sent to the terminal let mut test_attempts = 10; let last_snapshot = loop { - drop(RemoteRunner::kill_running_sessions(fake_win_size)); + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); let mut runner = RemoteRunner::new(fake_win_size).add_step(Step { name: "Send pasted text followed by normal text", instruction: |mut remote_terminal: RemoteTerminal| -> bool { diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index c43b41656a..46e2b88f15 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -355,7 +355,7 @@ impl RemoteRunner { cols, }; setup_remote_environment(&mut channel, win_size); - start_zellij_in_session(&mut channel, &session_name); + start_zellij_in_session(&mut channel, session_name); let channel = Arc::new(Mutex::new(channel)); let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); @@ -390,7 +390,7 @@ impl RemoteRunner { cols, }; setup_remote_environment(&mut channel, win_size); - attach_to_existing_session(&mut channel, &session_name); + attach_to_existing_session(&mut channel, session_name); let channel = Arc::new(Mutex::new(channel)); let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); @@ -523,7 +523,7 @@ impl RemoteRunner { loop { if retries_left == 0 { self.test_timed_out = true; - return String::from(self.last_snapshot.lock().unwrap().clone()); + return self.last_snapshot.lock().unwrap().clone(); } let (cursor_x, cursor_y) = *self.cursor_coordinates.lock().unwrap(); let remote_terminal = RemoteTerminal { @@ -533,7 +533,7 @@ impl RemoteRunner { channel: self.channel.clone(), }; if instruction(remote_terminal) { - return String::from(self.last_snapshot.lock().unwrap().clone()); + return self.last_snapshot.lock().unwrap().clone(); } else { retries_left -= 1; std::thread::sleep(std::time::Duration::from_millis(100)); diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index fbf1d8c388..933dd9adef 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -462,7 +462,7 @@ impl Tab { return; } pane_ids.sort(); // TODO: make this predictable - let first_pane_id = pane_ids.iter().next().unwrap(); + let first_pane_id = pane_ids.get(0).unwrap(); self.connected_clients.insert(client_id); self.active_panes.insert(client_id, *first_pane_id); } @@ -760,7 +760,7 @@ impl Tab { viewport_pane.reset_size_and_position_override(); } self.panes_to_hide.clear(); - let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); + let active_terminal = self.panes.get_mut(active_pane_id).unwrap(); active_terminal.reset_size_and_position_override(); self.set_force_render(); self.resize_whole_tab(self.display_area); @@ -2388,7 +2388,7 @@ impl Tab { // colored let previously_active_pane = self .panes - .get_mut(&self.active_panes.get(&client_id).unwrap()) + .get_mut(self.active_panes.get(&client_id).unwrap()) .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); @@ -2445,7 +2445,7 @@ impl Tab { // colored let previously_active_pane = self .panes - .get_mut(&self.active_panes.get(&client_id).unwrap()) + .get_mut(self.active_panes.get(&client_id).unwrap()) .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); @@ -2495,7 +2495,7 @@ impl Tab { // colored let previously_active_pane = self .panes - .get_mut(&self.active_panes.get(&client_id).unwrap()) + .get_mut(self.active_panes.get(&client_id).unwrap()) .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); @@ -2546,7 +2546,7 @@ impl Tab { // colored let previously_active_pane = self .panes - .get_mut(&self.active_panes.get(&client_id).unwrap()) + .get_mut(self.active_panes.get(&client_id).unwrap()) .unwrap(); previously_active_pane.set_should_render(true); let next_active_pane = self.panes.get_mut(&p).unwrap(); @@ -2647,7 +2647,7 @@ impl Tab { .map(|(_, (pid, _))| pid); if let Some(&p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); - let current_position = self.panes.get(&active_pane_id).unwrap(); + let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); let prev_geom_override = current_position.geom_override(); @@ -2703,7 +2703,7 @@ impl Tab { resize_pty!(new_position, self.os_api); new_position.set_should_render(true); - let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + let current_position = self.panes.get_mut(active_pane_id).unwrap(); current_position.set_geom(next_geom); if let Some(geom) = next_geom_override { current_position.get_geom_override(geom); @@ -2731,7 +2731,7 @@ impl Tab { .map(|(_, (pid, _))| pid); if let Some(&p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); - let current_position = self.panes.get(&active_pane_id).unwrap(); + let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); let prev_geom_override = current_position.geom_override(); @@ -2745,7 +2745,7 @@ impl Tab { resize_pty!(new_position, self.os_api); new_position.set_should_render(true); - let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + let current_position = self.panes.get_mut(active_pane_id).unwrap(); current_position.set_geom(next_geom); if let Some(geom) = next_geom_override { current_position.get_geom_override(geom); @@ -2773,7 +2773,7 @@ impl Tab { .map(|(_, (pid, _))| pid); if let Some(&p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); - let current_position = self.panes.get(&active_pane_id).unwrap(); + let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); let prev_geom_override = current_position.geom_override(); @@ -2787,7 +2787,7 @@ impl Tab { resize_pty!(new_position, self.os_api); new_position.set_should_render(true); - let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + let current_position = self.panes.get_mut(active_pane_id).unwrap(); current_position.set_geom(next_geom); if let Some(geom) = next_geom_override { current_position.get_geom_override(geom);