Skip to content
Open
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
18 changes: 13 additions & 5 deletions yazi-actor/src/mgr/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
use yazi_dds::spark::SparkKind;
use yazi_fs::{FilesSorter, FolderStage};
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::mgr::SortOpt;
use yazi_parser::mgr::{SortBoolState, SortOpt};
use yazi_shared::{Source, data::Data};

use crate::{Actor, Ctx};
Expand All @@ -18,10 +18,10 @@ impl Actor for Sort {
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let pref = &mut cx.tab_mut().pref;
pref.sort_by = opt.by.unwrap_or(pref.sort_by);
pref.sort_reverse = opt.reverse.unwrap_or(pref.sort_reverse);
pref.sort_dir_first = opt.dir_first.unwrap_or(pref.sort_dir_first);
pref.sort_reverse = opt.reverse.unwrap_or_default().bool(pref.sort_reverse);
pref.sort_dir_first = opt.dir_first.unwrap_or_default().bool(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_translit = opt.translit.unwrap_or_default().bool(pref.sort_translit);
pref.sort_fallback = opt.fallback.unwrap_or(pref.sort_fallback);

let sorter = FilesSorter::from(&*pref);
Expand Down Expand Up @@ -59,7 +59,15 @@ impl Actor for Sort {
succ!();
}

fn hook(cx: &Ctx, _: &Self::Options) -> Option<SparkKind> {
fn hook(cx: &Ctx, opt: &Self::Options) -> Option<SparkKind> {
let has_toggle = [opt.reverse, opt.dir_first, opt.translit]
.iter()
.any(|f| matches!(f, Some(SortBoolState::Toggle)));

if has_toggle {
return None;
}

match cx.source() {
Source::Ind => Some(SparkKind::IndSort),
Source::Key => Some(SparkKind::KeySort),
Expand Down
99 changes: 95 additions & 4 deletions yazi-parser/src/mgr/sort.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use std::str::FromStr;

use anyhow::bail;
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize};
use yazi_binding::SER_OPT;
use yazi_fs::{SortBy, SortFallback};
use yazi_shared::event::ActionCow;
use yazi_shared::{data::Data, event::ActionCow};

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct SortOpt {
pub by: Option<SortBy>,
pub reverse: Option<bool>,
pub dir_first: Option<bool>,
pub reverse: Option<SortBoolState>,
pub dir_first: Option<SortBoolState>,
pub sensitive: Option<bool>,
pub translit: Option<bool>,
pub translit: Option<SortBoolState>,
pub fallback: Option<SortFallback>,
}

Expand All @@ -36,3 +39,91 @@ impl FromLua for SortOpt {
impl IntoLua for SortOpt {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value_with(&self, SER_OPT) }
}

// --- State
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SortBoolState {
#[default]
None,
On,
Off,
Toggle,
}

impl serde::Serialize for SortBoolState {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::On => serializer.serialize_bool(true),
Self::Off => serializer.serialize_bool(false),
Self::Toggle => serializer.serialize_str("toggle"),
Self::None => serializer.serialize_str("none"),
}
}
}

impl FromStr for SortBoolState {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"yes" => Ok(Self::On),
"no" => Ok(Self::Off),
"toggle" => Ok(Self::Toggle),
_ => bail!("unknown sort bool state: {s:?}"),
}
}
}

impl<'de> serde::Deserialize<'de> for SortBoolState {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::{self, Visitor};

struct V;

impl<'de> Visitor<'de> for V {
type Value = SortBoolState;

fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(r#"a boolean or one of "yes", "no", "toggle""#)
}

fn visit_bool<E: de::Error>(self, v: bool) -> Result<Self::Value, E> {
Ok(if v { SortBoolState::On } else { SortBoolState::Off })
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
v.parse().map_err(de::Error::custom)
}

fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
self.visit_str(&v)
}
}

deserializer.deserialize_any(V)
}
}

impl SortBoolState {
pub fn bool(self, old: bool) -> bool {
match self {
Self::None => old,
Self::On => true,
Self::Off => false,
Self::Toggle => !old,
}
}
}

impl TryFrom<&Data> for SortBoolState {
type Error = anyhow::Error;

fn try_from(value: &Data) -> Result<Self, Self::Error> {
match value {
Data::Boolean(true) => Ok(Self::On),
Data::Boolean(false) => Ok(Self::Off),
Data::String(s) => s.parse(),
_ => bail!("not a valid bool state"),
}
}
}
Loading