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_name: Option<Value>,
|
||||||
v_linemode: 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 {
|
impl Deref for Preference {
|
||||||
|
|
@ -28,7 +29,8 @@ impl Preference {
|
||||||
v_name: None,
|
v_name: None,
|
||||||
v_linemode: 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_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_dir_first", |_, me| Ok(me.sort_dir_first));
|
||||||
fields.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit));
|
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_dir_first = opt.dir_first.unwrap_or(pref.sort_dir_first);
|
||||||
pref.sort_sensitive = opt.sensitive.unwrap_or(pref.sort_sensitive);
|
pref.sort_sensitive = opt.sensitive.unwrap_or(pref.sort_sensitive);
|
||||||
pref.sort_translit = opt.translit.unwrap_or(pref.sort_translit);
|
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 sorter = FilesSorter::from(&*pref);
|
||||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
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 = [ ",", "S" ], run = [ "sort size --reverse=yes", "linemode size" ], desc = "Sort by size (reverse)" },
|
||||||
{ on = [ ",", "r" ], run = "sort random --reverse=no", desc = "Sort randomly" },
|
{ 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
|
# Goto
|
||||||
{ on = [ "g", "h" ], run = "cd ~", desc = "Go home" },
|
{ on = [ "g", "h" ], run = "cd ~", desc = "Go home" },
|
||||||
{ on = [ "g", "c" ], run = "cd ~/.config", desc = "Go ~/.config" },
|
{ on = [ "g", "c" ], run = "cd ~/.config", desc = "Go ~/.config" },
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ sort_sensitive = false
|
||||||
sort_reverse = false
|
sort_reverse = false
|
||||||
sort_dir_first = true
|
sort_dir_first = true
|
||||||
sort_translit = false
|
sort_translit = false
|
||||||
|
sort_dir_by = "none"
|
||||||
linemode = "none"
|
linemode = "none"
|
||||||
show_hidden = false
|
show_hidden = false
|
||||||
show_symlink = true
|
show_symlink = true
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ pub struct Mgr {
|
||||||
pub sort_reverse: SyncCell<bool>,
|
pub sort_reverse: SyncCell<bool>,
|
||||||
pub sort_dir_first: SyncCell<bool>,
|
pub sort_dir_first: SyncCell<bool>,
|
||||||
pub sort_translit: SyncCell<bool>,
|
pub sort_translit: SyncCell<bool>,
|
||||||
|
pub sort_dir_by: SyncCell<SortBy>,
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
pub linemode: String,
|
pub linemode: String,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ pub struct Preference {
|
||||||
pub sort_reverse: bool,
|
pub sort_reverse: bool,
|
||||||
pub sort_dir_first: bool,
|
pub sort_dir_first: bool,
|
||||||
pub sort_translit: bool,
|
pub sort_translit: bool,
|
||||||
|
pub sort_dir_by: Option<SortBy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Preference {
|
impl Default for Preference {
|
||||||
|
|
@ -30,6 +31,10 @@ impl Default for Preference {
|
||||||
sort_reverse: YAZI.mgr.sort_reverse.get(),
|
sort_reverse: YAZI.mgr.sort_reverse.get(),
|
||||||
sort_dir_first: YAZI.mgr.sort_dir_first.get(),
|
sort_dir_first: YAZI.mgr.sort_dir_first.get(),
|
||||||
sort_translit: YAZI.mgr.sort_translit.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,
|
reverse: value.sort_reverse,
|
||||||
dir_first: value.sort_dir_first,
|
dir_first: value.sort_dir_first,
|
||||||
translit: value.sort_translit,
|
translit: value.sort_translit,
|
||||||
|
dir_by: value.sort_dir_by,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub struct FilesSorter {
|
||||||
pub reverse: bool,
|
pub reverse: bool,
|
||||||
pub dir_first: bool,
|
pub dir_first: bool,
|
||||||
pub translit: bool,
|
pub translit: bool,
|
||||||
|
pub dir_by: Option<SortBy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilesSorter {
|
impl FilesSorter {
|
||||||
|
|
@ -21,6 +22,32 @@ impl FilesSorter {
|
||||||
return;
|
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| {
|
let by_alphabetical = |a: &File, b: &File| {
|
||||||
if self.sensitive {
|
if self.sensitive {
|
||||||
self.cmp(a.urn().encoded_bytes(), b.urn().encoded_bytes(), self.promote(a, b))
|
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 dir_first: Option<bool>,
|
||||||
pub sensitive: Option<bool>,
|
pub sensitive: Option<bool>,
|
||||||
pub translit: Option<bool>,
|
pub translit: Option<bool>,
|
||||||
|
pub dir_by: Option<SortBy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<CmdCow> for SortOpt {
|
impl TryFrom<CmdCow> for SortOpt {
|
||||||
|
|
@ -22,6 +23,7 @@ impl TryFrom<CmdCow> for SortOpt {
|
||||||
dir_first: c.get("dir-first").ok(),
|
dir_first: c.get("dir-first").ok(),
|
||||||
sensitive: c.get("sensitive").ok(),
|
sensitive: c.get("sensitive").ok(),
|
||||||
translit: c.get("translit").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