diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 789263ccf..82c4cdde9 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -183,5 +183,10 @@ open_title = "Open with:" open_origin = "hovered" open_offset = [ 0, 1, 50, 7 ] +[which] +sort_by = "none" +sort_sensitive = false +sort_reverse = false + [log] enabled = false diff --git a/yazi-config/src/keymap/control.rs b/yazi-config/src/keymap/control.rs index 5fae59002..a41b310a6 100644 --- a/yazi-config/src/keymap/control.rs +++ b/yazi-config/src/keymap/control.rs @@ -5,7 +5,7 @@ use yazi_shared::event::Cmd; use super::Key; -#[derive(Debug, Deserialize)] +#[derive(Debug, Default, Deserialize)] pub struct Control { pub on: Vec, #[serde(deserialize_with = "super::exec_deserialize")] @@ -43,6 +43,7 @@ impl Control { } } +#[derive(Debug)] pub enum ControlCow { Owned(Control), Borrowed(&'static Control), @@ -67,6 +68,10 @@ impl Deref for ControlCow { } } +impl Default for ControlCow { + fn default() -> Self { Self::Owned(Control::default()) } +} + impl ControlCow { pub fn into_seq(self) -> VecDeque { match self { diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 3347986c8..cf7826dc1 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -17,6 +17,7 @@ mod priority; mod tasks; pub mod theme; mod validation; +pub mod which; mod xdg; pub use layout::*; @@ -43,6 +44,7 @@ pub static TASKS: RoCell = RoCell::new(); pub static THEME: RoCell = RoCell::new(); pub static INPUT: RoCell = RoCell::new(); pub static SELECT: RoCell = RoCell::new(); +pub static WHICH: RoCell = RoCell::new(); pub fn init() { ARGS.with(Default::default); @@ -63,4 +65,5 @@ pub fn init() { THEME.with(Default::default); INPUT.with(Default::default); SELECT.with(Default::default); + WHICH.with(Default::default); } diff --git a/yazi-config/src/which/mod.rs b/yazi-config/src/which/mod.rs new file mode 100644 index 000000000..2033591be --- /dev/null +++ b/yazi-config/src/which/mod.rs @@ -0,0 +1,5 @@ +mod sorting; +mod which; + +pub use sorting::*; +pub use which::*; diff --git a/yazi-config/src/which/sorting.rs b/yazi-config/src/which/sorting.rs new file mode 100644 index 000000000..cb38e8030 --- /dev/null +++ b/yazi-config/src/which/sorting.rs @@ -0,0 +1,32 @@ +use std::str::FromStr; + +use anyhow::bail; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] +#[serde(try_from = "String")] +pub enum SortBy { + #[default] + None, + Key, + Desc, +} + +impl FromStr for SortBy { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Self::None, + "key" => Self::Key, + "desc" => Self::Desc, + _ => bail!("Invalid sort option: {s}"), + }) + } +} + +impl TryFrom for SortBy { + type Error = anyhow::Error; + + fn try_from(value: String) -> Result { Self::from_str(&value) } +} diff --git a/yazi-config/src/which/which.rs b/yazi-config/src/which/which.rs new file mode 100644 index 000000000..b8e3305cb --- /dev/null +++ b/yazi-config/src/which/which.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; +use validator::Validate; + +use super::SortBy; +use crate::MERGED_YAZI; + +#[derive(Debug, Deserialize, Serialize, Validate)] +pub struct Which { + // Sorting + pub sort_by: SortBy, + pub sort_sensitive: bool, + pub sort_reverse: bool, +} + +impl Default for Which { + fn default() -> Self { + #[derive(Deserialize)] + struct Outer { + which: Which, + } + + toml::from_str::(&MERGED_YAZI).unwrap().which + } +} diff --git a/yazi-core/src/folder/sorter.rs b/yazi-core/src/folder/sorter.rs index 4dea60833..779b22e29 100644 --- a/yazi-core/src/folder/sorter.rs +++ b/yazi-core/src/folder/sorter.rs @@ -12,9 +12,9 @@ pub struct FilesSorter { } impl FilesSorter { - pub(super) fn sort(&self, items: &mut Vec, sizes: &BTreeMap) -> bool { + pub(super) fn sort(&self, items: &mut Vec, sizes: &BTreeMap) { if items.is_empty() { - return false; + return; } let by_alphabetical = |a: &File, b: &File| { @@ -30,7 +30,7 @@ impl FilesSorter { }; match self.by { - SortBy::None => return false, + SortBy::None => {} SortBy::Modified => items.sort_unstable_by(|a, b| { let ord = self.cmp(a.modified, b.modified, self.promote(a, b)); if ord == Ordering::Equal { by_alphabetical(a, b) } else { ord } @@ -60,7 +60,6 @@ impl FilesSorter { if ord == Ordering::Equal { by_alphabetical(a, b) } else { ord } }), } - true } fn sort_naturally(&self, items: &mut Vec) { @@ -81,11 +80,7 @@ impl FilesSorter { if self.reverse { ordering.reverse() } else { ordering } }); - let mut new = Vec::with_capacity(indices.len()); - for i in indices { - new.push(mem::take(&mut items[i])); - } - *items = new; + *items = indices.into_iter().map(|i| mem::take(&mut items[i])).collect(); } #[inline(always)] diff --git a/yazi-core/src/which/commands/show.rs b/yazi-core/src/which/commands/show.rs index 639704627..22994c6d7 100644 --- a/yazi-core/src/which/commands/show.rs +++ b/yazi-core/src/which/commands/show.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use yazi_config::{keymap::{Control, Key}, KEYMAP}; use yazi_shared::{event::Cmd, render, Layer}; -use crate::which::Which; +use crate::which::{Which, WhichSorter}; pub struct Opt { cands: Vec, @@ -52,6 +52,7 @@ impl Which { .map(|c| c.into()) .collect(); + WhichSorter::default().sort(&mut self.cands); self.visible = true; self.silent = false; render!(); diff --git a/yazi-core/src/which/mod.rs b/yazi-core/src/which/mod.rs index b2098e80f..ebc217a84 100644 --- a/yazi-core/src/which/mod.rs +++ b/yazi-core/src/which/mod.rs @@ -1,4 +1,6 @@ mod commands; +mod sorter; mod which; +pub use sorter::*; pub use which::*; diff --git a/yazi-core/src/which/sorter.rs b/yazi-core/src/which/sorter.rs new file mode 100644 index 000000000..6fd64dc28 --- /dev/null +++ b/yazi-core/src/which/sorter.rs @@ -0,0 +1,47 @@ +use std::{borrow::Cow, mem}; + +use yazi_config::{keymap::ControlCow, which::SortBy, WHICH}; +use yazi_shared::natsort; + +#[derive(Clone, Copy, PartialEq)] +pub struct WhichSorter { + pub by: SortBy, + pub sensitive: bool, + pub reverse: bool, +} + +impl Default for WhichSorter { + fn default() -> Self { + Self { + by: WHICH.sort_by, + sensitive: WHICH.sort_sensitive, + reverse: WHICH.sort_reverse, + } + } +} + +impl WhichSorter { + pub(super) fn sort(&self, items: &mut Vec) { + if self.by == SortBy::None || items.is_empty() { + return; + } + + let mut indices = Vec::with_capacity(items.len()); + let mut entities = Vec::with_capacity(items.len()); + for (i, ctrl) in items.iter().enumerate() { + indices.push(i); + entities.push(match self.by { + SortBy::None => unreachable!(), + SortBy::Key => Cow::Owned(ctrl.on()), + SortBy::Desc => ctrl.desc_or_exec(), + }); + } + + indices.sort_unstable_by(|&a, &b| { + let ordering = natsort(entities[a].as_bytes(), entities[b].as_bytes(), !self.sensitive); + if self.reverse { ordering.reverse() } else { ordering } + }); + + *items = indices.into_iter().map(|i| mem::take(&mut items[i])).collect(); + } +} diff --git a/yazi-core/src/which/which.rs b/yazi-core/src/which/which.rs index 8cc627c80..d6227775c 100644 --- a/yazi-core/src/which/which.rs +++ b/yazi-core/src/which/which.rs @@ -7,19 +7,12 @@ pub struct Which { pub times: usize, pub cands: Vec, + // Visibility pub visible: bool, pub silent: bool, } impl Which { - fn reset(&mut self) { - self.times = 0; - self.cands.clear(); - - self.visible = false; - self.silent = false; - } - pub fn type_(&mut self, key: Key) -> bool { self.cands.retain(|c| c.on.len() > self.times && c.on[self.times] == key); self.times += 1; @@ -37,4 +30,12 @@ impl Which { render!(); true } + + fn reset(&mut self) { + self.times = 0; + self.cands.clear(); + + self.visible = false; + self.silent = false; + } }