feat: add toggle support for sort boolean flags

This commit is contained in:
Alastair Scheuermann 2026-03-19 13:45:22 -06:00
parent a246e9c7c0
commit d67dc26990
2 changed files with 108 additions and 9 deletions

View file

@ -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};
@ -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);
@ -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),

View file

@ -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>,
}
@ -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"),
}
}
}