From 8f5e5491f2cafdb2b5fe14a53eec51586a263a19 Mon Sep 17 00:00:00 2001 From: Arijit Basu Date: Sat, 11 Dec 2021 17:52:10 +0530 Subject: [PATCH] Optimize change directory performance This PR breaks the custom layout renderer API by deprecating the following heavyweight fields in the Lua Context passed to the renderer functions. The following fields are being deprecated: - app.directory_buffer - app.history - app.last_modes However, there's no change in the Lua Context passed to the functions called via `CallLua*` messages. Closes: https://github.com/sayanarijit/xplr/issues/418 --- docs/en/src/layouts.md | 35 ++++++++++++++++++++++-- docs/en/src/message.md | 26 ++++++------------ src/__cache__.lua | 25 ----------------- src/app.rs | 62 +++++++++++++++++++++++------------------- src/lua.rs | 26 ------------------ src/runner.rs | 14 ++++------ src/ui.rs | 14 +++++----- 7 files changed, 88 insertions(+), 114 deletions(-) delete mode 100644 src/__cache__.lua diff --git a/docs/en/src/layouts.md b/docs/en/src/layouts.md index ac0ab82..6ea59d3 100644 --- a/docs/en/src/layouts.md +++ b/docs/en/src/layouts.md @@ -410,7 +410,7 @@ It contains the following information: - [screen_size][37] - [app][38] -## Size +### Size It contains the following information: @@ -421,6 +421,25 @@ It contains the following information: Every field is of integer type. +### app + +This is a lightweight version of the [Lua Context][39]. In this context, the +heavyweight fields like [directory_buffer][50] are omitted for performance +reasons. + +Hence, only the following fields are avilable. + +- [version][40] +- [pwd][41] +- [focused_node][42] +- [selection][43] +- [mode][44] +- [layout][45] +- [input_buffer][46] +- [pid][47] +- [session_path][48] +- [explorer_config][49] + [1]: #builtin [2]: #custom [3]: #layout @@ -458,4 +477,16 @@ Every field is of integer type. [35]: #content-renderer [36]: #content-renderer-argument [37]: #size -[38]: message.md#calllua-argument +[38]: #app +[39]: message.md#lua-context +[40]: message.md#version +[41]: message.md#pwd +[42]: message.md#focused_node +[43]: message.md#selection +[44]: message.md#mode +[45]: message.md#layout +[46]: message.md#input_buffer +[47]: message.md#pid +[48]: message.md#session_path +[49]: message.md#explorer_config +[50]: message.md#directory_buffer diff --git a/docs/en/src/message.md b/docs/en/src/message.md index 8281ec9..7a0759d 100644 --- a/docs/en/src/message.md +++ b/docs/en/src/message.md @@ -445,8 +445,8 @@ stderr will be piped to null. So it's non-interactive. **YAML:** `CallLua: string` Call a Lua function. -A [`CallLuaArg`][14] object will be passed to the -[function][3] as argument. + +A [Lua Context][14] object will be passed to the [function][3] as argument. The function can optionally return a list of messages for xplr to handle after the executing the function. @@ -469,8 +469,8 @@ stderr will be piped to null. So it's non-interactive. **YAML:** `LuaEval: string` -Execute Lua code without needing to define a function. -However, `CallLuaArg` won't be available. +Execute Lua code without needing to define a function. However, +[Lua Context][14] won't be available. **YAML Example:** `LuaEval: "return { { LogInfo = io.read() } }"` @@ -856,7 +856,7 @@ When called the function receives a [special argument][14] that contains some useful information. The function can optionally return a list of messages which will be handled by xplr. -### CallLua Argument +### Lua Context This is a special argument passed to the lua functions when called using the `CallLua`, `CallLuaSilently` messages. @@ -864,7 +864,6 @@ This is a special argument passed to the lua functions when called using the It contains the following information: - [version][29] -- [config][30] - [pwd][31] - [focused_node][32] - [directory_buffer][33] @@ -884,12 +883,6 @@ Type: string xplr version. Can be used to test compatibility. -### config - -Type: [Config][43] - -The loaded configuration. - ### pwd Type: string @@ -904,7 +897,7 @@ The node under focus. ### directory_buffer -Type: nullable [DirectoryBuffer][62] +Type: nullable [Directory Buffer][62] The directory buffer being rendered. @@ -1073,7 +1066,7 @@ A node contains the following fields: - [canonical][58] - [symlink][59] -### DirectoryBuffer +### Directory Buffer Directory buffer contains the following fields: @@ -1285,11 +1278,10 @@ xplr.config.modes.builtin.default.key_bindings.on_key.space = { [11]: layouts.md#layout [12]: layouts.md#builtin [13]: layouts.md#custom -[14]: #calllua-argument +[14]: #lua-context [15]: filtering.md#filter [16]: filtering.md [17]: sorting.md#sorter -[18]: https://docs.rs/xplr/latest/xplr/app/struct.CallLuaArg.html#fields [19]: configure-key-bindings.md#tutorial-adding-a-new-mode [20]: #xplr_pipe_msg_in [21]: #xplr_pipe_selection_out @@ -1333,7 +1325,7 @@ xplr.config.modes.builtin.default.key_bindings.on_key.space = { [59]: #symlink [60]: column-renderer.md#permission [61]: column-renderer.md#resolved-node-metadata -[62]: #directorybuffer +[62]: #directory-buffer [63]: #nodes [64]: #total [65]: #focus diff --git a/src/__cache__.lua b/src/__cache__.lua deleted file mode 100644 index f4e2556..0000000 --- a/src/__cache__.lua +++ /dev/null @@ -1,25 +0,0 @@ --- Nothing to see here. Please move on. --- Or if you insist, see https://github.com/sayanarijit/xplr/issues/412 - -local xplr = xplr - -xplr.__CACHE__ = { directory_nodes = {} } - -function xplr.__CACHE__.set_directory_nodes(nodes) - xplr.__CACHE__.directory_nodes = nodes -end - -function xplr.__CACHE__.call(fun, arg) - if arg.app and arg.app.directory_buffer then - arg.app.directory_buffer.nodes = xplr.__CACHE__.directory_nodes - elseif arg.directory_buffer then - arg.directory_buffer.nodes = xplr.__CACHE__.directory_nodes - end - return fun(arg) -end - -function xplr.__CACHE__.caller(fun) - return function(arg) - return xplr.__CACHE__.call(fun, arg) - end -end diff --git a/src/app.rs b/src/app.rs index c1cd404..ab01c5d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1133,7 +1133,6 @@ pub struct Command { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum MsgOut { - CacheDirectoryNodes(Vec), ExplorePwdAsync, ExploreParentsAsync, Refresh, @@ -1247,29 +1246,12 @@ impl History { } } -#[derive(Debug, Clone, PartialEq, Serialize)] -pub struct CachedDirectoryBuffer { - pub parent: String, - pub total: usize, - pub focus: usize, -} - -impl CachedDirectoryBuffer { - pub fn new(buf: &DirectoryBuffer) -> Self { - Self { - parent: buf.parent.clone(), - total: buf.total, - focus: buf.focus, - } - } -} - #[derive(Debug, Clone, Serialize)] -pub struct CallLuaArg { +pub struct LuaContextHeavy { pub version: String, pub pwd: String, pub focused_node: Option, - pub directory_buffer: Option, + pub directory_buffer: Option, pub selection: IndexSet, pub mode: Mode, pub layout: Layout, @@ -1281,6 +1263,20 @@ pub struct CallLuaArg { pub last_modes: Vec, } +#[derive(Debug, Clone, Serialize)] +pub struct LuaContextLight { + pub version: String, + pub pwd: String, + pub focused_node: Option, + pub selection: IndexSet, + pub mode: Mode, + pub layout: Layout, + pub input_buffer: Option, + pub pid: u32, + pub session_path: String, + pub explorer_config: ExplorerConfig, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct App { pub version: String, @@ -2280,8 +2276,6 @@ impl App { dir.focused_node().map(|n| n.relative_path.clone()), )?; if dir.parent == self.pwd { - self.msg_out - .push_back(MsgOut::CacheDirectoryNodes(dir.nodes.clone())); self.directory_buffer = Some(dir); } Ok(self) @@ -2772,15 +2766,12 @@ impl App { Ok(()) } - pub fn to_lua_arg(&self) -> CallLuaArg { - CallLuaArg { + pub fn to_lua_ctx_heavy(&self) -> LuaContextHeavy { + LuaContextHeavy { version: self.version.clone(), pwd: self.pwd.clone(), focused_node: self.focused_node().cloned(), - directory_buffer: self - .directory_buffer - .as_ref() - .map(|buf| CachedDirectoryBuffer::new(buf)), + directory_buffer: self.directory_buffer.clone(), selection: self.selection.clone(), mode: self.mode.clone(), layout: self.layout.clone(), @@ -2792,4 +2783,19 @@ impl App { last_modes: self.last_modes.clone(), } } + + pub fn to_lua_ctx_light(&self) -> LuaContextLight { + LuaContextLight { + version: self.version.clone(), + pwd: self.pwd.clone(), + focused_node: self.focused_node().cloned(), + selection: self.selection.clone(), + mode: self.mode.clone(), + layout: self.layout.clone(), + input_buffer: self.input.as_ref().map(|i| i.value().into()), + pid: self.pid, + session_path: self.session_path.clone(), + explorer_config: self.explorer_config.clone(), + } + } } diff --git a/src/lua.rs b/src/lua.rs index 7a445a1..3b0d930 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -1,4 +1,3 @@ -use crate::app::Node; use crate::app::VERSION; use crate::config::Config; use anyhow::bail; @@ -12,7 +11,6 @@ use serde::Serialize; use std::fs; const DEFAULT_LUA_SCRIPT: &str = include_str!("init.lua"); -const CACHE_LUA_SCRIPT: &str = include_str!("__cache__.lua"); const UPGRADE_GUIDE_LINK: &str = "https://xplr.dev/en/upgrade-guide.html"; pub fn serialize<'lua, T: Serialize + Sized>( @@ -83,7 +81,6 @@ pub fn init(lua: &Lua) -> Result { globals.set("xplr", lua_xplr)?; lua.load(DEFAULT_LUA_SCRIPT).set_name("init")?.exec()?; - lua.load(CACHE_LUA_SCRIPT).set_name("internal")?.exec()?; let lua_xplr: mlua::Table = globals.get("xplr")?; let config: Config = lua.from_value(lua_xplr.get("config")?)?; @@ -147,29 +144,6 @@ pub fn call<'lua, R: Deserialize<'lua>>( Ok(res) } -/// Used to call lua functions with cache support. -pub fn call_with_cache<'lua, R: Deserialize<'lua>>( - lua: &'lua Lua, - func: &str, - arg: mlua::Value<'lua>, -) -> Result { - let caller: mlua::Function = - resolve_fn(&lua.globals(), "xplr.__CACHE__.call")?; - let func = format!("xplr.fn.{}", func); - let func: mlua::Function = resolve_fn(&lua.globals(), &func)?; - let res: mlua::Value = caller.call((func, arg))?; - let res: R = lua.from_value(res)?; - Ok(res) -} - -/// Used to cache the directory nodes. -pub fn cache_directory_nodes(lua: &Lua, nodes: &[Node]) -> Result<()> { - let func = "xplr.__CACHE__.set_directory_nodes"; - let func: mlua::Function = resolve_fn(&lua.globals(), func)?; - func.call(serialize(lua, &nodes)?)?; - Ok(()) -} - #[cfg(test)] mod tests { diff --git a/src/runner.rs b/src/runner.rs index d770b2d..c395da6 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -32,15 +32,15 @@ pub fn get_tty() -> Result { } } -fn call_lua( +fn call_lua_heavy( app: &app::App, lua: &mlua::Lua, func: &str, _silent: bool, ) -> Result>> { - let arg = app.to_lua_arg(); + let arg = app.to_lua_ctx_heavy(); let arg = lua::serialize(lua, &arg)?; - lua::call_with_cache(lua, func, arg) + lua::call(lua, func, arg) } fn call(app: &app::App, cmd: app::Command, silent: bool) -> Result { @@ -273,10 +273,6 @@ impl Runner { tx_msg_in.send(task)?; } - CacheDirectoryNodes(nodes) => { - lua::cache_directory_nodes(&lua, &nodes)?; - } - Quit => { result = Ok(None); break 'outer; @@ -455,7 +451,7 @@ impl Runner { } CallLuaSilently(func) => { - match call_lua(&app, &lua, &func, false) { + match call_lua_heavy(&app, &lua, &func, false) { Ok(Some(msgs)) => { app = app .handle_batch_external_msgs(msgs)?; @@ -513,7 +509,7 @@ impl Runner { term::disable_raw_mode()?; terminal.show_cursor()?; - match call_lua(&app, &lua, &func, false) { + match call_lua_heavy(&app, &lua, &func, false) { Ok(Some(msgs)) => { app = app .handle_batch_external_msgs(msgs)?; diff --git a/src/ui.rs b/src/ui.rs index 30b0b13..53ef9f9 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1032,14 +1032,14 @@ pub fn draw_custom_content( ContentBody::DynamicParagraph { render } => { let ctx = ContentRendererArg { - app: app.to_lua_arg(), + app: app.to_lua_ctx_light(), layout_size: layout_size.into(), screen_size: screen_size.into(), }; let render = lua::serialize(lua, &ctx) .map(|arg| { - lua::call_with_cache(lua, &render, arg) + lua::call(lua, &render, arg) .unwrap_or_else(|e| format!("{:?}", e)) }) .unwrap_or_else(|e| e.to_string()); @@ -1073,14 +1073,14 @@ pub fn draw_custom_content( ContentBody::DynamicList { render } => { let ctx = ContentRendererArg { - app: app.to_lua_arg(), + app: app.to_lua_ctx_light(), layout_size: layout_size.into(), screen_size: screen_size.into(), }; let items = lua::serialize(lua, &ctx) .map(|arg| { - lua::call_with_cache(lua, &render, arg) + lua::call(lua, &render, arg) .unwrap_or_else(|e| vec![format!("{:?}", e)]) }) .unwrap_or_else(|e| vec![e.to_string()]) @@ -1142,14 +1142,14 @@ pub fn draw_custom_content( render, } => { let ctx = ContentRendererArg { - app: app.to_lua_arg(), + app: app.to_lua_ctx_light(), layout_size: layout_size.into(), screen_size: screen_size.into(), }; let rows = lua::serialize(lua, &ctx) .map(|arg| { - lua::call_with_cache(lua, &render, arg) + lua::call(lua, &render, arg) .unwrap_or_else(|e| vec![vec![format!("{:?}", e)]]) }) .unwrap_or_else(|e| vec![vec![e.to_string()]]) @@ -1208,7 +1208,7 @@ impl From for Rect { #[derive(Debug, Clone, Serialize)] pub struct ContentRendererArg { - app: app::CallLuaArg, + app: app::LuaContextLight, screen_size: Rect, layout_size: Rect, }