From 32de25a6fed4bcc85184820f73afb9d0f642582e Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Thu, 9 Nov 2023 20:00:17 +0100 Subject: [PATCH 1/7] added history --- plugins/applications/src/history.rs | 64 ++++++++++++++++++++++++++++ plugins/applications/src/lib.rs | 63 ++++++++++++++++++++------- plugins/applications/src/scrubber.rs | 4 +- 3 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 plugins/applications/src/history.rs diff --git a/plugins/applications/src/history.rs b/plugins/applications/src/history.rs new file mode 100644 index 00000000..c23ddb3a --- /dev/null +++ b/plugins/applications/src/history.rs @@ -0,0 +1,64 @@ +use std::collections::VecDeque; +use std::fs; +use std::env; + +use crate::scrubber::DesktopEntry; + + +pub struct History(VecDeque); + +impl History { + pub fn new() -> Self { + Self(VecDeque::new()) + } + + pub fn load() -> Self { + + let path = format!( + "{}/.cache/anyrun-applications-history", + env::var("HOME").expect("Unable to determine HOME directory") + ); + + if let Ok(content) = fs::read_to_string(&path) { + let mut history: VecDeque = ron::from_str(&content).unwrap_or_else(|why| { + eprintln!("Error parsing history: {}", why); + VecDeque::new() + }); + return Self(history); + } + + Self::new() + } + + pub fn write(&self) { + + let path = format!( + "{}/.cache/anyrun-applications-history", + env::var("HOME").expect("Unable to determine HOME directory") + ); + + let content = ron::to_string(&self.0).unwrap_or_else(|why| { + eprintln!("Error serializing history: {}", why); + String::new() + }); + if let Err(why) = fs::write(&path, content) { + eprintln!("Error writing history: {}", why); + } + } + pub fn add_entry(&mut self, entry: DesktopEntry) { + + self.0.push_back(entry); + } + + pub fn truncate(&mut self, max_entries: usize) { + self.0.truncate(max_entries); + } + + pub fn get_entry_info(&self, entry: &DesktopEntry) -> Option<(usize, usize)> { + let index = self.0.iter().position(|x| x == entry)?; + let count = self.0.iter().filter(|x| *x == entry).count(); + Some((index, count)) + } + +} + diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs index 30cebb85..a92d3658 100644 --- a/plugins/applications/src/lib.rs +++ b/plugins/applications/src/lib.rs @@ -10,6 +10,7 @@ pub struct Config { desktop_actions: bool, max_entries: usize, terminal: Option, + history_size: usize, } impl Default for Config { @@ -18,21 +19,24 @@ impl Default for Config { desktop_actions: false, max_entries: 5, terminal: None, + history_size: 50, } } } pub struct State { - config: Config, + config: Config, entries: Vec<(DesktopEntry, u64)>, + history: history::History, } mod scrubber; +mod history; const SENSIBLE_TERMINALS: &[&str] = &["alacritty", "foot", "kitty", "wezterm", "wterm"]; #[handler] -pub fn handler(selection: Match, state: &State) -> HandleResult { +pub fn handler(selection: Match, state: &mut State) -> HandleResult { let entry = state .entries .iter() @@ -74,6 +78,11 @@ pub fn handler(selection: Match, state: &State) -> HandleResult { eprintln!("Error running desktop entry: {}", why); } + state.history.add_entry(entry.clone()); + state.history.truncate(state.config.history_size); + state.history.write(); + + HandleResult::Close } @@ -95,11 +104,27 @@ pub fn init(config_dir: RString) -> State { Vec::new() }); - State { config, entries } + let history = history::History::load(); + + State { config, entries, history } +} + +fn format_entries(entries: Vec<(&DesktopEntry, u64)>) -> RVec { + entries + .into_iter() + .map(|(entry, id)| Match { + title: entry.name.clone().into(), + description: entry.desc.clone().map(|desc| desc.into()).into(), + use_pango: false, + icon: ROption::RSome(entry.icon.clone().into()), + id: ROption::RSome(id), + }) + .collect() } #[get_matches] pub fn get_matches(input: RString, state: &State) -> RVec { + let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case(); let mut entries = state .entries @@ -116,9 +141,17 @@ pub fn get_matches(input: RString, state: &State) -> RVec { .keywords .iter() .map(|keyword| matcher.fuzzy_match(keyword, &input).unwrap_or(0)) - .sum::(); + .sum::(); + let history_score = state.history.get_entry_info(entry).map(|(index, count)| { + let recency = 10-index; + ((count + recency) * 20) as i64 + }).unwrap_or(0); + + if app_score + keyword_score == 0 { + return None; + } - let mut score = (app_score * 25 + keyword_score) - entry.offset; + let mut score = (app_score * 25 + keyword_score + history_score) - entry.offset; // prioritize actions if entry.desc.is_some() { @@ -135,17 +168,17 @@ pub fn get_matches(input: RString, state: &State) -> RVec { entries.sort_by(|a, b| b.2.cmp(&a.2)); - entries.truncate(state.config.max_entries); + entries.truncate(state.config.max_entries); entries - .into_iter() - .map(|(entry, id, _)| Match { - title: entry.name.clone().into(), - description: entry.desc.clone().map(|desc| desc.into()).into(), - use_pango: false, - icon: ROption::RSome(entry.icon.clone().into()), - id: ROption::RSome(id), - }) - .collect() + .into_iter() + .map(|(entry, id, _)| Match { + title: entry.name.clone().into(), + description: entry.desc.clone().map(|desc| desc.into()).into(), + use_pango: false, + icon: ROption::RSome(entry.icon.clone().into()), + id: ROption::RSome(id), + }) + .collect() } #[info] diff --git a/plugins/applications/src/scrubber.rs b/plugins/applications/src/scrubber.rs index d72065c3..767ff1e0 100644 --- a/plugins/applications/src/scrubber.rs +++ b/plugins/applications/src/scrubber.rs @@ -1,8 +1,10 @@ use std::{collections::HashMap, env, ffi::OsStr, fs, path::PathBuf}; +use serde::{Deserialize, Serialize}; + use crate::Config; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct DesktopEntry { pub exec: String, pub path: Option, From 57fdace5a29459cdc61112ab4936c83e3a57113e Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Thu, 9 Nov 2023 20:06:08 +0100 Subject: [PATCH 2/7] removed format_entries test function --- plugins/applications/src/lib.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs index a92d3658..4dade0dd 100644 --- a/plugins/applications/src/lib.rs +++ b/plugins/applications/src/lib.rs @@ -109,19 +109,6 @@ pub fn init(config_dir: RString) -> State { State { config, entries, history } } -fn format_entries(entries: Vec<(&DesktopEntry, u64)>) -> RVec { - entries - .into_iter() - .map(|(entry, id)| Match { - title: entry.name.clone().into(), - description: entry.desc.clone().map(|desc| desc.into()).into(), - use_pango: false, - icon: ROption::RSome(entry.icon.clone().into()), - id: ROption::RSome(id), - }) - .collect() -} - #[get_matches] pub fn get_matches(input: RString, state: &State) -> RVec { From dab1260523759418081684b36bb128e79a686efd Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Thu, 9 Nov 2023 20:08:17 +0100 Subject: [PATCH 3/7] updated readme --- plugins/applications/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/applications/README.md b/plugins/applications/README.md index ab77fe41..1fa9c15d 100644 --- a/plugins/applications/README.md +++ b/plugins/applications/README.md @@ -19,5 +19,7 @@ Config( // The terminal used for running terminal based desktop entries, if left as `None` a static list of terminals is used // to determine what terminal to use. terminal: Some("alacritty"), + // The history size for the application history, set to 0 to disable history + history_size: 50, ) ``` \ No newline at end of file From 4a00576ff13e6e90b9c43c64080e0ba750c839f1 Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Mon, 13 Nov 2023 14:38:12 +0100 Subject: [PATCH 4/7] fix --- plugins/applications/src/history.rs | 13 ++++++++----- plugins/applications/src/lib.rs | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/applications/src/history.rs b/plugins/applications/src/history.rs index c23ddb3a..d91c0520 100644 --- a/plugins/applications/src/history.rs +++ b/plugins/applications/src/history.rs @@ -20,7 +20,8 @@ impl History { ); if let Ok(content) = fs::read_to_string(&path) { - let mut history: VecDeque = ron::from_str(&content).unwrap_or_else(|why| { + let mut history: VecDeque = ron::from_str(&content) + .unwrap_or_else(|why| { eprintln!("Error parsing history: {}", why); VecDeque::new() }); @@ -45,9 +46,9 @@ impl History { eprintln!("Error writing history: {}", why); } } - pub fn add_entry(&mut self, entry: DesktopEntry) { - - self.0.push_back(entry); + + pub fn add_entry(&mut self, entry: DesktopEntry) { + self.0.push_front(entry); } pub fn truncate(&mut self, max_entries: usize) { @@ -59,6 +60,8 @@ impl History { let count = self.0.iter().filter(|x| *x == entry).count(); Some((index, count)) } - + pub fn count(&self) -> usize { + self.0.len() + } } diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs index 4dade0dd..3f621ad2 100644 --- a/plugins/applications/src/lib.rs +++ b/plugins/applications/src/lib.rs @@ -105,6 +105,7 @@ pub fn init(config_dir: RString) -> State { }); let history = history::History::load(); + println!("Loaded {} history entries", history.count()); State { config, entries, history } } @@ -128,7 +129,8 @@ pub fn get_matches(input: RString, state: &State) -> RVec { .keywords .iter() .map(|keyword| matcher.fuzzy_match(keyword, &input).unwrap_or(0)) - .sum::(); + .sum::(); + let history_score = state.history.get_entry_info(entry).map(|(index, count)| { let recency = 10-index; ((count + recency) * 20) as i64 From 01813a5d19b4912924c035b4edcc26a348138803 Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Mon, 13 Nov 2023 17:37:57 +0100 Subject: [PATCH 5/7] fixed subtraction overflow --- plugins/applications/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs index 3f621ad2..c1aed78f 100644 --- a/plugins/applications/src/lib.rs +++ b/plugins/applications/src/lib.rs @@ -132,8 +132,8 @@ pub fn get_matches(input: RString, state: &State) -> RVec { .sum::(); let history_score = state.history.get_entry_info(entry).map(|(index, count)| { - let recency = 10-index; - ((count + recency) * 20) as i64 + let recency = 10-index as i64; + (count as i64 + recency) * 20 }).unwrap_or(0); if app_score + keyword_score == 0 { From d5cae8209172794b2557fe8f102a9769fa065f40 Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Wed, 6 Dec 2023 15:55:24 +0100 Subject: [PATCH 6/7] removed unneccesary mut --- plugins/applications/src/history.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/applications/src/history.rs b/plugins/applications/src/history.rs index d91c0520..c9cded42 100644 --- a/plugins/applications/src/history.rs +++ b/plugins/applications/src/history.rs @@ -20,7 +20,7 @@ impl History { ); if let Ok(content) = fs::read_to_string(&path) { - let mut history: VecDeque = ron::from_str(&content) + let history: VecDeque = ron::from_str(&content) .unwrap_or_else(|why| { eprintln!("Error parsing history: {}", why); VecDeque::new() From 48cd51e740408ef1784c632f3bf21f32de8a243b Mon Sep 17 00:00:00 2001 From: Pepijn Bakker Date: Wed, 6 Dec 2023 16:11:07 +0100 Subject: [PATCH 7/7] improved recency bias --- plugins/applications/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs index c1aed78f..fcca878b 100644 --- a/plugins/applications/src/lib.rs +++ b/plugins/applications/src/lib.rs @@ -132,8 +132,8 @@ pub fn get_matches(input: RString, state: &State) -> RVec { .sum::(); let history_score = state.history.get_entry_info(entry).map(|(index, count)| { - let recency = 10-index as i64; - (count as i64 + recency) * 20 + let recency_bias = i64::max(5-index as i64, 0); + (count as i64 + recency_bias) * 20 }).unwrap_or(0); if app_score + keyword_score == 0 {