feat: Vim-like lua action that runs an inline Lua snippet (#3813)

This commit is contained in:
sxyazi 2026-03-25 19:57:31 +08:00
parent 5b2dd41e64
commit b67798ea19
No known key found for this signature in database
10 changed files with 90 additions and 18 deletions

View file

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- Custom tab name ([#3666])
- New `--in` for `search` action to set search directory ([#3696])
- Multi-file spotter ([#3733])
- Vim-like `lua` action that runs an inline Lua snippet ([#3813])
- Certificate authentication for SFTP VFS provider ([#3716])
- New `hovered` condition specifying different icons for hovered files ([#3728])
- Allow using `ps.sub()` in `init.lua` directly without a plugin ([#3638])
@ -1692,3 +1693,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
[#3781]: https://github.com/sxyazi/yazi/pull/3781
[#3792]: https://github.com/sxyazi/yazi/pull/3792
[#3804]: https://github.com/sxyazi/yazi/pull/3804
[#3813]: https://github.com/sxyazi/yazi/pull/3813

32
Cargo.lock generated
View file

@ -2269,9 +2269,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]]
name = "libredox"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08"
dependencies = [
"libc",
]
@ -2608,9 +2608,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-derive"
@ -3972,9 +3972,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "1.0.4"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98"
dependencies = [
"serde_core",
]
@ -4615,9 +4615,9 @@ dependencies = [
[[package]]
name = "toml"
version = "1.0.7+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96"
checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc"
dependencies = [
"indexmap 2.13.0",
"serde_core",
@ -4630,27 +4630,27 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "1.0.1+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.10+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.7+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed"
[[package]]
name = "tracing"
@ -4784,9 +4784,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
checksum = "da36089a805484bcccfffe0739803392c8298778a2d2f09febf76fac5ad9025b"
[[package]]
name = "unicode-truncate"

View file

@ -72,7 +72,7 @@ thiserror = "2.0.18"
tokio = { version = "1.50.0", features = [ "full" ] }
tokio-stream = "0.1.18"
tokio-util = "0.7.18"
toml = { version = "1.0.7" }
toml = { version = "1.1.0" }
tracing = { version = "0.1.44", features = [ "max_level_debug", "release_max_level_debug" ] }
twox-hash = { version = "2.1.2", default-features = false, features = [ "std", "random", "xxhash3_128" ] }
typed-path = "0.12.3"

25
yazi-actor/src/app/lua.rs Normal file
View file

@ -0,0 +1,25 @@
use anyhow::Result;
use yazi_binding::runtime_scope;
use yazi_dds::Sendable;
use yazi_macro::succ;
use yazi_parser::app::LuaOpt;
use yazi_plugin::LUA;
use yazi_shared::data::Data;
use crate::{Actor, Ctx, lives::Lives};
pub struct Lua;
impl Actor for Lua {
type Options = LuaOpt;
const NAME: &str = "lua";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
let chunk = LUA.load(&*opt.code).set_name("anonymous");
let result = Lives::scope(cx.core, || {
runtime_scope!(LUA, "inline", Sendable::value_to_data(&LUA, chunk.eval()?))
});
succ!(result?);
}
}

View file

@ -3,6 +3,7 @@ yazi_macro::mod_flat!(
bootstrap
deprecate
focus
lua
mouse
plugin
plugin_do

View file

@ -12,6 +12,7 @@ pub enum Spark<'a> {
AppBootstrap(yazi_parser::VoidOpt),
AppDeprecate(yazi_parser::app::DeprecateOpt),
AppFocus(yazi_parser::VoidOpt),
AppLua(yazi_parser::app::LuaOpt),
AppMouse(yazi_parser::app::MouseOpt),
AppPlugin(yazi_parser::app::PluginOpt),
AppPluginDo(yazi_parser::app::PluginOpt),
@ -194,6 +195,7 @@ impl<'a> IntoLua for Spark<'a> {
Self::AppBootstrap(b) => b.into_lua(lua),
Self::AppDeprecate(b) => b.into_lua(lua),
Self::AppFocus(b) => b.into_lua(lua),
Self::AppLua(b) => b.into_lua(lua),
Self::AppMouse(b) => b.into_lua(lua),
Self::AppPlugin(b) => b.into_lua(lua),
Self::AppPluginDo(b) => b.into_lua(lua),
@ -364,6 +366,7 @@ try_from_spark!(
// App
try_from_spark!(yazi_parser::ArrowOpt, mgr:arrow, mgr:tab_swap);
try_from_spark!(yazi_parser::app::DeprecateOpt, app:deprecate);
try_from_spark!(yazi_parser::app::LuaOpt, app:lua);
try_from_spark!(yazi_parser::app::MouseOpt, app:mouse);
try_from_spark!(yazi_parser::app::PluginOpt, app:plugin, app:plugin_do);
try_from_spark!(yazi_parser::app::QuitOpt, app:quit, mgr:quit);

View file

@ -45,6 +45,7 @@ impl<'a> Executor<'a> {
on!(plugin);
on!(plugin_do);
on!(update_progress);
on!(lua);
on!(deprecate);
on!(quit);
@ -156,6 +157,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Mgr),
// Plugin
"plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(),
}
}
@ -186,6 +189,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Tasks),
// Plugin
"plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(),
}
}
@ -211,6 +216,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Spot),
// Plugin
"plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(),
}
}
@ -235,6 +242,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Pick),
// Plugin
"plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(),
}
}
@ -262,6 +271,8 @@ impl<'a> Executor<'a> {
"help" => return act!(help:toggle, cx, Layer::Input),
// Plugin
"plugin" => return act!(app:plugin, cx, action),
// Lua
"lua" => return act!(app:lua, cx, action),
_ => {}
}
}
@ -312,6 +323,8 @@ impl<'a> Executor<'a> {
"close" => act!(help:toggle, cx, Layer::Help),
// Plugin
"plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(),
}
}
@ -337,6 +350,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Cmp),
// Plugin
"plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(),
}
}

View file

@ -0,0 +1,24 @@
use std::fmt::Debug;
use anyhow::Result;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_shared::{SStr, event::ActionCow};
#[derive(Clone, Debug, Default)]
pub struct LuaOpt {
pub code: SStr,
}
impl TryFrom<ActionCow> for LuaOpt {
type Error = anyhow::Error;
fn try_from(mut a: ActionCow) -> Result<Self, Self::Error> { Ok(Self { code: a.take_first()? }) }
}
impl FromLua for LuaOpt {
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
}
impl IntoLua for LuaOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(deprecate mouse plugin quit reflow resume stop title update_progress);
yazi_macro::mod_flat!(deprecate lua mouse plugin quit reflow resume stop title update_progress);

View file

@ -69,6 +69,8 @@ impl Default for Loader {
("tab".to_owned(), [][..].into()),
("tabs".to_owned(), [][..].into()),
("tasks".to_owned(), [][..].into()),
// Reserved
("inline".to_owned(), [][..].into()),
]);
Self { cache: RwLock::new(cache) }
}