Better plugin creation interface
This commit is contained in:
parent
a5a597611e
commit
0cd68cb58c
@ -26,7 +26,10 @@ pub trait LauncherPlugin {
|
|||||||
// Only search/use this plugin if the prefix was typed
|
// Only search/use this plugin if the prefix was typed
|
||||||
fn by_prefix_only(&self) -> bool;
|
fn by_prefix_only(&self) -> bool;
|
||||||
// Actual item searching functions
|
// Actual item searching functions
|
||||||
fn default_list(&self) -> Vec<Box<dyn LauncherListItem>>;
|
fn default_list(&self) -> Vec<Box<dyn LauncherListItem>> {
|
||||||
|
// Default empty list - plugins can override this
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>>;
|
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ impl PluginConfig {
|
|||||||
quote! {}
|
quote! {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate default_list method
|
// Generate default_list method only if explicitly specified in config
|
||||||
let default_list_method = if let Some(ref default_list_fn) = self.default_list_fn {
|
let default_list_method = if let Some(ref default_list_fn) = self.default_list_fn {
|
||||||
quote! {
|
quote! {
|
||||||
fn default_list(&self) -> Vec<Box<dyn waycast_core::LauncherListItem>> {
|
fn default_list(&self) -> Vec<Box<dyn waycast_core::LauncherListItem>> {
|
||||||
@ -162,14 +162,11 @@ impl PluginConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
// Don't generate default_list method - user must implement it themselves
|
||||||
fn default_list(&self) -> Vec<Box<dyn waycast_core::LauncherListItem>> {
|
quote! {}
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate filter method
|
// Generate filter method only if explicitly specified in config
|
||||||
let filter_method = if let Some(ref filter_fn) = self.filter_fn {
|
let filter_method = if let Some(ref filter_fn) = self.filter_fn {
|
||||||
quote! {
|
quote! {
|
||||||
fn filter(&self, query: &str) -> Vec<Box<dyn waycast_core::LauncherListItem>> {
|
fn filter(&self, query: &str) -> Vec<Box<dyn waycast_core::LauncherListItem>> {
|
||||||
@ -177,11 +174,8 @@ impl PluginConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
// Don't generate filter method - user must implement it themselves
|
||||||
fn filter(&self, query: &str) -> Vec<Box<dyn waycast_core::LauncherListItem>> {
|
quote! {}
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -79,32 +79,7 @@ pub fn get_desktop_entries() -> Vec<DesktopEntry> {
|
|||||||
entries
|
entries
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drun_default_list(_plugin: &DrunPlugin) -> Vec<Box<dyn LauncherListItem>> {
|
|
||||||
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
|
||||||
|
|
||||||
for e in get_desktop_entries() {
|
|
||||||
entries.push(Box::new(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
entries
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drun_filter(plugin: &DrunPlugin, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
|
||||||
if query.is_empty() {
|
|
||||||
return drun_default_list(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
let query_lower = query.to_lowercase();
|
|
||||||
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
|
||||||
for entry in drun_default_list(plugin) {
|
|
||||||
let title_lower = entry.title().to_lowercase();
|
|
||||||
if title_lower.contains(&query_lower) {
|
|
||||||
entries.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entries
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DrunPlugin;
|
pub struct DrunPlugin;
|
||||||
|
|
||||||
@ -119,9 +94,34 @@ impl LauncherPlugin for DrunPlugin {
|
|||||||
name: "drun",
|
name: "drun",
|
||||||
priority: 1000,
|
priority: 1000,
|
||||||
description: "List and launch an installed application",
|
description: "List and launch an installed application",
|
||||||
prefix: "app",
|
prefix: "app"
|
||||||
default_list: drun_default_list,
|
}
|
||||||
filter: drun_filter
|
|
||||||
|
fn default_list(&self) -> Vec<Box<dyn LauncherListItem>> {
|
||||||
|
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
||||||
|
|
||||||
|
for e in get_desktop_entries() {
|
||||||
|
entries.push(Box::new(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
entries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
||||||
|
if query.is_empty() {
|
||||||
|
return self.default_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
let query_lower = query.to_lowercase();
|
||||||
|
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
||||||
|
for entry in self.default_list() {
|
||||||
|
let title_lower = entry.title().to_lowercase();
|
||||||
|
if title_lower.contains(&query_lower) {
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,9 +192,7 @@ impl LauncherPlugin for FileSearchPlugin {
|
|||||||
name: "Files",
|
name: "Files",
|
||||||
priority: 500,
|
priority: 500,
|
||||||
description: "Search and open files",
|
description: "Search and open files",
|
||||||
prefix: "f",
|
prefix: "f"
|
||||||
default_list: file_search_default_list,
|
|
||||||
filter: file_search_filter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self) {
|
fn init(&self) {
|
||||||
@ -214,35 +212,33 @@ impl LauncherPlugin for FileSearchPlugin {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn file_search_default_list(_plugin: &FileSearchPlugin) -> Vec<Box<dyn LauncherListItem>> {
|
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
||||||
Vec::new()
|
if query.is_empty() {
|
||||||
}
|
return self.default_list();
|
||||||
|
}
|
||||||
|
|
||||||
fn file_search_filter(plugin: &FileSearchPlugin, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
||||||
if query.is_empty() {
|
|
||||||
return file_search_default_list(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
// Try to get files without blocking - if indexing is still in progress, return empty
|
||||||
|
if let Ok(files) = self.files.try_lock() {
|
||||||
// Try to get files without blocking - if indexing is still in progress, return empty
|
for f in files.iter() {
|
||||||
if let Ok(files) = plugin.files.try_lock() {
|
if let Some(file_name) = f.path.file_name() {
|
||||||
for f in files.iter() {
|
let cmp = file_name.to_string_lossy().to_lowercase();
|
||||||
if let Some(file_name) = f.path.file_name() {
|
if cmp.contains(&query.to_lowercase()) {
|
||||||
let cmp = file_name.to_string_lossy().to_lowercase();
|
entries.push(Box::new(f.clone()));
|
||||||
if cmp.contains(&query.to_lowercase()) {
|
}
|
||||||
entries.push(Box::new(f.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
entries
|
entries
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn new() -> FileSearchPlugin {
|
pub fn new() -> FileSearchPlugin {
|
||||||
FileSearchPlugin::new()
|
FileSearchPlugin::new()
|
||||||
}
|
}
|
@ -81,28 +81,26 @@ impl LauncherPlugin for ProjectsPlugin {
|
|||||||
name: "Projects",
|
name: "Projects",
|
||||||
priority: 800,
|
priority: 800,
|
||||||
description: "Search and open code projects",
|
description: "Search and open code projects",
|
||||||
prefix: "proj",
|
prefix: "proj"
|
||||||
default_list: projects_default_list,
|
|
||||||
filter: projects_filter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self) {
|
fn init(&self) {
|
||||||
let files_clone = Arc::clone(&self.files);
|
let files_clone = Arc::clone(&self.files);
|
||||||
let search_paths = self.search_paths.clone();
|
let search_paths = self.search_paths.clone();
|
||||||
let skip_dirs = self.skip_dirs.clone();
|
let skip_dirs = self.skip_dirs.clone();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
let mut project_entries = Vec::new();
|
let mut project_entries = Vec::new();
|
||||||
|
|
||||||
for search_path in &search_paths {
|
for search_path in &search_paths {
|
||||||
if let Ok(entries) = fs::read_dir(search_path) {
|
if let Ok(entries) = fs::read_dir(search_path) {
|
||||||
for entry in entries.flatten() {
|
for entry in entries.flatten() {
|
||||||
if let Ok(file_type) = entry.file_type() {
|
if let Ok(file_type) = entry.file_type() {
|
||||||
if file_type.is_dir() {
|
if file_type.is_dir() {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
// Skip hidden directories (starting with .)
|
// Skip hidden directories (starting with .)
|
||||||
if let Some(file_name) = path.file_name() {
|
if let Some(file_name) = path.file_name() {
|
||||||
if let Some(name_str) = file_name.to_str() {
|
if let Some(name_str) = file_name.to_str() {
|
||||||
@ -110,12 +108,12 @@ impl LauncherPlugin for ProjectsPlugin {
|
|||||||
if name_str.starts_with('.') {
|
if name_str.starts_with('.') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip directories in skip list
|
// Skip directories in skip list
|
||||||
if should_skip_dir(name_str, &skip_dirs) {
|
if should_skip_dir(name_str, &skip_dirs) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
project_entries.push(ProjectEntry { path });
|
project_entries.push(ProjectEntry { path });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,44 +122,39 @@ impl LauncherPlugin for ProjectsPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the shared files collection
|
// Update the shared files collection
|
||||||
let mut files_guard = files_clone.lock().await;
|
let mut files_guard = files_clone.lock().await;
|
||||||
*files_guard = project_entries;
|
*files_guard = project_entries;
|
||||||
|
|
||||||
println!("Projects plugin: Found {} projects", files_guard.len());
|
println!("Projects plugin: Found {} projects", files_guard.len());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn projects_default_list(_plugin: &ProjectsPlugin) -> Vec<Box<dyn LauncherListItem>> {
|
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
||||||
Vec::new()
|
if query.is_empty() {
|
||||||
}
|
return self.default_list();
|
||||||
|
}
|
||||||
|
|
||||||
fn projects_filter(plugin: &ProjectsPlugin, query: &str) -> Vec<Box<dyn LauncherListItem>> {
|
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
||||||
if query.is_empty() {
|
|
||||||
return projects_default_list(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries: Vec<Box<dyn LauncherListItem>> = Vec::new();
|
// Try to get files without blocking - if indexing is still in progress, return empty
|
||||||
|
if let Ok(files) = self.files.try_lock() {
|
||||||
// Try to get files without blocking - if indexing is still in progress, return empty
|
for f in files.iter() {
|
||||||
if let Ok(files) = plugin.files.try_lock() {
|
if let Some(file_name) = f.path.file_name() {
|
||||||
for f in files.iter() {
|
let cmp = file_name.to_string_lossy().to_lowercase();
|
||||||
if let Some(file_name) = f.path.file_name() {
|
if cmp.contains(&query.to_lowercase()) {
|
||||||
let cmp = file_name.to_string_lossy().to_lowercase();
|
entries.push(Box::new(f.clone()));
|
||||||
if cmp.contains(&query.to_lowercase()) {
|
}
|
||||||
entries.push(Box::new(f.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entries
|
||||||
}
|
}
|
||||||
|
|
||||||
entries
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn new() -> ProjectsPlugin {
|
pub fn new() -> ProjectsPlugin {
|
||||||
ProjectsPlugin {
|
ProjectsPlugin {
|
||||||
search_paths: Vec::new(),
|
search_paths: Vec::new(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user