diff --git a/rmenu-plugin/Cargo.toml b/rmenu-plugin/Cargo.toml index fce0de4..aca54e7 100644 --- a/rmenu-plugin/Cargo.toml +++ b/rmenu-plugin/Cargo.toml @@ -14,6 +14,7 @@ name = "rmenu-build" path = "src/bin/main.rs" [dependencies] +bincode = "1.3.3" clap = { version = "4.3.22", features = ["derive"] } serde = { version = "1.0.171", features = ["derive"] } serde_json = "1.0.105" diff --git a/rmenu-plugin/src/bin/main.rs b/rmenu-plugin/src/bin/main.rs index e0ad5ab..ace6d87 100644 --- a/rmenu-plugin/src/bin/main.rs +++ b/rmenu-plugin/src/bin/main.rs @@ -155,6 +155,12 @@ struct OptionArgs { /// Override Close-Menu Keybinds #[arg(short = 'c', long)] pub key_close_menu: Option>, + /// Override Jump-Next Keybinds + #[arg(short = 'j', long)] + pub key_jump_next: Option>, + /// Override Jump-Previous Keybinds + #[arg(short = 'J', long)] + pub key_jump_prev: Option>, // window settings /// Override Window Title #[arg(short, long)] @@ -187,6 +193,8 @@ impl Into for OptionArgs { key_move_prev: self.key_move_prev, key_open_menu: self.key_open_menu, key_close_menu: self.key_close_menu, + key_jump_next: self.key_jump_next, + key_jump_prev: self.key_jump_prev, title: self.title, decorate: self.deocorate, fullscreen: self.fullscreen, diff --git a/rmenu-plugin/src/lib.rs b/rmenu-plugin/src/lib.rs index bb07f1b..17cd7e0 100644 --- a/rmenu-plugin/src/lib.rs +++ b/rmenu-plugin/src/lib.rs @@ -110,6 +110,10 @@ pub struct Options { pub key_open_menu: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub key_close_menu: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub key_jump_next: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub key_jump_prev: Option>, // window settings #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, diff --git a/rmenu/Cargo.toml b/rmenu/Cargo.toml index 19a6f5c..28c3e48 100644 --- a/rmenu/Cargo.toml +++ b/rmenu/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bincode = "1.3.3" cached = "0.44.0" clap = { version = "4.3.15", features = ["derive"] } dioxus = "0.4.0" diff --git a/rmenu/src/cache.rs b/rmenu/src/cache.rs index f9522e4..396fed3 100644 --- a/rmenu/src/cache.rs +++ b/rmenu/src/cache.rs @@ -1,6 +1,5 @@ //! RMenu Plugin Result Cache use std::fs; -use std::io::Write; use std::path::PathBuf; use std::time::{Duration, SystemTime}; @@ -25,7 +24,7 @@ pub enum CacheError { #[error("Cache File Error")] FileError(#[from] std::io::Error), #[error("Encoding Error")] - EncodingError(#[from] bincode::Error), + EncodingError(#[from] serde_json::Error), } #[inline] @@ -69,7 +68,7 @@ pub fn read_cache(name: &str, cfg: &PluginConfig) -> Result, CacheErr } // attempt to read content let data = fs::read(path)?; - let results: Vec = bincode::deserialize(&data)?; + let results: Vec = serde_json::from_slice(&data)?; Ok(results) } @@ -79,10 +78,10 @@ pub fn write_cache(name: &str, cfg: &PluginConfig, entries: &Vec) -> Resu match cfg.cache { CacheSetting::NoCache => {} _ => { + println!("writing {} entries", entries.len()); let path = cache_file(name); - let data = bincode::serialize(entries)?; - let mut f = fs::File::create(path)?; - f.write_all(&data)?; + let f = fs::File::create(path)?; + serde_json::to_writer(f, entries)?; } } Ok(()) diff --git a/rmenu/src/cli.rs b/rmenu/src/cli.rs index 6187284..3916e83 100644 --- a/rmenu/src/cli.rs +++ b/rmenu/src/cli.rs @@ -117,6 +117,12 @@ pub struct Args { /// Override close-menu keybind #[arg(long)] key_close_menu: Option>, + /// Override jump-next keybind + #[arg(long)] + key_jump_next: Option>, + /// Override jump-previous keybind + #[arg(long)] + key_jump_prev: Option>, //window settings /// Override Window Title @@ -240,6 +246,8 @@ impl Args { cli_replace!(config.keybinds.move_prev, self.key_move_prev, true); cli_replace!(config.keybinds.open_menu, self.key_open_menu, true); cli_replace!(config.keybinds.close_menu, self.key_close_menu, true); + cli_replace!(config.keybinds.jump_next, self.key_jump_next, true); + cli_replace!(config.keybinds.jump_prev, self.key_jump_prev, true); // override window settings cli_replace!(config.window.title, self.title, true); cli_replace!(config.window.size.width, self.width, true); @@ -307,6 +315,8 @@ impl Args { cli_keybind!(c.keybinds.move_prev, options.key_move_prev); cli_keybind!(c.keybinds.open_menu, options.key_open_menu); cli_keybind!(c.keybinds.close_menu, options.key_close_menu); + cli_keybind!(c.keybinds.jump_next, options.key_jump_next); + cli_keybind!(c.keybinds.jump_prev, options.key_jump_prev); // window settings cli_replace!(c.window.title, options.title, true); cli_replace!(c.window.decorate, options.decorate, true); @@ -362,7 +372,7 @@ impl Args { let main = args .get(0) .ok_or_else(|| RMenuError::InvalidPlugin(name.to_owned()))?; - // spawn command and handle command entries + // spawn command let mut command = Command::new(main) .args(&args[1..]) .stdout(Stdio::piped()) @@ -371,8 +381,10 @@ impl Args { .stdout .as_mut() .ok_or_else(|| RMenuError::CommandError(None))?; + // parse and read entries into vector of results let reader = BufReader::new(stdout); - self.read_entries(reader, &mut entries, config)?; + let mut entry = vec![]; + self.read_entries(reader, &mut entry, config)?; let status = command.wait()?; if !status.success() { return Err(RMenuError::CommandError(Some(status))); @@ -381,10 +393,12 @@ impl Args { if config.search.placeholder.is_none() { config.search.placeholder = plugin.placeholder.clone(); } - match crate::cache::write_cache(&name, &plugin, &entries) { + match crate::cache::write_cache(&name, &plugin, &entry) { Ok(_) => {} Err(err) => log::error!("cache write error: {err:?}"), } + // write collected entries to main output + entries.append(&mut entry); } Ok(entries) } diff --git a/rmenu/src/config.rs b/rmenu/src/config.rs index 1f69263..b1b197a 100644 --- a/rmenu/src/config.rs +++ b/rmenu/src/config.rs @@ -87,6 +87,8 @@ pub struct KeyConfig { pub move_prev: Vec, pub open_menu: Vec, pub close_menu: Vec, + pub jump_next: Vec, + pub jump_prev: Vec, } impl Default for KeyConfig { @@ -98,6 +100,8 @@ impl Default for KeyConfig { move_prev: vec![Keybind::new(Code::ArrowDown)], open_menu: vec![], close_menu: vec![], + jump_next: vec![Keybind::new(Code::PageDown)], + jump_prev: vec![Keybind::new(Code::PageUp)], }; } } @@ -236,6 +240,7 @@ impl Default for SearchConfig { pub struct Config { pub page_size: usize, pub page_load: f64, + pub jump_dist: usize, #[serde(default = "_true")] pub use_icons: bool, #[serde(default = "_true")] @@ -252,6 +257,7 @@ impl Default for Config { Self { page_size: 50, page_load: 0.8, + jump_dist: 5, use_icons: true, use_comments: true, search: Default::default(), diff --git a/rmenu/src/gui.rs b/rmenu/src/gui.rs index 0461f07..a8fcbac 100644 --- a/rmenu/src/gui.rs +++ b/rmenu/src/gui.rs @@ -205,14 +205,18 @@ fn App<'a>(cx: Scope) -> Element { k_updater.set_event(KeyEvent::Exec); } else if matches(&keybinds.exit, &mods, &code) { k_updater.set_event(KeyEvent::Exit); - } else if matches(&keybinds.move_prev, &mods, &code) { - k_updater.set_event(KeyEvent::ShiftUp); } else if matches(&keybinds.move_next, &mods, &code) { - k_updater.set_event(KeyEvent::ShiftDown); + k_updater.set_event(KeyEvent::MoveNext); + } else if matches(&keybinds.move_prev, &mods, &code) { + k_updater.set_event(KeyEvent::MovePrev); } else if matches(&keybinds.open_menu, &mods, &code) { k_updater.set_event(KeyEvent::OpenMenu); } else if matches(&keybinds.close_menu, &mods, &code) { k_updater.set_event(KeyEvent::CloseMenu); + } else if matches(&keybinds.jump_next, &mods, &code) { + k_updater.set_event(KeyEvent::JumpNext) + } else if matches(&keybinds.jump_prev, &mods, &code) { + k_updater.set_event(KeyEvent::JumpPrev) } }; diff --git a/rmenu/src/state.rs b/rmenu/src/state.rs index 43d0ae1..2faa4f2 100644 --- a/rmenu/src/state.rs +++ b/rmenu/src/state.rs @@ -18,10 +18,12 @@ fn scroll(cx: Scope, pos: usize) { pub enum KeyEvent { Exec, Exit, - ShiftUp, - ShiftDown, + MovePrev, + MoveNext, OpenMenu, CloseMenu, + JumpNext, + JumpPrev, } pub struct InnerState { @@ -46,8 +48,20 @@ impl InnerState { self.pos = std::cmp::min(self.pos + x, max - 1) } + /// Jump a spefified number of results upwards + #[inline] + pub fn jump_up(&mut self, jump: usize) { + self.move_up(jump) + } + + /// Jump a specified number of results downwards + pub fn jump_down(&mut self, jump: usize, results: &Vec<&Entry>) { + let max = std::cmp::max(results.len(), 1); + self.move_down(jump, max); + } + /// Move Up Once With Context of SubMenu - pub fn shift_up(&mut self) { + pub fn move_prev(&mut self) { if self.subpos > 0 { self.subpos -= 1; return; @@ -56,15 +70,14 @@ impl InnerState { } /// Move Down Once With Context of SubMenu - pub fn shift_down(&mut self, results: &Vec<&Entry>) { + pub fn move_next(&mut self, results: &Vec<&Entry>) { if let Some(result) = results.get(self.pos) { if self.subpos > 0 && self.subpos < result.actions.len() - 1 { self.subpos += 1; return; } } - let max = std::cmp::max(results.len(), 1); - self.move_down(1, max); + self.jump_down(1, results) } } @@ -164,13 +177,22 @@ impl<'a> AppState<'a> { KeyEvent::Exec => self.execute(), KeyEvent::OpenMenu => self.open_menu(), KeyEvent::CloseMenu => self.close_menu(), - KeyEvent::ShiftUp => { - self.shift_up(); + KeyEvent::MovePrev => { + self.move_prev(); let pos = self.position().0; scroll(cx, if pos <= 3 { pos } else { pos + 3 }) } - KeyEvent::ShiftDown => { - self.shift_down(); + KeyEvent::MoveNext => { + self.move_next(); + scroll(cx, self.position().0 + 3) + } + KeyEvent::JumpPrev => { + self.jump_prev(); + let pos = self.position().0; + scroll(cx, if pos <= 3 { pos } else { pos + 3 }) + } + KeyEvent::JumpNext => { + self.jump_next(); scroll(cx, self.position().0 + 3) } }; @@ -265,13 +287,28 @@ impl<'a> AppState<'a> { /// Move Up Once With Context of SubMenu #[inline] - pub fn shift_up(&self) { - self.state.with_mut(|s| s.shift_up()); + pub fn move_prev(&self) { + self.state.with_mut(|s| s.move_prev()); } /// Move Down Once With Context of SubMenu #[inline] - pub fn shift_down(&self) { - self.state.with_mut(|s| s.shift_down(&self.results)) + pub fn move_next(&self) { + self.state.with_mut(|s| s.move_next(&self.results)) + } + + /// Jump a Configured Distance Up the Results + #[inline] + pub fn jump_prev(&self) { + let distance = self.app.config.jump_dist; + self.state.with_mut(|s| s.jump_up(distance)) + } + + /// Jump a Configured Distance Down the Results + #[inline] + pub fn jump_next(&self) { + let distance = self.app.config.jump_dist; + self.state + .with_mut(|s| s.jump_down(distance, &self.results)) } }