diff --git a/Cargo.lock b/Cargo.lock index 8acbe88..b30db4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1823,6 +1823,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stdin" +version = "0.1.0" +dependencies = [ + "abi_stable", + "anyrun-plugin", + "fuzzy-matcher", +] + [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index c87583f..5599cef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ members = [ "plugins/kidex", "plugins/translate", "plugins/randr", + "plugins/stdin", ] \ No newline at end of file diff --git a/README.md b/README.md index 1fb8fc8..7474e6e 100644 --- a/README.md +++ b/README.md @@ -126,19 +126,22 @@ cp examples/config.ron ~/.config/anyrun/config.ron # Copy the default config fil Anyrun requires plugins to function, as they provide the results for input. The list of plugins in this repository is as follows: - [Applications](plugins/applications) - - Search and run system & user specific desktop entries + - Search and run system & user specific desktop entries. - [Symbols](plugins/symbols) - - Search unicode symbols + - Search unicode symbols. - [User defined symbols](plugins/symbols/README.md#User-defined-symbols) - [Rink](plugins/rink) - - Calculator & unit conversion + - Calculator & unit conversion. - [Shell](plugins/shell) - - Run shell commands + - Run shell commands. - [Kidex](plugins/kidex) - - File search provided by [Kidex](https://github.com/Kirottu/kidex) + - File search provided by [Kidex](https://github.com/Kirottu/kidex). - [Randr](plugins/randr) - Rotate and resize; quickly change monitor configurations on the fly. - TODO: Only supports Hyprland, needs support for other compositors. +- [Stdin](plugins/stdin) + - Turn Anyrun into a dmenu like fuzzy selector. + - Should generally be used exclusively with the `-o` argument. ## Configuration diff --git a/anyrun-interface/src/lib.rs b/anyrun-interface/src/lib.rs index 146e85f..efd6dca 100644 --- a/anyrun-interface/src/lib.rs +++ b/anyrun-interface/src/lib.rs @@ -56,6 +56,8 @@ pub enum HandleResult { Refresh(bool), /// Copy the content, due to how copying works it must be done like this. Copy(RVec<u8>), + /// Output the content to stdout, printing to stdout has issues in plugins. + Stdout(RVec<u8>), } #[repr(C)] diff --git a/anyrun/src/main.rs b/anyrun/src/main.rs index 474828f..4079fd6 100644 --- a/anyrun/src/main.rs +++ b/anyrun/src/main.rs @@ -1,4 +1,12 @@ -use std::{cell::RefCell, env, fs, mem, path::PathBuf, rc::Rc, time::Duration}; +use std::{ + cell::RefCell, + env, fs, + io::{self, Write}, + mem, + path::PathBuf, + rc::Rc, + time::Duration, +}; use abi_stable::std_types::{ROption, RVec}; use anyrun_interface::{HandleResult, Match, PluginInfo, PluginRef, PollResult}; @@ -492,6 +500,13 @@ fn activate(app: >k::Application, runtime_data: Rc<RefCell<Option<RuntimeData> window.close(); Inhibit(true) } + HandleResult::Stdout(bytes) => { + if let Err(why) = io::stdout().lock().write_all(&bytes) { + eprintln!("Error outputting content to stdout: {}", why); + } + window.close(); + Inhibit(true) + } } } _ => Inhibit(false), diff --git a/plugins/stdin/Cargo.toml b/plugins/stdin/Cargo.toml new file mode 100644 index 0000000..a45bc28 --- /dev/null +++ b/plugins/stdin/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "stdin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyrun-plugin = { path = "../../anyrun-plugin" } +fuzzy-matcher = "0.3.7" +abi_stable = "0.11.1" diff --git a/plugins/stdin/src/lib.rs b/plugins/stdin/src/lib.rs new file mode 100644 index 0000000..2e048de --- /dev/null +++ b/plugins/stdin/src/lib.rs @@ -0,0 +1,52 @@ +use std::io::stdin; + +use abi_stable::std_types::{ROption, RString, RVec}; +use anyrun_plugin::{anyrun_interface::HandleResult, plugin, Match, PluginInfo}; +use fuzzy_matcher::FuzzyMatcher; + +fn init(_config_dir: RString) -> Vec<String> { + stdin().lines().filter_map(|line| line.ok()).collect() +} + +fn handler(_match: Match, _lines: &mut Vec<String>) -> HandleResult { + HandleResult::Stdout(_match.title.into_bytes()) +} + +fn get_matches(input: RString, lines: &mut Vec<String>) -> RVec<Match> { + let matcher = fuzzy_matcher::skim::SkimMatcherV2::default().smart_case(); + + let mut lines = lines + .clone() + .into_iter() + .filter_map(|line| { + matcher + .fuzzy_match(&line, &input) + .map(|score| (line, score)) + }) + .collect::<Vec<_>>(); + + lines.sort_by(|a, b| b.1.cmp(&a.1)); + + lines.truncate(5); + + lines + .into_iter() + .map(|(line, _)| Match { + title: line.into(), + description: ROption::RNone, + use_pango: false, + icon: ROption::RNone, + id: ROption::RNone, + }) + .collect::<Vec<_>>() + .into() +} + +fn plugin_info() -> PluginInfo { + PluginInfo { + name: "Stdin".into(), + icon: "format-indent-more".into(), + } +} + +plugin!(init, plugin_info, get_matches, handler, Vec<String>);