mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: prioritize paths that need to be processed first during bulk renaming (#1801)
Co-authored-by: sxyazi <sxyazi@gmail.com>
This commit is contained in:
parent
5eabd7d897
commit
81b8c89ec1
1 changed files with 88 additions and 1 deletions
|
|
@ -53,7 +53,8 @@ impl Manager {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let todo: Vec<_> = old.into_iter().zip(new).filter(|(o, n)| o != n).collect();
|
let (old, new) = old.into_iter().zip(new).filter(|(o, n)| o != n).unzip();
|
||||||
|
let todo = Self::prioritized_paths(old, new);
|
||||||
if todo.is_empty() {
|
if todo.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -117,4 +118,90 @@ impl Manager {
|
||||||
stdin().read_exact(&mut [0]).await?;
|
stdin().read_exact(&mut [0]).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prioritized_paths(old: Vec<PathBuf>, new: Vec<PathBuf>) -> Vec<(PathBuf, PathBuf)> {
|
||||||
|
let orders: HashMap<_, _> = old.iter().enumerate().map(|(i, p)| (p, i)).collect();
|
||||||
|
let mut incomes: HashMap<_, _> = old.iter().map(|p| (p, false)).collect();
|
||||||
|
let mut todos: HashMap<_, _> = old
|
||||||
|
.iter()
|
||||||
|
.zip(new)
|
||||||
|
.map(|(o, n)| {
|
||||||
|
incomes.get_mut(&n).map(|b| *b = true);
|
||||||
|
(o, n)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut sorted = Vec::with_capacity(old.len());
|
||||||
|
while !todos.is_empty() {
|
||||||
|
// Paths that are non-incomes and don't need to be prioritized in this round
|
||||||
|
let mut outcomes: Vec<_> = incomes.iter().filter(|(_, &b)| !b).map(|(&p, _)| p).collect();
|
||||||
|
outcomes.sort_unstable_by(|a, b| orders[b].cmp(&orders[a]));
|
||||||
|
|
||||||
|
// If there're no outcomes, it means there are cycles in the renaming
|
||||||
|
if outcomes.is_empty() {
|
||||||
|
let mut remain: Vec<_> = todos.into_iter().map(|(o, n)| (o.clone(), n)).collect();
|
||||||
|
remain.sort_unstable_by(|(a, _), (b, _)| orders[a].cmp(&orders[b]));
|
||||||
|
sorted.reverse();
|
||||||
|
sorted.extend(remain);
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
for old in outcomes {
|
||||||
|
let Some(new) = todos.remove(old) else { unreachable!() };
|
||||||
|
incomes.remove(&old);
|
||||||
|
incomes.get_mut(&new).map(|b| *b = false);
|
||||||
|
sorted.push((old.clone(), new));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sorted.reverse();
|
||||||
|
sorted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sort() {
|
||||||
|
fn cmp(input: &[(&str, &str)], expected: &[(&str, &str)]) {
|
||||||
|
let sorted = Manager::prioritized_paths(
|
||||||
|
input.iter().map(|&(o, _)| o.into()).collect(),
|
||||||
|
input.iter().map(|&(_, n)| n.into()).collect(),
|
||||||
|
);
|
||||||
|
let sorted: Vec<_> =
|
||||||
|
sorted.iter().map(|(o, n)| (o.to_str().unwrap(), n.to_str().unwrap())).collect();
|
||||||
|
assert_eq!(sorted, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
cmp(
|
||||||
|
&[("2", "3"), ("1", "2"), ("3", "4")],
|
||||||
|
&[("3", "4"), ("2", "3"), ("1", "2")]
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
cmp(
|
||||||
|
&[("1", "3"), ("2", "3"), ("3", "4")],
|
||||||
|
&[("3", "4"), ("1", "3"), ("2", "3")]
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
cmp(
|
||||||
|
&[("2", "1"), ("1", "2")],
|
||||||
|
&[("2", "1"), ("1", "2")]
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
cmp(
|
||||||
|
&[("3", "2"), ("2", "1"), ("1", "3"), ("a", "b"), ("b", "c")],
|
||||||
|
&[("b", "c"), ("a", "b"), ("3", "2"), ("2", "1"), ("1", "3")]
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
cmp(
|
||||||
|
&[("b", "b_"), ("a", "a_"), ("c", "c_")],
|
||||||
|
&[("b", "b_"), ("a", "a_"), ("c", "c_")],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue