diff --git a/docs/en/src/configuration.md b/docs/en/src/configuration.md index 8a64f11..0be0ac4 100644 --- a/docs/en/src/configuration.md +++ b/docs/en/src/configuration.md @@ -80,21 +80,40 @@ You can also use nested tables such as ## Hooks This section of the configuration cannot be overwritten by another config -file, as it is an optional lua return statement specific to each config -file. It can be used to define things that are append only, such as various -hooks and callbacks. +file or plugin, since this is an optional lua return statement specific to +each config file. It can be used to define things that should be explicit +for reasons like performance concerns, such as hooks. + +Plugins should expose the hooks, and require users to subscribe to them +explicitly. Example: ```lua return { - -- Messages to send when the config file loads. + -- Add messages to send when the xplr loads. -- This is similar to the `--on-load` command-line option. -- -- Type: list of [Message](https://xplr.dev/en/message#message)s on_load = { - { LogInfo = "Hello xplr user," }, { LogSuccess = "Configuration successfully loaded!" }, + { CallLuaSilently = "custom.some_plugin_with_hooks.on_load" }, + }, + + -- Add messages to send when the directory changes. + -- + -- Type: list of [Message](https://xplr.dev/en/message#message)s + on_directory_change = { + { LogSuccess = "Changed directory" }, + { CallLuaSilently = "custom.some_plugin_with_hooks.on_directory_change" }, + }, + + -- Add messages to send when the focus changes. + -- + -- Type: list of [Message](https://xplr.dev/en/message#message)s + on_focus_change = { + { LogSuccess = "Changed focus" }, + { CallLuaSilently = "custom.some_plugin_with_hooks.on_focus_change" }, } } ``` diff --git a/src/app.rs b/src/app.rs index ca59f82..fdec0a7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -165,6 +165,7 @@ pub struct App { pub bin: String, pub version: String, pub config: Config, + pub hooks: Hooks, pub pwd: String, pub directory_buffer: Option, pub last_focus: HashMap>, @@ -191,8 +192,9 @@ impl App { lua: &mlua::Lua, config_file: Option, extra_config_files: Vec, - ) -> Result<(Self, Option)> { - let (mut config, mut hooks) = lua::init(lua)?; + ) -> Result { + let (mut config, hooks) = lua::init(lua)?; + let mut hooks = hooks.unwrap_or_default(); let config_file = if let Some(path) = config_file { Some(path) @@ -221,10 +223,8 @@ impl App { match lua::extend(lua, &config_file.to_string_lossy()) { Ok((c, maybe_hooks)) => { config = c; - match (hooks.as_mut(), maybe_hooks) { - (Some(h1), Some(h2)) => h1.on_load.extend(h2.on_load), - (None, Some(h2)) => hooks = Some(h2), - (_, _) => {} + if let Some(h) = maybe_hooks { + hooks = hooks.extend(h); } } Err(e) => { @@ -323,6 +323,7 @@ impl App { history: Default::default(), last_modes: Default::default(), hostname, + hooks, }; let has_errs = !load_errs.is_empty(); @@ -334,7 +335,7 @@ impl App { app = app.switch_mode_builtin("debug_error")?; } - Ok((app, hooks)) + Ok(app) } pub fn focused_node(&self) -> Option<&Node> { diff --git a/src/config.rs b/src/config.rs index 7d92f3c..a4b4b9f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -618,4 +618,22 @@ pub struct Config { pub struct Hooks { #[serde(default)] pub on_load: Vec, + + #[serde(default)] + pub on_directory_change: Vec, + + #[serde(default)] + pub on_focus_change: Vec, + // TODO After cleanup or Runner::run + // #[serde(default)] + // pub before_quit: Vec, +} + +impl Hooks { + pub fn extend(mut self, other: Self) -> Self { + self.on_load.extend(other.on_load); + self.on_directory_change.extend(other.on_directory_change); + self.on_focus_change.extend(other.on_focus_change); + self + } } diff --git a/src/init.lua b/src/init.lua index 3fe339e..44b5479 100644 --- a/src/init.lua +++ b/src/init.lua @@ -2567,9 +2567,12 @@ xplr.fn.custom = {} -- ## Hooks ------------------------------------------------------------------- -- -- This section of the configuration cannot be overwritten by another config --- file, as it is an optional lua return statement specific to each config --- file. It can be used to define things that are append only, such as various --- hooks and callbacks. +-- file or plugin, since this is an optional lua return statement specific to +-- each config file. It can be used to define things that should be explicit +-- for reasons like performance concerns, such as hooks. +-- +-- Plugins should expose the hooks, and require users to subscribe to them +-- explicitly. -- -- Example: -- @@ -2580,12 +2583,30 @@ xplr.fn.custom = {} -- -- -- -- Type: list of [Message](https://xplr.dev/en/message#message)s -- on_load = { --- { LogInfo = "Hello xplr user," }, -- { LogSuccess = "Configuration successfully loaded!" }, +-- { CallLuaSilently = "custom.some_plugin_with_hooks.on_load" }, +-- }, +-- +-- -- Add messages to send when the directory changes. +-- -- +-- -- Type: list of [Message](https://xplr.dev/en/message#message)s +-- on_directory_change = { +-- { LogSuccess = "Changed directory" }, +-- { CallLuaSilently = "custom.some_plugin_with_hooks.on_directory_change" }, +-- }, +-- +-- -- Add messages to send when the focus changes. +-- -- +-- -- Type: list of [Message](https://xplr.dev/en/message#message)s +-- on_focus_change = { +-- { LogSuccess = "Changed focus" }, +-- { CallLuaSilently = "custom.some_plugin_with_hooks.on_focus_change" }, -- } -- } -- ``` return { on_load = {}, + on_directory_change = {}, + on_focus_change = {}, } diff --git a/src/runner.rs b/src/runner.rs index 7382a72..0756f5d 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -245,7 +245,7 @@ impl Runner { pub fn run(self) -> Result> { // Why unsafe? See https://github.com/sayanarijit/xplr/issues/309 let lua = unsafe { mlua::Lua::unsafe_new() }; - let (mut app, hooks) = app::App::create( + let mut app = app::App::create( self.bin, self.pwd, &lua, @@ -329,13 +329,8 @@ impl Runner { event_reader.start(); // Enqueue on_load messages - for msg in hooks - .unwrap_or_default() - .on_load - .into_iter() - .chain(self.on_load) - { - tx_msg_in.send(app::Task::new(app::MsgIn::External(msg), None))?; + for msg in app.hooks.on_load.iter().chain(self.on_load.iter()) { + tx_msg_in.send(app::Task::new(app::MsgIn::External(msg.clone()), None))?; } // Refresh once after loading @@ -460,16 +455,25 @@ impl Runner { } Refresh => { - // Fifo let focus = app.focused_node(); if focus != last_focus.as_ref() { + last_focus = focus.cloned(); + + // Fifo if let Some(ref mut file) = fifo { writeln!(file, "{}", app.focused_node_str())?; }; - last_focus = focus.cloned(); + + // Hooks + if !app.hooks.on_focus_change.is_empty() { + let msgs = app.hooks.on_focus_change.clone(); + app = app.handle_batch_external_msgs(msgs)? + } } if app.pwd != last_pwd { + last_pwd = app.pwd.clone(); + // $PWD watcher tx_pwd_watcher.send(app.pwd.clone())?; @@ -483,7 +487,11 @@ impl Runner { )?; } - last_pwd = app.pwd.clone(); + // Hooks + if !app.hooks.on_directory_change.is_empty() { + let msgs = app.hooks.on_directory_change.clone(); + app = app.handle_batch_external_msgs(msgs)? + } } // UI