diff --git a/sample-config/default-config.toml b/sample-config/default-config.toml index 94e8434..ef053de 100644 --- a/sample-config/default-config.toml +++ b/sample-config/default-config.toml @@ -41,10 +41,13 @@ exclude = [] # list of regexes for excluded app ids (name of the .desktop file) # use "" to disable launching commands command_prefix = ":" +frequent_first = true # sort matches of equal quality by most frequently used recent_first = true # sort matches of equal quality by most recently used +# when both frequent_first and recent_first are set, +# sorting is by frequency first, and recency is used to break ties in frequency # term_command = "alacritty -e {}" # command for applications run in terminal (default uses "$TERMINAL -e") # specify name overrides (id is the name of the desktop file) [name_overrides] -# id = "name\rextra" \ No newline at end of file +# id = "name\rextra" diff --git a/src/app_entry.rs b/src/app_entry.rs index 0855ddf..e09fe17 100644 --- a/src/app_entry.rs +++ b/src/app_entry.rs @@ -27,7 +27,7 @@ use gio::AppInfo; use glib::shell_unquote; use crate::locale::string_collate; -use super::{consts::*, Config, Field, History}; +use super::{consts::*, Config, Field, HistoryData}; use regex::RegexSet; #[derive(Eq)] @@ -38,7 +38,7 @@ pub struct AppEntry { pub info: AppInfo, pub label: Label, pub score: i64, - pub last_used: u64, + pub history: HistoryData } impl AppEntry { @@ -73,7 +73,7 @@ impl AppEntry { add_attrs(&attr_list, &config.markup_default, 0, self.display_string.len() as u32); if let Some((lo, hi)) = self.extra_range { - add_attrs(&attr_list, &config.markup_extra, lo, hi); + add_attrs(&attr_list, &config.markup_extra, lo, hi); } self.label.set_attributes(Some(&attr_list)); } @@ -81,15 +81,18 @@ impl AppEntry { impl PartialEq for AppEntry { fn eq(&self, other: &Self) -> bool { - self.score.eq(&other.score) && self.last_used.eq(&other.last_used) + self.score.eq(&other.score) && self.history.eq(&other.history) } } impl Ord for AppEntry { fn cmp(&self, other: &Self) -> Ordering { match self.score.cmp(&other.score) { - Ordering::Equal => match self.last_used.cmp(&other.last_used) { - Ordering::Equal => string_collate(&self.display_string, &other.display_string), + Ordering::Equal => match self.history.usage_count.cmp(&other.history.usage_count) { + Ordering::Equal => match self.history.last_used.cmp(&other.history.last_used) { + Ordering::Equal => string_collate(&self.display_string, &other.display_string), + ord => ord.reverse() + } ord => ord.reverse() } ord => ord.reverse() @@ -129,7 +132,8 @@ fn add_attrs(list: &AttrList, attrs: &Vec, start: u32, end: u32) { } } -pub fn load_entries(config: &Config, history: &History) -> HashMap { +pub fn load_entries(config: &Config, history: &HashMap) + -> HashMap { let mut entries = HashMap::new(); let icon_theme = IconTheme::default().unwrap(); let apps = gio::AppInfo::all(); @@ -151,14 +155,14 @@ pub fn load_entries(config: &Config, history: &History) -> HashMap (format!("{} {}", name, e), + Some(e) if (!config.hide_extra_if_contained || !name.to_lowercase().contains(&e.to_lowercase())) => (format!("{} {}", name, e), Some((name.len() as u32 + 1, name.len() as u32 + 1 + e.len() as u32))), _ => (name, None) } @@ -202,9 +206,9 @@ pub fn load_entries(config: &Config, history: &History) -> HashMap HashMap = (parse_attributes("foreground=\"red\" underline=\"double\"").unwrap()) "markup_highlight" [deserialize_with = "deserialize_markup"], markup_extra: Vec = (parse_attributes("font_style=\"italic\" font_size=\"smaller\"").unwrap()) "markup_extra" [deserialize_with = "deserialize_markup"], exclusive: bool = (true) "exclusive", + frequent_first: bool = (true) "frequent_first", recent_first: bool = (true) "recent_first", icon_size: i32 = (64) "icon_size", lines: i32 = (2) "lines", diff --git a/src/history.rs b/src/history.rs index 76cd35f..d3537df 100644 --- a/src/history.rs +++ b/src/history.rs @@ -5,31 +5,42 @@ use std::time::{SystemTime, UNIX_EPOCH}; use super::util::get_history_file; use std::fs::File; -#[derive(Deserialize, Serialize)] -pub struct History { - pub last_used: HashMap +#[derive(Copy, Clone, Default, Eq, Deserialize, Serialize)] +pub struct HistoryData { + pub last_used : u64, + pub usage_count : u32 } -impl History { - pub fn load() -> History { - match get_history_file(false) { - Some(file) => { - let config_str = std::fs::read_to_string(file).expect("Cannot read history file"); - toml::from_str(&config_str).expect("Cannot parse config: {}") - }, - _ => History { last_used: HashMap::new() } - } +impl PartialEq for HistoryData { + fn eq(&self, other: &Self) -> bool { + self.last_used.eq(&other.last_used) && self.usage_count.eq(&other.usage_count) + } +} + +pub fn load_history() -> HashMap { + match get_history_file(false) { + Some(file) => { + let config_str = std::fs::read_to_string(file).expect("Cannot read history file"); + toml::from_str(&config_str).expect("Cannot parse config: {}") + }, + _ => HashMap::new() + } +} + +pub fn save_history(history: &HashMap) { + let file = get_history_file(true).expect("Cannot create history file or cache directory"); + let mut file = File::create(file).expect("Cannot open history file for writing"); + let s = toml::to_string(history).unwrap(); + file.write_all(s.as_bytes()).expect("Cannot write to history file"); +} + +pub fn update_history(history: &mut HashMap, id: &str) { + let epoch = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"); + let usage_count; + match history.get(&id.to_string()) { + Some(history_match) => { usage_count = history_match.usage_count + 1 }, + None => { usage_count = 1 } } - pub fn save(&self) { - let file = get_history_file(true).expect("Cannot create history file or cache directory"); - let mut file = File::create(file).expect("Cannot open history file for writing"); - let s = toml::to_string(self).unwrap(); - file.write_all(s.as_bytes()).expect("Cannot write to history file"); - } - - pub fn update(&mut self, id: &str) { - let epoch = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"); - self.last_used.insert(id.to_string(), epoch.as_secs()); - } -} \ No newline at end of file + history.insert( id.to_string(), HistoryData{ last_used: epoch.as_secs(), usage_count } ); +} diff --git a/src/main.rs b/src/main.rs index b02e31a..ab02d00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,7 +85,7 @@ fn app_startup(application: >k::Application) { let listbox = gtk::ListBoxBuilder::new().name(LISTBOX_NAME).build(); scroll.add(&listbox); - let history = Rc::new(RefCell::new(History::load())); + let history = Rc::new(RefCell::new(load_history())); let entries = Rc::new(RefCell::new(load_entries(&config, &history.borrow()))); for (row, _) in &entries.borrow() as &HashMap { @@ -155,8 +155,8 @@ fn app_startup(application: >k::Application) { launch_app(&e.info, term_command.as_deref()); let mut history = history.borrow_mut(); - history.update(e.info.id().unwrap().as_str()); - history.save(); + update_history(&mut history, e.info.id().unwrap().as_str()); + save_history(&history); window.close(); }