From 39224c3cba9c88c2822359b86279ba76e8c31f52 Mon Sep 17 00:00:00 2001 From: Arijit Dey Date: Thu, 27 Jul 2023 14:25:31 +0530 Subject: [PATCH] chore: Update all versions of dependencies --- Cargo.toml | 22 ++++-- src/core/search.rs | 3 + src/data_store.rs | 115 +++++++++++++++++++++++++++++++ src/input/definitions/keydefs.rs | 84 ++++++++++++++++------ src/input/event_wrapper.rs | 4 +- src/input/mod.rs | 20 ++++++ 6 files changed, 220 insertions(+), 28 deletions(-) create mode 100644 src/data_store.rs diff --git a/Cargo.toml b/Cargo.toml index c29bc506..f61bf8de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,19 @@ categories = ["Text processing", "Command-line interface", "Asynchronous"] all-features = true rustdoc-args = ["--cfg", "docsrs"] +[lib] +name = "minus" +path = "src/lib.rs" +crate-type = ["lib"] + [dependencies] -crossterm = "0.22.1" -textwrap = { version = "~0.13", default-features = false, features = ["unicode-width"] } +crossterm = "^0.26" +textwrap = { version = "~0.16", default-features = false, features = ["unicode-width"] } thiserror = "^1" -regex = { version = ">=1.5.5", optional = true } -crossbeam-channel = "0.5.1" +regex = { version = "^1", optional = true } +crossbeam-channel = "^0.5" parking_lot = "0.12.1" -once_cell = { version = "1.15.0", features = ["parking_lot"] } +once_cell = { version = "^1.18", features = ["parking_lot"] } [features] search = [ "regex" ] @@ -34,28 +39,35 @@ tokio = { version = "^1.0", features = ["rt", "macros", "rt-multi-thread", "time [[example]] name = "dyn_tokio" +path = "examples/dyn_tokio.rs" required-features = ["dynamic_output"] [[example]] name = "less-rs" +path = "examples/less-rs.rs" required-features = ["dynamic_output"] [[example]] name = "static" +path = "examples/static.rs" required-features = ["static_output"] [[example]] name = "large_lines" +path = "examples/large-lines.rs" required-features = ["static_output"] [[example]] name = "color-output" +path = "examples/color-output.rs" required-features = ["static_output"] [[example]] name = "static-no-overflow" +path = "examples/static-no-overflow.rs" required-features = ["static_output"] [[example]] name = "msg-tokio" +path = "examples/msg-tokio.rs" required-features = ["dynamic_output"] diff --git a/src/core/search.rs b/src/core/search.rs index 9b01f54b..fef575af 100644 --- a/src/core/search.rs +++ b/src/core/search.rs @@ -80,6 +80,7 @@ pub fn fetch_input( Event::Key(KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::NONE, + .. }) => { write!(out, "{}", cursor::Hide)?; return Ok(String::new()); @@ -88,6 +89,7 @@ pub fn fetch_input( Event::Key(KeyEvent { code: KeyCode::Backspace, modifiers: KeyModifiers::NONE, + .. }) => { string.pop(); // Update the line @@ -97,6 +99,7 @@ pub fn fetch_input( Event::Key(KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, + .. }) => { write!(out, "{}", cursor::Hide)?; // Return the string when enter is pressed diff --git a/src/data_store.rs b/src/data_store.rs new file mode 100644 index 00000000..f2fa1124 --- /dev/null +++ b/src/data_store.rs @@ -0,0 +1,115 @@ +//! Provides [DataStore] for storing variable data +//! +//! While building complex applications with minus, it is sometimes necessory to store variable data +//! that should be available while configuring minus. Lets say you want to know when the user presses `G` +//! and maybe use this information to call some other part of your application, you may be tempted use +//! something like this +//! ```rust,compile_fail +//! # use minus::input::{HashedEventRegister, InputEvent}; +//! +//! let mut pressed_G = false; +//! +//! let mut event_register = HashedEventRegister::default(); +//! event_register.add_key_events(&["G"], |_, ps|{ +//! pressed_G = true; +//! // ... +//! # InputEvent::NextMatch +//! }); +//! ``` +//! But this will cause a compilation error and that's quite obvious. The `pressed_G` function may go out of scope +//! whenever the function goes out of scope, but `add_key_events` requies all variables to be `'static` i.e. they +//! should'nt go out of scope till these callbacks go out of scope. +//! +//! You can use other workaround for this problem but minus provides the [DataStore] type that can easily solve this +//! problem for you. Lets see how you can solve this easily with the help of [DataStore] +//! ```rust +//! # use minus::input::{HashedEventRegister, InputEvent}; +//! +//! let mut event_register = HashedEventRegister::default(); +//! event_register.add_key_events(&["G"], |_, ps|{ +//! ps.store.push("pressed_G", Box::new(true)); +//! // ... +//! # InputEvent::NextMatch +//! }); +//! ``` +//! Out of everything the most important line is this one. +//! ```text +//! ps.store.push("pressed_G", Box::new(true)); +//! ``` +//! Lets talk about it in detail. +//! +//! Any data that you store in the store must be associated with a name. In this case, the first argument `pressed_G` +//! is that name. It can be anything that can be converted to a [String]. In other words, it must implement +//! `Into`. The name will be useful when you want to access that data again. +//! The next argument of course is the value +//! that we want to store. The data must be wrapped inside a [Box]. This might seem a bit annoying but it allows the +//! [DataStore] to store all sorts of data inside it. Do note that whatever data you store must implement [Hash], +//! [Send], [Sync] and must have a `'static` lifetime. +//! +//! But how do you access that data in other parts of your application? Well, for this you need to do some + +use std::collections::HashMap; +use std::hash::Hash; +use std::sync::Arc; +use parking_lot::Mutex; + +pub trait Hashable {} + +impl Hashable for T where T: Hash + Send + Sync + 'static { +} + + +pub trait HashableType: Hashable + Send + Sync + 'static { + +} + +// pub trait HashableTypeExit: Sized + DynClone { +// fn into_box(&self) -> Box; +// } +// +// impl HashableTypeExit for &dyn HashableType { +// fn into_box(&self) -> Box { +// Box::new(*self.clone()) +// } +// } + + +pub struct DataStore(Arc>>>); + +impl DataStore { + pub fn new() -> Self { + Self(Arc::new(Mutex::new(HashMap::new()))) + } + + pub fn push(&self, k: impl Into, v: Box) { + let mut map = self.0.lock(); + + let v = *v.as_ref(); + + + let v = Arc::from(v); + map.insert(k.into(), v); + } + + pub fn remove(&self, k: impl Into) { + let mut map = self.0.lock(); + map.remove(&k.into()); + } + + pub fn get(&self, k: impl Into) -> Option> + where K: Into { + let map = self.0.lock(); + map.get(&k.into()).map(|v| { + v.data.clone().as_ref() + // let t = v.as_ref().clone(); + // t.into_box() + }) + } +} + +impl Default for DataStore { + fn default() -> Self { + let ds = Self::new(); + ds + } +} diff --git a/src/input/definitions/keydefs.rs b/src/input/definitions/keydefs.rs index 3e3ac3de..84a3476c 100644 --- a/src/input/definitions/keydefs.rs +++ b/src/input/definitions/keydefs.rs @@ -3,7 +3,7 @@ use super::{Token, MODIFIERS}; use std::collections::HashMap; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, KeyEventState}; use once_cell::sync::Lazy; static SPECIAL_KEYS: Lazy> = Lazy::new(|| { @@ -118,6 +118,8 @@ impl KeySeq { KeyEvent { code: ks.code.unwrap_or(KeyCode::Null), modifiers: ks.modifiers, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } } } @@ -130,140 +132,180 @@ fn test_parse_key_event() { parse_key_event("up"), KeyEvent { code: KeyCode::Up, - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("k"), KeyEvent { code: KeyCode::Char('k'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("j"), KeyEvent { code: KeyCode::Char('j'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("down"), KeyEvent { code: KeyCode::Down, - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("down"), KeyEvent { code: KeyCode::Down, - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("enter"), KeyEvent { code: KeyCode::Enter, - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("c-u"), KeyEvent { code: KeyCode::Char('u'), - modifiers: KeyModifiers::CONTROL + modifiers: KeyModifiers::CONTROL, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("c-d"), KeyEvent { code: KeyCode::Char('d'), - modifiers: KeyModifiers::CONTROL + modifiers: KeyModifiers::CONTROL, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("g"), KeyEvent { code: KeyCode::Char('g'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("s-g"), KeyEvent { code: KeyCode::Char('g'), - modifiers: KeyModifiers::SHIFT + modifiers: KeyModifiers::SHIFT, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("G"), KeyEvent { code: KeyCode::Char('G'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("pageup"), KeyEvent { code: KeyCode::PageUp, - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("pagedown"), KeyEvent { code: KeyCode::PageDown, - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("c-l"), KeyEvent { code: KeyCode::Char('l'), - modifiers: KeyModifiers::CONTROL + modifiers: KeyModifiers::CONTROL, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("q"), KeyEvent { code: KeyCode::Char('q'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("c-c"), KeyEvent { code: KeyCode::Char('c'), - modifiers: KeyModifiers::CONTROL + modifiers: KeyModifiers::CONTROL, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("/"), KeyEvent { code: KeyCode::Char('/'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("?"), KeyEvent { code: KeyCode::Char('?'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("n"), KeyEvent { code: KeyCode::Char('n'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); assert_eq!( parse_key_event("p"), KeyEvent { code: KeyCode::Char('p'), - modifiers: KeyModifiers::NONE + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: KeyEventState::NONE, } ); } diff --git a/src/input/event_wrapper.rs b/src/input/event_wrapper.rs index 3fe3d9e2..977b7514 100644 --- a/src/input/event_wrapper.rs +++ b/src/input/event_wrapper.rs @@ -87,7 +87,7 @@ pub struct HashedEventRegister(HashMap); /// A convinient type for the return type of [`HashedEventRegister::get`] type EventReturnType = Arc InputEvent + Send + Sync>; -#[derive(Copy, Clone, Eq)] +#[derive(Clone, Eq)] enum EventWrapper { ExactMatchEvent(Event), WildEvent, @@ -101,7 +101,7 @@ impl From for EventWrapper { impl From<&Event> for EventWrapper { fn from(e: &Event) -> Self { - Self::ExactMatchEvent(*e) + Self::ExactMatchEvent(e.clone()) } } diff --git a/src/input/mod.rs b/src/input/mod.rs index 97c98cd4..b612038b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -228,6 +228,7 @@ where if let Event::Key(KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, + .. }) = ev { if c.is_ascii_digit() { @@ -255,6 +256,7 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code, modifiers: KeyModifiers::NONE, + .. }) if code == KeyCode::Up || code == KeyCode::Char('k') => { let position = ps.prefix_num.parse::().unwrap_or(1); Some(InputEvent::UpdateUpperMark( @@ -266,6 +268,7 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code, modifiers: KeyModifiers::NONE, + .. }) if code == KeyCode::Down || code == KeyCode::Char('j') => { let position = ps.prefix_num.parse::().unwrap_or(1); Some(InputEvent::UpdateUpperMark( @@ -277,12 +280,14 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, + .. }) if c.is_ascii_digit() => Some(InputEvent::Number(c)), // Enter key Event::Key(KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, + .. }) => { if ps.message.is_some() { Some(InputEvent::RestorePrompt) @@ -298,6 +303,7 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::Char('u'), modifiers, + .. }) if modifiers == KeyModifiers::CONTROL || modifiers == KeyModifiers::NONE => { let half_screen = ps.rows / 2; Some(InputEvent::UpdateUpperMark( @@ -308,6 +314,7 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::Char('d'), modifiers, + .. }) if modifiers == KeyModifiers::CONTROL || modifiers == KeyModifiers::NONE => { let half_screen = ps.rows / 2; Some(InputEvent::UpdateUpperMark( @@ -328,19 +335,23 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::Char('g'), modifiers: KeyModifiers::NONE, + .. }) => Some(InputEvent::UpdateUpperMark(0)), // Go to bottom. Event::Key(KeyEvent { code: KeyCode::Char('g'), modifiers: KeyModifiers::SHIFT, + .. }) | Event::Key(KeyEvent { code: KeyCode::Char('G'), modifiers: KeyModifiers::SHIFT, + .. }) | Event::Key(KeyEvent { code: KeyCode::Char('G'), modifiers: KeyModifiers::NONE, + .. }) => { let mut position = ps .prefix_num @@ -359,12 +370,14 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::PageUp, modifiers: KeyModifiers::NONE, + .. }) => Some(InputEvent::UpdateUpperMark( ps.upper_mark.saturating_sub(ps.rows - 1), )), Event::Key(KeyEvent { code: c, modifiers: KeyModifiers::NONE, + .. }) if c == KeyCode::PageDown || c == KeyCode::Char(' ') => Some( InputEvent::UpdateUpperMark(ps.upper_mark.saturating_add(ps.rows - 1)), ), @@ -377,30 +390,36 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::Char('l'), modifiers: KeyModifiers::CONTROL, + .. }) => Some(InputEvent::UpdateLineNumber(!ps.line_numbers)), // Quit. Event::Key(KeyEvent { code: KeyCode::Char('q'), modifiers: KeyModifiers::NONE, + .. }) | Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, + .. }) => Some(InputEvent::Exit), #[cfg(feature = "search")] Event::Key(KeyEvent { code: KeyCode::Char('/'), modifiers: KeyModifiers::NONE, + .. }) => Some(InputEvent::Search(SearchMode::Forward)), #[cfg(feature = "search")] Event::Key(KeyEvent { code: KeyCode::Char('?'), modifiers: KeyModifiers::NONE, + .. }) => Some(InputEvent::Search(SearchMode::Reverse)), #[cfg(feature = "search")] Event::Key(KeyEvent { code: KeyCode::Char('n'), modifiers: KeyModifiers::NONE, + .. }) => { let position = ps.prefix_num.parse::().unwrap_or(1); if ps.search_mode == SearchMode::Reverse { @@ -413,6 +432,7 @@ impl InputClassifier for DefaultInputClassifier { Event::Key(KeyEvent { code: KeyCode::Char('p'), modifiers: KeyModifiers::NONE, + .. }) => { let position = ps.prefix_num.parse::().unwrap_or(1); if ps.search_mode == SearchMode::Reverse {