diff --git a/yazi-core/src/mgr/snap.rs b/yazi-core/src/mgr/snap.rs index 8019aeea..18ccfd78 100644 --- a/yazi-core/src/mgr/snap.rs +++ b/yazi-core/src/mgr/snap.rs @@ -23,10 +23,16 @@ impl Splatable for MgrSnap { fn selected(&self, tab: usize, mut idx: Option) -> impl Iterator { idx = idx.and_then(|i| i.checked_sub(1)); - tab - .checked_sub(1) - .and_then(|tab| self.tabs.get(tab)) - .map_or_else(|| &[][..], |s| &s.selected) + + // Fall back to the hovered item when nothing is explicitly selected, the + // same convention `Tab::selected_or_hovered_urls()` used before #4108. + let urls: &[UrlBuf] = match tab.checked_sub(1).and_then(|tab| self.tabs.get(tab)) { + Some(s) if !s.selected.is_empty() => &s.selected, + Some(s) => s.hovered.as_slice(), + None => &[], + }; + + urls .iter() .skip(idx.unwrap_or(0)) .take(if idx.is_some() { 1 } else { usize::MAX }) @@ -41,3 +47,44 @@ impl Splatable for MgrSnap { self.yanked.iter().skip(idx.unwrap_or(0)).take(if idx.is_some() { 1 } else { usize::MAX }) } } + +#[cfg(test)] +mod tests { + use std::path::Path; + + use super::*; + + fn url(s: &str) -> UrlBuf { UrlBuf::from(Path::new(s)) } + + fn snap(selected: &[&str], hovered: Option<&str>) -> MgrSnap { + MgrSnap { + tab: 0, + tabs: vec![TabSnap { + hovered: hovered.map(url), + selected: selected.iter().map(|s| url(s)).collect(), + }], + yanked: vec![], + } + } + + #[test] + fn selected_falls_back_to_hovered_when_empty() { + let s = snap(&[], Some("hovered")); + + assert_eq!(s.selected(1, None).collect::>(), [url("hovered").as_url()]); + } + + #[test] + fn selected_prefers_explicit_selection_over_hovered() { + let s = snap(&["a", "b"], Some("hovered")); + + assert_eq!(s.selected(1, None).collect::>(), [url("a").as_url(), url("b").as_url()]); + } + + #[test] + fn selected_empty_when_nothing_selected_or_hovered() { + let s = snap(&[], None); + + assert_eq!(s.selected(1, None).count(), 0); + } +}