feat: added jump next/prev keybinds, bincode does not support tagged items

This commit is contained in:
imgurbot12 2023-08-20 12:04:47 -07:00
parent 59e8cf6c22
commit 3f7baff1fb
9 changed files with 99 additions and 27 deletions

View File

@ -14,6 +14,7 @@ name = "rmenu-build"
path = "src/bin/main.rs" path = "src/bin/main.rs"
[dependencies] [dependencies]
bincode = "1.3.3"
clap = { version = "4.3.22", features = ["derive"] } clap = { version = "4.3.22", features = ["derive"] }
serde = { version = "1.0.171", features = ["derive"] } serde = { version = "1.0.171", features = ["derive"] }
serde_json = "1.0.105" serde_json = "1.0.105"

View File

@ -155,6 +155,12 @@ struct OptionArgs {
/// Override Close-Menu Keybinds /// Override Close-Menu Keybinds
#[arg(short = 'c', long)] #[arg(short = 'c', long)]
pub key_close_menu: Option<Vec<String>>, pub key_close_menu: Option<Vec<String>>,
/// Override Jump-Next Keybinds
#[arg(short = 'j', long)]
pub key_jump_next: Option<Vec<String>>,
/// Override Jump-Previous Keybinds
#[arg(short = 'J', long)]
pub key_jump_prev: Option<Vec<String>>,
// window settings // window settings
/// Override Window Title /// Override Window Title
#[arg(short, long)] #[arg(short, long)]
@ -187,6 +193,8 @@ impl Into<Options> for OptionArgs {
key_move_prev: self.key_move_prev, key_move_prev: self.key_move_prev,
key_open_menu: self.key_open_menu, key_open_menu: self.key_open_menu,
key_close_menu: self.key_close_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, title: self.title,
decorate: self.deocorate, decorate: self.deocorate,
fullscreen: self.fullscreen, fullscreen: self.fullscreen,

View File

@ -110,6 +110,10 @@ pub struct Options {
pub key_open_menu: Option<Vec<String>>, pub key_open_menu: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub key_close_menu: Option<Vec<String>>, pub key_close_menu: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub key_jump_next: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub key_jump_prev: Option<Vec<String>>,
// window settings // window settings
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>, pub title: Option<String>,

View File

@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
bincode = "1.3.3"
cached = "0.44.0" cached = "0.44.0"
clap = { version = "4.3.15", features = ["derive"] } clap = { version = "4.3.15", features = ["derive"] }
dioxus = "0.4.0" dioxus = "0.4.0"

View File

@ -1,6 +1,5 @@
//! RMenu Plugin Result Cache //! RMenu Plugin Result Cache
use std::fs; use std::fs;
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@ -25,7 +24,7 @@ pub enum CacheError {
#[error("Cache File Error")] #[error("Cache File Error")]
FileError(#[from] std::io::Error), FileError(#[from] std::io::Error),
#[error("Encoding Error")] #[error("Encoding Error")]
EncodingError(#[from] bincode::Error), EncodingError(#[from] serde_json::Error),
} }
#[inline] #[inline]
@ -69,7 +68,7 @@ pub fn read_cache(name: &str, cfg: &PluginConfig) -> Result<Vec<Entry>, CacheErr
} }
// attempt to read content // attempt to read content
let data = fs::read(path)?; let data = fs::read(path)?;
let results: Vec<Entry> = bincode::deserialize(&data)?; let results: Vec<Entry> = serde_json::from_slice(&data)?;
Ok(results) Ok(results)
} }
@ -79,10 +78,10 @@ pub fn write_cache(name: &str, cfg: &PluginConfig, entries: &Vec<Entry>) -> Resu
match cfg.cache { match cfg.cache {
CacheSetting::NoCache => {} CacheSetting::NoCache => {}
_ => { _ => {
println!("writing {} entries", entries.len());
let path = cache_file(name); let path = cache_file(name);
let data = bincode::serialize(entries)?; let f = fs::File::create(path)?;
let mut f = fs::File::create(path)?; serde_json::to_writer(f, entries)?;
f.write_all(&data)?;
} }
} }
Ok(()) Ok(())

