mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: add sort_dir_by option for separate directory sorting
When `sort_dir_first = true`, directories and files are currently sorted by the same key. This adds a `sort_dir_by` config option that, when set, sorts directories independently from files. For example, setting `sort_by = "mtime"` with `sort_dir_by = "natural"` keeps directories alphabetically ordered at the top while files sort by modification time below. Includes: - New `sort_dir_by` config option (default: "none", preserving existing behavior) - `,d` keymap submenu for directory sort options - Lua API exposure via `preference.sort_dir_by` - `--dir-by` flag for the sort command
This commit is contained in:
parent
1a121bbfb0
commit
9e7617634b
8 changed files with 52 additions and 2 deletions
|
|
@ -11,7 +11,8 @@ pub(super) struct Preference {
|
|||
v_name: Option<Value>,
|
||||
v_linemode: Option<Value>,
|
||||
|
||||
v_sort_by: Option<Value>,
|
||||
v_sort_by: Option<Value>,
|
||||
v_sort_dir_by: Option<Value>,
|
||||
}
|
||||
|
||||
impl Deref for Preference {
|
||||
|
|
@ -28,7 +29,8 @@ impl Preference {
|
|||
v_name: None,
|
||||
v_linemode: None,
|
||||
|
||||
v_sort_by: None,
|
||||
v_sort_by: None,
|
||||
v_sort_dir_by: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -46,5 +48,8 @@ impl UserData for Preference {
|
|||
fields.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse));
|
||||
fields.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first));
|
||||
fields.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit));
|
||||
cached_field!(fields, sort_dir_by, |_, me| {
|
||||
Ok(me.sort_dir_by.map(|s| s.to_string()).unwrap_or_default())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ impl Actor for Sort {
|
|||
pref.sort_dir_first = opt.dir_first.unwrap_or(pref.sort_dir_first);
|
||||
pref.sort_sensitive = opt.sensitive.unwrap_or(pref.sort_sensitive);
|
||||
pref.sort_translit = opt.translit.unwrap_or(pref.sort_translit);
|
||||
pref.sort_dir_by = match (opt.by, opt.dir_by) {
|
||||
(_, Some(yazi_fs::SortBy::None)) => None,
|
||||
(_, Some(v)) => Some(v),
|
||||
(Some(_), None) => None,
|
||||
(None, None) => pref.sort_dir_by,
|
||||
};
|
||||
|
||||
let sorter = FilesSorter::from(&*pref);
|
||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ keymap = [
|
|||
{ on = [ ",", "S" ], run = [ "sort size --reverse=yes", "linemode size" ], desc = "Sort by size (reverse)" },
|
||||
{ on = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" },
|
||||
|
||||
{ on = [ ",", "d" ], run = [ "sort mtime --reverse=yes --dir-by=natural", "linemode mtime" ], desc = "Sort by modified time, dirs naturally" },
|
||||
|
||||
# Goto
|
||||
{ on = [ "g", "h" ], run = "cd ~", desc = "Go home" },
|
||||
{ on = [ "g", "c" ], run = "cd ~/.config", desc = "Go ~/.config" },
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ sort_sensitive = false
|
|||
sort_reverse = false
|
||||
sort_dir_first = true
|
||||
sort_translit = false
|
||||
sort_dir_by = "none"
|
||||
linemode = "none"
|
||||
show_hidden = false
|
||||
show_symlink = true
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub struct Mgr {
|
|||
pub sort_reverse: SyncCell<bool>,
|
||||
pub sort_dir_first: SyncCell<bool>,
|
||||
pub sort_translit: SyncCell<bool>,
|
||||
pub sort_dir_by: SyncCell<SortBy>,
|
||||
|
||||
// Display
|
||||
pub linemode: String,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pub struct Preference {
|
|||
pub sort_reverse: bool,
|
||||
pub sort_dir_first: bool,
|
||||
pub sort_translit: bool,
|
||||
pub sort_dir_by: Option<SortBy>,
|
||||
}
|
||||
|
||||
impl Default for Preference {
|
||||
|
|
@ -30,6 +31,10 @@ impl Default for Preference {
|
|||
sort_reverse: YAZI.mgr.sort_reverse.get(),
|
||||
sort_dir_first: YAZI.mgr.sort_dir_first.get(),
|
||||
sort_translit: YAZI.mgr.sort_translit.get(),
|
||||
sort_dir_by: match YAZI.mgr.sort_dir_by.get() {
|
||||
SortBy::None => None,
|
||||
v => Some(v),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +47,7 @@ impl From<&Preference> for FilesSorter {
|
|||
reverse: value.sort_reverse,
|
||||
dir_first: value.sort_dir_first,
|
||||
translit: value.sort_translit,
|
||||
dir_by: value.sort_dir_by,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pub struct FilesSorter {
|
|||
pub reverse: bool,
|
||||
pub dir_first: bool,
|
||||
pub translit: bool,
|
||||
pub dir_by: Option<SortBy>,
|
||||
}
|
||||
|
||||
impl FilesSorter {
|
||||
|
|
@ -21,6 +22,32 @@ impl FilesSorter {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.dir_first && self.dir_by.is_some_and(|b| b != self.by) {
|
||||
let dir_by = self.dir_by.unwrap();
|
||||
// Stable-partition: dirs first, then files
|
||||
items.sort_by(|a, b| b.is_dir().cmp(&a.is_dir()));
|
||||
let mid = items.iter().position(|f| !f.is_dir()).unwrap_or(items.len());
|
||||
let (dirs, files) = items.split_at_mut(mid);
|
||||
|
||||
// Sort dirs with dir_by, non-reversed (already partitioned)
|
||||
let dir_sorter =
|
||||
Self { by: dir_by, reverse: false, dir_first: false, dir_by: None, ..*self };
|
||||
dir_sorter.sort_with_by(dirs, sizes);
|
||||
|
||||
// Sort files with the main sort_by
|
||||
let file_sorter = Self { dir_first: false, dir_by: None, ..*self };
|
||||
file_sorter.sort_with_by(files, sizes);
|
||||
return;
|
||||
}
|
||||
|
||||
self.sort_with_by(items, sizes);
|
||||
}
|
||||
|
||||
fn sort_with_by(&self, items: &mut [File], sizes: &HashMap<PathBufDyn, u64>) {
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let by_alphabetical = |a: &File, b: &File| {
|
||||
if self.sensitive {
|
||||
self.cmp(a.urn().encoded_bytes(), b.urn().encoded_bytes(), self.promote(a, b))
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub struct SortOpt {
|
|||
pub dir_first: Option<bool>,
|
||||
pub sensitive: Option<bool>,
|
||||
pub translit: Option<bool>,
|
||||
pub dir_by: Option<SortBy>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for SortOpt {
|
||||
|
|
@ -22,6 +23,7 @@ impl TryFrom<CmdCow> for SortOpt {
|
|||
dir_first: c.get("dir-first").ok(),
|
||||
sensitive: c.get("sensitive").ok(),
|
||||
translit: c.get("translit").ok(),
|
||||
dir_by: c.get::<&str>("dir-by").ok().map(str::parse).transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue