diff --git a/Cargo.lock b/Cargo.lock index 16ee02a..267b600 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -542,6 +542,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.10.1" @@ -620,6 +629,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +[[package]] +name = "osstrtools" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f328dada9eee3ab878d2d367d8bf323c12b20f881c5566ae6e6708b3862dcaba" +dependencies = [ + "itertools 0.8.2", +] + [[package]] name = "pango" version = "0.14.8" @@ -840,7 +858,7 @@ dependencies = [ [[package]] name = "sirula" -version = "0.0.1" +version = "1.0.0" dependencies = [ "freedesktop_entry_parser", "futures", @@ -853,6 +871,7 @@ dependencies = [ "gtk-layer-shell", "libc", "locale-types", + "osstrtools", "pango", "regex", "serde", @@ -917,7 +936,7 @@ dependencies = [ "anyhow", "cfg-expr", "heck", - "itertools", + "itertools 0.10.1", "pkg-config", "strum", "strum_macros", diff --git a/Cargo.toml b/Cargo.toml index c7e4bc5..da391c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sirula" -version = "0.0.1" +version = "1.0.0" authors = ["Dorian Rudolph"] edition = "2018" @@ -22,6 +22,7 @@ serde = "1.0.130" serde_derive = "1.0.130" toml = "0.5.8" regex = "1.5.4" +osstrtools = "0.2.2" [profile.release] lto = true diff --git a/README.md b/README.md index 05d523d..2d0f074 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ I'd be happy to hear any criticism of my code. - Build: `cargo build --release` - Optionally, `strip` the binary to reduce size - Alternatively, install with `cargo install --path .` +- There is also an unofficial [AUR package](https://aur.archlinux.org/packages/sirula-git/) ## Configuration diff --git a/sample-config/default-config.toml b/sample-config/default-config.toml index 1c7567d..94e8434 100644 --- a/sample-config/default-config.toml +++ b/sample-config/default-config.toml @@ -43,6 +43,8 @@ command_prefix = ":" recent_first = true # sort matches of equal quality by most recently used +# term_command = "alacritty -e {}" # command for applications run in terminal (default uses "$TERMINAL -e") + # specify name overrides (id is the name of the desktop file) [name_overrides] -# id = "name\rextra"hide_extra_if_contained \ No newline at end of file +# id = "name\rextra" \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 9a6d706..2830890 100644 --- a/src/config.rs +++ b/src/config.rs @@ -68,7 +68,8 @@ make_config!(Config { name_overrides: HashMap = (HashMap::new()) "name_overrides", hide_extra_if_contained: bool = (true) "hide_extra_if_contained", command_prefix: String = (":".into()) "command_prefix", - exclude: Vec = (Vec::new()) "exclude" + exclude: Vec = (Vec::new()) "exclude", + term_command: Option = (None) "term_command" }); fn deserialize_markup<'de, D>(deserializer: D) -> Result, D::Error> diff --git a/src/history.rs b/src/history.rs index b4ade12..76cd35f 100644 --- a/src/history.rs +++ b/src/history.rs @@ -7,7 +7,7 @@ use std::fs::File; #[derive(Deserialize, Serialize)] pub struct History { - pub last_used : HashMap + pub last_used: HashMap } impl History { @@ -17,7 +17,7 @@ impl History { let config_str = std::fs::read_to_string(file).expect("Cannot read history file"); toml::from_str(&config_str).expect("Cannot parse config: {}") }, - _ => History { last_used : HashMap::new() } + _ => History { last_used: HashMap::new() } } } diff --git a/src/main.rs b/src/main.rs index b5d5b3a..b02e31a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,6 +116,7 @@ fn app_startup(application: >k::Application) { })); let matcher = SkimMatcherV2::default(); + let term_command = config.term_command.clone(); entry.connect_changed(clone!(entries, listbox, cmd_prefix => move |e| { let text = e.text(); let is_cmd = is_cmd(&text, &cmd_prefix); @@ -151,7 +152,7 @@ fn app_startup(application: >k::Application) { let es = entries.borrow(); let e = &es[r]; if e.score >= min_score { - launch_app(&e.info); + launch_app(&e.info, term_command.as_deref()); let mut history = history.borrow_mut(); history.update(e.info.id().unwrap().as_str()); diff --git a/src/util.rs b/src/util.rs index 0ada9b2..6f71376 100644 --- a/src/util.rs +++ b/src/util.rs @@ -22,6 +22,8 @@ use std::path::PathBuf; use gio::{AppInfo, prelude::{AppInfoExt, AppInfoExtManual}, AppInfoCreateFlags}; use gtk::{CssProvider, prelude::CssProviderExt}; use freedesktop_entry_parser::parse_entry; +use osstrtools::OsStrTools; +use std::ffi::OsStr; pub fn get_xdg_dirs() -> xdg::BaseDirectories { xdg::BaseDirectories::with_prefix(APP_NAME).unwrap() @@ -69,32 +71,36 @@ pub fn launch_cmd(cmd_line: &str) { child.spawn().expect("Error spawning command"); } -pub fn launch_app(info: &AppInfo) { +pub fn launch_app(info: &AppInfo, term_command: Option<&str>) { let context = gdk::Display::default().unwrap().app_launch_context().unwrap(); - // launch terminal applications ourselves because GTK ignores the TERMINAL environment variable - if let Some(term) = std::env::var_os("TERMINAL") { - let use_terminal = info.property("filename").ok().and_then(|p| p.get::().ok()) - .and_then(|s| parse_entry(s.to_string()).ok()) - .and_then(|e| e.section("Desktop Entry").attr("Terminal").map(|t| t == "1" || t == "true")) - .unwrap_or(false); - if use_terminal { - let command = match info.commandline() { - Some(c) => c, - _ => info.executable() - }; - let mut cmd_line = term; - cmd_line.push(" -e "); - cmd_line.push(command); - if let Ok(info) = AppInfo::create_from_commandline(cmd_line, None, AppInfoCreateFlags::NONE) { - info.launch(&[], Some(&context)).expect("Error while launching terminal app"); - return; - } - } + if info.property("filename").ok().and_then(|p| p.get::().ok()) + .and_then(|s| parse_entry(s.to_string()).ok()) + .and_then(|e| e.section("Desktop Entry").attr("Terminal").map(|t| t == "1" || t == "true")) + .unwrap_or_default() { + + let command = (match info.commandline() { + Some(c) => c, + _ => info.executable() + }).as_os_str().quote_single(); + + let commandline = if let Some(term) = term_command { + OsStr::new(term).replace("{}", command) + } else if let Some(mut term) = std::env::var_os("TERMINAL") { + term.push(" -e "); + term.push(command); + term + } else { + return; + }; + + let info = AppInfo::create_from_commandline(commandline, None, AppInfoCreateFlags::NONE) + .expect("Failed to create AppInfo from commandline"); + info.launch(&[], Some(&context)).expect("Error while launching terminal app"); + } else { + let future = info.launch_uris_async_future(&[], Some(&context)); + MainContext::default().block_on(future).expect("Error while launching app"); } - - let future = info.launch_uris_async_future(&[], Some(&context)); - MainContext::default().block_on(future).expect("Error while launching app"); } #[macro_export]