From b5de72e9bc42e81c76aff94e844ff885939e22ac Mon Sep 17 00:00:00 2001 From: Javier Feliz Date: Fri, 5 Sep 2025 21:15:35 -0400 Subject: [PATCH] WIP --- waycast-gtk/src/main.rs | 4 +- waycast-macros/src/lib.rs | 98 +++++++++----------------- waycast-plugins/src/drun.rs | 29 +++++--- waycast-plugins/src/file_search.rs | 106 ++++++++++++----------------- 4 files changed, 96 insertions(+), 141 deletions(-) diff --git a/waycast-gtk/src/main.rs b/waycast-gtk/src/main.rs index 3ec8d87..7901de3 100644 --- a/waycast-gtk/src/main.rs +++ b/waycast-gtk/src/main.rs @@ -13,9 +13,9 @@ fn main() { .build(); app.connect_activate(|app| { - let file_search_plugin = waycast_plugins::file_search::new(); + let mut file_search_plugin = waycast_plugins::file_search::new(); - match waycast_plugins::file_search::add_search_path("/home/javi/working-files/DJ Music/") { + match file_search_plugin.add_search_path("/home/javi/working-files/DJ Music/") { Err(e) => eprintln!("{}", e), _ => (), } diff --git a/waycast-macros/src/lib.rs b/waycast-macros/src/lib.rs index 9619f63..3ef7dbb 100644 --- a/waycast-macros/src/lib.rs +++ b/waycast-macros/src/lib.rs @@ -9,7 +9,6 @@ use syn::{ /// Plugin configuration parsed from the macro input struct PluginConfig { - struct_name: Ident, name: LitStr, priority: Option, description: Option, @@ -22,10 +21,6 @@ struct PluginConfig { impl Parse for PluginConfig { fn parse(input: ParseStream) -> Result { - // Parse "struct StructName;" - input.parse::()?; - let struct_name = input.parse::()?; - input.parse::()?; let mut name = None; let mut priority = None; @@ -98,11 +93,10 @@ impl Parse for PluginConfig { // Validate required fields let name = name.ok_or_else(|| { - syn::Error::new_spanned(&struct_name, "Plugin must have a 'name' field") + syn::Error::new(input.span(), "Plugin must have a 'name' field") })?; Ok(PluginConfig { - struct_name, name, priority, description, @@ -116,15 +110,8 @@ impl Parse for PluginConfig { } impl PluginConfig { - /// Generate the full plugin struct name with "Plugin" suffix - fn plugin_struct_name(&self) -> Ident { - let name_str = format!("{}Plugin", self.struct_name); - Ident::new(&name_str, self.struct_name.span()) - } - - /// Generate the implementation of LauncherPlugin trait - fn generate_plugin_impl(&self) -> proc_macro2::TokenStream { - let plugin_struct_name = self.plugin_struct_name(); + /// Generate the implementation of LauncherPlugin trait methods + fn generate(&self) -> proc_macro2::TokenStream { let name_str = &self.name; // Generate priority method @@ -201,65 +188,46 @@ impl PluginConfig { }; quote! { - impl waycast_core::LauncherPlugin for #plugin_struct_name { - #init_method + #init_method - fn name(&self) -> String { - #name_str.to_string() - } - - fn priority(&self) -> i32 { - #priority - } - - fn description(&self) -> Option { - #description - } - - fn prefix(&self) -> Option { - #prefix - } - - fn by_prefix_only(&self) -> bool { - #by_prefix_only - } - - #default_list_method - - #filter_method - } - } - } - - /// Generate the complete plugin code - fn generate(&self) -> proc_macro2::TokenStream { - let plugin_struct_name = self.plugin_struct_name(); - let plugin_impl = self.generate_plugin_impl(); - - quote! { - pub struct #plugin_struct_name {} - - impl #plugin_struct_name { - pub fn new() -> Self { - #plugin_struct_name {} - } + fn name(&self) -> String { + #name_str.to_string() } - #plugin_impl + fn priority(&self) -> i32 { + #priority + } + + fn description(&self) -> Option { + #description + } + + fn prefix(&self) -> Option { + #prefix + } + + fn by_prefix_only(&self) -> bool { + #by_prefix_only + } + + #default_list_method + + #filter_method } } } /// The main plugin! proc macro /// -/// Usage: +/// Usage inside impl LauncherPlugin block: /// ```rust -/// plugin! { -/// struct Calculator; -/// name: "calculator", -/// priority: 500, -/// prefix: "calc", -/// filter: calc_filter, +/// impl LauncherPlugin for MyPlugin { +/// plugin! { +/// name: "calculator", +/// priority: 500, +/// prefix: "calc", +/// filter: calc_filter, +/// } /// } /// ``` #[proc_macro] diff --git a/waycast-plugins/src/drun.rs b/waycast-plugins/src/drun.rs index b00dd90..1c71319 100644 --- a/waycast-plugins/src/drun.rs +++ b/waycast-plugins/src/drun.rs @@ -1,6 +1,6 @@ use gio::{prelude::*, AppInfo, DesktopAppInfo, Icon}; -use waycast_core::{LaunchError, LauncherListItem}; -use crate::plugin; +use waycast_core::{LaunchError, LauncherListItem, LauncherPlugin}; +use waycast_macros::plugin; #[derive(Debug)] pub struct DesktopEntry { @@ -113,14 +113,23 @@ fn drun_filter(plugin: &DrunPlugin, query: &str) -> Vec Self { + DrunPlugin + } +} + +impl LauncherPlugin for DrunPlugin { + plugin! { + name: "drun", + priority: 1000, + description: "List and launch an installed application", + prefix: "app", + default_list: drun_default_list, + filter: drun_filter + } } pub fn new() -> DrunPlugin { diff --git a/waycast-plugins/src/file_search.rs b/waycast-plugins/src/file_search.rs index f6e1231..db7e4b3 100644 --- a/waycast-plugins/src/file_search.rs +++ b/waycast-plugins/src/file_search.rs @@ -9,7 +9,7 @@ use tokio::sync::Mutex; use walkdir::{DirEntry, WalkDir}; use waycast_macros::plugin; -use waycast_core::{LaunchError, LauncherListItem}; +use waycast_core::{LaunchError, LauncherListItem, LauncherPlugin}; #[derive(Clone)] struct FileEntry { @@ -104,16 +104,27 @@ pub fn default_search_list() -> Vec { Vec::new() } -// Global state for file search -static mut FILE_SEARCH_DATA: Option = None; - -struct FileSearchData { +pub struct FileSearchPlugin { search_paths: Vec, skip_dirs: Vec, + // Running list of files in memory files: Arc>>, } -impl FileSearchData { +impl FileSearchPlugin { + pub fn new() -> Self { + FileSearchPlugin { + search_paths: default_search_list(), + skip_dirs: vec![ + String::from("vendor"), + String::from("node_modules"), + String::from("cache"), + String::from("zig-cache"), + ], + files: Arc::new(Mutex::new(Vec::new())), + } + } + pub fn add_search_path>(&mut self, path: P) -> Result<(), String> { let p = path.as_ref(); @@ -170,43 +181,6 @@ impl FileSearchData { } } -fn get_file_search_data() -> &'static FileSearchData { - unsafe { - FILE_SEARCH_DATA.as_ref().unwrap() - } -} - -fn get_file_search_data_mut() -> &'static mut FileSearchData { - unsafe { - FILE_SEARCH_DATA.as_mut().unwrap() - } -} - -pub fn new() -> FileSearchPlugin { - unsafe { - FILE_SEARCH_DATA = Some(FileSearchData { - search_paths: default_search_list(), - skip_dirs: vec![ - String::from("vendor"), - String::from("node_modules"), - String::from("cache"), - String::from("zig-cache"), - ], - files: Arc::new(Mutex::new(Vec::new())), - }); - } - - FileSearchPlugin::new() -} - -pub fn add_search_path>(path: P) -> Result<(), String> { - get_file_search_data_mut().add_search_path(path) -} - -pub fn add_skip_dir(directory_name: String) -> Result<(), String> { - get_file_search_data_mut().add_skip_dir(directory_name) -} - fn skip_hidden(entry: &DirEntry) -> bool { entry .file_name() @@ -223,20 +197,31 @@ fn skip_dir(entry: &DirEntry, dirs: &Vec) -> bool { .unwrap_or(false) } +impl LauncherPlugin for FileSearchPlugin { + plugin! { + name: "Files", + priority: 500, + description: "Search and open files", + prefix: "f", + init: file_search_init, + default_list: file_search_default_list, + filter: file_search_filter + } +} + fn file_search_default_list(_plugin: &FileSearchPlugin) -> Vec> { Vec::new() } -fn file_search_filter(_plugin: &FileSearchPlugin, query: &str) -> Vec> { +fn file_search_filter(plugin: &FileSearchPlugin, query: &str) -> Vec> { if query.is_empty() { - return file_search_default_list(_plugin); + return file_search_default_list(plugin); } let mut entries: Vec> = Vec::new(); - let data = get_file_search_data(); // Try to get files without blocking - if indexing is still in progress, return empty - if let Ok(files) = data.files.try_lock() { + if let Ok(files) = plugin.files.try_lock() { for f in files.iter() { if let Some(file_name) = f.path.file_name() { let cmp = file_name.to_string_lossy().to_lowercase(); @@ -250,31 +235,24 @@ fn file_search_filter(_plugin: &FileSearchPlugin, query: &str) -> Vec FileSearchPlugin { + FileSearchPlugin::new() +} \ No newline at end of file