Desktop Action support for the Applications plugin
This commit is contained in:
		
							
								
								
									
										57
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										57
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -37,7 +37,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "rustc_version 0.4.0", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
|  "typed-arena", | ||||
| ] | ||||
|  | ||||
| @@ -101,6 +101,8 @@ dependencies = [ | ||||
|  "abi_stable", | ||||
|  "anyrun-plugin", | ||||
|  "fuzzy-matcher", | ||||
|  "ron", | ||||
|  "serde", | ||||
|  "sublime_fuzzy", | ||||
| ] | ||||
|  | ||||
| @@ -113,7 +115,7 @@ dependencies = [ | ||||
|  "core_extensions", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -352,7 +354,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "scratch", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -369,7 +371,7 @@ checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -380,7 +382,7 @@ checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -509,7 +511,7 @@ checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -686,7 +688,7 @@ dependencies = [ | ||||
|  "proc-macro-error", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -790,7 +792,7 @@ dependencies = [ | ||||
|  "proc-macro-error", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -1267,7 +1269,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -1433,7 +1435,7 @@ dependencies = [ | ||||
|  "proc-macro-error-attr", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
|  "version_check", | ||||
| ] | ||||
|  | ||||
| @@ -1450,18 +1452,18 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
| version = "1.0.49" | ||||
| version = "1.0.56" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" | ||||
| checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" | ||||
| dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.23" | ||||
| version = "1.0.26" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" | ||||
| checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
| @@ -1676,22 +1678,22 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.152" | ||||
| version = "1.0.159" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" | ||||
| checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.152" | ||||
| version = "1.0.159" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" | ||||
| checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 2.0.13", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -1792,6 +1794,17 @@ dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.13" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "system-deps" | ||||
| version = "6.0.3" | ||||
| @@ -1845,7 +1858,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -2120,7 +2133,7 @@ dependencies = [ | ||||
|  "once_cell", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
|  "wasm-bindgen-shared", | ||||
| ] | ||||
|  | ||||
| @@ -2154,7 +2167,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "syn 1.0.107", | ||||
|  "wasm-bindgen-backend", | ||||
|  "wasm-bindgen-shared", | ||||
| ] | ||||
|   | ||||
| @@ -13,3 +13,5 @@ anyrun-plugin = { path = "../../anyrun-plugin" } | ||||
| abi_stable = "0.11.1" | ||||
| sublime_fuzzy = "0.7.0" | ||||
| fuzzy-matcher = "0.3.7" | ||||
| ron = "0.8.0" | ||||
| serde = { version = "1.0.159", features = ["derive"] } | ||||
|   | ||||
| @@ -2,7 +2,13 @@ use abi_stable::std_types::{ROption, RString, RVec}; | ||||
| use anyrun_plugin::{anyrun_interface::HandleResult, *}; | ||||
| use fuzzy_matcher::FuzzyMatcher; | ||||
| use scrubber::DesktopEntry; | ||||
| use std::process::Command; | ||||
| use serde::Deserialize; | ||||
| use std::{fs, process::Command}; | ||||
|  | ||||
| #[derive(Deserialize, Default)] | ||||
| pub struct Config { | ||||
|     desktop_actions: bool, | ||||
| } | ||||
|  | ||||
| mod scrubber; | ||||
|  | ||||
| @@ -25,8 +31,22 @@ pub fn handler(selection: Match, entries: &mut Vec<(DesktopEntry, u64)>) -> Hand | ||||
|     HandleResult::Close | ||||
| } | ||||
|  | ||||
| pub fn init(_config_dir: RString) -> Vec<(DesktopEntry, u64)> { | ||||
|     scrubber::scrubber().expect("Failed to load desktop entries!") | ||||
| pub fn init(config_dir: RString) -> Vec<(DesktopEntry, u64)> { | ||||
|     let config: Config = match fs::read_to_string(format!("{}/applications.ron", config_dir)) { | ||||
|         Ok(content) => ron::from_str(&content).unwrap_or_else(|why| { | ||||
|             eprintln!("Error parsing applications plugin config: {}", why); | ||||
|             Config::default() | ||||
|         }), | ||||
|         Err(why) => { | ||||
|             eprintln!("Error reading applications plugin config: {}", why); | ||||
|             Config::default() | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     scrubber::scrubber(config).unwrap_or_else(|why| { | ||||
|         eprintln!("Failed to load desktop entries: {}", why); | ||||
|         Vec::new() | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub fn get_matches(input: RString, entries: &mut Vec<(DesktopEntry, u64)>) -> RVec<Match> { | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| use std::{collections::HashMap, env, ffi::OsStr, fs, io}; | ||||
|  | ||||
| use crate::Config; | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct DesktopEntry { | ||||
|     pub exec: String, | ||||
| @@ -12,55 +14,118 @@ const FIELD_CODE_LIST: &[&str] = &[ | ||||
| ]; | ||||
|  | ||||
| impl DesktopEntry { | ||||
|     fn from_dir_entry(entry: &fs::DirEntry) -> Option<Self> { | ||||
|     fn from_dir_entry(entry: &fs::DirEntry, config: &Config) -> Vec<Self> { | ||||
|         if entry.path().extension() == Some(OsStr::new("desktop")) { | ||||
|             let content = match fs::read_to_string(entry.path()) { | ||||
|                 Ok(content) => content, | ||||
|                 Err(_) => return None, | ||||
|                 Err(_) => return Vec::new(), | ||||
|             }; | ||||
|  | ||||
|             let mut map = HashMap::new(); | ||||
|             for line in content.lines() { | ||||
|                 if line.starts_with("[") && line != "[Desktop Entry]" { | ||||
|                     break; | ||||
|             let lines = content.lines().collect::<Vec<_>>(); | ||||
|  | ||||
|             let sections = lines | ||||
|                 .split_inclusive(|line| line.starts_with('[')) | ||||
|                 .collect::<Vec<_>>(); | ||||
|  | ||||
|             let mut line = None; | ||||
|             let mut new_sections = Vec::new(); | ||||
|  | ||||
|             for section in sections.iter() { | ||||
|                 if let Some(line) = line { | ||||
|                     let mut section = section.to_vec(); | ||||
|                     section.insert(0, line); | ||||
|                     section.pop(); | ||||
|                     new_sections.push(section); | ||||
|                 } | ||||
|                 let (key, val) = match line.split_once("=") { | ||||
|                     Some(keyval) => keyval, | ||||
|                     None => continue, | ||||
|                 }; | ||||
|                 map.insert(key, val); | ||||
|                 line = Some(section.last().unwrap_or(&"")); | ||||
|             } | ||||
|  | ||||
|             if map.get("Type")? == &"Application" | ||||
|                 && match map.get("NoDisplay") { | ||||
|                     Some(no_display) => !no_display.parse::<bool>().unwrap_or(true), | ||||
|                     None => true, | ||||
|                 } | ||||
|             { | ||||
|                 Some(DesktopEntry { | ||||
|                     exec: { | ||||
|                         let mut exec = map.get("Exec")?.to_string(); | ||||
|                         for field_code in FIELD_CODE_LIST { | ||||
|                             exec = exec.replace(field_code, ""); | ||||
|             let mut ret = Vec::new(); | ||||
|  | ||||
|             let entry = match new_sections.iter().find_map(|section| { | ||||
|                 if section[0].starts_with("[Desktop Entry]") { | ||||
|                     let mut map = HashMap::new(); | ||||
|  | ||||
|                     for line in section.iter().skip(1) { | ||||
|                         if let Some((key, val)) = line.split_once('=') { | ||||
|                             map.insert(key, val); | ||||
|                         } | ||||
|                         exec | ||||
|                     }, | ||||
|                     name: map.get("Name")?.to_string(), | ||||
|                     icon: map | ||||
|                         .get("Icon") | ||||
|                         .unwrap_or(&"application-x-executable") | ||||
|                         .to_string(), | ||||
|                 }) | ||||
|             } else { | ||||
|                 None | ||||
|                     } | ||||
|  | ||||
|                     if map.get("Type")? == &"Application" | ||||
|                         && match map.get("NoDisplay") { | ||||
|                             Some(no_display) => !no_display.parse::<bool>().unwrap_or(true), | ||||
|                             None => true, | ||||
|                         } | ||||
|                     { | ||||
|                         Some(DesktopEntry { | ||||
|                             exec: { | ||||
|                                 let mut exec = map.get("Exec")?.to_string(); | ||||
|  | ||||
|                                 for field_code in FIELD_CODE_LIST { | ||||
|                                     exec = exec.replace(field_code, ""); | ||||
|                                 } | ||||
|                                 exec | ||||
|                             }, | ||||
|                             name: map.get("Name")?.to_string(), | ||||
|                             icon: map | ||||
|                                 .get("Icon") | ||||
|                                 .unwrap_or(&"application-x-executable") | ||||
|                                 .to_string(), | ||||
|                         }) | ||||
|                     } else { | ||||
|                         None | ||||
|                     } | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }) { | ||||
|                 Some(entry) => entry, | ||||
|                 None => return Vec::new(), | ||||
|             }; | ||||
|  | ||||
|             if config.desktop_actions { | ||||
|                 for section in new_sections { | ||||
|                     let mut map = HashMap::new(); | ||||
|  | ||||
|                     for line in section.iter().skip(1) { | ||||
|                         if let Some((key, val)) = line.split_once('=') { | ||||
|                             map.insert(key, val); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if section[0].starts_with("[Desktop Action") { | ||||
|                         ret.push(DesktopEntry { | ||||
|                             exec: match map.get("Exec") { | ||||
|                                 Some(exec) => { | ||||
|                                     let mut exec = exec.to_string(); | ||||
|  | ||||
|                                     for field_code in FIELD_CODE_LIST { | ||||
|                                         exec = exec.replace(field_code, ""); | ||||
|                                     } | ||||
|                                     exec | ||||
|                                 } | ||||
|                                 None => continue, | ||||
|                             }, | ||||
|                             name: match map.get("Name") { | ||||
|                                 Some(name) => format!("{}: {}", entry.name, name), | ||||
|                                 None => continue, | ||||
|                             }, | ||||
|                             icon: entry.icon.clone(), | ||||
|                         }) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             ret.push(entry); | ||||
|             ret | ||||
|         } else { | ||||
|             None | ||||
|             Vec::new() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn scrubber() -> 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 | ||||
|     // XDG compliancy is cool | ||||
|     let mut paths: Vec<Result<fs::DirEntry, io::Error>> = match env::var("XDG_DATA_DIRS") { | ||||
| @@ -68,7 +133,7 @@ pub fn scrubber() -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std::error::Error> | ||||
|             // The vec for all the DirEntry objects | ||||
|             let mut paths = Vec::new(); | ||||
|             // Parse the XDG_DATA_DIRS variable and list files of all the paths | ||||
|             for dir in data_dirs.split(":") { | ||||
|             for dir in data_dirs.split(':') { | ||||
|                 match fs::read_dir(format!("{}/applications/", dir)) { | ||||
|                     Ok(dir) => { | ||||
|                         paths.extend(dir); | ||||
| @@ -102,20 +167,19 @@ pub fn scrubber() -> Result<Vec<(DesktopEntry, u64)>, Box<dyn std::error::Error> | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     paths.extend(fs::read_dir(&user_path)?); | ||||
|  | ||||
|     // Keeping track of the entries | ||||
|     let mut id = 0; | ||||
|     paths.extend(fs::read_dir(user_path)?); | ||||
|  | ||||
|     Ok(paths | ||||
|         .iter() | ||||
|         .filter_map(|entry| { | ||||
|             id += 1; | ||||
|             let entry = match entry { | ||||
|                 Ok(entry) => entry, | ||||
|                 Err(_why) => return None, | ||||
|             }; | ||||
|             DesktopEntry::from_dir_entry(entry).map(|val| (val, id)) | ||||
|             Some(DesktopEntry::from_dir_entry(entry, &config)) | ||||
|         }) | ||||
|         .flatten() | ||||
|         .enumerate() | ||||
|         .map(|(i, val)| (val, i as u64)) | ||||
|         .collect()) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Kirottu
					Kirottu