diff options
| author | Aram Drevekenin <aram@poor.dev> | 2026-02-15 12:55:22 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-15 12:55:22 +0100 |
| commit | 753110669046196d94ffb1824f6c827a7497eaf0 (patch) | |
| tree | 589a9515cdc0e37ef00446955503dbb001cc7c03 | |
| parent | Allow plugins to read env vars present upon session creation (behind permissi... (diff) | |
| download | sl0ppij-753110669046196d94ffb1824f6c827a7497eaf0.tar.gz sl0ppij-753110669046196d94ffb1824f6c827a7497eaf0.tar.xz sl0ppij-753110669046196d94ffb1824f6c827a7497eaf0.zip | |
Fix startup message and cache file creation (#4705)
* only show startup loading message if we've been loading for more than 400ms
* create relevant cache folders before attempting to write startup cache files
* fix flaky tests
* rustfmt
* add pr
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | zellij-client/src/lib.rs | 51 | ||||
| -rw-r--r-- | zellij-client/src/stdin_ansi_parser.rs | 3 | ||||
| -rw-r--r-- | zellij-server/src/lib.rs | 3 | ||||
| -rw-r--r-- | zellij-server/src/unit/screen_tests.rs | 21 | ||||
| -rw-r--r-- | zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap | 19 | ||||
| -rw-r--r-- | zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap | 4 | ||||
| -rw-r--r-- | zellij-utils/src/channels.rs | 3 |
8 files changed, 85 insertions, 20 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index fb9ac53f..567a2b15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * feat: allow querying tab info (id, name and position) from the CLI as well as general info about all tabs (https://github.com/zellij-org/zellij/pull/4695) * feat: propagate plugin configuration changes to plugins (https://github.com/zellij-org/zellij/pull/4698) * feat: allow plugins to query env vars set upon session creation (gated behind a relevant permission) (https://github.com/zellij-org/zellij/pull/4700) +* fix: make sure cache folders exist in all startup paths (https://github.com/zellij-org/zellij/pull/4705) ## [0.43.1] - 2025-08-08 diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index b5224eb4..4061b690 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -83,7 +83,7 @@ use crate::{ use termwiz::input::InputEvent; use zellij_utils::cli::CliArgs; use zellij_utils::{ - channels::{self, ChannelWithContext, SenderWithContext}, + channels::{self, ChannelWithContext, RecvTimeoutError, SenderWithContext}, consts::{set_permissions, ZELLIJ_SOCK_DIR}, data::{ClientId, ConnectToSession, KeyWithModifier, LayoutInfo, LayoutMetadata}, envs, @@ -947,6 +947,9 @@ pub fn start_client( let mut exit_msg = String::new(); let mut loading = true; + let mut showed_loading_message = false; + let loading_start = std::time::Instant::now(); + let loading_delay = std::time::Duration::from_millis(400); let mut pending_instructions = vec![]; let mut synchronised_output = match os_input.env_variable("TERM").as_deref() { Some("alacritty") => Some(SyncOutput::DCS), @@ -954,16 +957,34 @@ pub fn start_client( }; let mut stdout = os_input.get_stdout_writer(); - stdout - .write_all("\u{1b}[1m\u{1b}[HLoading Zellij\u{1b}[m\n\r".as_bytes()) - .expect("cannot write to stdout"); - stdout.flush().expect("could not flush"); loop { let (client_instruction, mut err_ctx) = if !loading && !pending_instructions.is_empty() { // there are buffered instructions, we need to go through them before processing the // new ones pending_instructions.remove(0) + } else if loading && !showed_loading_message { + let remaining = loading_delay + .checked_sub(loading_start.elapsed()) + .unwrap_or(std::time::Duration::ZERO); + match receive_client_instructions.recv_timeout(remaining) { + Ok(instruction) => instruction, + Err(RecvTimeoutError::Timeout) => { + // 400ms elapsed with no completion — show loading UI + stdout + .write_all("\u{1b}[1m\u{1b}[HLoading Zellij\u{1b}[m\n\r".as_bytes()) + .expect("cannot write to stdout"); + stdout.flush().expect("could not flush"); + showed_loading_message = true; + // Now block normally + receive_client_instructions + .recv() + .expect("failed to receive app instruction on channel") + }, + Err(RecvTimeoutError::Disconnected) => { + panic!("client instruction channel disconnected"); + }, + } } else { receive_client_instructions .recv() @@ -974,16 +995,20 @@ pub fn start_client( // when the app is still loading, we buffer instructions and show a loading screen match client_instruction { ClientInstruction::StartedParsingStdinQuery => { - stdout - .write_all("Querying terminal emulator for \u{1b}[32;1mdefault colors\u{1b}[m and \u{1b}[32;1mpixel/cell\u{1b}[m ratio...".as_bytes()) - .expect("cannot write to stdout"); - stdout.flush().expect("could not flush"); + if showed_loading_message { + stdout + .write_all("Querying terminal emulator for \u{1b}[32;1mdefault colors\u{1b}[m and \u{1b}[32;1mpixel/cell\u{1b}[m ratio...".as_bytes()) + .expect("cannot write to stdout"); + stdout.flush().expect("could not flush"); + } }, ClientInstruction::DoneParsingStdinQuery => { - stdout - .write_all("done".as_bytes()) - .expect("cannot write to stdout"); - stdout.flush().expect("could not flush"); + if showed_loading_message { + stdout + .write_all("done".as_bytes()) + .expect("cannot write to stdout"); + stdout.flush().expect("could not flush"); + } loading = false; }, instruction => { diff --git a/zellij-client/src/stdin_ansi_parser.rs b/zellij-client/src/stdin_ansi_parser.rs index 7fa3492e..310360e3 100644 --- a/zellij-client/src/stdin_ansi_parser.rs +++ b/zellij-client/src/stdin_ansi_parser.rs @@ -129,6 +129,9 @@ impl StdinAnsiParser { } pub fn write_cache(&self, events: Vec<AnsiStdinInstruction>) { if let Ok(serialized_events) = serde_json::to_string(&events) { + if let Some(parent) = ZELLIJ_STDIN_CACHE_FILE.parent() { + let _ = std::fs::create_dir_all(parent); + } if let Ok(mut file) = File::create(ZELLIJ_STDIN_CACHE_FILE.as_path()) { let _ = file.write_all(serialized_events.as_bytes()); } diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index e24aa38e..38628129 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -2012,6 +2012,9 @@ fn should_show_release_notes( if ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE.exists() { return false; } else { + if let Some(parent) = ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE.parent() { + let _ = std::fs::create_dir_all(parent); + } if let Err(e) = std::fs::write(&*ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE, &[]) { log::error!( "Failed to write seen release notes indication to disk: {}", diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 059235a1..96dd25aa 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -2196,7 +2196,16 @@ pub fn send_cli_send_keys_action_to_screen() { send_cli_action_to_server(&session_metadata, cli_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![pty_writer_thread, screen_thread]); - assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); + let received_write_instructions: Vec<_> = received_pty_instructions + .lock() + .unwrap() + .clone() + .into_iter() + .filter(|i| matches!(i, PtyWriteInstruction::Write(..))) + .collect(); + // here we assert only the write instructions to make sure they arrived properly and in + // sequence to the pane + assert_snapshot!(format!("{:#?}", received_write_instructions)); } #[test] @@ -2965,7 +2974,15 @@ pub fn send_cli_toggle_active_tab_sync_action() { send_cli_action_to_server(&session_metadata, cli_write_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_writer_thread, screen_thread]); - assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); + let received_write_instructions: Vec<_> = received_pty_instructions + .lock() + .unwrap() + .clone() + .into_iter() + .filter(|i| matches!(i, PtyWriteInstruction::Write(..))) + .collect(); + // here we should have 2 Write instructions, one for each pane + assert_snapshot!(format!("{:?}", received_write_instructions)); } #[test] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap index 75e4aeef..c2acd715 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap @@ -1,5 +1,20 @@ --- source: zellij-server/src/./unit/screen_tests.rs -expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" +expression: "format!(\"{:#?}\", received_write_instructions)" --- -[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([1], 0, None), ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([120], 0, None), ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, Exit] +[ + Write( + [ + 1, + ], + 0, + None, + ), + Write( + [ + 120, + ], + 0, + None, + ), +] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap index d9fe6929..69157166 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap @@ -1,5 +1,5 @@ --- source: zellij-server/src/./unit/screen_tests.rs -expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" +expression: "format!(\"{:?}\", received_write_instructions)" --- -[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([102, 111, 111], 0, None), Write([102, 111, 111], 1, None), ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, Exit] +[Write([102, 111, 111], 0, None), Write([102, 111, 111], 1, None)] diff --git a/zellij-utils/src/channels.rs b/zellij-utils/src/channels.rs index 4fdbc8bc..18506329 100644 --- a/zellij-utils/src/channels.rs +++ b/zellij-utils/src/channels.rs @@ -5,7 +5,8 @@ use std::cell::RefCell; use crate::errors::{get_current_ctx, ErrorContext}; pub use crossbeam::channel::{ - bounded, unbounded, Receiver, RecvError, Select, SendError, Sender, TrySendError, + bounded, unbounded, Receiver, RecvError, RecvTimeoutError, Select, SendError, Sender, + TrySendError, }; /// An [MPSC](mpsc) asynchronous channel with added error context. |
