feat(service): prefer recently/often used applications in search

This commit is contained in:
Thomas Ruprecht 2022-11-01 22:41:23 +01:00 committed by GitHub
parent f382690b28
commit 4eef0caae5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 225 additions and 25 deletions

View file

@ -3,12 +3,16 @@
mod client;
mod plugins;
mod recent;
mod priority;
pub use client::*;
pub use plugins::config;
pub use plugins::external::load;
use crate::plugins::*;
use crate::recent::RecentUseStorage;
use crate::priority::Priority;
use flume::{Receiver, Sender};
use futures::{future, SinkExt, Stream, StreamExt};
use pop_launcher::*;
@ -16,7 +20,7 @@ use regex::Regex;
use slab::Slab;
use std::{
collections::{HashMap, HashSet},
io::{self, Write},
io::{self, Write}, path::PathBuf,
};
pub type PluginKey = usize;
@ -34,7 +38,38 @@ pub struct PluginHelp {
pub help: Option<String>,
}
pub fn ensure_cache_path() -> Result<PathBuf, Box<dyn std::error::Error>> {
let cachepath = dirs::home_dir().ok_or("failed to find home dir")?.join(".cache/pop-launcher");
std::fs::create_dir_all(&cachepath)?;
Ok(cachepath.join("recent"))
}
pub fn store_cache(storage: &RecentUseStorage) {
let write_recent = || -> Result<(), Box<dyn std::error::Error>> {
let cachepath = ensure_cache_path()?;
Ok(serde_json::to_writer(
std::fs::File::create(cachepath)?,
storage
)?)
};
if let Err(e)= write_recent() {
eprintln!("could not write to cache file\n{}", e);
}
}
pub async fn main() {
let cachepath = ensure_cache_path();
let read_recent = || -> Result<RecentUseStorage, Box<dyn std::error::Error>> {
let cachepath = std::fs::File::open(cachepath?)?;
Ok(serde_json::from_reader(cachepath)?)
};
let recent = match read_recent() {
Ok(r) => r,
Err(e) => {eprintln!("could not read cache file\n{}", e); RecentUseStorage::default()}
};
// Listens for a stream of requests from stdin.
let input_stream = json_input_stream(tokio::io::stdin()).filter_map(|result| {
future::ready(match result {
@ -49,7 +84,7 @@ pub async fn main() {
let (output_tx, output_rx) = flume::bounded(16);
// Service will operate for as long as it is being awaited
let service = Service::new(output_tx.into_sink()).exec(input_stream);
let service = Service::new(output_tx.into_sink(), recent).exec(input_stream);
// Responses from the service will be streamed to stdout
let responder = async move {
@ -73,10 +108,11 @@ pub struct Service<O> {
output: O,
plugins: Slab<PluginConnector>,
search_scheduled: bool,
recent: RecentUseStorage,
}
impl<O: futures::Sink<Response> + Unpin> Service<O> {
pub fn new(output: O) -> Self {
pub fn new(output: O, recent: RecentUseStorage) -> Self {
Self {
active_search: Vec::new(),
associated_list: HashMap::new(),
@ -86,6 +122,7 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
no_sort: false,
plugins: Slab::new(),
search_scheduled: false,
recent,
}
}
@ -241,16 +278,24 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
}
async fn activate(&mut self, id: Indice) {
let mut ex = None;
if let Some((plugin, meta)) = self.search_result(id as usize) {
ex = meta.cache_identifier();
let _ = plugin
.sender_exec()
.send_async(Request::Activate(meta.id))
.await;
}
if let Some(e) = ex {
self.recent.add(&e);
store_cache(&self.recent);
}
}
async fn activate_context(&mut self, id: Indice, context: Indice) {
let mut ex = None;
if let Some((plugin, meta)) = self.search_result(id as usize) {
ex = meta.cache_identifier();
let _ = plugin
.sender_exec()
.send_async(Request::ActivateContext {
@ -259,6 +304,10 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
})
.await;
}
if let Some(e) = ex {
self.recent.add(&e);
store_cache(&self.recent);
}
}
fn append(&mut self, plugin: PluginKey, append: PluginSearchResult) {
@ -445,6 +494,7 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
ref mut no_sort,
ref last_query,
ref plugins,
ref recent,
..
} = self;
@ -497,23 +547,6 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
})
}
let a_weight = calculate_weight(&a.1, query);
let b_weight = calculate_weight(&b.1, query);
match a_weight.partial_cmp(&b_weight) {
Some(Ordering::Equal) => {
let a_len = a.1.name.len();
let b_len = b.1.name.len();
a_len.cmp(&b_len)
}
Some(Ordering::Less) => Ordering::Greater,
Some(Ordering::Greater) => Ordering::Less,
None => Ordering::Greater,
}
});
active_search.sort_by(|a, b| {
let plug1 = match plugins.get(a.0) {
Some(plug) => plug,
None => return Ordering::Greater,
@ -524,11 +557,18 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
None => return Ordering::Less,
};
plug1
.config
.query
.priority
.cmp(&plug2.config.query.priority)
let get_prio = |sr: &PluginSearchResult, plg: &PluginConnector| -> Priority {
let ex = sr.cache_identifier();
Priority {
plugin_priority: plg.config.query.priority,
match_score: calculate_weight(sr, query),
recent_use_index: ex.as_ref().map(|s| recent.get_recent(s)).unwrap_or(0),
use_freq: ex.as_ref().map(|s| recent.get_freq(s)).unwrap_or(0),
execlen: sr.name.len()
}
};
get_prio(&b.1, plug2).cmp(&get_prio(&a.1, plug1))
})
}