Merge branch 'master' of https://github.com/Kirottu/anyrun
This commit is contained in:
@@ -94,6 +94,8 @@ Anyrun requires plugins to function, as they provide the results for input. The
|
||||
- Calculator & unit conversion.
|
||||
- [Shell](plugins/shell/README.md)
|
||||
- Run shell commands.
|
||||
- [Translate](plugins/translate/README.md)
|
||||
- Quickly translate text.
|
||||
- [Kidex](plugins/kidex/README.md)
|
||||
- File search provided by [Kidex](https://github.com/Kirottu/kidex).
|
||||
- [Randr](plugins/randr/README.md)
|
||||
|
@@ -29,9 +29,6 @@ in
|
||||
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
outputHashes = {
|
||||
"kidex-common-0.1.0" = "sha256-sPzCTK0gdIYkKWxrtoPJ/F2zrG2ZKHOSmANW2g00fSQ=";
|
||||
};
|
||||
};
|
||||
|
||||
checkInputs = [cargo rustc];
|
||||
|
@@ -6,6 +6,8 @@ Launch applications.
|
||||
|
||||
Simply search for the application you wish to launch.
|
||||
|
||||
*NOTE: The applications plugin does not look for executables in your $PATH, it looks for [desktop entries](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html) in standard locations (`XDG_DATA_DIRS`).*
|
||||
|
||||
## Configuration
|
||||
|
||||
```ron
|
||||
|
@@ -179,7 +179,11 @@ pub fn scrubber(config: &Config) -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std
|
||||
Err(_why) => return None,
|
||||
};
|
||||
let entries = DesktopEntry::from_dir_entry(&entry, config);
|
||||
Some(entries.into_iter().map(|entry| (entry.name.clone(), entry)))
|
||||
Some(
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|entry| (format!("{}{}", entry.name, entry.icon), entry)),
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
@@ -195,7 +199,11 @@ pub fn scrubber(config: &Config) -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std
|
||||
Err(_why) => return None,
|
||||
};
|
||||
let entries = DesktopEntry::from_dir_entry(&entry, config);
|
||||
Some(entries.into_iter().map(|entry| (entry.name.clone(), entry)))
|
||||
Some(
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|entry| (format!("{}{}", entry.name, entry.icon), entry)),
|
||||
)
|
||||
})
|
||||
.flatten(),
|
||||
),
|
||||
|
@@ -4,7 +4,8 @@ Quickly translate text using the Google Translate API.
|
||||
|
||||
## Usage
|
||||
|
||||
Type in `<prefix><target lang> <text to translate>`, where prefix is the configured prefix (default is in [Configuration](#Configuration)) and the rest are pretty obvious.
|
||||
Type in `<prefix><target lang> <text to translate>` or `<prefix><src lang><language_delimiter><target lang> <text to translate>`,
|
||||
where the `prefix` and `language_delimiter` are config options (defaults are in [Configuration](#Configuration)) and the rest are pretty obvious.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -12,6 +13,7 @@ Type in `<prefix><target lang> <text to translate>`, where prefix is the configu
|
||||
// <Anyrun config dir>/translate.ron
|
||||
Config(
|
||||
prefix: ":",
|
||||
language_delimiter: ">",
|
||||
max_entries: 3,
|
||||
)
|
||||
```
|
@@ -3,11 +3,14 @@ use std::fs;
|
||||
use abi_stable::std_types::{ROption, RString, RVec};
|
||||
use anyrun_plugin::*;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Config {
|
||||
prefix: String,
|
||||
language_delimiter: String,
|
||||
max_entries: usize,
|
||||
}
|
||||
|
||||
@@ -15,6 +18,7 @@ impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
prefix: ":".to_string(),
|
||||
language_delimiter: ">".to_string(),
|
||||
max_entries: 3,
|
||||
}
|
||||
}
|
||||
@@ -22,6 +26,8 @@ impl Default for Config {
|
||||
|
||||
struct State {
|
||||
config: Config,
|
||||
client: Client,
|
||||
runtime: Runtime,
|
||||
langs: Vec<(&'static str, &'static str)>,
|
||||
}
|
||||
|
||||
@@ -32,6 +38,8 @@ fn init(config_dir: RString) -> State {
|
||||
Ok(content) => ron::from_str(&content).unwrap_or_default(),
|
||||
Err(_) => Config::default(),
|
||||
},
|
||||
client: Client::new(),
|
||||
runtime: Runtime::new().expect("Failed to create tokio runtime"),
|
||||
langs: vec![
|
||||
("af", "Afrikaans"),
|
||||
("sq", "Albanian"),
|
||||
@@ -151,51 +159,94 @@ fn info() -> PluginInfo {
|
||||
}
|
||||
|
||||
#[get_matches]
|
||||
fn get_matches(input: RString, data: &State) -> RVec<Match> {
|
||||
if !input.starts_with(&data.config.prefix) {
|
||||
fn get_matches(input: RString, state: &State) -> RVec<Match> {
|
||||
if !input.starts_with(&state.config.prefix) {
|
||||
return RVec::new();
|
||||
}
|
||||
|
||||
// Ignore the prefix
|
||||
let input = &input[data.config.prefix.len()..];
|
||||
let (lang, text) = match input.split_once(' ') {
|
||||
let input = &input[state.config.prefix.len()..];
|
||||
let (lang_split, text) = match input.split_once(' ') {
|
||||
Some(split) => split,
|
||||
None => return RVec::new(),
|
||||
};
|
||||
|
||||
let (src, dest) = match lang_split.split_once(&state.config.language_delimiter) {
|
||||
Some(split) => (Some(split.0), split.1),
|
||||
None => (None, lang_split),
|
||||
};
|
||||
|
||||
if text.is_empty() {
|
||||
return RVec::new();
|
||||
}
|
||||
|
||||
let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().ignore_case();
|
||||
|
||||
// Fuzzy match the input language with the languages in the Vec
|
||||
let mut matches = data
|
||||
let dest_matches = state
|
||||
.langs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(|(code, name)| {
|
||||
matcher
|
||||
.fuzzy_match(code, lang)
|
||||
.max(matcher.fuzzy_match(name, lang))
|
||||
.fuzzy_match(code, dest)
|
||||
.max(matcher.fuzzy_match(name, dest))
|
||||
.map(|score| (code, name, score))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
matches.sort_by(|a, b| b.2.cmp(&a.2));
|
||||
// Fuzzy match the input language with the languages in the Vec
|
||||
let mut matches = match src {
|
||||
Some(src) => {
|
||||
let src_matches = state
|
||||
.langs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(|(code, name)| {
|
||||
matcher
|
||||
.fuzzy_match(code, src)
|
||||
.max(matcher.fuzzy_match(name, src))
|
||||
.map(|score| (code, name, score))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut matches = src_matches
|
||||
.into_iter()
|
||||
.flat_map(|src| dest_matches.clone().into_iter().map(move |dest| (Some(src), dest)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
matches.sort_by(|a, b| (b.1 .2 + b.0.unwrap().2).cmp(&(a.1 .2 + a.0.unwrap().2)));
|
||||
matches
|
||||
}
|
||||
None => {
|
||||
let mut matches = dest_matches
|
||||
.into_iter()
|
||||
.map(|dest| (None, dest))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
matches.sort_by(|a, b| b.1 .2.cmp(&a.1 .2));
|
||||
matches
|
||||
}
|
||||
};
|
||||
|
||||
// We only want 3 matches
|
||||
matches.truncate(data.config.max_entries);
|
||||
matches.truncate(state.config.max_entries);
|
||||
|
||||
tokio::runtime::Runtime::new().expect("Failed to spawn tokio runtime!").block_on(async move {
|
||||
state.runtime.block_on(async move {
|
||||
// Create the futures for fetching the translation results
|
||||
let futures = matches
|
||||
.into_iter()
|
||||
.map(|(code, name, _)| async move {
|
||||
(name, reqwest::get(format!("https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl={}&dt=t&q={}", code, text)).await)
|
||||
.map(|(src, dest)| async move {
|
||||
match src {
|
||||
Some(src) =>
|
||||
(dest.1, state.client.get(format!("https://translate.googleapis.com/translate_a/single?client=gtx&sl={}&tl={}&dt=t&q={}", src.0, dest.0, text)).send().await),
|
||||
None => (dest.1, state.client.get(format!("https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl={}&dt=t&q={}", dest.0, text)).send().await)
|
||||
}
|
||||
});
|
||||
futures::future::join_all(futures) // Wait for all futures to complete
|
||||
.await
|
||||
|
||||
let res = futures::future::join_all(futures) // Wait for all futures to complete
|
||||
.await;
|
||||
|
||||
res
|
||||
.into_iter()
|
||||
.filter_map(|(name, res)| res
|
||||
.ok()
|
||||
@@ -215,7 +266,7 @@ fn get_matches(input: RString, data: &State) -> RVec<Match> {
|
||||
description: ROption::RSome(
|
||||
format!(
|
||||
"{} -> {}",
|
||||
data.langs.iter()
|
||||
state.langs.iter()
|
||||
.find_map(|(code, name)| if *code == json[2].as_str().expect("Malformed JSON!") {
|
||||
Some(*name)
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user