WIP
This commit is contained in:
parent
f81acdc962
commit
b5de72e9bc
@ -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),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ use syn::{
|
||||
|
||||
/// Plugin configuration parsed from the macro input
|
||||
struct PluginConfig {
|
||||
struct_name: Ident,
|
||||
name: LitStr,
|
||||
priority: Option<LitInt>,
|
||||
description: Option<LitStr>,
|
||||
@ -22,10 +21,6 @@ struct PluginConfig {
|
||||
|
||||
impl Parse for PluginConfig {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
// Parse "struct StructName;"
|
||||
input.parse::<Token![struct]>()?;
|
||||
let struct_name = input.parse::<Ident>()?;
|
||||
input.parse::<Token![;]>()?;
|
||||
|
||||
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<String> {
|
||||
#description
|
||||
}
|
||||
|
||||
fn prefix(&self) -> Option<String> {
|
||||
#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<String> {
|
||||
#description
|
||||
}
|
||||
|
||||
fn prefix(&self) -> Option<String> {
|
||||
#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]
|
||||
|
@ -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<Box<dyn LauncherListItem
|
||||
entries
|
||||
}
|
||||
|
||||
plugin! {
|
||||
struct Drun;
|
||||
name: "drun",
|
||||
priority: 1000,
|
||||
description: "List and launch an installed application",
|
||||
prefix: "app",
|
||||
default_list: drun_default_list,
|
||||
filter: drun_filter,
|
||||
pub struct DrunPlugin;
|
||||
|
||||
impl DrunPlugin {
|
||||
pub fn new() -> 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 {
|
||||
|
@ -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<PathBuf> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
// Global state for file search
|
||||
static mut FILE_SEARCH_DATA: Option<FileSearchData> = None;
|
||||
|
||||
struct FileSearchData {
|
||||
pub struct FileSearchPlugin {
|
||||
search_paths: Vec<PathBuf>,
|
||||
skip_dirs: Vec<String>,
|
||||
// Running list of files in memory
|
||||
files: Arc<Mutex<Vec<FileEntry>>>,
|
||||
}
|
||||
|
||||
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<P: AsRef<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<P: AsRef<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<String>) -> 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<Box<dyn LauncherListItem>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn file_search_filter(_plugin: &FileSearchPlugin, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
||||
fn file_search_filter(plugin: &FileSearchPlugin, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
||||
if query.is_empty() {
|
||||
return file_search_default_list(_plugin);
|
||||
return file_search_default_list(plugin);
|
||||
}
|
||||
|
||||
let mut entries: Vec<Box<dyn LauncherListItem>> = 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<Box<dyn La
|
||||
entries
|
||||
}
|
||||
|
||||
fn file_search_init(_plugin: &FileSearchPlugin) {
|
||||
let data = get_file_search_data();
|
||||
let data_clone = FileSearchData {
|
||||
search_paths: data.search_paths.clone(),
|
||||
skip_dirs: data.skip_dirs.clone(),
|
||||
files: Arc::clone(&data.files),
|
||||
fn file_search_init(plugin: &FileSearchPlugin) {
|
||||
// Start async file scanning with 500ms timeout
|
||||
let self_clone = FileSearchPlugin {
|
||||
search_paths: plugin.search_paths.clone(),
|
||||
skip_dirs: plugin.skip_dirs.clone(),
|
||||
files: Arc::clone(&plugin.files),
|
||||
};
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
data_clone
|
||||
self_clone
|
||||
.init_with_timeout(Duration::from_millis(2000))
|
||||
.await;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
plugin! {
|
||||
struct FileSearch;
|
||||
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
|
||||
pub fn new() -> FileSearchPlugin {
|
||||
FileSearchPlugin::new()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user