From 6715f4629c664885e0800da76cd73ca470705b27 Mon Sep 17 00:00:00 2001 From: har7an <99636919+har7an@users.noreply.github.com> Date: Thu, 6 Oct 2022 06:46:18 +0000 Subject: [PATCH] Server: Remove `panic`s in `tab` module (#1748) * utils/errors: Add `ToAnyhow` trait for converting `Result` types that don't satisfy `anyhow`s trait constraints (`Display + Send + Sync + 'static`) conveniently. An example of such a Result is the `SendError` returned from `send_to_plugins`, which sends `PluginInstruction`s as message type. One of the enum variants can contain a `mpsc::Sender`, which is `!Sync` and hence makes the whole `SendError` be `!Sync` in this case. Add an implementation for this case that takes the message and converts it into an error containing the message formatted as string, with the additional `ErrorContext` as anyhow context. * server/tab: Remove calls to `unwrap()` and apply error reporting via `anyhow` instead. Make all relevant functions return `Result`s where previously a panic could occur and attach error context. * server/screen: Modify `update_tab!` to accept an optional 4th parameter, a literal "?". If present, this will append a `?` to the given closure verbatim to handle/propagate errors from within the generated macro code. * server/screen: Handle new `Result`s from `Tab` and apply appropriate error context and propagate errors further up. * server/tab/unit: `unwrap` on new `Result`s * server/unit: Unwrap `Results` in screen tests * server/tab: Better message for ad-hoc errors created with `anyhow!`. Since these errors don't have an underlying cause, we describe the cause in the macro instead and then attach the error context as usual before `?`ing the error back up. * utils/cargo: Activate `anyhow`s "backtrace" feature to capture error backtraces at the error origins (i.e. where we first receive an error and convert it to a `anyhow::Error`). Since we propagate error back up the call stack now, the place where we `unwrap` on errors doesn't match the place where the error originated. Hence, the callstack, too, is quite misleading since it contains barely any references of the functions that triggered the error. As a consequence, we have 2 backtraces now when zellij crashes: One from `anyhow` (that is implicitly attached to anyhows error reports), and one from the custom panic handler (which is displayed through `miette`). * utils/errors: Separate stack traces in the output of miette. Since we record backtraces with `anyhow` now, we end up having two backtraces in the output: One from the `anyhow` error and one from the actual call to `panic`. Adds a comment explaining the situation and another "section" to the error output of miette: We print the backtrace from anyhow as "Stack backtrace", and the output from the panic handler as "Panic backtrace". We keep both for the (hopefully unlikely) case that the anyhow backtrace isn't existent, so we still have at least something to work with. * server/screen: Remove calls to `fatal` and leave the `panic`ing to the calling function instead. * server/screen: Remove needless macro which extended `active_tab!` by passing the client IDs to the closure. However, this isn't necessary because closures capture their environment already, and the client_id needn't be mutable. * server/screen: Handle unused result * server/screen: Reintroduce arcane macro that defaults to some default client_id if it isn't valid (e.g. when the ScreenInstruction is sent via CLI). * server/tab/unit: Unwrap new results --- Cargo.lock | 3 + zellij-server/src/screen.rs | 272 ++++-- zellij-server/src/tab/mod.rs | 656 +++++++++---- .../src/tab/unit/tab_integration_tests.rs | 876 +++++++++++------- zellij-server/src/tab/unit/tab_tests.rs | 565 +++++------ zellij-server/src/unit/screen_tests.rs | 4 +- zellij-utils/Cargo.toml | 2 +- zellij-utils/src/errors.rs | 42 +- 8 files changed, 1517 insertions(+), 903 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad8705d6..23049df6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,9 @@ name = "anyhow" version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +dependencies = [ + "backtrace", +] [[package]] name = "arc-swap" diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 012cc557..fcb1de8c 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -40,8 +40,10 @@ use zellij_utils::{ /// /// - screen: An instance of `Screen` to operate on /// - client_id: The client_id, usually taken from the `ScreenInstruction` that's being processed -/// - closure: A closure satisfying `|tab: &mut Tab| -> ()` - +/// - closure: A closure satisfying `|tab: &mut Tab| -> ()` OR `|tab: &mut Tab| -> Result` (see +/// '?' below) +/// - ?: A literal "?", to append a `?` to the closure when it returns a `Result` type. This +/// argument is optional and not needed when the closure returns `()` macro_rules! active_tab { ($screen:ident, $client_id:ident, $closure:expr) => { if let Some(active_tab) = $screen.get_active_tab_mut($client_id) { @@ -54,7 +56,16 @@ macro_rules! active_tab { log::error!("Active tab not found for client id: {:?}", $client_id); } }; + // Same as above, but with an added `?` for when the close returns a `Result` type. + ($screen:ident, $client_id:ident, $closure:expr, ?) => { + if let Some(active_tab) = $screen.get_active_tab_mut($client_id) { + $closure(active_tab)?; + } else { + log::error!("Active tab not found for client id: {:?}", $client_id); + } + }; } + macro_rules! active_tab_and_connected_client_id { ($screen:ident, $client_id:ident, $closure:expr) => { match $screen.get_active_tab_mut($client_id) { @@ -71,6 +82,30 @@ macro_rules! active_tab_and_connected_client_id { log::error!("Active tab not found for client id: {:?}", $client_id); }, } + } else { + log::error!("No client ids in screen found"); + }; + }, + } + }; + // Same as above, but with an added `?` for when the closure returns a `Result` type. + ($screen:ident, $client_id:ident, $closure:expr, ?) => { + match $screen.get_active_tab_mut($client_id) { + Some(active_tab) => { + $closure(active_tab, $client_id)?; + }, + None => { + if let Some(client_id) = $screen.get_first_client_id() { + match $screen.get_active_tab_mut(client_id) { + Some(active_tab) => { + $closure(active_tab, client_id)?; + }, + None => { + log::error!("Active tab not found for client id: {:?}", $client_id); + }, + } + } else { + log::error!("No client ids in screen found"); }; }, } @@ -393,32 +428,44 @@ impl Screen { fn move_clients_from_closed_tab( &mut self, client_ids_and_mode_infos: Vec<(ClientId, ModeInfo)>, - ) { + ) -> Result<()> { + let err_context = || "failed to move clients from closed tab".to_string(); + if self.tabs.is_empty() { log::error!( "No tabs left, cannot move clients: {:?} from closed tab", client_ids_and_mode_infos ); - return; + return Ok(()); } - let first_tab_index = *self.tabs.keys().next().unwrap(); + let first_tab_index = *self + .tabs + .keys() + .next() + .context("screen contained no tabs") + .with_context(err_context)?; for (client_id, client_mode_info) in client_ids_and_mode_infos { let client_tab_history = self.tab_history.entry(client_id).or_insert_with(Vec::new); if let Some(client_previous_tab) = client_tab_history.pop() { if let Some(client_active_tab) = self.tabs.get_mut(&client_previous_tab) { self.active_tab_indices .insert(client_id, client_previous_tab); - client_active_tab.add_client(client_id, Some(client_mode_info)); + client_active_tab + .add_client(client_id, Some(client_mode_info)) + .with_context(err_context)?; continue; } } self.active_tab_indices.insert(client_id, first_tab_index); self.tabs .get_mut(&first_tab_index) - .unwrap() - .add_client(client_id, Some(client_mode_info)); + .with_context(err_context)? + .add_client(client_id, Some(client_mode_info)) + .with_context(err_context)?; } + Ok(()) } + fn move_clients_between_tabs( &mut self, source_tab_index: usize, @@ -440,13 +487,18 @@ impl Screen { .get_indexed_tab_mut(destination_tab_index) .context("failed to get destination tab by index") .with_context(err_context)?; - destination_tab.add_multiple_clients(client_mode_info_in_source_tab); - destination_tab.update_input_modes(); + destination_tab + .add_multiple_clients(client_mode_info_in_source_tab) + .with_context(err_context)?; + destination_tab + .update_input_modes() + .with_context(err_context)?; destination_tab.set_force_render(); - destination_tab.visible(true); + destination_tab.visible(true).with_context(err_context)?; } Ok(()) } + fn update_client_tab_focus(&mut self, client_id: ClientId, new_tab_index: usize) { match self.active_tab_indices.remove(&client_id) { Some(old_active_index) => { @@ -498,7 +550,7 @@ impl Screen { if let Some(current_tab) = self.get_indexed_tab_mut(current_tab_index) { if current_tab.has_no_connected_clients() { - current_tab.visible(false); + current_tab.visible(false).with_context(err_context)?; } } else { log::error!("Tab index: {:?} not found", current_tab_index); @@ -564,6 +616,7 @@ impl Screen { fn close_tab_at_index(&mut self, tab_index: usize) -> Result<()> { let err_context = || format!("failed to close tab at index {tab_index:?}"); + let mut tab_to_close = self.tabs.remove(&tab_index).with_context(err_context)?; let pane_ids = tab_to_close.get_all_pane_ids(); // below we don't check the result of sending the CloseTab instruction to the pty thread @@ -581,13 +634,14 @@ impl Screen { .with_context(err_context) } else { let client_mode_infos_in_closed_tab = tab_to_close.drain_connected_clients(None); - self.move_clients_from_closed_tab(client_mode_infos_in_closed_tab); + self.move_clients_from_closed_tab(client_mode_infos_in_closed_tab) + .with_context(err_context)?; 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(); - t.visible(true); + t.visible(true).with_context(err_context)?; } if t.position > tab_to_close.position { t.position -= 1; @@ -683,7 +737,8 @@ impl Screen { for (tab_index, tab) in &mut self.tabs { if tab.has_selectable_tiled_panes() { let vte_overlay = overlay.generate_overlay(size); - tab.render(&mut output, Some(vte_overlay)); + tab.render(&mut output, Some(vte_overlay)) + .context(err_context)?; } else { tabs_to_close.push(*tab_index); } @@ -793,13 +848,15 @@ impl Screen { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), ); - tab.apply_layout(layout, new_pids, tab_index, client_id); + tab.apply_layout(layout, new_pids, tab_index, client_id) + .with_context(err_context)?; if self.session_is_mirrored { if let Some(active_tab) = self.get_active_tab_mut(client_id) { let client_mode_infos_in_source_tab = active_tab.drain_connected_clients(None); - tab.add_multiple_clients(client_mode_infos_in_source_tab); + tab.add_multiple_clients(client_mode_infos_in_source_tab) + .with_context(err_context)?; if active_tab.has_no_connected_clients() { - active_tab.visible(false); + active_tab.visible(false).with_context(err_context)?; } } let all_connected_clients: Vec = @@ -810,14 +867,15 @@ impl Screen { } else if let Some(active_tab) = self.get_active_tab_mut(client_id) { let client_mode_info_in_source_tab = active_tab.drain_connected_clients(Some(vec![client_id])); - tab.add_multiple_clients(client_mode_info_in_source_tab); + tab.add_multiple_clients(client_mode_info_in_source_tab) + .with_context(err_context)?; if active_tab.has_no_connected_clients() { - active_tab.visible(false); + active_tab.visible(false).with_context(err_context)?; } self.update_client_tab_focus(client_id, tab_index); } - tab.update_input_modes(); - tab.visible(true); + tab.update_input_modes().with_context(err_context)?; + tab.visible(true).with_context(err_context)?; self.tabs.insert(tab_index, tab); if !self.active_tab_indices.contains_key(&client_id) { // this means this is a new client and we need to add it to our state properly @@ -829,6 +887,10 @@ impl Screen { } pub fn add_client(&mut self, client_id: ClientId) -> Result<()> { + let err_context = |tab_index| { + format!("failed to attach client {client_id} to tab with index {tab_index}") + }; + let mut tab_history = vec![]; if let Some((_first_client, first_tab_history)) = self.tab_history.iter().next() { tab_history = first_tab_history.clone(); @@ -843,7 +905,7 @@ impl Screen { } else if let Some(tab_index) = self.tabs.keys().next() { tab_index.to_owned() } else { - panic!("Can't find a valid tab to attach client to!"); + bail!("Can't find a valid tab to attach client to!"); }; self.active_tab_indices.insert(client_id, tab_index); @@ -851,18 +913,20 @@ impl Screen { self.tab_history.insert(client_id, tab_history); self.tabs .get_mut(&tab_index) - .with_context(|| format!("Failed to attach client to tab with index {tab_index}"))? - .add_client(client_id, None); - Ok(()) + .with_context(|| err_context(tab_index))? + .add_client(client_id, None) + .with_context(|| err_context(tab_index)) } pub fn remove_client(&mut self, client_id: ClientId) -> Result<()> { - self.tabs.iter_mut().for_each(|(_, tab)| { + let err_context = || format!("failed to remove client {client_id}"); + + for (_, tab) in self.tabs.iter_mut() { tab.remove_client(client_id); if tab.has_no_connected_clients() { - tab.visible(false); + tab.visible(false).with_context(err_context)?; } - }); + } if self.active_tab_indices.contains_key(&client_id) { self.active_tab_indices.remove(&client_id); } @@ -870,8 +934,7 @@ impl Screen { self.tab_history.remove(&client_id); } self.connected_clients.borrow_mut().remove(&client_id); - self.update_tabs() - .with_context(|| format!("failed to remove client {client_id:?}")) + self.update_tabs().with_context(err_context) } pub fn update_tabs(&self) -> Result<()> { @@ -908,12 +971,8 @@ impl Screen { Some(*client_id), Event::TabUpdate(tab_data), )) - .or_else(|err| { - let (_, error_context) = err.0; - Err(anyhow!("failed to send data to plugins")) - .context(error_context) - .context("failed to update tabs") - })?; + .to_anyhow() + .context("failed to update tabs")?; } Ok(()) } @@ -978,13 +1037,20 @@ impl Screen { } } - pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) { + pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) -> Result<()> { let previous_mode = self .mode_info .get(&client_id) .unwrap_or(&self.default_mode_info) .mode; + let err_context = || { + format!( + "failed to change from mode '{:?}' to mode '{:?}' for client {client_id}", + previous_mode, mode_info.mode + ) + }; + // If we leave the Search-related modes, we need to clear all previous searches let search_related_modes = [InputMode::EnterSearch, InputMode::Search, InputMode::Scroll]; if search_related_modes.contains(&previous_mode) @@ -997,7 +1063,9 @@ impl Screen { && (mode_info.mode == InputMode::Normal || mode_info.mode == InputMode::Locked) { if let Some(active_tab) = self.get_active_tab_mut(client_id) { - active_tab.clear_active_terminal_scroll(client_id); + active_tab + .clear_active_terminal_scroll(client_id) + .with_context(err_context)?; } } @@ -1023,16 +1091,27 @@ impl Screen { tab.change_mode_info(mode_info.clone(), client_id); tab.mark_active_pane_for_rerender(client_id); } + Ok(()) } - pub fn change_mode_for_all_clients(&mut self, mode_info: ModeInfo) { + + pub fn change_mode_for_all_clients(&mut self, mode_info: ModeInfo) -> Result<()> { + let err_context = || { + format!( + "failed to change input mode to {:?} for all clients", + mode_info.mode + ) + }; + let connected_client_ids: Vec = self.active_tab_indices.keys().copied().collect(); for client_id in connected_client_ids { - self.change_mode(mode_info.clone(), client_id); + self.change_mode(mode_info.clone(), client_id) + .with_context(err_context)?; if let Some(os_input) = &mut self.bus.os_input { let _ = os_input .send_to_client(client_id, ServerToClientMsg::SwitchToMode(mode_info.mode)); } } + Ok(()) } pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) -> Result<()> { let client_id = if self.get_active_tab(client_id).is_some() { @@ -1131,8 +1210,7 @@ pub(crate) fn screen_thread_main( let (event, mut err_ctx) = screen .bus .recv() - .context("failed to receive event on channel") - .fatal(); + .context("failed to receive event on channel")?; err_ctx.add_call(ContextType::Screen((&event).into())); match event { @@ -1140,7 +1218,8 @@ pub(crate) fn screen_thread_main( 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); + tab.handle_pty_bytes(pid, vte_bytes) + .context("failed to process pty bytes")?; break; } } @@ -1151,15 +1230,14 @@ pub(crate) fn screen_thread_main( ScreenInstruction::NewPane(pid, client_or_tab_index) => { match client_or_tab_index { ClientOrTabIndex::ClientId(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab.new_pane(pid, Some(client_id)) - ); + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, + client_id: ClientId| tab .new_pane(pid, + Some(client_id)), + ?); }, ClientOrTabIndex::TabIndex(tab_index) => { if let Some(active_tab) = screen.tabs.get_mut(&tab_index) { - active_tab.new_pane(pid, None); + active_tab.new_pane(pid, None)?; } else { log::error!("Tab index not found: {:?}", tab_index); } @@ -1172,32 +1250,26 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::OpenInPlaceEditor(pid, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .suppress_active_pane(pid, client_id)); + .suppress_active_pane(pid, client_id), ?); screen.unblock_input()?; screen.update_tabs()?; screen.render()?; }, ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab - .toggle_pane_embed_or_floating(client_id) - ); + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab + .toggle_pane_embed_or_floating(client_id), ?); screen.unblock_input()?; screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::ToggleFloatingPanes(client_id, default_shell) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab - .toggle_floating_panes(client_id, default_shell) - ); + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab + .toggle_floating_panes(client_id, default_shell), ?); screen.unblock_input()?; screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::ShowFloatingPanes(client_id) => { @@ -1208,6 +1280,7 @@ pub(crate) fn screen_thread_main( ); screen.unblock_input()?; screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::HideFloatingPanes(client_id) => { @@ -1218,13 +1291,15 @@ pub(crate) fn screen_thread_main( ); screen.unblock_input()?; screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::HorizontalSplit(pid, client_id) => { active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.horizontal_split(pid, client_id) + |tab: &mut Tab, client_id: ClientId| tab.horizontal_split(pid, client_id), + ? ); screen.unblock_input()?; screen.update_tabs()?; @@ -1234,7 +1309,8 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.vertical_split(pid, client_id) + |tab: &mut Tab, client_id: ClientId| tab.vertical_split(pid, client_id), + ? ); screen.unblock_input()?; screen.update_tabs()?; @@ -1249,7 +1325,8 @@ pub(crate) fn screen_thread_main( true => tab.write_to_terminals_on_current_tab(bytes), false => tab.write_to_active_terminal(bytes, client_id), } - } + }, + ? ); }, ScreenInstruction::ResizeLeft(client_id) => { @@ -1393,7 +1470,8 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.edit_scrollback(client_id) + |tab: &mut Tab, client_id: ClientId| tab.edit_scrollback(client_id), + ? ); screen.render()?; }, @@ -1456,7 +1534,7 @@ pub(crate) fn screen_thread_main( screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .handle_scrollwheel_up(&point, 3, client_id) + .handle_scrollwheel_up(&point, 3, client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1465,7 +1543,7 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.scroll_active_terminal_down(client_id) + |tab: &mut Tab, client_id: ClientId| tab.scroll_active_terminal_down(client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1475,7 +1553,7 @@ pub(crate) fn screen_thread_main( screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .handle_scrollwheel_down(&point, 3, client_id) + .handle_scrollwheel_down(&point, 3, client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1485,7 +1563,7 @@ pub(crate) fn screen_thread_main( screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_to_bottom(client_id) + .scroll_active_terminal_to_bottom(client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1505,7 +1583,7 @@ pub(crate) fn screen_thread_main( screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_down_page(client_id) + .scroll_active_terminal_down_page(client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1525,7 +1603,7 @@ pub(crate) fn screen_thread_main( screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_down_half_page(client_id) + .scroll_active_terminal_down_half_page(client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1535,7 +1613,7 @@ pub(crate) fn screen_thread_main( screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .clear_active_terminal_scroll(client_id) + .clear_active_terminal_scroll(client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1544,7 +1622,7 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.close_focused_pane(client_id) + |tab: &mut Tab, client_id: ClientId| tab.close_focused_pane(client_id), ? ); screen.update_tabs()?; screen.render()?; @@ -1585,7 +1663,7 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.update_active_pane_name(c, client_id) + |tab: &mut Tab, client_id: ClientId| tab.update_active_pane_name(c, client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1594,7 +1672,7 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.undo_active_rename_pane(client_id) + |tab: &mut Tab, client_id: ClientId| tab.undo_active_rename_pane(client_id), ? ); screen.render()?; screen.unblock_input()?; @@ -1641,7 +1719,10 @@ pub(crate) fn screen_thread_main( ScreenInstruction::GoToTab(tab_index, client_id) => { let client_id = if client_id.is_none() { None - } else if screen.active_tab_indices.contains_key(&client_id.unwrap()) { + } else if screen + .active_tab_indices + .contains_key(&client_id.expect("This is checked above")) + { client_id } else { screen.active_tab_indices.keys().next().copied() @@ -1679,12 +1760,12 @@ pub(crate) fn screen_thread_main( screen.update_terminal_color_registers(color_registers); }, ScreenInstruction::ChangeMode(mode_info, client_id) => { - screen.change_mode(mode_info, client_id); + screen.change_mode(mode_info, client_id)?; screen.render()?; screen.unblock_input()?; }, ScreenInstruction::ChangeModeForAllClients(mode_info) => { - screen.change_mode_for_all_clients(mode_info); + screen.change_mode_for_all_clients(mode_info)?; screen.render()?; screen.unblock_input()?; }, @@ -1700,62 +1781,59 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::LeftClick(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .handle_left_click(&point, client_id)); + .handle_left_click(&point, client_id), ?); screen.update_tabs()?; screen.render()?; screen.unblock_input()?; }, ScreenInstruction::RightClick(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .handle_right_click(&point, client_id)); + .handle_right_click(&point, client_id), ?); screen.update_tabs()?; screen.render()?; screen.unblock_input()?; }, ScreenInstruction::MiddleClick(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .handle_middle_click(&point, client_id)); + .handle_middle_click(&point, client_id), ?); screen.update_tabs()?; screen.render()?; screen.unblock_input()?; }, ScreenInstruction::LeftMouseRelease(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .handle_left_mouse_release(&point, client_id)); + .handle_left_mouse_release(&point, client_id), ?); screen.render()?; screen.unblock_input()?; }, ScreenInstruction::RightMouseRelease(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .handle_right_mouse_release(&point, client_id)); + .handle_right_mouse_release(&point, client_id), ?); screen.render()?; }, ScreenInstruction::MiddleMouseRelease(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .handle_middle_mouse_release(&point, client_id)); + .handle_middle_mouse_release(&point, client_id), ?); screen.render()?; }, ScreenInstruction::MouseHoldLeft(point, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| { - tab.handle_mouse_hold_left(&point, client_id); - }); + active_tab!(screen, client_id, |tab: &mut Tab| tab + .handle_mouse_hold_left(&point, client_id), ?); screen.render()?; }, ScreenInstruction::MouseHoldRight(point, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| { - tab.handle_mouse_hold_right(&point, client_id); - }); + active_tab!(screen, client_id, |tab: &mut Tab| tab + .handle_mouse_hold_right(&point, client_id), ?); screen.render()?; }, ScreenInstruction::MouseHoldMiddle(point, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| { - tab.handle_mouse_hold_middle(&point, client_id); - }); + active_tab!(screen, client_id, |tab: &mut Tab| tab + .handle_mouse_hold_middle(&point, client_id), ?); screen.render()?; }, ScreenInstruction::Copy(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab - .copy_selection(client_id)); + .copy_selection(client_id), ?); screen.render()?; }, ScreenInstruction::Exit => { @@ -1807,7 +1885,7 @@ pub(crate) fn screen_thread_main( active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.update_search_term(c, client_id) + |tab: &mut Tab, client_id: ClientId| tab.update_search_term(c, client_id), ? ); screen.render()?; }, diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 883caf0d..7a3554df 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -7,6 +7,7 @@ mod copy_command; use copy_command::CopyCommand; use std::env::temp_dir; use uuid::Uuid; +use zellij_utils::errors::prelude::*; use zellij_utils::position::{Column, Line}; use zellij_utils::{position::Position, serde}; @@ -458,7 +459,12 @@ impl Tab { new_pids: Vec, tab_index: usize, client_id: ClientId, - ) { + ) -> Result<()> { + let err_context = || { + format!( + "failed to apply layout {layout:#?} in tab {tab_index} for client id {client_id}" + ) + }; if self.tiled_panes.has_panes() { log::error!( "Applying a layout to a tab with existing panes - this is not yet supported!" @@ -491,12 +497,17 @@ impl Tab { let pane_title = run.location.to_string(); self.senders .send_to_plugin(PluginInstruction::Load(pid_tx, run, tab_index, client_id)) - .unwrap(); - let pid = pid_rx.recv().unwrap(); + .to_anyhow() + .with_context(err_context)?; + let pid = pid_rx.recv().with_context(err_context)?; let mut new_plugin = PluginPane::new( pid, *position_and_size, - self.senders.to_plugin.as_ref().unwrap().clone(), + self.senders + .to_plugin + .as_ref() + .with_context(err_context)? + .clone(), pane_title, layout.name.clone().unwrap_or_default(), ); @@ -506,7 +517,7 @@ impl Tab { set_focus_pane_id(layout, PaneId::Plugin(pid)); } else { // there are still panes left to fill, use the pids we received in this method - let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout + let pid = new_pids.next().with_context(err_context)?; // if this crashes it means we got less pids than there are panes in this layout let next_terminal_position = self.get_next_terminal_position(); let mut new_pane = TerminalPane::new( *pid, @@ -532,7 +543,7 @@ impl Tab { // fixing this will require a bit of an architecture change self.senders .send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid))) - .unwrap(); + .with_context(err_context)?; } // FIXME: This is another hack to crop the viewport to fixed-size panes. Once you can have // non-fixed panes that are part of the viewport, get rid of this! @@ -564,8 +575,9 @@ impl Tab { }, } } + Ok(()) } - pub fn update_input_modes(&mut self) { + pub fn update_input_modes(&mut self) -> Result<()> { // this updates all plugins with the client's input mode let mode_infos = self.mode_info.borrow(); for client_id in self.connected_clients.borrow().iter() { @@ -576,10 +588,17 @@ impl Tab { Some(*client_id), Event::ModeUpdate(mode_info.clone()), )) - .unwrap(); + .to_anyhow() + .with_context(|| { + format!( + "failed to update plugins with mode info {:?}", + mode_info.mode + ) + })?; } + Ok(()) } - pub fn add_client(&mut self, client_id: ClientId, mode_info: Option) { + pub fn add_client(&mut self, client_id: ClientId, mode_info: Option) -> Result<()> { let other_clients_exist_in_tab = { !self.connected_clients.borrow().is_empty() }; if other_clients_exist_in_tab { if let Some(first_active_floating_pane_id) = @@ -601,13 +620,17 @@ impl Tab { let mut pane_ids: Vec = self.tiled_panes.pane_ids().copied().collect(); if pane_ids.is_empty() { // no panes here, bye bye - return; + return Ok(()); } - let focus_pane_id = self.focus_pane_id.unwrap_or_else(|| { + let focus_pane_id = if let Some(id) = self.focus_pane_id { + id + } else { pane_ids.sort(); // TODO: make this predictable pane_ids.retain(|p| !self.tiled_panes.panes_to_hide_contains(*p)); - *pane_ids.get(0).unwrap() - }); + *(pane_ids.get(0).with_context(|| { + format!("failed to acquire id of focused pane while adding client {client_id}",) + })?) + }; self.tiled_panes.focus_pane(focus_pane_id, client_id); self.connected_clients.borrow_mut().insert(client_id); self.mode_info.borrow_mut().insert( @@ -616,18 +639,26 @@ impl Tab { ); } self.set_force_render(); - self.update_input_modes(); + self.update_input_modes() + .with_context(|| format!("failed to add client {client_id} to tab")) } + pub fn change_mode_info(&mut self, mode_info: ModeInfo, client_id: ClientId) { self.mode_info.borrow_mut().insert(client_id, mode_info); } - pub fn add_multiple_clients(&mut self, client_ids_to_mode_infos: Vec<(ClientId, ModeInfo)>) { + + pub fn add_multiple_clients( + &mut self, + client_ids_to_mode_infos: Vec<(ClientId, ModeInfo)>, + ) -> Result<()> { for (client_id, client_mode_info) in client_ids_to_mode_infos { - self.add_client(client_id, None); + self.add_client(client_id, None) + .context("failed to add clients")?; self.mode_info .borrow_mut() .insert(client_id, client_mode_info); } + Ok(()) } pub fn remove_client(&mut self, client_id: ClientId) { self.focus_pane_id = None; @@ -659,16 +690,18 @@ impl Tab { pub fn has_no_connected_clients(&self) -> bool { self.connected_clients.borrow().is_empty() } - pub fn toggle_pane_embed_or_floating(&mut self, client_id: ClientId) { + pub fn toggle_pane_embed_or_floating(&mut self, client_id: ClientId) -> Result<()> { if self.tiled_panes.fullscreen_is_active() { self.tiled_panes.unset_fullscreen(); } if self.floating_panes.panes_are_visible() { if let Some(focused_floating_pane_id) = self.floating_panes.active_pane_id(client_id) { if self.tiled_panes.has_room_for_new_pane() { - // this unwrap is safe because floating panes should not be visible if there are no floating panes - let floating_pane_to_embed = - self.close_pane(focused_floating_pane_id, true).unwrap(); + let floating_pane_to_embed = self + .close_pane(focused_floating_pane_id, true) + .with_context(|| format!( + "failed to find floating pane (ID: {focused_floating_pane_id:?}) to embed for client {client_id}", + ))?; self.tiled_panes .insert_pane(focused_floating_pane_id, floating_pane_to_embed); self.should_clear_display_before_rendering = true; @@ -681,7 +714,7 @@ impl Tab { if let Some(new_pane_geom) = self.floating_panes.find_room_for_new_pane() { if self.get_selectable_tiled_panes().count() <= 1 { // don't close the only pane on screen... - return; + return Ok(()); } if let Some(mut embedded_pane_to_float) = self.close_pane(focused_pane_id, true) { embedded_pane_to_float.set_geom(new_pane_geom); @@ -694,12 +727,13 @@ impl Tab { } } } + Ok(()) } pub fn toggle_floating_panes( &mut self, client_id: ClientId, default_shell: Option, - ) { + ) -> Result<()> { if self.floating_panes.panes_are_visible() { self.floating_panes.toggle_show_panes(false); self.set_force_render(); @@ -727,23 +761,30 @@ impl Tab { default_shell, ClientOrTabIndex::ClientId(client_id), ); - self.senders.send_to_pty(instruction).unwrap(); + self.senders.send_to_pty(instruction).with_context(|| { + format!("failed to open a floating pane for client {client_id}") + })?; }, } self.floating_panes.set_force_render(); } self.set_force_render(); + Ok(()) } + pub fn show_floating_panes(&mut self) { self.floating_panes.toggle_show_panes(true); self.set_force_render(); } + pub fn hide_floating_panes(&mut self) { self.floating_panes.toggle_show_panes(false); self.set_force_render(); } - pub fn new_pane(&mut self, pid: PaneId, client_id: Option) { - self.close_down_to_max_terminals(); + + pub fn new_pane(&mut self, pid: PaneId, client_id: Option) -> Result<()> { + self.close_down_to_max_terminals() + .with_context(|| format!("failed to create new pane with id {pid:?}"))?; if self.floating_panes.panes_are_visible() { if let Some(new_pane_geom) = self.floating_panes.find_room_for_new_pane() { let next_terminal_position = self.get_next_terminal_position(); @@ -793,8 +834,9 @@ impl Tab { } } } + Ok(()) } - pub fn suppress_active_pane(&mut self, pid: PaneId, client_id: ClientId) { + pub fn suppress_active_pane(&mut self, pid: PaneId, client_id: ClientId) -> Result<()> { // this method creates a new pane from pid and replaces it with the active pane // the active pane is then suppressed (hidden and not rendered) until the current // created pane is closed, in which case it will be replaced back by it @@ -824,7 +866,11 @@ impl Tab { Some(replaced_pane) => { self.suppressed_panes .insert(PaneId::Terminal(pid), replaced_pane); - let current_active_pane = self.get_active_pane(client_id).unwrap(); // this will be the newly replaced pane we just created + // this will be the newly replaced pane we just created + let current_active_pane = + self.get_active_pane(client_id).with_context(|| { + format!("failed to suppress active pane for client {client_id}") + })?; resize_pty!(current_active_pane, self.os_api); }, None => { @@ -836,12 +882,18 @@ impl Tab { // TBD, currently unsupported }, } + Ok(()) } - pub fn horizontal_split(&mut self, pid: PaneId, client_id: ClientId) { + + pub fn horizontal_split(&mut self, pid: PaneId, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to split pane {pid:?} horizontally for client {client_id}"); + if self.floating_panes.panes_are_visible() { - return; + return Ok(()); } - self.close_down_to_max_terminals(); + self.close_down_to_max_terminals() + .with_context(err_context)?; if self.tiled_panes.fullscreen_is_active() { self.toggle_active_pane_fullscreen(client_id); } @@ -866,12 +918,18 @@ impl Tab { self.tiled_panes.focus_pane(pid, client_id); } } + Ok(()) } - pub fn vertical_split(&mut self, pid: PaneId, client_id: ClientId) { + + pub fn vertical_split(&mut self, pid: PaneId, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to split pane {pid:?} vertically for client {client_id}"); + if self.floating_panes.panes_are_visible() { - return; + return Ok(()); } - self.close_down_to_max_terminals(); + self.close_down_to_max_terminals() + .with_context(err_context)?; if self.tiled_panes.fullscreen_is_active() { self.toggle_active_pane_fullscreen(client_id); } @@ -896,7 +954,9 @@ impl Tab { self.tiled_panes.focus_pane(pid, client_id); } } + Ok(()) } + pub fn get_active_pane(&self, client_id: ClientId) -> Option<&dyn Pane> { self.get_active_pane_id(client_id).and_then(|ap| { if self.floating_panes.panes_are_visible() { @@ -947,7 +1007,9 @@ impl Tab { .values() .any(|s_p| s_p.pid() == PaneId::Terminal(pid)) } - pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) { + pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) -> Result<()> { + let err_context = || format!("failed to handle pty bytes from fd {pid}"); + if let Some(terminal_output) = self .tiled_panes .get_pane_mut(PaneId::Terminal(pid)) @@ -965,23 +1027,30 @@ impl Tab { // Reset scroll - and process all pending events for this pane if evs.len() >= MAX_PENDING_VTE_EVENTS { terminal_output.clear_scroll(); - self.process_pending_vte_events(pid); + self.process_pending_vte_events(pid) + .with_context(err_context)?; } } - return; + return Ok(()); } } - self.process_pty_bytes(pid, bytes); + self.process_pty_bytes(pid, bytes).with_context(err_context) } - pub fn process_pending_vte_events(&mut self, pid: RawFd) { + + pub fn process_pending_vte_events(&mut self, pid: RawFd) -> Result<()> { if let Some(pending_vte_events) = self.pending_vte_events.get_mut(&pid) { let vte_events: Vec = pending_vte_events.drain(..).collect(); for vte_event in vte_events { - self.process_pty_bytes(pid, vte_event); + self.process_pty_bytes(pid, vte_event) + .context("failed to process pending vte events")?; } } + Ok(()) } - fn process_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) { + + fn process_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) -> Result<()> { + let err_context = || format!("failed to process pty bytes from pid {pid}"); + if let Some(terminal_output) = self .tiled_panes .get_pane_mut(PaneId::Terminal(pid)) @@ -996,45 +1065,88 @@ impl Tab { let messages_to_pty = terminal_output.drain_messages_to_pty(); let clipboard_update = terminal_output.drain_clipboard_update(); for message in messages_to_pty { - self.write_to_pane_id(message, PaneId::Terminal(pid)); + self.write_to_pane_id(message, PaneId::Terminal(pid)) + .with_context(err_context)?; } if let Some(string) = clipboard_update { - self.write_selection_to_clipboard(&string); + self.write_selection_to_clipboard(&string) + .with_context(err_context)?; } } + Ok(()) } - pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec) { + + pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec) -> Result<()> { let pane_ids = self.get_static_and_floating_pane_ids(); - pane_ids.iter().for_each(|&pane_id| { - self.write_to_pane_id(input_bytes.clone(), pane_id); - }); + for pane_id in pane_ids { + self.write_to_pane_id(input_bytes.clone(), pane_id) + .context("failed to write to terminals on current tab")?; + } + Ok(()) } - pub fn write_to_active_terminal(&mut self, input_bytes: Vec, client_id: ClientId) { + + pub fn write_to_active_terminal( + &mut self, + input_bytes: Vec, + client_id: ClientId, + ) -> Result<()> { + let err_context = || { + format!( + "failed to write to active terminal for client {client_id} - msg: {input_bytes:?}" + ) + }; + self.clear_search(client_id); // this is an inexpensive operation if empty, if we need more such cleanups we should consider moving this and the rest to some sort of cleanup method let pane_id = if self.floating_panes.panes_are_visible() { self.floating_panes .get_active_pane_id(client_id) - .unwrap_or_else(|| self.tiled_panes.get_active_pane_id(client_id).unwrap()) + .or_else(|| self.tiled_panes.get_active_pane_id(client_id)) + .ok_or_else(|| { + anyhow!(format!( + "failed to find active pane id for client {client_id}" + )) + }) + .with_context(err_context)? } else { - self.tiled_panes.get_active_pane_id(client_id).unwrap() + self.tiled_panes + .get_active_pane_id(client_id) + .with_context(err_context)? }; - self.write_to_pane_id(input_bytes, pane_id); + // Can't use 'err_context' here since it borrows 'input_bytes' + self.write_to_pane_id(input_bytes, pane_id) + .with_context(|| format!("failed to write to active terminal for client {client_id}")) } - pub fn write_to_terminal_at(&mut self, input_bytes: Vec, position: &Position) { + + pub fn write_to_terminal_at( + &mut self, + input_bytes: Vec, + position: &Position, + ) -> Result<()> { + let err_context = || format!("failed to write to terminal at position {position:?}"); + if self.floating_panes.panes_are_visible() { let pane_id = self.floating_panes.get_pane_id_at(position, false); if let Some(pane_id) = pane_id { - self.write_to_pane_id(input_bytes, pane_id); - return; + return self + .write_to_pane_id(input_bytes, pane_id) + .with_context(err_context); } } - let pane_id = self.get_pane_id_at(position, false); + let pane_id = self + .get_pane_id_at(position, false) + .with_context(err_context)?; if let Some(pane_id) = pane_id { - self.write_to_pane_id(input_bytes, pane_id); + return self + .write_to_pane_id(input_bytes, pane_id) + .with_context(err_context); } + Ok(()) } - pub fn write_to_pane_id(&mut self, input_bytes: Vec, pane_id: PaneId) { + + pub fn write_to_pane_id(&mut self, input_bytes: Vec, pane_id: PaneId) -> Result<()> { + let err_context = || format!("failed to write to pane with id {pane_id:?}"); + match pane_id { PaneId::Terminal(active_terminal_id) => { let active_terminal = self @@ -1042,7 +1154,8 @@ impl Tab { .get(&pane_id) .or_else(|| self.tiled_panes.get_pane(pane_id)) .or_else(|| self.suppressed_panes.get(&pane_id)) - .unwrap(); + .ok_or_else(|| anyhow!(format!("failed to find pane with id {pane_id:?}"))) + .with_context(err_context)?; let adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes); self.senders @@ -1050,16 +1163,18 @@ impl Tab { adjusted_input, active_terminal_id, )) - .unwrap(); + .with_context(err_context)?; }, PaneId::Plugin(pid) => { for key in parse_keys(&input_bytes) { self.senders .send_to_plugin(PluginInstruction::Update(Some(pid), None, Event::Key(key))) - .unwrap() + .to_anyhow() + .with_context(err_context)?; } }, } + Ok(()) } pub fn get_active_terminal_cursor_position( &self, @@ -1124,7 +1239,7 @@ impl Tab { active_pane.set_should_render(true); } } - fn update_active_panes_in_pty_thread(&self) { + fn update_active_panes_in_pty_thread(&self) -> Result<()> { // this is a bit hacky and we should ideally not keep this state in two different places at // some point let connected_clients: Vec = @@ -1135,16 +1250,21 @@ impl Tab { self.get_active_pane_id(client_id), client_id, )) - .unwrap(); + .with_context(|| format!("failed to update active pane for client {client_id}"))?; } + Ok(()) } - pub fn render(&mut self, output: &mut Output, overlay: Option) { + + pub fn render(&mut self, output: &mut Output, overlay: Option) -> Result<()> { + let err_context = || "failed to render tab".to_string(); + let connected_clients: HashSet = { self.connected_clients.borrow().iter().copied().collect() }; if connected_clients.is_empty() || !self.tiled_panes.has_active_panes() { - return; + return Ok(()); } - self.update_active_panes_in_pty_thread(); + self.update_active_panes_in_pty_thread() + .with_context(err_context)?; let floating_panes_stack = self.floating_panes.stack(); output.add_clients( @@ -1169,7 +1289,9 @@ impl Tab { } self.render_cursor(output); + Ok(()) } + fn hide_cursor_and_clear_display_as_needed(&mut self, output: &mut Output) { let hide_cursor = "\u{1b}[?25l"; let connected_clients: Vec = @@ -1489,16 +1611,17 @@ impl Tab { self.tiled_panes.move_active_pane_left(client_id); } } - fn close_down_to_max_terminals(&mut self) { + fn close_down_to_max_terminals(&mut self) -> Result<()> { if let Some(max_panes) = self.max_panes { let terminals = self.get_tiled_pane_ids(); for &pid in terminals.iter().skip(max_panes - 1) { self.senders .send_to_pty(PtyInstruction::ClosePane(pid)) - .unwrap(); + .context("failed to close down to max terminals")?; self.close_pane(pid, false); } } + Ok(()) } pub fn get_tiled_pane_ids(&self) -> Vec { self.get_tiled_panes().map(|(&pid, _)| pid).collect() @@ -1594,22 +1717,27 @@ impl Tab { replaced_pane }) } - pub fn close_focused_pane(&mut self, client_id: ClientId) { + pub fn close_focused_pane(&mut self, client_id: ClientId) -> Result<()> { + let err_context = |pane_id| { + format!("failed to close focused pane (ID {pane_id:?}) for client {client_id}") + }; + if self.floating_panes.panes_are_visible() { if let Some(active_floating_pane_id) = self.floating_panes.active_pane_id(client_id) { self.close_pane(active_floating_pane_id, false); self.senders .send_to_pty(PtyInstruction::ClosePane(active_floating_pane_id)) - .unwrap(); - return; + .with_context(|| err_context(active_floating_pane_id))?; + return Ok(()); } } if let Some(active_pane_id) = self.tiled_panes.get_active_pane_id(client_id) { self.close_pane(active_pane_id, false); self.senders .send_to_pty(PtyInstruction::ClosePane(active_pane_id)) - .unwrap(); + .with_context(|| err_context(active_pane_id))?; } + Ok(()) } pub fn dump_active_terminal_screen(&mut self, file: Option, client_id: ClientId) { if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { @@ -1617,7 +1745,7 @@ impl Tab { self.os_api.write_to_file(dump, file); } } - pub fn edit_scrollback(&mut self, client_id: ClientId) { + pub fn edit_scrollback(&mut self, client_id: ClientId) -> Result<()> { let mut file = temp_dir(); file.push(format!("{}.dump", Uuid::new_v4())); self.dump_active_terminal_screen(Some(String::from(file.to_string_lossy())), client_id); @@ -1630,23 +1758,29 @@ impl Tab { line_number, client_id, )) - .unwrap(); + .with_context(|| format!("failed to edit scrollback for client {client_id}")) } pub fn scroll_active_terminal_up(&mut self, client_id: ClientId) { if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { active_pane.scroll_up(1, client_id); } } - pub fn scroll_active_terminal_down(&mut self, client_id: ClientId) { + + pub fn scroll_active_terminal_down(&mut self, client_id: ClientId) -> Result<()> { + let err_context = || format!("failed to scroll down active pane for client {client_id}"); + if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { active_pane.scroll_down(1, client_id); if !active_pane.is_scrolled() { if let PaneId::Terminal(raw_fd) = active_pane.pid() { - self.process_pending_vte_events(raw_fd); + self.process_pending_vte_events(raw_fd) + .with_context(err_context)?; } } } + Ok(()) } + pub fn scroll_active_terminal_up_page(&mut self, client_id: ClientId) { if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { // prevent overflow when row == 0 @@ -1654,17 +1788,24 @@ impl Tab { active_pane.scroll_up(scroll_rows, client_id); } } - pub fn scroll_active_terminal_down_page(&mut self, client_id: ClientId) { + + pub fn scroll_active_terminal_down_page(&mut self, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to scroll down one page in active pane for client {client_id}"); + if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { let scroll_rows = active_pane.get_content_rows(); active_pane.scroll_down(scroll_rows, client_id); if !active_pane.is_scrolled() { if let PaneId::Terminal(raw_fd) = active_pane.pid() { - self.process_pending_vte_events(raw_fd); + self.process_pending_vte_events(raw_fd) + .with_context(err_context)?; } } } + Ok(()) } + pub fn scroll_active_terminal_up_half_page(&mut self, client_id: ClientId) { if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { // prevent overflow when row == 0 @@ -1672,116 +1813,183 @@ impl Tab { active_pane.scroll_up(scroll_rows, client_id); } } - pub fn scroll_active_terminal_down_half_page(&mut self, client_id: ClientId) { + + pub fn scroll_active_terminal_down_half_page(&mut self, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to scroll down half a page in active pane for client {client_id}"); + if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { let scroll_rows = (active_pane.rows().max(1) - 1) / 2; active_pane.scroll_down(scroll_rows, client_id); if !active_pane.is_scrolled() { if let PaneId::Terminal(raw_fd) = active_pane.pid() { - self.process_pending_vte_events(raw_fd); + self.process_pending_vte_events(raw_fd) + .with_context(err_context)?; } } } + Ok(()) } - pub fn scroll_active_terminal_to_bottom(&mut self, client_id: ClientId) { + + pub fn scroll_active_terminal_to_bottom(&mut self, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to scroll to bottom in active pane for client {client_id}"); + if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { active_pane.clear_scroll(); if !active_pane.is_scrolled() { if let PaneId::Terminal(raw_fd) = active_pane.pid() { - self.process_pending_vte_events(raw_fd); + self.process_pending_vte_events(raw_fd) + .with_context(err_context)?; } } } + Ok(()) } - pub fn clear_active_terminal_scroll(&mut self, client_id: ClientId) { + + pub fn clear_active_terminal_scroll(&mut self, client_id: ClientId) -> Result<()> { // TODO: is this a thing? + let err_context = + || format!("failed to clear scroll in active pane for client {client_id}"); + if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { active_pane.clear_scroll(); if !active_pane.is_scrolled() { if let PaneId::Terminal(raw_fd) = active_pane.pid() { - self.process_pending_vte_events(raw_fd); + self.process_pending_vte_events(raw_fd) + .with_context(err_context)?; } } } + Ok(()) } - pub fn handle_scrollwheel_up(&mut self, point: &Position, lines: usize, client_id: ClientId) { - if let Some(pane) = self.get_pane_at(point, false) { + + pub fn handle_scrollwheel_up( + &mut self, + point: &Position, + lines: usize, + client_id: ClientId, + ) -> Result<()> { + let err_context = || { + format!("failed to handle scrollwheel up at position {point:?} for client {client_id}") + }; + + if let Some(pane) = self.get_pane_at(point, false).with_context(err_context)? { let relative_position = pane.relative_position(point); if let Some(mouse_event) = pane.mouse_scroll_up(&relative_position) { - self.write_to_terminal_at(mouse_event.into_bytes(), point); + self.write_to_terminal_at(mouse_event.into_bytes(), point) + .with_context(err_context)?; } else if pane.is_alternate_mode_active() { // faux scrolling, send UP n times // do n separate writes to make sure the sequence gets adjusted for cursor keys mode for _ in 0..lines { self.write_to_terminal_at("\u{1b}[A".as_bytes().to_owned(), point) + .with_context(err_context)?; } } else { pane.scroll_up(lines, client_id); } } + Ok(()) } - pub fn handle_scrollwheel_down(&mut self, point: &Position, lines: usize, client_id: ClientId) { - if let Some(pane) = self.get_pane_at(point, false) { + + pub fn handle_scrollwheel_down( + &mut self, + point: &Position, + lines: usize, + client_id: ClientId, + ) -> Result<()> { + let err_context = || { + format!( + "failed to handle scrollwheel down at position {point:?} for client {client_id}" + ) + }; + + if let Some(pane) = self.get_pane_at(point, false).with_context(err_context)? { let relative_position = pane.relative_position(point); if let Some(mouse_event) = pane.mouse_scroll_down(&relative_position) { - self.write_to_terminal_at(mouse_event.into_bytes(), point); + self.write_to_terminal_at(mouse_event.into_bytes(), point) + .with_context(err_context)?; } else if pane.is_alternate_mode_active() { // faux scrolling, send DOWN n times // do n separate writes to make sure the sequence gets adjusted for cursor keys mode for _ in 0..lines { self.write_to_terminal_at("\u{1b}[B".as_bytes().to_owned(), point) + .with_context(err_context)?; } } else { pane.scroll_down(lines, client_id); if !pane.is_scrolled() { if let PaneId::Terminal(pid) = pane.pid() { - self.process_pending_vte_events(pid); + self.process_pending_vte_events(pid) + .with_context(err_context)?; } } } } + Ok(()) } + fn get_pane_at( &mut self, point: &Position, search_selectable: bool, - ) -> Option<&mut Box> { + ) -> Result>> { if self.floating_panes.panes_are_visible() { if let Some(pane_id) = self.floating_panes.get_pane_id_at(point, search_selectable) { - return self.floating_panes.get_pane_mut(pane_id); + return Ok(self.floating_panes.get_pane_mut(pane_id)); } } - if let Some(pane_id) = self.get_pane_id_at(point, search_selectable) { - self.tiled_panes.get_pane_mut(pane_id) + if let Some(pane_id) = self + .get_pane_id_at(point, search_selectable) + .with_context(|| format!("failed to get pane at position {point:?}"))? + { + Ok(self.tiled_panes.get_pane_mut(pane_id)) } else { - None + Ok(None) } } - fn get_pane_id_at(&self, point: &Position, search_selectable: bool) -> Option { - if self.tiled_panes.fullscreen_is_active() && self.is_position_inside_viewport(point) { - let first_client_id = { - self.connected_clients - .borrow() - .iter() - .copied() - .next() - .unwrap() - }; // TODO: instead of doing this, record the pane that is in fullscreen - return self.tiled_panes.get_active_pane_id(first_client_id); + fn get_pane_id_at(&self, point: &Position, search_selectable: bool) -> Result> { + let err_context = || format!("failed to get id of pane at position {point:?}"); + + if self.tiled_panes.fullscreen_is_active() + && self + .is_position_inside_viewport(point) + .with_context(err_context)? + { + // TODO: instead of doing this, record the pane that is in fullscreen + let first_client_id = self + .connected_clients + .borrow() + .iter() + .copied() + .next() + .with_context(err_context)?; + return Ok(self.tiled_panes.get_active_pane_id(first_client_id)); } if search_selectable { - self.get_selectable_tiled_panes() + Ok(self + .get_selectable_tiled_panes() .find(|(_, p)| p.contains(point)) - .map(|(&id, _)| id) + .map(|(&id, _)| id)) } else { - self.get_tiled_panes() + Ok(self + .get_tiled_panes() .find(|(_, p)| p.contains(point)) - .map(|(&id, _)| id) + .map(|(&id, _)| id)) } } - pub fn handle_left_click(&mut self, position: &Position, client_id: ClientId) { - self.focus_pane_at(position, client_id); + + pub fn handle_left_click(&mut self, position: &Position, client_id: ClientId) -> Result<()> { + let err_context = || { + format!( + "failed to handle mouse left click at position {position:?} for client {client_id}" + ) + }; + + self.focus_pane_at(position, client_id) + .with_context(err_context)?; let search_selectable = false; if self.floating_panes.panes_are_visible() @@ -1790,14 +1998,18 @@ impl Tab { .move_pane_with_mouse(*position, search_selectable) { self.set_force_render(); - return; + return Ok(()); } - if let Some(pane) = self.get_pane_at(position, false) { + if let Some(pane) = self + .get_pane_at(position, false) + .with_context(err_context)? + { let relative_position = pane.relative_position(position); if let Some(mouse_event) = pane.mouse_left_click(&relative_position, false) { if !pane.position_is_on_frame(position) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; } } else { pane.start_selection(&relative_position, client_id); @@ -1806,42 +2018,73 @@ impl Tab { } } }; + Ok(()) } - pub fn handle_right_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) { + pub fn handle_right_click(&mut self, position: &Position, client_id: ClientId) -> Result<()> { + let err_context = || { + format!( + "failed to handle mouse right click at position {position:?} for client {client_id}" + ) + }; + + self.focus_pane_at(position, client_id) + .with_context(err_context)?; + + if let Some(pane) = self + .get_pane_at(position, false) + .with_context(err_context)? + { let relative_position = pane.relative_position(position); if let Some(mouse_event) = pane.mouse_right_click(&relative_position, false) { if !pane.position_is_on_frame(position) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; } } else { pane.handle_right_click(&relative_position, client_id); } }; + Ok(()) } - pub fn handle_middle_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) { + pub fn handle_middle_click(&mut self, position: &Position, client_id: ClientId) -> Result<()> { + let err_context = || { + format!( + "failed to handle mouse middle click at position {position:?} for client {client_id}" + ) + }; + + self.focus_pane_at(position, client_id) + .with_context(err_context)?; + + if let Some(pane) = self + .get_pane_at(position, false) + .with_context(err_context)? + { let relative_position = pane.relative_position(position); if let Some(mouse_event) = pane.mouse_middle_click(&relative_position, false) { if !pane.position_is_on_frame(position) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; } } }; + Ok(()) } - fn focus_pane_at(&mut self, point: &Position, client_id: ClientId) { + + fn focus_pane_at(&mut self, point: &Position, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to focus pane at position {point:?} for client {client_id}"); + if self.floating_panes.panes_are_visible() { if let Some(clicked_pane) = self.floating_panes.get_pane_id_at(point, true) { self.floating_panes.focus_pane(clicked_pane, client_id); self.set_pane_active_at(clicked_pane); - return; + return Ok(()); } } - if let Some(clicked_pane) = self.get_pane_id_at(point, true) { + if let Some(clicked_pane) = self.get_pane_id_at(point, true).with_context(err_context)? { self.tiled_panes.focus_pane(clicked_pane, client_id); self.set_pane_active_at(clicked_pane); if self.floating_panes.panes_are_visible() { @@ -1849,8 +2092,18 @@ impl Tab { self.set_force_render(); } } + Ok(()) } - pub fn handle_right_mouse_release(&mut self, position: &Position, client_id: ClientId) { + + pub fn handle_right_mouse_release( + &mut self, + position: &Position, + client_id: ClientId, + ) -> Result<()> { + let err_context = || { + format!("failed to handle right mouse release at position {position:?} for client {client_id}") + }; + self.last_mouse_hold_position = None; let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); if let Some(active_pane) = active_pane { @@ -1868,11 +2121,22 @@ impl Tab { ); if let Some(mouse_event) = active_pane.mouse_right_click_release(&relative_position) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; } } + Ok(()) } - pub fn handle_middle_mouse_release(&mut self, position: &Position, client_id: ClientId) { + + pub fn handle_middle_mouse_release( + &mut self, + position: &Position, + client_id: ClientId, + ) -> Result<()> { + let err_context = || { + format!("failed to handle middle mouse release at position {position:?} for client {client_id}") + }; + self.last_mouse_hold_position = None; let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); if let Some(active_pane) = active_pane { @@ -1890,18 +2154,29 @@ impl Tab { ); if let Some(mouse_event) = active_pane.mouse_middle_click_release(&relative_position) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; } } + Ok(()) } - pub fn handle_left_mouse_release(&mut self, position: &Position, client_id: ClientId) { + + pub fn handle_left_mouse_release( + &mut self, + position: &Position, + client_id: ClientId, + ) -> Result<()> { + let err_context = || { + format!("failed to handle left mouse release at position {position:?} for client {client_id}") + }; + self.last_mouse_hold_position = None; if self.floating_panes.panes_are_visible() && self.floating_panes.pane_is_being_moved_with_mouse() { self.floating_panes.stop_moving_pane_with_mouse(*position); - return; + return Ok(()); } // read these here to avoid use of borrowed `*self`, since we are holding active_pane @@ -1924,7 +2199,8 @@ impl Tab { ); if let Some(mouse_event) = active_pane.mouse_left_click_release(&relative_position) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; } else { let relative_position = active_pane.relative_position(position); if let PaneId::Terminal(_) = active_pane.pid() { @@ -1934,7 +2210,8 @@ impl Tab { active_pane.reset_selection(); if let Some(selected_text) = selected_text { - self.write_selection_to_clipboard(&selected_text); + self.write_selection_to_clipboard(&selected_text) + .with_context(err_context)?; } } } else { @@ -1944,12 +2221,18 @@ impl Tab { self.selecting_with_mouse = false; } } + Ok(()) } + pub fn handle_mouse_hold_left( &mut self, position_on_screen: &Position, client_id: ClientId, - ) -> bool { + ) -> Result { + let err_context = || { + format!("failed to handle left mouse hold at position {position_on_screen:?} for client {client_id}") + }; + // return value indicates whether we should trigger a render // determine if event is repeated to enable smooth scrolling let is_repeated = if let Some(last_position) = self.last_mouse_hold_position { @@ -1968,8 +2251,8 @@ impl Tab { .move_pane_with_mouse(*position_on_screen, search_selectable) { self.set_force_render(); - return !is_repeated; // we don't need to re-render in this case if the pane did not move - // return; + return Ok(!is_repeated); // we don't need to re-render in this case if the pane did not move + // return; } let selecting = self.selecting_with_mouse; @@ -1991,21 +2274,27 @@ impl Tab { .min(active_pane.get_content_rows() as isize), ); if let Some(mouse_event) = active_pane.mouse_left_click(&relative_position, true) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); - return true; // we need to re-render in this case so the selection disappears + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; + return Ok(true); // we need to re-render in this case so the selection disappears } } else if selecting { active_pane.update_selection(&relative_position, client_id); - return true; // we need to re-render in this case so the selection is updated + return Ok(true); // we need to re-render in this case so the selection is updated } } - false // we shouldn't even get here, but might as well not needlessly render if we do + Ok(false) // we shouldn't even get here, but might as well not needlessly render if we do } + pub fn handle_mouse_hold_right( &mut self, position_on_screen: &Position, client_id: ClientId, - ) -> bool { + ) -> Result { + let err_context = || { + format!("failed to handle left mouse hold at position {position_on_screen:?} for client {client_id}") + }; + // return value indicates whether we should trigger a render // determine if event is repeated to enable smooth scrolling let is_repeated = if let Some(last_position) = self.last_mouse_hold_position { @@ -2032,18 +2321,24 @@ impl Tab { .min(active_pane.get_content_rows() as isize), ); if let Some(mouse_event) = active_pane.mouse_right_click(&relative_position, true) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); - return true; // we need to re-render in this case so the selection disappears + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; + return Ok(true); // we need to re-render in this case so the selection disappears } } } - false // we shouldn't even get here, but might as well not needlessly render if we do + Ok(false) // we shouldn't even get here, but might as well not needlessly render if we do } + pub fn handle_mouse_hold_middle( &mut self, position_on_screen: &Position, client_id: ClientId, - ) -> bool { + ) -> Result { + let err_context = || { + format!("failed to handle left mouse hold at position {position_on_screen:?} for client {client_id}") + }; + // return value indicates whether we should trigger a render // determine if event is repeated to enable smooth scrolling let is_repeated = if let Some(last_position) = self.last_mouse_hold_position { @@ -2071,31 +2366,40 @@ impl Tab { ); if let Some(mouse_event) = active_pane.mouse_middle_click(&relative_position, true) { - self.write_to_active_terminal(mouse_event.into_bytes(), client_id); - return true; // we need to re-render in this case so the selection disappears + self.write_to_active_terminal(mouse_event.into_bytes(), client_id) + .with_context(err_context)?; + return Ok(true); // we need to re-render in this case so the selection disappears } } } - false // we shouldn't even get here, but might as well not needlessly render if we do + Ok(false) // we shouldn't even get here, but might as well not needlessly render if we do } - pub fn copy_selection(&self, client_id: ClientId) { + pub fn copy_selection(&self, client_id: ClientId) -> Result<()> { 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.write_selection_to_clipboard(&selected_text) + .with_context(|| { + format!("failed to write selection to clipboard for client {client_id}") + })?; self.senders .send_to_plugin(PluginInstruction::Update( None, None, Event::CopyToClipboard(self.clipboard_provider.as_copy_destination()), )) - .unwrap(); + .to_anyhow() + .with_context(|| { + format!("failed to inform plugins about copy selection for client {client_id}") + }) + .non_fatal(); } + Ok(()) } - fn write_selection_to_clipboard(&self, selection: &str) { + fn write_selection_to_clipboard(&self, selection: &str) -> Result<()> { let mut output = Output::default(); let connected_clients: HashSet = { self.connected_clients.borrow().iter().copied().collect() }; @@ -2110,7 +2414,7 @@ impl Tab { let serialized_output = output.serialize(); self.senders .send_to_server(ServerInstruction::Render(Some(serialized_output))) - .unwrap(); + .context("failed to write selection to clipboard")?; Event::CopyToClipboard(self.clipboard_provider.as_copy_destination()) }, Err(err) => { @@ -2120,7 +2424,11 @@ impl Tab { }; self.senders .send_to_plugin(PluginInstruction::Update(None, None, clipboard_event)) - .unwrap(); + .to_anyhow() + .context("failed to notify plugins about new clipboard event") + .non_fatal(); + + Ok(()) } fn offset_viewport(&mut self, position_and_size: &Viewport) { let mut viewport = self.viewport.borrow_mut(); @@ -2146,7 +2454,7 @@ impl Tab { } } - pub fn visible(&self, visible: bool) { + pub fn visible(&self, visible: bool) -> Result<()> { let pids_in_this_tab = self.tiled_panes.pane_ids().filter_map(|p| match p { PaneId::Plugin(pid) => Some(pid), _ => None, @@ -2158,11 +2466,16 @@ impl Tab { None, Event::Visible(visible), )) - .unwrap(); + .to_anyhow() + .with_context(|| format!("failed to set visibility of tab to {visible}"))?; } + Ok(()) } - pub fn update_active_pane_name(&mut self, buf: Vec, client_id: ClientId) { + pub fn update_active_pane_name(&mut self, buf: Vec, client_id: ClientId) -> Result<()> { + let err_context = + || format!("failed to update name of active pane to '{buf:?}' for client {client_id}"); + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = if self.are_floating_panes_visible() { self.floating_panes @@ -2171,18 +2484,19 @@ impl Tab { self.tiled_panes .get_pane_mut(PaneId::Terminal(active_terminal_id)) } - .unwrap(); + .with_context(err_context)?; // It only allows printable unicode, delete and backspace keys. let is_updatable = buf.iter().all(|u| matches!(u, 0x20..=0x7E | 0x08 | 0x7F)); if is_updatable { - let s = str::from_utf8(&buf).unwrap(); + let s = str::from_utf8(&buf).with_context(err_context)?; active_terminal.update_name(s); } } + Ok(()) } - pub fn undo_active_rename_pane(&mut self, client_id: ClientId) { + pub fn undo_active_rename_pane(&mut self, client_id: ClientId) -> Result<()> { if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { let active_terminal = if self.are_floating_panes_visible() { self.floating_panes @@ -2191,24 +2505,29 @@ impl Tab { self.tiled_panes .get_pane_mut(PaneId::Terminal(active_terminal_id)) } - .unwrap(); + .with_context(|| { + format!("failed to undo rename of active pane for client {client_id}") + })?; active_terminal.load_pane_name(); } + Ok(()) } - pub fn is_position_inside_viewport(&self, point: &Position) -> bool { + pub fn is_position_inside_viewport(&self, point: &Position) -> Result { let Position { line: Line(line), column: Column(column), } = *point; - let line: usize = line.try_into().unwrap(); + let line: usize = line.try_into().with_context(|| { + format!("failed to determine if position {point:?} is inside viewport") + })?; let viewport = self.viewport.borrow(); - line >= viewport.y + Ok(line >= viewport.y && column >= viewport.x && line <= viewport.y + viewport.rows - && column <= viewport.x + viewport.cols + && column <= viewport.x + viewport.cols) } pub fn set_pane_frames(&mut self, should_set_pane_frames: bool) { @@ -2220,15 +2539,18 @@ impl Tab { self.tiled_panes.panes_to_hide_count() } - pub fn update_search_term(&mut self, buf: Vec, client_id: ClientId) { + pub fn update_search_term(&mut self, buf: Vec, client_id: ClientId) -> Result<()> { if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) { // It only allows printable unicode, delete and backspace keys. let is_updatable = buf.iter().all(|u| matches!(u, 0x20..=0x7E | 0x08 | 0x7F)); if is_updatable { - let s = str::from_utf8(&buf).unwrap(); + let s = str::from_utf8(&buf).with_context(|| { + format!("failed to update search term to '{buf:?}' for client {client_id}") + })?; active_pane.update_search_term(s); } } + Ok(()) } pub fn search_down(&mut self, client_id: ClientId) { diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 3b26bc26..632b65b1 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -212,7 +212,8 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id) + .unwrap(); tab } @@ -267,7 +268,8 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) .enumerate() .map(|(i, _)| i as i32) .collect(); - tab.apply_layout(tab_layout, pane_ids, index, client_id); + tab.apply_layout(tab_layout, pane_ids, index, client_id) + .unwrap(); tab } @@ -325,7 +327,8 @@ fn create_new_tab_with_mock_pty_writer( vec![1], index, client_id, - ); + ) + .unwrap(); tab } @@ -379,7 +382,8 @@ fn create_new_tab_with_sixel_support( terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id) + .unwrap(); tab } @@ -487,8 +491,9 @@ fn dump_screen() { file_dumps: map.clone(), }); let new_pane_id = PaneId::Terminal(2); - tab.new_pane(new_pane_id, Some(client_id)); - tab.handle_pty_bytes(2, Vec::from("scratch".as_bytes())); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); + tab.handle_pty_bytes(2, Vec::from("scratch".as_bytes())) + .unwrap(); let file = "/tmp/log.sh"; tab.dump_active_terminal_screen(Some(file.to_string()), client_id); assert_eq!( @@ -508,13 +513,14 @@ fn new_floating_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.render(&mut output, None); + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -534,17 +540,18 @@ fn floating_panes_persist_across_toggles() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); - tab.toggle_floating_panes(client_id, None); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); + tab.toggle_floating_panes(client_id, None).unwrap(); // here we send bytes to the pane when it's not visible to make sure they're still handled and // we see them once we toggle the panes back tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.toggle_floating_panes(client_id, None); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -564,14 +571,15 @@ fn toggle_floating_panes_off() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.toggle_floating_panes(client_id, None); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -591,15 +599,16 @@ fn toggle_floating_panes_on() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.toggle_floating_panes(client_id, None); - tab.toggle_floating_panes(client_id, None); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -623,21 +632,26 @@ fn five_new_floating_panes() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); - tab.render(&mut output, None); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -657,14 +671,15 @@ fn increase_floating_pane_size() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); + ) + .unwrap(); tab.resize_increase(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -684,14 +699,15 @@ fn decrease_floating_pane_size() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); + ) + .unwrap(); tab.resize_decrease(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -711,14 +727,15 @@ fn resize_floating_pane_left() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); + ) + .unwrap(); tab.resize_left(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -738,14 +755,15 @@ fn resize_floating_pane_right() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); + ) + .unwrap(); tab.resize_right(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -765,14 +783,15 @@ fn resize_floating_pane_up() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); + ) + .unwrap(); tab.resize_up(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -792,14 +811,15 @@ fn resize_floating_pane_down() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); + ) + .unwrap(); tab.resize_down(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -823,22 +843,27 @@ fn move_floating_pane_focus_left() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.move_focus_left(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -868,23 +893,28 @@ fn move_floating_pane_focus_right() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.move_focus_left(client_id); tab.move_focus_right(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -914,22 +944,27 @@ fn move_floating_pane_focus_up() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.move_focus_up(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -959,23 +994,28 @@ fn move_floating_pane_focus_down() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.move_focus_up(client_id); tab.move_focus_down(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1005,23 +1045,30 @@ fn move_floating_pane_focus_with_mouse() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_left_click(&Position::new(9, 71), client_id); - tab.handle_left_mouse_release(&Position::new(9, 71), client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_left_click(&Position::new(9, 71), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(9, 71), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1051,23 +1098,30 @@ fn move_pane_focus_with_mouse_to_non_floating_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_left_click(&Position::new(4, 71), client_id); - tab.handle_left_mouse_release(&Position::new(4, 71), client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_left_click(&Position::new(4, 71), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(4, 71), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1097,23 +1151,30 @@ fn drag_pane_with_mouse() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1143,31 +1204,38 @@ fn mark_text_inside_floating_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_left_click(&Position::new(9, 71), client_id); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_left_click(&Position::new(9, 71), client_id) + .unwrap(); assert!( tab.selecting_with_mouse, "started selecting with mouse on click" ); - tab.handle_left_mouse_release(&Position::new(8, 50), client_id); + tab.handle_left_mouse_release(&Position::new(8, 50), client_id) + .unwrap(); assert!( !tab.selecting_with_mouse, "stopped selecting with mouse on release" ); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1197,25 +1265,30 @@ fn resize_tab_with_floating_panes() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.resize_whole_tab(Size { cols: 100, rows: 10, }); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, _cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1240,22 +1313,27 @@ fn shrink_whole_tab_with_floating_panes_horizontally_and_vertically() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.resize_whole_tab(Size { cols: 50, rows: 10 }); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, _cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1280,26 +1358,31 @@ fn shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_b let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id_1, Some(client_id)); - tab.new_pane(new_pane_id_2, Some(client_id)); - tab.new_pane(new_pane_id_3, Some(client_id)); - tab.new_pane(new_pane_id_4, Some(client_id)); - tab.new_pane(new_pane_id_5, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id_1, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_2, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_3, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_4, Some(client_id)).unwrap(); + tab.new_pane(new_pane_id_5, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); - tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); + ) + .unwrap(); + tab.handle_pty_bytes(3, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(4, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())) + .unwrap(); tab.resize_whole_tab(Size { cols: 50, rows: 10 }); tab.resize_whole_tab(Size { cols: 121, rows: 20, }); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let (snapshot, _cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1320,14 +1403,15 @@ fn embed_floating_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), - ); - tab.toggle_pane_embed_or_floating(client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_pane_embed_or_floating(client_id).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1347,13 +1431,14 @@ fn float_embedded_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.new_pane(new_pane_id, Some(client_id)); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am an embedded pane".as_bytes()), - ); - tab.toggle_pane_embed_or_floating(client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_pane_embed_or_floating(client_id).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1375,9 +1460,10 @@ fn cannot_float_only_embedded_pane() { tab.handle_pty_bytes( 1, Vec::from("\n\n\n I am an embedded pane".as_bytes()), - ); - tab.toggle_pane_embed_or_floating(client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_pane_embed_or_floating(client_id).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1400,8 +1486,8 @@ fn replacing_existing_wide_characters() { let mut tab = create_new_tab(size, ModeInfo::default()); let mut output = Output::default(); let pane_content = read_fixture("ncmpcpp-wide-chars"); - tab.handle_pty_bytes(1, pane_content); - tab.render(&mut output, None); + tab.handle_pty_bytes(1, pane_content).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1423,9 +1509,11 @@ fn rename_embedded_pane() { tab.handle_pty_bytes( 1, Vec::from("\n\n\n I am an embedded pane".as_bytes()), - ); - tab.update_active_pane_name("Renamed empedded pane".as_bytes().to_vec(), client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.update_active_pane_name("Renamed empedded pane".as_bytes().to_vec(), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1445,14 +1533,16 @@ fn rename_floating_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.new_pane(new_pane_id, Some(client_id)); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am a floating pane".as_bytes()), - ); - tab.toggle_pane_embed_or_floating(client_id); - tab.update_active_pane_name("Renamed floating pane".as_bytes().to_vec(), client_id); - tab.render(&mut output, None); + ) + .unwrap(); + tab.toggle_pane_embed_or_floating(client_id).unwrap(); + tab.update_active_pane_name("Renamed floating pane".as_bytes().to_vec(), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1473,8 +1563,8 @@ fn wide_characters_in_left_title_side() { let mut tab = create_new_tab(size, ModeInfo::default()); let mut output = Output::default(); let pane_content = read_fixture("title-wide-chars"); - tab.handle_pty_bytes(1, pane_content); - tab.render(&mut output, None); + tab.handle_pty_bytes(1, pane_content).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1496,11 +1586,12 @@ fn save_cursor_position_across_resizes() { tab.handle_pty_bytes( 1, Vec::from("\n\nI am some text\nI am another line of text\nLet's save the cursor position here \u{1b}[sI should be ovewritten".as_bytes()), - ); + ).unwrap(); tab.resize_whole_tab(Size { cols: 100, rows: 3 }); - tab.handle_pty_bytes(1, Vec::from("\u{1b}[uthis overwrote me!".as_bytes())); + tab.handle_pty_bytes(1, Vec::from("\u{1b}[uthis overwrote me!".as_bytes())) + .unwrap(); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1526,14 +1617,16 @@ fn move_floating_pane_with_sixel_image() { }))); let mut output = Output::new(sixel_image_store.clone(), character_cell_size); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); let fixture = read_fixture("sixel-image-500px.six"); - tab.handle_pty_bytes(2, fixture); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); + tab.handle_pty_bytes(2, fixture).unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot_with_sixel( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1561,14 +1654,16 @@ fn floating_pane_above_sixel_image() { }))); let mut output = Output::new(sixel_image_store.clone(), character_cell_size); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); let fixture = read_fixture("sixel-image-500px.six"); - tab.handle_pty_bytes(1, fixture); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); + tab.handle_pty_bytes(1, fixture).unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot_with_sixel( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1590,9 +1685,10 @@ fn suppress_tiled_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.suppress_active_pane(new_pane_id, client_id); - tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())); - tab.render(&mut output, None); + tab.suppress_active_pane(new_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1614,11 +1710,12 @@ fn suppress_floating_pane() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); - tab.suppress_active_pane(editor_pane_id, client_id); - tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())); - tab.render(&mut output, None); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); + tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1638,11 +1735,13 @@ fn close_suppressing_tiled_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.suppress_active_pane(new_pane_id, client_id); - tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())); - tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes())); + tab.suppress_active_pane(new_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes())) + .unwrap(); tab.close_pane(new_pane_id, false); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1664,13 +1763,15 @@ fn close_suppressing_floating_pane() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); - tab.suppress_active_pane(editor_pane_id, client_id); - tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())); - tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes())); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); + tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes())) + .unwrap(); tab.close_pane(editor_pane_id, false); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1690,12 +1791,14 @@ fn suppress_tiled_pane_float_it_and_close() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.suppress_active_pane(new_pane_id, client_id); - tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())); - tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes())); - tab.toggle_pane_embed_or_floating(client_id); + tab.suppress_active_pane(new_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes())) + .unwrap(); + tab.toggle_pane_embed_or_floating(client_id).unwrap(); tab.close_pane(new_pane_id, false); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1717,14 +1820,16 @@ fn suppress_floating_pane_embed_it_and_close_it() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); - tab.suppress_active_pane(editor_pane_id, client_id); - tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())); - tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes())); - tab.toggle_pane_embed_or_floating(client_id); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); + tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); + tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes())) + .unwrap(); + tab.toggle_pane_embed_or_floating(client_id).unwrap(); tab.close_pane(editor_pane_id, false); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1744,13 +1849,14 @@ fn resize_whole_tab_while_tiled_pane_is_suppressed() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.suppress_active_pane(new_pane_id, client_id); - tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())); + tab.suppress_active_pane(new_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); tab.resize_whole_tab(Size { cols: 100, rows: 10, }); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1772,15 +1878,16 @@ fn resize_whole_tab_while_floting_pane_is_suppressed() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); - tab.suppress_active_pane(editor_pane_id, client_id); - tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); + tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); + tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes())) + .unwrap(); tab.resize_whole_tab(Size { cols: 100, rows: 10, }); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1804,8 +1911,8 @@ fn enter_search_pane() { let mut tab = create_new_tab(size, mode_info); let mut output = Output::default(); let pane_content = read_fixture("grid_copy"); - tab.handle_pty_bytes(1, pane_content); - tab.render(&mut output, None); + tab.handle_pty_bytes(1, pane_content).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1817,8 +1924,9 @@ fn enter_search_pane() { // Pane title should show 'tortor' as search term // Only lines containing 'tortor' get marked as render-targets, so // only those are updated (search-styling is not visible here). - tab.update_search_term("tortor".as_bytes().to_vec(), client_id); - tab.render(&mut output, None); + tab.update_search_term("tortor".as_bytes().to_vec(), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1831,7 +1939,7 @@ fn enter_search_pane() { tab.toggle_search_wrap(client_id); tab.toggle_search_whole_words(client_id); tab.toggle_search_case_sensitivity(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1845,7 +1953,7 @@ fn enter_search_pane() { tab.toggle_search_whole_words(client_id); tab.toggle_search_case_sensitivity(client_id); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1869,12 +1977,12 @@ fn enter_search_floating_pane() { let mut tab = create_new_tab(size, mode_info); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None); - tab.new_pane(new_pane_id, Some(client_id)); + tab.toggle_floating_panes(client_id, None).unwrap(); + tab.new_pane(new_pane_id, Some(client_id)).unwrap(); let pane_content = read_fixture("grid_copy"); - tab.handle_pty_bytes(2, pane_content); - tab.render(&mut output, None); + tab.handle_pty_bytes(2, pane_content).unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1884,8 +1992,9 @@ fn enter_search_floating_pane() { assert_snapshot!("search_floating_tab_nothing_highlighted", snapshot); // Only the line inside the floating tab which contain 'fring' should be in the new snapshot - tab.update_search_term("fring".as_bytes().to_vec(), client_id); - tab.render(&mut output, None); + tab.update_search_term("fring".as_bytes().to_vec(), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -1912,18 +2021,30 @@ fn pane_in_sgr_button_event_tracking_mouse_mode() { pty_instruction_bus.start(); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1002;1006h"); // button event tracking (1002) with SGR encoding (1006) - tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); - tab.handle_right_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); - tab.handle_right_mouse_release(&Position::new(7, 75), client_id); - tab.handle_middle_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); - tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); - tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); - tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); + tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()) + .unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_right_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_middle_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id) + .unwrap(); pty_instruction_bus.exit(); @@ -1962,18 +2083,30 @@ fn pane_in_sgr_normal_event_tracking_mouse_mode() { pty_instruction_bus.start(); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1000;1006h"); // normal event tracking (1000) with sgr encoding (1006) - tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); - tab.handle_right_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); - tab.handle_right_mouse_release(&Position::new(7, 75), client_id); - tab.handle_middle_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); - tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); - tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); - tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); + tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()) + .unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_right_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_middle_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id) + .unwrap(); pty_instruction_bus.exit(); @@ -2012,18 +2145,30 @@ fn pane_in_utf8_button_event_tracking_mouse_mode() { pty_instruction_bus.start(); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1002;1005h"); // button event tracking (1002) with utf8 encoding (1005) - tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); - tab.handle_right_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); - tab.handle_right_mouse_release(&Position::new(7, 75), client_id); - tab.handle_middle_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); - tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); - tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); - tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); + tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()) + .unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_right_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_middle_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id) + .unwrap(); pty_instruction_bus.exit(); @@ -2062,18 +2207,30 @@ fn pane_in_utf8_normal_event_tracking_mouse_mode() { pty_instruction_bus.start(); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1000;1005h"); // normal event tracking (1000) with sgr encoding (1006) - tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); - tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); - tab.handle_left_mouse_release(&Position::new(7, 75), client_id); - tab.handle_right_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); - tab.handle_right_mouse_release(&Position::new(7, 75), client_id); - tab.handle_middle_click(&Position::new(5, 71), client_id); - tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); - tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); - tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); - tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); + tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()) + .unwrap(); + tab.handle_left_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_right_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_middle_click(&Position::new(5, 71), client_id) + .unwrap(); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id) + .unwrap(); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id) + .unwrap(); + tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id) + .unwrap(); pty_instruction_bus.exit(); @@ -2115,7 +2272,7 @@ fn tab_with_basic_layout() { let client_id = 1; let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); let mut output = Output::default(); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -2155,7 +2312,7 @@ fn tab_with_nested_layout() { let client_id = 1; let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); let mut output = Output::default(); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -2189,7 +2346,7 @@ fn tab_with_nested_uneven_layout() { let client_id = 1; let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); let mut output = Output::default(); - tab.render(&mut output, None); + tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().get(&client_id).unwrap(), size.rows, @@ -2218,9 +2375,12 @@ fn pane_bracketed_paste_ignored_when_not_in_bracketed_paste_mode() { let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~ let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201 - tab.write_to_active_terminal(bracketed_paste_start, client_id); - tab.write_to_active_terminal("test".as_bytes().to_vec(), client_id); - tab.write_to_active_terminal(bracketed_paste_end, client_id); + tab.write_to_active_terminal(bracketed_paste_start, client_id) + .unwrap(); + tab.write_to_active_terminal("test".as_bytes().to_vec(), client_id) + .unwrap(); + tab.write_to_active_terminal(bracketed_paste_end, client_id) + .unwrap(); pty_instruction_bus.exit(); @@ -2248,18 +2408,26 @@ fn pane_faux_scrolling_in_alternate_mode() { let set_application_mode = String::from("\u{1b}[?1h"); // no output since alternate scren not active yet - tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id); - tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id); + tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id) + .unwrap(); - tab.handle_pty_bytes(1, enable_alternate_screen.as_bytes().to_vec()); + tab.handle_pty_bytes(1, enable_alternate_screen.as_bytes().to_vec()) + .unwrap(); // CSI A * lines_to_scroll, CSI B * lines_to_scroll - tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id); - tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id); + tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id) + .unwrap(); - tab.handle_pty_bytes(1, set_application_mode.as_bytes().to_vec()); + tab.handle_pty_bytes(1, set_application_mode.as_bytes().to_vec()) + .unwrap(); // SS3 A * lines_to_scroll, SS3 B * lines_to_scroll - tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id); - tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id); + tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id) + .unwrap(); + tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id) + .unwrap(); pty_instruction_bus.exit(); diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 60604b00..3f20ded9 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -131,7 +131,8 @@ fn create_new_tab(size: Size) -> Tab { terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id) + .unwrap(); tab } @@ -177,7 +178,8 @@ fn create_new_tab_with_cell_size( terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id) + .unwrap(); tab } @@ -188,16 +190,17 @@ fn write_to_suppressed_pane() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); // Suppress pane 2 and remove it from active panes - tab.suppress_active_pane(PaneId::Terminal(2), 1); + tab.suppress_active_pane(PaneId::Terminal(2), 1).unwrap(); tab.tiled_panes.remove_pane(PaneId::Terminal(2)); // Make sure it's suppressed now tab.suppressed_panes.get(&PaneId::Terminal(2)).unwrap(); // Write content to it - tab.write_to_pane_id(vec![34, 127, 31, 82, 17, 182], PaneId::Terminal(2)); + tab.write_to_pane_id(vec![34, 127, 31, 82, 17, 182], PaneId::Terminal(2)) + .unwrap(); } #[test] @@ -208,7 +211,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, 1); + tab.vertical_split(new_pane_id, 1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "The tab has two panes"); assert_eq!( tab.tiled_panes @@ -304,7 +307,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, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "The tab has two panes"); assert_eq!( @@ -403,7 +406,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, Some(1)); + tab.new_pane(new_pane_id, Some(1)).unwrap(); } assert_eq!(tab.tiled_panes.panes.len(), 4, "The tab has four panes"); @@ -584,7 +587,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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); assert_eq!( tab.tiled_panes.panes.len(), 1, @@ -596,7 +599,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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); assert_eq!( tab.tiled_panes.panes.len(), 1, @@ -608,7 +611,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), Some(1)); + tab.new_pane(PaneId::Terminal(2), Some(1)).unwrap(); assert_eq!( tab.tiled_panes.panes.len(), 1, @@ -625,7 +628,7 @@ 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, Some(1)); + tab.new_pane(new_pane_id, Some(1)).unwrap(); } tab.toggle_active_pane_fullscreen(1); assert_eq!( @@ -698,10 +701,10 @@ fn switch_to_next_pane_fullscreen() { let mut active_tab = create_new_tab(size); - active_tab.new_pane(PaneId::Terminal(1), Some(1)); - active_tab.new_pane(PaneId::Terminal(2), Some(1)); - active_tab.new_pane(PaneId::Terminal(3), Some(1)); - active_tab.new_pane(PaneId::Terminal(4), Some(1)); + active_tab.new_pane(PaneId::Terminal(1), Some(1)).unwrap(); + active_tab.new_pane(PaneId::Terminal(2), Some(1)).unwrap(); + active_tab.new_pane(PaneId::Terminal(3), Some(1)).unwrap(); + active_tab.new_pane(PaneId::Terminal(4), Some(1)).unwrap(); active_tab.toggle_active_pane_fullscreen(1); // order is now 1 ->2 -> 3 -> 4 due to how new panes are inserted @@ -730,10 +733,10 @@ fn switch_to_prev_pane_fullscreen() { //testing four consecutive switches in fullscreen mode - active_tab.new_pane(PaneId::Terminal(1), Some(1)); - active_tab.new_pane(PaneId::Terminal(2), Some(1)); - active_tab.new_pane(PaneId::Terminal(3), Some(1)); - active_tab.new_pane(PaneId::Terminal(4), Some(1)); + active_tab.new_pane(PaneId::Terminal(1), Some(1)).unwrap(); + active_tab.new_pane(PaneId::Terminal(2), Some(1)).unwrap(); + active_tab.new_pane(PaneId::Terminal(3), Some(1)).unwrap(); + active_tab.new_pane(PaneId::Terminal(4), Some(1)).unwrap(); active_tab.toggle_active_pane_fullscreen(1); // order is now 1 2 3 4 @@ -768,8 +771,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, 1); - tab.close_focused_pane(1); + tab.horizontal_split(new_pane_id, 1).unwrap(); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -833,9 +836,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, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.move_focus_up(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -896,8 +899,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, 1); - tab.close_focused_pane(1); + tab.vertical_split(new_pane_id, 1).unwrap(); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -958,9 +961,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, 1); + tab.vertical_split(new_pane_id, 1).unwrap(); tab.move_focus_left(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 1, "One pane left in tab"); assert_eq!( @@ -1024,11 +1027,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, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_2, 1).unwrap(); tab.move_focus_down(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1135,10 +1138,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, 1); - tab.vertical_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); + tab.vertical_split(new_pane_id_2, 1).unwrap(); tab.move_focus_up(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1245,11 +1248,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, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); tab.move_focus_right(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1356,10 +1359,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, 1); - tab.horizontal_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); tab.move_focus_left(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "Two panes left in tab"); assert_eq!( @@ -1471,21 +1474,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, 1); - tab.vertical_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); + tab.vertical_split(new_pane_id_2, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_left(1); - tab.horizontal_split(new_pane_id_3, 1); + tab.horizontal_split(new_pane_id_3, 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(new_pane_id_4, 1); + tab.horizontal_split(new_pane_id_4, 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(new_pane_id_5, 1); + tab.horizontal_split(new_pane_id_5, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); tab.resize_down(1); - tab.vertical_split(new_pane_id_6, 1); + tab.vertical_split(new_pane_id_6, 1).unwrap(); tab.move_focus_down(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 6, "Six panes left in tab"); @@ -1771,20 +1774,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, 1); - tab.vertical_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); + tab.vertical_split(new_pane_id_2, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_left(1); - tab.horizontal_split(new_pane_id_3, 1); + tab.horizontal_split(new_pane_id_3, 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(new_pane_id_4, 1); + tab.horizontal_split(new_pane_id_4, 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(new_pane_id_5, 1); + tab.horizontal_split(new_pane_id_5, 1).unwrap(); tab.move_focus_left(1); tab.resize_up(1); - tab.vertical_split(new_pane_id_6, 1); + tab.vertical_split(new_pane_id_6, 1).unwrap(); tab.move_focus_up(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 6, "Six panes left in tab"); @@ -2072,23 +2075,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, 1); - tab.horizontal_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); tab.move_focus_up(1); tab.move_focus_up(1); - tab.vertical_split(new_pane_id_3, 1); + tab.vertical_split(new_pane_id_3, 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(new_pane_id_4, 1); + tab.vertical_split(new_pane_id_4, 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(new_pane_id_5, 1); + tab.vertical_split(new_pane_id_5, 1).unwrap(); 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.horizontal_split(new_pane_id_6, 1).unwrap(); tab.move_focus_right(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 6, "Six panes left in tab"); @@ -2376,22 +2379,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, 1); - tab.horizontal_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); tab.move_focus_up(1); tab.move_focus_up(1); - tab.vertical_split(new_pane_id_3, 1); + tab.vertical_split(new_pane_id_3, 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(new_pane_id_4, 1); + tab.vertical_split(new_pane_id_4, 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(new_pane_id_5, 1); + tab.vertical_split(new_pane_id_5, 1).unwrap(); 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.horizontal_split(new_pane_id_6, 1).unwrap(); tab.move_focus_left(1); - tab.close_focused_pane(1); + tab.close_focused_pane(1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 6, "Six panes left in tab"); @@ -2663,7 +2666,7 @@ 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, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.move_focus_up(1); tab.move_focus_down(1); @@ -2685,9 +2688,9 @@ 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, 1); - tab.vertical_split(new_pane_id_2, 1); - tab.vertical_split(new_pane_id_3, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); + tab.vertical_split(new_pane_id_2, 1).unwrap(); + tab.vertical_split(new_pane_id_3, 1).unwrap(); tab.move_focus_up(1); tab.move_focus_down(1); @@ -2712,7 +2715,7 @@ 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, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.move_focus_up(1); assert_eq!( @@ -2733,10 +2736,10 @@ 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, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(new_pane_id_2, 1); - tab.vertical_split(new_pane_id_3, 1); + tab.vertical_split(new_pane_id_2, 1).unwrap(); + tab.vertical_split(new_pane_id_3, 1).unwrap(); tab.move_focus_down(1); tab.move_focus_up(1); @@ -2761,7 +2764,7 @@ 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, 1); + tab.vertical_split(new_pane_id, 1).unwrap(); tab.move_focus_left(1); assert_eq!( @@ -2782,10 +2785,10 @@ 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, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(new_pane_id_2, 1); - tab.horizontal_split(new_pane_id_3, 1); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); + tab.horizontal_split(new_pane_id_3, 1).unwrap(); tab.move_focus_right(1); tab.move_focus_left(1); @@ -2810,7 +2813,7 @@ 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, 1); + tab.vertical_split(new_pane_id, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_right(1); @@ -2832,9 +2835,9 @@ 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, 1); - tab.horizontal_split(new_pane_id_2, 1); - tab.horizontal_split(new_pane_id_3, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); + tab.horizontal_split(new_pane_id_3, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_right(1); @@ -2859,7 +2862,7 @@ pub fn move_active_pane_down() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.move_focus_up(1); tab.move_active_pane_down(1); @@ -2886,9 +2889,9 @@ pub fn move_active_pane_down_to_the_most_recently_used_position() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - 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.horizontal_split(new_pane_id_1, 1).unwrap(); + tab.vertical_split(new_pane_id_2, 1).unwrap(); + tab.vertical_split(new_pane_id_3, 1).unwrap(); tab.move_focus_up(1); tab.move_active_pane_down(1); @@ -2918,7 +2921,7 @@ pub fn move_active_pane_up() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.horizontal_split(new_pane_id, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.move_active_pane_up(1); assert_eq!( @@ -2944,10 +2947,10 @@ pub fn move_active_pane_up_to_the_most_recently_used_position() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.horizontal_split(new_pane_id_1, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(new_pane_id_2, 1); - tab.vertical_split(new_pane_id_3, 1); + tab.vertical_split(new_pane_id_2, 1).unwrap(); + tab.vertical_split(new_pane_id_3, 1).unwrap(); tab.move_focus_down(1); tab.move_active_pane_up(1); @@ -2978,7 +2981,7 @@ pub fn move_active_pane_left() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id, 1); + tab.vertical_split(new_pane_id, 1).unwrap(); tab.move_active_pane_left(1); assert_eq!( @@ -3004,10 +3007,10 @@ pub fn move_active_pane_left_to_the_most_recently_used_position() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.vertical_split(new_pane_id_1, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(new_pane_id_2, 1); - tab.horizontal_split(new_pane_id_3, 1); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); + tab.horizontal_split(new_pane_id_3, 1).unwrap(); tab.move_focus_right(1); tab.move_active_pane_left(1); @@ -3038,7 +3041,7 @@ pub fn move_active_pane_right() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); - tab.vertical_split(new_pane_id, 1); + tab.vertical_split(new_pane_id, 1).unwrap(); tab.move_focus_left(1); tab.move_active_pane_right(1); @@ -3065,9 +3068,9 @@ pub fn move_active_pane_right_to_the_most_recently_used_position() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - 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.vertical_split(new_pane_id_1, 1).unwrap(); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); + tab.horizontal_split(new_pane_id_3, 1).unwrap(); tab.move_focus_left(1); tab.move_active_pane_right(1); @@ -3105,7 +3108,7 @@ 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, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.resize_down(1); assert_eq!( @@ -3211,7 +3214,7 @@ 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, 1); + tab.horizontal_split(new_pane_id, 1).unwrap(); tab.move_focus_up(1); tab.resize_down(1); @@ -3324,8 +3327,8 @@ 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, 1); - tab.horizontal_split(new_pane_id_2, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); + tab.horizontal_split(new_pane_id_2, 1).unwrap(); tab.move_focus_up(1); tab.resize_down(1); @@ -3477,9 +3480,9 @@ 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, 1); + tab.horizontal_split(new_pane_id_1, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(new_pane_id_2, 1); + tab.vertical_split(new_pane_id_2, 1).unwrap(); tab.move_focus_down(1); tab.resize_down(1); @@ -3632,10 +3635,10 @@ 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, 1); - tab.vertical_split(focused_pane, 1); + tab.horizontal_split(pane_to_the_left, 1).unwrap(); + tab.vertical_split(focused_pane, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(pane_above, 1); + tab.vertical_split(pane_above, 1).unwrap(); tab.move_focus_down(1); tab.resize_down(1); @@ -3832,10 +3835,10 @@ 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, 1); - tab.vertical_split(pane_below, 1); + tab.horizontal_split(pane_below_and_left, 1).unwrap(); + tab.vertical_split(pane_below, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(focused_pane, 1); + tab.vertical_split(focused_pane, 1).unwrap(); tab.resize_down(1); assert_eq!( @@ -4031,10 +4034,10 @@ 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, 1); - tab.vertical_split(pane_to_the_right, 1); + tab.horizontal_split(focused_pane, 1).unwrap(); + tab.vertical_split(pane_to_the_right, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(pane_above_and_right, 1); + tab.vertical_split(pane_above_and_right, 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); tab.resize_down(1); @@ -4232,10 +4235,10 @@ 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, 1); - tab.vertical_split(pane_below_and_right, 1); + tab.horizontal_split(pane_below, 1).unwrap(); + tab.vertical_split(pane_below_and_right, 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(pane_to_the_right, 1); + tab.vertical_split(pane_to_the_right, 1).unwrap(); tab.move_focus_left(1); tab.resize_down(1); @@ -4428,12 +4431,12 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); tab.move_focus_down(1); tab.resize_down(1); @@ -4713,12 +4716,12 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); tab.resize_down(1); @@ -4997,16 +5000,16 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); - tab.vertical_split(PaneId::Terminal(7), 1); - tab.vertical_split(PaneId::Terminal(8), 1); + tab.vertical_split(PaneId::Terminal(7), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_left(1); tab.resize_down(1); @@ -5371,16 +5374,16 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_left(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(7), 1); - tab.vertical_split(PaneId::Terminal(8), 1); + tab.vertical_split(PaneId::Terminal(7), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); tab.move_focus_left(1); @@ -5745,7 +5748,7 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); tab.resize_down(1); @@ -5787,7 +5790,7 @@ pub fn resize_left_with_pane_to_the_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.resize_left(1); assert_eq!( @@ -5890,7 +5893,7 @@ pub fn resize_left_with_pane_to_the_right() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); tab.resize_left(1); @@ -5995,8 +5998,8 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_left(1); tab.resize_left(1); @@ -6143,9 +6146,9 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); tab.resize_left(1); @@ -6293,10 +6296,10 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_down(1); tab.resize_left(1); @@ -6487,10 +6490,10 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); tab.resize_left(1); @@ -6682,10 +6685,10 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.resize_left(1); assert_eq!( @@ -6875,10 +6878,10 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_left(1); tab.resize_left(1); @@ -7071,13 +7074,13 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_down(1); tab.resize_left(1); @@ -7356,13 +7359,13 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); tab.resize_left(1); @@ -7642,17 +7645,17 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); 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.vertical_split(PaneId::Terminal(6), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(7), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_up(1); tab.resize_left(1); @@ -8017,18 +8020,18 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); tab.move_focus_down(1); tab.resize_down(1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(7), 1); - tab.horizontal_split(PaneId::Terminal(8), 1); + tab.horizontal_split(PaneId::Terminal(7), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_up(1); tab.resize_left(1); @@ -8388,7 +8391,7 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.resize_left(1); assert_eq!( @@ -8429,7 +8432,7 @@ pub fn resize_right_with_pane_to_the_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.resize_right(1); assert_eq!( @@ -8533,7 +8536,7 @@ pub fn resize_right_with_pane_to_the_right() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); tab.resize_right(1); @@ -8638,8 +8641,8 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_left(1); tab.resize_right(1); @@ -8787,9 +8790,9 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); tab.resize_right(1); @@ -8937,11 +8940,11 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(4), 1).unwrap(); tab.resize_right(1); assert_eq!( @@ -9130,11 +9133,11 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_left(1); tab.resize_right(1); @@ -9325,11 +9328,11 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.resize_right(1); @@ -9520,11 +9523,11 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); - tab.horizontal_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.move_focus_left(1); tab.resize_right(1); @@ -9718,13 +9721,13 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_down(1); tab.resize_right(1); @@ -10002,13 +10005,13 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); tab.resize_right(1); @@ -10287,17 +10290,17 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); 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.vertical_split(PaneId::Terminal(6), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(7), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_up(1); tab.resize_right(1); @@ -10661,18 +10664,18 @@ 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), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); tab.move_focus_down(1); tab.resize_up(1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(7), 1); - tab.horizontal_split(PaneId::Terminal(8), 1); + tab.horizontal_split(PaneId::Terminal(7), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_up(1); tab.resize_right(1); @@ -11031,7 +11034,7 @@ 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), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.resize_right(1); assert_eq!( @@ -11073,7 +11076,7 @@ pub fn resize_up_with_pane_above() { rows: 20, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.resize_up(1); assert_eq!( @@ -11178,7 +11181,7 @@ pub fn resize_up_with_pane_below() { rows: 20, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); tab.resize_up(1); @@ -11287,8 +11290,8 @@ pub fn resize_up_with_panes_above_and_below() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2), 1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_up(1); tab.resize_up(1); @@ -11436,9 +11439,9 @@ pub fn resize_up_with_multiple_panes_above() { rows: 30, }; let mut tab = create_new_tab(size); - tab.horizontal_split(PaneId::Terminal(2), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_down(1); tab.resize_up(1); @@ -11585,11 +11588,11 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.resize_up(1); assert_eq!( @@ -11780,11 +11783,11 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); tab.resize_up(1); @@ -11976,11 +11979,11 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_left(1); tab.resize_up(1); @@ -12172,11 +12175,11 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); tab.resize_up(1); @@ -12369,12 +12372,12 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); tab.resize_up(1); @@ -12652,12 +12655,12 @@ 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), 1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); tab.resize_up(1); @@ -12936,16 +12939,16 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); tab.move_focus_left(1); - tab.vertical_split(PaneId::Terminal(7), 1); - tab.vertical_split(PaneId::Terminal(8), 1); + tab.vertical_split(PaneId::Terminal(7), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_left(1); tab.resize_up(1); @@ -13309,17 +13312,17 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_up(1); - tab.vertical_split(PaneId::Terminal(3), 1); - tab.vertical_split(PaneId::Terminal(4), 1); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(4), 1).unwrap(); tab.move_focus_down(1); - tab.vertical_split(PaneId::Terminal(5), 1); - tab.vertical_split(PaneId::Terminal(6), 1); + tab.vertical_split(PaneId::Terminal(5), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(6), 1).unwrap(); 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.vertical_split(PaneId::Terminal(7), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(8), 1).unwrap(); tab.move_focus_left(1); tab.resize_up(1); @@ -13682,7 +13685,7 @@ 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), 1); + tab.horizontal_split(PaneId::Terminal(2), 1).unwrap(); tab.resize_down(1); assert_eq!( @@ -13739,7 +13742,7 @@ pub fn nondirectional_resize_increase_with_1_pane_to_left() { }; let mut tab = create_new_tab(size); let new_pane_id_1 = PaneId::Terminal(2); - tab.vertical_split(new_pane_id_1, 1); + tab.vertical_split(new_pane_id_1, 1).unwrap(); tab.resize_increase(1); // should behave like `resize_left_with_pane_to_the_left` @@ -13772,9 +13775,9 @@ pub fn nondirectional_resize_increase_with_2_panes_to_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_right(1); tab.resize_increase(1); @@ -13830,9 +13833,9 @@ pub fn nondirectional_resize_increase_with_1_pane_to_right_1_pane_above() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); tab.move_focus_left(1); - tab.horizontal_split(PaneId::Terminal(3), 1); + tab.horizontal_split(PaneId::Terminal(3), 1).unwrap(); tab.resize_increase(1); assert_eq!( @@ -13886,8 +13889,8 @@ pub fn nondirectional_resize_increase_with_1_pane_to_right_1_pane_to_left() { rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_left(1); tab.resize_increase(1); @@ -13942,8 +13945,8 @@ pub fn nondirectional_resize_increase_with_pane_above_aligned_right_with_current rows: 20, }; let mut tab = create_new_tab(size); - tab.vertical_split(PaneId::Terminal(2), 1); - tab.vertical_split(PaneId::Terminal(3), 1); + tab.vertical_split(PaneId::Terminal(2), 1).unwrap(); + tab.vertical_split(PaneId::Terminal(3), 1).unwrap(); tab.move_focus_left(1); tab.resize_increase(1); diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 16b962f1..9a08be2a 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -746,7 +746,7 @@ fn switch_to_tab_with_fullscreen() { new_tab(&mut screen, 1); { let active_tab = screen.get_active_tab_mut(1).unwrap(); - active_tab.new_pane(PaneId::Terminal(2), Some(1)); + active_tab.new_pane(PaneId::Terminal(2), Some(1)).unwrap(); active_tab.toggle_active_pane_fullscreen(1); } new_tab(&mut screen, 2); @@ -859,7 +859,7 @@ fn attach_after_first_tab_closed() { new_tab(&mut screen, 1); { let active_tab = screen.get_active_tab_mut(1).unwrap(); - active_tab.new_pane(PaneId::Terminal(2), Some(1)); + active_tab.new_pane(PaneId::Terminal(2), Some(1)).unwrap(); active_tab.toggle_active_pane_fullscreen(1); } new_tab(&mut screen, 2); diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 02fad102..0c2ad1e9 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.45" +anyhow = { version = "1.0.45", features = ["backtrace"] } backtrace = "0.3.55" rmp-serde = "1.1.0" clap = { version = "3.2.2", features = ["derive", "env"] } diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index ec9d4de3..f729c036 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -22,6 +22,8 @@ use thiserror::Error as ThisError; pub mod prelude { pub use super::FatalError; pub use super::LoggableError; + #[cfg(not(target_family = "wasm"))] + pub use super::ToAnyhow; pub use anyhow::anyhow; pub use anyhow::bail; pub use anyhow::Context; @@ -38,10 +40,18 @@ pub trait ErrorInstruction { struct Panic(String); impl Panic { + // We already capture a backtrace with `anyhow` using the `backtrace` crate in the background. + // The advantage is that this is the backtrace of the real errors source (i.e. where we first + // encountered the error and turned it into an `anyhow::Error`), whereas the backtrace recorded + // here is the backtrace leading to the call to any `panic`ing function. Since now we propagate + // errors up before `unwrap`ing them (e.g. in `zellij_server::screen::screen_thread_main`), the + // former is what we really want to diagnose. + // We still keep the second one around just in case the first backtrace isn't meaningful or + // non-existent in the first place (Which really shouldn't happen, but you never know). fn show_backtrace(&self) -> String { if let Ok(var) = std::env::var("RUST_BACKTRACE") { if !var.is_empty() && var != "0" { - return format!("\n{:?}", backtrace::Backtrace::new()); + return format!("\n\nPanic backtrace:\n{:?}", backtrace::Backtrace::new()); } } "".into() @@ -513,4 +523,34 @@ mod not_wasm { Ok(()) } } + + /// Helper trait to convert error types that don't satisfy `anyhow`s trait requirements to + /// anyhow errors. + pub trait ToAnyhow { + fn to_anyhow(self) -> crate::anyhow::Result; + } + + /// `SendError` doesn't satisfy `anyhow`s trait requirements due to `T` possibly being a + /// `PluginInstruction` type, which wraps an `mpsc::Send` and isn't `Sync`. Due to this, in turn, + /// the whole error type isn't `Sync` and doesn't work with `anyhow` (or pretty much any other + /// error handling crate). + /// + /// Takes the `SendError` and creates an `anyhow` error type with the message that was sent + /// (formatted as string), attaching the [`ErrorContext`] as anyhow context to it. + impl ToAnyhow + for Result> + { + fn to_anyhow(self) -> crate::anyhow::Result { + match self { + Ok(val) => crate::anyhow::Ok(val), + Err(e) => { + let (msg, context) = e.into_inner(); + Err( + crate::anyhow::anyhow!("failed to send message to client: {:#?}", msg) + .context(context.to_string()), + ) + }, + } + } + } }