View File

@ -117,6 +117,12 @@ pub struct Args {
/// Override close-menu keybind /// Override close-menu keybind
#[arg(long)] #[arg(long)]
key_close_menu: Option<Vec<Keybind>>, key_close_menu: Option<Vec<Keybind>>,
/// Override jump-next keybind
#[arg(long)]
key_jump_next: Option<Vec<Keybind>>,
/// Override jump-previous keybind
#[arg(long)]
key_jump_prev: Option<Vec<Keybind>>,
//window settings //window settings
/// Override Window Title /// Override Window Title
@ -240,6 +246,8 @@ impl Args {
cli_replace!(config.keybinds.move_prev, self.key_move_prev, true); 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.open_menu, self.key_open_menu, true);
cli_replace!(config.keybinds.close_menu, self.key_close_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 // override window settings
cli_replace!(config.window.title, self.title, true); cli_replace!(config.window.title, self.title, true);
cli_replace!(config.window.size.width, self.width, 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.move_prev, options.key_move_prev);
cli_keybind!(c.keybinds.open_menu, options.key_open_menu); cli_keybind!(c.keybinds.open_menu, options.key_open_menu);
cli_keybind!(c.keybinds.close_menu, options.key_close_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 // window settings
cli_replace!(c.window.title, options.title, true); cli_replace!(c.window.title, options.title, true);
cli_replace!(c.window.decorate, options.decorate, true); cli_replace!(c.window.decorate, options.decorate, true);
@ -362,7 +372,7 @@ impl Args {
let main = args let main = args
.get(0) .get(0)
.ok_or_else(|| RMenuError::InvalidPlugin(name.to_owned()))?; .ok_or_else(|| RMenuError::InvalidPlugin(name.to_owned()))?;
// spawn command and handle command entries // spawn command
let mut command = Command::new(main) let mut command = Command::new(main)
.args(&args[1..]) .args(&args[1..])
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@ -371,8 +381,10 @@ impl Args {
.stdout .stdout
.as_mut() .as_mut()
.ok_or_else(|| RMenuError::CommandError(None))?; .ok_or_else(|| RMenuError::CommandError(None))?;
// parse and read entries into vector of results
let reader = BufReader::new(stdout); 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()?; let status = command.wait()?;
if !status.success() { if !status.success() {
return Err(RMenuError::CommandError(Some(status))); return Err(RMenuError::CommandError(Some(status)));
@ -381,10 +393,12 @@ impl Args {
if config.search.placeholder.is_none() { if config.search.placeholder.is_none() {
config.search.placeholder = plugin.placeholder.clone(); config.search.placeholder = plugin.placeholder.clone();
} }
match crate::cache::write_cache(&name, &plugin, &entries) { match crate::cache::write_cache(&name, &plugin, &entry) {
Ok(_) => {} Ok(_) => {}
Err(err) => log::error!("cache write error: {err:?}"), Err(err) => log::error!("cache write error: {err:?}"),
} }
// write collected entries to main output
entries.append(&mut entry);
} }
Ok(entries) Ok(entries)
} }

View File

@ -87,6 +87,8 @@ pub struct KeyConfig {
pub move_prev: Vec<Keybind>, pub move_prev: Vec<Keybind>,
pub open_menu: Vec<Keybind>, pub open_menu: Vec<Keybind>,
pub close_menu: Vec<Keybind>, pub close_menu: Vec<Keybind>,
pub jump_next: Vec<Keybind>,
pub jump_prev: Vec<Keybind>,
} }
impl Default for KeyConfig { impl Default for KeyConfig {
@ -98,6 +100,8 @@ impl Default for KeyConfig {
move_prev: vec![Keybind::new(Code::ArrowDown)], move_prev: vec![Keybind::new(Code::ArrowDown)],
open_menu: vec![], open_menu: vec![],
close_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 struct Config {
pub page_size: usize, pub page_size: usize,
pub page_load: f64, pub page_load: f64,
pub jump_dist: usize,
#[serde(default = "_true")] #[serde(default = "_true")]
pub use_icons: bool, pub use_icons: bool,
#[serde(default = "_true")] #[serde(default = "_true")]
@ -252,6 +257,7 @@ impl Default for Config {
Self { Self {
page_size: 50, page_size: 50,
page_load: 0.8, page_load: 0.8,
jump_dist: 5,
use_icons: true, use_icons: true,
use_comments: true, use_comments: true,
search: Default::default(), search: Default::default(),

View File

@ -205,14 +205,18 @@ fn App<'a>(cx: Scope<App>) -> Element {
k_updater.set_event(KeyEvent::Exec); k_updater.set_event(KeyEvent::Exec);
} else if matches(&keybinds.exit, &mods, &code) { } else if matches(&keybinds.exit, &mods, &code) {
k_updater.set_event(KeyEvent::Exit); 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) { } 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) { } else if matches(&keybinds.open_menu, &mods, &code) {
k_updater.set_event(KeyEvent::OpenMenu); k_updater.set_event(KeyEvent::OpenMenu);
} else if matches(&keybinds.close_menu, &mods, &code) { } else if matches(&keybinds.close_menu, &mods, &code) {
k_updater.set_event(KeyEvent::CloseMenu); 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)
} }
}; };

View File

@ -18,10 +18,12 @@ fn scroll<T>(cx: Scope<T>, pos: usize) {
pub enum KeyEvent { pub enum KeyEvent {
Exec, Exec,
Exit, Exit,
ShiftUp, MovePrev,
ShiftDown, MoveNext,
OpenMenu, OpenMenu,
CloseMenu, CloseMenu,
JumpNext,
JumpPrev,
} }
pub struct InnerState { pub struct InnerState {
@ -46,8 +48,20 @@ impl InnerState {
self.pos = std::cmp::min(self.pos + x, max - 1) 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 /// Move Up Once With Context of SubMenu
pub fn shift_up(&mut self) { pub fn move_prev(&mut self) {
if self.subpos > 0 { if self.subpos > 0 {
self.subpos -= 1; self.subpos -= 1;
return; return;
@ -56,15 +70,14 @@ impl InnerState {
} }
/// Move Down Once With Context of SubMenu /// 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 let Some(result) = results.get(self.pos) {
if self.subpos > 0 && self.subpos < result.actions.len() - 1 { if self.subpos > 0 && self.subpos < result.actions.len() - 1 {
self.subpos += 1; self.subpos += 1;
return; return;
} }
} }
let max = std::cmp::max(results.len(), 1); self.jump_down(1, results)
self.move_down(1, max);
} }
} }
@ -164,13 +177,22 @@ impl<'a> AppState<'a> {
KeyEvent::Exec => self.execute(), KeyEvent::Exec => self.execute(),
KeyEvent::OpenMenu => self.open_menu(), KeyEvent::OpenMenu => self.open_menu(),
KeyEvent::CloseMenu => self.close_menu(), KeyEvent::CloseMenu => self.close_menu(),
KeyEvent::ShiftUp => { KeyEvent::MovePrev => {
self.shift_up(); self.move_prev();
let pos = self.position().0; let pos = self.position().0;
scroll(cx, if pos <= 3 { pos } else { pos + 3 }) scroll(cx, if pos <= 3 { pos } else { pos + 3 })
} }
KeyEvent::ShiftDown => { KeyEvent::MoveNext => {
self.shift_down(); 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) scroll(cx, self.position().0 + 3)
} }
}; };
@ -265,13 +287,28 @@ impl<'a> AppState<'a> {
/// Move Up Once With Context of SubMenu /// Move Up Once With Context of SubMenu
#[inline] #[inline]
pub fn shift_up(&self) { pub fn move_prev(&self) {
self.state.with_mut(|s| s.shift_up()); self.state.with_mut(|s| s.move_prev());
} }
/// Move Down Once With Context of SubMenu /// Move Down Once With Context of SubMenu
#[inline] #[inline]
pub fn shift_down(&self) { pub fn move_next(&self) {
self.state.with_mut(|s| s.shift_down(&self.results)) 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))
} }
} }