Skip to content

Commit 07a5deb

Browse files
authored
feat: dynamic keymap Lua API (#4031)
1 parent 4086fbb commit 07a5deb

28 files changed

Lines changed: 315 additions & 167 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
1616

1717
- Drag and drop ([#4005])
1818
- Bulk create ([#3793])
19+
- Dynamic keymap Lua API ([#4031])
1920
- Image preview with Überzug++ on Niri ([#3990])
2021
- New gait for input `backward` and `forward` actions ([#4012])
2122

@@ -1742,3 +1743,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
17421743
[#4005]: https://github.com/sxyazi/yazi/pull/4005
17431744
[#4012]: https://github.com/sxyazi/yazi/pull/4012
17441745
[#4022]: https://github.com/sxyazi/yazi/pull/4022
1746+
[#4031]: https://github.com/sxyazi/yazi/pull/4031

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This guide will help you understand how to contribute to the project.
1111
3. [Development Setup](#development-setup)
1212
4. [How to Contribute](#how-to-contribute)
1313
5. [Pull Requests](#pull-requests)
14+
6. [AI Policy](#ai-policy)
1415

1516
## Getting Started
1617

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

yazi-actor/src/lives/which.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl UserData for Which {
2626
fields.add_cached_field("tx", |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx)));
2727
fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
2828
fields.add_cached_field("cands", |lua, me| {
29-
lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow))
29+
lua.create_sequence_from(me.inner.cands.iter().map(yazi_binding::keymap::Chord::from))
3030
});
3131

3232
fields.add_field_method_get("active", |_, me| Ok(me.inner.active));

yazi-binding/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ mlua = { workspace = true }
3939
ordered-float = { workspace = true }
4040
paste = { workspace = true }
4141
ratatui = { workspace = true }
42+
serde = { workspace = true }
4243
serde_json = { workspace = true }
4344
tokio = { workspace = true }
4445
tokio-stream = { workspace = true }

yazi-binding/src/event/action.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::{mem, ops::{Deref, DerefMut}};
1+
use std::ops::{Deref, DerefMut};
22

3-
use mlua::{UserData, UserDataFields};
3+
use mlua::{FromLua, Lua, UserData, UserDataFields, Value};
44
use yazi_shim::mlua::UserDataFieldsExt;
55

66
use crate::event::Cmd;
@@ -19,12 +19,34 @@ impl DerefMut for Action {
1919
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
2020
}
2121

22+
impl From<Action> for yazi_shared::event::Action {
23+
fn from(value: Action) -> Self { value.inner }
24+
}
25+
26+
impl From<Action> for yazi_shared::event::Actions {
27+
fn from(value: Action) -> Self { Self::from(value.inner) }
28+
}
29+
2230
impl Action {
2331
pub fn new(inner: impl Into<yazi_shared::event::Action>) -> Self { Self { inner: inner.into() } }
2432
}
2533

34+
impl FromLua for Action {
35+
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
36+
Ok(match value {
37+
Value::String(s) => Self { inner: s.to_str()?.parse()? },
38+
Value::UserData(ud) => ud.take()?,
39+
_ => Err(mlua::Error::FromLuaConversionError {
40+
from: value.type_name(),
41+
to: "Action".to_owned(),
42+
message: Some("expected a string or an Action".to_string()),
43+
})?,
44+
})
45+
}
46+
}
47+
2648
impl UserData for Action {
2749
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
28-
fields.add_cached_field_mut("cmd", |_, me| Ok(Cmd::new(mem::take(&mut me.cmd))));
50+
fields.add_cached_field("cmd", |_, me| Ok(Cmd::new(me.cmd.clone())));
2951
}
3052
}

yazi-binding/src/event/actions.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use std::ops::{Deref, DerefMut};
2+
3+
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
4+
5+
use crate::event::Action;
6+
7+
#[derive(Clone)]
8+
pub struct Actions {
9+
inner: yazi_shared::event::Actions,
10+
}
11+
12+
impl Deref for Actions {
13+
type Target = yazi_shared::event::Actions;
14+
15+
fn deref(&self) -> &Self::Target { &self.inner }
16+
}
17+
18+
impl DerefMut for Actions {
19+
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
20+
}
21+
22+
impl From<Actions> for yazi_shared::event::Actions {
23+
fn from(value: Actions) -> Self { value.inner }
24+
}
25+
26+
impl Actions {
27+
pub fn new(inner: yazi_shared::event::Actions) -> Self { Self { inner } }
28+
}
29+
30+
impl FromLua for Actions {
31+
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
32+
let inner = match value {
33+
Value::Table(t) => t
34+
.sequence_values::<Action>()
35+
.map(|a| a.map(Into::into))
36+
.collect::<mlua::Result<Vec<_>>>()?
37+
.into(),
38+
v @ Value::String(_) => Action::from_lua(v, lua)?.into(),
39+
_ => Err("expected a string or a table of actions".into_lua_err())?,
40+
};
41+
42+
Ok(Self { inner })
43+
}
44+
}
45+
46+
impl IntoLua for Actions {
47+
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
48+
lua.create_sequence_from(self.inner.into_iter().map(Action::new))?.into_lua(lua)
49+
}
50+
}

yazi-binding/src/event/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
yazi_macro::mod_flat!(action cmd);
1+
yazi_macro::mod_flat!(action actions cmd);

yazi-binding/src/keymap/chord.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,61 @@
1-
use std::{ops::Deref, sync::Arc};
1+
use std::ops::Deref;
22

3-
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
3+
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, Value};
4+
use serde::Deserialize;
5+
use yazi_codegen::FromLuaOwned;
46
use yazi_shim::mlua::UserDataFieldsExt;
57

6-
use crate::{Id, Iter, event::Action, keymap::Key};
8+
use crate::{Id, Iter, event::Actions, keymap::Key};
79

10+
#[derive(FromLuaOwned)]
811
pub struct Chord {
9-
inner: Arc<yazi_config::keymap::Chord>,
12+
inner: yazi_config::keymap::ChordArc,
1013
}
1114

1215
impl Deref for Chord {
13-
type Target = Arc<yazi_config::keymap::Chord>;
16+
type Target = yazi_config::keymap::ChordArc;
1417

1518
fn deref(&self) -> &Self::Target { &self.inner }
1619
}
1720

18-
impl From<Chord> for Arc<yazi_config::keymap::Chord> {
21+
impl From<&yazi_config::keymap::ChordArc> for Chord {
22+
fn from(value: &yazi_config::keymap::ChordArc) -> Self { Self { inner: value.clone() } }
23+
}
24+
25+
impl From<Chord> for yazi_config::keymap::ChordArc {
1926
fn from(value: Chord) -> Self { value.inner }
2027
}
2128

2229
impl Chord {
23-
pub fn new(inner: impl Into<Arc<yazi_config::keymap::Chord>>) -> Self {
30+
pub fn new(inner: impl Into<yazi_config::keymap::ChordArc>) -> Self {
2431
Self { inner: inner.into() }
2532
}
2633
}
2734

28-
impl FromLua for Chord {
29-
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
30-
Ok(Self::new(lua.from_value::<yazi_config::keymap::Chord>(value)?))
35+
impl TryFrom<(Value, yazi_shared::Layer)> for Chord {
36+
type Error = mlua::Error;
37+
38+
fn try_from((value, layer): (Value, yazi_shared::Layer)) -> Result<Self, Self::Error> {
39+
use yazi_config::keymap::Chord as C;
40+
use yazi_shared::Layer as L;
41+
42+
let de = mlua::serde::Deserializer::new(value);
43+
let inner = match layer {
44+
L::Null => C::<{ L::Null as u8 }>::deserialize(de)?.into(),
45+
L::App => C::<{ L::App as u8 }>::deserialize(de)?.into(),
46+
L::Mgr => C::<{ L::Mgr as u8 }>::deserialize(de)?.into(),
47+
L::Tasks => C::<{ L::Tasks as u8 }>::deserialize(de)?.into(),
48+
L::Spot => C::<{ L::Spot as u8 }>::deserialize(de)?.into(),
49+
L::Pick => C::<{ L::Pick as u8 }>::deserialize(de)?.into(),
50+
L::Input => C::<{ L::Input as u8 }>::deserialize(de)?.into(),
51+
L::Confirm => C::<{ L::Confirm as u8 }>::deserialize(de)?.into(),
52+
L::Help => C::<{ L::Help as u8 }>::deserialize(de)?.into(),
53+
L::Cmp => C::<{ L::Cmp as u8 }>::deserialize(de)?.into(),
54+
L::Which => C::<{ L::Which as u8 }>::deserialize(de)?.into(),
55+
L::Notify => C::<{ L::Notify as u8 }>::deserialize(de)?.into(),
56+
};
57+
58+
Ok(Self { inner })
3159
}
3260
}
3361

@@ -38,9 +66,7 @@ impl UserData for Chord {
3866
fields
3967
.add_cached_field("on", |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key)));
4068

41-
fields.add_cached_field("run", |lua, me| {
42-
lua.create_sequence_from(me.run.iter().cloned().map(Action::new))
43-
});
69+
fields.add_cached_field("run", |_, me| Ok(Actions::new(me.run.clone())));
4470

4571
fields.add_cached_field("desc", |lua, me| lua.create_string(&me.desc));
4672
}

yazi-binding/src/keymap/chord_cow.rs

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)