Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions app/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::{emit, files::FilesSort, input::InputMode};
use core::{emit, files::FilesSorter, input::InputMode};
use std::path::PathBuf;

use config::{keymap::{Control, Exec, Key, KeymapLayer}, manager::SortBy, KEYMAP};
Expand Down Expand Up @@ -130,7 +130,7 @@ impl Executor {

// Sorting
"sort" => {
let b = cx.manager.current_mut().files.set_sort(FilesSort {
let b = cx.manager.current_mut().files.set_sorter(FilesSorter {
by: SortBy::try_from(exec.args.get(0).cloned().unwrap_or_default())
.unwrap_or_default(),
reverse: exec.named.contains_key("reverse"),
Expand Down
3 changes: 2 additions & 1 deletion config/docs/yazi.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

- sort_by: File sorting method

- `"alphabetical"`: Sort alphabetically
- `"alphabetical"`: Sort alphabetically, e.g. `1.md` < `10.md` < `2.md`
- `"created"`: Sort by creation time
- `"modified"`: Sort by last modified time
- `"natural"`: Sort naturally, e.g. `1.md` < `2.md` < `10.md`
- `"size"`: Sort by file size

- sort_reverse: Display files in reverse order
Expand Down
18 changes: 10 additions & 8 deletions config/preset/keymap.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,16 @@ keymap = [
{ on = [ "c", "n" ], exec = "copy name_without_ext" },

# Sorting
{ on = [ ",", "a" ], exec = "sort alphabetical" },
{ on = [ ",", "A" ], exec = "sort alphabetical --reverse" },
{ on = [ ",", "c" ], exec = "sort created" },
{ on = [ ",", "C" ], exec = "sort created --reverse" },
{ on = [ ",", "m" ], exec = "sort modified" },
{ on = [ ",", "M" ], exec = "sort modified --reverse" },
{ on = [ ",", "s" ], exec = "sort size" },
{ on = [ ",", "S" ], exec = "sort size --reverse" },
{ on = [ ",", "a" ], exec = "sort alphabetical --dir_first" },
{ on = [ ",", "A" ], exec = "sort alphabetical --reverse --dir_first" },
{ on = [ ",", "c" ], exec = "sort created --dir_first" },
{ on = [ ",", "C" ], exec = "sort created --reverse --dir_first" },
{ on = [ ",", "m" ], exec = "sort modified --dir_first" },
{ on = [ ",", "M" ], exec = "sort modified --reverse --dir_first" },
{ on = [ ",", "n" ], exec = "sort natural --dir_first" },
{ on = [ ",", "N" ], exec = "sort natural --reverse --dir_first" },
{ on = [ ",", "s" ], exec = "sort size --dir_first" },
{ on = [ ",", "S" ], exec = "sort size --reverse --dir_first" },

# Tabs
{ on = [ "t" ], exec = "tab_create --current" },
Expand Down
2 changes: 2 additions & 0 deletions config/src/manager/sorting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub enum SortBy {
Alphabetical,
Created,
Modified,
Natural,
Size,
}

Expand All @@ -19,6 +20,7 @@ impl TryFrom<String> for SortBy {
"alphabetical" => Self::Alphabetical,
"created" => Self::Created,
"modified" => Self::Modified,
"natural" => Self::Natural,
"size" => Self::Size,
_ => bail!("invalid sort_by value: {s}"),
})
Expand Down
1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ async-channel = "^1"
crossterm = "^0"
futures = "^0"
indexmap = "^2"
natord = "^1"
notify = { version = "^6", default-features = false, features = [ "macos_fsevent" ] }
parking_lot = "^0"
ratatui = "^0"
Expand Down
86 changes: 12 additions & 74 deletions core/src/files/files.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
use std::{cmp::Ordering, collections::BTreeMap, ops::{Deref, DerefMut}, path::{Path, PathBuf}};
use std::{collections::BTreeMap, ops::{Deref, DerefMut}, path::{Path, PathBuf}};

use anyhow::Result;
use config::{manager::SortBy, MANAGER};
use config::MANAGER;
use indexmap::IndexMap;
use tokio::fs;

use super::File;
use super::{File, FilesSorter};

pub struct Files {
items: IndexMap<PathBuf, File>,
pub sort: FilesSort,
pub sorter: FilesSorter,
pub show_hidden: bool,
}

impl Default for Files {
fn default() -> Self {
Self {
items: Default::default(),
sort: Default::default(),
sorter: Default::default(),
show_hidden: MANAGER.show_hidden,
}
}
Expand Down Expand Up @@ -53,12 +53,12 @@ impl Files {
}

#[inline]
pub fn set_sort(&mut self, sort: FilesSort) -> bool {
if self.sort == sort {
pub fn set_sorter(&mut self, sort: FilesSorter) -> bool {
if self.sorter == sort {
return false;
}
self.sort = sort;
self.sort()
self.sorter = sort;
self.sorter.sort(&mut self.items)
}

pub fn update_read(&mut self, mut items: BTreeMap<PathBuf, File>) -> bool {
Expand All @@ -80,7 +80,7 @@ impl Files {

self.items.clear();
self.items.extend(items);
self.sort();
self.sorter.sort(&mut self.items);
true
}

Expand All @@ -92,14 +92,14 @@ impl Files {
}

self.items.extend(items);
self.sort();
self.sorter.sort(&mut self.items);
true
}

pub fn update_search(&mut self, items: BTreeMap<PathBuf, File>) -> bool {
if !items.is_empty() {
self.items.extend(items);
self.sort();
self.sorter.sort(&mut self.items);
return true;
}

Expand All @@ -110,51 +110,6 @@ impl Files {

false
}

fn sort(&mut self) -> bool {
if self.items.is_empty() {
return false;
}

#[inline]
#[allow(clippy::collapsible_else_if)]
fn cmp<T: Ord>(a: T, b: T, reverse: bool, promote: Ordering) -> Ordering {
if promote != Ordering::Equal {
promote
} else {
if reverse { b.cmp(&a) } else { a.cmp(&b) }
}
}

#[inline]
fn promote(a: &File, b: &File, dir_first: bool) -> Ordering {
if dir_first { b.meta.is_dir().cmp(&a.meta.is_dir()) } else { Ordering::Equal }
}

let reverse = self.sort.reverse;
let dir_first = self.sort.dir_first;
match self.sort.by {
SortBy::Alphabetical => {
self.items.sort_by(|_, a, _, b| cmp(&a.path, &b.path, reverse, promote(a, b, dir_first)))
}
SortBy::Created => self.items.sort_by(|_, a, _, b| {
if let (Ok(aa), Ok(bb)) = (a.meta.created(), b.meta.created()) {
return cmp(aa, bb, reverse, promote(a, b, dir_first));
}
Ordering::Equal
}),
SortBy::Modified => self.items.sort_by(|_, a, _, b| {
if let (Ok(aa), Ok(bb)) = (a.meta.modified(), b.meta.modified()) {
return cmp(aa, bb, reverse, promote(a, b, dir_first));
}
Ordering::Equal
}),
SortBy::Size => self.items.sort_by(|_, a, _, b| {
cmp(a.length.unwrap_or(0), b.length.unwrap_or(0), reverse, promote(a, b, dir_first))
}),
}
true
}
}

impl Deref for Files {
Expand All @@ -167,23 +122,6 @@ impl DerefMut for Files {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items }
}

#[derive(PartialEq)]
pub struct FilesSort {
pub by: SortBy,
pub reverse: bool,
pub dir_first: bool,
}

impl Default for FilesSort {
fn default() -> Self {
Self {
by: MANAGER.sort_by,
reverse: MANAGER.sort_reverse,
dir_first: MANAGER.sort_dir_first,
}
}
}

#[derive(Debug)]
pub enum FilesOp {
Read(PathBuf, BTreeMap<PathBuf, File>),
Expand Down
2 changes: 2 additions & 0 deletions core/src/files/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod file;
mod files;
mod sorter;

pub use file::*;
pub use files::*;
pub use sorter::*;
96 changes: 96 additions & 0 deletions core/src/files/sorter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use std::{cmp::Ordering, path::PathBuf};

use config::{manager::SortBy, MANAGER};
use indexmap::IndexMap;

use super::File;

#[derive(PartialEq)]
pub struct FilesSorter {
pub by: SortBy,
pub reverse: bool,
pub dir_first: bool,
}

impl Default for FilesSorter {
fn default() -> Self {
Self {
by: MANAGER.sort_by,
reverse: MANAGER.sort_reverse,
dir_first: MANAGER.sort_dir_first,
}
}
}

impl FilesSorter {
pub(super) fn sort(&self, items: &mut IndexMap<PathBuf, File>) -> bool {
if items.is_empty() {
return false;
}

match self.by {
SortBy::Alphabetical => {
items.sort_unstable_by(|_, a, _, b| self.cmp(&a.path, &b.path, self.promote(a, b)))
}
SortBy::Created => items.sort_unstable_by(|_, a, _, b| {
if let (Ok(aa), Ok(bb)) = (a.meta.created(), b.meta.created()) {
return self.cmp(aa, bb, self.promote(a, b));
}
Ordering::Equal
}),
SortBy::Modified => items.sort_unstable_by(|_, a, _, b| {
if let (Ok(aa), Ok(bb)) = (a.meta.modified(), b.meta.modified()) {
return self.cmp(aa, bb, self.promote(a, b));
}
Ordering::Equal
}),
SortBy::Natural => self.sort_naturally(items),
SortBy::Size => items.sort_unstable_by(|_, a, _, b| {
self.cmp(a.length.unwrap_or(0), b.length.unwrap_or(0), self.promote(a, b))
}),
}
true
}

fn sort_naturally(&self, items: &mut IndexMap<PathBuf, File>) {
let mut indices = Vec::with_capacity(items.len());
let mut entities = Vec::with_capacity(items.len());
for (i, (path, file)) in items.into_iter().enumerate() {
indices.push(i);
entities.push((path.to_string_lossy(), file));
}

indices.sort_unstable_by(|&a, &b| {
let promote = self.promote(entities[a].1, entities[b].1);
if promote != Ordering::Equal {
promote
} else if self.reverse {
natord::compare(&entities[b].0, &entities[a].0)
} else {
natord::compare(&entities[a].0, &entities[b].0)
}
});

let mut new = IndexMap::with_capacity(indices.len());
for i in indices {
let file = entities[i].1.clone();
new.insert(file.path(), file);
}
*items = new;
}

#[inline]
#[allow(clippy::collapsible_else_if)]
fn cmp<T: Ord>(&self, a: T, b: T, promote: Ordering) -> Ordering {
if promote != Ordering::Equal {
promote
} else {
if self.reverse { b.cmp(&a) } else { a.cmp(&b) }
}
}

#[inline]
fn promote(&self, a: &File, b: &File) -> Ordering {
if self.dir_first { b.meta.is_dir().cmp(&a.meta.is_dir()) } else { Ordering::Equal }
}
}
2 changes: 1 addition & 1 deletion core/src/tasks/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl Tasks {

#[inline]
pub fn precache_size(&self, targets: &Files) -> bool {
if targets.sort.by != SortBy::Size {
if targets.sorter.by != SortBy::Size {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel"],"language":"en","flagWords":[],"version":"0.2"}
{"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord"],"language":"en","version":"0.2"}