Add max_entries config to plugins (#19)

This commit is contained in:
Kangwook Lee (이강욱)
2023-05-01 21:16:11 +09:00
committed by GitHub
parent 90ecba8261
commit 0aabb332c9
10 changed files with 144 additions and 70 deletions

3
Cargo.lock generated
View File

@@ -436,6 +436,7 @@ dependencies = [
"abi_stable", "abi_stable",
"anyrun-plugin", "anyrun-plugin",
"reqwest", "reqwest",
"ron",
"serde", "serde",
] ]
@@ -1073,6 +1074,8 @@ dependencies = [
"fuzzy-matcher", "fuzzy-matcher",
"kidex-common", "kidex-common",
"open", "open",
"ron",
"serde",
] ]
[[package]] [[package]]

View File

@@ -5,16 +5,32 @@ use scrubber::DesktopEntry;
use serde::Deserialize; use serde::Deserialize;
use std::{fs, process::Command}; use std::{fs, process::Command};
#[derive(Deserialize, Default)] #[derive(Deserialize)]
pub struct Config { pub struct Config {
desktop_actions: bool, desktop_actions: bool,
max_entries: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
desktop_actions: false,
max_entries: 5,
}
}
}
pub struct State {
config: Config,
entries: Vec<(DesktopEntry, u64)>,
} }
mod scrubber; mod scrubber;
#[handler] #[handler]
pub fn handler(selection: Match, entries: &Vec<(DesktopEntry, u64)>) -> HandleResult { pub fn handler(selection: Match, state: &State) -> HandleResult {
let entry = entries let entry = state
.entries
.iter() .iter()
.find_map(|(entry, id)| { .find_map(|(entry, id)| {
if *id == selection.id.unwrap() { if *id == selection.id.unwrap() {
@@ -33,7 +49,7 @@ pub fn handler(selection: Match, entries: &Vec<(DesktopEntry, u64)>) -> HandleRe
} }
#[init] #[init]
pub fn init(config_dir: RString) -> Vec<(DesktopEntry, u64)> { pub fn init(config_dir: RString) -> State {
let config: Config = match fs::read_to_string(format!("{}/applications.ron", config_dir)) { let config: Config = match fs::read_to_string(format!("{}/applications.ron", config_dir)) {
Ok(content) => ron::from_str(&content).unwrap_or_else(|why| { Ok(content) => ron::from_str(&content).unwrap_or_else(|why| {
eprintln!("Error parsing applications plugin config: {}", why); eprintln!("Error parsing applications plugin config: {}", why);
@@ -45,24 +61,26 @@ pub fn init(config_dir: RString) -> Vec<(DesktopEntry, u64)> {
} }
}; };
scrubber::scrubber(config).unwrap_or_else(|why| { let entries = scrubber::scrubber(&config).unwrap_or_else(|why| {
eprintln!("Failed to load desktop entries: {}", why); eprintln!("Failed to load desktop entries: {}", why);
Vec::new() Vec::new()
}) });
State { config, entries }
} }
#[get_matches] #[get_matches]
pub fn get_matches(input: RString, entries: &Vec<(DesktopEntry, u64)>) -> RVec<Match> { pub fn get_matches(input: RString, state: &State) -> RVec<Match> {
let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case(); let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case();
let mut entries = entries let mut entries = state
.clone() .entries
.into_iter() .iter()
.filter_map(|(entry, id)| { .filter_map(|(entry, id)| {
let score = matcher.fuzzy_match(&entry.name, &input).unwrap_or(0) let score = matcher.fuzzy_match(&entry.name, &input).unwrap_or(0)
+ matcher.fuzzy_match(&entry.exec, &input).unwrap_or(0); + matcher.fuzzy_match(&entry.exec, &input).unwrap_or(0);
if score > 0 { if score > 0 {
Some((entry, id, score)) Some((entry, *id, score))
} else { } else {
None None
} }
@@ -71,14 +89,14 @@ pub fn get_matches(input: RString, entries: &Vec<(DesktopEntry, u64)>) -> RVec<M
entries.sort_by(|a, b| b.2.cmp(&a.2)); entries.sort_by(|a, b| b.2.cmp(&a.2));
entries.truncate(5); entries.truncate(state.config.max_entries);
entries entries
.into_iter() .into_iter()
.map(|(entry, id, _)| Match { .map(|(entry, id, _)| Match {
title: entry.name.into(), title: entry.name.clone().into(),
description: entry.desc.map(|desc| desc.into()).into(), description: entry.desc.clone().map(|desc| desc.into()).into(),
use_pango: false, use_pango: false,
icon: ROption::RSome(entry.icon.into()), icon: ROption::RSome(entry.icon.clone().into()),
id: ROption::RSome(id), id: ROption::RSome(id),
}) })
.collect() .collect()

View File

@@ -132,7 +132,7 @@ impl DesktopEntry {
} }
} }
pub fn scrubber(config: Config) -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std::error::Error>> { pub fn scrubber(config: &Config) -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std::error::Error>> {
// Create iterator over all the files in the XDG_DATA_DIRS // Create iterator over all the files in the XDG_DATA_DIRS
// XDG compliancy is cool // XDG compliancy is cool
let mut paths: Vec<Result<fs::DirEntry, io::Error>> = match env::var("XDG_DATA_DIRS") { let mut paths: Vec<Result<fs::DirEntry, io::Error>> = match env::var("XDG_DATA_DIRS") {
@@ -186,7 +186,7 @@ pub fn scrubber(config: Config) -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std:
Ok(entry) => entry, Ok(entry) => entry,
Err(_why) => return None, Err(_why) => return None,
}; };
Some(DesktopEntry::from_dir_entry(entry, &config)) Some(DesktopEntry::from_dir_entry(entry, config))
}) })
.flatten() .flatten()
.enumerate() .enumerate()

View File

@@ -13,3 +13,4 @@ anyrun-plugin = { path = "../../anyrun-plugin" }
abi_stable = "0.11.1" abi_stable = "0.11.1"
reqwest = { version = "0.11.16", default-features = false, features = ["blocking", "json", "rustls-tls"] } reqwest = { version = "0.11.16", default-features = false, features = ["blocking", "json", "rustls-tls"] }
serde = { version = "1.0.160", features = ["derive"] } serde = { version = "1.0.160", features = ["derive"] }
ron = "0.8.0"

View File

@@ -1,7 +1,24 @@
use std::fs;
use abi_stable::std_types::{ROption, RString, RVec}; use abi_stable::std_types::{ROption, RString, RVec};
use anyrun_plugin::*; use anyrun_plugin::*;
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize)]
pub struct Config {
prefix: String,
max_entries: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
prefix: ":def".to_string(),
max_entries: 3,
}
}
}
#[allow(unused)] #[allow(unused)]
#[derive(Deserialize)] #[derive(Deserialize)]
struct ApiResponse { struct ApiResponse {
@@ -36,7 +53,12 @@ struct Definition {
} }
#[init] #[init]
pub fn init(_config_dir: RString) {} pub fn init(config_dir: RString) -> Config {
match fs::read_to_string(format!("{}/dictionary.ron", config_dir)) {
Ok(content) => ron::from_str(&content).unwrap_or_default(),
Err(_) => Config::default(),
}
}
#[handler] #[handler]
pub fn handler(_match: Match) -> HandleResult { pub fn handler(_match: Match) -> HandleResult {
@@ -44,12 +66,12 @@ pub fn handler(_match: Match) -> HandleResult {
} }
#[get_matches] #[get_matches]
pub fn get_matches(input: RString) -> RVec<Match> { pub fn get_matches(input: RString, config: &Config) -> RVec<Match> {
if !input.starts_with(":def") { let input = if let Some(input) = input.strip_prefix(&config.prefix) {
input.trim()
} else {
return RVec::new(); return RVec::new();
} };
let input = &input[4..].trim();
let responses: Vec<ApiResponse> = match reqwest::blocking::get(format!( let responses: Vec<ApiResponse> = match reqwest::blocking::get(format!(
"https://api.dictionaryapi.dev/api/v2/entries/en/{}", "https://api.dictionaryapi.dev/api/v2/entries/en/{}",
@@ -89,7 +111,7 @@ pub fn get_matches(input: RString) -> RVec<Match> {
}) })
.collect::<RVec<_>>() .collect::<RVec<_>>()
}) })
.take(3) .take(config.max_entries)
.collect() .collect()
} }

View File

@@ -14,3 +14,5 @@ kidex-common = { git = "https://github.com/Kirottu/kidex", features = ["util"] }
abi_stable = "0.11.1" abi_stable = "0.11.1"
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
open = "3.2.0" open = "3.2.0"
serde = { version = "1.0.160", features = ["derive"] }
ron = "0.8.0"

View File

@@ -2,9 +2,22 @@ use abi_stable::std_types::{ROption, RString, RVec};
use anyrun_plugin::{anyrun_interface::HandleResult, *}; use anyrun_plugin::{anyrun_interface::HandleResult, *};
use fuzzy_matcher::FuzzyMatcher; use fuzzy_matcher::FuzzyMatcher;
use kidex_common::IndexEntry; use kidex_common::IndexEntry;
use std::{os::unix::prelude::OsStrExt, process::Command}; use serde::Deserialize;
use std::{fs, os::unix::prelude::OsStrExt, process::Command};
#[derive(Deserialize)]
struct Config {
max_entries: usize,
}
impl Default for Config {
fn default() -> Self {
Self { max_entries: 3 }
}
}
pub struct State { pub struct State {
config: Config,
index: Vec<(usize, IndexEntry)>, index: Vec<(usize, IndexEntry)>,
selection: Option<IndexEntry>, selection: Option<IndexEntry>,
} }
@@ -58,15 +71,21 @@ pub fn handler(selection: Match, state: &mut State) -> HandleResult {
} }
#[init] #[init]
pub fn init(_config_dir: RString) -> State { pub fn init(config_dir: RString) -> State {
State { let config = match fs::read_to_string(format!("{}/kidex.ron", config_dir)) {
index: match kidex_common::util::get_index(None) { Ok(content) => ron::from_str(&content).unwrap_or_default(),
Err(_) => Config::default(),
};
let index = match kidex_common::util::get_index(None) {
Ok(index) => index.into_iter().enumerate().collect(), Ok(index) => index.into_iter().enumerate().collect(),
Err(why) => { Err(why) => {
println!("Failed to get kidex index: {}", why); println!("Failed to get kidex index: {}", why);
Vec::new() Vec::new()
} }
}, };
State {
config,
index,
selection: None, selection: None,
} }
} }
@@ -116,7 +135,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec<Match> {
index.sort_by(|a, b| b.2.cmp(&a.2)); index.sort_by(|a, b| b.2.cmp(&a.2));
index.truncate(3); index.truncate(state.config.max_entries);
index index
.into_iter() .into_iter()
.map(|(entry_index, id, _)| Match { .map(|(entry_index, id, _)| Match {

View File

@@ -11,12 +11,14 @@ mod randr;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Config { struct Config {
prefix: String, prefix: String,
max_entries: usize,
} }
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Config { Config {
prefix: ":dp".to_string(), prefix: ":dp".to_string(),
max_entries: 5,
} }
} }
} }
@@ -103,11 +105,11 @@ pub fn handler(_match: Match, state: &mut State) -> HandleResult {
#[get_matches] #[get_matches]
pub fn get_matches(input: RString, state: &State) -> RVec<Match> { pub fn get_matches(input: RString, state: &State) -> RVec<Match> {
if !input.starts_with(&state.config.prefix) { let input = if let Some(input) = input.strip_prefix(&state.config.prefix) {
input.trim()
} else {
return RVec::new(); return RVec::new();
} };
let input = &input[state.config.prefix.len()..].trim();
let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case(); let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case();
let mut vec = match &state.inner { let mut vec = match &state.inner {
@@ -187,7 +189,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec<Match> {
vec.sort_by(|a, b| b.1.cmp(&a.1)); vec.sort_by(|a, b| b.1.cmp(&a.1));
vec.truncate(5); vec.truncate(state.config.max_entries);
vec.into_iter().map(|(_match, _)| _match).collect() vec.into_iter().map(|(_match, _)| _match).collect()
} }

View File

@@ -16,35 +16,40 @@ struct Symbol {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct Config { struct Config {
symbols: HashMap<String, String>, symbols: HashMap<String, String>,
max_entries: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
symbols: HashMap::new(),
max_entries: 3,
}
}
}
struct State {
config: Config,
symbols: Vec<Symbol>,
} }
#[init] #[init]
fn init(config_dir: RString) -> Vec<Symbol> { fn init(config_dir: RString) -> State {
// Try to load the config file, if it does not exist only use the static unicode characters // Try to load the config file, if it does not exist only use the static unicode characters
if let Ok(content) = fs::read_to_string(format!("{}/symbols.ron", config_dir)) { let config = if let Ok(content) = fs::read_to_string(format!("{}/symbols.ron", config_dir)) {
match ron::from_str::<Config>(&content) { ron::from_str(&content).unwrap_or_default()
Ok(config) => { } else {
Config::default()
};
let symbols = UNICODE_CHARS let symbols = UNICODE_CHARS
.iter() .iter()
.map(|(name, chr)| (name.to_string(), chr.to_string())) .map(|(name, chr)| (name.to_string(), chr.to_string()))
.chain(config.symbols.into_iter()) .chain(config.symbols.clone().into_iter())
.map(|(name, chr)| Symbol { chr, name }) .map(|(name, chr)| Symbol { chr, name })
.collect(); .collect();
return symbols;
}
Err(why) => {
println!("Error parsing symbols config file: {}", why);
}
}
}
UNICODE_CHARS State { config, symbols }
.iter()
.map(|(name, chr)| Symbol {
chr: chr.to_string(),
name: name.to_string(),
})
.collect()
} }
#[info] #[info]
@@ -56,11 +61,11 @@ fn info() -> PluginInfo {
} }
#[get_matches] #[get_matches]
fn get_matches(input: RString, symbols: &Vec<Symbol>) -> RVec<Match> { fn get_matches(input: RString, state: &State) -> RVec<Match> {
let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().ignore_case(); let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().ignore_case();
let mut symbols = symbols let mut symbols = state
.clone() .symbols
.into_iter() .iter()
.filter_map(|symbol| { .filter_map(|symbol| {
matcher matcher
.fuzzy_match(&symbol.name, &input) .fuzzy_match(&symbol.name, &input)
@@ -71,13 +76,13 @@ fn get_matches(input: RString, symbols: &Vec<Symbol>) -> RVec<Match> {
// Sort the symbol list according to the score // Sort the symbol list according to the score
symbols.sort_by(|a, b| b.1.cmp(&a.1)); symbols.sort_by(|a, b| b.1.cmp(&a.1));
symbols.truncate(3); symbols.truncate(state.config.max_entries);
symbols symbols
.into_iter() .into_iter()
.map(|(symbol, _)| Match { .map(|(symbol, _)| Match {
title: symbol.chr.into(), title: symbol.chr.clone().into(),
description: ROption::RSome(symbol.name.into()), description: ROption::RSome(symbol.name.clone().into()),
use_pango: false, use_pango: false,
icon: ROption::RNone, icon: ROption::RNone,
id: ROption::RNone, id: ROption::RNone,

View File

@@ -8,12 +8,14 @@ use serde::Deserialize;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Config { struct Config {
prefix: String, prefix: String,
max_entries: usize,
} }
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
prefix: ":".to_string(), prefix: ":".to_string(),
max_entries: 3,
} }
} }
} }
@@ -182,7 +184,7 @@ fn get_matches(input: RString, data: &State) -> RVec<Match> {
matches.sort_by(|a, b| b.2.cmp(&a.2)); matches.sort_by(|a, b| b.2.cmp(&a.2));
// We only want 3 matches // We only want 3 matches
matches.truncate(3); matches.truncate(data.config.max_entries);
tokio::runtime::Runtime::new().expect("Failed to spawn tokio runtime!").block_on(async move { tokio::runtime::Runtime::new().expect("Failed to spawn tokio runtime!").block_on(async move {
// Create the futures for fetching the translation results // Create the futures for fetching the translation results