Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ Built-in plugins and their command prefixes are:
- Recycle Bin cleanup (`rec`)
- Temporary files (`tmp`, `tmp new [name]`, `tmp open`, `tmp clear`, `tmp list`, `tmp rm`)
- ASCII art (`ascii text`)
- Saved apps (`app <filter>` or just `app`)
- Volume control (`vol 50`) *(Windows only)*
- Brightness control (`bright 50`) *(Windows only)*
- Command overview (`help`)
Expand Down Expand Up @@ -280,6 +281,9 @@ button or by right clicking an app in the results list and choosing *Edit
App*. Apps can also be removed from the list. All changes are written to
`actions.json` immediately.

Type `app <filter>` in the launcher to search these saved entries. Typing `app`
alone lists all saved apps.

## Packaging

The project can be compiled for Windows using `cargo build --release`.
Expand Down
65 changes: 41 additions & 24 deletions src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const SUBCOMMANDS: &[&str] = &[
"edit", "ma",
];

/// Prefix used to search user saved applications.
pub const APP_PREFIX: &str = "app";

fn scale_ui<R>(ui: &mut egui::Ui, scale: f32, add_contents: impl FnOnce(&mut egui::Ui) -> R) -> R {
ui.scope(|ui| {
if (scale - 1.0).abs() > f32::EPSILON {
Expand Down Expand Up @@ -449,9 +452,21 @@ impl LauncherApp {
let mut res: Vec<(Action, f32)> = Vec::new();

let trimmed = self.query.trim();
let query_lc = self.query.to_lowercase();
let trimmed_lc = trimmed.to_lowercase();

let search_actions =
trimmed_lc == APP_PREFIX || trimmed_lc.starts_with(&format!("{} ", APP_PREFIX));
let action_query = if search_actions {
if trimmed_lc == APP_PREFIX {
"".to_string()
} else {
trimmed.splitn(2, ' ').nth(1).unwrap_or("").to_string()
}
} else {
String::new()
};
let action_query_lc = action_query.to_lowercase();

if trimmed_lc.starts_with("g ") {
let filter = vec!["web_search".to_string()];
let plugin_results = self.plugins.search_filtered(
Expand Down Expand Up @@ -493,29 +508,31 @@ impl LauncherApp {
}
}
} else {
if self.query.is_empty() {
res.extend(self.actions.iter().cloned().map(|a| (a, 0.0)));
} else {
for a in &self.actions {
if self.fuzzy_weight <= 0.0 {
let alias_match = self
.folder_aliases
.get(&a.action)
.or_else(|| self.bookmark_aliases.get(&a.action))
.and_then(|v| v.as_ref())
.map(|s| s.to_lowercase().contains(&query_lc))
.unwrap_or(false);
let label_match = a.label.to_lowercase().contains(&query_lc);
let desc_match = a.desc.to_lowercase().contains(&query_lc);
if label_match || desc_match || alias_match {
let score = if alias_match { 1.0 } else { 0.0 };
res.push((a.clone(), score));
}
} else {
let s1 = self.matcher.fuzzy_match(&a.label, &self.query);
let s2 = self.matcher.fuzzy_match(&a.desc, &self.query);
if let Some(score) = s1.max(s2) {
res.push((a.clone(), score as f32 * self.fuzzy_weight));
if search_actions {
if action_query.is_empty() {
res.extend(self.actions.iter().cloned().map(|a| (a, 0.0)));
} else {
for a in &self.actions {
if self.fuzzy_weight <= 0.0 {
let alias_match = self
.folder_aliases
.get(&a.action)
.or_else(|| self.bookmark_aliases.get(&a.action))
.and_then(|v| v.as_ref())
.map(|s| s.to_lowercase().contains(&action_query_lc))
.unwrap_or(false);
let label_match = a.label.to_lowercase().contains(&action_query_lc);
let desc_match = a.desc.to_lowercase().contains(&action_query_lc);
if label_match || desc_match || alias_match {
let score = if alias_match { 1.0 } else { 0.0 };
res.push((a.clone(), score));
}
} else {
let s1 = self.matcher.fuzzy_match(&a.label, &action_query);
let s2 = self.matcher.fuzzy_match(&a.desc, &action_query);
if let Some(score) = s1.max(s2) {
res.push((a.clone(), score as f32 * self.fuzzy_weight));
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions tests/ranking.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use multi_launcher::gui::LauncherApp;
use multi_launcher::gui::{LauncherApp, APP_PREFIX};
use multi_launcher::plugin::PluginManager;
use multi_launcher::actions::Action;
use multi_launcher::settings::Settings;
Expand Down Expand Up @@ -35,7 +35,7 @@ fn usage_ranking() {
let settings = Settings::default();
let mut app = new_app_with_settings(&ctx, actions, settings);
app.usage.insert("b".into(), 5);
app.query = "foo".into();
app.query = format!("{} foo", APP_PREFIX);
app.search();
assert_eq!(app.results[0].action, "b");
}
Expand All @@ -52,7 +52,7 @@ fn fuzzy_vs_usage_weight() {
settings.usage_weight = 1.0;
let mut app = new_app_with_settings(&ctx, actions, settings);
app.usage.insert("b".into(), 20);
app.query = "abc".into();
app.query = format!("{} abc", APP_PREFIX);
app.search();
assert_eq!(app.results[0].action, "a");
}
4 changes: 3 additions & 1 deletion tests/selection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use multi_launcher::gui::LauncherApp;
use multi_launcher::gui::{LauncherApp, APP_PREFIX};
use multi_launcher::plugin::PluginManager;
use multi_launcher::actions::Action;
use multi_launcher::settings::Settings;
Expand Down Expand Up @@ -33,6 +33,7 @@ fn arrow_keys_update_selection() {
Action { label: "two".into(), desc: "".into(), action: "two".into(), args: None },
];
let mut app = new_app(&ctx, acts);
app.query = APP_PREFIX.into();
app.search();
assert_eq!(app.selected, None);
app.handle_key(egui::Key::ArrowDown);
Expand All @@ -51,6 +52,7 @@ fn enter_returns_selected_index() {
Action { label: "two".into(), desc: "".into(), action: "two".into(), args: None },
];
let mut app = new_app(&ctx, acts);
app.query = APP_PREFIX.into();
app.search();
app.handle_key(egui::Key::ArrowDown);
let idx = app.handle_key(egui::Key::Enter);
Expand Down