Compare commits

...

4 commits

Author SHA1 Message Date
dev_Hakaze
0ee3167250
Merge 751d5729bc into 58f1013348 2026-07-23 02:45:21 +08:00
arimu1
751d5729bc style(mgr): rustfmt MgrSnap::selected chain for nightly CI
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 12:14:40 +07:00
arimu1
4531842b40 fix(mgr): avoid Box allocation in MgrSnap::selected
Iterate a &[UrlBuf] slice instead of boxing dyn Iterator.
2026-07-21 06:55:46 +07:00
arimu1
2e60b1741b fix(mgr): restore hovered fallback for %s/%d shell-formatting when nothing is selected
The Splatable impl for Mgr used to fall back to the hovered item via
Tab::selected_or_hovered_urls() when the selection was empty, the same
convention used elsewhere (yank, copy, remove, open). PR #4108 rewrote
this into MgrSnap/TabSnap but only ported the plain selected list,
dropping the hovered fallback. This made %s and %d expand to nothing
when hovering a file without an explicit selection.

Restore the fallback in MgrSnap::selected(), which both %s/%S and
%d/%D read from.

Resolves #4132
2026-07-20 07:29:53 +07:00

View file

@ -23,10 +23,16 @@ impl Splatable for MgrSnap {
fn selected(&self, tab: usize, mut idx: Option<usize>) -> impl Iterator<Item = &File> { fn selected(&self, tab: usize, mut idx: Option<usize>) -> impl Iterator<Item = &File> {
idx = idx.and_then(|i| i.checked_sub(1)); idx = idx.and_then(|i| i.checked_sub(1));
tab
.checked_sub(1) // Fall back to the hovered item when nothing is explicitly selected, the
.and_then(|tab| self.tabs.get(tab)) // same convention `Tab::selected_or_hovered_urls()` used before #4108.
.map_or_else(|| &[][..], |s| &s.selected) 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() .iter()
.skip(idx.unwrap_or(0)) .skip(idx.unwrap_or(0))
.take(if idx.is_some() { 1 } else { usize::MAX }) .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 }) 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::<Vec<_>>(), [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::<Vec<_>>(), [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);
}
}