Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plugins/applications/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
```
67 changes: 67 additions & 0 deletions plugins/applications/src/history.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::collections::VecDeque;
use std::fs;
use std::env;

use crate::scrubber::DesktopEntry;


pub struct History(VecDeque<DesktopEntry>);

impl History {
pub fn new() -> Self {
Self(VecDeque::new())
}

pub fn load() -> Self {

let path = format!(
"{}/.cache/anyrun-applications-history",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be ~/.cache/anyrun/applications-history in line with what the nix-run plugin uses.

env::var("HOME").expect("Unable to determine HOME directory")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer $XDG_CACHE_HOME, and use $HOME/.cache as a fallback.

);

if let Ok(content) = fs::read_to_string(&path) {
let history: VecDeque<DesktopEntry> = 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_front(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))
}
pub fn count(&self) -> usize {
self.0.len()
}
}

52 changes: 37 additions & 15 deletions plugins/applications/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct Config {
desktop_actions: bool,
max_entries: usize,
terminal: Option<String>,
history_size: usize,
}

impl Default for Config {
Expand All @@ -18,21 +19,24 @@ impl Default for Config {
desktop_actions: false,
max_entries: 5,
terminal: None,
history_size: 50,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be an optional, in a way that None will disable the history alltogether.

}
}
}

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()
Expand Down Expand Up @@ -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
}

Expand All @@ -95,11 +104,15 @@ pub fn init(config_dir: RString) -> State {
Vec::new()
});

State { config, entries }
let history = history::History::load();
println!("Loaded {} history entries", history.count());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be eprintln!("[applications] ...", ...);


State { config, entries, history }
}

#[get_matches]
pub fn get_matches(input: RString, state: &State) -> RVec<Match> {

let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case();
let mut entries = state
.entries
Expand All @@ -116,9 +129,18 @@ pub fn get_matches(input: RString, state: &State) -> RVec<Match> {
.keywords
.iter()
.map(|keyword| matcher.fuzzy_match(keyword, &input).unwrap_or(0))
.sum::<i64>();
.sum::<i64>();

let mut score = (app_score * 25 + keyword_score) - entry.offset;
let history_score = state.history.get_entry_info(entry).map(|(index, count)| {
let recency_bias = i64::max(5-index as i64, 0);
(count as i64 + recency_bias) * 20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the weighting has changed recently, this likely needs different weighting to make it work properly.

}).unwrap_or(0);

if app_score + keyword_score == 0 {
return None;
}

let mut score = (app_score * 25 + keyword_score + history_score) - entry.offset;

// prioritize actions
if entry.desc.is_some() {
Expand All @@ -135,17 +157,17 @@ pub fn get_matches(input: RString, state: &State) -> RVec<Match> {

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]
Expand Down
4 changes: 3 additions & 1 deletion plugins/applications/src/scrubber.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf>,
Expand Down