fix(resurrection): do not parse resurrection layouts on startup (#4292)

* fix(resurrection): do not parse resurrection layouts on startup

* proper error when file is corrupt

* docs(changelog): add PR

* fix attach command

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2025-07-16 12:03:32 +02:00 committed by GitHub
parent 403f0a07be
commit 4a68c6d90b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 64 additions and 50 deletions

View file

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* fix: don't serialize when only ui elements present and provide post command disovery hook (https://github.com/zellij-org/zellij/pull/4276) * fix: don't serialize when only ui elements present and provide post command disovery hook (https://github.com/zellij-org/zellij/pull/4276)
* fix: use plugin `/host` folder as cwd when opening new panes (https://github.com/zellij-org/zellij/pull/4290) * fix: use plugin `/host` folder as cwd when opening new panes (https://github.com/zellij-org/zellij/pull/4290)
* fix: better command detection when serializing layouts for resurrection (https://github.com/zellij-org/zellij/pull/4287) * fix: better command detection when serializing layouts for resurrection (https://github.com/zellij-org/zellij/pull/4287)
* fix: slow startup on very large caches (https://github.com/zellij-org/zellij/pull/4292)
* fix: don't show popups in the welcome screen (https://github.com/zellij-org/zellij/pull/4294) * fix: don't show popups in the welcome screen (https://github.com/zellij-org/zellij/pull/4294)
## [0.42.2] - 2025-04-15 ## [0.42.2] - 2025-04-15

View file

@ -90,7 +90,7 @@ pub(crate) fn delete_all_sessions(yes: bool, force: bool) {
} else { } else {
resurrectable_sessions resurrectable_sessions
.iter() .iter()
.filter(|(name, _, _)| !active_sessions.contains(name)) .filter(|(name, _)| !active_sessions.contains(name))
.cloned() .cloned()
.collect() .collect()
}; };
@ -699,7 +699,15 @@ pub(crate) fn start_client(opts: CliArgs) {
.and_then(|s| session_exists(&s).ok()) .and_then(|s| session_exists(&s).ok())
.unwrap_or(false); .unwrap_or(false);
let resurrection_layout = let resurrection_layout =
session_name.as_ref().and_then(|s| resurrection_layout(&s)); session_name
.as_ref()
.and_then(|s| match resurrection_layout(&s) {
Ok(layout) => layout,
Err(e) => {
eprintln!("{}", e);
process::exit(2);
},
});
if (create || should_create_detached) if (create || should_create_detached)
&& !session_exists && !session_exists
&& resurrection_layout.is_none() && resurrection_layout.is_none()

View file

@ -113,7 +113,10 @@ pub fn spawn_session_if_needed(
} else { } else {
let force_run_commands = false; let force_run_commands = false;
let resurrection_layout = let resurrection_layout =
resurrection_layout(&session_name).map(|mut resurrection_layout| { resurrection_layout(&session_name)
.ok()
.flatten()
.map(|mut resurrection_layout| {
if force_run_commands { if force_run_commands {
resurrection_layout.recursively_add_start_suspended(Some(false)); resurrection_layout.recursively_add_start_suspended(Some(false));
} }

View file

@ -62,6 +62,8 @@ impl SessionManager for RealSessionManager {
session_name: &str, session_name: &str,
) -> Option<zellij_utils::input::layout::Layout> { ) -> Option<zellij_utils::input::layout::Layout> {
zellij_utils::sessions::resurrection_layout(session_name) zellij_utils::sessions::resurrection_layout(session_name)
.ok()
.flatten()
} }
fn spawn_session_if_needed( fn spawn_session_if_needed(

View file

@ -21,7 +21,7 @@ pub fn get_sessions() -> Result<Vec<(String, Duration)>, io::ErrorKind> {
Ok(files) => { Ok(files) => {
let mut sessions = Vec::new(); let mut sessions = Vec::new();
files.for_each(|file| { files.for_each(|file| {
let file = file.unwrap(); if let Ok(file) = file {
let file_name = file.file_name().into_string().unwrap(); let file_name = file.file_name().into_string().unwrap();
let ctime = std::fs::metadata(&file.path()) let ctime = std::fs::metadata(&file.path())
.ok() .ok()
@ -32,6 +32,7 @@ pub fn get_sessions() -> Result<Vec<(String, Duration)>, io::ErrorKind> {
if file.file_type().unwrap().is_socket() && assert_socket(&file_name) { if file.file_type().unwrap().is_socket() && assert_socket(&file_name) {
sessions.push((file_name, duration)); sessions.push((file_name, duration));
} }
}
}); });
Ok(sessions) Ok(sessions)
}, },
@ -40,7 +41,7 @@ pub fn get_sessions() -> Result<Vec<(String, Duration)>, io::ErrorKind> {
} }
} }
pub fn get_resurrectable_sessions() -> Vec<(String, Duration, Layout)> { pub fn get_resurrectable_sessions() -> Vec<(String, Duration)> {
match fs::read_dir(&*ZELLIJ_SESSION_INFO_CACHE_DIR) { match fs::read_dir(&*ZELLIJ_SESSION_INFO_CACHE_DIR) {
Ok(files_in_session_info_folder) => { Ok(files_in_session_info_folder) => {
let files_that_are_folders = files_in_session_info_folder let files_that_are_folders = files_in_session_info_folder
@ -50,30 +51,12 @@ pub fn get_resurrectable_sessions() -> Vec<(String, Duration, Layout)> {
.filter_map(|folder_name| { .filter_map(|folder_name| {
let layout_file_name = let layout_file_name =
session_layout_cache_file_name(&folder_name.display().to_string()); session_layout_cache_file_name(&folder_name.display().to_string());
let raw_layout = match std::fs::read_to_string(&layout_file_name) {
Ok(raw_layout) => raw_layout,
Err(_e) => {
return None;
},
};
let ctime = match std::fs::metadata(&layout_file_name) let ctime = match std::fs::metadata(&layout_file_name)
.and_then(|metadata| metadata.created()) .and_then(|metadata| metadata.created())
{ {
Ok(created) => Some(created), Ok(created) => Some(created),
Err(_e) => None, Err(_e) => None,
}; };
let layout = match Layout::from_kdl(
&raw_layout,
Some(layout_file_name.display().to_string()),
None,
None,
) {
Ok(layout) => layout,
Err(e) => {
log::error!("Failed to parse resurrection layout file: {}", e);
return None;
},
};
let elapsed_duration = ctime let elapsed_duration = ctime
.map(|ctime| { .map(|ctime| {
Duration::from_secs(ctime.elapsed().ok().unwrap_or_default().as_secs()) Duration::from_secs(ctime.elapsed().ok().unwrap_or_default().as_secs())
@ -82,7 +65,7 @@ pub fn get_resurrectable_sessions() -> Vec<(String, Duration, Layout)> {
let session_name = folder_name let session_name = folder_name
.file_name() .file_name()
.map(|f| std::path::PathBuf::from(f).display().to_string())?; .map(|f| std::path::PathBuf::from(f).display().to_string())?;
Some((session_name, elapsed_duration, layout)) Some((session_name, elapsed_duration))
}) })
.collect() .collect()
}, },
@ -291,7 +274,7 @@ pub fn list_sessions(no_formatting: bool, short: bool, reverse: bool) {
let resurrectable_sessions = get_resurrectable_sessions(); let resurrectable_sessions = get_resurrectable_sessions();
let mut all_sessions: HashMap<String, (Duration, bool)> = resurrectable_sessions let mut all_sessions: HashMap<String, (Duration, bool)> = resurrectable_sessions
.iter() .iter()
.map(|(name, timestamp, _layout)| (name.clone(), (timestamp.clone(), true))) .map(|(name, timestamp)| (name.clone(), (timestamp.clone(), true)))
.collect(); .collect();
for (session_name, duration) in running_sessions { for (session_name, duration) in running_sessions {
all_sessions.insert(session_name.clone(), (duration, false)); all_sessions.insert(session_name.clone(), (duration, false));
@ -362,17 +345,34 @@ pub fn session_exists(name: &str) -> Result<bool, io::ErrorKind> {
} }
// if the session is resurrecable, the returned layout is the one to be used to resurrect it // if the session is resurrecable, the returned layout is the one to be used to resurrect it
pub fn resurrection_layout(session_name_to_resurrect: &str) -> Option<Layout> { pub fn resurrection_layout(session_name_to_resurrect: &str) -> Result<Option<Layout>, String> {
let resurrectable_sessions = get_resurrectable_sessions(); let layout_file_name = session_layout_cache_file_name(&session_name_to_resurrect);
resurrectable_sessions let raw_layout = match std::fs::read_to_string(&layout_file_name) {
.iter() Ok(raw_layout) => raw_layout,
.find_map(|(name, _timestamp, layout)| { Err(_e) => {
if name == session_name_to_resurrect { return Ok(None);
Some(layout.clone()) },
} else { };
None match Layout::from_kdl(
&raw_layout,
Some(layout_file_name.display().to_string()),
None,
None,
) {
Ok(layout) => Ok(Some(layout)),
Err(e) => {
log::error!(
"Failed to parse resurrection layout file {}: {}",
layout_file_name.display(),
e
);
return Err(format!(
"Failed to parse resurrection layout file {}: {}.",
layout_file_name.display(),
e
));
},
} }
})
} }
pub fn assert_session(name: &str) { pub fn assert_session(name: &str) {