feat: Vim-like lua action that runs an inline Lua snippet

This commit is contained in:
sxyazi 2026-03-25 19:21:04 +08:00
parent 5b2dd41e64
commit 4a4ce25768
No known key found for this signature in database
7 changed files with 69 additions and 1 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]) - Custom tab name ([#3666])
- New `--in` for `search` action to set search directory ([#3696]) - New `--in` for `search` action to set search directory ([#3696])
- Multi-file spotter ([#3733]) - Multi-file spotter ([#3733])
- Vim-like `lua` action that runs an inline Lua snippet ([#1234])
- Certificate authentication for SFTP VFS provider ([#3716]) - Certificate authentication for SFTP VFS provider ([#3716])
- New `hovered` condition specifying different icons for hovered files ([#3728]) - New `hovered` condition specifying different icons for hovered files ([#3728])
- Allow using `ps.sub()` in `init.lua` directly without a plugin ([#3638]) - Allow using `ps.sub()` in `init.lua` directly without a plugin ([#3638])

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

@ -0,0 +1,24 @@
use anyhow::Result;
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 result = Lives::scope(cx.core, || {
let chunk = LUA.load(&*opt.code).set_name("anonymous");
Sendable::value_to_data(&LUA, chunk.eval()?)
});
succ!(result?);
}
}

View file

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

View file

@ -12,6 +12,7 @@ pub enum Spark<'a> {
AppBootstrap(yazi_parser::VoidOpt), AppBootstrap(yazi_parser::VoidOpt),
AppDeprecate(yazi_parser::app::DeprecateOpt), AppDeprecate(yazi_parser::app::DeprecateOpt),
AppFocus(yazi_parser::VoidOpt), AppFocus(yazi_parser::VoidOpt),
AppLua(yazi_parser::app::LuaOpt),
AppMouse(yazi_parser::app::MouseOpt), AppMouse(yazi_parser::app::MouseOpt),
AppPlugin(yazi_parser::app::PluginOpt), AppPlugin(yazi_parser::app::PluginOpt),
AppPluginDo(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::AppBootstrap(b) => b.into_lua(lua),
Self::AppDeprecate(b) => b.into_lua(lua), Self::AppDeprecate(b) => b.into_lua(lua),
Self::AppFocus(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::AppMouse(b) => b.into_lua(lua),
Self::AppPlugin(b) => b.into_lua(lua), Self::AppPlugin(b) => b.into_lua(lua),
Self::AppPluginDo(b) => b.into_lua(lua), Self::AppPluginDo(b) => b.into_lua(lua),
@ -364,6 +366,7 @@ try_from_spark!(
// App // App
try_from_spark!(yazi_parser::ArrowOpt, mgr:arrow, mgr:tab_swap); 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::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::MouseOpt, app:mouse);
try_from_spark!(yazi_parser::app::PluginOpt, app:plugin, app:plugin_do); try_from_spark!(yazi_parser::app::PluginOpt, app:plugin, app:plugin_do);
try_from_spark!(yazi_parser::app::QuitOpt, app:quit, mgr:quit); 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);
on!(plugin_do); on!(plugin_do);
on!(update_progress); on!(update_progress);
on!(lua);
on!(deprecate); on!(deprecate);
on!(quit); on!(quit);
@ -156,6 +157,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Mgr), "help" => act!(help:toggle, cx, Layer::Mgr),
// Plugin // Plugin
"plugin" => act!(app:plugin, cx, action), "plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(), _ => succ!(),
} }
} }
@ -186,6 +189,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Tasks), "help" => act!(help:toggle, cx, Layer::Tasks),
// Plugin // Plugin
"plugin" => act!(app:plugin, cx, action), "plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(), _ => succ!(),
} }
} }
@ -211,6 +216,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Spot), "help" => act!(help:toggle, cx, Layer::Spot),
// Plugin // Plugin
"plugin" => act!(app:plugin, cx, action), "plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(), _ => succ!(),
} }
} }
@ -235,6 +242,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Pick), "help" => act!(help:toggle, cx, Layer::Pick),
// Plugin // Plugin
"plugin" => act!(app:plugin, cx, action), "plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(), _ => succ!(),
} }
} }
@ -262,6 +271,8 @@ impl<'a> Executor<'a> {
"help" => return act!(help:toggle, cx, Layer::Input), "help" => return act!(help:toggle, cx, Layer::Input),
// Plugin // Plugin
"plugin" => return act!(app:plugin, cx, action), "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), "close" => act!(help:toggle, cx, Layer::Help),
// Plugin // Plugin
"plugin" => act!(app:plugin, cx, action), "plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(), _ => succ!(),
} }
} }
@ -337,6 +350,8 @@ impl<'a> Executor<'a> {
"help" => act!(help:toggle, cx, Layer::Cmp), "help" => act!(help:toggle, cx, Layer::Cmp),
// Plugin // Plugin
"plugin" => act!(app:plugin, cx, action), "plugin" => act!(app:plugin, cx, action),
// Lua
"lua" => act!(app:lua, cx, action),
_ => succ!(), _ => 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);