Something went wrong. Try again.
a krunner plugin to open projects in your editor
Something went wrong. Try again.
4.6 kB · 167 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168use clap::Parser;use color_eyre::eyre;use fuzzy_matcher::skim::SkimMatcherV2;use itertools::Itertools;use std::{ collections::HashMap, fs::ReadDir, future::pending, path::PathBuf, process::{Command, Stdio},};use zbus::{connection, interface, zvariant::OwnedValue};
type Match = ( String, String, String, i32, f64, HashMap<String, OwnedValue>,);
struct Runner { editor_bin: String, project_path: PathBuf,}
impl Runner { fn filter_dirs(dirs: ReadDir, query: &str) -> Vec<Match> { if query.len() < 3 { return vec![]; }
let matcher = SkimMatcherV2::default();
let min_score = (query.len() as i64) * 20;
dirs.filter_map(|dir| { dir.map(|dir| (dir.path(), dir.file_name().to_str().unwrap().to_string())) .ok() }) .filter_map(|(_path, name)| { matcher .fuzzy(&name, &query.replace(" ", ""), false) .map(|some| (some, _path, name)) }) .filter(|((score, _), _, _)| score >= &min_score) .sorted_by(|(fuzzy_a, _, _), (fuzzy_b, _, _)| fuzzy_a.0.cmp(&fuzzy_b.0).reverse()) .map(|(_, path, name)| { ( path.to_str().unwrap().to_string(), format!("Open Project {}", Self::to_display(&name)), "zed".to_string(), 100, 0.0, HashMap::new(), ) }) .collect() }
fn to_display(name: &str) -> String { let mut words: Vec<String> = Vec::new(); let mut current = String::new(); let mut prev: Option<char> = None;
for c in name.chars() { if c == '-' || c == '_' || c.is_whitespace() { if !current.is_empty() { words.push(std::mem::take(&mut current)); } } else { if c.is_uppercase() && prev.is_some_and(|p| p.is_lowercase() || p.is_ascii_digit()) && !current.is_empty() { words.push(std::mem::take(&mut current)); } current.push(c); } prev = Some(c); } if !current.is_empty() { words.push(current); }
words .iter() .map(|w| { let mut chars = w.chars(); match chars.next() { Some(first) => { first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase() } None => String::new(), } }) .collect::<Vec<_>>() .join(" ") }}
#[interface(name = "org.kde.krunner1")]impl Runner { #[zbus(name = "Actions")] fn do_actions(&self) -> Vec<(String, String, String)> { vec![] }
#[zbus(name = "Run")] fn do_run(&self, match_id: &str, _action_id: &str) { println!("starting {} in {}", self.editor_bin, match_id);
let res = Command::new("systemd-run") .args(["--user", "--collect", "--quiet", "-p", "KillMode=process"]) .arg(&self.editor_bin) .arg(match_id) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn();
if let Err(e) = res { eprintln!("{}", e); } }
#[zbus(name = "Match")] fn do_match(&self, query: &str) -> Vec<Match> { self.project_path .read_dir() .map(|dirs| Self::filter_dirs(dirs, query)) .unwrap_or(vec![]) }}
#[derive(Parser)]#[command(version, about, long_about = None)]struct Cli { #[arg(long)] editor_bin: Option<String>, #[arg(long)] projects_path: Option<PathBuf>,}
#[tokio::main]async fn main() -> eyre::Result<()> { let cli = Cli::parse();
let _conn = connection::Builder::session()? .name("dev.tobinio.ProjectRunner")? .serve_at( "/dev/tobinio/ProjectRunner", Runner { editor_bin: cli.editor_bin.unwrap_or("zeditor".to_string()), project_path: cli .projects_path .or_else(|| dirs::home_dir().map(|h| h.join("Projects"))) .ok_or_else(|| eyre::eyre!("could not determine home directory"))?, }, )? .build() .await?;
pending::<()>().await; Ok(())}