mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: add deunicoded, natural sorting
This commit is contained in:
parent
f0108dba40
commit
6eb50d9218
6 changed files with 82 additions and 16 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -550,6 +550,12 @@ dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deunicode"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -2915,6 +2921,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"deunicode",
|
||||||
"dirs",
|
"dirs",
|
||||||
"filetime",
|
"filetime",
|
||||||
"futures",
|
"futures",
|
||||||
|
|
|
||||||
|
|
@ -106,18 +106,20 @@ keymap = [
|
||||||
{ on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" },
|
{ on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" },
|
||||||
|
|
||||||
# Sorting
|
# Sorting
|
||||||
{ on = [ ",", "m" ], run = "sort modified --dir-first", desc = "Sort by modified time" },
|
{ on = [ ",", "m" ], run = "sort modified --dir-first", desc = "Sort by modified time" },
|
||||||
{ on = [ ",", "M" ], run = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" },
|
{ on = [ ",", "M" ], run = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" },
|
||||||
{ on = [ ",", "c" ], run = "sort created --dir-first", desc = "Sort by created time" },
|
{ on = [ ",", "c" ], run = "sort created --dir-first", desc = "Sort by created time" },
|
||||||
{ on = [ ",", "C" ], run = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" },
|
{ on = [ ",", "C" ], run = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" },
|
||||||
{ on = [ ",", "e" ], run = "sort extension --dir-first", desc = "Sort by extension" },
|
{ on = [ ",", "e" ], run = "sort extension --dir-first", desc = "Sort by extension" },
|
||||||
{ on = [ ",", "E" ], run = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" },
|
{ on = [ ",", "E" ], run = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" },
|
||||||
{ on = [ ",", "a" ], run = "sort alphabetical --dir-first", desc = "Sort alphabetically" },
|
{ on = [ ",", "a" ], run = "sort alphabetical --dir-first", desc = "Sort alphabetically" },
|
||||||
{ on = [ ",", "A" ], run = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" },
|
{ on = [ ",", "A" ], run = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" },
|
||||||
{ on = [ ",", "n" ], run = "sort natural --dir-first", desc = "Sort naturally" },
|
{ on = [ ",", "n" ], run = "sort natural --dir-first", desc = "Sort naturally" },
|
||||||
{ on = [ ",", "N" ], run = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" },
|
{ on = [ ",", "N" ], run = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" },
|
||||||
{ on = [ ",", "s" ], run = "sort size --dir-first", desc = "Sort by size" },
|
{ on = [ ",", "d" ], run = "sort deunicode_natural --dir-first", desc = "Sort naturally (deunicode)" },
|
||||||
{ on = [ ",", "S" ], run = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" },
|
{ on = [ ",", "D" ], run = "sort deunicode_natural --reverse --dir-first", desc = "Sort naturally (deunicode + reverse)" },
|
||||||
|
{ on = [ ",", "s" ], run = "sort size --dir-first", desc = "Sort by size" },
|
||||||
|
{ on = [ ",", "S" ], run = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" },
|
||||||
|
|
||||||
# Tabs
|
# Tabs
|
||||||
{ on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" },
|
{ on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" },
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub enum SortBy {
|
||||||
Extension,
|
Extension,
|
||||||
Alphabetical,
|
Alphabetical,
|
||||||
Natural,
|
Natural,
|
||||||
|
DeunicodeNatural,
|
||||||
Size,
|
Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +28,7 @@ impl FromStr for SortBy {
|
||||||
"extension" => Self::Extension,
|
"extension" => Self::Extension,
|
||||||
"alphabetical" => Self::Alphabetical,
|
"alphabetical" => Self::Alphabetical,
|
||||||
"natural" => Self::Natural,
|
"natural" => Self::Natural,
|
||||||
|
"deunicode_natural" => Self::DeunicodeNatural,
|
||||||
"size" => Self::Size,
|
"size" => Self::Size,
|
||||||
_ => bail!("invalid sort_by value: {s}"),
|
_ => bail!("invalid sort_by value: {s}"),
|
||||||
})
|
})
|
||||||
|
|
@ -48,6 +50,7 @@ impl Display for SortBy {
|
||||||
Self::Extension => "extension",
|
Self::Extension => "extension",
|
||||||
Self::Alphabetical => "alphabetical",
|
Self::Alphabetical => "alphabetical",
|
||||||
Self::Natural => "natural",
|
Self::Natural => "natural",
|
||||||
|
Self::DeunicodeNatural => "deunicode_natural",
|
||||||
Self::Size => "size",
|
Self::Size => "size",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{cmp::Ordering, collections::HashMap, mem};
|
use std::{cmp::Ordering, collections::HashMap, mem};
|
||||||
|
|
||||||
use yazi_config::manager::SortBy;
|
use yazi_config::manager::SortBy;
|
||||||
use yazi_shared::{fs::{File, Url}, natsort};
|
use yazi_shared::{deunicode_natsort, fs::{File, Url}, natsort};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq)]
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
pub struct FilesSorter {
|
pub struct FilesSorter {
|
||||||
|
|
@ -52,7 +52,8 @@ impl FilesSorter {
|
||||||
if ord == Ordering::Equal { by_alphabetical(a, b) } else { ord }
|
if ord == Ordering::Equal { by_alphabetical(a, b) } else { ord }
|
||||||
}),
|
}),
|
||||||
SortBy::Alphabetical => items.sort_unstable_by(by_alphabetical),
|
SortBy::Alphabetical => items.sort_unstable_by(by_alphabetical),
|
||||||
SortBy::Natural => self.sort_naturally(items),
|
SortBy::Natural => self.sort_naturally(items, false),
|
||||||
|
SortBy::DeunicodeNatural => self.sort_naturally(items, true),
|
||||||
SortBy::Size => items.sort_unstable_by(|a, b| {
|
SortBy::Size => items.sort_unstable_by(|a, b| {
|
||||||
let aa = if a.is_dir() { sizes.get(&a.url).copied() } else { None };
|
let aa = if a.is_dir() { sizes.get(&a.url).copied() } else { None };
|
||||||
let bb = if b.is_dir() { sizes.get(&b.url).copied() } else { None };
|
let bb = if b.is_dir() { sizes.get(&b.url).copied() } else { None };
|
||||||
|
|
@ -62,7 +63,7 @@ impl FilesSorter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_naturally(&self, items: &mut Vec<File>) {
|
fn sort_naturally(&self, items: &mut Vec<File>, deunicoded: bool) {
|
||||||
let mut indices = Vec::with_capacity(items.len());
|
let mut indices = Vec::with_capacity(items.len());
|
||||||
let mut entities = Vec::with_capacity(items.len());
|
let mut entities = Vec::with_capacity(items.len());
|
||||||
for (i, file) in items.iter().enumerate() {
|
for (i, file) in items.iter().enumerate() {
|
||||||
|
|
@ -76,7 +77,11 @@ impl FilesSorter {
|
||||||
return promote;
|
return promote;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ordering = natsort(entities[a], entities[b], !self.sensitive);
|
let ordering = match deunicoded {
|
||||||
|
true => deunicode_natsort(entities[a], entities[b], !self.sensitive),
|
||||||
|
false => natsort(entities[a], entities[b], !self.sensitive)
|
||||||
|
};
|
||||||
|
|
||||||
if self.reverse { ordering.reverse() } else { ordering }
|
if self.reverse { ordering.reverse() } else { ordering }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ repository = "https://github.com/sxyazi/yazi"
|
||||||
anyhow = "1.0.83"
|
anyhow = "1.0.83"
|
||||||
bitflags = "2.5.0"
|
bitflags = "2.5.0"
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
|
deunicode = "1.6.0"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
filetime = "0.2.23"
|
filetime = "0.2.23"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use deunicode::deunicode;
|
||||||
|
|
||||||
macro_rules! return_unless_equal {
|
macro_rules! return_unless_equal {
|
||||||
($ord:expr) => {
|
($ord:expr) => {
|
||||||
match $ord {
|
match $ord {
|
||||||
|
|
@ -123,6 +125,17 @@ pub fn natsort(left: &[u8], right: &[u8], insensitive: bool) -> Ordering {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deunicode_natsort(left: &[u8], right: &[u8], insensitive: bool) -> Ordering {
|
||||||
|
let utf8_left = std::str::from_utf8(left);
|
||||||
|
let utf8_right = std::str::from_utf8(right);
|
||||||
|
match (utf8_left, utf8_right) {
|
||||||
|
(Ok(l), Ok(r)) => natsort(deunicode(l).as_bytes(), deunicode(r).as_bytes(), insensitive),
|
||||||
|
(Ok(l), Err(_)) => natsort(deunicode(l).as_bytes(), right, insensitive),
|
||||||
|
(Err(_), Ok(r)) => natsort(left, deunicode(r).as_bytes(), insensitive),
|
||||||
|
_ => natsort(left, right, insensitive),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -133,6 +146,12 @@ mod tests {
|
||||||
assert_eq!(left, right);
|
assert_eq!(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cmp_deun(left: &[&str]) {
|
||||||
|
let mut right = left.to_vec();
|
||||||
|
right.sort_by(|a, b| deunicode_natsort(a.as_bytes(), b.as_bytes(), true));
|
||||||
|
assert_eq!(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_natsort() {
|
fn test_natsort() {
|
||||||
let dates = vec!["1999-3-3", "1999-12-25", "2000-1-2", "2000-1-10", "2000-3-23"];
|
let dates = vec!["1999-3-3", "1999-12-25", "2000-1-2", "2000-1-10", "2000-3-23"];
|
||||||
|
|
@ -176,4 +195,33 @@ mod tests {
|
||||||
cmp(&fractions);
|
cmp(&fractions);
|
||||||
cmp(&words);
|
cmp(&words);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deunicode_natsort() {
|
||||||
|
let deun = vec![
|
||||||
|
"Acorn",
|
||||||
|
"Ætt",
|
||||||
|
"Alpha",
|
||||||
|
"Átriu",
|
||||||
|
"Attention",
|
||||||
|
"Beta",
|
||||||
|
"Elemér",
|
||||||
|
"Érvényes",
|
||||||
|
"Große",
|
||||||
|
"Grotto",
|
||||||
|
"Hedvig",
|
||||||
|
"Ilona",
|
||||||
|
"Írott",
|
||||||
|
"Olga",
|
||||||
|
"Órmotlan",
|
||||||
|
"Öveges",
|
||||||
|
"Őzike",
|
||||||
|
"Ubul",
|
||||||
|
"Űr",
|
||||||
|
"Útvonal",
|
||||||
|
"Üveg",
|
||||||
|
];
|
||||||
|
|
||||||
|
cmp_deun(&deun)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue