feat: platform-specific key binding (#2526)

This commit is contained in:
三咲雅 · Misaki Masa 2025-03-24 19:09:37 +08:00 committed by GitHub
parent 83f4deecfe
commit e138364102
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 57 additions and 43 deletions

View file

@ -1,4 +1,4 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result, bail};
use tokio::fs; use tokio::fs;
use twox_hash::XxHash3_128; use twox_hash::XxHash3_128;
use yazi_fs::ok_or_not_found; use yazi_fs::ok_or_not_found;
@ -22,18 +22,21 @@ impl Dependency {
&["LICENSE", "README.md", "main.lua"][..] &["LICENSE", "README.md", "main.lua"][..]
}; };
let mut hasher = XxHash3_128::new(); let mut h = XxHash3_128::new();
for file in files { for &file in files {
hasher.write(file.as_bytes()); h.write(file.as_bytes());
hasher.write(b"VpvFw9Atb7cWGOdqhZCra634CcJJRlsRl72RbZeV0vpG1\0"); h.write(b"VpvFw9Atb7cWGOdqhZCra634CcJJRlsRl72RbZeV0vpG1\0");
hasher.write(&ok_or_not_found(fs::read(dir.join(file)).await)?); h.write(&ok_or_not_found(fs::read(dir.join(file)).await)?);
} }
let mut assets = vec![]; let mut assets = vec![];
match fs::read_dir(dir.join("assets")).await { match fs::read_dir(dir.join("assets")).await {
Ok(mut it) => { Ok(mut it) => {
while let Some(entry) = it.next_entry().await? { while let Some(entry) = it.next_entry().await? {
assets.push((entry.file_name(), fs::read(entry.path()).await?)); let Ok(name) = entry.file_name().into_string() else {
bail!("asset path is not valid UTF-8: {}", entry.path().display());
};
assets.push((name, fs::read(entry.path()).await?));
} }
} }
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
@ -42,11 +45,11 @@ impl Dependency {
assets.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); assets.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
for (name, data) in assets { for (name, data) in assets {
hasher.write(name.as_encoded_bytes()); h.write(name.as_bytes());
hasher.write(b"pQU2in0xcsu97Y77Nuq2LnT8mczMlFj22idcYRmMrglqU\0"); h.write(b"pQU2in0xcsu97Y77Nuq2LnT8mczMlFj22idcYRmMrglqU\0");
hasher.write(&data); h.write(&data);
} }
Ok(format!("{:x}", hasher.finish_128())) Ok(format!("{:x}", h.finish_128()))
} }
} }

View file

@ -1,5 +1,6 @@
use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock}; use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock};
use anyhow::Result;
use regex::Regex; use regex::Regex;
use serde::Deserialize; use serde::Deserialize;
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::{Layer, event::Cmd};
@ -15,6 +16,8 @@ pub struct Chord {
#[serde(deserialize_with = "super::deserialize_run")] #[serde(deserialize_with = "super::deserialize_run")]
pub run: Vec<Cmd>, pub run: Vec<Cmd>,
pub desc: Option<String>, pub desc: Option<String>,
#[serde(rename = "for")]
pub for_: Option<String>,
} }
impl PartialEq for Chord { impl PartialEq for Chord {
@ -53,14 +56,15 @@ impl Chord {
pub(super) fn noop(&self) -> bool { pub(super) fn noop(&self) -> bool {
self.run.len() == 1 && self.run[0].name == "noop" && self.run[0].args.is_empty() self.run.len() == 1 && self.run[0].name == "noop" && self.run[0].args.is_empty()
} }
}
#[inline] impl Chord {
pub(super) fn with_layer(mut self, layer: Layer) -> Self { pub(super) fn reshape(mut self, layer: Layer) -> Result<Self> {
for c in &mut self.run { for cmd in &mut self.run {
if c.layer == Default::default() { if cmd.layer == Default::default() {
c.layer = layer; cmd.layer = layer;
} }
} }
self Ok(self)
} }
} }

View file

@ -23,7 +23,7 @@ where
where where
A: de::SeqAccess<'de>, A: de::SeqAccess<'de>,
{ {
let mut cmds = vec![]; let mut cmds = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(value) = &seq.next_element::<String>()? { while let Some(value) = &seq.next_element::<String>()? {
cmds.push(Key::from_str(value).map_err(de::Error::custom)?); cmds.push(Key::from_str(value).map_err(de::Error::custom)?);
} }
@ -61,7 +61,7 @@ where
where where
A: de::SeqAccess<'de>, A: de::SeqAccess<'de>,
{ {
let mut cmds = vec![]; let mut cmds = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(value) = &seq.next_element::<String>()? { while let Some(value) = &seq.next_element::<String>()? {
cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?); cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?);
} }

View file

@ -3,19 +3,19 @@ use serde::Deserialize;
use yazi_codegen::DeserializeOver1; use yazi_codegen::DeserializeOver1;
use yazi_shared::Layer; use yazi_shared::Layer;
use super::{Chord, KeymapRule}; use super::{Chord, KeymapRules};
#[derive(Deserialize, DeserializeOver1)] #[derive(Deserialize, DeserializeOver1)]
pub struct Keymap { pub struct Keymap {
#[serde(rename = "manager")] #[serde(rename = "manager")]
pub mgr: KeymapRule, pub mgr: KeymapRules,
pub tasks: KeymapRule, pub tasks: KeymapRules,
pub spot: KeymapRule, pub spot: KeymapRules,
pub pick: KeymapRule, pub pick: KeymapRules,
pub input: KeymapRule, pub input: KeymapRules,
pub confirm: KeymapRule, pub confirm: KeymapRules,
pub help: KeymapRule, pub help: KeymapRules,
pub cmp: KeymapRule, pub cmp: KeymapRules,
} }
impl Keymap { impl Keymap {

View file

@ -1 +1 @@
yazi_macro::mod_flat!(chord cow deserializers key keymap rule); yazi_macro::mod_flat!(chord cow deserializers key keymap rules);

View file

@ -6,10 +6,10 @@ use yazi_codegen::DeserializeOver2;
use yazi_shared::Layer; use yazi_shared::Layer;
use super::Chord; use super::Chord;
use crate::{Preset, keymap::Key}; use crate::{Preset, check_for, keymap::Key};
#[derive(Default, Deserialize, DeserializeOver2)] #[derive(Default, Deserialize, DeserializeOver2)]
pub struct KeymapRule { pub struct KeymapRules {
pub keymap: Vec<Chord>, pub keymap: Vec<Chord>,
#[serde(default)] #[serde(default)]
prepend_keymap: Vec<Chord>, prepend_keymap: Vec<Chord>,
@ -17,13 +17,13 @@ pub struct KeymapRule {
append_keymap: Vec<Chord>, append_keymap: Vec<Chord>,
} }
impl Deref for KeymapRule { impl Deref for KeymapRules {
type Target = Vec<Chord>; type Target = Vec<Chord>;
fn deref(&self) -> &Self::Target { &self.keymap } fn deref(&self) -> &Self::Target { &self.keymap }
} }
impl KeymapRule { impl KeymapRules {
pub(crate) fn reshape(self, layer: Layer) -> Result<Self> { pub(crate) fn reshape(self, layer: Layer) -> Result<Self> {
#[inline] #[inline]
fn on(Chord { on, .. }: &Chord) -> [Key; 2] { fn on(Chord { on, .. }: &Chord) -> [Key; 2] {
@ -38,9 +38,10 @@ impl KeymapRule {
self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))), self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))),
self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))), self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))),
) )
.filter(|chord| !chord.noop()) .map(|mut chord| (chord.for_.take(), chord))
.map(|chord| chord.with_layer(layer)) .filter(|(for_, chord)| !chord.noop() && check_for(for_.as_deref()))
.collect(); .map(|(_, chord)| chord.reshape(layer))
.collect::<Result<_>>()?;
Ok(Self { keymap, ..Default::default() }) Ok(Self { keymap, ..Default::default() })
} }

View file

@ -2,7 +2,7 @@
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which); yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which);
yazi_macro::mod_flat!(layout pattern preset priority yazi); yazi_macro::mod_flat!(layout pattern platform preset priority yazi);
use std::io::{Read, Write}; use std::io::{Read, Write};

View file

@ -5,6 +5,7 @@ use indexmap::IndexSet;
use serde::Deserialize; use serde::Deserialize;
use super::OpenerRule; use super::OpenerRule;
use crate::check_for;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Opener(HashMap<String, Vec<OpenerRule>>); pub struct Opener(HashMap<String, Vec<OpenerRule>>);
@ -44,12 +45,7 @@ impl Opener {
*rules = mem::take(rules) *rules = mem::take(rules)
.into_iter() .into_iter()
.map(|mut r| (r.for_.take(), r)) .map(|mut r| (r.for_.take(), r))
.filter(|(for_, _)| match for_.as_ref().map(|s| s.as_str()) { .filter(|(for_, _)| check_for(for_.as_deref()))
Some("unix") if cfg!(unix) => true,
Some(os) if os == std::env::consts::OS => true,
Some(_) => false,
None => true,
})
.map(|(_, r)| r.reshape()) .map(|(_, r)| r.reshape())
.collect::<Result<IndexSet<_>>>()? .collect::<Result<IndexSet<_>>>()?
.into_iter() .into_iter()

View file

@ -0,0 +1,9 @@
#[inline]
pub(crate) fn check_for(for_: Option<&str>) -> bool {
match for_.as_ref().map(|s| s.as_ref()) {
Some("unix") if cfg!(unix) => true,
Some(os) if os == std::env::consts::OS => true,
Some(_) => false,
None => true,
}
}

View file

@ -23,6 +23,7 @@ impl Utils {
on: Self::parse_keys(cand.raw_get("on")?)?, on: Self::parse_keys(cand.raw_get("on")?)?,
run: vec![Cmd::args("which:callback", &[i]).with_any("tx", tx.clone())], run: vec![Cmd::args("which:callback", &[i]).with_any("tx", tx.clone())],
desc: cand.raw_get("desc").ok(), desc: cand.raw_get("desc").ok(),
for_: None,
}); });
} }